diff --git a/.gitignore b/.gitignore index 4c6a2d6..c38e706 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ resources/ # OS files .DS_Store - +GIT_INSTRUCTIONS.md +PR_DESCRIPTION.md diff --git a/GIT_INSTRUCTIONS.md b/GIT_INSTRUCTIONS.md new file mode 100644 index 0000000..4fa9c8a --- /dev/null +++ b/GIT_INSTRUCTIONS.md @@ -0,0 +1,43 @@ +# How to apply this content update + +## Prerequisites +- Close PR #7 (this supersedes it) +- You have push access to dcm-project/dcm-project.github.io + +## Steps + +```bash +# 1. Clone the repo +git clone git@github.com:dcm-project/dcm-project.github.io.git +cd dcm-project.github.io + +# 2. Create branch +git checkout -b content-update + +# 3. Remove old content (preserve everything else) +rm -rf content/ static/ +git add -A +git commit -m "Remove old content for replacement" + +# 4. Copy new content from the package +cp -r /path/to/website-content/content . +cp -r /path/to/website-content/static . + +# 5. Do NOT commit PR_DESCRIPTION.md or GIT_INSTRUCTIONS.md +# (they're for your reference only) + +# 6. Add and commit +git add -A +git commit -m "Update content: fixed, deduplicated architecture (55 data model docs, 15 specs, 4 OpenAPI) + +Addresses all PR #7 review comments: +- Eliminated duplicate data-model directories +- Fixed all broken cross-references +- Removed 116 duplicate files +- Updated provider count (12), policy count (8), classification enum (8) +- Synced all OpenAPI YAMLs with canonical schemas" + +# 7. Push and open PR +git push origin content-update +# Use PR_DESCRIPTION.md as the PR body +``` diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md new file mode 100644 index 0000000..b652c55 --- /dev/null +++ b/PR_DESCRIPTION.md @@ -0,0 +1,47 @@ +# Replace content with fixed, deduplicated architecture + +**Closes PR #7** — this PR replaces the content from PR #7 with the complete, fixed architecture documentation. All review comments from PR #7 are addressed: + +## Issues fixed from PR #7 + +- **@jenniferubah, @gabriel-farache:** Duplicate `data-model/` and `architecture/data-model/` directories → **eliminated**. Only `architecture/data-model/` exists now (55 docs with number prefixes). +- **@gabriel-farache:** Broken links to foundations, provider-contract, policy-contract, capabilities matrix → **all fixed**. Zero broken cross-references verified. +- **@gabriel-farache:** "links broken, I think all links should be reviewed" → **done**. Full automated cross-reference scan, zero broken links. +- **@machacekondra:** Slug duplicates (e.g., `foundations.md` duplicating `00-foundations.md`) → **removed**. 53 slug duplicates eliminated. +- **@machacekondra:** K8s operator SDK imperative vs declarative concern → addressed in updated `dcm-operator-sdk-api.md` +- **@pkliczewski:** Accreditation flow diagrams, data_classification ownership, GitOps handling → all clarified in updated docs + +## What changed vs PR #7 + +- 116 duplicate files removed (55 flat data-model copy + 53 slug dupes + 8 spec slug dupes) +- Provider count: 11 → 12 (ITSM Provider added) +- Policy count: 7 → 8 (ITSM Action added) +- data_classification enum: 5 → 8 values (phi, pci, classified added) +- All OpenAPI YAMLs synced with canonical schemas +- Capabilities Matrix deduplicated (6 duplicate summary tables removed) +- All Discussion Topics marked Resolved with cross-references + +## Content structure + +``` +content/ +├── _index.md +└── docs/ + ├── architecture/ + │ ├── data-model/ ← 55 docs + _index.md (SINGLE location) + │ └── specifications/ ← 15 specs + 4 OpenAPI YAMLs + _index.md + ├── capabilities-matrix.md + ├── DISCUSSION-TOPICS.md + ├── taxonomy.md + ├── project-overview.md + ├── enhancements/ + ├── implementations/ + └── schemas/ +static/ +└── capabilities/ + └── DCM-Capabilities-Matrix.csv +``` + +## How to apply + +This PR only touches `content/` and `static/`. No changes to `.github/workflows/`, `hugo.yaml`, `go.mod`, `go.sum`, `Makefile`, `assets/`, or `layouts/`. diff --git a/content/_index.md b/content/_index.md index 4115fd6..287c6b2 100644 --- a/content/_index.md +++ b/content/_index.md @@ -3,66 +3,53 @@ title: DCM Project toc: false --- -
+# Data Center Management -{{< hextra/hero-headline >}} - Data Center Management -{{< /hextra/hero-headline >}} +**Hyperscaler-like cloud experience for enterprise on-premises and sovereign cloud infrastructure.** -
+DCM is an open-source governing framework that gives enterprise IT organizations the operational +model and self-service capabilities of a public cloud provider — on infrastructure they own and control. -{{< hextra/hero-subtitle >}} - Hyperscaler-like cloud experience for 
enterprise on-premises and sovereign cloud infrastructure -{{< /hextra/hero-subtitle >}} +{{< button href="/docs/project-overview" >}}What is DCM?{{< /button >}} +{{< button href="/docs" >}}Documentation{{< /button >}} +{{< button href="/docs/implementations" >}}Implementations{{< /button >}} +{{< button href="https://github.com/dcm-project" >}}GitHub{{< /button >}} -
- -{{< hextra/hero-button text="Get Started" link="docs" >}} -{{< hextra/hero-button text="View on GitHub" link="https://github.com/dcm-project" >}} - -
+--- -## About DCM +## The Problem DCM Solves -The Data Center Management (DCM) project provides a hyperscaler-like cloud experience for enterprise on-premises and sovereign cloud infrastructure. DCM is an **API-first, technology-agnostic framework** that relies on a declarative data model where the desired state of infrastructure is defined as code. +Enterprise on-premises infrastructure is managed by dozens of disconnected tools and manual +processes. A single VM can take weeks across five teams. No one has a trustworthy answer to: +*what exists, what was requested, what was provisioned, and does current state match intended state?* -The overarching goal is to define the configuration of an entire data center in version-controlled code, enabling rapid deployment and full environment recovery. +DCM establishes a unified, declarative single source of truth for all infrastructure state +and connects it to a policy-driven control plane that governs every lifecycle operation. -
+## Three Foundational Abstractions -## Core Principles +{{< cards >}} + {{< card title="Data" icon="database" subtitle="Every artifact with a UUID, lifecycle state, and field-level provenance. Four states: Intent · Requested · Realized · Discovered." >}} + {{< card title="Provider" icon="plug" subtitle="11 typed provider contracts. Service, Information, Storage, Meta, Credential, Auth, Policy, Notification, Message Bus, ITSM, Peer DCM." >}} + {{< card title="Policy" icon="shield-check" subtitle="7 typed policy schemas. GateKeeper · Validation · Transformation · Orchestration Flow · Recovery · Governance Matrix · Lifecycle." >}} +{{< /cards >}} -{{< hextra/feature-grid >}} - {{< hextra/feature-card - title="🔌 API-First Design" - subtitle="Technology-agnostic framework built on modern API principles for maximum flexibility." - style="background: radial-gradient(ellipse at 50% 80%,rgba(194,97,254,0.15),hsla(0,0%,100%,0));" - >}} - {{< hextra/feature-card - title="☁️ Cloud-Native Approach" - subtitle="Moving away from monolithic management toward distributed, cloud-native principles." - style="background: radial-gradient(ellipse at 50% 80%,rgba(62,151,255,0.15),hsla(0,0%,100%,0));" - >}} - {{< hextra/feature-card - title="📚 Service Catalog" - subtitle="Comprehensive catalog system for managing services and infrastructure offerings." - >}} - {{< hextra/feature-card - title="⚖️ Rules Engine" - subtitle="Built-in governance and policy enforcement through a flexible rules engine." - >}} -{{< /hextra/feature-grid >}} +## Who Benefits -
+{{< cards >}} + {{< card title="Application Teams" icon="users" subtitle="Self-service catalog. Request infrastructure. Receive provisioned resources. No tickets, no manual coordination." >}} + {{< card title="Platform Engineers" icon="cog" subtitle="Single control plane. Policy-governed standards. Automatic drift detection. Consistency is structural." >}} + {{< card title="Security & Compliance" icon="lock-closed" subtitle="Policy-as-code tested before activation. Tamper-evident audit trail. Continuous accreditation monitoring." >}} + {{< card title="Regulated Industries" icon="building-library" subtitle="FedRAMP · CMMC · HIPAA · SOC 2 · ISO 27001 · DoD IL2–IL6. Compliance by construction." >}} +{{< /cards >}} --- -
- -## Explore More +## Explore {{< cards >}} - {{< card link="docs" title="Documentation" icon="book-open" subtitle="Guides, tutorials, and technical documentation" >}} - {{< card link="docs/enhancements" title="Enhancements" icon="light-bulb" subtitle="Enhancement proposals documenting new features and changes" >}} - {{< card link="blog" title="Blog & Updates" icon="newspaper" subtitle="Latest news, releases, and articles" >}} + {{< card link="docs/project-overview" title="Project Overview" icon="information-circle" subtitle="What DCM is, what it does, who it benefits, where it operates." >}} + {{< card link="docs/architecture" title="Architecture" icon="template" subtitle="55 data model docs · 15 specs · 299 capabilities across 38 domains." >}} + {{< card link="docs/implementations" title="Implementations" icon="rocket-launch" subtitle="Reference implementations. Example #1: Summit 2026 demo on OpenShift." >}} + {{< card link="https://github.com/dcm-project" title="GitHub" icon="code-bracket" subtitle="Source, issues, and contributions." >}} {{< /cards >}} diff --git a/content/about/_index.md b/content/about/_index.md deleted file mode 100644 index a0835da..0000000 --- a/content/about/_index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: About DCM ---- - -# About the DCM Project - -DCM (Data Center Management) is an open-source platform for managing data center resources. - -More information coming soon. - diff --git a/content/blog/_index.md b/content/blog/_index.md deleted file mode 100644 index 2f5ee7e..0000000 --- a/content/blog/_index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Blog ---- - -# DCM Blog - -Stay updated with the latest news, releases, and technical articles about the DCM project. - -Blog posts coming soon. - diff --git a/content/docs/AI-ONBOARDING.md b/content/docs/AI-ONBOARDING.md new file mode 100644 index 0000000..fce634f --- /dev/null +++ b/content/docs/AI-ONBOARDING.md @@ -0,0 +1,107 @@ +# Using the DCM AI Prompt for Architecture Exploration + +**Purpose:** The DCM AI prompt (`DCM-AI-PROMPT.md`) is a 5,772-line comprehensive knowledge base covering every architectural decision, capability, data structure, and cross-reference in DCM. When loaded into Claude or another capable LLM, it enables conversational exploration of the architecture — faster than reading documentation. + +--- + +## How to Use It + +### Option 1: Claude.ai (Recommended) + +1. Go to [claude.ai](https://claude.ai) +2. Start a new conversation +3. Attach `DCM-AI-PROMPT.md` as a file (drag and drop, or use the attachment button) +4. Ask your question + +The prompt stays active for the entire conversation. You can ask follow-up questions without re-uploading. + +### Option 2: Claude Projects + +1. Create a Project in Claude +2. Add `DCM-AI-PROMPT.md` to the Project's knowledge base +3. Every conversation in that Project automatically has the full architecture context + +This is the best option for ongoing use — the prompt is always available without re-uploading. + +### Option 3: Other LLMs + +The prompt works with any LLM that supports large context windows (128K+ tokens). Upload `DCM-AI-PROMPT.md` as context. The prompt is plain Markdown with no tool-specific formatting. + +--- + +## Example Questions + +### Understanding a concept +> "How does the override model work?" +> → 2-paragraph answer explaining the 5 mechanisms with severity ordering + +> "What's the difference between Internal and External policy evaluation?" +> → Comparison of the two modes with when to use each + +> "What happens when a sovereignty policy blocks a request?" +> → Full flow: POLICY_BLOCKED state, consumer guidance, resolution options + +### Finding specific details +> "What are the 5 provider types and when would I use each?" +> → Table with type, capability, data direction, and example + +> "What SQL tables exist and what's in each one?" +> → Full schema overview with 18 tables, their purposes, and RLS details + +> "What events does the system emit during a VM provision?" +> → Ordered list of events with payloads from intent through realization + +### Implementation guidance +> "I'm building a new service provider. What endpoints do I need to implement?" +> → Provider contract requirements by level (Level 1: basic, Level 2: discovery, Level 3: full) + +> "How do I write a GateKeeper policy that blocks VMs over 16 CPUs for a specific tenant?" +> → Example Rego policy with match conditions, output schema, and test cases + +> "How does the three-tier app example work end to end?" +> → Full walkthrough with YAML payloads, dependency resolution, and binding field injection + +### Cross-cutting questions +> "Does the architecture cover [specific capability] from PR #50?" +> → Yes/no with specific doc references and capability IDs + +> "What would change if we added a new provider type?" +> → Schema migration needed, contract extension points, capabilities to declare + +--- + +## What the Prompt Covers + +The prompt contains 125 sections organized into these areas: + +| Area | Sections | What's in it | +|------|----------|-------------| +| Foundational Abstractions | 0-3 | Data, Provider, Policy — the three pillars | +| Data Model | 4-14 | Four states, layers, entities, relationships, groups | +| Control Plane | 15-16 | 9 services, pipeline flow, request routing | +| Providers | 16-22 | 6 types, contracts, naturalization, discovery | +| Policy | 17, 23-28 | 8 types, evaluation model, overrides, templates | +| Audit & Security | 29-35 | Merkle tree, stage signing, governance matrix, zero trust | +| API Specifications | 36-40 | Consumer API (74 paths), Admin API (61 paths), Provider Callback | +| Capabilities | 41 | 331 capabilities across 39 domains | +| SQL Schema | 42 | 18 tables with column definitions | +| Event Catalog | 43 | 109 events across 23 domains | +| Implementation | 44-50 | Deployment, profiles, test framework | + +--- + +## Tips + +- **Be specific.** "How does policy evaluation work?" gets a better answer than "Tell me about policies." +- **Ask for examples.** "Show me the YAML payload at each stage of a VM provision" gets concrete data structures. +- **Ask for cross-references.** "Which docs should I read for provider development?" gets a prioritized list. +- **Ask for comparisons.** "How does this differ from Crossplane / Kessel / KRO?" gets an informed comparison. +- **Ask implementation questions.** "What Go interfaces would a new service provider need?" gets code-level guidance. + +--- + +## Where to Find the Prompt + +- **DCM repo:** `architecture/ai/DCM-AI-PROMPT.md` +- **Website:** Published at `dcm-project.github.io/docs/architecture/ai/DCM-AI-PROMPT/` +- **Examples repo:** Copied into `docs/DCM-AI-PROMPT.md` for reference diff --git a/content/docs/DCM-Capabilities-Matrix.md b/content/docs/DCM-Capabilities-Matrix.md new file mode 100644 index 0000000..721ce0c --- /dev/null +++ b/content/docs/DCM-Capabilities-Matrix.md @@ -0,0 +1,715 @@ +# DCM — Foundational Capabilities Matrix + +> **Purpose:** This document defines the core operational capabilities required for DCM to perform lifecycle management as defined by the data model. Each capability maps to a consumer/service provider perspective and will be used to drive implementation work in Jira. +> +> **How to read this document:** +> - **Capability Domain** — the architectural area the capability belongs to +> - **Capability** — a discrete operational function; the smallest unit of independently implementable behavior +> - **Consumer perspective** — what the end user / application team experiences +> - **Service Provider perspective** — what the Service Provider or platform component must implement +> - **Platform/Admin perspective** — what the platform engineer or SRE must configure or operate +> - **Depends on** — other capabilities that must exist first + +--- + +## 1. Identity and Access Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| IAM-001 | Actor Authentication | Authenticate to DCM via configured IdP | — | Register and configure Auth Providers; manage local user store | — | +| IAM-002 | Session Token Management | Receive and use session tokens; token refresh | — | Configure session TTL, failover chain | IAM-001 | +| IAM-003 | Role-Based Access Control | Receive role-appropriate service catalog and API responses | — | Declare role mappings; assign roles to actors | IAM-001 | +| IAM-004 | Group Membership Resolution | Group memberships automatically applied from IdP | — | Map IdP groups to DCM groups; declare group-role relationships | IAM-001, IAM-003 | +| IAM-005 | Multi-Factor Authentication | Satisfy per-session and step-up MFA challenges | — | Configure MFA methods; declare step-up operations | IAM-001 | +| IAM-006 | SCIM Automated Provisioning | Actor created/updated/deprovisioned from IdP automatically | — | Configure SCIM endpoint and attribute mappings | IAM-001 | +| IAM-007 | Tenant Scope Enforcement | Access restricted to authorized Tenants | — | Declare Tenant membership; configure cross-tenant policies | IAM-003, IAM-004 | +| AUTH-002 | Multi-Auth-Provider Routing | — | — | Register multiple Auth Providers simultaneously; ingress routes by authentication signal | IAM-001 | +| AUTH-003 | Auth Provider Trust Level Enforcement | Requests evaluated per provider trust level (authoritative / verified / advisory) | — | Configure trust level per registered Auth Provider | IAM-001 | +| AUTH-004 | Auth Provider Artifact Versioning | — | — | Manage role/tenant mapping versioning through standard DCM artifact lifecycle; activate/deprecate mappings | IAM-003 | +| AUTH-005 | Auth Provider Failover | Existing sessions remain valid on provider failure; new auth routes to failover chain | — | Configure failover chain; monitor provider health; manage session cache TTL | IAM-001 | +| AUTH-006 | Auth Context in Audit Trail | — | — | Auth Provider identity and ingress context automatically recorded in all audit records | IAM-001 | +| AUTH-007 | Auth Provider Credential Security | — | — | Enforce Auth Provider config credentials reference secrets management; no plaintext credentials | IAM-001 | +| AUTH-008 | No Anonymous Access | — | — | Enforce authenticated access at all ingress surfaces across all profiles | IAM-001 | +| AUTH-009 | Webhook and Message Bus Authentication | Authenticate webhook registrations | — | Enforce authentication on all inbound surfaces regardless of profile | IAM-001 | +| AUTH-010 | Per-Actor Rate Limiting | Receive 429 responses when rate limit exceeded | — | Configure rate limits per actor; manage burst allowances | IAM-001 | +| AUTH-011 | Git PR Identity Resolution | Git PR submissions resolve to same actor identity as API/UI login | — | Configure Auth Provider to trust Git server's identity assertion | IAM-001 | +| AUTH-012 | SCIM Automated Provisioning | Actor created/updated/deprovisioned from IdP automatically via SCIM | — | Configure SCIM 2.0 endpoint; manage suspension-on-deprovision policy | IAM-001 | +| AUTH-013 | In-Flight Request Continuity on Auth Failure | In-flight requests before auth failure are not interrupted | — | Configure session cache TTL; manage graceful degradation | IAM-001 | +| AUTH-014 | Two-Tier MFA Enforcement | Satisfy per-session MFA at login and step-up MFA for high-risk operations | — | Configure per-session and step-up MFA; declare step-up trigger operations | IAM-005 | +| AUTH-015 | Built-In Auth Provider Storage Backend | — | — | Configure built-in Auth Provider storage backend (SQLite for minimal/dev; PostgreSQL/MySQL for standard+) | IAM-001 | + +--- + +## 2. Service Catalog + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| CAT-001 | Service Catalog Presentation | Browse available services filtered by RBAC | Declare catalog items for offered resource types | Activate catalog items; configure catalog visibility policies | IAM-003, IAM-007 | +| CAT-002 | Service Schema Discovery | View field schemas, constraints, and edit constraints for a catalog item | Declare field schemas in Resource Type Spec | Configure constraint visibility level per profile | CAT-001 | +| CAT-003 | Catalog Item Search and Filter | Search catalog by keyword, resource type, tag | — | Configure Search Index for catalog | CAT-001 | +| CAT-004 | Catalog Item Versioning | Request a specific version of a catalog item | Publish new catalog item versions following semver | Manage version lifecycle; enforce deprecation timelines | CAT-001 | +| CAT-005 | Cost Estimation | Receive estimated cost before submitting a request | Declare cost metadata on provider registration | Configure Cost Analysis component | CAT-001 | +| CAT-006 | Dependency Visualization | See required dependencies for a catalog item before requesting | Declare dependency graph in Resource Type Spec | — | CAT-001 | +| CAT-007 | Catalog Item Deprecation | Receive deprecation warnings on deprecated catalog items | Declare successor types in deprecation notice | Manage deprecation lifecycle; notify consumers | CAT-004 | + +--- + +## 3. Request Lifecycle Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| REQ-001 | Submit Service Request | Submit a resource request via UI, API, or Git PR | — | Configure request ingress surfaces | IAM-007, CAT-001 | +| REQ-002 | Intent State Capture | Request stored as versioned GitOps artifact before processing | — | Configure Intent Store; manage Git repository structure | REQ-001 | +| REQ-003 | Layer Assembly | Request enriched with organizational defaults and context layers | Contribute Service Layers for resource types | Manage Core Layers; configure Layer Cache | REQ-002 | +| REQ-004 | Policy Evaluation | Request validated, transformed, and gated by applicable policies | Contribute provider-specific policies | Manage Policy Engine; configure Policy Groups and Profiles | REQ-003 | +| REQ-005 | Placement Engine Execution | Resource placed with the best available provider instance | Implement capacity reserve_query response | Configure placement constraints; manage provider priorities | REQ-004 | +| REQ-006 | Requested State Persistence | Assembled payload stored as authoritative GitOps record | — | Configure Requested Store; manage storage redundancy | REQ-005 | +| REQ-007 | Provider Dispatch | Request payload delivered to selected provider | Implement Services API to receive DCM payloads | Configure API Gateway and egress | REQ-006 | +| REQ-008 | Request Status Tracking | Monitor request status from submitted through realized | Report realization status back to DCM | Configure observability for request tracking | REQ-007 | +| REQ-009 | Request Cancellation | Cancel a pending request before realization | Handle cancellation payloads | Configure cancellation policies | REQ-002 | +| REQ-010 | Git PR Request Ingress | Submit requests via Git Pull Request with policy dry-run feedback | — | Configure Git Request Watcher; manage repository structure | REQ-001, IAM-001 | + +--- + +## 4. Provider Contract and Realization + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| PRV-001 | Provider Registration | — | Register provider with DCM: declare type, capabilities, sovereignty, cost metadata | Configure Provider Registry; validate sovereignty declarations | IAM-001 | +| PRV-002 | Naturalization | — | Convert DCM unified payload to provider-native format | — | PRV-001, REQ-007 | +| PRV-003 | Realization | — | Execute required actions to provision/configure/change resource | — | PRV-002 | +| PRV-004 | Denaturalization | — | Convert provider-native result back to DCM unified format | — | PRV-003 | +| PRV-005 | Realized State Reporting | — | Report realized payload and status to DCM API Gateway | Configure Realized State Store; manage Event Stream | PRV-004 | +| PRV-006 | Capacity Reporting | — | Respond to reserve_query with current capacity and availability | Configure placement engine; manage capacity confidence | PRV-001 | +| PRV-007 | Provider Health Reporting | — | Expose health check endpoint; report availability | Monitor provider health; configure trust score updates | PRV-001 | +| PRV-008 | Sovereignty Declaration Maintenance | — | Notify DCM when sovereignty data changes within declared SLA | Monitor sovereignty changes; trigger re-evaluation | PRV-001 | +| PRV-009 | Compound Service Orchestration | — | Compose sub-providers to deliver higher-order services; manage composition visibility | Configure composite provider federation eligibility | PRV-001, PRV-003 | +| PRR-001 | OpenAPI Spec Declaration (GATE-SP-01) | — | Declare OpenAPI spec URL at registration; spec must be machine-readable and reachable | Validate spec URL reachability during approval pipeline | PRV-001 | +| PRR-002 | Healthy API at Activation (GATE-SP-02) | — | Health endpoint returns `{"status": "healthy"}` at activation time | Enforce health check as activation precondition | PRV-001, HLT-001 | +| PRR-003 | State Management Callback (GATE-SP-03) | — | Implement realized_state_push callback at all conformance levels | Validate callback endpoint reachability during approval | PRV-001 | +| PRR-004 | Tenant Metadata Endpoint (GATE-SP-04) | — | Implement GET /api/v1/tenants/{uuid}/metadata returning usage data | Require for standard+ profile activation; enforce quota integration | PRV-001 | +| PRR-005 | Prometheus Metrics (GATE-SP-05) | — | Expose required metric families at declared metrics_endpoint | Validate metric presence during approval; gate standard+ activation | PRV-001, HLT-005 | +| PRR-006 | AEP.DEV Linting (GATE-SP-06) | — | Pass AEP linter against OpenAPI spec with no errors before registration; include linting report URL | Gate standard+ activation on linting pass; block activation on errors | PRV-001 | + +| PRV-010 | Provider Sandbox/Test Mode | Submit test requests targeting sandbox providers via `_test_context.target_provider_uuid`; sandbox providers visible in registry with `status: sandbox` | Register with `sandbox_mode: true`; implement full OIS contract; graduate to production via standard approval | Manage sandbox provider registry; review graduation requests; sandbox providers excluded from production placement | PRV-001, GATE-SP-01 || PRR-007 | Multi-Tenant Dispatch (GATE-SP-07) | — | Accept tenant_uuid in all dispatch payloads; return tenant-scoped resources | Gate standard+ activation on multi-tenant compatibility test | PRV-001 | + +--- + +## 5. Resource Lifecycle Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| LCM-001 | Resource State Transitions | Trigger lifecycle actions: suspend, resume, decommission | Handle state transition payloads | Configure lifecycle policies; manage state machine | REQ-008 | +| LCM-002 | Post-Realization Field Updates | Update editable fields on realized resources (targeted delta) | Handle delta update payloads; apply partial changes | Configure editable field declarations; manage edit policies | PRV-005 | +| LCM-003 | Resource TTL Management | Declare and extend resource TTLs; receive expiry notifications | Handle TTL-triggered decommission payloads | Configure Lifecycle Constraint Enforcer; manage expiry policies | LCM-001 | +| LCM-004 | Ownership Transfer | Transfer resource ownership to a different Tenant | — | Authorize and execute ownership transfers; record transfer history | IAM-007, LCM-001 | +| LCM-005 | Rehydration | Replay a resource's intent state to a new provider or context | Receive and execute rehydration payloads | Manage rehydration leases; configure auth level requirements | REQ-002, PRV-003 | +| LCM-006 | Billing State Management | — | — | Configure billing state policies; integrate with Cost Analysis | LCM-001 | +| LCM-007 | Resource Decommission | Decommission resources individually or as part of group decommission | Handle decommission payloads; release resources | Manage decommission workflows; coordinate dependency teardown | LCM-001 | + +--- + +## 6. Drift Detection and Remediation + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| DRF-001 | Active Discovery | — | Expose discovery endpoint; respond to interrogation queries | Configure discovery schedules; manage Discovered Store | PRV-005 | +| DRF-002 | Drift Comparison | Receive drift notifications for owned resources | — | Configure drift detection policies; manage comparison logic | DRF-001, PRV-005 | +| DRF-003 | Drift Notification | Receive actionable drift alerts with field-level detail | — | Configure drift notification channels and escalation policies | DRF-002 | +| DRF-004 | Drift Remediation | Approve or reject automatic drift remediation | Execute remediation payloads | Configure remediation policies (revert/update/alert/escalate) | DRF-002, LCM-002 | +| DRF-005 | Unsanctioned Change Detection | Receive alerts on unauthorized resource modifications | Report all external state changes to DCM | Configure unsanctioned change policies | DRF-001 | + +--- + +## 7. Policy Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| POL-001 | Policy Authoring | — | Contribute provider-specific policy rules | Author and manage policies via API or GitOps ingress | IAM-003 | +| POL-002 | Policy Validation and Shadow Mode | View shadow evaluation results on own requests | — | Configure shadow mode; review shadow results | POL-001 | +| POL-003 | Policy Activation and Review | — | — | Manage policy review periods; authorize policy activation | POL-001, POL-002 | +| POL-004 | Policy Group Management | — | — | Compose Policy Groups; manage profile assignments | POL-003 | +| POL-005 | Profile Management | — | — | Configure deployment profiles; manage compliance domain groups | POL-004 | +| POL-006 | External Policy Evaluation | — | Register as external evaluation endpoint; implement BBQ-001–009 governance | Configure external evaluation trust levels; manage trust elevation | PRV-001, POL-001 | +| POL-007 | Policy Override and Constraint Visibility | View constraint details for service catalog fields | Declare constraint schemas on Resource Type Specs | Configure constraint visibility levels per profile | CAT-002, POL-003 | +| POL-008 | Constraint Type Registry | — | — | Register constraint types with OpenAPI v3 schemas; configure emittable_by/consumable_by; manage core and organization tiers | POL-001 | +| POL-009 | Evaluation Context and Multi-Pass Convergence | — | — | Configure max evaluation passes; monitor convergence; manage escalation for unresolvable conflicts | POL-001, POL-008 | +| POL-010 | Policy Templates | — | Contribute policy templates with parameterized Rego | Register templates (Gatekeeper ConstraintTemplate pattern); validate parameter schemas and constraint type references | POL-001, POL-008 | +| POL-011 | DCM Constraint Types Library | — | — | Manage auto-generated Rego library (data.dcm.constraint_types); sync with Constraint Type Registry | POL-008, POL-010 | +| POL-012 | Data-Driven Policy Matching | — | — | Configure match sources (request payload, operation context, evaluation context, entity metadata); validate match fields at activation | POL-001 | +| POL-013 | Lifecycle-Scoped Policy Evaluation | — | — | Configure lifecycle_scope per policy (which operation types trigger it); enforce profile minimums (fsi/sovereign require sovereignty policies on all operations); configure changed_field_filter for update/scale operations | POL-001, POL-012 | +| POL-014 | Override Policies | — | — | Author override policies targeting specific policies for defined scopes; enforce expiry and review dates; cannot target hard enforcement policies | POL-001, POL-003 | +| POL-015 | Exception Grants | — | — | Create time-bounded, scope-limited waivers with compensating controls; track usage count; enforce dual-approval for hard policies; manage renewal limits | POL-001, AUD-008 | +| POL-016 | Manual Override | Submit override request for blocked request with justification | — | Grant single-request overrides; enforce dual-approval for hard policies; manage override authority roles | POL-001, IAM-001 | +| POL-017 | Dual-Approval Escalation | — | — | Configure role separation requirements; manage approval workflow for hard policy overrides; enforce fsi/sovereign dual-approval on all overrides | POL-001 | +| POL-018 | Compensating Control Substitution | — | — | Define substitute control sets that satisfy policy intent through different mechanisms; require compliance officer validation | POL-001 | +| POL-019 | Override Approval Flow | Submit override request for blocked request; view pending overrides awaiting approval | — | Configure override notification routing (internal, webhook); configure timeout per profile; configure escalation rules; manage override authority roles | POL-016, IAM-001 | +| POL-020 | Override Notification Routing | — | — | Configure per-profile notification channels (LISTEN/NOTIFY, webhook to ServiceNow/Jira/Slack); configure routing by policy domain and enforcement level; configure escalation timeouts | POL-019 | +| POL-021 | Policy Block Resolution | View blocking details, resolution guidance with compliant values; choose resolution action (modify, request override, cancel, escalate) | — | Configure block timeout per profile; configure resolution guidance generation; manage escalation routing | POL-001 | + +--- + +## 8. Data Layer Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| LAY-001 | Core Layer Authoring | — | — | Author and manage Core and Organizational Layers in GitOps | IAM-003 | +| LAY-002 | Service Layer Contribution | — | Contribute Service Layers for offered resource types | Manage layer compatibility declarations | PRV-001, LAY-001 | +| LAY-003 | Layer Cache Management | — | — | Manage Layer Cache synchronization; handle cache invalidation | LAY-001, LAY-002 | +| LAY-004 | Layer Exclusion | Declare layer exclusions on specific requests | — | Configure which layers may be excluded; manage non-excludable declarations | REQ-003 | +| LAY-005 | Layer Versioning and Lifecycle | — | — | Manage layer versions; handle deprecation; enforce immutability | LAY-001 | + +--- + +## 9. Information and Data Integration + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| INF-001 | Information Provider Registration | — | Register Information Provider; declare authority scope and schema | Configure Information Provider Registry; manage authority layers | IAM-001 | +| INF-002 | Information Provider Push | — | Push field value updates to DCM; respond to conflict notifications | Configure ingestion pipeline; manage conflict resolution policies | INF-001 | +| INF-003 | Information Provider Pull / Discovery | — | Expose data query endpoint for DCM pull operations | Configure pull schedules; manage cache TTLs | INF-001 | +| INF-004 | Write-Back | — | Implement write-back endpoint to receive DCM-initiated updates | Configure write-back triggers via policy | INF-001, INF-002 | +| INF-005 | Confidence Score Visibility | View confidence bands on entity field values; query confidence aggregation API | — | Configure confidence scoring formula; manage trust score thresholds | INF-001 | +| INF-006 | Conflict Resolution Management | — | — | Review and resolve contested field values; manage conflict escalation | INF-002 | + +--- + +## 10. Ingestion and Brownfield Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| ING-001 | Resource Discovery and Ingestion | — | Expose discovery endpoints for brownfield resources | Configure ingestion pipeline; manage __transitional__ Tenant | DRF-001 | +| ING-002 | Ingested Entity Review | — | — | Review ingested entities; resolve conflicts; promote to active Tenants | ING-001 | +| ING-003 | Bulk Promotion | — | — | Execute bulk entity promotions with preview and rollback | ING-002 | +| ING-004 | Catalog Item Association | — | — | Associate ingested entities with Resource Type Specs; create catalog items | ING-002, CAT-001 | + +--- + +## 11. Audit and Compliance + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| AUD-001 | Audit Trail Access | Query audit records for own resources | — | Configure Audit Store; manage retention policies | IAM-003 | +| AUD-002 | Compliance Reporting | — | — | Generate compliance reports; manage report schedules | AUD-001 | +| AUD-003 | Merkle Tree Verification | — | — | Run inclusion proofs, consistency proofs, request chain verification; manage integrity incidents | AUD-001 | +| AUD-004 | Cross-DCM Audit Correlation | — | — | Correlate audit records across DCM instances via correlation_id; authorize cross-DCM pulls | AUD-001, DCM-001 | +| AUD-005 | Audit Record Retention Management | — | — | Configure reference-based retention; manage post-lifecycle retention | AUD-001 | +| AUD-006 | Audit Granularity Configuration | — | — | Configure granularity level per profile (stage, mutation, field); enforce minimum for fsi/sovereign | AUD-001 | +| AUD-007 | Signed Tree Heads | — | — | Configure STH interval; manage audit signing keys; publish STH for external verification | AUD-001 | +| AUD-008 | Payload Chain of Custody | View chain-of-custody proof for own requests | Verify dispatched payload matches DCM's signed output | Verify full pipeline integrity via request chain verification API | AUD-003 | +| AUD-009 | Inter-Stage Verification | — | — | Configure verification mode per profile (synchronous, asynchronous, disabled) | AUD-001 | + +--- + +## 12. Observability and Operations + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| OBS-001 | Operational Dashboard | View health and status of own resources | — | Configure and manage observability dashboard | — | +| OBS-002 | Metrics and Telemetry Export | — | Expose resource-level metrics to DCM | Configure observability export; integrate enterprise observability platform | — | +| OBS-003 | Curated Event Stream Subscription | Subscribe to observability event types via Message Bus | — | Configure event stream publication policies; manage subscriber roles | OBS-002 | +| OBS-004 | Alert and Notification Management | Receive resource and policy alerts via declared channels | — | Configure alert routing; manage notification channels and escalation | OBS-001 | + +| OBS-006 | SLA/SLO Declaration | View SLO status for owned resources (`GET /resources/{uuid}/slo-status`) | Declare resource_type SLOs in Resource Type Specification; report realization timing via callbacks | Configure SLO targets per resource type; view aggregate SLO performance report (`GET /admin/slo/report`) | RLM-001, LCM-001 | +| OBS-007 | SLO Breach Detection and Notification | Receive `slo.breach_approaching` and `slo.breach_detected` events | — | Configure SLO breach routing and escalation; review aggregate breach reports | OBS-006, EVT-001 || OBS-005 | Cost Analysis and Attribution | View cost estimates and actuals for owned resources | Provide cost metadata; report utilization | Configure Cost Analysis component; manage cost attribution policies | PRV-006 | + +--- + +## 13. Storage and State Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| STO-001 | Data Store Management | — | — | Configure PostgreSQL data domains (intent, requested, realized, discovered); manage schema, RLS, and tenant isolation | — | +| STO-002 | Realized State Management | — | — | Configure realized_entities table; manage version retention and is_current flag | PRV-005 | +| STO-003 | Discovered State Management | — | — | Configure discovered_records table; manage retention policies per profile | DRF-001 | +| STO-004 | Search and Query | Use entity and catalog search | — | Configure materialized views and indexes; manage cache refresh | STO-001 | +| STO-005 | Backup and Recovery | — | — | Configure PostgreSQL backup (PITR); test recovery; verify audit hash chain integrity | STO-001 | +| STO-006 | Provenance Model Configuration | — | — | Select and configure provenance model (full_inline / deduplicated / tiered); manage tier transitions | STO-001 | +| STO-007 | Sovereignty Partitioning | — | Declare sovereignty constraints at registration | Configure separate PostgreSQL instances per sovereignty zone; manage cross-zone prohibition | STO-001, GOV-001 | +| STO-008 | Tenant-Scoped Storage Isolation | Data is isolated by tenant via RLS | — | Configure RLS policies per table; enforce STI-001 through STI-004 | STO-001, IAM-001 | +| STO-009 | Tenant-Scoped Encryption (fsi/sovereign) | — | — | Configure per-tenant AES-256-GCM encryption via secrets management; manage key rotation | STO-008, CPX-001 | +| STO-010 | Internal Secrets Management | — | — | Configure envelope encryption (KEK source: env var, K8s secret, or HSM); manage secrets table; optional Vault external backend | STO-001 | +| STO-011 | Pipeline Event Routing | — | — | Configure LISTEN/NOTIFY for pipeline events; optional Kafka for high-throughput; manage consumption tracking | STO-001 | +| STO-012 | Internal Authentication | — | — | Configure local actor accounts (argon2id hashes, DCM-issued JWT); optional OIDC/SAML external auth_provider | STO-001, IAM-001 | + +--- + +## 14. DCM Federation and Multi-Instance + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| FED-001 | DCM Provider Registration | Submit requests that are routed to peer DCMs | Register as DCM Provider in peer instances | Configure DCM Provider registrations; manage federation trust | PRV-001, IAM-001 | +| FED-002 | Federation Routing | Requests automatically routed to appropriate Regional/Sovereign DCM | Respond to reserve queries from Hub DCM | Configure federation placement policies; manage sovereignty pre-filters | FED-001, REQ-005 | +| FED-003 | Federation Trust Management | — | — | Manage mTLS certificates; monitor federation trust scores; handle cert rotation | FED-001 | +| FED-004 | Cross-DCM Drift Detection | Receive drift alerts for federated resources | Publish Discovered State events to federation Message Bus | Configure federated drift detection; manage alert-and-hold policies | FED-001, DRF-002 | +| FED-005 | DCM Export and Import | — | — | Export and import DCM state packages; verify import trust scores | STO-001, STO-002 | + +--- + +## 15. Platform Governance and Administration + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| GOV-001 | Tenant Management | — | — | Create, configure, and decommission Tenants; manage compliance overlays | IAM-007 | +| GOV-002 | Group Management | — | — | Create and manage DCM Groups; configure sovereignty rules; manage time-bounded memberships | IAM-003 | +| GOV-003 | Registry Management | — | Register and maintain Resource Type Specifications in organization registry | Manage registry sync; configure registry policies; manage Tier 3 types | PRV-001 | +| GOV-004 | Resource Type Lifecycle | — | Manage deprecation notices; declare successor types; maintain migration guidance | Enforce deprecation timelines; manage sunset periods | GOV-003 | +| GOV-005 | Platform Configuration Management | — | — | Manage platform-wide layers; configure profiles; manage deployment manifest | LAY-001, POL-005 | +| GOV-006 | Bootstrap and Self-Hosting | — | — | Manage DCM self-deployment; verify bootstrap manifest; handle repave scenarios | STO-001 | + +| GOV-008 | Tenant Onboarding Workflow | Trigger onboarding completion: receive `tenant.onboarding_complete` when first entity OPERATIONAL | — | Execute full provisioning sequence: tenant entity, default groups, quota, admin actor, GitOps namespace, audit stream; dispatch `tenant.created` and member invitation events | IAM-001, STO-008 || GOV-007 | Sovereign Deployment Management | — | — | Manage air-gapped DCM instances; configure signed bundle import; manage offline registry | FED-001, STO-001 | + +--- + +## 16. Accreditation Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| ACC-001 | Accreditation Submission | — | Submit accreditation records (BAA, ISO 27001, FedRAMP, etc.) referencing external certificate evidence | Register accrediting bodies; configure minimum accreditation types per profile | PRV-001 | +| ACC-002 | Accreditation Review and Approval | — | Receive approval/rejection notification | Review submitted accreditations; verify certificate references; approve or reject via Admin API | ACC-001 | +| ACC-003 | Accreditation Lifecycle Monitoring | Receive notification when a provider's accreditation is nearing expiry or revoked | Renew accreditations before expiry; submit renewal documentation | Monitor expiry timelines; fire P90D renewal warnings; handle accreditation gaps | ACC-001 | +| ACC-004 | Accreditation Gap Response | Receive notification when a provider enters accreditation gap affecting owned resources | — | Configure Recovery Policy for accreditation gap events; manage affected entity remediation | ACC-003, POL-005 | +| ACC-005 | Data Classification Enforcement | Receive enforcement feedback when request payload contains data the selected provider cannot handle | Declare max_data_classification_accepted in capability registration | Configure classification immutability rules; manage phi/sovereign classification locks | ACC-001, PRV-001 | +| ACC-006 | DCM Deployment Accreditation | — | — | Register DCM deployment-level accreditations; expose to federation peers for trust verification | PRV-001, FED-001 | + +--- + +## 17. Zero Trust and Security Posture + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| ZTS-001 | Mutual TLS Enforcement | All interactions authenticated via mTLS at the client side | Present valid mTLS certificate on every interaction; rotate certificates on declared schedule | Configure trust anchors; manage CA chain; enforce mTLS at all interaction boundaries | IAM-001 | +| ZTS-002 | Scoped Interaction Credentials | Receive scoped short-lived credentials for authorized operations | Validate credential scope before executing operations; reject out-of-scope credentials | Configure credential lifetime per profile; manage credential issuance via secrets management | IAM-001, PRV-001 | +| ZTS-003 | Certificate Rotation Management | — | Implement certificate rotation before expiry; use transition window to avoid downtime | Monitor certificate expiry; fire P14D rotation warnings; manage P7D transition window | ZTS-001 | +| ZTS-004 | Zero Trust Posture Configuration | — | — | Configure zero_trust_posture per profile (none/boundary/full/hardware_attested); manage posture overrides | POL-005 | +| ZTS-005 | Hardware Attestation (Sovereign Profile) | — | Present hardware-attested identity (TPM/HSM) for sovereign profile interactions | Configure hardware attestation requirements; manage HSM integration; enforce for sovereign profile | ZTS-001, ZTS-002 | + +| ZTS-007 | Provider OpenAPI Spec Signing (SEC-001) | — | Sign OpenAPI spec with mTLS private key at registration; rejected at GATE-SP-01 if unsigned | Verify signature during registration approval pipeline | PRV-001, ZTS-001 | +| ZTS-008 | GitOps Secrets Scanning (SEC-002) | Commits with detected secrets rejected with `SECRETS_DETECTED` audit record | Ensure service layer SCM does not contain plaintext secrets | Configure scanning ruleset; review and remediate detected secrets | GOV-001, AUD-001 | +| ZTS-009 | Software Bill of Materials (SBOM) Declaration (SEC-003) | — | Declare SBOM reference at registration (mandatory for fsi/sovereign) | Enforce SBOM requirement during registration approval for fsi/sovereign profiles | PRV-001, ACR-001 || ZTS-006 | Five-Check Boundary Enforcement | — | Pass all five boundary checks on every interaction: identity → authorization → accreditation → matrix → sovereignty | Monitor boundary check audit records; respond to INTERACTION_DENIED events | ZTS-001, ACC-001, GMX-001 | + +--- + +## 18. Unified Governance Matrix + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| GMX-001 | Governance Matrix Rule Authoring | — | — | Author governance matrix rules in GitOps; declare match conditions across four axes (subject/data/target/context); declare field permissions | POL-001 | +| GMX-002 | Boundary Enforcement Evaluation | Receive DENY response with governing rule_uuid and human-readable reason when a request crosses a prohibited boundary | Receive field-stripped or redacted payloads when STRIP_FIELD/REDACT decisions apply | Monitor GMX evaluation audit records; respond to DENY events | GMX-001, ZTS-006 | +| GMX-003 | Field-Level Data Control | Receive request feedback when specific payload fields are stripped or redacted by active matrix rules | Receive filtered payloads; handle missing optional fields gracefully | Configure allowlist/blocklist field permissions per rule; manage STRIP_FIELD vs REDACT vs DENY_REQUEST escalation | GMX-001 | +| GMX-004 | Sovereignty Zone Management | — | Declare operating sovereignty zones in provider registration | Register sovereignty zones; declare jurisdictions, regulatory frameworks, inter-zone agreements | PRV-001, GMX-001 | +| GMX-005 | Compliance Domain Matrix Activation | — | — | Activate compliance domain matrix rules (HIPAA, GDPR, etc.) by enabling compliance domain in profile; rules apply automatically | POL-005, GMX-001 | +| GMX-006 | Tenant and Resource-Type Matrix Overrides | — | — | Declare Tenant-level and resource-type-level matrix rules that tighten (never relax) platform defaults | GMX-001, GOV-001 | +| GMX-007 | Matrix Rule Lifecycle Management | — | — | Manage governance matrix rule lifecycle (developing → proposed → active); use shadow mode for safe validation before activation | GMX-001, POL-002 | + +--- + +## 19. Drift Reconciliation + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| DRC-001 | Drift Record Production | Receive drift records with field-level detail: realized value, discovered value, field criticality, severity, unsanctioned flag | — | Configure Drift Reconciliation Component; manage comparison algorithm and severity thresholds | DRF-001, PRV-005 | +| DRC-002 | Unsanctioned Change Classification | Receive elevated-severity alert when change has no corresponding Requested State record | — | Configure unsanctioned change detection; manage severity escalation rules | DRC-001 | +| DRC-003 | Drift Severity Classification | Receive severity-classified drift records (minor/significant/critical) based on field criticality × change magnitude | — | Declare field criticality in Resource Type Specifications; configure magnitude thresholds per profile | DRC-001, GOV-003 | +| DRC-004 | Drift Resolution Tracking | View drift record status (open/acknowledged/resolved/escalated); receive resolved notification when next discovery confirms clean state | — | Monitor drift resolution rates; configure escalation policies for aged-open drift records | DRC-001, DRF-004 | +| DRC-005 | Governance Matrix Drift Integration | — | — | Configure governance matrix check in drift comparison pipeline: expected provider changes are not flagged as drift | DRC-001, GMX-001 | + + +## 20. Federated Contribution Model + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| FCM-001 | Consumer Policy Authoring | Author and submit tenant-domain policies (GateKeeper, Transformation, Recovery, Lifecycle, Orchestration Flow, Governance Matrix rules) via API or Flow GUI; receive PR URL and shadow mode results | — | Configure consumer policy authoring permissions (policy_author role); manage review requirements per profile | POL-001, IAM-003, IAM-007 | +| FCM-002 | Provider Resource Type Publication | — | Publish Resource Type Specifications and Catalog Items for offered resource types via provider contribution API; receive registry PR for platform admin review | Manage provider contribution registry; configure review requirements for provider specs; manage Organization-tier registry | PRV-001, GOV-003 | +| FCM-003 | Provider Service Layer Contribution | — | Contribute Service Layers for offered resource types; layers applied during request assembly for all consumers requesting that resource type | Review and activate provider-contributed layers; manage layer compatibility | PRV-001, LAY-002 | +| FCM-004 | Consumer Resource Group and Definition Contribution | Author and manage resource groups, notification subscriptions, webhook registrations, and cross-tenant authorization records within own Tenant | — | Configure contribution permissions per role; manage Tenant-scoped artifact lifecycle | IAM-007, GOV-002 | +| FCM-005 | Federation Contribution (Peer DCM) | — | Peer DCM contributes registry entries, policy templates, and service layers via federation channels | Manage federation contribution trust posture (verified/vouched/provisional); configure review requirements per trust posture; manage cross-DCM artifact lifecycle | FED-001, GOV-003, POL-003 | +| FCM-006 | Contribution Review and Lifecycle | View contribution status (proposed, pending_review, active, withdrawn); withdraw a pending contribution; receive notification when contribution is approved or rejected | Receive notification when provider contributions are reviewed | Review and approve/reject contributions via Admin API; manage shadow review periods; assign new owners to orphaned artifacts | POL-002, POL-003 | +| FCM-007 | Contributor Scope Enforcement | Receive clear DENY response when attempting to contribute outside permitted domain scope | Receive DENY when contributing specs for resource types not offered | Monitor Governance Matrix enforcement at contribution time; configure scope violation audit and notification | GMX-001, GMX-002 | + +--- + + +## 21. Scoring Model + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| SMX-001 | Operational GateKeeper Scoring | Receive risk score and score_drivers with request acknowledgment; understand why score is at its level | Declare `enforcement_class: operational` and `scoring_weight` on contributed GateKeeper policies | Configure operational GateKeeper policies with appropriate weights; manage per-policy enforcement class | POL-001, REQ-004 | +| SMX-002 | Advisory Validation and Completeness Score | Receive advisory_warnings list with request acknowledgment; understand what optional improvements exist | Declare `output_class: advisory` on advisory Validation policies | Configure advisory Validation policies; manage completeness score thresholds | POL-001, REQ-004 | +| SMX-003 | Actor Risk History Tracking | View own risk history score and contributing events via Consumer API | — | Monitor actor risk history; reset scores for trusted automation accounts; configure decay parameters | AUD-001, IAM-001 | +| SMX-004 | Quota Pressure Scoring | Receive quota_pressure as a score driver when approaching Tenant quota limits | — | Configure per-resource-type quota limits; manage free_threshold parameter | IAM-007, REQ-004 | +| SMX-005 | Provider Accreditation Richness Scoring | — | Benefit from lower risk contribution by maintaining rich accreditation portfolio | Configure accreditation richness weights; manage portfolio scoring | ACC-001, PRV-001 | +| SMX-006 | Profile Scoring Threshold Management | — | — | Configure approval routing thresholds per profile (auto/reviewed/verified/authorized + custom tiers via named-tier list); manage signal weights; enforce SMX-008 (max auto_approve_below: 50) | POL-005, REQ-004 | +| SMX-007 | Policy Enforcement Class Override | — | Contribute policies with declared enforcement_class; receive notification when profile overrides enforcement class | Declare per-profile enforcement class overrides; manage regulatory_mandate flag to protect compliance-class policies from demotion | POL-004, POL-005 | +| SMX-008 | Score Audit Trail | Query risk score and routing decision for own requests; view score_drivers and advisory_warnings | — | Query full Score Record detail including signal breakdown and actor risk history; manage score audit retention | AUD-001, REQ-004 | +| SMX-009 | Scoring Weight Range Enforcement | — | Declare operational GateKeeper scoring_weight between 1 and 100 | Enforce weight range at policy activation; reject out-of-range weights | SMX-001 | +| SMX-010 | Score Breakdown Audit Inclusion | View score breakdown in request audit record | — | Configure score breakdown storage in Audit Store for all scored requests | SMX-001, AUD-001 | + +--- + + +## 22. Compound Service Composability + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| MPX-001 | Compound Service Request | Request a compound service as a single catalog item; receive composite entity UUID; track compound execution status via standard request status endpoint | Register as Compound Service with constituent specification; implement compound dispatch endpoint | Configure Compound Service registration; manage composite service catalog items | CAT-001, REQ-007, PRV-001 | +| MPX-002 | Dependency-Ordered Constituent Execution | — | Execute constituents in declared dependency order; manage parallel rounds; respect depends_on declarations | Configure composition model; monitor execution round progress via status events | MPX-001, PRV-003 | +| MPX-003 | Partial Delivery and DEGRADED State | Receive DEGRADED composite entity when partial delivery is accepted; choose to accept or reject degraded state | Declare partial_delivery_supported and required_for_delivery per constituent; return DEGRADED compound payload | Configure accept_degraded_delivery per profile; manage degraded notification urgency | MPX-001, PRV-005 | +| MPX-004 | Compound Compensation | Receive notification and recovery decision when compound service fails; approve or reject compensation | Implement compensation in dependency-reverse order; guarantee idempotent decommission calls | Configure compensation timeout; manage PARTIALLY_COMPENSATED orphan detection | MPX-001, LCM-007, DRC-001 | +| MPX-005 | Transparent Constituent Visibility | Query and manage DCM-visible constituent entities independently (when transparency mode); receive constituent-level drift alerts | Declare composition_visibility mode; register transparent constituents with deterministic UUIDs | Configure visibility mode per compound resource type; manage constituent entity lifecycle policies | MPX-001, DRF-001 | +| MPX-006 | Compound Execution Status Tracking | Monitor compound execution round progress via request status; see component-level status during long-running compositions | Send intermediate status events to DCM during execution; declare status_reporting.interval | Monitor compound execution health; configure execution timeout alerts | MPX-001, REQ-008 | +| MPX-007 | Nested Compound Service Composition | Request high-order compound services composed of other compound services (max depth 3) | Implement as a Compound Service that calls other Compound Services as constituents; declare max_nesting_depth | Configure nesting depth limits; manage nested compensation chains | MPX-001, PRV-009 | +| MPX-008 | Compound Service Nesting Depth Enforcement | — | Declare nesting depth in Compound Service registration | Enforce maximum nesting depth of 3 at placement time; reject deeper compositions | MPX-001 | + +--- + + +## 23. Credential Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| CPX-001 | Resource Credential Issuance | Receive credential metadata and retrieval URL with realized resource; retrieve credential value via authenticated endpoint | Declare credential requirements in Resource Type Spec; receive credential issuance confirmation | Register service_provider with Credential.* resource types; configure credential types and lifetimes per resource type; manage issuance policies | PRV-005, ZTS-002 | +| CPX-002 | DCM Interaction Credential Issuance | — | Validate scoped interaction credential on every DCM dispatch; reject interactions without valid scoped credential (CPX-002) | Configure interaction credential lifetime per profile; manage secrets table for DCM-internal credentials | ZTS-002, PRV-001 | +| CPX-003 | Credential Rotation | Receive rotation notification before old credential expires; retrieve new credential during transition window | Implement rotate endpoint; honor transition window; notify DCM when rotation is complete | Configure rotation schedules and transition windows per credential type; manage pre-expiry rotation warnings | CPX-001, IAM-001 | +| CPX-004 | Emergency Rotation and Security Event Response | Receive immediate notification on emergency rotation; retrieve new credential via fastest channel | Implement immediate revocation with no transition window on security_event trigger | Configure security event triggers; manage emergency rotation audit trail; notify platform admin | CPX-003, OBS-004 | +| CPX-005 | Credential Revocation | Receive revocation notification when credentials are revoked (actor deprovisioned, entity decommissioned); confirm transition to new credential | Implement revoke endpoint with declared SLA; invalidate value immediately on emergency revocation | Manage Credential Revocation Registry; configure revocation cache TTL per profile (PT1M standard, PT30S fsi/sovereign); enforce CPX-007 (decommission blocks on credential revocation) | CPX-001, LCM-007 | +| CPX-006 | Revocation Propagation | — | Refresh revocation cache within profile-governed TTL; validate credential UUID against cache at use time (not only at receipt) | Configure revocation cache TTL; monitor revocation propagation latency; alert on SLA violations | CPX-005, IAM-001 | +| CPX-007 | Audit Trail for Credential Lifecycle | View own credential record history (issue, rotate, revoke events); every value retrieval audited with retrieval_uuid | — | Query full credential audit trail including retrieval count; manage credential audit retention | CPX-001, AUD-001 | +| CPX-008 | IP-Bound Credentials for fsi/sovereign | — | — | Enforce IP binding (bound_to_ip) on all credentials issued for fsi and sovereign profiles | CPX-001, ZTS-002 | +| CPX-009 | Algorithm and Key Usage Declaration | — | Declare algorithm and key_usage on credential records at issuance | Enforce declaration at issuance; reject credentials without declared algorithm | CPX-001 | +| CPX-010 | Idle Credential Detection | Receive notification when credential reaches idle threshold | — | Configure idle_detection_threshold per profile; enforce alert-only action | CPX-001 | +| CPX-011 | Compliance Domain Additive Credential Requirements | — | — | Enforce additive credential requirements when compliance domains are active on a profile | CPX-001, POL-005 | +| CPX-012 | Credential Value Store Isolation (All Profiles) | — | — | Enforce credential values never stored in DCM stores in ALL profiles including minimal | CPX-001 | + +--- + + +## 24. Authority Tier Model + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| ATM-001 | Authority Tier Registry | Reference approval decisions by tier name; tier weight resolved dynamically from ordered list | Implement approval workflows that reference tier names (not hardcoded weights) | Manage the ordered authority tier list; control tier positions and gravity values | POL-005 | +| ATM-002 | Custom Tier Definition | — | — | Contribute custom tiers between existing tiers; declare decision_gravity and dcm_gate semantics; requires verified-tier approval | ATM-001, FCM-001 | +| ATM-003 | Dynamic Threshold Configuration | View which tier an action routes to | — | Configure profile approval_routing as named-tier threshold list; adjust score ranges when new tiers inserted | ATM-001, SMX-001 | +| ATM-004 | Tier Registry Change Impact Detection | Receive notification when tier changes affect owned resources or pending approvals | — | Propose tier registry changes; receive tier impact diff report; review SECURITY_DEGRADATION and BROKEN_REFERENCE items; accept degradations via Admin API | ATM-001, AUD-001 | +| ATM-005 | Degradation Review Gate | — | — | Review and explicitly accept each SECURITY_DEGRADATION item before a registry change activates; provide compensating control rationale; must hold verified or authorized tier reviewer role | ATM-004, IAM-001 | +| ATM-006 | Profile Gap Detection | — | — | Receive PROFILE_GAP warnings when tier registry changes leave profile threshold lists incomplete; update threshold lists or acknowledge gap within approval window | ATM-003, ATM-004 | +| ATM-007 | Tier Registry Audit Trail | Query historical tier registry versions and impact reports | — | Access full audit trail of all tier registry changes: proposal, impact assessment, degradation acceptances, activation | ATM-004, AUD-001 | +| ATM-008 | Approval Record Tier Weight Snapshot | — | — | Store tier name and resolved weight at approval record creation; historical records retain weight for audit comparison across regime changes | ATM-001 | +| ATM-009 | Tier Registry Degradation Gate | Receive notification when tier change produces degradation affecting owned resources | — | Block tier registry activation on SECURITY_DEGRADATION items; require verified-tier acceptance per item | ATM-001 | +| ATM-010 | Broken Reference Gate | — | — | Block tier registry activation when BROKEN_REFERENCE items exist (removed tier still referenced in active config) | ATM-001 | +| ATM-011 | Tier Change Impact Report | — | — | Generate and store tier impact report in Audit Store at proposal and activation time | ATM-001, AUD-001 | +| ATM-012 | Profile Gap Warning on Tier Insertion | — | — | Detect PROFILE_GAP when new tier inserted but profile threshold list not updated; emit non-blocking warning | ATM-001, POL-005 | + +--- + + +## 25. Event Catalog + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| EVT-001 | Event Subscription | Subscribe to DCM events via notification service_provider or Message Bus; filter by event type, entity type, urgency; idempotency via event_uuid | Publish standard events when provider actions occur; use reverse-DNS prefix for non-standard events | Configure notification service_provider channels and audience routing | IAM-001, OBS-001 | +| EVT-002 | Request Pipeline Events | Receive real-time status of own requests (submitted → intent_captured → policies_evaluated → requires_approval → approved → dispatched → realized/failed) | — | Configure request event delivery per profile; manage urgency routing | REQ-001 | +| EVT-003 | Entity Lifecycle Events | Receive entity lifecycle events (realized, state_changed, ttl_warning, decommissioning, etc.) for owned entities and entities with stakes | — | Configure entity event delivery; manage stakeholder audience routing | LCM-001 | +| EVT-004 | Security and Critical Events | Receive critical security events (audit chain alerts, sovereignty violations, unsanctioned provider writes) regardless of subscription preferences | — | Configure non-suppressable event delivery; manage security team routing | AUD-001, ZTS-001 | +| EVT-005 | Approval Pipeline Events | Receive approval events (requires_approval, decision_recorded, quorum_reached, window_expiring, expired) for own requests and approvals | — | Configure reviewer notification routing; manage approval window alerts | ATM-001, IAM-001 | +| EVT-006 | Provider and Infrastructure Events | — | Publish provider health events (registered, healthy, unhealthy, degraded); publish provider_update events on entity changes | Monitor provider health events; configure provider degradation alerts | PRV-001 | +| EVT-007 | Tier Registry and Governance Events | — | — | Receive tier_registry events (proposed, impact_assessed, degradation_detected, activated); configure governance event routing | ATM-004 | + +--- + + +## 26. API Versioning + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| VER-001 | Version Discovery | Discover available API versions and their status via `GET /.well-known/dcm-api-versions`; learn current, supported, and deprecated versions; get changelog and migration guide URLs | Declare supported OIS version in capability registration | Monitor version adoption; manage sunset schedules | — | +| VER-002 | Breaking Change Governance | Receive at least the profile-governed deprecation notice period before a breaking change takes effect; continue using deprecated versions until sunset date | Receive OIS version deprecation notice; migrate to new OIS version before sunset | Declare new major versions; configure deprecation timeline per profile; ensure VER-002 (breaking change definition) is applied | VER-001 | +| VER-003 | Deprecation Headers | Receive `Deprecation`, `Sunset`, and `Link` headers on all responses from deprecated API versions (RFC 8594/RFC 9745); use these to drive migration priority | — | Configure header injection for deprecated versions; ensure headers are accurate | VER-002 | +| VER-004 | Migration Guide | Access machine-readable migration guide at `GET /api/v{N}/migration-guide`; understand all breaking changes from previous version with migration instructions | Access OIS migration guide at `GET /provider/api/v{N}/migration-guide` | Maintain migration guides for all new major versions (required by VER-008) | VER-002 | +| VER-005 | Preview Endpoints | Access preview endpoints at `/api/v{N}/preview/`; understand stability commitment is none; provide feedback before graduation | — | Mark endpoints as preview; graduate to stable in new major version | VER-001 | +| VER-006 | Latest Alias Production Warning | — | — | Support `latest` version alias; emit response header discouraging production use | VER-001 | +| VER-007 | Preview Endpoint Instability Declaration | — | — | Mark preview endpoints explicitly; may change or be removed without major version bump | VER-001 | +| VER-009 | Provider Dispatch Compatibility | — | Maintain backward compatibility with DCM dispatch payloads from supported prior versions | Manage provider dispatch versioning; maintain supported version matrix | VER-001 | + +--- + + +## 27. Session Revocation + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| SES-001 | Session Lifecycle Management | View own active sessions; logout single session (DELETE /api/v1/auth/session); logout all sessions; revoke specific session by UUID | — | Force revoke sessions for any actor; view session store health | IAM-001, AUTH-001 | +| SES-002 | Actor Deprovisioning Session Revocation | — | — | Parallel session + credential revocation on actor deprovisioning; deprovisioning not acknowledged until both complete (AUTH-016) | SES-001, CPX-005 | +| SES-003 | Emergency Session Revocation | Receive critical notification on security-event session revocation | — | Trigger emergency revocation (security_event); revocation propagates within profile SLA (PT5S sovereign to PT30S standard) | SES-001, EVT-001 | +| SES-004 | Token Introspection | — | Call POST /api/v1/auth/introspect to validate bearer tokens without maintaining own revocation cache | Configure introspection endpoint access; manage introspection scope grants | SES-001, IAM-001 | +| SES-005 | Concurrent Session Enforcement | Oldest session auto-revoked when new session exceeds concurrent limit; receive notification via notification service_provider | — | Configure concurrent_sessions limit per profile; monitor session counts | SES-001 | +| AUTH-017 | Session Revocation Propagation SLA | — | — | Propagate revocation to Session Revocation Registry within profile SLA (PT5S sovereign → PT30S standard) | SES-001 | +| AUTH-018 | Per-Request Revocation Registry Check | — | Check Session Revocation Registry on each request bearing a bearer token | Configure revocation registry query path; manage registry availability | SES-001 | +| AUTH-019 | Emergency Revocation No-Grace Period | Receive critical notification on emergency revocation | — | Emergency session revocation fires immediately with no grace period | SES-003 | +| AUTH-020 | Introspection Endpoint Authentication | — | Authenticate introspection calls using provider interaction credential | Configure introspection scope grants; manage endpoint access | SES-004 | +| AUTH-021 | Oldest Session Revocation on Limit | Receive notification when oldest session is auto-revoked | — | Configure concurrent_sessions limit; enforce oldest-first revocation order | SES-005 | +| AUTH-022 | Refresh Token Invalidation on Session Revoke | — | — | Invalidate refresh token when parent session is revoked; return REVOKED_SESSION error on use | SES-001 | + +--- + +## 28. Internal Component Authentication + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| ICOM-001 | Component Identity and mTLS | — | — | Manage Internal CA; issue and rotate component certificates; all inter-component calls use mTLS (ICOM-001) | ZTS-001, CPX-001 | +| ICOM-002 | Component Bootstrap | — | — | Generate one-time bootstrap tokens (PT1H max lifetime); components acquire first certificate via bootstrap token; token invalidated after single use (ICOM-007) | ICOM-001 | +| ICOM-003 | Internal Call Authorization | — | — | Declare allowed_sources per internal endpoint; declare allowed_targets per component; unauthorized source calls rejected with ICOM_UNAUTHORIZED_SOURCE audit record (urgency: high) | ICOM-001, AUD-001 | +| ICOM-004 | Internal Interaction Credentials | — | — | Every internal call presents a scoped ZTS-002 interaction credential in addition to mTLS; credential scoped to specific operation and target component | ICOM-001, CPX-002, ZTS-002 | +| ICOM-005 | Component Certificate Revocation | — | — | Compromised component certificates added to Internal CA CRL immediately; CRL cache refresh within profile SLA (PT15S sovereign to PT1M standard); ICOM_CERT_COMPROMISED audit record (urgency: critical) | ICOM-001, AUD-001 | +| ICOM-006 | Component Certificate Maximum Validity | — | — | Issue internal component certificates with maximum validity P90D; enforce expiry and rotation | ICOM-001 | +| ICOM-008 | Compromised Certificate Immediate CRL | — | — | Add compromised internal component certificates to Internal CA CRL immediately; propagate within PT60S | ICOM-001 | +| ICOM-009 | Trust Anchor Registration | — | — | Register root or intermediate CA as trust anchor for internal mTLS; reject certificates not chaining to registered trust anchor | ICOM-001 | + +--- + + +## 29. Scheduled and Deferred Requests + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| SCH-001 | Request Scheduling | Submit requests with schedule.dispatch: at/window/recurring; SCHEDULED requests visible in GET /api/v1/requests; cancellable before dispatch; receive request.scheduled event | — | Manage Maintenance Windows; configure Request Scheduler; monitor scheduled queue depth | REQ-001 | +| SCH-002 | Maintenance Windows | Reference maintenance windows in scheduled requests; view available windows at GET /api/v1/maintenance-windows | — | Create/manage/suspend maintenance windows; approve window schedules; configure platform-wide windows | SCH-001, GOV-001 | +| SCH-003 | Dual Policy Evaluation | — | — | Understand that scheduled requests run GateKeeper at declaration AND at dispatch; dispatch-time failure → FAILED with schedule_policy_rejection (SCH-003) | SCH-001, POL-001 | +| SCH-004 | Deadline Enforcement | Set not_after on scheduled requests; receive request.failed(schedule_deadline_missed) if deadline passes without dispatch | — | Monitor deadline miss rates; configure alerting on deadline misses | SCH-001, EVT-001 | +| SCH-005 | Not-After Expiry Failure | Receive FAILED status when scheduled request expires before dispatch | — | Configure not_after enforcement; manage SCHEDULE_EXPIRED recovery policy | SCH-001 | +| SCH-006 | Maintenance Window Platform Authorization | — | — | Require platform_admin or tenant_admin authority to create/modify Maintenance Windows | SCH-004 | + +--- + +## 30. Request Dependency Graph + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| RDG-001 | Dependency Group Submission | Submit POST /api/v1/request-groups with requests and depends_on declarations; local refs within submission; receive group_uuid and per-request entity_uuids | — | Monitor group queue depth; configure max group size | REQ-001 | +| RDG-002 | Field Injection | Declare inject_fields to pass realized output fields (e.g. IP address) from dependency into dependent request fields automatically at dispatch time | — | — | RDG-001, REQ-001 | +| RDG-003 | PENDING_DEPENDENCY Status | Track dependent requests in PENDING_DEPENDENCY status; cancel pending requests individually or cancel whole group; receive request.pending_dependency and request.dependency_met events | — | Monitor PENDING_DEPENDENCY queue depth; detect stalled groups | RDG-001, EVT-001 | +| RDG-004 | Group Failure Handling | Configure on_failure: cancel_remaining or continue; group-level timeout; group status via GET /api/v1/request-groups/{uuid} | — | Monitor group failure rates | RDG-001 | +| RDG-005 | Group-Level Timeout Enforcement | Receive TIMEOUT failure when group-level timeout elapses | — | Configure group_timeout independent of individual request timeouts | RDG-001 | +| RDG-006 | Single Group Membership Enforcement | — | — | Reject attempts to add a request to more than one dependency group | RDG-001 | + +--- + +## 31. DCM Self-Health + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| HLT-001 | Liveness Probe | — | — | GET /livez: fast liveness check (PT5S max, no external calls); Kubernetes restarts pod on failure; unauthenticated | — | +| HLT-002 | Readiness Probe | — | — | GET /readyz: checks Session Store, Audit Store, Policy Engine, Message Bus, Auth Provider connectivity; Kubernetes removes from LB on failure; startup sequence observable via readyz | — | +| HLT-003 | Component Health Detail | — | — | GET /api/v1/admin/health: per-component status (pass/warn/fail), metrics, queue depths, provider/auth summary; admin auth required | IAM-001 | +| HLT-004 | Prometheus Metrics | — | — | GET /metrics: Prometheus scrape endpoint; request pipeline, policy, session, drift, provider, internal CA metrics | OBS-001 | +| HLT-005 | Prometheus Metrics Endpoint | — | — | Expose Prometheus-compatible metrics at GET /metrics including request throughput, store latency, policy eval time, provider health counters | HLT-001 | +| HLT-006 | Startup Readiness via /readyz | — | — | Report PASSING on /readyz only after all required stores available and bootstrap complete | HLT-001 | + +--- + + +## 32. Operational Reference + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| OPS-001 | Data Store Partitioning | — | — | Declare partitioning strategy in deployment manifest; execute tenant-shard, per-tenant, or time-based archiving migration; configure shard routing and mirror lag monitoring | STO-001 | +| OPS-002 | Store Migration | — | — | Execute dual-write migration between store implementations; maintain audit chain continuity across cutover; enforce profile-governed burn-in before source decommission | STO-001, AUD-001 | +| OPS-003 | Disaster Recovery | — | — | Execute scenario-specific recovery procedures (component/store/full-CP/repave); meet profile-governed RTOs (PT1M–PT15M component, PT5M–PT2H store, PT5M–PT30M full-CP); complete post-recovery validation checklist | HLT-001, AUD-001 | +| OPS-004 | Backup Management | — | — | Configure PostgreSQL backup schedules (PITR for all data domains); enforce P365D minimum audit retention | STO-001 | + +--- + + +## 33. Web Interfaces + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| GUI-001 | Consumer Portal — Catalog, Requests, Resources | Browse catalog; live cost estimate; submit requests with scheduling and dependency groups; live SSE status with constituent tracking; cross-resource drift report; consumer-scoped audit trail with correlation ID trace | — | — | CAT-001, REQ-001, SCH-001, RDG-001, EVT-002 | +| GUI-002 | Consumer Portal — Live Request Status | Real-time status via SSE stream (status_change, progress_updated, approval events, heartbeat); constituent status for compound requests; approval flow inline; fallback to polling | — | — | REQ-001, EVT-002 | +| GUI-003 | Consumer Portal — Resource Management | View/filter owned resources by state and type; lifecycle state badges; drift indicator; resource detail with Overview/Drift/Audit/Cost/Credentials/Relationships/Groups tabs; state-sensitive action buttons; bulk operations | — | — | LCM-001, DRF-001, AUD-001 | +| GUI-004 | Consumer Portal — Session and Security | View active sessions; revoke individual or all other sessions; step-up MFA prompt for gated operations; tenant context selector; role-gated navigation (hide not disable) | — | — | SES-001, IAM-001 | +| GUI-005 | Admin Panel — Platform Dashboard | Control plane component health grid; provider health summary; pending approvals count; open drift records by severity; request throughput; all driven by GET /api/v1/admin/health | — | Platform Admins, SREs configure dashboard widgets; role-gated sections | HLT-003 | +| GUI-006 | Admin Panel — Governance and Approvals | Approval queue (all tenants); approval detail with risk score breakdown; authority tier registry editor (drag-and-drop reordering, impact report visualization, degradation acceptance flow); scoring threshold editor (auto_approve_below ≤ 50 hard-stop) | — | Policy Owners and Platform Admins | ATM-004, SMX-001 | +| GUI-007 | Admin Panel — Audit and Compliance | Platform-wide cross-tenant audit trail; pre-built compliance reports (SOC 2, FedRAMP, HIPAA); audit chain integrity status; correlation ID trace; session and security event feed | — | Auditors, Security team, Platform Admins | AUD-001, SES-003 | +| GUI-008 | Provider Management — Common Shell | Overview, configuration, health history, audit trail, and notification tabs for all 11 provider types; provider owner role gates access; Platform Admins see all providers | — | Provider owners manage own providers; Platform Admins manage all | PRV-001, IAM-001 | +| GUI-009 | Provider Management — Type Extensions | Service Provider: capacity, managed entities, naturalization mapping, realization history; secrets management: inventory, rotation, revocation, external CA config, algorithm compliance; Auth Provider: session stats, SCIM sync, connection status; external policy evaluation: trust level, contribution pipeline | — | Provider owners access type-specific tabs for their provider type | GUI-008, PRV-001 | +| GUI-011 | RHDH Plugin Suite | Use DCM capabilities within Red Hat Developer Hub (RHDH) or Backstage via Dynamic Plugins (@dcm/backstage-plugin-*); no RHDH rebuild required for updates | — | Configure RHDH app-config.yaml with DCM connection; configure Dynamic Plugin loading | +| GUI-012 | Scaffolder Template Auto-Generation | DCM catalog items automatically generate Backstage Software Templates; new resource types appear as templates without UI code; field schema → JSON Schema → Scaffolder form | — | Configure @dcm/backstage-plugin-catalog-backend; template generation is automatic | +| GUI-013 | DCM Entity Provider | DCMService (catalog items) and DCMResource (realized entities) appear in RHDH Software Catalog; entities sync every PT5M; search-indexed; tenancy enforced via namespace | — | Service account credential configuration; sync interval configuration | +| GUI-014 | ITSM Integration Bridge | View ITSM references (ServiceNow, Jira) on resource entity Overview tab; link change records to DCM requests; see ITSM-sourced approval votes in request status; CMDB reference on entity pages | ITSM systems receive DCM lifecycle events via notification service_provider and call Admin API to record approval votes; CMDB sync via webhook subscription | Configure ITSM notification service_provider; map DCM event → ITSM action; configure CMDB field mapping | EVT-001, GUI-002 | +| GUI-010 | Unified Shell | Single DCM web application with role-gated surfaces: Consumer Portal (all actors), Admin Panel (platform roles), Provider Management (provider_owner role), Flow GUI link (policy_owner/sre); one login, one session; no separate applications | — | Platform Admins configure which surfaces are available | IAM-001, SES-001 | + +--- + + +## 34. ITSM Integration + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| ITSM-001 | ITSM integration Registration | — | Register as ITSM integration with declared capabilities (supported_actions, itsm_system, field_mapping_ref, cmdb_ci_type_map); implement standard OIS health check | Register ITSM integrations; review and approve ITSM integration registrations; configure inbound webhook authentication | PRV-001, CPX-001 | +| ITSM-002 | Outbound ITSM Record Creation | View ITSM references on resource entities (change request, incident, CMDB CI links with deep links to ITSM system) | Receive action requests from DCM; create/update records in ITSM system; return record ID for storage on entity | Configure ITSM Policies (create_change_request, create_incident, update_cmdb_ci); configure block_until_created for compliance gates | ITSM-001, POL-001 | +| ITSM-003 | Inbound ITSM Approval Routing | — | Verify HMAC signature on inbound webhook; forward ITSM approval decisions to DCM Admin API approval vote endpoint | Configure inbound webhook secret (secrets management); monitor approval routing from ITSM systems (ServiceNow CAB, Jira workflow) | ITSM-001, CPX-001, ATM-001 | +| ITSM-004 | ITSM Policy Authoring | — | — | Author ITSM Action policies with template expressions; configure shadow validation; configure on_failure behavior; use block_until_created for pipeline gates (with mandatory timeout per ITSM-005) | ITSM-001, POL-001 | +| ITSM-005 | CMDB Synchronization | View CMDB CI reference on resource entities; CI auto-created on realization, auto-retired on decommission | Receive create_cmdb_ci and retire_cmdb_ci actions; maintain dcm_entity_uuid correlation on CMDB CI records | Configure CMDB CI type mapping per resource type; monitor CMDB sync failures | ITSM-001, ITSM-002 | +| ITSM-006 | ITSM Field Mapping Declaration | — | — | Declare field mappings between DCM entity fields and ITSM CI types in ITSM integration config; validate against Resource Type Specs | ITSM-001 | +| ITSM-007 | ITSM Policy Template Expression Validation | — | — | Validate template expressions in ITSM Policy action_payload at policy activation; reject unresolvable expressions | ITSM-001, POL-003 | + +--- + + +## 35. Provider Callback Authentication + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------| +| PCA-001 | Two-Layer Provider Callback Authentication | — | Present valid mTLS certificate (Layer 1) and provider callback credential (Layer 2) on every call to DCM callback endpoints | Configure DCM CA trust anchor; issue provider callback credentials at activation; enforce both layers | PRV-001, ZTS-001 | +| PCA-002 | Provider Callback Credential Scope Enforcement | — | Use callback credential scoped to own provider_uuid only; cannot act on other providers | Enforce credential scope at validation; reject cross-provider credential use | PRV-001 | +| PCA-003 | Entity-Level Callback Authorization | — | Receive 403 ENTITY_NOT_OWNED_BY_PROVIDER when pushing state for entities not dispatched to this provider | Enforce per-call entity ownership check independent of credential validity | PRV-001, REQ-007 | +| PCA-004 | Scope Violation Auto-Suspension | Receive critical notification when owned provider is suspended due to scope violations | — | Auto-suspend provider and notify platform admin after 5 consecutive scope violations within PT1H | PRV-001, ZTS-001 | +| PCA-005 | Callback Credential Issued by secrets management | — | Retrieve callback credential via secrets management at activation; not directly from API Gateway | Issue callback credentials exclusively through secrets management; reject direct credential issuance requests | PRV-001, CPX-001 | +| PCA-006 | Registration Token Single-Use Enforcement | — | Use registration token for initial registration only; obtain callback credential after activation | Invalidate registration token after first successful use regardless of expiry timestamp | PRV-001 | +| PCA-007 | Sovereignty Change Re-Registration | — | Submit new registration with new registration token when sovereignty declaration changes | Require new registration token and approval pipeline for sovereignty declaration changes | PRV-001, GMX-004 | +| PCA-008 | Callback Credential Pre-Expiry Rotation | — | Implement credential refresh; receive new credential before old expires during transition window | Initiate rotation before expiry; maintain transition window (50% of credential lifetime) | PRV-001, CPX-001 | +| PCA-009 | IP-Bound Callback Credentials for fsi/sovereign | — | Present callback calls from declared bound_to_ip address for fsi/sovereign profiles | Enforce IP binding on callback credentials for fsi and sovereign profiles | PCA-001, CPX-008 | +| PCA-010 | All Inbound Provider Calls Produce Audit Records | — | — | Write audit record for every inbound provider call including rejected calls; no silent failures | PCA-001, AUD-001 | + +--- + + +## 36. Workload Analysis + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------|| WLA-001 | Automated Workload Classification | View workload profile for owned resources; see archetype, resource type match, confidence | Report resource metadata via discovery for classification input | Configure classification ruleset version; review low-confidence classifications manually | DRC-001, INF-001 | +| WLA-002 | Migration Readiness Scoring | View containerization score and migration blockers for owned resources | Report workload characteristics that inform migration scoring | Configure migration readiness thresholds; integrate MTA Information Provider | WLA-001 | +| WLA-003 | MTA Information Provider Integration | — | Implement workload_analysis information type OR delegate to MTA | Register MTA as Information Provider; configure analysis trigger policies | WLA-001, INF-001 | +| WLA-004 | On-Demand Re-Analysis | Request re-analysis when resource role changes (`POST /resources/{uuid}/workload-profile:analyze`) | — | Trigger re-analysis for any resource; override archetype manually with reason | WLA-001 | +| WLA-005 | WorkloadProfile Audit Chain | View analysis history for owned resources | — | Query full analysis history including superseded profiles | WLA-001, AUD-001 | +| WLA-006 | Low-Confidence Manual Review Gate | Receive notification when owned resource requires manual classification | — | Review and resolve low-confidence classifications; unblock ingestion | WLA-001, LCM-001 | + +--- + + +## 37. Accreditation Monitoring + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------|| ACM-001 | Tier 1 External Registry Verification | — | Declare `external_registry_id` at registration for FedRAMP, CMMC, ISO 27001 | Configure registry poll intervals; review status change alerts; manage `external_registry_id` accuracy | PRV-001, ACR-001 | +| ACM-002 | Tier 2 Document Currency Verification | — | Maintain current `certificate_ref` and `audit_report_ref` URLs pointing to valid, accessible documents | Configure `max_age` per framework; review document expiry alerts; upload new reports when notified | PRV-001, ACR-001 | +| ACM-003 | Tier 3 Contract Webhook Integration | — | Configure contract management webhook for BAA and DoD IL accreditations | Register contract system; configure inbound webhook credential; receive BAA/contract lifecycle events | PRV-001, ACR-001 | +| ACM-004 | Verification Staleness Enforcement | — | Ensure monitoring infrastructure can reach DCM to deliver verification events | Configure stale_after thresholds and stale_action per profile; enforce sovereign/fsi minimum tier requirements | ACM-001, ACR-001 | +| ACM-005 | Immediate Revocation on External Revoke | Receive notification when provider accreditation is revoked; understand service impact | — | Review `accreditation.status_changed` events; confirm immediate revocations; trigger recovery policy | ACM-001, ACR-001 | +| ACM-006 | Verification Currency in Scoring | — | Maintain verification currency to maximize accreditation richness score | Monitor verification multiplier impact on provider placement; prioritize externally verified providers | ACM-001, SMX-001 | +| ACM-007 | Manual Override in Air-Gapped Mode | — | — | Manually update `last_verified_at` with justification in air-gapped deployments; maintain audit trail of manual verifications | ACR-001, AUD-001 | + +--- + + +## 38. Location Topology Management + +| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On | +|----|-----------|---------|---------|---------------|-----------|| LOC-001 | Location Type Registry | Browse available location types (standard and custom) | Declare supported locations in provider registration capability declaration | Register custom location types; manage standard type definitions; deprecate types | PRV-001 | +| LOC-002 | Location Node Management | Browse available locations via `GET /api/v1/locations`; filter by resource type, data classification, sovereignty zone | Declare which location nodes (DC, Zone, etc.) the provider serves at registration | Create, version, and retire location layer instances via GitOps; update mutable capacity fields | LOC-001 | +| LOC-003 | Location Selection at Request Time | Submit `location_uuid` or `location_handle` with service request; select at any level (Country through Rack); DCM refines to specific DC at placement | — | Configure default location selection rules; enforce location-based placement policies | LOC-001, LOC-002 | +| LOC-004 | Location Layer Assembly | Transparent — full location context injected into payload automatically | Receive full location context in dispatch payload (location.country_code, location.zone_code, location.dc_code, etc.) | Configure layer assembly order; define location-level field overrides | LOC-002, DLM-001 | +| LOC-005 | Location-Based Sovereignty Enforcement | See sovereignty zone and data residency on each location node | Declare sovereignty capabilities per served location | Configure `max_data_classification` per location; enforce cross-border policies at location layer | LOC-002, GOV-001 | +| LOC-006 | Location Hierarchy Navigation | Browse parent/child location relationships; query ancestors of a selected node | — | Manage location hierarchy; validate acyclicity on layer submission | LOC-001, LOC-002 | +| LOC-007 | Custom Location Types | Use custom location types in selection (e.g., Fleet/Ship in Navy context) | Declare support for resources at custom location types | Register and manage custom types; define level insertion point in hierarchy | LOC-001 | +| LOC-008 | Location Capacity Visibility | See `capacity_status` (available/limited/full) and `providers_available` count per location | Report capacity scoped to location during reserve query | Update mutable capacity fields (e.g., rack_units_available) without a new layer version | LOC-002, PRV-001 | + +--- + + + + +## 39. Subscription Management (SUB) + +| ID | Capability | Consumer | Provider | Platform/Admin | Dependencies | +|----|-----------|----------|----------|----------------|-------------| +| SUB-001 | Create subscription | Submit subscription request with tier and terms | Acknowledge subscription, provision initial entities | Approve subscription creation | CAT-001, REQ-001, POL-001 | +| SUB-002 | Manage subscription lifecycle | Suspend, resume, cancel own subscriptions | Report managed entity status | Force-cancel, view all subscriptions | LCM-001, AUD-001 | +| SUB-003 | Change subscription tier | Request tier upgrade/downgrade | Adjust capacity for managed entities | Approve tier changes requiring admin | POL-001, POL-003 | +| SUB-004 | Subscription renewal | Approve/decline renewal; view renewal status | Honor renewed terms | Configure renewal policies | SCH-001, POL-001 | +| SUB-005 | Provider-originated updates | Approve/reject non-auto updates | Submit updates via standard callback | View update audit trail | PRV-003, AUD-001 | +| SUB-006 | Update channel management | Configure auto_apply per channel | Declare available update channels | Set organization-wide channel policies | POL-001, POL-003 | +| SUB-007 | Subscription cost attribution | View subscription cost | Declare cost model per tier | Configure cost allocation rules | — | +| SUB-008 | Entitlement enforcement | View remaining capacity | Stay within entitlement bounds | Override entitlements in exceptional cases | POL-002 | +| SUB-009 | Grace period management | View grace period; request extension | Continue health reporting during grace | Configure grace period defaults | LCM-001 | +| SUB-010 | Subscription audit trail | View own subscription audit history | — | View all subscription audit records | AUD-001, AUD-002 | + +## Capability Count Summary + +| Domain | Capabilities | +|--------|-------------| +| Identity and Access Management | 21 | +| Service Catalog | 7 | +| Request Lifecycle Management | 10 | +| Provider Contract and Realization | 16 | +| Resource Lifecycle Management | 7 | +| Drift Detection and Remediation | 5 | +| Policy Management | 7 | +| Data Layer Management | 5 | +| Information and Data Integration | 6 | +| Ingestion and Brownfield Management | 4 | +| Audit and Compliance | 5 | +| Observability and Operations | 6 | +| Storage and State Management | 8 | +| DCM Federation and Multi-Instance | 5 | +| Platform Governance and Administration | 7 | +| Accreditation Management | 6 | +| Zero Trust and Security Posture | 8 | +| Unified Governance Matrix | 7 | +| Drift Reconciliation | 5 | +| Federated Contribution Model | 7 | +| Scoring Model | 10 | +| Compound Service Composability | 8 | +| secrets management Model | 12 | +| Authority Tier Model | 12 | +| Event Catalog | 7 | +| API Versioning | 8 | +| Session Revocation | 11 | +| Internal Component Authentication | 8 | +| Scheduled and Deferred Requests | 6 | +| Request Dependency Graph | 6 | +| DCM Self-Health | 6 | +| Operational Reference | 4 | +| Web Interfaces | 14 | +| ITSM Integration | 7 | +| Provider Callback Authentication | 10 | +| Workload Analysis | 5 | +| Accreditation Monitoring | 6 | +| Location Topology Management | 7 | +| Subscription Management | 10 | +| **Total** | **309** | +--- + +## Dependency Map — Critical Path Capabilities + +These capabilities block the most downstream work and should be implemented first: + +``` +IAM-001 (Actor Authentication) + └── IAM-002 (Session Tokens) → IAM-003 (RBAC) → IAM-007 (Tenant Scope) + └── CAT-001 (Service Catalog) + └── REQ-001 (Submit Request) + └── REQ-002 (Intent State) → REQ-003 (Layer Assembly) + └── REQ-004 (Policy Evaluation) → REQ-005 (Placement) + └── REQ-007 (Provider Dispatch) + └── PRV-003 (Realization) → PRV-005 (Realized State) + └── LCM-001 (State Transitions) + └── DRF-001 (Discovery) → DRF-002 (Drift) + +PRV-001 (Provider Registration) — parallel critical path + └── PRV-002 (Naturalization) → PRV-003 (Realization) + └── PRV-006 (Capacity Reporting) → REQ-005 (Placement) +``` + +**Minimum viable DCM capability set (to demonstrate end-to-end lifecycle):** + +IAM-001 → IAM-002 → IAM-003 → IAM-007 → CAT-001 → REQ-001 → REQ-002 → REQ-003 → REQ-004 → REQ-005 → REQ-006 → REQ-007 → PRV-001 → PRV-002 → PRV-003 → PRV-004 → PRV-005 → LCM-001 → DRF-001 → DRF-002 → AUD-001 + +**21 capabilities for a functional end-to-end demonstration.** + +**Note:** FCM-001 through FCM-007 (Federated Contribution Model) are not on the critical path — they extend DCM's multi-user capabilities but are not required for the initial end-to-end lifecycle demonstration. + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* diff --git a/content/docs/DISCUSSION-TOPICS.md b/content/docs/DISCUSSION-TOPICS.md new file mode 100644 index 0000000..8829094 --- /dev/null +++ b/content/docs/DISCUSSION-TOPICS.md @@ -0,0 +1,614 @@ +# DCM — Discussion Topics + +> **Status Update (2026-03):** The DCM architecture has been substantially completed. All original 19 architecture review items, 10 session-added items, and 21 community questions have been resolved. The items below represent topics for ongoing community discussion and future evolution. +> +> **Current state:** 0 unresolved architectural questions · 331 capabilities across 39 domains · 58 data model documents · 15 specifications · 4 OpenAPI schemas · 6 JSON schemas · 5 provider types · 2 policy evaluation modes · 9 control plane services +> +> **Foundation:** Three abstractions (Data · Provider · Policy) · Unified Governance Matrix · Federated Contribution Model · Full OPA/Rego validation complete + +## Open Community Discussion Topics + +### 1. Kubernetes / CNCF Strategy (community decisions) +- CNCF submission scope: Operator Interface Spec as specification project first; DCM project after Level 2 reference implementation +- SIG App Delivery and SIG Cluster Lifecycle engagement before Sandbox submission +- Named adopters and TOC sponsor: project team action items +- Level 2 conformance scope now formally defined + +### 2. Implementation Decisions (engineering decisions) +- KubeVirt reference implementation timeline → team estimates against defined Level 2 scope +- SDK language support beyond Go → community SDKs encouraged; Go SDK is reference implementation +- Non-Kubernetes container runtime support → implementation detail + +### 3. Future Evolution Topics +- Normative data specifications (JSON Schema / OpenAPI) — the code-generation layer +- AI/ML Provider type — as DCM becomes AI-ready, a dedicated ML workload provider type +- Billing Provider type — deeper integration with enterprise billing and showback systems +- CMDB Provider type — dedicated contract for CMDB integration +- Multi-cloud federation model — extending DCM federation to public cloud providers +- GitOps PR UX improvements — better tooling for policy review workflow + + +### 5. Kessel Integration (pre-implementation evaluation) +- Evaluation document written: `44-kessel-integration-evaluation.md` +- **Action required:** Discussion with Kessel development team to validate assumptions before any implementation work begins +- Key questions: API stability, sovereign/air-gapped deployment model, SpiceDB schema extensibility, resource type registry extensibility, HA/DR patterns +- **Do not implement until alignment confirmed** — document is for discussion only +- Two integration paths evaluated: Kessel Relations as Auth Provider (checks 1-2 of five-check model), Kessel Inventory as Discovered State data store +- 10 blocking items identified in doc 44 Section 10 + +### 4. Governance Questions +- Community governance model — how will the DCM project make decisions once public? +- Certified Profile Program — self-certified vs project-reviewed for compliance profiles +- Registry tier promotion — Tier 3 (Organization) to Tier 2 (Verified Community) pathway + +--- + +*See [00-foundations.md](data-model/00-foundations.md) for the three-abstraction model that resolved the major architectural questions.* + + +--- + +**Original discussion topics (archived):** + +# DCM — Topics for Discussion and Modification + +**Document Status:** 🔄 Active +**Purpose:** A living document capturing topics that require further discussion, design decisions that need revisiting, and new capabilities to be incorporated into the DCM architecture and data model. +**Process:** When a topic is resolved, move it to the appropriate architecture or data model document and mark it resolved here with a reference to where it was documented. + +--- + +## How to Use This Document + +- **Add** any topic that surfaces during design, review, or implementation that needs a decision or deeper discussion +- **Tag** each item with its area, priority, and status +- **Resolve** items by documenting the decision in the appropriate document and updating the status here +- **Never delete** resolved items — keep the full history for audit and traceability + +--- + +## Status Key + +| Status | Meaning | +|--------|---------| +| 🔴 Blocking | Must be resolved before dependent work can proceed | +| 🟡 Active | Under active discussion | +| 🟢 Resolved | Decision made — documented in referenced document | +| ⚪ Parked | Acknowledged but deferred — revisit later | + +--- + +## Priority Key + +| Priority | Meaning | +|----------|---------| +| P1 | Critical — affects foundational architecture | +| P2 | High — affects multiple components or documents | +| P3 | Medium — affects a specific component or document | +| P4 | Low — enhancement or refinement | + +--- + +## Open Topics + +--- + +### TOPIC-001 — Webhook Integration + +**Area:** Control Plane, Provider Contract, Egress +**Priority:** P2 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Notification Model (doc 23) supersedes standalone webhooks. Webhooks are one delivery channel within the notification model. Full event taxonomy in doc 33 (Event Catalog). See AI Prompt Sections 14, 25, 48. +**Raised:** 2026-03 + +#### Description + +DCM needs a webhook integration model that allows external systems to be notified of DCM events and state changes in real time. Webhooks are a standard integration pattern that complements the existing API-first model and Message Bus — they enable a push-based notification model for consumers, providers, and external systems that cannot or do not poll DCM. + +#### Use Cases + +**Consumer Notifications** +- Notify a consumer's CI/CD pipeline when a resource request transitions to REALIZED state +- Notify an application team when their Resource/Service Entity enters DEGRADED state +- Notify a Tenant owner when an ownership transfer is initiated or completed +- Notify consumers when a dependency graph node fails during realization + +**Provider Notifications** +- Notify a provider when a new request payload is dispatched to them +- Notify a provider when DCM initiates a discovery request +- Notify a provider when a decommission is requested for one of their entities + +**External System Integration** +- Notify an external ITSM system (ServiceNow, Jira) when a request is created, updated, or completed +- Notify a monitoring system when Entity lifecycle state changes +- Notify a FinOps platform when new Resource/Service Entities are realized or decommissioned +- Notify a compliance system when GateKeeper policies fire or sovereignty constraints are applied + +**Operational Notifications** +- Notify SRE teams when provider capacity falls below threshold +- Notify security teams when unsanctioned changes are detected +- Notify auditors when specific policy types are triggered + +#### Design Questions to Resolve + +1. **Webhook registration model** — how do consumers, providers, and external systems register webhooks with DCM? Is registration via the Consumer API, Provider Registration, or a dedicated Webhook API? + +2. **Event taxonomy** — what is the full list of events DCM can emit via webhook? Should this be an extensible registry similar to the Resource Type Registry? + +3. **Payload format** — should webhook payloads use the DCM unified data model format, or a simplified event notification format? Should the full state payload be included or just a reference + event type? + +4. **Authentication and security** — how does DCM authenticate outbound webhook calls? Options include: HMAC signatures, OAuth tokens, mTLS, API keys. How does the receiving system verify the webhook is genuinely from DCM? + +5. **Retry and reliability** — what is DCM's obligation if a webhook delivery fails? Should DCM retry? How many times? With what backoff strategy? What happens if a webhook endpoint is consistently unavailable? + +6. **Ordering guarantees** — are webhook events delivered in order? What happens if events arrive out of order at the receiving end? + +7. **Filtering** — can webhook registrations declare filters — only receive events of specific types, for specific Resource Types, for specific Tenants, or for specific Resource Groups? + +8. **Policy Engine integration** — should GateKeeper and other policy types be able to trigger webhook notifications as a policy action? This would make webhooks a first-class policy response alongside ALERT, REVERT, etc. + +9. **Provider webhook obligations** — should providers be required to support webhook endpoints as part of their Provider Contract? Or is webhook support optional for providers? + +10. **Tenant scoping** — should webhook registrations be scoped to a Tenant, meaning a webhook can only receive events for resources owned by the registering Tenant? Or should there be platform-level webhooks that span Tenants (for SRE/Audit personas)? + +11. **Webhook versioning** — as DCM evolves, webhook payload schemas will change. How are webhook payload versions managed? Should webhook registrations declare which payload schema version they expect? + +12. **Relationship to Message Bus** — DCM already has a Message Bus component. What is the distinction between webhook integration and Message Bus integration? Are webhooks the outbound consumer-facing layer on top of the Message Bus? + +#### Initial Design Thoughts + +Webhooks fit naturally as an **Egress capability** — they are outbound notifications from DCM to external systems, which is consistent with the existing Egress zone in the architecture (Messaging Protocol, Interoperability API). + +The webhook registration model should likely be part of the **Consumer API** for consumer-facing webhooks and part of the **Provider Registration** for provider-facing webhooks. + +Webhook events should be **typed and versioned** — consistent with DCM's universal versioning model. An event type like `entity.state.changed` should have a version, and webhook registrations should declare which version they support. + +Webhook payloads should carry **provenance information** — the event payload should include enough context to trace back to the originating request, entity, and policy that caused the event. This is consistent with DCM's auditability requirements. + +**Policy Engine integration** is particularly interesting — if the Policy Engine can fire webhooks as a response action, it enables real-time governance notifications without requiring consumers to poll DCM. This aligns with the DCM goal of getting actionable information to the right people as fast as possible. + +#### References +- [Resource/Service Entities](data-model/06-resource-service-entities.md) — provider lifecycle events +- [Service Dependencies](data-model/07-service-dependencies.md) — dependency failure notifications +- Architecture: Egress zone, Message Bus, API Gateway + +--- + +### TOPIC-002 — Intent Store and Intent Payload Structure + +**Area:** Data Model +**Priority:** P1 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Intent State fully specified in doc 02 (Four States), doc 04 (Examples), consumer-api-spec. Resource type referenced by FQN or UUID (both accepted). See AI Prompt Sections 4, 5, 80. +**Raised:** 2026-03 + +#### Description + +The Intent State is captured when a consumer submits a request but the exact structure of the Intent payload has not been formally specified. The Intent payload is the consumer's raw declared desire — what they asked for before any processing, enrichment, or policy application. It needs a formal definition that is consistent with the four-state model and the layering model. + +#### Questions to Resolve + +1. What fields are required in an Intent payload vs optional? +2. How does the Intent payload reference a Resource Type — by UUID, by fully qualified name, or both? +3. How does the Intent payload declare its Tenant membership? +4. How does the Intent payload declare dependency requirements at the intent level? +5. How does the Intent payload declare group memberships? +6. How is the Intent payload versioned — does it carry a version or is it always a snapshot? + +#### References +- [Context and Purpose](data-model/00-context-and-purpose.md) — four states +- [Layering and Versioning](data-model/03-layering-and-versioning.md) — Request Layer +- [Resource Grouping](data-model/08-resource-grouping.md) — Tenant and group membership + +--- + +### TOPIC-003 — GateKeeper vs Validation Policy Distinction + +**Area:** Policy Engine +**Priority:** P2 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** GateKeeper has enforcement_class (compliance=boolean deny, operational=risk score). Validation has output_class (structural=boolean fail, advisory=completeness score). GateKeeper can both block AND modify. See doc 29 (Scoring Model), AI Prompt Sections 17, 61. +**Raised:** 2026-03 + +#### Description + +The distinction between GateKeeper and Validation policy categories needs better examples and a clearer formal definition. Both involve checking data against rules, but GateKeeper has override authority while Validation is pass/fail only. The boundary between them needs to be unambiguous. + +#### Questions to Resolve + +1. What is the precise trigger condition that makes a policy a GateKeeper vs a Validation policy? +2. Can a GateKeeper policy both block AND modify in the same execution? +3. Are there cases where Validation and GateKeeper would produce different outcomes for the same rule? +4. Should GateKeeper policies require explicit authorization (e.g., only CISO-owned policies can be GateKeeper)? + +#### References +- [Layering and Versioning](data-model/03-layering-and-versioning.md) — Policy Layer section + +--- + +### TOPIC-004 — Audit vs Observability Component Separation + +**Area:** Control Plane +**Priority:** P3 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Definitively separate (AUD-013). Opposite trade-offs: audit is 100% accuracy, append-only, 7+ year retention; observability is statistical sampling, downsampled, operational window. Cannot be combined. See doc 16, AI Prompt Section 37.3. +**Raised:** 2026-03 + +#### Description + +The original architecture documents noted that Audit and Observability were separated for a reason that was later forgotten. This needs to be formally resolved — are they truly separate components with distinct responsibilities, or should they be merged? + +#### Initial Thinking + +**Audit** — focused on compliance evidence and transaction traceability. Reads provenance data intrinsic to data objects. Produces compliance reports, audit trails, and interrogation capability for Auditors, Security teams, and SRE personas. Historical record oriented. + +**Observability** — focused on operational visibility — metrics, health, performance, real-time monitoring. Consumes provider lifecycle events, Entity state changes, and system health data. Operational present-state oriented. + +These are likely genuinely separate concerns. Audit is about what happened and why. Observability is about what is happening now. + +#### Questions to Resolve + +1. Are Audit and Observability separate Atomic Components with separate APIs? +2. Do they share a data store or maintain separate stores? +3. How do provider lifecycle events flow to both components? + +--- + +### TOPIC-005 — Message Bus Consumer Ingress Question + +**Area:** Control Plane, Consumer Ingress +**Priority:** P3 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Message Bus supports bidirectional integration. Inbound messages processed as authenticated API calls via registered webhook actor identity. Same Policy Engine evaluation as any API call. See doc 18, AI Prompt Section 25. +**Raised:** 2026-03 + +#### Description + +The original architecture documents raised an unresolved question: should the Message Bus be offered as consumer ingress (inbound) in addition to egress (outbound)? The API-first principle suggests it should be egress only, but there are valid integration scenarios where external systems need to push data into DCM asynchronously. + +#### Questions to Resolve + +1. Should consumers be able to submit requests via the Message Bus, or only via the Consumer API? +2. If Message Bus ingress is supported, how are requests authenticated and authorized? +3. How does Message Bus ingress interact with the Intent State capture — is the message treated as an Intent payload? + +--- + +### TOPIC-006 — Cache Architecture + +**Area:** Data Model, Control Plane +**Priority:** P2 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Hybrid push-pull model. Caches placed closest to consumption, subject to sovereignty. GitOps and Event Streams always authoritative; caches are derived projections. Sovereign DCM uses signed bundles only. See CACHE-001 through CACHE-004, AI Prompt Section 40.2-40.4. +**Raised:** 2026-03 + +#### Description + +Several unresolved questions exist about where data caches live, how they are synchronized, and which cache is authoritative when caches diverge. This has implications for distributed DCM deployments and sovereignty scenarios. + +#### Questions to Resolve + +1. Where should data caches live? Hub DCM? Regional DCM? Sovereign DCM? All locations? +2. Should cache synchronization be push, pull, or both? +3. Which cache is authoritative when caches diverge? +4. What mechanism maintains consistency across distributed caches? +5. How do cache architecture decisions interact with sovereignty constraints — can cached data cross sovereignty boundaries? + +--- + +### TOPIC-007 — Cross-Tenant Dependencies + +**Area:** Data Model, Multi-Tenancy +**Priority:** P2 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Relationship nature governs cross-tenant: constituent=never, operational=dual authorization, informational=unless deny_all. explicit_only is default stance. cross_tenant_authorization DCMGroup required. See doc 09, REL-010/011/012, XTA-001-005, AI Prompt Sections 11.13-11.14, 21.6. +**Raised:** 2026-03 + +#### Description + +The dependency model currently assumes dependencies are resolved within a single Tenant. However, real-world deployments will have cross-tenant dependencies — a Payments Tenant application that depends on a shared DNS service owned by a Platform Tenant. The data model needs to formally address how cross-tenant dependencies are declared, resolved, and governed. + +#### Questions to Resolve + +1. How is a cross-tenant dependency declared — does it reference the Tenant UUID of the dependency owner? +2. What authorization is required for a cross-tenant dependency? Does the owning Tenant need to approve? +3. How does cost attribution work for cross-tenant service consumption? +4. How does drift detection work when a dependency is in another Tenant? +5. Can a GateKeeper policy block cross-tenant dependencies? + +--- + +### TOPIC-008 — Provider Trust Validation Mechanism + +**Area:** Service Providers, Provider Contract +**Priority:** P1 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Registration spec defines full approval pipeline (auto/reviewed/verified/authorized). Provider Callback Auth (doc 43, PCA-001-010) specifies two-layer mTLS + credential model. Trust levels: trusted/verified/untrusted. Accreditation model (doc 26) governs ongoing trust. See AI Prompt Sections 53.2, 76. +**Raised:** 2026-03 + +#### Description + +The Provider Contract includes a Trust Contract — providers must be validated and certified to participate in the DCM ecosystem. The mechanism for establishing, maintaining, and revoking trust has not been designed. + +#### Questions to Resolve + +1. What is the certification process for a new provider? +2. What technical mechanism validates trust at request time? +3. How is trust revoked if a provider violates their contract? +4. Should trust be per-provider or per-catalog-item? +5. How does the trust chain interact with sovereignty requirements? + +--- + +### TOPIC-009 — Physical Shared Infrastructure Tenancy + +**Area:** Data Model, Tenancy +**Priority:** P2 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** Three ownership patterns: Whole Allocation (indivisible, provider retains), Allocation (pool yields sub-resources), Shareable (one resource, multiple stakeholders). Physical infrastructure owned by __platform__ Tenant. See doc 04b, OWN-001 through OWN-008, AI Prompt Sections 8.3, 46.2. +**Raised:** 2026-03 + +#### Description + +The current model assigns every Resource/Service Entity to exactly one DCM Tenant. This works cleanly for most cases but edge cases exist for truly shared physical infrastructure — a rack, a network switch, a power circuit — where the ownership model may be genuinely joint or ambiguous. + +#### Questions to Resolve + +1. Is jointly-owned physical infrastructure a valid DCM use case, or is it always owned by a Platform/Infrastructure Tenant? +2. If joint ownership is valid, how is it modeled without breaking the single-Tenant rule? +3. Does the Whole Allocation model cover all physical infrastructure scenarios? + +--- + +### TOPIC-010 — Embedded Technology-Specific Data Bundles + +**Area:** Data Model +**Priority:** P3 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** native_passthrough field sanctioned for genuinely untranslatable provider-specific data. Always audit-logged (content if transparent, hash if opaque). Opaque passthrough blocked in fsi/sovereign by default. See DATA-001, AI Prompt Section 40.5. +**Raised:** 2026-03 + +#### Description + +The original data model discussion raised the question of whether the data model should allow embedded target-technology-specific data bundles — for example, a Terraform HCL block or an Ansible vars file embedded directly in an entity definition. The authors noted "not convinced this is something we want." + +#### Questions to Resolve + +1. Should technology-specific data bundles be allowed in entity definitions? +2. If allowed, must they be in clear text and appropriate for Git storage? +3. How would embedded bundles interact with the portability model — they would clearly be portability-breaking? +4. Is there a better mechanism — such as a provider-specific extension field with an appropriate portability classification? + +--- + +## Resolved Topics + +--- + +### TOPIC-R001 — Field Override Control Mechanism + +**Area:** Data Model, Policy Engine, Layering +**Priority:** P1 +**Status:** 🟢 Resolved +**Raised:** 2026-03 +**Resolved:** 2026-03 + +#### Decision + +Field override control is implemented as a **standard Policy Engine mechanism** using a graduated three-level model: Level 1 (no declaration — fully overridable), Level 2 (simple `override: allow|constrained|immutable`), Level 3 (full actor matrix with per-actor permissions, trusted grants, and expansion rules). The Policy Engine is the sole authority for setting override control. The Request Payload Processor enforces structural layer rules only. + +#### Documented In +- [Data Layers and Assembly Process](data-model/03-layering-and-versioning.md) — Section 5a +- [Context and Purpose](data-model/00-context-and-purpose.md) — Section 4.4 + +--- + +### TOPIC-R002 — Storage/Networking Bundling vs Dependency Model (Q53) + +**Area:** Data Model, Entity Relationships, Service Dependencies +**Priority:** P1 +**Status:** 🟢 Resolved +**Raised:** 2026-03 +**Resolved:** 2026-03 + +#### Decision + +The conflict between the enhancement documents (storage bundled in VM schema) and the data model (separate first-class entities) is resolved through a **universal Entity Relationship model**: + +- Bundled consumer declarations are expanded by the Request Payload Processor into first-class Resource/Service Entities with their own UUIDs +- The relationship between parent and child entities is expressed using the universal relationship model — bidirectional, UUID-keyed, with lifecycle policies +- The same relationship model is used for ALL entity relationships — compute to storage, application to web server, resource to business unit — minimizing variance +- Lifecycle policies (destroy|retain|detach|notify on parent destroy/suspend/modify) replace the ephemeral/persistent classification +- Expansion rules live in the Resource Type Specification — portable and declarative +- The dependency graph concept is unified into the Entity Relationship Graph + +#### Documented In +- [Entity Relationships](data-model/09-entity-relationships.md) — complete relationship model +- [Information Providers](data-model/10-information-providers.md) — external data relationships +- [Service Dependencies](data-model/07-service-dependencies.md) — updated to reference entity relationships + +--- + +### TOPIC-R003 — Information Provider Model + +**Area:** Data Model, Provider Contract +**Priority:** P1 +**Status:** 🟢 Resolved +**Raised:** 2026-03 +**Resolved:** 2026-03 + +#### Decision + +Information Providers are a first-class provider type in DCM. They follow the same registration, health check, trust, and contract model as Service Providers where applicable. Key decisions: + +- Information types live in the same DCM registry as Resource Types — distinguished by category prefix (Business.*, Identity.*, Compliance.*, Operations.*) +- Standard vs extended data — DCM only relies on standard fields for operational decisions; organizations can extend with domain-specific fields +- Stable external key model — DCM UUID wraps external UUID; if external system changes its UUID, only the reference record changes +- Three-mode verification — scheduled (Mode 1), provider push (Mode 2, contractual obligation), on-demand (Mode 3, fallback) +- Internal business data follows the standard resource entity model when DCM owns it; external references use the Information Provider model + +#### Documented In +- [Information Providers](data-model/10-information-providers.md) +- [Entity Relationships](data-model/09-entity-relationships.md) — external relationship structure +- [Resource Type Hierarchy](data-model/05-resource-type-hierarchy.md) — information type categories added + +--- + +--- + +### TOPIC-011 — Enhancement Document Compatibility Findings + +**Area:** Data Model, Provider Contract, Policy Engine, Catalog +**Priority:** P1 +**Status:** 🟢 Resolved +**Resolved:** 2026-03 +**Resolution:** All cross-cutting gaps resolved: field-level provenance (doc 00, Section 4.3), UUIDs (universal identity requirement), portability classification (doc 05), sovereignty declarations (doc A provider contract), tenancy (doc 15 universal groups), versioning (semver throughout), deprecation (five-status lifecycle). Enhancement docs superseded by current architecture. +**Raised:** 2026-03 + +#### Description + +A compatibility review of six DCM enhancement documents against the data model identified areas of strong alignment and cross-cutting gaps that need resolution before the enhancements can be considered fully aligned with the data model. + +#### Cross-Cutting Gaps (affect all or most enhancements) + +1. **Field-Level Provenance** — absent in all enhancement documents. The Policy Engine spec mutates request payloads without recording provenance. The KubeVirt SP returns minimal status rather than full DCM-format realized payloads. + +2. **UUIDs as Primary Identifiers** — most specs use `name` as the natural key rather than UUID as the primary key. The data model requires UUIDs as primary identifiers on all entities. + +3. **Portability Classification** — absent from all provider and catalog specs. Every field must declare `universal|conditional|provider-specific|exclusive` classification. + +4. **Sovereignty Declarations** — entirely absent from SP Registration and KubeVirt SP. Provider registration must include sovereignty capability declarations as a contractual obligation. + +5. **Tenant Support** — explicitly deferred in V1 in the Policy Engine spec. The data model treats Tenant as mandatory and non-overridable. A clear migration path from V1 to Tenant support is needed. + +6. **Universal Versioning** — catalog items use `apiVersion: v1alpha1` rather than Major.Minor.Revision. All definitions require universal versioning. + +7. **Deprecation Model** — absent from all enhancement documents. All definitions require the `active → deprecated → retired` lifecycle. + +#### Document-Specific Gaps + +**SP Registration Flow:** +- Registration payload missing: sovereignty declarations, ownership model declaration, dependency declarations, capacity model mode declaration +- Uses `name` as natural key rather than UUID as primary key +- Capacity data (totalCpu, totalMemory) treated as static rather than dynamic — needs reconciliation with three-mode capacity model + +**Service Type Definitions:** +- `providerHints` not formally marked as portability-breaking — silently bypasses portability enforcement +- Storage and networking bundled with compute — conflicts with the service dependency model which treats these as separate dependent services with their own lifecycle +- No UUIDs on field definitions, no provenance metadata, no versioning beyond v1alpha1 + +**Service Provider Health Check:** +- Covers liveness only — insufficient for full Provider Lifecycle Events contract +- Does not address DEGRADATION, MAINTENANCE, UNSANCTIONED_CHANGE, CAPACITY_CHANGE events +- Binary 200/non-200 model needs to coexist with structured event payload model + +**Policy Engine:** +- `selected_provider` as a direct policy output conflicts with the data model's specificity narrowing model for provider selection +- No policy versioning or GitOps integration — data model requires policies maintained via GitOps +- No provenance recording on field mutations — data model requires provenance on every modification +- Constraint immutability model is compatible and can be mapped to `override_preference` field metadata + +**Catalog Item Schema:** +- No UUIDs, no Tenant scoping, no portability classification, no deprecation model, no provenance +- `editable` field concept is valuable and not explicitly covered in data model — worth incorporating into the Resource Type Specification field definition +- `dependsOn` conditional field pattern maps to `conditional` portability classification but needs formal alignment + +**KubeVirt Service Provider:** +- Realized payload returns minimal status rather than complete DCM-format payload — Denaturalization requirement not met +- No unsanctioned change detection — only VMI phase changes reported +- Registration gaps same as SP Registration Flow spec +- `namespace` in response payload is provider-native concept with no DCM equivalent + +#### Questions to Resolve + +1. Should the enhancement documents be updated to align with the data model, or should the data model be adjusted where the enhancements reveal practical implementation constraints? +2. For the storage/networking bundling vs. dependency model tension — is this a V1 simplification that gets resolved in V2, or does the data model need a "monolithic service" concept? +3. For `selected_provider` in the Policy Engine — should provider selection remain a policy output, or should it be moved to a dedicated placement component that consumes narrowed field sets from the Policy Engine? +4. What is the migration path from V1 (no Tenant support) to Tenant-mandatory? +5. Should the `editable` field concept from the Catalog Item Schema be formally incorporated into the Resource Type Specification? + +#### References +- SP Registration: https://github.com/dcm-project/enhancements/blob/main/enhancements/sp-registration-flow/sp-registration-flow.md +- Service Types: https://github.com/dcm-project/enhancements/blob/main/enhancements/service-type-definitions/service-type-definitions.md +- Health Check: https://github.com/dcm-project/enhancements/blob/main/enhancements/service-provider-health-check/service-provider-health-check.md +- Policy Engine: https://github.com/dcm-project/enhancements/blob/main/enhancements/policy-engine/policy-engine.md +- Catalog Item: https://github.com/dcm-project/enhancements/blob/main/enhancements/catalog-item-schema/catalog-item-schema.md +- KubeVirt SP: https://github.com/dcm-project/enhancements/blob/main/enhancements/kubevirt-sp/kubevirt-sp.md + +--- + +Copy the following template and fill in the fields: + +```markdown +### TOPIC-NNN — Title + +**Area:** +**Priority:** +**Status:** 🟡 Active +**Raised:** + +#### Description + + + +#### Questions to Resolve + +1. + +#### References +- +``` + +--- + +*This document is maintained by the DCM Project team. Add topics freely — no topic is too small if it needs a decision.* + +--- + +### 6. Universal Lightspeed Interface for Operations (future concept) + +**Note captured for future exploration.** + +A universal operations interface concept — working title "Lightspeed" — for DCM. Intent is a unified, high-velocity operational surface for all DCM actions regardless of the underlying provider, resource type, or lifecycle stage. Think of it as the operational equivalent of what the unified data model is to data: a single consistent interaction model for humans and automation alike across the full DCM estate. + +**Initial thoughts to explore:** +- Single interface for any operation on any resource managed by DCM — no provider-specific tooling, no context switching +- "Lightspeed" implies minimal friction: operations that currently take multiple steps, approvals, and tool handoffs should be expressible and executable in a single interaction +- Applicable to both human operators (Web UI / CLI) and automation (agentic workflows, AIOps) +- Likely surfaces DCM's existing policy engine, lifecycle model, and unified data model as the execution layer — the interface is the innovation, not new backend capability +- Could be the primary surface for DCM's "AI Ready" design principle (README): agentic workflows operating over DCM's control plane via a natural-language-capable interface that still enforces all policy, sovereignty, and accreditation constraints + +**Questions to answer when this gets scoped:** +- Is this a new GUI surface, a CLI, an AI agent interface, or all three? +- How does it relate to the existing Web UI spec and Flow GUI spec? +- What does "lightspeed" mean operationally — sub-second execution, zero-confirmation for pre-approved patterns, predictive pre-staging? +- How does it interact with the Authority Tier model — can it auto-route approval gates without interrupting the operator's flow? +- Is this the primary interface for the AIOps layer referenced in the README? + +**Status:** Concept note — no design work started. Capture for roadmap planning. + +### 7. Spectral AEP Linter + CI Workflow Adoption (PR #18 review) + +**Source:** [PR #18](https://github.com/dcm-project/dcm/pull/18) by Fale — first draft of Interoperability API and Service Spec design guidelines. + +**PR #18 disposition:** The interoperability API and object design RFC are superseded by the current architecture (4 canonical OpenAPI specs, 58 data model docs). Several conventions in PR #18 (camelCase fields, PascalCase enums, YAML content type, K8s apiVersion/kind/spec/status structure) contradict the team's established patterns (snake_case fields, UPPER_SNAKE_CASE enums, JSON, flat REST entities). PR should be closed with acknowledgment and pointer to current architecture. + +**What to adopt:** The `.spectral.yaml` AEP linter config and the GitHub Actions CI workflow that validates OpenAPI specs against AEP rules. This enforces PRR-006 (AEP.DEV Linting) in CI. + +**Blockers before adoption:** +- The team's greenfield specs use OpenAPI 3.1.0; the AEP Spectral ruleset was built for 3.0.x. Must test for false positives on 3.1 constructs before enabling CI enforcement. +- Determine scope: lint only greenfield specs in `src/api/v1alpha1/`, or also the canonical specs in `dcm/schemas/openapi/`? +- Decide if this goes in dcm-examples only, or also in the dcm architecture repo for the canonical specs. + +**Status:** Future — pending OpenAPI 3.1 compatibility verification. + diff --git a/content/docs/README.md b/content/docs/README.md new file mode 100644 index 0000000..31d1438 --- /dev/null +++ b/content/docs/README.md @@ -0,0 +1,135 @@ +# DCM — Data Center Management + +Data Center Management (DCM) is an open-source governing framework for enterprise on-premises and sovereign cloud infrastructure. It provides a hyperscaler-like cloud experience — the operational model and self-service capabilities of a public cloud provider — on infrastructure that organizations own and control. + +**GitHub:** https://github.com/dcm-project | **License:** Apache 2.0 + +--- + +## What DCM Is + +DCM is the governing control plane that sits above provisioning tools, automation platforms, and infrastructure systems — making them coherent, governed, and self-service. It is not a deployment tool or a configuration manager. It is the management plane that connects them. + +**[Full project description →](project-overview.md)** — what DCM is, what it does, why, who benefits, and where it operates. + +--- + +## Architecture in One Sentence + +DCM is built on three foundational abstractions — **Data**, **Provider**, and **Policy** — connected by a policy-driven event loop. Every concept maps to one of these three. See [00-foundations.md](data-model/00-foundations.md). + +--- + +## Documentation Structure + +### Foundation Documents (read these first) + +| Document | Purpose | +|----------|---------| +| [project-overview.md](project-overview.md) | What DCM is, what it does, who it benefits, where it operates | +| [00-foundations.md](data-model/00-foundations.md) | The three abstractions — Data, Provider, Policy | +| [00-design-priorities.md](data-model/00-design-priorities.md) | Design priority framework — decision hierarchy for all contributors | +| [A-provider-contract.md](data-model/A-provider-contract.md) | Unified Provider base contract + 11 typed extensions | +| [B-policy-contract.md](data-model/B-policy-contract.md) | Unified Policy base contract + 7 output schemas | + +### Data Model (55 documents) + +| Range | Coverage | +|-------|---------| +| 00–05 | Context, foundations, entity types, four states, layering, ownership, resource types | +| 06–12 | Resource/service entities, dependencies, grouping, relationships, information providers, storage providers, audit | +| 13–19 | Ingestion, policy profiles, universal groups, universal audit, deployment, webhooks, auth providers | +| 20–27 | Registry governance, advanced information providers, federation, notifications, operational models, control plane, accreditation, governance matrix | +| 28–35 | Federated contribution, scoring model, meta provider, credential provider, authority tier, event catalog, API versioning, session revocation | +| 36–42 | Internal component auth, scheduled requests, dependency graph, self-health, standards catalog, operational reference, ITSM integration | +| 43–49 | Provider callback auth, Kessel evaluation, consistency review, workload analysis, accreditation monitor, location topology, implementation specifications | +| A, B | Unified Provider Contract, Unified Policy Contract | + +### Specifications (15 documents) + +Consumer API · Admin API · Operator Interface · OPA Integration · Flow GUI · Admin GUI · Consumer GUI · Provider GUI · Registration · Examples · Use Case Examples · Kubernetes Compatibility · Operator SDK · RHDH Integration · CNCF Strategy + +### Example Implementations + +| Path | Purpose | +|------|---------| +| [implementations/example-01-summit-demo/](implementations/example-01-summit-demo/) | Summit 2026 Demo — Intelligent Placement, Rehydration, Application as a Service on OpenShift | + + +### AI Model Prompt + +[DCM-AI-PROMPT.md](DCM-AI-PROMPT.md) — load into any AI model to provide full project context. 103 sections covering the complete architecture, all decisions, implementation guidance, and working instructions. + +--- + +## Key Numbers + +| Metric | Value | +|--------|-------| +| Foundational abstractions | 3 (Data, Provider, Policy) | +| Provider types | 11 (unified base contract + typed capability extensions) | +| Policy types | 7 (unified base contract + typed output schemas) | +| Entity lifecycle states | 4 (Intent · Requested · Realized · Discovered) | +| Capabilities | 299 across 38 domains | +| Data model documents | 55 | +| Specifications | 15 | +| Consumer API paths | 63 | +| Admin API paths | 57 | +| Unresolved architectural questions | 0 | + +--- + +## Core Principles + +1. **Declarative** — data describes desired state, not procedures +2. **API-First** — every capability available via standard AEP-aligned API +3. **Policy-Governed** — all business logic through the Policy Engine, never hard-coded +4. **Idempotent** — applying the same data multiple times produces the same result +5. **Immutable if Versioned** — published versions never change; changes produce new versions +6. **Provider-Agnostic** — DCM defines contracts, not implementations +7. **GitOps-Native** — intent and policy artifacts are Git-native +8. **Federated** — all authorized actor types contribute within permitted scope +9. **Compliance by Construction** — audit trail, provenance, and sovereignty enforcement are structural + +--- + +## Capabilities Matrix + +[DCM-Capabilities-Matrix.md](DCM-Capabilities-Matrix.md) — 299 capabilities across 38 domains including: Identity and Access, Service Catalog, Request Lifecycle, Provider Contract, Resource Lifecycle, Drift Detection, Policy Management, Data Layer, Information Integration, Ingestion, Audit, Observability, Storage, Federation, Platform Governance, Accreditation, Zero Trust, Governance Matrix, Scoring Model, Meta Provider, Credential Provider, Authority Tier, Event Catalog, API Versioning, Session Revocation, Internal Component Auth, Scheduled Requests, Dependency Graph, Self-Health, Operational Reference, Web Interfaces, ITSM Integration, Provider Callback Auth, Workload Analysis, Accreditation Monitoring, and Location Topology. + +--- + +## How DCM Works + +DCM's runtime is a **policy-driven event loop**: every data state change triggers Policy Engine evaluation, policies produce typed outputs (approve/halt/enrich/route/recover), outputs invoke Providers or produce new Data, and new Data triggers new events. There is no hard-coded pipeline — the pipeline is the sum of active Policies. + +A request flows through: **intent declared** → **layer assembly** (Core Layers + Service Layers + Transformation Policies inject and lock required fields) → **policy evaluation** (Validation, GateKeeper, Placement) → **Requested State written** → **dispatch to Provider** (Naturalization → execution → Denaturalization) → **Realized State written** → **ongoing drift monitoring**. + +Every business rule is a Policy artifact stored in Git, versioned, tested in shadow mode before activation, and enforced deterministically. Adding a new approval step, changing placement rules, or building a named workflow requires writing a policy — not changing code. + +Providers wrap existing automation (Ansible, Terraform, vendor APIs). They implement one base contract and translate between DCM's unified data model and their native format. Organizations do not replace their automation — they govern it. + +**[Full technical walkthrough →](project-overview.md#how-dcm-works)** + +--- + +## Ethos + +Four design priorities — applied in order when they conflict: + +1. **Security is the baseline.** Security properties are present in every profile. Profiles control enforcement strictness and operational burden — not whether the property applies. The `minimal` profile is "security with minimal overhead" — not "minimal security." + +2. **The governed path must also be the easy path.** Self-service is how governance scales. If consuming resources through DCM is harder than raising a ticket, teams route around it. The entire scoring, auto-approval, and visual policy authoring system exists to make the governed path the path of least resistance. + +3. **Compliance is constructed, not audited.** Audit evidence, field-level provenance, and sovereignty enforcement are structural products of every operation — not reconstructed from logs after the fact. An auditor can answer "who touched this data and when" directly from DCM's audit store. + +4. **No silent behavior.** Every operation produces an observable artifact. Every state transition produces an audit record. Every policy decision produces a typed output. When something goes wrong, "what happened and why" is always answerable from the system's own output. + +**[Full ethos document →](project-overview.md#ethos)** + +--- + + +## Contributing + +DCM is open-source. Community contributions welcome via GitHub at https://github.com/dcm-project diff --git a/content/docs/WALKTHROUGH.md b/content/docs/WALKTHROUGH.md new file mode 100644 index 0000000..721a78f --- /dev/null +++ b/content/docs/WALKTHROUGH.md @@ -0,0 +1,581 @@ +# DCM End-to-End Walkthrough — VM Provision + +**Purpose:** Trace a single request through the entire DCM pipeline with real data structures at every stage. This is the single document to read if you want to understand how DCM works in practice. + +**Time to read:** ~20 minutes +**Prerequisites:** None — this document is self-contained. + +--- + +## The Scenario + +A developer on the AppTeam tenant requests a standard Linux VM for a payments API server. The request must comply with EU data residency requirements. The VM requires an IP address — DCM automatically resolves this dependency by requesting an IP from the appropriate IPAM provider, governed by core sovereignty policies and service-specific subnet policies. We follow this request from the consumer's API call through to a running VM and its first discovery cycle. + +--- + +## Stage 1: Consumer Submits Intent + +The developer calls the Consumer API: + +``` +POST /api/v1/requests +Authorization: Bearer +Content-Type: application/json + +{ + "catalog_item_uuid": "compute-vm-standard-uuid", + "fields": { + "cpu_count": 4, + "memory_gb": 8, + "storage_gb": 100, + "os_family": "rhel", + "environment": "production", + "name": "payments-api-server-01" + } +} +``` + +**What the consumer declares:** What they need. Not where it runs, not which provider, not which datacenter. Just the desired outcome. + +**What DCM creates — Intent State:** + +```yaml +entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0 +resource_type: Compute.VirtualMachine +tenant_uuid: a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6 # AppTeam +submitted_by: b2c3d4e5-actor-uuid +submitted_at: 2026-03-15T09:00:00Z + +fields: + cpu_count: 4 + memory_gb: 8 + storage_gb: 100 + os_family: rhel + environment: production + name: "payments-api-server-01" +``` + +**What happens immediately:** +- Authorization check: actor has `request:compute:vm` permission in AppTeam tenant +- Sovereignty check: AppTeam's tenant has `data_residency: EU-WEST` — this will constrain placement later +- The `entity_uuid` is assigned now and will never change — it links every stage of this request's lifecycle + +**Audit leaf written:** `INTENT_CAPTURED` — SHA-256 hash of the intent payload, signed by the API gateway service. + +> **Key concept:** The consumer never chooses a provider or a datacenter. The control plane handles that. + +--- + +## Stage 2: Layer Assembly + +The Request Processor assembles the full payload by merging data layers in precedence order. Layers are organizational data — datacenter configs, environment defaults, tenant overrides, compliance requirements. + +**Layer chain resolved (highest to lowest precedence):** + +``` +1. system/core/datacenter-layer.yaml → data_center: "EU-WEST-DC1" +2. system/core/environment-layer.yaml → monitoring defaults, log retention +3. system/compliance/eu-west-layer.yaml → backup_policy: "daily-30d-eu-west" +4. org/appteam-defaults-layer.yaml → monitoring_agent: "datadog-agent:7.42" +5. providers/openstack/vm-defaults-layer.yaml → provider-specific defaults +6. Consumer intent → cpu_count: 4, memory_gb: 8, ... +``` + +**Resulting merged payload (selected fields with provenance):** + +```yaml +cpu_count: + value: 4 + provenance: { source_type: consumer, source_uuid: f5e6d7c8-entity } + +memory_gb: + value: 8 + provenance: { source_type: consumer } + +data_center: + value: "EU-WEST-DC1" + provenance: { source_type: base_layer, source_uuid: dc-layer-uuid } + +environment: + value: production + provenance: + origin: { source_type: intermediate_layer, value: dev } + modifications: + - previous: dev → modified: production, source_type: consumer # consumer override + +monitoring_agent: + value: "datadog-agent:7.42" + provenance: { source_type: intermediate_layer, source_uuid: appteam-defaults-uuid } + +backup_policy: + value: "daily-30d-eu-west" + provenance: { source_type: intermediate_layer, source_uuid: eu-west-compliance-uuid } +``` + +**Key concept:** The consumer declared 6 fields. After assembly, there are 10+ fields. The extra fields come from layers — organizational data that the consumer doesn't need to know about but that provisioning requires. + +**Key concept:** Every field carries provenance — where the value came from and what modified it. This is how auditors trace any value back to its origin. + +**Audit leaf written:** `ASSEMBLY_COMPLETE` — hash of the assembled payload with layer chain reference. + +--- + +## Stage 3: Policy Evaluation + +The Policy Engine evaluates all matching policies against the assembled payload. Evaluation follows a three-phase model: + +### Phase 1: GateKeeper + Validation (pass/fail, no mutations) + +``` +Policy: vm-size-limits (GateKeeper) + Match: resource_type = Compute.VirtualMachine, lifecycle_scope = initial_provisioning + Result: APPROVED — 4 CPU within AppTeam's 16 CPU quota + +Policy: approved-os-images (GateKeeper, tenant-scoped) + Match: resource_type = Compute.VirtualMachine, tenant_uuid = AppTeam + Result: APPROVED — rhel is in AppTeam's approved images list + +Policy: eu-data-residency (GateKeeper, hard enforcement) + Match: data_residency = EU-WEST + Result: APPROVED — data_center value "EU-WEST-DC1" is within EU-WEST zone +``` + +### Phase 2: Transformation (mutations applied) + +``` +Policy: inject-monitoring-endpoint (Transformation) + Match: environment = production, has monitoring_agent + Action: INJECT field monitoring_endpoint + Result: + monitoring_endpoint: + value: "https://metrics.internal.eu-west.example.com" + provenance: { source_type: policy, source_uuid: inject-monitoring-policy-uuid, + reason: "Standard endpoint for EU-WEST production resources" } +``` + +The transformation pass runs again to check for convergence. No new mutations — converged in 1 pass. + +### Phase 3: Post-mutation GateKeeper + +``` +Policy: vm-size-limits — re-evaluated after transformations + Result: APPROVED (no relevant mutations occurred) +``` + +**All policies pass. Request proceeds to placement.** + +**Audit leaves written:** One per policy evaluation with result, constraint emissions, and hash of the payload at evaluation time. + +> **What if a policy blocked?** The request would enter `POLICY_BLOCKED` state and the consumer would receive resolution guidance — compliant values, override options, cancel, or escalate. See ADR-009. + +--- + +## Stage 4: Dependency Resolution + +Before placement can proceed, DCM checks the Resource Type Specification for `Compute.VirtualMachine` and finds a **type-level dependency**: + +```yaml +# From the Resource Type Specification for Compute.VirtualMachine +resource_type: Compute.VirtualMachine +type_level_dependencies: + - required_resource_type: Network.IPAddress + dependency_type: hard + cardinality: one_to_one + description: "Every VM requires exactly one IP address" + payload_fields: # fields to inject from the realized IP into the VM payload + - source: "address" + target: "assigned_ip_address" + - source: "subnet" + target: "network_subnet" + - source: "gateway" + target: "network_gateway" +``` + +The VM cannot be dispatched until this dependency is satisfied. DCM creates a **sub-request** for the IP address — the consumer never sees this; it's an internal orchestration step driven by the resource type definition. + +**IP Address sub-request created:** + +```yaml +entity_uuid: aabb1122-ip-uuid +resource_type: Network.IPAddress +tenant_uuid: a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6 # same tenant as parent VM +parent_entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0 # linked to the VM + +fields: + address_family: IPv4 + purpose: vm_interface + environment: production + attachment_ref: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0 # the VM this IP is for +``` + +> **Key concept:** The consumer requested a VM. DCM knows a VM requires an IP address because the resource type spec declares it. The consumer doesn't manage IP allocation — DCM does. + +**Audit leaf written:** `DEPENDENCY_CREATED` — hash of IP sub-request + parent VM entity_uuid. + +--- + +## Stage 5: IP Address — Policy Evaluation and Provider Selection + +The IP sub-request goes through its own policy evaluation pipeline — the same three-phase model as the parent VM, but with policies scoped to `Network.IPAddress`. + +### Core Policies (system-scoped, apply to all IP allocations) + +``` +Policy: ip-sovereignty-zone (GateKeeper, hard enforcement) + Match: resource_type = Network.IPAddress, data_residency = EU-WEST + Check: IP pool must be in EU-WEST sovereignty zone + Result: APPROVED — only EU-WEST pools will be considered + +Policy: ip-subnet-isolation (GateKeeper, system-scoped) + Match: resource_type = Network.IPAddress, environment = production + Check: Production IPs must come from production-designated subnets + Result: APPROVED — filters candidate pools to production subnets only +``` + +### Service Provider Policies (provider-scoped, specific to IPAM capabilities) + +``` +Policy: ipam-pool-selection (Transformation, provider-scoped) + Match: resource_type = Network.IPAddress, purpose = vm_interface + Action: Enriches request with pool selection criteria + Result: INJECT pool_selector field + pool_selector: + value: { zone: "eu-west", environment: "production", address_family: "IPv4" } + provenance: { source_type: policy, source_uuid: ipam-pool-selection-uuid, + reason: "Production VM interfaces use production pool in matching zone" } + +Policy: ip-address-format (Validation, provider-scoped) + Match: resource_type = Network.IPAddress + Check: address_family is valid (IPv4 or IPv6), purpose is recognized + Result: PASSED +``` + +### Placement selects IP Provider + +``` +Sovereignty pre-filter: + Eligible IPAM providers must satisfy data_residency: EU-WEST + → 2 IPAM providers in EU-WEST zone + +Pool capacity query: + EU-WEST-IPAM-1 (InfoBlox): pool 10.1.0.0/16, 65,420 available → confidence 98% + EU-WEST-IPAM-2 (NetBox): pool 10.2.0.0/16, 12,100 available → confidence 91% + +Selection: EU-WEST-IPAM-1 (highest confidence, largest available pool) +``` + +**Audit leaves written:** One per IP policy evaluation + `IP_PLACEMENT_COMPLETE`. + +--- + +## Stage 6: IP Address Realization + +The IP provider (InfoBlox IPAM) receives the sub-request, naturalizes it to its native API, and allocates an address: + +``` +DCM sub-request → InfoBlox API call: + +POST /wapi/v2.12/record:host +{ + "name": "payments-api-server-01.eu-west.internal", + "ipv4addrs": [{ "ipv4addr": "func:nextavailableip:10.1.0.0/16" }], + "comment": "DCM entity aabb1122-ip-uuid, tenant AppTeam" +} +``` + +**IP provider callback:** + +```yaml +entity_uuid: aabb1122-ip-uuid +status: OPERATIONAL +provider_entity_id: "record:host/ZG5z:10.1.45.23" + +realized_fields: + address: "10.1.45.23" + subnet: "10.1.0.0/16" + gateway: "10.1.0.1" + dns_name: "payments-api-server-01.eu-west.internal" + lease_type: static + pool_ref: "10.1.0.0/16" +``` + +> **Key concept:** The IP address is now a first-class DCM entity. It has its own entity_uuid, its own realized state, its own audit trail. When the VM is decommissioned, DCM knows to release this IP back to the pool. + +**Audit leaf written:** `IP_REALIZED` — hash of realized IP state + IPAM provider signature. + +--- + +## Stage 7: Dependency Injection and VM Placement + +Now the IP dependency is satisfied. DCM injects the realized IP data into the VM's payload via **dependency payload passing**: + +```yaml +# VM payload enriched with dependency data +fields: + cpu_count: { value: 4, provenance: {...} } + memory_gb: { value: 8, provenance: {...} } + storage_gb: { value: 100, provenance: {...} } + # ... all previously assembled fields ... + + # Injected from realized IP address dependency + assigned_ip_address: + value: "10.1.45.23" + provenance: + source_type: dependency_payload + source_uuid: aabb1122-ip-uuid # the IP entity + dependency_type: Network.IPAddress + timestamp: 2026-03-15T09:01:15Z + network_subnet: + value: "10.1.0.0/16" + provenance: { source_type: dependency_payload, source_uuid: aabb1122-ip-uuid } + network_gateway: + value: "10.1.0.1" + provenance: { source_type: dependency_payload, source_uuid: aabb1122-ip-uuid } + +dependencies_satisfied: + - dependency_type: Network.IPAddress + entity_uuid: aabb1122-ip-uuid + status: SATISFIED + satisfied_at: 2026-03-15T09:01:15Z +``` + +The Placement Engine now scores VM providers (same as before, but the payload now includes the IP): + +``` +Sovereignty pre-filter: + Eligible VM providers must satisfy data_residency: EU-WEST + → 3 OpenStack instances in EU-WEST zone + +Reserve query results: + EU-WEST-Prod-1: capacity available, confidence 94% + EU-WEST-Prod-2: capacity available, confidence 87% + EU-WEST-Prod-3: insufficient capacity — excluded + +Selection: EU-WEST-Prod-1 (highest confidence score) +``` + +**Requested State committed (write-once):** + +```yaml +entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0 +resource_type: Compute.VirtualMachine +assembled_at: 2026-03-15T09:01:18Z + +fields: + cpu_count: { value: 4, provenance: {...} } + memory_gb: { value: 8, provenance: {...} } + storage_gb: { value: 100, provenance: {...} } + os_family: { value: rhel, provenance: {...} } + environment: { value: production, provenance: {...} } + name: { value: "payments-api-server-01", provenance: {...} } + data_center: { value: "EU-WEST-DC1", provenance: {...} } + monitoring_agent: { value: "datadog-agent:7.42", provenance: {...} } + backup_policy: { value: "daily-30d-eu-west", provenance: {...} } + monitoring_endpoint: { value: "https://metrics...", provenance: {...} } + assigned_ip_address: { value: "10.1.45.23", provenance: { source_type: dependency_payload } } + network_subnet: { value: "10.1.0.0/16", provenance: { source_type: dependency_payload } } + network_gateway: { value: "10.1.0.1", provenance: { source_type: dependency_payload } } + +placement: + selected_provider_uuid: eu-west-prod-1-provider-uuid + sovereignty_satisfied: true + +dependencies: + - type: Network.IPAddress + entity_uuid: aabb1122-ip-uuid + status: SATISFIED +``` + +**Audit leaf written:** `PLACEMENT_COMPLETE` — hash of requested state + placement decision + dependency references. + +> **Key concept:** Requested State is write-once. It now includes both the assembled fields AND the dependency data from the IP allocation. This is the permanent auditable record of exactly what was approved for provisioning. + +--- + +## Stage 8: VM Provider Dispatch (Naturalization) + +The Request Orchestrator sends the enriched payload to the selected VM provider. The provider **naturalizes** the DCM unified payload — including the dependency-injected IP address — into its native API format: + +``` +DCM unified payload → OpenStack Nova API call: + +POST /servers +{ + "server": { + "name": "payments-api-server-01", + "flavorRef": "m1.xlarge", ← resolved from cpu_count: 4 + memory_gb: 8 + "imageRef": "rhel-9.3-latest", ← resolved from os_family: rhel + "networks": [{ + "uuid": "eu-west-net-uuid", + "fixed_ip": "10.1.45.23" ← from dependency: Network.IPAddress + }], + "availability_zone": "eu-west-az1", + "metadata": { + "dcm_entity_uuid": "f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0", + "dcm_tenant": "AppTeam", + "dcm_ip_entity_uuid": "aabb1122-ip-uuid", + "backup_policy": "daily-30d-eu-west" + } + } +} +``` + +**Key concept:** The VM provider receives the IP address as a known field in the payload — it doesn't call the IPAM system itself. DCM resolved the dependency, allocated the IP through the proper provider with full policy evaluation, and injected the result. The VM provider just uses it. + +**Audit leaf written:** `DISPATCH_SENT` — hash of naturalized payload + provider identity. + +--- + +## Stage 9: VM Provider Callback (Denaturalization) + +The provider provisions the VM with the pre-allocated IP, then **denaturalizes** the result back into DCM's unified format: + +``` +POST /api/v1/provider/entities/f5e6d7c8-.../status +Authorization: Bearer + +{ + "operation_uuid": "...", + "status": "OPERATIONAL", + "provider_entity_id": "vm-0a1b2c3d", + "realized_fields": { + "cpu_count": 4, + "memory_gb": 8, + "storage_gb": 102, ← actual (rounded up) + "assigned_ip_address": "10.1.45.23", ← confirmed: matches dependency + "hypervisor_host": "compute-node-07", ← provider-assigned + "console_url": "https://console.eu-west.example.com/vm/0a1b2c3d" + } +} +``` + +**Realized State recorded:** + +```yaml +entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0 +realized_at: 2026-03-15T09:03:12Z +lifecycle_state: OPERATIONAL + +fields: + cpu_count: 4 + memory_gb: 8 + storage_gb: 102 + assigned_ip_address: "10.1.45.23" + hypervisor_host: "compute-node-07" + console_url: "https://console.eu-west..." + provider_entity_id: "vm-0a1b2c3d" + +dependencies: + - type: Network.IPAddress + entity_uuid: aabb1122-ip-uuid + realized_value: "10.1.45.23" +``` + +**Consumer receives status update via webhook/polling.** + +**Audit leaf written:** `REALIZED` — hash of realized state + provider signature. + +--- + +## Stage 10: Discovery Cycle (Drift Detection) + +24 hours later, the Discovery service polls the provider: + +```yaml +discovered_at: 2026-03-16T09:00:00Z +provider_entity_id: "vm-0a1b2c3d" +status: ACTIVE + +cpu_count: 4 # matches Realized — no drift +memory_gb: 8 # matches +storage_gb: 102 # matches +assigned_ip_address: "10.1.45.23" # matches +``` + +**Drift comparison: Realized ≡ Discovered. No drift detected.** + +Discovery also polls the IP entity independently: + +```yaml +discovered_at: 2026-03-16T09:00:05Z +provider_entity_id: "record:host/ZG5z:10.1.45.23" +status: ACTIVE + +address: "10.1.45.23" # matches IP Realized State +lease_type: static # matches +``` + +If someone had manually changed the VM's IP outside DCM, the discovered IP on the VM would differ from the dependency record — that's drift on both the VM entity and the IP entity. + +> **Key concept:** Both the VM and the IP are independently discoverable DCM entities. Drift detection runs on each. The dependency relationship means drift on the IP triggers review of the VM too. + +--- + +## Stage 11: The Audit Trail + +At the end of this lifecycle, the Merkle tree contains these leaves (at mutation granularity). Note how both the VM and the IP dependency have their own complete audit chains: + +``` +VM Request: + Leaf 1: INTENT_CAPTURED hash(intent_payload) signed by api-gateway + Leaf 2: ASSEMBLY_COMPLETE hash(assembled_payload) signed by request-processor + Leaf 3: POLICY_EVAL:vm-size hash(eval_context + result) signed by policy-engine + Leaf 4: POLICY_EVAL:os-images hash(eval_context + result) signed by policy-engine + Leaf 5: POLICY_EVAL:residency hash(eval_context + result) signed by policy-engine + Leaf 6: POLICY_EVAL:monitoring hash(eval_context + mutation) signed by policy-engine + +IP Dependency Sub-Request: + Leaf 7: DEPENDENCY_CREATED hash(ip_sub_request + parent_vm_uuid) signed by orchestrator + Leaf 8: IP_POLICY:sovereignty hash(eval_context + result) signed by policy-engine + Leaf 9: IP_POLICY:subnet-iso hash(eval_context + result) signed by policy-engine + Leaf 10: IP_POLICY:pool-select hash(eval_context + mutation) signed by policy-engine + Leaf 11: IP_PLACEMENT_COMPLETE hash(ip_requested_state + ipam_selection) signed by placement + Leaf 12: IP_DISPATCH_SENT hash(naturalized_infoblox_request) signed by orchestrator + Leaf 13: IP_REALIZED hash(ip_realized_state) signed by ipam-provider + +VM Continues After Dependency Satisfied: + Leaf 14: DEPENDENCY_SATISFIED hash(ip_realized_fields + vm_entity_uuid) signed by orchestrator + Leaf 15: PLACEMENT_COMPLETE hash(vm_requested_state + placement + deps) signed by placement + Leaf 16: DISPATCH_SENT hash(naturalized_nova_payload) signed by orchestrator + Leaf 17: REALIZED hash(vm_realized_state) signed by vm-provider +``` + +Any auditor can: +- **Inclusion proof:** Verify leaf 8 (IP sovereignty check) exists in the tree +- **Consistency proof:** Verify the tree has only grown since the last signed tree head +- **Dependency chain:** Follow leaf 7→13 for the complete IP allocation audit, then leaf 14→17 for the VM +- **Policy provenance:** Trace why the IP came from InfoBlox pool 10.1.0.0/16 (leaves 9-11: subnet isolation + pool selection policies) +- **Non-repudiation:** Every leaf is signed by the service that produced it (Ed25519) + +--- + +## Summary: What Happened in 3 Minutes + +| Time | Stage | What happened | +|------|-------|---------------| +| T+0s | 1. Intent | Consumer declared 6 fields via API | +| T+0.5s | 2. Assembly | 5 layers merged in, provenance tracked for all 10+ fields | +| T+1s | 3. Policy (VM) | 4 policies evaluated — all passed. 1 field injected by transformation | +| T+1.1s | 4. Dependencies | VM requires Network.IPAddress — sub-request created automatically | +| T+1.3s | 5. Policy (IP) | 4 IP policies evaluated — sovereignty, subnet isolation, pool selection, format | +| T+1.5s | 6. IP Realized | IPAM provider allocated 10.1.45.23 from EU-WEST production pool | +| T+1.6s | 7. Injection | Realized IP injected into VM payload via dependency payload passing | +| T+2s | 7. Placement (VM) | 3 VM providers scored, 1 selected based on sovereignty + confidence | +| T+3s | 8. Dispatch | Payload naturalized to OpenStack Nova API with pre-allocated IP | +| T+192s | 9. Callback | Provider returned realized state — VM running at 10.1.45.23 | +| T+86400s | 10. Discovery | Both VM and IP confirmed matching realized state — no drift | + +**Consumer declared 6 fields. DCM handled everything else:** layer assembly, policy validation, data enrichment, IP allocation through the proper IPAM provider with full sovereignty and subnet policies, provider selection, API translation, state tracking, audit trail (17 Merkle leaves across 2 entities), and drift detection on both the VM and its IP dependency. + +--- + +## Where to Go Next + +- **To understand layers:** Doc 03 (Layering and Versioning) +- **To understand policies:** Doc B (Policy Contract) — start with §1-7 +- **To understand providers:** Doc A (Provider Contract) — start with §1-5 +- **To understand dependencies:** Doc 07 (Service Dependencies) — type-level deps, payload passing, resolution order +- **To understand audit:** Doc 16 (Universal Audit) — start with §1-3, then §8 for Merkle tree +- **To see the three-tier app example:** Doc 04 §8 (compound resource type specification with binding fields) +- **To see the IP allocation example:** Doc 04 §4 (IP Address Allocation with pool model) +- **To see all ADRs:** [Architecture Decision Records](adr/README.md) diff --git a/content/docs/_index.md b/content/docs/_index.md index 4585ceb..a61c8b2 100644 --- a/content/docs/_index.md +++ b/content/docs/_index.md @@ -2,13 +2,20 @@ title: Documentation type: docs sidebar: - open: true + open: false --- # DCM Documentation -Documentation for the DCM project. +Documentation for the DCM (Data Center Management) project — an open-source governing +framework for enterprise on-premises and sovereign cloud infrastructure. -## Documentation Sections +**New here?** Start with the [Project Overview](/docs/project-overview). -- **[Enhancements](enhancements/)** - Enhancement proposals documenting new features, architectural decisions, and significant changes to the DCM project. +{{< cards >}} + {{< card link="project-overview" title="Project Overview" icon="information-circle" subtitle="What DCM is, why it exists, who it benefits, and where it operates." >}} + {{< card link="architecture" title="Architecture" icon="template" subtitle="Data model (55 docs), specifications (15 docs), and OpenAPI schemas." >}} + {{< card link="capabilities-matrix" title="Capabilities Matrix" icon="table-cells" subtitle="299 capabilities across 38 domains." >}} + {{< card link="taxonomy" title="Taxonomy" icon="book-open" subtitle="Precise vocabulary for the DCM architecture." >}} + {{< card link="implementations" title="Implementations" icon="rocket-launch" subtitle="Reference implementations. Example #1: Summit 2026 demo on OpenShift." >}} +{{< /cards >}} diff --git a/content/docs/architecture/_index.md b/content/docs/architecture/_index.md new file mode 100644 index 0000000..5d16d7e --- /dev/null +++ b/content/docs/architecture/_index.md @@ -0,0 +1,37 @@ +--- +title: Architecture +type: docs +weight: 1 +sidebar: + open: true +--- + +# DCM Architecture + +The DCM architecture is documented across three layers: + +{{< cards >}} + {{< card link="data-model" title="Data Model" icon="database" subtitle="55 documents covering all entity types, four states, policies, providers, and lifecycle management." >}} + {{< card link="specifications" title="Specifications" icon="document-text" subtitle="15 specification documents: Consumer API, Admin API, Operator Interface, OPA Integration, Flow GUI, and more." >}} + {{< card link="ai-prompt" title="AI Prompt" icon="chip" subtitle="Full project context for AI models — 103 sections covering all architecture decisions." >}} +{{< /cards >}} + +--- + +## Architecture in One Sentence + +DCM is built on three foundational abstractions — **Data**, **Provider**, and **Policy** — connected by a policy-driven event loop. Every concept maps to one of these three. See [00-foundations.md](data-model/00-foundations.md). + +--- + +## Key Numbers + +| | | +|-|-| +| Data model documents | 55 | +| Specification documents | 15 | +| API paths | 63 consumer · 57 admin · 5 operator · 7 provider callback | +| Capabilities | 299 across 38 domains | +| Provider types | 11 | +| Policy types | 7 | +| Unresolved architectural questions | 0 | diff --git a/content/docs/architecture/adr/001-why-dcm-exists.md b/content/docs/architecture/adr/001-why-dcm-exists.md new file mode 100644 index 0000000..7c1bf56 --- /dev/null +++ b/content/docs/architecture/adr/001-why-dcm-exists.md @@ -0,0 +1,31 @@ +# ADR-001: Why DCM Exists + +**Status:** Accepted +**Date:** March 2026 + +## Context + +Enterprise data centers run hundreds of thousands of resources — VMs, containers, network segments, storage volumes — across multiple infrastructure platforms. Today, each platform has its own provisioning workflow, API, data format, and lifecycle model. The result: + +- **No unified view of what's deployed.** Intended state, deployed state, and actual state diverge silently. Nobody can answer "what's running, who owns it, and does it match what was approved?" +- **No consistent governance.** Policy enforcement is tribal knowledge. Security reviews are manual gates. Compliance is verified after the fact rather than enforced at request time. +- **No common abstraction.** A team requesting a VM goes through one process; requesting a database goes through another; requesting a three-tier application requires manually coordinating both plus networking. + +Public cloud solves this with unified control planes (AWS CloudFormation, Azure Resource Manager, GCP Deployment Manager). On-premises infrastructure has no equivalent. + +## Decision + +Build DCM — a management plane for enterprise data center infrastructure that provides: +- A unified data model and API across all infrastructure platforms +- Policy-as-code enforcement on every request before provisioning +- Full lifecycle management from request through decommission with tamper-evident audit +- A provider abstraction that makes any infrastructure platform consumable through the same interface + +DCM is **not** a provisioning tool. It is the governance and orchestration layer that sits above provisioning tools (Ansible, Terraform, operators) and governs what gets requested, approved, built, owned, and decommissioned. + +## Consequences + +- DCM must be infrastructure-agnostic — it cannot favor any single platform +- The data model must be extensible to any resource type without code changes +- Policy evaluation must be mandatory, not optional — governance is the value proposition +- Audit must be tamper-evident to satisfy regulated environments (the primary adopters) diff --git a/content/docs/architecture/adr/002-three-abstractions.md b/content/docs/architecture/adr/002-three-abstractions.md new file mode 100644 index 0000000..a211aa8 --- /dev/null +++ b/content/docs/architecture/adr/002-three-abstractions.md @@ -0,0 +1,27 @@ +# ADR-002: Three Foundational Abstractions — Data, Provider, Policy + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 00 (Foundations) + +## Context + +A management plane for infrastructure must handle many concerns: data storage, external integrations, governance rules, audit trails, placement decisions, dependency resolution, lifecycle events, and more. Without a unifying model, the architecture becomes a collection of ad-hoc services with unclear boundaries. + +## Decision + +Every component of DCM maps to exactly one of three foundational abstractions: + +**DATA** — Everything stored and versioned. The unified data model, entity lifecycle states, field-level provenance, data layers, and audit records. Data flows through a deterministic pipeline: Intent → Requested → Realized → Discovered. + +**PROVIDER** — Everything external. Any system DCM interacts with through a defined contract. Providers receive data from DCM, act on it, and return data to DCM. Six provider types cover all external interactions: service, information, meta, auth, peer_dcm, and process. + +**POLICY** — Everything that decides. Rules that fire when data matches conditions and produce typed outputs: allow/deny, validation, field mutations, recovery actions, orchestration directives. Policies govern every transition and transformation in DCM. + +The interaction model: Data changes trigger Policy evaluation. Policy decisions may mutate Data or select Providers. Providers produce new Data. The cycle repeats. + +## Consequences + +- Any new capability must map to one of these three abstractions — if it doesn't fit, the abstraction model needs revision, not a fourth pillar +- Documentation, APIs, and code are organized around these three concepts +- Team members only need deep knowledge of 1-2 abstractions for their area of work diff --git a/content/docs/architecture/adr/003-four-lifecycle-states.md b/content/docs/architecture/adr/003-four-lifecycle-states.md new file mode 100644 index 0000000..2fa45a6 --- /dev/null +++ b/content/docs/architecture/adr/003-four-lifecycle-states.md @@ -0,0 +1,29 @@ +# ADR-003: Four Lifecycle States + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 02 (Four States) + +## Context + +A resource entity goes through multiple stages: the consumer declares intent, the system processes and approves, the provider provisions, and discovery observes what actually exists. If we track this as a single mutable record, we lose the ability to answer: "What did they ask for? What did we approve? What got built? What exists now?" + +These four questions are the foundation of governance, audit, compliance, and drift detection. + +## Decision + +Every resource entity flows through four immutable states: + +1. **Intent** — What the consumer asked for (raw declaration, no processing) +2. **Requested** — What was approved after layer assembly and policy evaluation (write-once) +3. **Realized** — What the provider actually created (snapshot from provider callback) +4. **Discovered** — What exists right now (independent observation via polling) + +The `entity_uuid` links all four states for the same resource. States are immutable — updates create new records. Drift is the delta between Realized and Discovered. Compliance is provable because Requested State records the policy-approved payload. + +## Consequences + +- Every resource has exactly 4 records linked by entity_uuid +- Drift detection is a comparison: Realized ≠ Discovered +- Rehydration (disaster recovery) re-enters at Intent with current policies +- Audit can trace any resource from consumer's original ask through to what's running diff --git a/content/docs/architecture/adr/004-service-catalog-consumer-experience.md b/content/docs/architecture/adr/004-service-catalog-consumer-experience.md new file mode 100644 index 0000000..6a6d528 --- /dev/null +++ b/content/docs/architecture/adr/004-service-catalog-consumer-experience.md @@ -0,0 +1,38 @@ +# ADR-004: Service Catalog and Consumer Experience + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 05 (Resource Type Hierarchy), Doc 06 (Resource/Service Entities) + +## Context + +Consumers need a way to discover what services are available and request them. The service catalog must abstract away infrastructure complexity — a developer requesting a VM should not need to know which hypervisor, which datacenter, or which network configuration is required. + +## Decision + +A four-level hierarchy separates what consumers see from what providers implement: + +1. **Resource Type Category** — Broad groupings (Compute, Network, Storage, Database) +2. **Resource Type** — Specific resource kinds (Compute.VirtualMachine, Network.VLAN) +3. **Resource Type Specification** — Vendor-neutral field schemas, constraints, lifecycle rules +4. **Provider Catalog Item** — A specific provider's offering (pricing, SLAs, availability) + +Consumers browse the catalog, select a catalog item, and submit a request with only the fields they care about (e.g., CPU count, memory, OS). DCM handles everything else: layer assembly, policy evaluation, provider selection, dependency resolution. + +**Consumer request surface** is a JSON payload via the Consumer API: + +```json +POST /api/v1/requests +{ "catalog_item_uuid": "...", "fields": { "cpu_count": 4, "memory_gb": 8, "os_family": "rhel" } } +``` + +## Open Question — Application Definition Language + +The current consumer interface is an API call with a JSON payload. This works for single resources. For multi-resource applications (three-tier web app, data pipeline, ML training environment), the consumer needs a way to define the application as a whole. This is an open design question — see [ADR-016: Application Definition Language](016-application-definition-language.md). + +## Consequences + +- Resource types are vendor-neutral; provider catalog items are provider-specific +- Multiple providers can offer catalog items for the same resource type +- Consumers never choose a provider directly — placement does that +- The catalog is queryable via API; RHDH provides the frontend diff --git a/content/docs/architecture/adr/005-provider-abstraction.md b/content/docs/architecture/adr/005-provider-abstraction.md new file mode 100644 index 0000000..c15b2e0 --- /dev/null +++ b/content/docs/architecture/adr/005-provider-abstraction.md @@ -0,0 +1,33 @@ +# ADR-005: Why Providers Exist and What They Do + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc A (Provider Contract) + +## Context + +DCM must interact with many external systems: hypervisors, container platforms, network controllers, IPAM systems, identity services, other DCM instances, ITSM tools, and more. Each has its own API, data format, and operational model. Without a common abstraction, DCM becomes tightly coupled to specific infrastructure platforms. + +## Decision + +A **Provider** is any external system DCM interacts with through a defined contract. All providers share a base contract (registration, health check, sovereignty declaration, accreditation, zero trust authentication). What varies is the capability — what operations the provider exposes and what data flows in which direction. + +Five provider types: + +| Type | What it does | Example | +|------|-------------|---------| +| **Service** | Provisions and manages infrastructure resources | OpenStack Nova, KubeVirt, ACM | +| **Information** | Serves authoritative external data | CMDB, DNS, IPAM (InfoBlox) | +| **Meta** | Composes multiple providers into a compound service | Three-tier app, full-stack environment | +| **Auth** | Authenticates identities | Keycloak, LDAP, FreeIPA | +| **Peer DCM** | Another DCM instance for federation | Cross-region DCM | +| **Process** | Executes workflows without producing resources | Approval chains, ITSM integration | + +The key mechanism is **Naturalization/Denaturalization**: DCM sends a unified payload to the provider. The provider translates (naturalizes) it into its native API format, acts on it, then translates (denaturalizes) the result back into DCM's unified format. + +## Consequences + +- Adding a new infrastructure platform means writing one provider — not changing DCM core +- Consumers don't know or care which provider fulfills their request +- Provider selection is policy-driven (placement), not consumer-chosen +- All provider interactions are audited and sovereignty-checked diff --git a/content/docs/architecture/adr/006-policy-engine.md b/content/docs/architecture/adr/006-policy-engine.md new file mode 100644 index 0000000..5e17fd0 --- /dev/null +++ b/content/docs/architecture/adr/006-policy-engine.md @@ -0,0 +1,33 @@ +# ADR-006: Why Policy-as-Code and What It Governs + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc B (Policy Contract) + +## Context + +Enterprise infrastructure requires governance: sizing limits, security constraints, compliance rules, sovereignty requirements, cost controls, naming conventions. Today this governance is tribal knowledge enforced by manual review gates. Manual gates are slow, inconsistent, and unauditable. + +## Decision + +Every request is policy-evaluated before provisioning. Policies are code artifacts (Rego), not configuration. They fire automatically when data matches conditions and produce typed outputs. + +**What policies govern:** +- **Who can request what** (GateKeeper: allow/deny based on role, tenant, resource type) +- **Whether the request is valid** (Validation: field constraints, range checks, format) +- **How the request is enriched** (Transformation: inject monitoring agents, set backup policies, apply naming conventions) +- **What happens when things fail** (Recovery: retry, requeue, compensate) +- **How pipeline stages are ordered** (Orchestration Flow: dependency sequencing) +- **What crosses boundaries** (Governance Matrix: sovereignty, data classification) + +**Key design choices:** +- Multi-pass evaluation with convergence — transformation policies can inject fields that other policies depend on +- Lifecycle-scoped — a CPU-sizing policy fires on provisioning and scaling, not on hostname changes +- Override model with 5 mechanisms — governance is not rigid; legitimate exceptions are handled through audited overrides + +## Consequences + +- No request bypasses policy evaluation — this is mandatory, not opt-in +- Policies are versioned, have lifecycle (developing → active → retired), and support shadow mode for safe testing +- Every policy evaluation produces an audit record regardless of outcome +- Policy complexity is managed through templates (Gatekeeper ConstraintTemplate pattern) and a Constraint Type Registry diff --git a/content/docs/architecture/adr/007-placement-engine.md b/content/docs/architecture/adr/007-placement-engine.md new file mode 100644 index 0000000..1872601 --- /dev/null +++ b/content/docs/architecture/adr/007-placement-engine.md @@ -0,0 +1,32 @@ +# ADR-007: How DCM Decides Where Things Run + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 50 (Placement), Doc 14 (Profiles) + +## Context + +When a consumer requests a VM, they don't specify which provider or datacenter. Multiple providers may be capable of fulfilling the request. DCM must select the best provider based on sovereignty requirements, capacity, compliance, cost, and organizational policy. + +## Decision + +The Placement Engine selects providers through a multi-stage scoring process: + +1. **Sovereignty pre-filter** — Eliminate providers that don't satisfy data residency requirements (e.g., EU-WEST resources can only go to EU-WEST providers). This is a hard gate, not a score. + +2. **Capability filter** — Eliminate providers that don't support the requested resource type or lack required capabilities. + +3. **Reserve query** — Query remaining providers for capacity availability and get confidence scores. + +4. **Policy-driven scoring** — Apply placement policies that score providers on criteria like cost, performance tier, organizational preference, and existing affinity (e.g., co-locate with related resources). + +5. **Selection** — Highest-scoring provider wins. Ties broken by configurable rules. + +For compound services (compound resource type specifications), placement runs per-constituent — the database may land on a different provider than the app server, each scored independently but subject to the same sovereignty constraints. + +## Consequences + +- Consumers never choose providers — placement is always policy-driven +- Adding new providers to a zone automatically makes them candidates for placement +- Placement decisions are audited with full scoring rationale +- Provider health affects placement — unhealthy providers are excluded diff --git a/content/docs/architecture/adr/008-dependency-resolution.md b/content/docs/architecture/adr/008-dependency-resolution.md new file mode 100644 index 0000000..142bd4f --- /dev/null +++ b/content/docs/architecture/adr/008-dependency-resolution.md @@ -0,0 +1,32 @@ +# ADR-008: How Resources Know What They Need + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 07 (Service Dependencies), Doc 30 (Meta Provider Model) + +## Context + +Infrastructure resources have dependencies. A VM needs an IP address. A database needs a network port. A three-tier application needs all of its components provisioned in the right order with runtime values (IP addresses, connection strings) flowing from one resource to the next. + +## Decision + +Dependencies are declared at two levels: + +**Type-level** (in the Resource Type Specification): "Every VM requires exactly one IP address." These are portable, provider-agnostic, and apply to all implementations of the resource type. DCM automatically creates sub-requests for type-level dependencies. + +**Binding fields** (in compound service definitions): "The backend's db_host field gets its value from the database's realized ip_address." These connect resources via runtime values — the output of one resource becomes the input of another. + +**How it works:** +1. Request Processor reads the resource type spec and identifies dependencies +2. Dependencies without parents are dispatched first (topological sort) +3. When a dependency is realized, its output values are injected into dependent resources via dependency payload passing (with full provenance tracking) +4. Dependent resources are dispatched after their dependencies are satisfied + +For compound services, the compound resource type spec declares the full dependency graph with binding fields. + +## Consequences + +- Consumers don't manage dependencies — they request a catalog item and DCM resolves the graph +- Each dependency is a first-class DCM entity with its own audit trail and lifecycle +- Decommission reverses the dependency order — dependents are torn down before their dependencies +- Circular dependencies are detected at resource type registration time, not at request time diff --git a/content/docs/architecture/adr/009-api-gateway-control-plane.md b/content/docs/architecture/adr/009-api-gateway-control-plane.md new file mode 100644 index 0000000..479618d --- /dev/null +++ b/content/docs/architecture/adr/009-api-gateway-control-plane.md @@ -0,0 +1,39 @@ +# ADR-009: Why an API Gateway and What the Control Plane Services Do + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 25 (Control Plane Services), OpenAPI Specs + +## Context + +DCM has multiple consumers (developers, platform engineers, admins, providers, external systems) that interact via different APIs with different authorization scopes. Internally, DCM has multiple services that process requests through a pipeline. These services need a single entry point that handles authentication, routing, rate limiting, and API versioning. + +## Decision + +The **API Gateway** is the single entry point for all external traffic. It handles: +- Authentication (JWT validation, API key verification) +- Route multiplexing (consumer API, admin API, provider callback API) +- Rate limiting and throttling per tenant +- TLS termination +- API versioning (v1, v1alpha1) + +Behind the gateway, **9 control plane services** process requests through the pipeline: + +| Service | What it does | +|---------|-------------| +| API Gateway | Routes external traffic to internal services | +| Catalog Manager | Serves the service catalog and resource type registry | +| Request Processor | Assembles layers, resolves dependencies, builds requested state | +| Policy Engine | Evaluates all matching policies against the request payload | +| Placement Engine | Scores and selects providers for fulfillment | +| Request Orchestrator | Dispatches to providers, manages async callbacks, handles retries | +| Audit Service | Records tamper-evident audit trail with Merkle tree | +| Discovery Service | Polls providers for current state, detects drift | +| Provider Manager | Manages provider registration, health monitoring, sovereignty declarations | + +## Consequences + +- All external traffic goes through one endpoint — simplifies network policy and TLS +- Services communicate internally via direct calls or PostgreSQL LISTEN/NOTIFY +- Each service has its own health endpoint and can be scaled independently +- The pipeline is deterministic: assembly → policy → placement → dispatch → callback diff --git a/content/docs/architecture/adr/010-audit-tamper-evidence.md b/content/docs/architecture/adr/010-audit-tamper-evidence.md new file mode 100644 index 0000000..83ffe2a --- /dev/null +++ b/content/docs/architecture/adr/010-audit-tamper-evidence.md @@ -0,0 +1,31 @@ +# ADR-010: Why Tamper-Evident Audit and How It Works + +**Status:** Accepted +**Date:** April 2026 +**Docs:** Doc 16 (Universal Audit) + +## Context + +Regulated industries (financial services, government, healthcare) require provable audit trails. "We logged it" is insufficient — auditors need mathematical proof that records haven't been modified or deleted after the fact. This is a hard requirement for sovereign cloud deployments. + +## Decision + +DCM uses a **Merkle tree** audit model (RFC 9162 — the same pattern used in Certificate Transparency): + +- Every pipeline stage produces a signed audit record (Ed25519 signature) +- Records are leaves in a Merkle tree — a binary hash tree where modifying any leaf changes the root hash +- **Inclusion proofs** prove a specific record exists in the tree +- **Consistency proofs** prove the tree has only grown (no deletions) +- **Signed tree heads** provide non-repudiation by the DCM instance + +**Configurable granularity** because not every deployment needs the same detail: +- **Stage** (~6 leaves/request): one leaf per pipeline stage — sufficient for dev/homelab +- **Mutation** (~15-30 leaves/request): one leaf per field change — standard for production +- **Field** (mutation + per-field hashes): required for FedRAMP/sovereign deployments + +## Consequences + +- Any modification to audit records is mathematically detectable +- Auditors can independently verify the audit trail without trusting DCM +- Granularity is profile-governed — organizations choose their audit depth +- Three SQL tables support the model: audit_records, signed_tree_heads, merkle_tree_nodes diff --git a/content/docs/architecture/adr/011-sovereignty-data-residency.md b/content/docs/architecture/adr/011-sovereignty-data-residency.md new file mode 100644 index 0000000..b48d052 --- /dev/null +++ b/content/docs/architecture/adr/011-sovereignty-data-residency.md @@ -0,0 +1,28 @@ +# ADR-011: Why Sovereignty Is a First-Class Concept + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 14 (Profiles), Doc B §18 (Overrides), Doc 26 (Governance Matrix) + +## Context + +Organizations operating in regulated industries or across jurisdictions face data residency requirements: EU data must stay in EU, classified data must stay on approved infrastructure, healthcare data must meet HIPAA locality requirements. Public clouds handle this with regions. On-premises infrastructure has no equivalent enforcement mechanism. + +## Decision + +Sovereignty is enforced at three levels: + +1. **Provider declaration** — Every provider declares its sovereignty zones and data residency scope at registration. This is not self-reported trust — it's validated against the accreditation model. + +2. **Policy enforcement** — Sovereignty policies are GateKeeper policies with hard enforcement. They fire on every lifecycle operation (not just initial provisioning). A resource in EU-WEST stays in EU-WEST for its entire lifecycle, including updates, scaling, and rehydration. + +3. **Placement pre-filter** — The placement engine eliminates non-compliant providers before scoring begins. Sovereignty is a hard gate, not a soft preference. + +**Override governance:** Sovereignty policies can be overridden, but only through dual-approval (two approvers from different roles). Every override is audited at field granularity. + +## Consequences + +- Sovereignty violations are caught at request time, not after deployment +- Cross-zone data movement is impossible without explicit, audited override +- Rehydration (disaster recovery) respects current sovereignty policies — rebuilding in a non-compliant zone is blocked +- Profiles (minimal, standard, fsi, sovereign) set sovereignty enforcement minimums diff --git a/content/docs/architecture/adr/012-data-assembly-layering.md b/content/docs/architecture/adr/012-data-assembly-layering.md new file mode 100644 index 0000000..467d517 --- /dev/null +++ b/content/docs/architecture/adr/012-data-assembly-layering.md @@ -0,0 +1,29 @@ +# ADR-012: How Organizational Data Merges with Consumer Requests + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 03 (Layering and Versioning) + +## Context + +When a consumer requests a VM with 4 CPUs, the provisioning system needs much more information: which datacenter, which network, what monitoring agent, what backup policy, what compliance requirements apply. This organizational data shouldn't be the consumer's responsibility — they just want a VM. + +## Decision + +**Data Layers** carry organizational context that gets merged into every request: + +- **System layers** — Datacenter configurations, environment defaults, compliance requirements +- **Tenant layers** — Organization-specific overrides (monitoring agents, naming conventions) +- **Provider layers** — Provider-specific defaults (image mappings, flavor resolution) +- **Consumer intent** — What the consumer actually asked for + +Layers merge in precedence order (system → tenant → provider → consumer). Consumer values override layer defaults. Every field in the merged payload carries **provenance** — where the value came from and what modified it. + +**Layers are Data, not Logic.** Layers provide values. Policies provide decisions. A layer says "the datacenter is EU-WEST-DC1." A policy says "EU-WEST resources must use the EU-WEST monitoring endpoint." This separation means layers can be managed by infrastructure teams while policies are managed by security/governance teams. + +## Consequences + +- Consumers declare only what they need — organizational data is injected automatically +- Adding a new datacenter or changing a monitoring agent is a layer change, not a code change +- Provenance on every field answers "why does this VM have this backup policy?" +- Layer conflicts are resolved deterministically by precedence order diff --git a/content/docs/architecture/adr/013-override-exception-governance.md b/content/docs/architecture/adr/013-override-exception-governance.md new file mode 100644 index 0000000..9922d43 --- /dev/null +++ b/content/docs/architecture/adr/013-override-exception-governance.md @@ -0,0 +1,28 @@ +# ADR-013: How to Handle Legitimate Exceptions Without Undermining Governance + +**Status:** Accepted +**Date:** April 2026 +**Docs:** Doc B §18 (Override Model) + +## Context + +Policies will block legitimate requests. A data residency policy may block a valid exception for a disaster recovery scenario. A sizing policy may block a temporary capacity burst for a product launch. If the only options are "change the policy" or "work around the system," governance degrades. + +## Decision + +Five override mechanisms, layered from least to most disruptive: + +1. **Override Policy** — A planned exception registered in advance (e.g., "DR events may use US-EAST zone") +2. **Exception Grant** — A pre-authorized waiver with compensating controls and expiry +3. **Manual Override** — Immediate single-request authorization with written justification +4. **Compensating Control** — Replace a blocked requirement with an equivalent risk-reduction measure +5. **Dual-Approval** — Required modifier for hard-enforcement policies (two approvers, different roles) + +**The consumer experience:** When a policy blocks a request, the consumer sees the blocking reason, compliant value suggestions, and four options: modify the request, request an override, cancel, or escalate. Override is one path among four — not the default. + +## Consequences + +- Every override is audited with full Merkle tree leaf +- Frequently-overridden policies are surfaced in metrics for policy review +- Block timeout auto-cancels requests where the consumer takes no action +- The governance model is flexible without being permissive diff --git a/content/docs/architecture/adr/014-multi-tenancy-isolation.md b/content/docs/architecture/adr/014-multi-tenancy-isolation.md new file mode 100644 index 0000000..32c0af0 --- /dev/null +++ b/content/docs/architecture/adr/014-multi-tenancy-isolation.md @@ -0,0 +1,24 @@ +# ADR-014: How Tenants Are Separated + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 11 (Data Store Contracts), Doc 15 (Universal Groups) + +## Context + +DCM serves multiple teams (tenants) within an organization. Each tenant's data, resources, policies, and audit trails must be isolated. A developer on Team A must not see Team B's resources, and Team A's policies must not affect Team B's requests (unless they're system-level policies that apply to everyone). + +## Decision + +**Row-Level Security (RLS)** in PostgreSQL enforces tenant isolation at the database layer. Every query is automatically scoped to the actor's tenant — application code cannot accidentally leak cross-tenant data. + +Tenants are **DCMGroups** with type `tenant_boundary`. Groups can be nested (organization → department → team) and support the universal group model for flexible organizational mapping. + +**Policy domain precedence** respects tenancy: system > platform > tenant > resource_type > entity. A system-level sizing policy applies to all tenants. A tenant-level naming convention applies only to that tenant. + +## Consequences + +- Tenant isolation is enforced by the database, not application logic — defense in depth +- Cross-tenant operations (ownership transfer, shared resources) require explicit policy authorization +- RLS adds a small query overhead (~2-5%) — acceptable for the security guarantee +- 18 SQL tables all include tenant_uuid columns with RLS policies diff --git a/content/docs/architecture/adr/015-minimal-infrastructure.md b/content/docs/architecture/adr/015-minimal-infrastructure.md new file mode 100644 index 0000000..a6f3d45 --- /dev/null +++ b/content/docs/architecture/adr/015-minimal-infrastructure.md @@ -0,0 +1,30 @@ +# ADR-015: Why PostgreSQL Is the Only Required Dependency + +**Status:** Accepted +**Date:** March 2026 +**Docs:** Doc 11 (Data Store Contracts), Doc 17 (Deployment) + +## Context + +Infrastructure management platforms often require heavy middleware stacks: message brokers, secret managers, identity providers, search engines. This creates a bootstrap problem — you need significant infrastructure just to manage infrastructure. It also blocks adoption in resource-constrained environments (homelab, edge, evaluation). + +## Decision + +PostgreSQL is the only required dependency. DCM implements internal equivalents for every capability that optional services provide: + +| Capability | Internal (default) | External (optional) | +|-----------|-------------------|-------------------| +| Events | PostgreSQL LISTEN/NOTIFY | Kafka | +| Secrets | Envelope-encrypted table | Vault | +| Auth | Built-in bcrypt + JWT | Keycloak/OIDC | +| Search | PostgreSQL full-text + GIN | OpenSearch | +| Notifications | PostgreSQL LISTEN/NOTIFY + webhooks | External notification service | + +Every optional dependency follows the same pattern: internal by default, externally delegable by configuration. The same API surface is exposed regardless of which implementation is active. + +## Consequences + +- Bootstrap is `docker-compose up` with one PostgreSQL container +- Production deployments can delegate to Kafka, Vault, Keycloak when scale or policy requires it +- Internal implementations have performance ceilings (LISTEN/NOTIFY: ~1K events/sec vs Kafka: millions) +- Every new cross-cutting service must implement the internal path first diff --git a/content/docs/architecture/adr/016-application-definition-language.md b/content/docs/architecture/adr/016-application-definition-language.md new file mode 100644 index 0000000..39365e1 --- /dev/null +++ b/content/docs/architecture/adr/016-application-definition-language.md @@ -0,0 +1,114 @@ +# ADR-016: Application Definition Language + +**Status:** OPEN — Design Decision Required +**Date:** April 2026 +**Raised by:** Ondra (machacekondra), repeatedly + +## Context + +DCM currently has two consumer interfaces: + +1. **Single resource** — A JSON payload to the Consumer API: `POST /api/v1/requests { "catalog_item_uuid": "...", "fields": {...} }` +2. **Compound service** — A compound resource type spec that defines constituent resources, dependencies, and binding fields in YAML + +The single-resource API works well for atomic resources. The compound service definition (compound service model) works for platform engineers who define reusable application templates. But there is a gap: + +**How does a consumer define a custom application?** Not a pre-defined catalog item, but an ad-hoc composition: "I need a database, two app servers, and a load balancer, and here's how they connect." Today, this requires a platform engineer to create a compound resource type spec first. + +Comparable projects have made different choices: +- **Radius** uses Bicep (a DSL) for application definitions, with Recipes (Terraform/Bicep templates) for infrastructure implementation +- **KRO** uses ResourceGraphDefinitions with CEL expressions, generating CRDs from the definition +- **Crossplane** uses Compositions with embedded resource templates and patch sets + +## The Question + +What is DCM's application definition language? Options to evaluate: + +### Option A: API-Only (Current State) +Consumers submit JSON payloads. Compound services require pre-defined compound resource type specs. Platform engineers author specs; consumers consume them. + +**Pros:** Simple, API-first, no custom language to learn +**Cons:** No self-service composition. Every new application pattern requires a platform engineer. + +### Option B: YAML Application Manifests +A YAML document defining resources, dependencies, and binding fields — similar to the compound resource type spec but authored by consumers, not platform engineers. + +```yaml +apiVersion: dcm.io/v1 +kind: Application +metadata: + name: pet-clinic +spec: + resources: + - name: database + type: Database.PostgreSQL + fields: { engine: postgresql, storage_gb: 50 } + - name: backend + type: Compute.VirtualMachine + depends_on: [database] + bindings: + - from: database.ip_address + to: config.db_host + fields: { cpu_count: 4, memory_gb: 8 } + - name: frontend + type: Compute.VirtualMachine + depends_on: [backend] + bindings: + - from: backend.ip_address + to: config.api_host + fields: { cpu_count: 2, memory_gb: 4, replicas: 2 } +``` + +**Pros:** Declarative, GitOps-friendly, reviewable, versionable +**Cons:** New format to learn. Validation complexity. How does this interact with the service catalog? + +### Option C: Reference Existing DSL (Bicep, CEL, HCL) +Adopt an existing language like Radius does with Bicep or KRO does with CEL. Leverage existing tooling and developer familiarity. + +**Pros:** Existing tooling, IDE support, community +**Cons:** Tight coupling to an external project. Bicep is Azure-originated. CEL is K8s-specific. HCL is HashiCorp-specific. + +### Option D: Catalog Composition via API +Consumers compose applications by linking multiple catalog requests through the API, declaring dependencies between them. No new language — just structured API calls. + +```json +POST /api/v1/applications +{ + "name": "pet-clinic", + "components": [ + { "name": "database", "catalog_item_uuid": "pg-standard", "fields": {...} }, + { "name": "backend", "catalog_item_uuid": "vm-standard", "fields": {...}, + "depends_on": ["database"], + "bindings": [{ "from": "database.ip_address", "to": "config.db_host" }] } + ] +} +``` + +**Pros:** API-first, no DSL, consistent with existing patterns +**Cons:** JSON is verbose for complex compositions. Not as readable/reviewable as YAML. No GitOps-friendly file format. + +## Evaluation Criteria + +1. **Consumer UX** — How easy is it for a developer to define a three-tier app? +2. **Platform engineer UX** — How easy is it to create reusable templates? +3. **GitOps compatibility** — Can definitions be stored in Git and applied via PR? +4. **Validation** — Can DCM validate the definition before execution? +5. **Existing tooling** — Does it work with existing editors, linters, CI pipelines? +6. **Consistency with DCM patterns** — Does it align with the API-first, JSON, snake_case conventions? + +## Recommendation + +This decision needs team input. The author's preliminary assessment: + +**Option B (YAML manifests) or Option D (API composition) are most aligned** with DCM's existing patterns. Option B is better for GitOps. Option D is better for API-first consistency. They could coexist — the YAML manifest could be a file format that the API endpoint accepts. + +**Option C (external DSL) is least aligned** — it introduces a dependency on an external project's language and tooling, which conflicts with DCM's technology-agnostic principle. + +**Regardless of choice, the compound resource type spec remains the implementation mechanism.** The application definition language is a consumer-facing UX that ultimately produces a compound resource type spec (or equivalent) for execution. + +## Actions Required + +- [ ] Team discussion to evaluate options +- [ ] Prototype consumer UX for three-tier app with top 2 options +- [ ] Evaluate interaction with RHDH (Backstage) scaffolding templates +- [ ] Decision by [date TBD] diff --git a/content/docs/architecture/adr/README.md b/content/docs/architecture/adr/README.md new file mode 100644 index 0000000..2bf1325 --- /dev/null +++ b/content/docs/architecture/adr/README.md @@ -0,0 +1,24 @@ +# Architecture Decision Records + +Short, reviewable summaries of the major architectural decisions in DCM. Each ADR answers **"Why does this exist and what does it do?"** — not implementation details. + +**Reading order:** ADRs 001-003 establish the foundations. Read those first, then jump to whichever ADRs are relevant to your area. + +| ADR | Decision | One-Line Summary | +|-----|----------|-----------------| +| [001](001-why-dcm-exists.md) | Why DCM Exists | Unified management plane for on-prem infrastructure — the governance layer above provisioning tools | +| [002](002-three-abstractions.md) | Three Foundational Abstractions | Everything in DCM is Data, Provider, or Policy — no exceptions | +| [003](003-four-lifecycle-states.md) | Four Lifecycle States | Intent → Requested → Realized → Discovered — immutable states linked by entity_uuid | +| [004](004-service-catalog-consumer-experience.md) | Service Catalog & Consumer UX | Four-level hierarchy from resource types to catalog items; consumers declare what, not how | +| [005](005-provider-abstraction.md) | Provider Abstraction | 5 provider types with naturalization/denaturalization; any platform, same interface | +| [006](006-policy-engine.md) | Policy Engine | Policy-as-code on every request; 8 policy types from gatekeeping to orchestration flow | +| [007](007-placement-engine.md) | Placement Engine | Multi-stage scoring: sovereignty pre-filter → capability → capacity → policy scoring | +| [008](008-dependency-resolution.md) | Dependency Resolution | Type-level dependencies trigger automatic sub-requests; binding fields inject runtime values | +| [009](009-api-gateway-control-plane.md) | API Gateway & Control Plane | Single entry point routing to 9 internal services; deterministic pipeline | +| [010](010-audit-tamper-evidence.md) | Audit & Tamper Evidence | Merkle tree (RFC 9162) with configurable granularity; mathematically provable integrity | +| [011](011-sovereignty-data-residency.md) | Sovereignty & Data Residency | First-class enforcement on every lifecycle operation; dual-approval for overrides | +| [012](012-data-assembly-layering.md) | Data Assembly & Layering | Organizational data merges with consumer requests; field-level provenance on everything | +| [013](013-override-exception-governance.md) | Override & Exception Governance | 5 mechanisms from planned exceptions to dual-approval; governance with flexibility | +| [014](014-multi-tenancy-isolation.md) | Multi-Tenancy & Isolation | PostgreSQL RLS enforces tenant isolation at the database layer | +| [015](015-minimal-infrastructure.md) | Minimal Infrastructure | PostgreSQL is the only required dependency; everything else is optional | +| [016](016-application-definition-language.md) | Application Definition Language | **OPEN** — How should consumers define multi-resource applications? Options under evaluation | diff --git a/content/docs/architecture/ai-prompt.md b/content/docs/architecture/ai-prompt.md new file mode 100644 index 0000000..f03cf9a --- /dev/null +++ b/content/docs/architecture/ai-prompt.md @@ -0,0 +1,5895 @@ +## SECTION 0 — THE THREE FOUNDATIONAL ABSTRACTIONS (READ FIRST) + +DCM is built on three foundational abstractions. Every concept maps to one or more of these three. There is no fourth. + +### DATA — Everything That Exists +Any structured artifact with a type, UUID, lifecycle state, fields, data classification, and provenance. Entities, layers, policies, accreditations, audit records, groups, relationships, sovereignty zones, registration tokens — all Data. + +**Universal Data properties:** UUID (stable across full lifecycle) · typed · lifecycle state · artifact metadata (handle, version, status, owned_by) · field-level provenance · data_classification per field · immutable if versioned + +**Data lifecycle stages (four states):** Intent State (consumer declaration) → Requested State (assembled, policy-validated) → Realized State (provider-confirmed) → Discovered State (independently observed). These are the same entity at four lifecycle stages stored in different stores optimized for each access pattern. + +**Data is assembled via layers** in deterministic precedence order. Every field carries provenance of its origin and all modifications. + +### PROVIDER — Everything External +Any external component DCM calls or that calls DCM. All providers implement the **unified base contract** (registration, health, sovereignty, accreditation, governance matrix enforcement, zero trust) plus a **typed capability extension** that declares what operations they expose. + +**Five provider types (all implement the same base contract):** +Service Provider (realize resources — including Credential.*, Notification.*, ITSM.* resource types) · Information Provider (serve authoritative external data) · Auth Provider (authenticate identities) · Peer DCM (federation — another DCM instance IS a typed provider) · Process Provider (execute workflows without producing resources) + +**Adding a new provider type** = implement base contract + define capability extension. No core changes. + +### POLICY — Everything That Decides +Any rule artifact that fires when Data matches conditions, produces a typed output, and is enforced at a declared level. Policies govern every transition, transformation, and constraint. + +**Eight policy types (all implement the same base contract):** +GateKeeper (allow/deny) · Validation (pass/fail) · Transformation (field mutations) · Recovery (failure actions) · Orchestration Flow (pipeline ordering) · Governance Matrix Rule (boundary control) · Lifecycle Policy (relationship event actions) · ITSM Action (side-effect ITSM record creation) + +**Policies ARE the orchestration.** Pipeline steps are policies firing on payload type events. Static flows = Orchestration Flow policies with `ordered: true`. Dynamic flows = conditional policies. Adding/removing pipeline steps = adding/removing policies. + +**Adding a new policy type** = define a new output schema. Base contract inherited automatically. + +### THE RUNTIME — Connecting the Three +``` +Event (Data state change) + → Policy Engine evaluates all matching Policies + → Policies produce decisions / mutations / actions + → Actions invoke Providers or produce new Data + → New Data triggers new Events + → Repeat +``` + +Control plane "components" are runtime specializations — not a fourth abstraction: +- Request Orchestrator = event bus (runtime) +- Policy Engine = policy evaluator (runtime) +- Placement Engine = GateKeeper policy specialized for provider selection +- Cost Analysis = Information Provider (internal; data derivation) +- Lifecycle Constraint Enforcer = scheduled Recovery Policy trigger +- Discovery Scheduler = scheduled Provider invocation +- Notification Router = Transformation Policy + notification service invocation +- Drift Reconciliation = Data comparison producing Drift Record artifacts +- Search Index = PostgreSQL store contract (queryable projection) + +### THE CORE ETHOS +Effective at the core mission · Easy to use · Easy to implement · Easy to extend and integrate + +**Foundation documents:** 00-foundations.md (three abstractions) · A-provider-contract.md (unified provider base) · B-policy-contract.md (unified policy base) + +--- + +# DCM Project — AI Model Prompt Script + +**Purpose:** This script provides an AI model with the full context needed to participate effectively in DCM project work. It should be provided at the start of any AI-assisted session involving DCM architecture, documentation, code, or design work. + +**Usage:** Paste this document into the AI model's context at the start of a session. Follow with the specific task or question. + +**Maintainers:** Update this document whenever significant architectural decisions are made, new concepts are established, or open questions are resolved. + +**Last Updated:** 2026-03 +**Status:** Architecture complete — 0 unresolved questions — Ready for implementation + +**Note on section structure:** This prompt was built cumulatively across multiple design sessions. Sections 0–57 establish the architecture. Sections 58+ record subsequent additions and refinements. Capability counts, path counts, and domain counts in earlier sections reflect the state at the time that section was written. The authoritative current counts are: **331 capabilities across 39 domains · 58 data model docs · 15 specifications · 16 ADRs · 74 consumer API paths · 61 admin API paths · 109 event payloads across 23 domains · 5 provider types · 2 policy evaluation modes · 9 control plane services · 104 prompt sections.** When earlier sections conflict with later sections, the later section is authoritative. **Infrastructure (doc 51):** 5 provider types: service_provider, information_provider, auth_provider, peer_dcm, process_provider. Credentials and notifications are service_provider resource types. 2 policy evaluation modes: Internal (DCM evaluates via OPA) and External (external provider evaluates). Four data domains (Intent, Requested, Realized, Discovered) in a single PostgreSQL-compatible database. One required infrastructure: PostgreSQL-compatible DB. Authentication (local accounts + JWT), secrets (envelope encryption), and event routing (LISTEN/NOTIFY) are handled internally. OIDC IdP, Vault, Kafka, Redis, Git are optional deployment enhancements. 9 control plane services. + +--- + +## SECTION 0b — DESIGN PRIORITY ORDER (applies to all decisions) + +> **Full specification:** [00-design-priorities.md](data-model/00-design-priorities.md) — includes decision framework, profile scaling table, and DPO-001–006 system policies. + +**Priority 1 — Security (industry best practices):** Security properties are architecturally present in ALL profiles. What profiles control is enforcement strictness, threshold values, and automation level — not whether security applies. A `minimal` profile is "security with minimal operational overhead" — not "minimal security." + +**Priority 2 — Ease of use:** The secure path must be the easy path. If the right path is also the hard path, teams will find other paths. Auto-approval for ordinary requests, policy authoring without Rego expertise, and profile defaults that eliminate configuration burden all serve this priority. + +**Priority 3 — Extensibility/grouping:** Profile system, compliance domain overlays, policy groups, and registry governance enable adaptability through configuration, not code. New compliance requirements are policy additions. New deployment contexts are profile configurations. + +**Priority 4 — Fit for purpose (always required):** DCM must manage data center infrastructure lifecycle end-to-end. Everything above serves this purpose. A system that cannot provision, track, and decommission a VM has failed. + +**Implication for all design decisions:** When security and convenience conflict, security wins — but find a way to make the secure option easy. When extensibility and fit for purpose conflict, fit for purpose wins. When a profile tempts you to disable a security property rather than raise its threshold, the priority order says: keep the property, raise the threshold. + +--- + +## SECTION 1 — PROJECT IDENTITY + +You are assisting with the **DCM (Data Center Management)** project, an open-source strategic framework developed by the Red Hat FlightPath team developed by the Red Hat FlightPath Team. + +**Key facts:** +- DCM is NOT a provisioning tool — it is a **governing framework** +- DCM is a **top-down orchestration and policy enforcement layer** +- DCM enables enterprises to achieve a "Private Cloud" / "Sovereign Cloud" experience with public cloud efficiencies on-premises +- DCM is **API-first, data-driven, and policy-governed** +- DCM is inspired by Kubernetes' declarative control plane model +- GitHub: https://github.com/dcm-project +- Website: https://dcm-project.github.io + +**Authors:** Chris Roadfeldt (Principal Architect), Ryan Goodson (Senior Principal Architect), Adam Seeley (Global Director) — Red Hat FlightPath. + +**Vision:** To make life better for all customers, internal teams, and future-looking entities. + +**Mission:** Seamlessly manage the complete lifecycle of all data center infrastructure by providing a policy-governed, data-driven, and unified platform to enable and ensure sovereignty. + +--- + +## SECTION 2 — THE PROBLEM DCM SOLVES + +Organizations face these core challenges that DCM addresses: + +1. **Fragmented Operations** — disparate tools, no unified management, functionality trapped in monoliths +2. **No Single Source of Truth** — multiple CMDBs diverge, no trustworthy representation of infrastructure state +3. **High Time-to-Market** — a single VM lifecycle may be managed by dozens of teams; firewall changes can take weeks +4. **The Private Cloud Gap** — "Private Cloud" ≠ "On-Premises Compute"; a true private cloud requires networking, storage, identity, catalog, FinOps, observability, auditing, and risk management +5. **Drift and State Discrepancy** — no reconciliation between discovered inventory and intended inventory +6. **Sovereignty Requirements** — organizations must enforce data residency, compliance, and operational control across their infrastructure + +**The definitional clarification DCM makes:** +- Private Cloud ≠ On-Premises Compute +- Private Cloud > On-Premises IaaS +- Private Cloud = Hyperscale experience for on-premises infrastructure + +--- + +## SECTION 3 — FOUNDATIONAL PRINCIPLES + +These three constraints apply to ALL data, entities, and operations in DCM **universally and without exception:** + +### 3.1 Declarative +Data describes **what something is or should be**, not how to achieve it. Every entity is a complete, self-describing statement of state. The procedures required to achieve that state are the concern of the Service Provider, not the data model. + +### 3.2 Idempotent in Operation +Applying the same data to the same system multiple times must always produce the same result. No operation on DCM data should have different outcomes based on how many times it has been applied. + +### 3.3 Immutable if Versioned +Once a version of any entity is published, it cannot be modified. Changes produce a new version. Previous versions remain intact and accessible forever. + +--- + +## SECTION 4 — THE DCM DATA MODEL + +**The data model is the most foundational element of DCM.** Everything in DCM acts on data in some way — reading, validating, triggering, enriching, gatekeeping, parsing, transforming, or comparing. The data model is the lingua franca — the API between all components. + +### 4.1 Universal Identity Requirement +Every data object in DCM **must have a UUID**. This applies without exception to: resource definitions, catalog items, data layers, policies, policy sets, components, service providers, consumers, requests, and all other entities. UUIDs are used for provenance anchoring, dependency mapping, audit fidelity, and cross-state correlation. + +### 4.2 The Four States +DCM tracks every resource through four distinct states. Together they provide complete visibility into what was wanted, what was asked for, what was built, and what actually exists. + +| State | Description | Store | Created By | +|-------|-------------|-------|------------| +| **Intent** | What the consumer wants — raw declared desire before any processing | Intent Store | Consumer (Web UI / API) | +| **Requested** | Fully processed, policy-validated, enriched payload submitted to a provider | Request Store | Request Payload Processor | +| **Realized** | What was actually provisioned, returned by the provider in DCM unified format | Realized Store | Service Provider (via Denaturalization) | +| **Discovered** | What actually exists, as independently interrogated by a provider during discovery | Discovered Store | Service Provider (via Discovery) | + +**Key operations across states:** +- **Drift Detection:** Discovered State vs. Realized State +- **Request Validation:** Requested State vs. Policy definitions +- **Intent Portability:** Intent State → re-process through current policies → new Requested State +- **Brownfield Ingestion:** Discovered State → enrichment → Realized State (lifecycle ownership) + +**State lifecycle flow:** +``` +Consumer Request → INTENT → (Policy Engine) → REQUESTED → (Service Provider) → REALIZED + ↕ compare + DISCOVERED ────────► Drift Detection +``` + +### 4.3 Field-Level Provenance and Data Lineage +This is a **structural requirement** of the data model — not a logging concern. + +For any field, at any point in the pipeline, it must be possible to determine: +- What is the current value? +- Where did this value originate? (catalog item, layer, policy, consumer input, discovery) +- Has it been modified? If so — what changed it, which entity (by UUID), when, what was the previous value, and why? +- What is the complete chain of custody from origin to current value? + +**Provenance is carried within the data object itself** — co-located with the data, not in an external log. + +Every component that modifies data carries a **provenance obligation** — it must record its UUID, operation type, timestamp, and reason for every field it modifies. This is non-optional. + +Conceptual provenance structure per field: +```yaml +field_name: + value: + metadata: + override: + basis_for_value: + baseline_value: + locked_by_policy_uuid: + locked_at_level: + constraint_schema: + provenance: + origin: + value: + source_type: + source_uuid: + timestamp: + modifications: + - sequence: 1 + previous_value: + modified_value: + source_type: + source_uuid: + operation_type: + actor: + timestamp: + reason: +``` +The `metadata` block is set exclusively by the Policy Engine. `operation_type: lock` is used when a GateKeeper sets `override: immutable`. `operation_type: grant` is used when a trusted_grant is issued. + +### 4.4 Universal Versioning +All entities, definitions, and data objects in DCM follow one versioning scheme: **Major.Minor.Revision** + +| Component | Trigger | +|-----------|---------| +| **Major** | Breaking changes to the contract | +| **Minor** | Additive changes, backward compatible | +| **Revision** | Data/configuration changes, no contract impact | + +This applies universally to: resource types, layers, policies, catalog items, provider registrations, registry entries, and all other definitions. + +### 4.5 Universal Artifact Status Lifecycle +Every DCM artifact — layers, policies, resource types, catalog items, provider registrations — follows a five-status lifecycle: + +| Status | Meaning | Applied? | Shadow? | +|--------|---------|---------|---------| +| `developing` | In development | No — dev mode only | No | +| `proposed` | Submitted for review/validation | No | Yes — policies shadow-execute against real traffic, output captured not applied | +| `active` | Live and governing | Yes | Yes — audit records | +| `deprecated` | Being phased out, replacement available | Yes — with warning | Yes | +| `retired` | End of life | No | No — terminal | + +**Status transitions:** `developing → proposed → active → deprecated → retired` +Deprecated artifacts must include: replacement UUID, deprecation reason, migration guidance, sunset date. + +**Previously:** The model had only `active → deprecated → retired`. The `developing` and `proposed` statuses were added to support the full artifact development workflow — especially the shadow execution validation model for policies. + +### 4.6 Artifact Metadata Standard +Every DCM artifact carries a universal metadata block — applies to all artifacts without exception: +- `uuid` — immutable, DCM-assigned at creation +- `handle` — human-readable stable ID: `{domain}/{layer_type}/{name}` e.g. `platform/core/security-cpu-limits` +- `version` — Major.Minor.Revision +- `status` — five-status lifecycle (Section 4.5) +- `created_by` — audit record (who submitted): UUID optional + display_name required +- `owned_by` — accountability record (who is responsible, receives notifications): UUID optional + display_name required +- `created_via` — ingestion path: `pr | api | migration | system` +- `modifications` — append-only history of all changes with who/when/why + +**Contact modes:** UUID+display_name when Identity Provider registered; display_name+email standalone/air-gapped. Both supported. + +### 4.7 Provider Types +All five provider types follow the same base contract (registration, health check, trust, sovereignty, accreditation, governance matrix enforcement, zero trust, provenance emission). See Section 0 for the complete list. The four original categories and their data ownership model: + +| Type | Purpose | DCM Owns Result? | +|------|---------|-----------------| +| **Service Provider** | Realizes resources — KubeVirt, VMware, AAP, Terraform | Yes | +| **Information Provider** | Serves authoritative external data DCM references but does not own | No — external system is authoritative | +| **Meta Provider** | Composes multiple providers into higher-order services | Yes | + + +The five provider types (service_provider, information_provider, auth_provider, peer_dcm, process_provider) all implement the same base contract. Capabilities that were formerly separate provider types (credentials, notifications, ITSM, message bus, storage, policy evaluation, registry) are now either internal to DCM or handled by service_providers with specialized resource types. See [A-provider-contract.md](data-model/A-provider-contract.md) for the unified contract. + +--- + +## SECTION 5 — THE FOUR STATES AND STORAGE MODEL + +### 5.1 The Four States + +Every DCM entity exists across four independently maintained state records: + +| State | Question | Store Type | Characteristics | +|-------|----------|------------|----------------| +| **Intent State** | What did the consumer ask for? | GitOps | Immutable, branched, PR reviewed, CI/CD triggered | +| **Requested State** | What was approved and dispatched? | GitOps | Immutable, committed, CD triggered, full provenance | +| **Realized State** | What did the provider actually build? | Event Stream | Append-only, entity-keyed stream, high-frequency | +| **Discovered State** | What actually exists right now? | Event Stream (ephemeral) | Machine-generated, drift detection source | + +### 5.2 Storage Architecture — Contract, Not Implementation + +DCM defines store **contracts** — what capabilities, guarantees, and obligations each store must satisfy. Implementation technology is a deployment choice. PostgreSQL is the single required infrastructure. + +``` +GitOps Stores: Intent, Requested, Layers, Policies +Event Stream Stores: Realized, Discovered, Audit events +Search Index: Queryable projection of GitOps stores (non-authoritative) +Audit Store: Compliance-grade, immutable, long-retention +Observability Store: Time-series metrics, traces, logs +``` + +### 5.3 The Entity UUID — Universal Key +The entity UUID is assigned at Intent State creation and links the entity across all four states and all stores. Given an entity UUID, DCM can reconstruct the complete lifecycle history from any store. + +### 5.4 CI/CD Integration +GitOps stores are the natural CI/CD integration point: +- **CI pipeline** fires on branch/update: policy pre-validation (dry run), cost estimation, sovereignty check, auto-approve evaluation — results posted as PR comments +- **CD pipeline** fires on merge: layer assembly, full policy evaluation (binding), provider dispatch +- **Third rail — direct API ingress**: bypasses PR workflow, not governance. Same pipeline, no human review unless required by policy. + +### 5.5 Rehydration — Three Sources, Six Modes + +Rehydration uses a prior state record as the starting point for a new request. Governance always applies — rehydration is a new request, not a shortcut. + +**Three sources:** Intent Store (full assembly runs), Requested Store (assembly skipped, governance runs), Realized Store (provider fields stripped, assembly skipped, governance runs) + +**Two axes — six modes:** + +| Mode | re_evaluate | policy_version | Use Case | +|------|-------------|----------------|----------| +| Faithful | false | current | Same provider, current governance | +| Provider-Portable | true | current | New provider, current governance | +| Historical Exact | false | pinned | Same provider, historical governance (audit evidence) | +| Historical Portable | true | pinned | New provider, historical governance | + +**Placement flag on Requested/Realized rehydration:** +```yaml +placement: + re_evaluate: false # Honor original provider selection (default) + # re_evaluate: true # Strip provider, run placement policies fresh +governance: + apply_all_policies: true # Always true — never skippable + policy_version: current # current | pinned (pinned requires elevated auth) +``` + +**Named concept:** Provider-Portable Rehydration — rehydration with provider selection re-evaluated through current placement policies and current provider landscape. + +**Partial Q54 resolution:** Policies set placement constraints. The placement component selects the specific provider within those constraints. Policies never name a specific provider — that would be portability-breaking. + +### 5.6 Audit, Provenance, and Observability — Three Distinct Concerns + +| Concern | What It Is | Audience | Retention | +|---------|-----------|----------|-----------| +| **Provenance** | Field-level data lineage embedded in every payload | System | Permanent — part of the data | +| **Audit** | Compliance-grade queryable record of all actions | Auditors, Compliance | Regulatory period (7+ years FSI) | +| **Observability** | Real-time metrics, traces, logs | SRE, Platform Engineers | Operational window (90 days) | + +Audit is a **separate component** fed by provenance events emitted by all store contracts (contractual obligation). Surfaced through the DCM API Gateway — not a separate endpoint. + +All DCM capabilities — catalog, requests, entities, policies, audit, observability — are surfaced through a **unified API Gateway hierarchy**. + +--- + +## SECTION 6 — DATA LAYERS AND THE ASSEMBLY PROCESS + +### 6.1 Layers vs Policies — The Clear Distinction + +**Layers are data.** They carry static configuration, defaults, metadata, and context assembled into the request payload. A layer answers: "what values should these fields have?" Layers are passive — they declare values but do not execute logic. They come first in assembly (Steps 1-4). + +**Policies are logic.** They evaluate the assembled payload and enforce rules, inject derived values, and make decisions. A policy answers: "given this data, is it valid? what should change? should this proceed?" Policies execute — they run code. They come after layers (Steps 5-9). + +**The flow is strictly unidirectional:** +``` +Steps 1-4: LAYERS assembled → merged payload produced (data) +Steps 5-9: POLICIES execute → payload evaluated and acted upon (logic) +``` + +**The decision rule:** Value that should appear in payload → Layer. Rule about whether payload is correct → Policy. Value derived by evaluating payload → Policy (Transformation type). + +**What belongs in layers:** infrastructure defaults, organizational context, service configuration defaults, compliance metadata, business context labels. + +**What belongs in policies:** validation rules, compliance enforcement, derived value injection, placement constraints, approval gates, security enforcement. + +A policy that repeatedly injects the same static value into every request → that value belongs in a layer. A layer that contains conditional logic → that logic belongs in a policy. + +### 6.2 Layer Domain Model (mirrors Policy domain) + +| Domain | Authority | Can Override | +|--------|----------|-------------| +| `system` | DCM built-in — highest | Nothing above system | +| `platform` | Platform team | tenant, service, provider | +| `tenant` | Tenant Admin | service, provider within Tenant | +| `service` | Service Provider | provider | +| `provider` | Provider owner | Nothing above provider | +| `request` | Consumer — lowest | Nothing above request | + +### 6.3 The Full Layer Structure + +Every layer carries: artifact_metadata (standard), domain + priority (authority), concern_tags (discoverability), compatibility (resource_types, versions, profile_constraints), activation_condition (Q23 — conditional inclusion), fields with per-field override metadata (override: allow/constrained/immutable, basis_for_value), and usage context (description, applies_when, excludes_when, conflicts_with). + +**activation_condition** — layer only included if condition evaluates true during Step 2. Conditions reference: request fields, tenant attributes, resource type fields, resolved core layer fields, ingress fields. Enables role-specific layers, GPU-only layers, PCI-scope-only layers. + +### 6.4 Layer Groups + +Layer Groups are `DCMGroup` with `group_class: layer_grouping` — cohesive collections of related layers. Same model as Policy Groups. Enables discovery ("show me all PCI compliance layers"), composition, and governance. + +### 6.5 Consumer Layer Exclusion (Q21) + +Consumers declare `layer_exclusions` with mandatory reason. Excluded layers removed in Step 2, produce no fields, cannot satisfy validation requirements. GateKeeper policies may declare layers non-excludable (LAY-001). + +### 6.6 Service Layer Versioning (Q22) + +Service Layers independently versioned. Providers declare semver compatibility constraints (`^1.0.0`, `~1.2`). Cache entries carry version — invalidated when registered version changes (LAY-002). + +### 6.7 Conditional Layer Inclusion (Q23) + +`activation_condition` on layer evaluated in Step 2. False → layer excluded. Conditions reference request, tenant, resource type, core layer, and ingress fields. Recorded in assembly provenance (LAY-003). + +### 6.8 Dependency Layer Chains (Q24) + +Each service dependency has its own independent layer chain. Inherits parent's resolved placement fields (read-only). Does NOT inherit parent consumer declarations or type-specific layers. Layer exclusions declarable per-dependency (LAY-004). + +### 6.9 The Nine-Step Assembly Process + +Step 1 (Intent Capture) → Step 2 (Layer Resolution — with exclusions and activation_conditions) → Step 3 (Layer Merge — priority ordering, field-level provenance) → Step 4 (Request Layer Application) → Step 5 (Pre-Placement Policies: Transformation → Validation → GateKeeper) → Step 6 (Placement Engine Loop: reserve query + loop policy phase per candidate) → Step 7 (Post-Placement Policies) → Step 8 (Requested State Storage) → Step 9 (Provider Dispatch) + +### 6.10 Layer System Policies +- `LAY-001` — Consumer layer exclusions with mandatory reason; GateKeeper can lock layers as non-excludable +- `LAY-002` — Service Layers independently versioned; semver compatibility on provider; cache invalidation on version change +- `LAY-003` — activation_condition on layers evaluated in Step 2; results recorded in provenance +- `LAY-004` — Each dependency has own layer chain; inherits parent resolved placement; no consumer declaration inheritance + +## SECTION 7 — RESOURCE TYPE HIERARCHY AND SERVICE CATALOG + +The Resource Type Hierarchy is how DCM achieves **resource portability** — expressing what a consumer needs independently of which specific provider delivers it. + +### 7.1 The DCM Resource Type Registry +- DCM maintains an official registry of standard Resource Types +- Registry is **open** — community and implementors can propose new types +- All registry entries are **vendor-neutral by hard requirement** +- Exception: `exclusive` classification where one provider is the sole implementor +- Registry entries are versioned, immutable once published, and can be deprecated + +### 7.2 Four Hierarchy Levels + +**Level 1 — Resource Type Category** (broadest) +Organizational container. Examples: `Compute`, `Network`, `Storage`, `Platform`, `Security`, `Observability`, `Data` + +**Level 2 — Resource Type** (abstract) +Defines a class of resource. Must be vendor-neutral. Examples: `Compute.VirtualMachine`, `Network.FirewallRule` + +**Level 3 — Resource Type Specification** (standard contract) +The data contract for a Resource Type — all fields with types, constraints, and portability classifications. + +**Level 4 — Provider Catalog Item** (concrete) +A specific provider's implementation of a Resource Type Specification. + +### 7.3 Portability Classification +Every field in every Resource Type Specification carries a portability classification: + +| Classification | Meaning | Portability | +|---|---|---| +| `universal` | All providers must support it | Fully portable | +| `conditional` | Some providers support it | Portable to supporting providers | +| `provider-specific` | One provider only | Portability-breaking — must be marked | +| `exclusive` | One provider for entire tech stack | Not applicable — declared | + +**Hard requirements:** +- All `universal` fields MUST be supported by ALL implementing providers +- `provider-specific` fields MUST be marked portability-breaking +- Consumers MUST be warned when their request contains portability-breaking fields +- Portability warning enforcement is **organizational policy**: `block` | `warn` | `allow` + +### 7.4 Inheritance +Resource Types support inheritance. Rules: +- Child inherits ALL parent fields — none can be removed or redefined +- Child may add new fields +- Child portability can only be equal to or more restrictive than parent +- Each level is independently versioned with parent UUID reference + +### 7.5 Request Resolution — Specificity Narrowing +Provider selection is **never explicit**. It emerges from progressive specificity: +``` +Resource Type declared → matches all providers for that type +Universal fields specified → still matches all providers +Conditional fields specified → narrows to supporting providers +Provider-specific fields used → narrows to single provider (portability warning issued) +Placement/sovereignty applied → final provider selected by Policy Engine +``` + +--- + +## SECTION 8 — RESOURCE/SERVICE ENTITIES + +### 8.1 Core Terminology + +| Term | Definition | +|------|-----------| +| **Resource/Service Request** | What a consumer submits to DCM — the declared intent to consume a resource or service. The consumer side of the transaction. | +| **Resource/Service Entity** | The "thing" produced by a provider as a result of fulfilling a request — the allocation made real. The provider side of the transaction. | + +### 8.2 DCM as Authoritative Owner — Always +DCM is ALWAYS the system of record for Resource/Service Entity data. DCM is ALWAYS authoritative for the resource definition. DCM ALWAYS owns the lifecycle. This applies regardless of the operational ownership model. + +Providers are **custodians** of the underlying infrastructure — they are not the system of record. + +### 8.3 Four Ownership Models + +| Model | Description | Example | +|-------|-------------|---------| +| **Allocation** | Provider retains internal ownership. Consumer owns the Entity (the allocation). Provider has reclaim rights on decommission. | VM, Container, IP Address | +| **Cost Analysis Information Provider** | Specialized Information Provider supplying cost estimates, placement cost signals, cost actuals, and budget alerts; DCM provides input data; provider performs calculations | +| **Orchestration Flow Policy** | Named workflow artifact: Orchestration Flow Policy with `ordered: true`; declares explicit step sequence using payload type vocabulary; first-class Data artifact; versioned, GitOps-managed, profile-bound | +| **Request Orchestrator** | Runtime event bus; routes lifecycle events to Policy Engine; has no pipeline logic; both named workflows and dynamic policies are evaluated through it | +| **orchestration (DCM)** | Two-level composable model: Level 1 = named Orchestration Flow Policies (explicit sequence); Level 2 = dynamic policies (conditional, inline); both evaluated by Policy Engine; adding a step = adding to a workflow Policy; adding conditional behavior = writing a dynamic policy | +| **Provider Catalog Item** | What a specific Service Provider offers consumers: specific resource allocation or process with cost, availability, SLAs; linked to Resource Type Specification version | +| **cross_tenant_authorization** | DCMGroup with group_class: cross_tenant_authorization; grants one Tenant permission to reference/allocate/stake another Tenant's resources; revocation places active allocations in PENDING_REVIEW | +| **foundation Tenants** | Three system Tenants created at bootstrap: __platform__, __transitional__, __system__; cannot be decommissioned; declared in bootstrap manifest | +| **QUOTA_EXCEEDED** | GateKeeper rejection code when resource quota policy fires at Step 5 (pre-placement) | +| **Federated Contribution Model** | DCM defaults to federated data creation — all authorized actor types (platform admin, consumer/tenant, service provider, peer DCM) can contribute Data artifacts within their domain scope via the GitOps PR model; see doc 28 | +| **contributor** | Actor type that authored a Data artifact; recorded in artifact_metadata.contributed_by; determines review requirements; platform_admin / consumer / service_provider / peer_dcm | +| **contributed_by** | Artifact metadata block recording contributor_type, actor UUID, contribution_method, pr_url, reviewed_by; immutable once set | +| **DPO-001–006** | Design Priority system policies. DPO-001: security properties present in all profiles (not controlled by profiles). DPO-002: every security requirement needs an ease-of-use mechanism. DPO-005: minimal profile = "security with minimal overhead" not "minimal security". DPO-006: when security and ease conflict, redesign ease-of-use, not security. | +| **FCM-001–008** | Federated Contribution Model system policies; key: FCM-002 (domain scope violations = hard DENY), FCM-003 (GitOps PR for all), FCM-008 (contributor scope limits absolute) | +| **Unified Governance Matrix** | Single enforcement point for all cross-boundary decisions; four axes (subject/data/target/context); hard vs soft enforcement; field-level granularity (allowlist/blocklist/paths); profile-bound defaults; GMX-001–010 | +| **governance_matrix_rule** | Artifact declaring match conditions across four axes and a decision (ALLOW/DENY/ALLOW_WITH_CONDITIONS/STRIP_FIELD/REDACT/AUDIT_ONLY) with hard or soft enforcement | +| **sovereignty_zone** | Registered DCM artifact declaring geopolitical/regulatory boundary; rules reference zones by ID; inter-zone agreements declared explicitly | +| **STRIP_FIELD** | Governance matrix decision: remove named fields from payload and proceed; if stripped field is required → DENY_REQUEST | +| **REDACT** | Governance matrix decision: replace field value with ``; field presence preserved; receiver knows field exists but not its value | +| **Provider Type Registry** | Three-tier registry of approved provider types; each entry declares permissions, default_approval_method, enabled_in_profiles, capability_schema_ref | +| **registration_token** | Pre-issued by platform admin; scoped to provider_type/handle_pattern/zone; single_use; grants_auto_approval flag; value presented once only | +| **approval_method** | Registration approval: auto | reviewed | verified | authorized; resolved as most_restrictive(provider_type_default, profile_min, token_effect) | +| **Drift Reconciliation Component** | Control plane component; compares Discovered vs Realized State; produces drift records and events; never writes to Realized Store; DRC-001–005 | +| **drift_record** | Artifact produced by Drift Reconciliation; field-by-field comparison result with severity classification; unsanctioned flag; status tracking through resolution | +| **Placement Engine** | Six-step algorithm: sovereignty filter → accreditation filter → capability filter → reserve query → tie-breaking (policy/priority/affinity/cost/load/hash) → confirm; PLC-001–006 | +| **reserve_query** | Parallel capacity queries to all eligible provider candidates; PT5M capacity hold; non-responders and insufficient-capacity providers excluded | +| **consistent hash** | Final placement tie-breaker: SHA-256(request_uuid+resource_type+sorted_candidates); deterministic; never round-robin | +| **Lifecycle Constraint Enforcer** | Monitors TTL/expiry/max_execution_time; fires expiry actions through standard pipeline; grace period before action; Process Resources: immediate FAILED on breach; LCE-001–005 | +| **Search Index** | Non-authoritative queryable projection of GitOps stores; indexes key fields; returns git_path for full payload; max staleness PT5M; always rebuildable; SIX-001–004 | +| **Admin API** | Platform admin REST interface: Tenant lifecycle, provider review, accreditation approval, discovery trigger, orphan resolution, recovery decisions, quota management, Search Index rebuild, bootstrap operations | +| **PENDING_EXPIRY_ACTION** | Entity state when expiry action fails to execute; Lifecycle Constraint Enforcer retries per Recovery Policy; Platform Admin notified urgency: high | +| **data_classification** | First-class field metadata: public/internal/confidential/restricted/phi/pci/sovereign/classified; phi/sovereign/classified are immutable once set | +| **Accreditation** | Formal versioned attestation that a component satisfies a compliance framework; issued by an Accreditor; carries validity period; lifecycle: developing→proposed→active→expired/revoked | +| **Accreditor** | Entity that issues accreditations: government body, regulatory body, QSA, certification body, or internal audit team | +| **Accreditation Gap** | Missing, expired, or revoked accreditation required for an active interaction; always high/critical severity; Recovery Policy governs response | +| **Data/Capability Authorization Matrix** | Policy Group artifact (concern_type: data_authorization_boundary) declaring what data fields and capabilities are permitted across interaction boundaries given data classification and accreditation level | +| **zero_trust_posture** | Sixth Policy Group concern type; four levels: none/boundary/full/hardware_attested; profile defaults: minimal=none, dev/standard=boundary, prod/fsi=full, sovereign=hardware_attested | +| **Five-check boundary model** | Identity → Authorization → Accreditation → Matrix → Sovereignty; all five checks at every DCM interaction boundary; all produce audit records | +| **Federation tunnel** | Mutually authenticated, encrypted, scoped DCM-to-DCM channel; zero trust model; establishes secure transport only, not implicit trust; per-message signing; scoped non-transferable credentials | +| **hard_constraint** | Data/Capability Matrix declaration that cannot be overridden by any policy; sovereign/classified data never crossing federation boundaries is a hard_constraint | +| **STRIP_FIELD** | Matrix enforcement action: remove non-permitted field from payload and proceed; if stripped field is required → escalates to DENY_REQUEST | +| **DENY_REQUEST** | Matrix enforcement action: block entire interaction; entity enters PENDING_REVIEW; notification dispatched | +| **Request Orchestrator** | DCM control plane event bus; routes lifecycle events to Policy Engine; coordinates pipeline via event-condition-action; does not contain hardcoded pipeline logic | +| **Cost Analysis Component** | Internal DCM control plane component; three functions: pre-request estimation, placement input, ongoing attribution; not a billing system; not a provider type | +| **Module** | DCM capability extension adding new functions; distinct from Profile (which configures behavior) | +| **orchestration_flow** | Policy Group concern_type for static sequential flows; ordered: true; both static and dynamic flows compose through the same Policy Engine | +| **payload_type** | Closed vocabulary of event types the Request Orchestrator publishes; policies pattern-match on payload type + state | +| **OPA integration** | Reference implementation for Mode 3 External Policy Evaluators; DCM payload as OPA input document; built-in Rego functions provided by DCM | +| **Flow GUI** | Visual policy composer and orchestration manager; execution graph view, policy canvas, shadow mode dashboard, flow simulation | +| **__platform__** | Immutable system Tenant owning DCM control plane resources; created at bootstrap before Policy Engine comes online | +| **__transitional__** | Immutable system Tenant holding brownfield entities during INGEST phase | +| **bootstrap manifest** | Signed manifest declaring initial system Tenants, bootstrap admin, and initial profile; hash-verified at every DCM startup | +| **cross_tenant_authorization** | DCMGroup with this group_class formally grants one Tenant access to another's resources; has lifecycle (duration, renewal, revocation); revocation places active allocations in PENDING_REVIEW | +| **drift_criticality** | Field-level property in Resource Type Spec (low/medium/high/critical); combined with change magnitude to produce drift severity | +| **Ingress API** | Infrastructure-layer entry point for all inbound DCM requests; sets ingress block; routes to Consumer/Provider/Admin API surfaces | +| **Provider Catalog Item** | Provider-specific instantiation of a Resource Type Specification; what consumers actually request; distinct from the Resource Type Specification itself | +| **Recovery Policy** | Formal DCM policy type mapping trigger conditions (DISPATCH_TIMEOUT, PARTIAL_REALIZATION, etc.) to response actions; same authoring model as GateKeeper/Validation/Transformation | +| **recovery_posture** | Fifth Policy Group concern_type governing failure and ambiguity response; binds a recovery profile group to the deployment | +| **DRIFT_RECONCILE** | Recovery action: schedule discovery; let drift detection resolve actual state | +| **DISCARD_AND_REQUEUE** | Recovery action: best-effort cleanup; new request cycle created immediately | +| **NOTIFY_AND_WAIT** | Recovery action: notify human; wait for explicit decision up to declared deadline | +| **TIMEOUT_PENDING** | Infrastructure Resource Entity state: dispatch timeout fired; recovery policy evaluating | +| **LATE_REALIZATION_PENDING** | Entity state: provider responded after timeout; NOTIFY_AND_WAIT recovery decision pending | +| **INDETERMINATE_REALIZATION** | Entity state: state ambiguous; drift detection resolving | +| **COMPENSATION_FAILED** | Entity state: compound service rollback itself failed; orphan detection active | +| **orphan_candidate** | Resource discovered at provider with no corresponding Realized State record; surfaced to platform admin for human resolution | +| **Discovery Scheduler** | DCM control plane component maintaining priority queue of discovery requests; dispatches to provider discovery endpoints | +| **recovery-automated-reconciliation** | Built-in recovery profile: trust drift detection; accept late responses; appropriate for dev/standard | +| **recovery-notify-and-wait** | Built-in recovery profile: notify human; never act automatically; appropriate for prod/fsi/sovereign | +| **notification service** | Ninth DCM provider type; translates unified notification envelope to delivery channel; handles delivery, retry, dead letter, and delivery confirmation callbacks | +| **Notification Router** | DCM control plane component that resolves notification audiences and routes envelopes to notification services | +| **audience resolution** | Deriving notification recipients by traversing the entity relationship graph from the changed entity at event time | +| **notification_uuid** | Idempotency key on notification envelopes; notification services use this to deduplicate on retry | +| **audience_role** | owner / stakeholder / approver / observer — why this actor is in the notification audience | +| **stakeholder_reason** | Notification envelope field explaining which relationship caused the actor to be in the stakeholder audience | +| **Tier 1 / Tier 2 / Tier 3 notifications** | Mandatory system (non-suppressable) / Tenant defaults / Actor subscriptions — three subscription tiers that compose | +| **NOT-001 through NOT-008** | Notification model system policies | +| **write-once snapshot store** | Realized Store implementation model: each record is a complete immutable entity state snapshot; no event replay; direct point-in-time lookup; supersession chain links snapshots | +| **corresponding_requested_state_uuid** | Mandatory non-nullable field on every Realized State snapshot; traces every Realized State change to an authorized request | +| **Provider Update Notification** | Formal API for providers to report authorized state changes; DCM evaluates via Policy Engine; approved → new Requested State + Realized State; rejected → drift event | +| **notification_uuid** | Idempotency key on Provider Update Notifications; safe to resend on provider crash | +| **pre-authorized update** | Category of provider update pre-approved by GateKeeper policy; processed automatically without per-change human review | +| **Whole Allocation** | Entire resource allocated as indivisible unit. Provider retains ownership. Consumer has exclusive use. Not subdivided or shared. | Dedicated Bare Metal (provider-owned) | +| **Full Transfer** | Provider transfers complete ownership to consumer's DCM Tenant. Consumer controls full lifecycle including decommission. | Transferred Bare Metal, Licensed asset | +| **Hybrid Transfer** | Ownership can transfer multiple times. Current owner is always exactly one DCM Tenant. Every transfer is tracked and auditable. | Bare Metal reallocated between tenants | + +Every Provider Catalog Item must declare which ownership model(s) it supports. + +### 8.4 Resource/Service Entity Lifecycle +``` +REQUESTED → PENDING → PROVISIONING → REALIZED → OPERATIONAL + │ + ┌───────────┼───────────┐ + ▼ ▼ ▼ + DEGRADED MAINTENANCE SUSPENDED + │ + DECOMMISSIONING → DECOMMISSIONED +``` +`DECOMMISSIONED` is the only terminal state. Records are retained permanently and are immutable. + +### 8.5 Process Resource Entities +A distinct entity class for ephemeral execution resources — automation jobs, playbooks, pipelines, workflows. +- Ephemeral lifecycle — exists for duration of execution +- Execution record retained permanently after terminal state +- Must belong to a DCM Tenant +- Must be in provenance chain of any Resource/Service Entity they affect +- Lifecycle: `REQUESTED → INITIATED → EXECUTING → COMPLETED | FAILED | CANCELLED` + +### 8.6 Provider Capacity Model — Three Modes +- **Mode 1 — Dynamic Query**: DCM queries provider on-demand during request processing +- **Mode 2 — Provider Registration (preferred)**: Provider registers capacity on configurable schedule — default minimum twice daily +- **Mode 3 — Provider Denial (mandatory)**: Provider validates fulfillment capability before executing. Denies with `INSUFFICIENT_RESOURCES`. Triggers immediate DCM capacity rating update. + +All three modes are always available. Mode 3 is mandatory for all providers. + +### 8.7 Provider Lifecycle Events +Any provider event affecting Resource/Service Entity availability or operational characteristics MUST be reported to DCM immediately. This is a non-negotiable contractual obligation. + +When DCM receives a provider event, the Policy Engine evaluates and determines response: +`ALERT | REVERT | UPDATE_DEFINITION | INVESTIGATE | DECOMMISSION | ESCALATE` + +DCM acts as the **Tenant advocate** — protecting Tenant interests in all provider interactions. + +--- + +## SECTION 9 — SERVICE DEPENDENCIES + +### 9.1 Why Dependencies Must Be Declared in Advance +Dependencies must be declared in the data model — not discovered at runtime. This is required for: +- **Auditability** — complete dependency graph known before execution +- **Cost Analysis** — full resource footprint known before provisioning +- **Placement** — Policy Engine needs complete resource footprint for optimal placement +- **Idempotency** — same request always produces same dependency graph + +### 9.2 Hybrid Dependency Declaration Model + +**Type-Level Dependencies (Resource Type Specification)** +- Portable and provider-agnostic +- Apply to all Provider Catalog Items implementing that Resource Type +- Use Resource Type UUIDs — not provider-specific references +- Required for all implementations + +**Provider-Specific Dependencies (Provider Catalog Item)** +- Provider-specific additions beyond type-level dependencies +- Must be marked `portability_breaking: true` +- Visible to Policy Engine and consumers as portability warnings + +### 9.3 Dependency Types and Cardinality + +| Type | Behavior | +|------|---------| +| `hard` | Must be realized before/alongside dependent. Failure fails dependent. | +| `soft` | Preferred but not blocking. Failure recorded but does not block. | +| `conditional` | Required only if specific request payload conditions are met. | + +| Cardinality | Description | +|-------------|-------------| +| `one_to_one` | Exactly one dependency resource required | +| `one_to_many` | One or more dependency resources required | +| `one_to_optional` | Zero or one dependency resource | +| `one_to_range` | Specific numeric range required | + +### 9.4 Dependency Graph +When a request is processed, a complete **Dependency Graph** is constructed — all resources that must be created including all transitive dependencies. Each resource appears exactly once. Circular references are invalid. + +The graph is attached to the Requested State. Nodes updated to REALIZED as providers fulfill each dependency. + +### 9.5 Dependency Payload Passing +When a dependency is realized, its Realized State payload is passed to the dependent provider. The dependent resource's Requested State payload is enriched with the dependency entity's UUID and relevant realized data. Recorded in field-level provenance with `source_type: dependency_payload`. + +### 9.6 Failure Handling — Configurable per Request or Policy + +| Mode | Behavior | +|------|---------| +| `fail_all` | Any hard dependency failure fails entire request. Partially realized nodes decommissioned. | +| `fail_dependent` | Failure fails only the dependent and its dependents. Independent branches continue. | +| `retry` | Failed dependencies retried with same or alternative provider. | +| `partial_complete` | Request marked partially complete. Failed nodes flagged for retry or manual intervention. | + +### 9.7 Rehydration and Dependencies +Rehydration uses **Intent State** — not Realized State — to reconstruct the dependency graph. This ensures current policies and standards are applied. Resources can be rehydrated to a different provider as long as type-level dependencies are satisfiable. + +--- + +## SECTION 10 — UNIVERSAL GROUP MODEL AND TENANCY + +> The Universal Group Model (document 15-universal-groups.md) supersedes the separate Tenant and Resource Group models for new implementations. All existing constructs map 1:1 to `group_class` values. Existing UUIDs and API surfaces are preserved. + +### 10.1 Universal Group Structure +Every grouping construct in DCM is a `DCMGroup` with: +- `group_class` — determines system behavior (see 10.2) +- `group_subclass` — advisory label, no system behavior +- `member_types_permitted` — what can be a member +- `exclusivity` — one or many groups of this class per member +- `enforcement_model` — advisory | enforced | mandatory (tenant_boundary: profile-governed) +- `lifecycle_policy` — on_group_destroy: **detach (default)** | notify | cascade | retain +- `former_group_membership` records retained permanently after detach +- `group_destruction_record` retained in Audit Store permanently +- Time-bounded membership: `valid_from / valid_until` on every membership + +### 10.2 Group Classes + +| group_class | Purpose | enforcement | +|-------------|---------|------------| +| `tenant_boundary` | Ownership/isolation boundary (replaces Tenant) | profile-governed | +| `resource_grouping` | Flexible entity tagging (replaces Resource Groups) | advisory | +| `policy_collection` | Policy cohesion unit (replaces Policy Group) | advisory | +| `policy_profile` | Deployment configuration (replaces Policy Profile) | enforced | +| `layer_grouping` | Related layers for a context | advisory | +| `composite` | Cross-type organizational unit | configurable | +| `federation` | Peer-group association (federated Tenants) | advisory | + +### 10.3 DCM Tenant — tenant_boundary group_class +Every resource entity must belong to exactly one `tenant_boundary` group (GRP-001). Provides: ownership, isolation, cost attribution, policy scope, drift detection scope, rehydration scope, audit scope, sovereignty boundary. + +**Structurally locked invariants** (cannot be overridden by any policy): +- One tenant_boundary group per resource — always +- Constituent relationships never cross tenant_boundary boundaries — at any nesting level + +**Profile-governed enforcement:** `minimal` profile → advisory; `standard`/`prod`/`fsi`/`sovereign` → mandatory. A GateKeeper policy fires when advisory tenancy detected in prod/fsi/sovereign deployment (GRP-011). + +### 10.4 Nested Tenants +A `tenant_boundary` group can have `parent_group_uuid` pointing to another `tenant_boundary` group. The parent-child relationship is a governance and cost relationship — NOT ownership transfer. + +**Parent can:** aggregate cost, apply governance overlay, query aggregate audit, declare child lifecycle policies +**Parent cannot:** own child resources, cross child isolation boundaries, override more-restrictive child policies + +**Governance inheritance — most restrictive wins (GRP-009):** +``` +Most restrictive policy at ANY nesting level wins + Child policies that are more restrictive than parent → child wins + Parent policies cascade where child has no policy or is less restrictive + Platform policies govern all tenant_boundary groups +``` + +### 10.5 Federated Tenants +A `federation` group contains peer `tenant_boundary` groups. Enables shared policy application, cross-federation visibility, and consolidated reporting. Does NOT grant governance authority — member Tenants remain independent (GRP-010). + +### 10.6 Composite Groups +A `composite` group permits all member types — resource entities, policies, layers, and other groups. Enables "everything about Payments" as one organizational unit. + +Policy targeting composite groups defaults to all member types. Declare `member_type_filter` to narrow. Policy linting warns if composite is targeted without filter (GRP-012). + +### 10.7 GRP System Policies +GRP-001 through GRP-014. Key: GRP-001 (one tenant_boundary per resource), GRP-003 (no circular nesting), GRP-005 (detach is default on destroy), GRP-006 (resource in leaf Tenant always), GRP-009 (most restrictive wins in nesting), GRP-011 (advisory tenancy in prod triggers notification), GRP-013 (former_group_membership permanent), GRP-014 (destruction record permanent). + +### 10.5 DCM System Policies for Grouping + +| Policy | Rule | +|--------|------| +| `TEN-001` | Every Resource/Service Entity must belong to exactly one DCM Tenant | +| `TEN-002` | A Tenant must exist before resources can be created in it | +| `GRP-003` | Circular nesting in Resource Groups is invalid | +| `GRP-004` | Custom Resource Groups must implement the full Resource Group Interface | +| `GRP-005` | Exclusive membership groups must reject violating membership requests | + +--- + +## SECTION 11 — ENTITY RELATIONSHIPS + +### 11.1 Design Principle +**Single model. Minimum variance. Simple by default.** Every relationship in DCM — VM requires storage, application contains web server, resource references Business Unit — uses the same structure. No separate binding mechanism, no separate dependency graph, no separate business data association. One universal model. + +### 11.2 Universal Relationship Structure +Every relationship is a first-class data object with its own UUID. Recorded **bidirectionally** — on both participating entities. The same `relationship_uuid` appears on both sides. + +```yaml +relationship: + relationship_uuid: + this_entity_uuid: + this_role: + related_entity_uuid: + related_entity_type: + related_entity_role: + information_provider_uuid: + information_type: + relationship_type: + nature: + lifecycle_policy: + on_related_destroy: + on_related_suspend: + on_related_modify: + status: + provenance: +``` + +### 11.3 Relationship Types (fixed vocabulary) + +| Type | Inverse | Meaning | +|------|---------|---------| +| `requires` | `required_by` | Cannot function without the related entity | +| `depends_on` | `dependency_of` | Uses the related entity but can degrade without it | +| `contains` | `contained_by` | Logical container for the related entity | +| `references` | `referenced_by` | References without owning or requiring | +| `peer` | `peer` | Equal relationship | +| `manages` | `managed_by` | Has lifecycle management authority | + +### 11.4 Relationship Nature + +| Nature | Lifecycle Policy | Example | +|--------|-----------------|---------| +| `constituent` | Required | VM requires its boot disk | +| `operational` | Required | Web server depends on load balancer | +| `informational` | Not applicable | Resource references its Business Unit | + +### 11.5 Lifecycle Policy Authority +``` +Resource Type Specification default (lowest) + → Provider Catalog Item default + → Consumer declaration + → DCM System Policy (non-overridable) +``` + +### 11.6 Relationship Roles +Standard roles: `compute`, `storage`, `networking`, `security`, `database`, `web`, `app`, `cache`, `queue`, `pipeline`, `identity`, `monitoring`, `business_unit`, `cost_center`, `product_owner`, `regulatory_scope` + +Custom roles: extensible — organizations register domain-specific roles (e.g., `trading_engine`, `risk_calculator`). Semantic labels only — do not affect system behavior. + +### 11.7 Where Relationships Are Declared +- **Resource Type Specification** — declares possible relationships (ceiling) +- **Catalog Item** — declares actual relationships for an offering (can only restrict) +- **Request time** — consumer declares instance relationships +- **External data** — same model for Business Unit, Cost Center, Person, etc. + +### 11.8 Bundled Declaration Expansion +When consumer bundles storage/networking in a compute request: +1. **Processor** creates Resource/Service Entity stubs (PENDING) with UUIDs +2. **Processor** creates bidirectional Relationship records +3. **Processor** applies lifecycle policy hierarchy +4. **Policy Engine** validates binding type and lifecycle policy +5. **Service Provider** provisions natively, returns realized payloads in DCM format +6. **DCM** updates entities PENDING → REALIZED, activates relationships + +### 11.9 The Entity Relationship Graph +All relationships form a traversable graph. Used for: rehydration (full graph traversal), cost rollup (accumulates across constituent relationships), drift detection (discovered vs realized graph comparison), decommission ordering (lifecycle policies at each edge), placement (pre-realization footprint), impact analysis (change propagation). + +### 11.10 Supersedes Dependency Graph +The Entity Relationship model unifies the previously separate dependency graph concept. The dependency graph IS the relationship graph at pre-realization time — same structure, different lifecycle state. + +### 11.11 Lifecycle Policy Conflict Resolution (Q57 resolved) +Lifecycle policy fields on relationships are **just fields**. They carry the same `override` metadata, the same provenance obligations, and resolve under the same Policy Engine authority hierarchy as any other DCM field. No special case — minimum variance. + +**Priority schema governs conflicts within a tier.** Highest numeric priority runs first. First policy to set `override: immutable` on a lifecycle policy field locks it. `immutable_ceiling: absolute` applies for compliance mandates that must survive future policy changes. + +**Ingestion conflict detection applies.** Two policies declaring conflicting lifecycle policies without priority differentiation → CONFLICT ERROR at ingestion. + +**DCM System Policies:** +- `REL-008` — A `constituent` relationship lifecycle policy may not be set to `ignore` for `on_related_destroy` +- `REL-009` — Lifecycle policy conflicts between policies are resolved by the standard Policy Engine authority hierarchy — no special case + +### 11.12 Relationship Type × Nature Matrix (valid combinations) + +The two relationship dimensions form an explicit matrix. Invalid combinations are rejected by the Policy Engine at request time (REL-013). + +| | `constituent` | `operational` | `informational` | +|---|---|---|---| +| **`requires`** | ✅ Core constituent | ✅ Hard operational dependency | ❌ Invalid | +| **`depends_on`** | ✅ Soft constituent | ✅ **Allocated resource cell** | ✅ Awareness | +| **`contains`** | ✅ Ownership container | ⚠️ Rare — justify explicitly | ❌ Invalid | +| **`references`** | ❌ Invalid | ❌ Invalid | ✅ **Business context cell** | +| **`peer`** | ❌ Invalid | ✅ Operational peers | ✅ Informational peers | +| **`manages`** | ✅ Component management | ✅ Operational management | ✅ Audit management | + +- `operational` + `depends_on` = **allocated resource cell** — cross-tenant allocations live here +- `informational` + `references` = **business context cell** — Business Unit, Cost Center etc. + +### 11.13 Cross-Tenant Relationships (Q59 resolved) + +Relationship **nature** governs cross-tenant permissions: + +| Nature | Cross-Tenant? | Rule | +|--------|--------------|------| +| `constituent` | ❌ Never | REL-010 | +| `operational` | ✅ With dual authorization | REL-011 | +| `informational` | ✅ Unless deny_all | REL-012 | + +**Hard tenancy declaration** on Tenant entity: +```yaml +hard_tenancy: + cross_tenant_relationships: operational_only + # deny_all | operational_only | informational_only | allow_all +``` + +### 11.14 Allocated Resource Model (Q59 extension, Q61 partial) + +An **Allocated Resource** is a pre-defined discrete slice of a parent resource made available by the owning Tenant for consuming Tenants to claim. It becomes a **first-class entity** in the consuming Tenant's scope with its own UUID, lifecycle, and governance. + +**Relationship:** `depends_on` + `operational` + `cross_tenant: true` + `allocation_uuid` + +**Parent pre-defines** `available_allocations` — consuming Tenant claims → DCM creates allocated entity + relationship → parent tracks in `active_allocations` with `notification_endpoint`. + +**Lifecycle events** propagate from parent to all active allocations per each allocation's `parent_lifecycle_policy`: +`on_parent_destroy | on_parent_suspend | on_parent_maintenance | on_parent_degrade | on_parent_capacity_change` + +**System policies:** REL-013 (invalid matrix combinations rejected), REL-014 (claim requires available allocation record) + +### 11.15 Shared Resource Model — Same-Tenant (Q61 resolved) + +A **Shared Resource** is an entity within a single Tenant that has active relationships from multiple parent entities. DCM maintains `active_relationship_count` — the number of active constituent/operational relationships. Informational relationships never count (REL-016). + +**`sharing_model` on entity:** +```yaml +sharing_model: + shareable: true + sharing_scope: tenant + active_relationship_count: 3 # DCM-maintained + minimum_relationship_count: 0 + on_last_relationship_released: +``` + +**`shareability` on Resource Type Specification:** +- `shareability.allowed: true` — instances can have multiple active relationships +- `shareability.allowed: false` (e.g., `Compute.BootDisk`) — second relationship rejected (REL-017) +- `max_active_relationships` — optional cap (e.g., license seat limits) + +**Destruction deferral (REL-015):** Destructive lifecycle actions on shared resources are deferred until `active_relationship_count` reaches `minimum_relationship_count`. Deferred destruction is recorded in `deferred_destruction_record`. + +### 11.16 Lifecycle Action Hierarchy — Save Overrides Destroy (REL-018) + +When multiple relationships produce different lifecycle action recommendations on a shared resource, the most conservative action wins: + +``` +retain > notify > suspend > detach > cascade > destroy +``` + +`retain` always beats `destroy` — the save_overrides_destroy rule. Applies automatically per REL-018. + +**Conflict detection (REL-019):** When recommendations differ, a `lifecycle_conflict_record` is created: +- Severity `info` — hierarchy resolved cleanly (e.g., retain beats destroy non-adjacent) +- Severity `warning` — adjacent levels or `notify` is the winning action — notifications sent +- Severity `critical` — immutable lifecycle lock overridden by REL-018 — platform admin notified + +**Unified model:** Same-tenant sharing (`active_relationship_count`) and cross-tenant allocation (`active_allocations`) are the same reference-counting concept at different scopes. Both defer destructive actions until the last relationship/allocation is released. + +--- + +## SECTION 12 — INFORMATION PROVIDERS + +### 12.1 Purpose +Information Providers are a first-class DCM provider type that serves authoritative external data DCM needs to reference but does not own (HR systems, finance systems, CMDBs). DCM references but never caches or owns external data. + +### 12.2 Provider Types — Data Ownership + +DCM has five provider types (see Section 0 for the complete list). The data ownership question is most relevant to these four categories: + +| Type | Purpose | DCM Owns Result? | +|------|---------|-----------------| +| **Service Provider** | Realizes resources — KubeVirt, VMware, AAP, etc. | Yes | +| **Information Provider** | Serves authoritative external data DCM references but does not own | No — external system is authoritative | +| **Meta Provider** | Composes multiple providers into higher-order services | Yes | + + +All five provider types follow the same base contract: registration, health check, trust, sovereignty, accreditation, governance matrix enforcement, zero trust, and provenance emission obligation. + +### 12.3 Same Contract as Service Providers +Information Providers follow the same registration, health check, trust, and capacity model as Service Providers where applicable. Capacity = query capacity (requests/sec). Naturalization/Denaturalization = translating native format to DCM unified format. + +### 12.4 Standard vs Extended Data +- **Standard data** — DCM-defined fields. Used for lookups, policy evaluation, operational decisions. Portable across all implementations. +- **Extended data** — organization-defined fields. Carried in payload for downstream consumers. DCM core does not rely on extended data for operational decisions. + +### 12.5 Stable External Key Model +```yaml +external_entity_reference: + uuid: + external_uuid: + information_provider_uuid: + information_type_name: Business.BusinessUnit + lookup_method: + primary_key: external_uuid + fallback_keys: + - field: code + value: "BU-PAY" + display_name: + verification: + last_verified: + verification_status: +``` +DCM UUID wraps external UUID — if external system changes its UUID, only this record changes. All relationships remain valid. + +### 12.6 Three-Mode Verification +- **Mode 1** — DCM-initiated scheduled verification (configurable frequency) +- **Mode 2** — Information Provider push (contractual obligation — same as SP lifecycle events) +- **Mode 3** — On-demand verification fallback when reference is stale + +### 12.7 Information Types in the Registry +Same DCM registry as Resource Types — distinguished by category prefix: +- `Business.*` — BusinessUnit, CostCenter, ProductOwner +- `Identity.*` — Person, ServiceAccount, Group +- `Compliance.*` — RegulatoryScope, AuditFramework +- `Operations.*` — Runbook, SLA, SupportContract + +--- + +## SECTION 13 — KUBERNETES SUPERSET STRATEGY + +### 13.1 Position +DCM is a **superset of Kubernetes** — extending Kubernetes' declarative, controller-based model upward to provide unified management across multiple clusters, infrastructure types, and organizational boundaries. Kubernetes manages the execution plane. DCM manages the management plane. + +### 13.2 What DCM Adds Beyond Kubernetes + +| Capability | Kubernetes | DCM | +|------------|-----------|-----| +| Scope | Single cluster | Multi-cluster, multi-infrastructure | +| Tenancy | Namespace isolation | First-class Tenant ownership model | +| Policy | RBAC + admission webhooks | Full Policy Engine — Validation/Transformation/GateKeeper | +| Data lineage | Not provided | Field-level provenance on all data | +| Cost attribution | Not provided | Full lifecycle cost analysis | +| Drift detection | Controller reconciles | Four-state model — Intent/Requested/Realized/Discovered | +| Service catalog | Not provided | Self-service catalog with RBAC-governed presentation | +| Sovereignty | Not provided | Placement constraints, compliance evidence | +| Non-Kubernetes resources | Not provided | VMware, bare metal, OpenStack managed through same model | + +### 13.3 Operator Integration — The Adapter Pattern +Kubernetes operators become DCM Service Providers through the DCM Operator Interface Specification. The pattern: +``` +DCM Control Plane → Operator Adapter (Service Provider) → Kubernetes Operator → Cluster +``` +The adapter handles Naturalization (DCM Requested State → Kubernetes CR) and Denaturalization (CR status → DCM Realized State). + +### 13.4 Native Support Strategy +Goal: influence the Kubernetes operator ecosystem to adopt DCM as a superset. Three-phase approach: +1. **Generic Operator Adapter** — declarative field mappings, no operator changes needed +2. **DCM Operator SDK** — Go library, adds DCM support with minimal code changes, one day to Level 1 +3. **Upstream contributions** — contribute DCM support directly to priority operators (KubeVirt, CloudNativePG, Strimzi, Cert-Manager) + +### 13.5 Conformance Levels +- **Level 1** — Registration + health + basic status reporting. One day with SDK. Unlocks: catalog, health monitoring, basic cost tracking. +- **Level 2** — + Capacity + lifecycle events + full realized payloads + field mappings. 2-3 days. Unlocks: placement, drift detection, cross-cluster management. +- **Level 3** — + Sovereignty + provenance + discovery + decommission confirmation. 3-5 days. Unlocks: full audit chain, brownfield ingestion, sovereignty enforcement. + +### 13.6 CNCF Strategy +Target CNCF Sandbox submission. FSI consortium (leading FSI consortium members) provides multi-organization adopter evidence. Key artifacts needed before submission: DCM Operator Interface Spec v1.0, SDK v0.1.0, KubeVirt Level 2 reference implementation, conformance test suite, governance model. + +### 13.7 Key Kubernetes-to-DCM Concept Mappings +| Kubernetes | DCM | +|-----------|-----| +| CRD | Resource Type Specification | +| Custom Resource | Requested State → Realized State | +| Reconciliation loop | Realization + Drift Detection | +| Namespace | DCM Tenant boundary | +| ownerReference | Entity Relationship (contains/contained_by) | +| Finalizers | Lifecycle policy enforcement | +| Labels/Annotations | DCM entity metadata | +| Kubernetes conditions | DCM lifecycle states | + +--- + +## SECTION 14 — WEBHOOK INTEGRATION + +### 14.1 Purpose +Webhooks provide a **push-based notification model** for consumers, providers, and external systems that cannot or do not poll DCM. They complement the API-first model and Message Bus by enabling real-time outbound event notifications. + +### 14.2 Architectural Position +Webhooks are an **Egress capability** — outbound notifications from DCM to external systems. They fit within the existing Egress zone alongside the Messaging Protocol and Interoperability API. + +### 14.3 Core Use Cases + +| Audience | Example Events | +|----------|---------------| +| **Consumer/CI-CD** | Resource request transitions to REALIZED; Entity enters DEGRADED state; dependency graph node fails | +| **Provider** | New request payload dispatched; discovery request initiated; decommission requested | +| **External Systems** | ITSM notification on request create/update/complete; FinOps platform on Entity realization/decommission | +| **Operational** | Provider capacity below threshold; unsanctioned change detected; GateKeeper policy fired | +| **Compliance** | Sovereignty constraint applied; ownership transfer initiated/completed; audit-relevant policy triggered | + +### 14.4 Key Design Principles (Established) +- Webhook events are **typed and versioned** — consistent with DCM universal versioning model +- Webhook payloads carry **provenance information** — sufficient context to trace back to the originating request, entity, and policy +- **Policy Engine integration** — the Policy Engine can fire webhooks as a response action (alongside ALERT, REVERT, UPDATE_DEFINITION, etc.) +- Webhook registrations are **scoped** — consumer-facing webhooks registered via Consumer API; provider-facing webhooks registered via Provider Registration +- Events should align with a **DCM Event Type Registry** — extensible, versioned, following the same registry model as Resource Types + +### 14.5 Open Design Questions +See [DISCUSSION-TOPICS.md — TOPIC-001](DISCUSSION-TOPICS.md) for the full list of design questions. Key unresolved items: +- Webhook registration model and API +- Full event taxonomy and registry structure +- Payload format — full state vs reference + event type +- Authentication model for outbound webhook calls +- Retry and reliability obligations +- Ordering guarantees +- Relationship to the Message Bus +- Whether provider webhook support is mandatory in the Provider Contract +- Tenant vs platform-level scoping + +### 14.6 Status +**Under active discussion** — see DISCUSSION-TOPICS.md TOPIC-001. Do not make implementation assumptions until design questions are resolved. + +--- + +## SECTION 15 — DCM ARCHITECTURE COMPONENTS + +### 15.1 The Five Domains +DCM is organized into five horizontal domains from bottom to top: + +| Domain | Persona | Content | +|--------|---------|---------| +| **Data Center Domain** | CIO | Physical infrastructure — Compute, GPU, RAM, Storage, Networking, HSM | +| **Resource Domain** | SRE | Declarative resources — VMs, containers, pods, clusters, external IPs | +| **Control Plane Domain** | CTO | Policy, Validate & Placement, Audit, Orchestration | +| **Application Domain** | Engineering | Data Center Pipeline, CI/CD | +| **Value Domain** | Line of Business | Software Build & Deploy, business outcomes | + +### 15.2 Control Plane Components + +| Component | Purpose | +|-----------|---------| +| **API Gateway** | Central clearing house — ingress for consumers, egress to providers | +| **Job Queue** | Manages asynchronous task execution | +| **Request Payload Processor** | Assembles, enriches, and merges data layers into complete request payload | +| **Policy Engine** | Validates, transforms, gates, and enriches data based on policy definitions | +| **IDM / IAM** | Authentication and identity — source of truth for personas and RBAC | +| **Service Catalog** | Presents available services/resources per RBAC policy | +| **Orchestration** | Coordinates multi-step workflows and manages request lifecycle | +| **Cost Analysis** | Tracks service costs throughout the full resource lifecycle | +| **Audit** | Records all operations — reads provenance intrinsic to data objects | +| **Observability** | Monitoring, logging, and metrics | +| **Resource Discovery** | Interrogates providers to discover existing resource state | +| **Message Bus** | Async communication between control plane and external systems | + +### 15.3 Data Stores — PostgreSQL Store Contracts + +All DCM stores use **PostgreSQL** as the single required infrastructure. DCM defines store contracts for each data domain. Four store contract types: + +| Contract Type | Stores | Key Characteristics | +|--------------|--------|-------------------| +| **GitOps Store** | Intent, Requested, Layer, Policy | Branch/PR/merge semantics, immutable history, CI/CD hooks, Search Index companion | +| **Event Stream Store** | Realized, Discovered, Audit events | Append-only, entity-keyed streams, replayable, high-throughput | +| **Audit Store** | Compliance audit records | Compliance-grade, immutable, long-retention (7+ years FSI), hash-verified | +| **Observability Store** | Metrics, traces, logs | Time-series, short-to-medium retention, OpenTelemetry format | + +**Search Index** — non-authoritative queryable projection of GitOps stores. Rebuilt from Git on demand. Git always wins on disagreement. + +**DCM-internal caches** (Layer Cache, Policy Cache, Catalog Cache) — not data stores. Non-authoritative, cache-aside pattern, invalidated on writes. + +### 15.4 Consumer Ingress +- **Web UI** — web interface for human consumers +- **Consumer API** — API interface for programmatic consumers and external systems + +### 15.5 Egress +- **Messaging Protocol** — protocol translation to external systems +- **Interoperability API** — common API spec and data model for Service Provider communication + +--- + +## SECTION 16 — SERVICE PROVIDERS + +### 16.1 Core Principle +DCM is **not concerned with how a provider accomplishes its work** — only with: +- The data that crosses the boundary (conformant data in, conformant data out) +- The trust and contractual obligations the provider has declared and honored + +### 16.2 Naturalization and Denaturalization +- **Naturalization** — provider transforms DCM unified data into its own tool-specific format for execution +- **Denaturalization** — provider transforms tool-specific results back into DCM unified format for return to the control plane + +### 16.3 Provider Contract Dimensions +Providers must honor a multi-dimensional contract: + +**Data Contract** +- Accept DCM unified data format +- Return complete DCM unified data format (not just status codes) +- Implement Naturalization and Denaturalization + +**Sovereignty Contract** +- Explicitly declare which sovereignty dimensions they can satisfy +- Declaration is binding — if declared, it must be delivered +- Sovereignty dimensions: Data/Content, Operational, Security/Compliance, Placement/Mobility + +**Capability Contract** +- Declare which Resource Types they implement +- Declare which lifecycle operations they support (CRUD + Discovery) +- Declare what they do NOT support + +**Lifecycle Contract** +- Support all declared lifecycle states +- Participate in Discovery when requested +- Report Realized State completely and accurately +- Handle drift detection requests + +**Query Contract** +- Support `reserve_query` — atomic: verify constraints + return metadata + place hold +- Support `capacity_query` (informational, no hold) +- Support `metadata_query` (informational, no hold) +- Declare which query types and metadata fields are supported in provider registration +- Complete missing metadata in realized payload or discovery loop +- `reserve_query` response must include `missing_metadata` for any requested fields not returned + +**Trust Contract** *(validation mechanism — to be detailed)* +- Providers must be validated and certified to participate +- Trust is established at onboarding +- Chain of trust must be maintained + +**Compliance/Audit Contract** +- Maintain audit trail of actions taken +- Make audit data available to DCM in DCM unified format +- Must not take actions outside of DCM-initiated requests (for managed resources) + +**SLA/Operational Contract** +- Response time expectations +- Availability requirements +- Error handling and reporting standards +- Retry and idempotency guarantees + +### 16.4 Provider Types +- **Atomic Providers** — manage a single fundamental resource type (VM, IP, VLAN, container) +- **Meta Providers** — compose multiple providers as components of their own service +- **Process Providers** — purely process-based workflow or automation +- **External Policy Evaluators** — supply policies; Mode 4 evaluates/enriches via black box query +- **event routing services** — bidirectional bridge to external message buses (Kafka, AMQP, NATS, etc.) +- **credential management services** — resolve secrets from external stores (Vault, AWS SM, Azure KV, CyberArk, etc.) +- **Auth Providers** — authenticate identities and resolve permissions (OIDC, LDAP, AD, FreeIPA, etc.) +- **Real-world providers** are typically combinations of the above + +--- + +## SECTION 17 — THE POLICY ENGINE + +### 17.1 Purpose +The Policy Engine is the **single authoritative logic gate for all business rules** in DCM. It enforces governance without embedding business logic into the control plane. It is the **sole authority for field override control** — no other component sets override metadata on fields. + +### 17.2 Policy Categories + +| Category | Description | Modifies Data? | Example | +|----------|-------------|----------------|---------| +| **Transformation** | Enriches or modifies the payload. Adds missing fields, applies standards. All changes recorded in provenance. May set `override: constrained`. | Yes | Inject PCI-compliant cryptography standard | +| **Validation** | Checks payload against rules. Pass/fail — no field modification. Failure rejects request. | No | VM class allowed in DMZ Zone A | +| **GateKeeper** | Highest authority. Overrides any field including consumer input. Sets `override: immutable`. Halts execution. | Yes — overrides everything | Block request violating sovereignty | + +### 17.3 Policy Hierarchy +Three-tier execution — Global first, User last. Within each tier sorted by priority (higher value = higher authority): + +``` +Global (Super Admin) — runs first, highest authority +Tenant (Tenant Admin) — runs second +User (End User) — runs last, lowest authority +``` + +A Global policy cannot be overridden by Tenant or User. Field locks set by higher-authority policies cannot be unlocked by lower-authority policies. + +### 17.4 Policy Implementation +- **Engine:** OPA (Open Policy Agent) with Rego policy language +- **Storage:** GitOps — all policies in Git, versioned, immutable once published +- **Execution:** Stored-policy model — OPA pre-loads policies; evaluation calls pre-loaded modules +- **Outputs:** `rejected` (bool), `patch` (field mutations), `constraints` (JSON Schema locks), provider placement constraints (NOT direct provider naming) + +### 17.5 Policy Rules +- Policies operate only on policy definition, core data, and request payload data +- Outcomes must be **deterministic** — same input always produces same output for a given version +- All modifications recorded in field-level provenance with policy UUID, tier level, and reason +- Policies follow the five-status lifecycle: `developing → proposed → active → deprecated → retired` + +### 17.6 Policy Scope +- **Core Policies** — all DCM actions regardless of provider +- **Service Policies** — specific to a Service Provider's services +- **Organizational Policies** — defined by the implementing organization +- **Domain Policies** — scoped to a specific domain or business unit + +### 17.7 Proposed Policy — Shadow Execution +When a policy is in `proposed` status it runs in **shadow mode** against real request traffic: +- Executes alongside active policies on every relevant request +- Output captured in `proposed_evaluation_record` — what it would have done +- Output is **never applied** to the actual request +- Feeds the Validation Dashboard for reviewer analysis before activation +- Impact categories: `none | low | medium | high | critical` +- Activation requires approval after review period + +### 17.8 Policy and Override Control +The Policy Engine exclusively sets `override_control` metadata. Three levels (see Section 6.10–6.11): +- Level 1 — no declaration → fully overridable (default) +- Level 2 — simple `override: allow|constrained|immutable` +- Level 3 — full `override_matrix` with per-actor permissions and trusted grants + +### 17.9 Policy Placement Phase +Every policy declares when in the assembly process it executes: +- `pre` — before placement (default) — no provider context +- `loop` — inside the placement loop — has reserve query response data +- `post` — after placement confirmed — has full placement block +- `both` — pre and post + +### 17.10 Policy Required Context +Policies declare what fields they need and what to do when fields are absent: +```yaml +required_context: + - field: placement.provider_metadata.sovereignty_certifications + if_absent: + if_absent_reason: +``` +If no policy declares `required_context` for an absent field → `implicit_approval` recorded in `policy_gap_record`. The system has no implicit opinion beyond what policies state. + +--- + +## SECTION 18 — KEY USE CASES + +### 18.1 Datacenter Rehydration (Repave) +Reconstruct ALL required components and configurations from code after catastrophic failure (ransomware, DR event, mandatory 90-day repave). Industry benchmark: leading FSI organization 60-day repave. DCM must beat this. Key metric: TTR (Time to Recovery). + +### 18.2 Intelligent Placement +Automated placement of resources and workloads based on metadata, policies from CMDB/CISO/platform teams. Consumer declares criteria (location, SLA, security zones) — DCM determines optimal placement automatically. + +### 18.3 Application as a Service +Meta Service Provider that consumes application code and provides its full execution lifecycle. Consumer defines metadata for required technologies or SLAs — provider handles the rest. + +### 18.4 Regional Sovereignty +Enforce workload placement within specific regions or sovereignty constraints. CISO/CCO-driven policy ensuring data residency and jurisdictional compliance. + +### 18.5 Data Enrichment +System enriches consumer requests with ancillary implementation details the consumer should not need to know. Consumer declares intent — DCM fills in the details. + +### 18.6 Greening the Brownfield — Unified Ingestion Model +Bring existing unmanaged infrastructure under DCM lifecycle management using the unified ingestion model (see Section 20 and data model document 13-ingestion-model.md). The same three-step pattern applies to both brownfield ingestion and V1 migration: + +``` +1. INGEST — bring the entity into DCM with whatever identity/metadata is available +2. ENRICH — associate business data, ownership, Tenant assignment, relationships +3. PROMOTE — transition from holding state to full DCM lifecycle ownership +``` + +**Brownfield flow:** Service Provider performs discovery → DCM identifies unmanaged Discovered State records → Entity stubs created (state: INGESTED, Tenant: `__transitional__`) → Business data and Tenant assigned → Promotion authorized → Discovered State promoted to Realized State → Drift detection active from this point. + +**V1 Migration flow:** V1 resources inventoried → Auto-assignment attempted via signals (resource groups, business unit, request history) → Auto-assignable resources assigned in bulk → Manually assignable resources surfaced in admin queue → Orphaned resources assigned to `__transitional__` → Enrichment and promotion → Migration complete when `__transitional__` Tenant is empty. + +**Key concepts:** +- `__transitional__` Tenant — system-managed holding area for unassigned ingested entities. Cannot be deleted or used for new provisioning. Governance policy enforces max residency and escalation. +- `ingestion_record` — provenance record on every ingested entity: source, confidence, assignment method, enrichment history, promotion timestamp +- Ingestion confidence: `high` (strong signal) | `medium` (inferred) | `low` (orphaned) +- Entities in `INGESTED` or `ENRICHING` state cannot be parents for allocated resource claims or hard dependencies for new requests + +--- + +## SECTION 19 — DIGITAL SOVEREIGNTY + +DCM addresses four dimensions of digital sovereignty: + +| Dimension | DCM Enabler | Impact | +|-----------|-------------|--------| +| **Data and Content Sovereignty** | Data Model, Policy Engine, Validated Providers | Data residency and jurisdictional compliance | +| **Operational Sovereignty** | Policy Engine | Sovereign Execution Posture, Hard Tenancy enforcement | +| **Security and Compliance Sovereignty** | Audit, GRC | Evidence for strict regional mandates | +| **Mobility, Placement, Modernization** | Policy Engine, Providers, Data Model | Automated placement, provider mobility, brownfield ingestion | + +**Sovereign Execution Posture** — the target end state where all operations are governed, auditable, and compliant with sovereignty requirements. This is the north star concept of DCM. + +--- + +## SECTION 20 — INGESTION MODEL + +The Ingestion Model is the **unified mechanism for bringing entities that exist outside DCM's lifecycle control into DCM governance**. It covers V1 Migration, Brownfield Discovery, and Manual Import — all follow the same pattern. + +### 20.1 Three-Step Pattern +``` +INGEST → ENRICH → PROMOTE → OPERATIONAL +``` + +### 20.2 Ingestion Lifecycle States + +| State | Tenant | New Requests? | Parent for Allocations? | New Relationships? | +|-------|--------|--------------|------------------------|-------------------| +| `INGESTED` | `__transitional__` or assigned | No | No | Informational only | +| `ENRICHING` | Assigned | No | No | Operational (read-only) | +| `PROMOTED` | Assigned | Yes | Yes | All types | + +### 20.3 The `__transitional__` Tenant +System-managed holding Tenant for unassigned ingested entities: +- Cannot be deleted, renamed, or used for new resource provisioning +- Governance policy enforces `max_residency_days` and escalation action +- Hard tenancy: `operational_only` by default +- `created_via: system` — artifact metadata + +### 20.4 Ingestion Record +Every ingested entity carries an `ingestion_record` in provenance: +- `ingestion_source` — `v1_migration | brownfield_discovery | manual_import` +- `assigned_tenant_uuid` — real Tenant or null if still in `__transitional__` +- `assignment_method` — `auto | manual | transitional` +- `assignment_signal` — human-readable description of what drove auto-assignment +- `ingestion_confidence` — `high | medium | low` +- `enrichment_status` — `pending | partial | complete` +- `enrichment_history` — append-only log of all enrichment actions +- `promoted_at` — when entity reached PROMOTED state + +### 20.5 Auto-Assignment Signal Priority +DCM attempts auto-assignment in this order (configurable): +1. Explicit ownership metadata (high confidence) +2. Resource group membership (high confidence) +3. Request history (high confidence) +4. Network/location context (medium confidence) +5. Naming convention (medium confidence) +6. Provider context (medium confidence) +7. No signal → `__transitional__` (low confidence) + +### 20.6 V1 Migration (Q55 resolved) +V1 resources have no `tenant_uuid`. V2 requires one (TEN-001). Migration uses the ingestion model: +- Pre-migration analysis pass classifies all V1 resources: `auto_assignable | manually_assignable | orphaned` +- Auto-assignable → bulk Tenant assignment + ingestion record +- Manually assignable → admin queue for human review and assignment +- Orphaned → `__transitional__` Tenant + governance timer +- Migration complete when `__transitional__` Tenant is empty + +### 20.7 Brownfield Ingestion +Unmanaged discovered entities follow the same ingestion model: +- Service Provider discovery creates Discovered State records +- DCM identifies unmanaged Discovered State records (no matching Realized State) +- Entity stubs created (state: INGESTED, source: brownfield_discovery) +- Enriched → promoted → Discovered State becomes initial Realized State +- Drift detection active from promotion forward + +### 20.8 DCM System Policies for Ingestion + +| Policy | Rule | +|--------|------| +| `ING-001` | Every ingested entity must be assigned to one Tenant — real or `__transitional__` — before V2 eligibility | +| `ING-002` | Entities in `INGESTED` or `ENRICHING` state may not be parents for allocated resource claims | +| `ING-003` | `__transitional__` Tenant cannot be deleted, renamed, or used for new provisioning | +| `ING-004` | Every ingested entity must carry an `ingestion_record` in provenance | +| `ING-005` | Entities in `__transitional__` beyond `max_residency_days` must trigger configured escalation | +| `ING-006` | Brownfield entities may not be promoted without explicit actor authorization | +| `ING-007` | At brownfield promotion, Discovered State is promoted to Realized State — DCM assumes lifecycle ownership | + +--- + +## SECTION 21 — POLICY ORGANIZATION: GROUPS, PROFILES, AND POLICY PROVIDERS + +### 21.1 Three-Level Policy Organization +``` +Policy Profile — complete use-case configuration (composed of groups) + │ +Policy Groups — single-concern policy collections (composed of policies) + │ +Policies — individual Transformation / Validation / GateKeeper rules + │ optionally sourced from +External Policy Evaluators — external authoritative policy sources (fifth provider type) +``` + +### 21.2 Policy Groups +A **Policy Group** is a cohesive collection of policies addressing a single identifiable concern. + +**Concern types:** `security | compliance | operational | recovery_posture | zero_trust_posture | data_authorization_boundary | orchestration_flow` + +**Key fields:** `handle` (domain/group/name), `concern_type`, `concern_tags`, `extends` (inherits parent), `policies` (constituent policies), `activation_scope` (resource types, tenant tags, regions), `conflicts_with` (explicit conflict declarations), `source` (local or external_policy_evaluator) + +**DCM built-in groups include:** core-minimal, dev-defaults, ephemeral-resources, audit-basic, audit-compliance, data-classification, cost-governance, sla-enforcement, hard-tenancy, explicit-cross-tenant, zero-trust, encryption-baseline, pci-dss, gdpr-eu, nist-800-53, iso-27001, fsi-audit, lifecycle-ttl-enforcement, air-gap, kubevirt, openstack, vmware + +### 21.3 Policy Profiles +A **Policy Profile** is a complete DCM configuration for a specific use case composed of Policy Groups. + +**Six DCM built-in profiles (least to most restrictive):** + +| Profile | Handle | Tenancy | Enforcement | Cross-Tenant | Audit | +|---------|--------|---------|-------------|-------------|-------| +| `minimal` | `system/profile/minimal` | Optional — auto-created | Advisory only | allow_all | None | +| `dev` | `system/profile/dev` | Recommended | Warn only | operational_only | Basic 90-day | +| `standard` | `system/profile/standard` | Required | Blocking | explicit_only | Compliance-grade | +| `prod` | `system/profile/prod` | Required | Blocking + SLA | explicit_only | Compliance-grade | +| `fsi` | `system/profile/fsi` | Hard tenancy | Blocking | explicit_only | 7-year retention | +| `sovereign` | `system/profile/sovereign` | Hard tenancy | Blocking | deny_all | 10-year retention | + +**Profile inheritance chain:** `system/profile/sovereign` extends `system/profile/fsi` extends `system/profile/prod` extends `system/profile/standard` extends `system/profile/dev` extends `system/profile/minimal` + +**Profile activation levels (more specific wins):** +```yaml +installation_config: default_profile: minimal +platform_config: active_profile: prod; minimum_tenant_profile: dev +tenant_config: active_profile: fsi # must be >= minimum_tenant_profile +``` + +**Profile shadow validation:** proposed profiles run in shadow mode before activation — same as proposed policies. + +### 21.4 External Policy Evaluator (Fifth Provider Type) +A **External Policy Evaluator** is a fifth DCM provider type — an external authoritative source supplying policies into DCM or evaluating/enriching data via external logic. + +**Four delivery modes:** + +| Mode | Name | `delivery.mode` value | Logic Lives In | +|------|------|----------------------|---------------| +| 1 | DCM Native Push/Pull | `push` / `pull` / `webhook` | DCM Policy Engine | +| 2 | OPA/Rego Bundle | `opa_bundle` | DCM Policy Engine (OPA) | +| 3 | External Schema (naturalization) | `external_schema` | DCM Policy Engine (post-translation) | +| 4 | Black Box Query-Enrichment | `black_box_query` | External provider — opaque to DCM | + +**Modes 1-3** deliver policy rules. **Mode 4** is a query-response interface — DCM sends data, external system evaluates and/or enriches, returns structured result. + +**Mode 4 can:** +- **Evaluate** — return pass/fail, score, or recommendation +- **Enrich** — inject additional fields into the payload (risk scores, compliance citations, cost predictions, org context, case references) +- **Both** — multi_factor result combining decision + enrichment + +**Mode 4 governance requirements:** +- Data sovereignty check before ANY query is sent (BBQ-001, BBQ-003) +- Data minimization — only declared fields sent (BBQ-002) +- Full audit record per query-response cycle (BBQ-004) including `audit_token` for cross-system correlation +- Default failure behavior is `gatekeep` — unknown is not safe (BBQ-005) +- Injected enrichment fields carry standard field-level provenance with `source_type: black_box_provider` + `audit_token` (BBQ-007) +- Override control applies to injected fields — GateKeeper can refuse enrichment (BBQ-008) +- Mode 4 enrichment providers require minimum `transformation` trust level (BBQ-009) + +**Trust levels (all modes):** +- `trusted` → GateKeeper authority (dual approval elevation required) +- `verified` → Transformation/Validation only; Mode 4 enrichment minimum +- `untrusted` → advisory only + +**`on_update` (Modes 1-3):** `proposed` (shadow validation) | `active` (immediate — trusted only) +**On provider failure:** policies deprecated with configurable sunset; Mode 4 → `on_unavailable` behavior fires + +### 21.5 Lifecycle Time Constraints +First-class field on any resource entity. Follow standard data model precedence and override control. + +**Two types:** +- `ttl` — ISO 8601 duration relative to reference_point (created_at | realization_timestamp | last_modified) +- `expires_at` — absolute ISO 8601 timestamp + +When both declared, earliest wins (LTC-004). GateKeeper can lock as `override: immutable`. + +**`on_expiry` actions:** `destroy | suspend | notify | review` + +Expiry enforcement is a DCM control plane function — not a provider concern. Failed expiry action → `PENDING_EXPIRY_ACTION` state + escalation (LTC-005). + +### 21.6 Cross-Tenancy Authorization — Explicit_Only Default +Default stance is now **`explicit_only`** — informational sharing is NOT open by default. Every cross-tenant relationship of any nature requires an explicit `cross_tenant_authorization` record. + +**Authorization specifies who/what/when/where:** +```yaml +cross_tenant_authorization: + authorized_consumer_tenant_uuid: # WHO + permitted_fields: [field1, field2] # WHAT — empty = all fields + valid_from/valid_until: # WHEN + permitted_in_regions: [eu-west] # WHERE + authorization_level: + # Hierarchy: field_specific > resource_specific > tenant_global (more specific wins) +``` + +All cross-tenant authorization decisions are policy-driven and DCM-enforced (XTA-004). + +### 21.7 Rehydration Tenancy Controls +Tenancy controls, sovereignty directives, and cross-tenant authorizations **always use current policies during rehydration — cannot be pinned**. + +**`policy_version: pinned`** governs resource configuration policies only. Tenancy/sovereignty always current. + +When rehydration conflicts with current tenancy controls → entity enters **PENDING_REVIEW** state: +- Allocation not automatically released +- Notifications: entity owner, both Tenant admins, platform admin +- Resolution: re_authorize | release | escalate +- A policy may declare automatic resolution behavior (RHY-004) + +### 21.8 System Policy Summary + +**LTC:** LTC-001 through LTC-005 — lifecycle time constraint enforcement +**XTA:** XTA-001 through XTA-005 — cross-tenancy authorization model +**RHY:** RHY-001 through RHY-004 — rehydration tenancy controls +**DEP:** DEP-001 through DEP-003 — cross-tenant dependency rules +**BBQ:** BBQ-001 through BBQ-009 — Mode 4 black box query-enrichment governance + +--- + +## SECTION 22 — UNIVERSAL GROUP MODEL + +DCM collapses all grouping constructs into a single **DCMGroup** entity with `group_class` metadata. One mental model, one API, one registry. See `15-universal-groups.md` for the complete model. + +### 22.1 Group Classes + +| group_class | Replaces | member_types_permitted | exclusivity | +|-------------|---------|----------------------|------------| +| `tenant_boundary` | Tenant | resource_entity, group | one (structural lock) | +| `resource_grouping` | Resource Group | resource_entity | many | +| `policy_collection` | Policy Group | policy | many | +| `policy_profile` | Policy Profile | group | many | +| `layer_grouping` | Layer grouping | layer | many | +| `provider_grouping` | Provider collections | provider | many | +| `composite` | (new) | all types | many | +| `federation` | (new) | group (tenant_boundary) | many | + +### 22.2 Structural Invariants (non-overridable) +- `GRP-INV-001` — resource_entity belongs to exactly one tenant_boundary group +- `GRP-INV-002` — constituent relationships cannot cross tenant_boundary boundaries +- `GRP-INV-003` — destroying parent tenant_boundary requires explicit resolution of all children first +- `GRP-INV-004` — resource in child tenant_boundary belongs to child — never parent +- `GRP-INV-005` — circular group membership invalid +- `GRP-INV-006` — group cannot be a member of itself + +### 22.3 Composite Groups +`member_types_permitted: [resource_entity, policy, layer, group, provider]`. Policies targeting composite groups apply to all member types by default. Narrow with `member_type_filter`. + +### 22.4 Nested Tenants +`tenant_boundary` group with `parent_group_uuid`. Parent has governance overlay and cost rollup — not ownership. Policy inheritance direction is profile-governed (opt_in for minimal/dev/fsi/sovereign; opt_out for standard/prod). + +### 22.5 Federated Tenants +`federation` group containing `tenant_boundary` groups as peers. Members remain fully independent. Enables shared governance, consolidated reporting, scoped cross-member visibility. + +### 22.6 API Backward Compatibility +`GET /tenants` → `GET /groups?group_class=tenant_boundary`. All existing UUIDs and API endpoints preserved. + +--- + +## SECTION 23 — UNIVERSAL AUDIT MODEL + +Every modification to every DCM artifact produces an audit record. No exceptions. See `16-universal-audit.md` for the complete model. + +### 23.1 Four Required Fields +**Date/time** (ISO 8601 microseconds) | **Who** (composite actor chain) | **What** (subject entity) | **Action** (closed vocabulary) + +### 23.2 Two-Stage Audit — Synchronous Commit + Async Enrichment + +**Stage 1 (synchronous, < 1ms, in critical path):** +```yaml +commit_log_entry: + entry_uuid: + sequence: # monotonically increasing + timestamp: # AUTHORITATIVE audit timestamp + entity_uuid / entity_type / action / actor_uuid / tenant_uuid + change_fingerprint: + status: pending_forward +``` +Written using Raft consensus — confirmed when quorum (2/3 or 3/5) of Commit Log replicas acknowledge. Operation returns success after Stage 1. Stage 1 timestamp is the authoritative audit timestamp (AUD-013). + +**Stage 2 (asynchronous, out of critical path):** +Audit Forward Service enriches minimal Commit Log entry → full audit_record → hash chain computed → written to Audit Store with retry. Full record visible seconds to minutes after Stage 1. + +### 23.3 Composite Actor Chain — The "Who" +```yaml +actor: + immediate: + type: + uuid / display_name + authorized_by: + uuid / authorization_method: + request_uuid / policy_uuid / correlation_id +``` + +### 23.4 Action Vocabulary (closed — AUD-007) +`CREATE | MODIFY | STATE_TRANSITION | DELETE | ACTIVATE | DEACTIVATE | DEPRECATE | RETIRE | MEMBER_ADD | MEMBER_REMOVE | RELATIONSHIP_CREATE | RELATIONSHIP_RELEASE | AUTHORIZE | REVOKE | EVALUATE | ENRICH | LOCK | HOLD_PLACE | HOLD_CONFIRM | HOLD_RELEASE | DRIFT_DETECT | DRIFT_RESOLVE | INGEST | PROMOTE | EXPIRE | REHYDRATE | QUERY | DISCOVER | LOGIN | LOGOUT | CONFIG_CHANGE` + +### 23.5 Retention — Reference-Based +- `retention_status: live` — any referenced entity non-retired → retain unconditionally +- `retention_status: all_retired` → apply governing policy +- Post-lifecycle defaults: dev=P90D, standard=P3Y, prod/fsi=P7Y (DEFAULT), sovereign=P10Y + +### 23.6 Tamper-Evidence — Hash Chain +`record_hash` (SHA-256 of record) + `previous_record_hash` (preceding record for this entity) + `chain_sequence`. Chain breaks detectable and trigger security alerts (AUD-010). + +### 23.7 Recoverability +- DCM crash after Stage 1 → Audit Forward Service replays `pending_forward` entries on restart (AUD-011) +- Audit Store unavailable → Commit Log accumulates; Audit Forward Service retries when recovered +- Commit Log quorum unavailable → operation aborted — no silent change + +### 23.8 System Policies +- `AUD-001` — Every modification produces Commit Log entry synchronously; Commit Log write failure aborts operation +- `AUD-002` — Audit records append-only and immutable while retention obligations apply +- `AUD-003` — Audit records survive at least as long as any referenced entity is live +- `AUD-004` — Post-lifecycle retention governed by policy; default P7Y +- `AUD-005` — Actor field must identify immediate actor + authorized_by chain +- `AUD-006` — record_hash + previous_record_hash hash chain required +- `AUD-007` — Action field must use closed vocabulary +- `AUD-008` — Audit Store must support queries by entity_uuid, actor_uuid, action, timestamp range, tenant_uuid, request_uuid, retention_status +- `AUD-009` — Audit Forward Service delivers with exponential backoff retry; cleared only after Audit Store confirmation AND retention window +- `AUD-010` — Hash chain verification first-class; chain breaks trigger immediate security alerts +- `AUD-011` — On restart, Audit Forward Service replays all pending_forward entries before accepting new operations +- `AUD-012` — Commit Log uses Raft consensus with quorum writes +- `AUD-013` — Stage 1 Commit Log timestamp is authoritative audit timestamp + +--- + +## SECTION 24 — DEPLOYMENT AND REDUNDANCY + +Every DCM component and store is designed for redundancy by default. Everything containerized. Profile-governed. Self-hosting. See `17-deployment-redundancy.md` for the complete model. + +### 24.1 Core Principles +- **Redundant by default** — every component and store has a redundancy model +- **Everything containerized** — all components run as Kubernetes pods; no bare-metal DCM +- **Profile-governed** — replica counts and quorum set by active Profile; not per-component +- **Stateless control plane** — all state in external stores; any pod can fail and be replaced +- **Self-hosting** — DCM's own deployment is a DCM resource; DCM manages itself + +### 24.2 Redundancy Matrix by Profile + +| Profile | CP Replicas | Store Replicas | Write Quorum | Zone Spread | Geo-Replication | +|---------|------------|---------------|-------------|------------|----------------| +| `minimal` | 1 | 1 | No | No | No | +| `dev` | 1 | 1 | No | No | No | +| `standard` | 3 | 3 | 2/3 | Preferred | No | +| `prod` | 3 | 3 | 2/3 | Required | Yes | +| `fsi` | 5 | 5 | 3/5 | Required | Yes | +| `sovereign` | 5 | 5 | 3/5 | Required | Within boundary | + +### 24.3 Store Redundancy Model + +| Store | Implementation | Write Quorum | Notes | +|-------|---------------|-------------|-------| +| Commit Log | etcd (Raft) | 2/3 | Stage 1 audit; < 1ms write | +| GitOps Store | Gitea/equivalent | 2/3 | Intent, Requested, Layers, Policies | +| Event Stream | Kafka/equivalent | 2/3 | Realized, Discovered, Audit events | +| Audit Store | Elasticsearch/equivalent | 2/3 | Indexed, queryable, compliance-grade | +| Search Index | Elasticsearch/equivalent | 1 | Non-authoritative — rebuildable from Git | + +### 24.4 Pod Security Model (all components) +`run_as_non_root: true` | `run_as_user: 65534` | `read_only_root_filesystem: true` | `allow_privilege_escalation: false` | `capabilities.drop: [ALL]` | mTLS for all inter-component communication (RED-009) + +### 24.5 Self-Hosting +DCM's own deployment is declared as a `dcm_deployment` DCM resource in Git. DCM runs drift detection on its own components. Bootstrap sequence: bootstrap installer → reads `dcm_deployment` from Git → provisions to target redundant state → hands off. + +### 24.6 The Repave Scenario +DCM lost entirely → bootstrap installer on new cluster → reads `dcm_deployment` from Git → provisions itself → rehydrates customer workloads in dependency order → drift detection validates. Recovery bounded by infrastructure provisioning speed — not backup restoration. + +### 24.7 System Policies +- `RED-001` — All DCM components run as containers in Kubernetes pods +- `RED-002` — All control plane components stateless +- `RED-003` — Profiles above `minimal`: replicas >= 3 with anti-affinity +- `RED-004` — Profiles above `minimal`: quorum writes with write_quorum >= 2 +- `RED-005` — Commit Log uses Raft consensus with quorum writes +- `RED-006` — DCM deployment declared as DCM resource in Git +- `RED-007` — DCM runs drift detection on its own components +- `RED-008` — Rolling updates must not reduce replicas below min_available +- `RED-009` — All component communication uses mTLS +- `RED-010` — Bootstrap manifest is the only DCM config outside DCM's management scope + +--- + +## SECTION 25 — WEBHOOKS, MESSAGING, AND EXTERNAL INTEGRATION + +### 25.1 The Three Integration Mechanisms +- **Outbound Webhooks** — DCM pushes event notifications to external HTTP endpoints +- **Inbound Webhooks** — External systems push requests, queries, and events to DCM +- **event routing service** — Persistent bidirectional event streaming with external message buses + +All three are authenticated, authorized, and audited identically to any other DCM API call. No privileged back-channel. + +### 25.2 Universal Ingress/Egress Actor Model + +Every request carries an immutable `ingress` block set by the DCM ingress layer: +```yaml +ingress: + surface: + protocol: + authenticated_via: + actor: + uuid / type / display_name / identity_source + auth_provider_uuid / auth_provider_type + roles / tenant_scope / groups / permissions + authorized_by: {method, authorizing_entity_uuid, expiry} + session_uuid / mfa_verified + external_identity: {provider, subject, claims} + webhook_registration_uuid / service_provider_uuid / parent_request_uuid + source_ip # audit only — never used for authorization +``` +The ingress block is immutable — policies may read but never modify it. Carried verbatim into audit records. Policies can act on any ingress field (surface, actor.roles, auth_provider_type, mfa_verified, etc.). + +Egress calls carry DCM's authenticated identity via the `egress` block: component, authenticated_via, credential_ref, originating_request_uuid. + +### 25.3 Outbound Webhooks +Optional and policy-governed. Profile sets defaults — fsi/sovereign may require via Policy Group. + +Key properties: +- **Schema adapters** — consumer pins to a schema version; DCM transforms forever; 90-day deprecation notice +- **Managed secret rotation** — automatic with transition window; consumer notified via signed event +- **Endpoint health** — suspend-not-delete on failure; full config retained for reactivation +- **Versioned registrations** — standard DCM artifact lifecycle; Git-managed +- **Sovereignty-aware** — delivery blocked if endpoint jurisdiction incompatible with Tenant sovereignty + +Delivery: at-least-once; per-entity ordering guaranteed; cross-entity ordering not guaranteed; `event_uuid` is idempotency key. + +### 25.4 Inbound Webhooks +DCM exposes typed authenticated endpoints: +- `POST /webhooks/inbound/request` — submit service request +- `POST /webhooks/inbound/query` — query entity state/catalog +- `POST /webhooks/inbound/event` — push provider state change / CI/CD signal +- `POST /webhooks/inbound/ingestion` — push brownfield ingestion data +- `POST /webhooks/inbound/data` — push enrichment or information data + +Callers must be registered as **webhook actors** with role, tenant_scope, permitted_operations, and rate_limit. Full Policy Engine evaluation — same as any other API call. Returns 202 Accepted + request_uuid for async operations. + +### 25.5 event routing service (Sixth Provider Type) +Persistent bidirectional event streaming. Supports: kafka, amqp, nats, mqtt, azure_service_bus, aws_eventbridge, gcp_pubsub, rabbitmq, custom. + +Inbound messages processed as authenticated API calls via registered webhook actor identity. Same Policy Engine evaluation as inbound webhooks. + +Architecture: internal Message Bus → Message Bus Bridge Service ↔ external message bus. + +### 25.6 Webhook System Policies +WHK-001 through WHK-014 — see doc 18. Key: ING-008 (ingress block immutable), ING-009 (full actor context required), ING-010 (egress authenticated), ING-011 (no anonymous access), ING-012 (webhook/message bus always authenticated). + +--- + +## SECTION 26 — AUTHENTICATION, AUTHORIZATION, AND AUTH PROVIDERS + +### 26.1 Auth Provider — The Eighth Provider Type +An **Auth Provider** answers two questions: (1) is this identity who they claim to be? and (2) what are they permitted to do? Every authentication mode is an Auth Provider implementation. + +**Authentication is always required — no anonymous access in any profile.** The difference between profiles is how much effort setup requires. + +### 26.2 Built-In Auth Provider (zero configuration) +Always registered, cannot be deregistered. Supports: +- **Static API key** — generated at bootstrap, shown once, 30 seconds to start +- **Local users** — `dcm user create --username admin --role platform_admin` +- **GitHub/GitLab OAuth** — opt-in, requires client_id + secret + +### 26.3 Auth Modes by Profile + +| Profile | Auth Modes | Setup Effort | +|---------|-----------|-------------| +| `minimal` | Static API key, Local user/password | 30 seconds – 2 min | +| `dev` | + GitHub/GitLab OAuth, FreeIPA/AD direct bind | 5–15 minutes | +| `standard` | + OIDC via broker, AD/FreeIPA direct | 30–60 minutes | +| `prod` | + OIDC direct, MFA configurable | 1–2 hours | +| `fsi` | + mTLS required, MFA required | 4–8 hours | +| `sovereign` | + Air-gapped OIDC/mTLS | 1–2 days | + +No anonymous access in any profile. No static API key in standard+. mTLS required in fsi/sovereign. + +### 26.4 LDAP / FreeIPA / Active Directory +FreeIPA: direct LDAP bind with optional Kerberos SSO and HBAC enforcement. Ideal for Red Hat / Linux-first environments. + +Active Directory: LDAP bind with `LDAP_MATCHING_RULE_IN_CHAIN` (OID 1.2.840.113556.1.4.1941) for nested group resolution. `sAMAccountName` or UPN for user lookup. Automatic DC failover. + +Both support: group_role_map (external groups → DCM roles), tenant_mapping (external groups → DCM Tenants), group_sync (interval-based re-sync), multiple domain controllers for failover. + +### 26.5 Multiple Auth Providers and Routing +Multiple providers registered simultaneously. Ingress layer routes based on authentication signal (mtls_client_cert → mtls provider; bearer_token → OIDC or API key provider; basic_auth → LDAP; hmac_signature → webhook provider; none → reject). + +Auth providers can be chained: authentication (LDAP bind) → enrichment (LDAP groups) → augmentation (OIDC userinfo for rich claims like department, cost_center). + +### 26.6 credential management service (Seventh Provider Type) +Cross-cutting dependency for all secret resolution. Supports: hashicorp_vault, aws_secrets_manager, azure_key_vault, gcp_secret_manager, kubernetes_secrets, cyberark, delinea, external_api, dcm_internal. + +All provider registrations, webhook configurations, and Auth Provider connections reference credentials via: +```yaml +secret_ref: + service_provider_uuid: + secret_path: "dcm/path/to/secret" + version: latest +``` +Credentials never stored in Git. Never appear in audit record values (only secret_path logged). Cached in memory with configurable TTL. + +### 26.7 Auth Provider Health +On unhealthy: existing sessions remain valid until TTL expiry; new auth attempts route to fallback_provider_uuid or are rejected. On_unhealthy options: alert, fallback_to_next, block_new_sessions. + +### 26.8 System Policies +AUTH-001 through AUTH-010 — see doc 19. Key: AUTH-008 (no anonymous access in any profile), AUTH-009 (webhook/message bus always authenticated), AUTH-007 (credentials always via credential management service). + +--- + +## SECTION 27 — REGISTRY GOVERNANCE + +### 27.1 The Three-Tier Registry + +| Tier | Name | Maintained By | Contains | +|------|------|--------------|---------| +| 1 | DCM Core | DCM Project team | Universal types (Compute.VirtualMachine, Network.VLAN, etc.) | +| 2 | Verified Community | Named community maintainers | Technology-specific types (OpenStack.HeatStack, KubeVirt.VirtualMachine) | +| 3 | Organization | Deploying organization | Organization-specific/proprietary types | + +### 27.2 The Federated Registry Model +Not centralized, not fully distributed — federated: +``` +DCM Project Registry (origin) → Organization Registry (local mirror) → Air-gapped Registry (offline copy) +``` +Every DCM deployment has exactly one active **Resource Type Registry** (sub-type of Information Provider). Air-gapped deployments use signed bundles verified against the organization's public key — no external connectivity required. + +### 27.3 PR-Based Proposal Workflow (Q9) +Resource Type proposals are Pull Requests against the registry repository. Automated gates before review: schema validation, FQN conflict check, dependency resolution, breaking change detection, test case coverage. Shadow validation in `proposed` status is mandatory before `active` promotion. + +**Review periods by change type:** Revision=3 days, Minor/Tier2=7 days, Tier1=14 days, Breaking=21 days, Deprecation=30 days, Emergency=waived (7-day shadow minimum). + +### 27.4 Deprecation Lifecycle — Default Policies (Q11) +Deprecation lifecycle is governed by **default DCM system policies** (REG-DP-001 through REG-DP-007), overridable via standard policy priority. FSI/sovereign profiles lock sunset periods as immutable. + +| Policy | Default | Overridable? | +|--------|---------|-------------| +| `REG-DP-001` | 30-day notification before deprecation | Yes | +| `REG-DP-002` | Sunset: Tier 1=P12M, Tier 2=P6M | Yes (locked in fsi/sovereign) | +| `REG-DP-003` | Migration window: P90D after retirement | Yes | +| `REG-DP-004` | Successor type required in deprecation notice | Yes | +| `REG-DP-005` | Retired types reject new requests | **No — structural** | +| `REG-DP-006` | Existing realizations → DEPRECATED_RUNTIME | Yes | +| `REG-DP-007` | Emergency migration floor: P30D | **No — floor** | + +DEPRECATED_RUNTIME: eligible for modify/decommission; not eligible for rehydration using deprecated type; drift detection continues. + +### 27.5 Version Resolution Policy (Q12) +Strictly enforced — no silent resolution to different version. DCM never auto-upgrades across major versions. + +`version_policy` options: `exact` | `compatible` (^major) | `latest_minor` (~minor) | `latest` + +Profile defaults: minimal=latest, dev/standard/prod=compatible, fsi/sovereign=exact. + +### 27.6 Provider Tie-Breaking Hierarchy (Q13) +When multiple providers satisfy all placement criteria equally: +1. **Policy preference** — Transformation policy injected preference_score or preferred_provider_uuid +2. **Provider priority** — numeric field on registration (default: 50; higher = preferred) +3. **Tenant affinity** — Policy Group declares preferred providers for resource types +4. **Cost analysis** — if Cost Analysis has current data AND cost is determinable and comparable (skip if not) +5. **Least loaded** — capacity utilization from reserve_query (skip if data unavailable) +6. **Consistent hash** — SHA-256(request_uuid + resource_type + sorted_candidate_uuids); deterministic, never round-robin + +Cost ranks above operational load because it is a business decision. 5% threshold — candidates within 5% cost are treated as equal. + +### 27.7 Resource Type Registry — Policy Governed (Q14) +The Resource Type Registry is fully policy-governed. Policies act on registry sync, activation, bundle import, and version upgrades. Profile-appropriate registry policy groups activated by default: + +| Group | Profile | Behavior | +|-------|---------|---------| +| `system/group/registry-minimal` | minimal | Advisory; pull everything; warn only | +| `system/group/registry-dev` | dev | Warn on unverified sources; Tier 1+2 | +| `system/group/registry-standard` | standard | Block unverified; sovereignty filter | +| `system/group/registry-prod` | prod | Vendor allowlist; audit all syncs; major version manual approval | +| `system/group/registry-fsi` | fsi | Exact pinning; immutable sunset; dual-approval syncs | +| `system/group/registry-sovereign` | sovereign | Signed bundles only; offline; no external connectivity | + +### 27.8 System Policies +REG-001 through REG-007 and REG-DP-001 through REG-DP-007 — see doc 20. + +--- + +## SECTION 28 — STORAGE ARCHITECTURE + +### 28.1 Git Repository Structure (Q79) +Handle-based directory structure. Four repos: Intent, Requested, Layers, Policies. Minimal/dev may use monorepo; standard+ use separate repos. `main` is authoritative. Tenant isolation under `{tenant-uuid}` directories. DCM service account handles all Git reads/writes — no direct Tenant Git access. + +``` +dcm-intent/tenants/{tenant-uuid}/requests/{request-uuid}/intent.yaml +dcm-requested/tenants/{tenant-uuid}/requests/{request-uuid}/requested-payload.yaml +dcm-layers/{domain}/{type}/{name}/v{Major}.{Minor}.{Revision}.yaml +dcm-policies/{domain}/{type}/{name}/v{Major}.{Minor}.{Revision}.yaml +``` + +### 28.2 Multi-Region Replication (Q80) +Declared capability on store configuration. Active Profile determines minimum requirements: +- minimal/dev: 1 replica, no multi-region +- standard: 3 replicas, strong/bounded consistency +- prod/fsi/sovereign: 3-5 replicas, strong consistency, geo-replicated +- sovereign: multi-region required but within sovereignty boundary only + +(STO-001) + +### 28.3 Data Store Failure Handling (Q81) +Per store type — policy-governed: +- **Commit Log:** quorum unavailable → abort operation (no silent changes) +- **GitOps Stores:** unavailable → queue writes locally (max size + max age); explicit reject on exhaustion +- **Event Stream:** producer queues locally; consumer resumes from last offset on recovery +- **Audit Store:** two-stage model — accumulates in Commit Log; operations not blocked +- **Search Index:** non-authoritative; degrades gracefully; full rebuild on recovery + +(STO-002) + +### 28.4 Search Index — Separate Sub-Type (Q82) +Separate PostgreSQL store contract — distinct from GitOps stores. Non-authoritative, rebuildable from authoritative stores. Consistency lag declared (e.g., PT5M). API queries may specify `freshness: authoritative` to bypass index. (STO-003) + +### 28.5 Audit Store — Specialized Sub-Type (Q83) +Specialized PostgreSQL store contract — NOT the same as Event Stream. Properties: append-only with immutability enforcement, hash chain integrity, reference-based retention tracking, compliance-grade multi-dimensional queries. Event Stream is the delivery channel; Audit Store is the compliance destination. (STO-004) + +--- + +## SECTION 29 — PROVIDER SOVEREIGNTY DECLARATIONS + +### 29.1 Obligation +Every provider registration (Service, Information, Message Bus, Policy, Auth Provider) MUST include a `sovereignty_declaration` block. Contractual obligation — not optional metadata. + +### 29.2 What Sovereignty Declaration Covers +- **operating_jurisdictions** — countries and legal jurisdictions where provider physically operates +- **legal_frameworks** — applicable frameworks (GDPR, HIPAA, FedRAMP, ITAR, etc.) +- **data_residency_guarantee** — data never leaves declared jurisdictions (true/false) +- **data_transit_jurisdictions** — jurisdictions data transits through during operations +- **external_dependencies** — external connectivity requirements, air_gap_capable flag, external services with data sharing details +- **sub_processors** — third-party sub-processors with jurisdiction and data handled +- **government_access_risk** — which governments can legally compel access +- **certifications** — current certifications with validity periods (ISO-27001, SOC2, PCI-DSS, FedRAMP) +- **audit_rights** — customer audit rights and notice periods +- **change_notification** — mandatory notification events and SLA (e.g., PT24H) + +### 29.3 Change Notification and DCM Response +Provider MUST notify DCM when any sovereignty data changes. DCM treats sovereignty changes as discovered drift → Policy Engine re-evaluation: +- **No violations:** update record, emit webhook event, notify Tenants (informational) +- **Violations found:** for each affected resource, policy declares action: + - `notify_only` — inform Tenant; no automatic action + - `pause` — suspend resource; Tenant must act + - `migrate` — Provider-Portable Rehydration to compliant provider (sequential) + - `emergency_migrate` — parallel provisioning before decommission + +Sovereignty violation record created in Audit Store. Notifications: Tenant owner, platform admin, data_protection_officer. + +### 29.4 Auto-Migration +Policy declares `migrate` or `emergency_migrate` → DCM uses Provider-Portable Rehydration. Non-compliant provider excluded from placement candidate set. Full audit trail linking violation record to migration request. (SOV-001 through SOV-005) + +--- + +## SECTION 30 — GIT PR INGRESS + +### 30.1 Concept +DCM supports `git_pr_merge` and `git_pr_open` as ingress surfaces. Teams submit standard DCM resource definition YAML as Pull Requests. DCM's Git Request Watcher monitors designated repositories. + +### 30.2 Git Actor Identity Resolution +**DCM trusts the Git server's authentication assertion — not user-declared Git configuration.** Git `user.email` self-declaration is ignored — spoofing vector. + +Resolution methods (all go through registered Auth Provider): +- `oidc_subject_lookup` — Git server OAuth subject → OIDC Auth Provider → DCM actor +- `ldap_username_lookup` — Git server username → LDAP/AD Auth Provider → DCM actor +- `ssh_key_fingerprint` — key fingerprint → DCM SSH key registry → DCM actor +- `webhook_service_account` — CI/CD service account → registered webhook actor + +**The resolved actor has IDENTICAL roles, groups, and tenant scope to the same user logging in via web UI.** Git PR ingress does not grant different permissions than any other surface. Same Auth Provider, same group mappings, same tenant scope enforcement. + +**Unresolvable identity → explicit PR rejection comment** with actionable guidance. Never silently ignored. + +### 30.3 PR Lifecycle +1. PR opened → DCM resolves author → Auth Provider → shadow policy evaluation posted as PR comments +2. Human review + Git branch protection approvals +3. PR merged → actor re-verified at merge time (not assumed from PR open) → full nine-step assembly → realization result posted as PR comment +4. Realized state committed to `realized/` directory (optional) + +### 30.4 The git_context in ingress block +```yaml +ingress: + surface: git_pr_merge + actor: + git_context: + repository / pr_number / pr_url / merge_commit + pr_author / pr_reviewers / pr_approved_by + # pr_approved_by: DCM resolves reviewer Git identities via same Auth Provider +``` + +### 30.5 Policy Use Cases +- Require specific approvers in pr_approved_by before processing +- Require MFA for Git PR merges in prod Tenants +- Restrict resource types submittable via Git PR +- Require actor to be in authorized Tenant group +- Post shadow evaluation results as PR comments + +### 30.6 System Policies +GIT-001 through GIT-008 — see doc 18. AUTH-011 — Git identity resolution uses registered Auth Provider; same role/group/tenant scope as any other ingress. + +--- + +## SECTION 31 — ENTITY AND DEPENDENCY GAPS + +### 31.1 Ownership Transfers (Q25) +Ownership transfers are **unlimited by default**. Each transfer is immutably recorded with a monotonically incrementing `transfer_number` and mandatory reason field. Policy may declare a maximum per resource type via GateKeeper. ENT-001. + +### 31.2 Bare Metal Indivisibility (Q26) +`Compute.BareMetal` declares `allocation_model: whole_unit` and `shareability.allowed: false` (structural lock). Placement holds are exclusive — no concurrent holds on the same server. Provider must report full physical identity (serial_number, hardware_profile) in realized payload and notify DCM of any sharing attempt. ENT-002. + +### 31.3 Capacity Confidence Actions (Q27) +Confidence ratings trigger policy-governed automatic actions: +- `HIGH` → proceed (all profiles) +- `MEDIUM` → proceed_with_warning (minimal/dev) or refresh_before_placement (prod/fsi/sovereign) +- `LOW` → proceed_with_warning (minimal), refresh_before_placement (dev/standard), reject (prod/fsi/sovereign) + +LOW confidence triggers a Mode 1 Information Provider query before finalizing placement in standard+ profiles. Policy Group overrides per resource type. ENT-003. + +### 31.4 Process Resource Execution Time (Q28) +`max_execution_time` is **mandatory** on Process Resource entities. Enforced by the Lifecycle Constraint Enforcer as a standard TTL. Profile governs default `on_max_exceeded`: +- minimal/dev: `notify` +- standard/prod: `escalate` +- fsi/sovereign: `terminate` + +ENT-004. + +### 31.5 SUSPENDED State Billing (Q29) +`billing_state` is a first-class field on all entities: `billable | non_billable | reduced_rate`. Policy injects `billing_state` and `billing_metadata` (rate_multiplier, billable_components) during state transitions. Cost Analysis component consumes the field — DCM carries the billing signal, policy decides the billing model. ENT-005. + +### 31.6 Dependency Graph Versioning (Q30) +Dependency graphs versioned as part of their parent catalog item — not independently. New required dependency or removed dependency = **major (breaking) version bump**. New optional dependency = minor bump. Constraint change = revision bump. Dependency graph version captured in assembly provenance. ENT-006. + +### 31.7 Dependency Graph Storage (Q31) +Not a separate entity. Three levels: +- Declared graph: embedded in Resource Type Specification (GitOps) +- Resolved graph: embedded in `placement.yaml` in Requested State +- Realized graph: Realized State events per dependency + +ENT-007. + +### 31.8 Dependency Graph Depth (Q33) +Profile-governed maximum: minimal=20, dev=15, standard/prod=10, fsi/sovereign=7. Requests exceeding max depth rejected with clear error. Circular dependency detection always enforced regardless of depth configuration. ENT-008. + +### 31.9 Meta Provider Composition Visibility (Q34) +Meta Providers declare `composition_visibility`: +- `opaque` — consumer sees only top-level service; sub-resources not in DCM; drift on realized payload only +- `transparent` — all sub-resources registered as DCM entities; full drift detection +- `selective` — provider declares which sub-resources are DCM-visible + +ENT-009. + +### 31.10 System Policies +ENT-001 through ENT-009 — see docs 06 and 07. + +--- + +## SECTION 32 — INFORMATION PROVIDER CONFIDENCE SCORING AND AUTHORITY + +### 32.1 Confidence Scoring — 0 to 100 +Every Information Provider field value carries a confidence score (0-100). DCM computes scores — providers do not self-declare. Score bands for policy use: very_high (81-100), high (61-80), medium (41-60), low (21-40), very_low (0-20). + +**Formula:** +``` +confidence_score = min(100, base_score × freshness_multiplier × corroboration_multiplier × authority_multiplier) +``` + +| Factor | Values | +|--------|--------| +| Base score | primary_authoritative=90, secondary=70, discovered=60, advisory=50, self_reported=40, inferred=30 | +| Freshness | <1h=1.00, <1d=0.95, <7d=0.85, <30d=0.70, >30d=0.50 | +| Corroboration | 1 source=1.00, 2 agree=1.10, 3+ agree=1.15, disagree=0.60 | +| Authority | primary=1.00, secondary=0.85, advisory=0.70 | + +### 32.2 Authority as Layer Data +Authority scope and priority for Information Providers are declared in **platform or system domain layers** — not just policies. This is static organizational knowledge ("our CMDB is authoritative for business unit data"). Layer-defined authority establishes the default; policies act on confidence scores at runtime. + +### 32.3 Ingestion-Time Conflict Detection +Conflict detection at ingestion time (7-step flow): schema validation → authority scope check → confidence score computation → conflict detection → resolution policy → entity record update → INGEST audit record. + +**Resolution strategies:** `higher_authority_wins` | `higher_confidence_wins` | `higher_priority_wins` | `escalate` | `merge` (array fields only) + +Authority scope conflicts detected at **registration time** — two providers claiming primary authority for the same field cannot both go active without explicit resolution. + +### 32.4 Write-Back (Q63) +Optional declared capability. Policy triggers write-back — never automatic. Produces ENRICH audit records. Credentials via credential management service. (INF-002) + +### 32.5 Extended Schema Versioning (Q64) +Semver semantics: field removal/type change = major (breaking); new optional field = minor; constraint change = revision. Migration plan required for major bumps. (INF-003) + +### 32.6 Well-Known Provider Registry (Q65) +Three-tier registry (Core/Community/Organization) — same governance model as Resource Type Registry. Separate registries, shared infrastructure. (INF-004) + +### 32.7 Air-Gapped Verification (Q66) +Three modes: pre-verified signed bundle, internal mTLS (for internal providers), periodic online re-verification with cached tokens. Profile governs cache expiry behavior. (INF-005) + +### 32.8 System Policies +INF-001 through INF-008 — see docs 10 and 21. + +--- + +## SECTION 33 — DCM FEDERATION AND CROSS-INSTANCE COORDINATION + +### 33.1 Three Relationship Types +- **Peer DCM** — same organizational level; share resources/information +- **Parent-Child DCM** — hierarchical; parent has governance overlay; does not own child resources +- **Hub DCM** — specialized parent as resource allocation clearinghouse + +All use the Universal Group Model: federation group (peers) or tenant_boundary nesting (parent-child). + +### 33.2 Provider Federation Eligibility +Every provider registration declares `federation_eligibility`: +- `mode: none` — cannot participate in any federation (sovereign/classified providers) +- `mode: selective` — only with explicitly declared partners +- `mode: open` — any trusted DCM peer (sovereignty checks always apply) + +**Layer-defined defaults** in `platform` domain layer. Individual registrations may be **more restrictive** — never more permissive without GateKeeper approval. + +**Federation scope declares:** permitted resource types + operations, data sharing permissions, max concurrent allocations. Remote DCMs CANNOT decommission local resources through a tunnel. + +**Storage providers default to `mode: none`** — data sovereignty prohibits storage federation unless explicitly authorized. + +### 33.3 The DCM Provider — Ninth Provider Type +Wraps another DCM instance's API. Always mTLS (non-configurable). Sovereignty checks mandatory before tunnel establishment. Local DCM policies govern ALL resources from any tunnel. + +**Non-negotiable primary concerns on all tunnels:** +- Sovereignty: verified before establishment; data classification checked per egress +- Authentication: always mTLS — no API key or bearer token +- Authorization: local policies govern; remote policies cannot override +- Audit: records in BOTH DCM instances; shared correlation_id +- Observability: cross-DCM allocation visible in both instances + +### 33.4 Cross-DCM Confidence Scoring +``` +cross_dcm_confidence = source_resource_confidence × (tunnel_trust_score / 100) +``` +Federation trust score (0-100): factors include identity verification, sovereignty compatibility, certifications currency, audit trail integrity, uptime, compliance. + +### 33.5 DCM Export/Import +Signed export package: tenants, layers, policies, provider registrations (not credentials), entity intent/requested states, groups, audit records with hash chain. Never export credentials. + +Import trust score (0-100): source verification + sovereignty compatibility + data completeness + schema compatibility + audit trail integrity. Low score → reject or escalate. + +### 33.6 System Policies +DCM-001 through DCM-008 — see doc 22. + +--- + +## SECTION 34 — OPERATIONAL AND PERFORMANCE GAPS + +### 34.1 Field-Level Provenance Models (Q7, Q8) + +**Three configurable models — organization chooses; profile provides default:** + +**Model A — Full Inline** +All provenance stored on entity record. Simplest queries, highest storage cost, no tooling required. +- ✅ Auditors read one record — regulatory clarity +- ✅ No dependency on layer chain store +- ❌ Very high storage volume at scale +- ❌ Write amplification + +**Model B — Deduplicated (Content-Addressed) ← RECOMMENDED** +Layer chain is the deduplication key. Classical content-addressed dedup (like Git objects, Docker layers). Only delta fields store unique provenance. 95-99% storage reduction for standardized deployments. Lossless because layer chains are immutable. +- ✅ Dramatic storage reduction +- ✅ Full audit reconstruction always possible +- ✅ Write performance highest (layer-matching fields free) +- ❌ Chain traversal tooling required for queries +- ❌ Layer chain must be retained while any entity references it + +**Model C — Tiered Archive** +Hot (full detail) → warm (change events) → cold (hash anchors). Degrades gracefully. +- ✅ Balances cost and access speed +- ✅ Compliant for long retention +- ❌ Cross-tier queries for long time ranges +- ❌ Cold tier requires full records from warm/hot for reconstruction + +**Model B+C — Combined** +Maximum efficiency: content-addressed dedup + tiered archival of chains and deltas. + +**Profile defaults:** + +| Profile | Provenance Group | Rationale | +|---------|----------------|-----------| +| minimal, dev | `system/group/provenance-full-inline` | Simplicity; scale not a concern | +| standard, prod | `system/group/provenance-deduplicated` | Scale matters; tooling justified | +| fsi, sovereign | `system/group/provenance-full-inline` | Regulatory clarity; self-contained | + +Organizations override by swapping the active provenance Policy Group. + +**Audit completeness guarantee (OPS-002):** Regardless of model, full provenance is always reconstructable from entity record + layer chain store + Audit Store combined. + +### 34.2 Background Conflict Validation (Q85) +Event-triggered (primary) on layer ingestion/update — async, non-blocking. Scheduled weekly sweep as safety net. Both triggers produce same conflict record format and audit trail. (OPS-003) + +### 34.3 Policy Minimum Review Periods (Q86) +Change-type minimum periods: GateKeeper=14d, Validation=7d, Transformation=3d. Profile multipliers: minimal=0×, dev=0.5×, standard=1×, prod=1.5×, fsi/sovereign=2×. DCM enforces — not bypassable except emergency activation with dual-approval audit. (OPS-004) + +### 34.4 Shadow Evaluation Store (Q87) +Dedicated **Validation Store** (not Audit Store). Queryable and modifiable. Links to Audit Store EVALUATE events via audit_record_uuid. Default retention P90D after policy promotion/retirement. (OPS-005) + +### 34.5 Artifact Status Extensions (Q88) +Five standard statuses (developing/proposed/active/deprecated/retired) are invariant — no custom additions. Organizations use status_metadata for workflow state (purely informational, no system behavior). Policy gates status transitions based on status_metadata field values. (OPS-006) + +### 34.6 System Policies +OPS-001 through OPS-006 — see docs 03 and 06. + +--- + +## SECTION 35 — PROFILE COMPOSITION — POSTURE AND COMPLIANCE DOMAINS + +### 35.1 The Two-Dimensional Profile Model + +**Profiles compose two orthogonal dimensions:** + +``` +Complete Profile = Deployment Posture Group + Compliance Domain Group(s) +``` + +**Dimension 1 — Deployment Posture** (vertical axis): How DCM infrastructure behaves — redundancy, enforcement strictness, audit retention, tenancy model. + +| Posture Group | Key Behaviors | +|--------------|--------------| +| `system/group/posture-minimal` | Advisory; single instance; no redundancy | +| `system/group/posture-dev` | Warn-not-block; basic logging | +| `system/group/posture-standard` | Full enforcement; 3-replica; explicit cross-tenant | +| `system/group/posture-prod` | Full enforcement + SLA; geo-replicated | +| `system/group/posture-hardened` | 5-replica; 7-year audit; dual approval | +| `system/group/posture-sovereign` | Air-gap; deny_all; 10-year audit; signed bundles | + +**Dimension 2 — Compliance Domain** (horizontal): Which regulatory frameworks govern data and resources. + +| Compliance Group | Domain | +|----------------|--------| +| `system/group/compliance-fsi` | Financial Services — Basel III, SOX, Dodd-Frank | +| `system/group/compliance-pci-dss` | Payment Card Industry — PCI-DSS v4 | +| `system/group/compliance-hipaa` | Healthcare — HIPAA/HITECH PHI | +| `system/group/compliance-fedramp-moderate` | US Federal Moderate — NIST 800-53 Moderate | +| `system/group/compliance-fedramp-high` | US Federal High — NIST 800-53 High | +| `system/group/compliance-dod-il2` through `il6` | DoD Impact Levels | +| `system/group/compliance-government` | Government/public sector | +| `system/group/compliance-gdpr` | EU GDPR data protection | +| `system/group/compliance-iso27001` | ISO 27001 information security | +| `system/group/compliance-nist-800-53` | NIST 800-53 security framework | +| `system/group/compliance-soc2` | SOC 2 service organization controls | +| `system/group/compliance-nerc-cip` | Critical infrastructure energy/utilities | +| `system/group/compliance-sovereign` | Sovereign/classified — air-gap, HSM, signed bundles | + +### 35.2 Built-In Profile Compositions + +The six core profiles are posture+compliance compositions: + +``` +minimal = posture-minimal +dev = posture-dev +standard = posture-standard +prod = posture-prod +fsi = posture-hardened + compliance-fsi + compliance-pci-dss + compliance-iso27001 +sovereign = posture-sovereign + compliance-sovereign +``` + +**Extended built-in profiles:** + +| Profile | Extends | Compliance Groups Added | +|---------|---------|------------------------| +| `system/profile/hipaa-prod` | prod | compliance-hipaa, compliance-iso27001 | +| `system/profile/hipaa-sovereign` | sovereign | compliance-hipaa | +| `system/profile/fedramp-moderate` | prod | compliance-fedramp-moderate, compliance-nist-800-53 | +| `system/profile/fedramp-high` | sovereign | compliance-fedramp-high, compliance-nist-800-53 | +| `system/profile/government` | prod | compliance-government, compliance-nist-800-53 | +| `system/profile/dod-il4` | sovereign | compliance-dod-il4, compliance-fedramp-high, compliance-nist-800-53 | +| `system/profile/dod-il5` | dod-il4 | compliance-dod-il5 | +| `system/profile/dod-il6` | dod-il5 | compliance-dod-il6, compliance-sovereign | + +### 35.3 HIPAA Compliance Group Key Controls +- PHI field classification enforcement (phi: true tag required) +- PHI access control (phi_authorized role required) +- Audit retention: P6Y minimum +- AES-256 at rest, TLS 1.3 in transit for PHI +- Breach notification workflow via sovereignty_violation_record +- BAA tracking: providers declare baa_in_place in sovereignty_declaration +- Minimum Necessary standard on Mode 4 data_request_spec + +### 35.4 Government/DoD Key Controls +- Data classification mandatory on all resources +- Cross-boundary controls for classification levels +- Audit retention: P10Y minimum +- DoD IL4+: CUI handling markers; foreign sub-processor exclusion +- DoD IL5+: sovereign posture within US boundary +- DoD IL6: classified + HSM required for key management + +### 35.5 Tenant-Level Compliance Overlay +**One DCM deployment, multiple compliance postures per Tenant:** +```yaml +tenant_config: + active_profile: system/profile/prod # posture from platform + compliance_groups: + - system/group/compliance-hipaa # this Tenant handles PHI + - system/group/compliance-pci-dss # this Tenant processes payments +``` +Clinical Tenants (HIPAA) + Billing Tenants (HIPAA + PCI-DSS) + Admin Tenants (standard) — all on same DCM platform. + +### 35.6 System Policies +- `PROF-001` — Profiles compose posture + compliance domain groups +- `PROF-002` — Compliance groups apply at platform or Tenant level; additive not replacing +- `PROF-003` — DCM ships built-in compliance groups for all major domains +- `PROF-004` — implementation_posture groups (provenance model etc.) are independent of compliance domain + +--- + +## SECTION 36 — GROUPING AND RELATIONSHIP GAPS + +### 36.1 Group Subclass Registry (Q35) +No separate registry needed. `group_class` is the closed system-behavior set. `group_subclass` is open and advisory — freely declared, never validated. DCM ships a community subclass catalog as a non-authoritative reference (same infrastructure as well-known provider registry). GRP-011. + +### 36.2 Group Sovereignty Interaction (Q36) +Class-specific sovereignty rules: +- `tenant_boundary` — NEVER cross-sovereignty (structural, not configurable) +- `resource_grouping` — permitted by default; policy may restrict for classified resources +- `policy_collection` / `layer_grouping` — always permitted (governance artifacts, no data) +- `composite` — governed by most restrictive member type +- `federation` — permitted with DCM federation rules (DCM-003) + +GRP-012. + +### 36.3 Tenant Decommission Lifecycle (Q37) +Mandatory four-phase staged decommission: +1. **Pre-decommission validation** (blocking): resource state, cross-tenant relationships, compliance holds, rehydration leases, child groups resolved first +2. **Resource decommission**: cascade (default) / retain (ORPHANED state) / notify (PENDING_DECOMMISSION) +3. **Group membership cleanup**: remove from all memberships; empty federation groups → EMPTY state +4. **Audit record archival**: all records enter post-lifecycle retention — NEVER destroyed + +Child tenant_boundary groups must be resolved BEFORE parent decommission (GRP-INV-003). GRP-013. + +### 36.4 Time-Bounded Group Membership (Q38) +Already in Universal Group Model via `valid_from` / `valid_until` on every membership. Lifecycle Constraint Enforcer handles expiry. `on_expiry` actions: `remove` / `notify` (default) / `suspend_member`. `warn_before_expiry: P7D` standard. Expiry produces `MEMBER_REMOVE` audit record with `reason: membership_ttl_expired`. GRP-014. + +### 36.5 Group Policy Inheritance (Q39) +Class-specific defaults, all profile-governed: +- `tenant_boundary`: `opt_out` (standard/prod) — parent cascades unless child excludes; `opt_in` (minimal/dev/fsi/sovereign) +- `federation`: always `opt_in` — peer consent required, not configurable +- `composite`: `opt_out` by default, configurable +- `resource_grouping` / `policy_collection`: not applicable + +GRP-015. + +### 36.6 Relationship Role Validation (Q58) +Advisory by default. Resource Type Spec may declare `permitted_relationship_roles` with `role_validation: advisory | enforced`. Advisory → assembly warning for unknown roles. Enforced → unknown roles rejected at request time. DCM ships community role catalog as non-authoritative reference. REL-020. + +### 36.7 Relationship Graph Depth (Q60) +Profile-governed max: minimal=25, dev=20, standard/prod=15, fsi/sovereign=10. Circular detection always enforced. Depth = graph traversal distance between any two entities (NOT count of relationships on one entity). REL-021. + +### 36.8 System Policies +GRP-011 through GRP-015 — see doc 15. REL-020, REL-021 — see doc 09. + +--- + +## SECTION 37 — AUDIT AND OBSERVABILITY GAPS + +### 37.1 Information Provider Trust Score Validation (Q15) + +Dual-trigger model — same pattern as conflict validation: + +**Event-triggered (primary):** push fails schema validation → degraded; push conflicts with primary authority → degraded; health check fails → degraded; sovereignty declaration change → re-evaluated; registration update → re-verification triggered. + +**Scheduled (safety net):** daily health check; weekly full re-verification (identity, sovereignty, certifications, schema). fsi/sovereign: daily full re-verification. + +**Trust score → source_trust mapping:** +- Score ≥ 80: `verified` → confidence multiplier 1.00 +- Score 60-79: `degraded` → confidence multiplier 0.75 +- Score < 60: `suspended` → no new pushes accepted; score = 0 + +(INF-009) + +### 37.2 Confidence Scoring — The Hybrid Descriptor Model (Q15 extended) + +**Three-tier model:** +- **Confidence Descriptor** (primary — stored): `authority_level` + `corroboration` + `source_trust` + `last_updated_at` +- **Derived Score** (0-100, computed on demand): mathematical composition for placement and conflict resolution +- **Derived Band** (very_high/high/medium/low/very_low, computed on demand): what humans and policies use + +**Who sets each field:** +- `authority_level`: set at registration from authority declaration layer (static per field per provider) +- `corroboration`: computed at ingestion (confirmed/single_source/contested based on multi-provider comparison) +- `source_trust`: maintained by trust scoring system (event-triggered + scheduled) +- `last_updated_at`: set at each push event + +**Score formula:** `min(100, base(authority_level) × freshness_mult × corroboration_mult × trust_mult)` + +**Freshness is computed at query time** from `now - last_updated_at` — never stored (avoids staleness). Score and band computed at query time for the same reason. + +**Audit reconstruction:** authority_level (from registration) + corroboration (from ingestion event) + source_trust (from trust audit) + last_updated_at (from push event) → score and band fully reconstructable from stored facts. + +**Configurable derivation:** base scores, freshness thresholds, and band thresholds configurable via Policy Group — stored as versioned policy artifacts. + +### 37.3 Audit vs Observability — Definitively Separate (Q16) + +| | Audit | Observability | +|--|-------|--------------| +| Purpose | WHAT HAPPENED + WHO authorized | SYSTEM HEALTH + PERFORMANCE | +| Consumers | Auditors, compliance, legal | SREs, operators, dashboards | +| Write rate | Low (per action) | Very high (per second) | +| Retention | P7Y+ | Days to months | +| Mutability | Never — append-only | Downsampling acceptable | +| Accuracy | 100% required | Statistical sampling OK | +| Failure | Missing = compliance violation | Missing = operational inconvenience | + +They cannot be combined without violating one contract or the other. Observability may reference audit record UUIDs for correlation. AUD-013. + +### 37.4 Curated Observability Event Stream (Q17) + +DCM publishes a curated event stream via Message Bus — NOT raw metrics. Policy governs what is published, subscriber roles, and redaction. Published by default: component.health_changed, resource.state_transition, capacity.threshold_crossed, drift.detected, security.gatekeeper_triggered, provider.confidence_changed. NOT published by default: metrics.raw (explicit policy opt-in required). + +Observability events on Message Bus do NOT replace audit records. OBS-001. + +### 37.5 System Policies +- `INF-009` — dual-trigger trust score; degraded/suspended states; policy governs thresholds +- `AUD-013` — audit and observability definitively separate; different contracts/consumers +- `OBS-001` — curated observability event stream via Message Bus; policy-filtered; raw metrics opt-in only + +--- + +## SECTION 38 — OVERRIDE CONTROL AND ENHANCEMENT GAPS + +### 38.1 The Complete Field Lifecycle Contract + +Three questions together define the full field governance model across a resource's lifecycle: + +``` +ASSEMBLY TIME (Q50 — override_preference): + Which layers can set this field? + immutable → only this layer and higher-domain layers + constrained → any layer within declared bounds + allow → any layer + +CATALOG PRESENTATION (Q52 — constraint_visibility): + What does the consumer see about this field's constraints? + full → constraint + bounds + reason + suggestions + summary → bounds only + hidden → enforced silently + +POST-REALIZATION (Q56 — editability): + Can the consumer change this field after provisioning? + editable: false → requires reprovisioning + editable: true → targeted delta update permitted (within edit_constraints) +``` + +### 38.2 Override Preference Enforcement (Q50) + +`override: allow | constrained | immutable` on layer fields is enforced by the Request Payload Processor during assembly **Step 3 (Layer Merge)**. No separate GateKeeper policy needed. + +**Authority rule:** `immutable` prevents overrides from lower-authority domains only. A platform domain `immutable` field blocks tenant/service/provider/request — but system domain can still override. Higher authority always wins. + +**GateKeeper escalation:** A GateKeeper policy may additionally lock an `allow` or `constrained` field at runtime — for compliance mandates the layer author didn't anticipate. + +**Enforcement:** If a lower-priority layer or consumer sets an `immutable` field → assembly halts with clear error identifying the conflicting layer and locking layer. + +LAY-005. + +### 38.3 Constraint Schema Visibility (Q52) + +Constrained fields expose their constraint schema to consumers in the Service Catalog UI and Consumer API at a policy-governed disclosure level. + +**Disclosure levels:** `full` (constraint + bounds + reason + suggestions), `summary` (bounds only), `hidden` (silently enforced) + +**Profile defaults:** minimal/dev/standard → full; prod/fsi → summary; sovereign → hidden + +**API:** `GET /api/v1/catalog/items/{id}/schema` returns field schemas at the declared visibility level for the authenticated consumer's Tenant profile. + +Policy may override per field or resource type. + +LAY-006. + +### 38.4 Post-Realization Field Editability (Q56) + +**Editability is orthogonal to override_preference:** +- `override` governs assembly time (which layers can set the field during request construction) +- `editable` governs post-realization (can the consumer update the field on a running resource) + +**Declared on Resource Type Specification:** +```yaml +fields: + cpu_count: editable: true; edit_constraints: {range: 1-32}; requires_restart: true + hostname: editable: false; non_editable_reason: "Requires reprovisioning" + dns_servers: editable: true; requires_restart: false + region: editable: false; non_editable_reason: "Region immutable post-realization" +``` + +**Update request flow:** validate editable → validate edit_constraints → evaluate edit_policy → produce delta Requested State → dispatch delta to provider → update Realized State + +**Critical:** Updates are targeted deltas — **layers do NOT re-run**. Only changed fields validated and dispatched. Layer chain NOT re-assembled for updates. + +Editable fields and edit_constraints visible in Service Catalog at same constraint_visibility level as constraint schemas. + +ENT-010. + +### 38.5 System Policies +- `LAY-005` — override: allow/constrained/immutable enforced at Step 3; immutable = lower-authority only; GateKeeper may additionally lock +- `LAY-006` — constraint schema visible at full/summary/hidden level; profile-governed; API endpoint +- `ENT-010` — editability first-class on Resource Type Spec; independent of override_preference; updates = targeted deltas; layers do not re-run + +--- + +## SECTION 39 — FOUR STATES OPERATIONAL GAPS + +### 39.1 Entity UUID Preservation on Rehydration (Q75) + +Entity UUIDs are **preserved on rehydration** — UUID is the stable logical identity across all provider migrations, sovereignty changes, and lifecycle events. All external references (CMDB, cost attribution, audit trails, relationships, dependencies) use UUID. Generating a new UUID would silently break all references. + +What changes: the **provider-side identifier** (actual VM ID, container name, resource handle). Recorded in `rehydration_history`: +```yaml +entity: + uuid: # PRESERVED + rehydration_history: + - rehydration_uuid: + from_realized_entity_id: "vm-12345" # no longer valid + to_realized_entity_id: "vm-67890" # new provider ID + trigger / from_provider / to_provider / rehydrated_by + intent_state_ref / previous_requested_state_ref / new_requested_state_ref +``` + +**Rehydration is transactional** — failure preserves pre-rehydration state completely; no UUID change, no partial state. RHY-005. + +### 39.2 Pinned Authentication Level for Rehydration (Q76) + +Entities may declare `rehydration_constraints.min_auth_level` — a minimum floor the rehydrating actor must meet. Prevents privilege escalation through the rehydration mechanism. + +Auth levels (ascending): `api_key → ldap_password → oidc → oidc_mfa → hardware_token → hardware_token_mfa` + +**Profile enforcement:** minimal/dev = not enforced; standard = advisory warn; prod = enforced reject; fsi = enforced + dual approval on mismatch; sovereign = dual approval always. + +**Automated rehydration** (DCM service account for provider migration): requires `allow_delegated_rehydration: true` OR platform admin manual authorization → full audit trail preserving accountability. RHY-006. + +### 39.3 Concurrent Rehydration Handling (Q77) + +**Exclusive rehydration lease per entity** — only one rehydration active at a time. + +```yaml +rehydration_lease: + entity_uuid / lease_uuid / acquired_by / acquired_at + lease_ttl: PT2H # expires if rehydration hangs + trigger / status: active|completed|failed|expired +``` + +**Concurrent request:** +- Active lease + higher priority incoming → escalate to platform admin; queue +- Active lease + same/lower priority → reject with retry guidance; REHYDRATION_BLOCKED audit + +**Priority (1=highest):** security/compliance emergency → manual platform admin → automated sovereignty migration → provider decommission → manual consumer request + +**TTL expiry:** marks rehydration `failed`; releases lease; triggers drift detection for partial completion assessment. RHY-007. + +### 39.4 Discovered State Retention (Q78) + +Ephemeral operational data — NOT the source of truth (Realized State is). Three modes: + +| Mode | Behavior | +|------|---------| +| `rolling_window` | Keep last N days; useful for trending | +| `event_driven` | Retain until drift_resolved; ensures investigation has snapshot | +| `hybrid` (recommended) | min_retention + retain_until_drift_resolved + max_retention ceiling | + +**Profile defaults:** +- minimal: rolling P3D +- dev: rolling P7D +- standard/prod: hybrid P24-48H min / P30D max +- fsi/sovereign: hybrid P7D min / P90D max + +**Audit relationship:** Discovered State records are NOT in the Audit Store (too high-volume, too ephemeral). Drift events triggered by Discovered State ARE in the Audit Store with discovery snapshot UUID reference. After snapshot expires: audit record preserved; snapshot no longer available. RHY-008. + +### 39.5 Complete Rehydration Policy Set +RHY-001 through RHY-008 — see doc 02. + +--- + +## SECTION 40 — ARCHITECTURE GAPS: CACHE MODEL, INGESTION, DEPLOYMENT + +### 40.1 DCM Deployment Topology — Hub/Regional/Sovereign + +The Ship/Shore/Enclave terminology from defense IT contexts has been replaced throughout with universally understood terms aligned to DCM's federation model: + +| Former Term | Replacement | Meaning | +|-------------|-------------|---------| +| Shore | **Cost Analysis Information Provider** | Specialized Information Provider supplying cost estimates, placement cost signals, cost actuals, and budget alerts; DCM provides input data; provider performs calculations | +| **Orchestration Flow Policy** | Named workflow artifact: Orchestration Flow Policy with `ordered: true`; declares explicit step sequence using payload type vocabulary; first-class Data artifact; versioned, GitOps-managed, profile-bound | +| **Request Orchestrator** | Runtime event bus; routes lifecycle events to Policy Engine; has no pipeline logic; both named workflows and dynamic policies are evaluated through it | +| **orchestration (DCM)** | Two-level composable model: Level 1 = named Orchestration Flow Policies (explicit sequence); Level 2 = dynamic policies (conditional, inline); both evaluated by Policy Engine; adding a step = adding to a workflow Policy; adding conditional behavior = writing a dynamic policy | +| **Provider Catalog Item** | What a specific Service Provider offers consumers: specific resource allocation or process with cost, availability, SLAs; linked to Resource Type Specification version | +| **cross_tenant_authorization** | DCMGroup with group_class: cross_tenant_authorization; grants one Tenant permission to reference/allocate/stake another Tenant's resources; revocation places active allocations in PENDING_REVIEW | +| **foundation Tenants** | Three system Tenants created at bootstrap: __platform__, __transitional__, __system__; cannot be decommissioned; declared in bootstrap manifest | +| **QUOTA_EXCEEDED** | GateKeeper rejection code when resource quota policy fires at Step 5 (pre-placement) | +| **Federated Contribution Model** | DCM defaults to federated data creation — all authorized actor types (platform admin, consumer/tenant, service provider, peer DCM) can contribute Data artifacts within their domain scope via the GitOps PR model; see doc 28 | +| **contributor** | Actor type that authored a Data artifact; recorded in artifact_metadata.contributed_by; determines review requirements; platform_admin / consumer / service_provider / peer_dcm | +| **contributed_by** | Artifact metadata block recording contributor_type, actor UUID, contribution_method, pr_url, reviewed_by; immutable once set | +| **DPO-001–006** | Design Priority system policies. DPO-001: security properties present in all profiles (not controlled by profiles). DPO-002: every security requirement needs an ease-of-use mechanism. DPO-005: minimal profile = "security with minimal overhead" not "minimal security". DPO-006: when security and ease conflict, redesign ease-of-use, not security. | +| **FCM-001–008** | Federated Contribution Model system policies; key: FCM-002 (domain scope violations = hard DENY), FCM-003 (GitOps PR for all), FCM-008 (contributor scope limits absolute) | +| **Unified Governance Matrix** | Single enforcement point for all cross-boundary decisions; four axes (subject/data/target/context); hard vs soft enforcement; field-level granularity (allowlist/blocklist/paths); profile-bound defaults; GMX-001–010 | +| **governance_matrix_rule** | Artifact declaring match conditions across four axes and a decision (ALLOW/DENY/ALLOW_WITH_CONDITIONS/STRIP_FIELD/REDACT/AUDIT_ONLY) with hard or soft enforcement | +| **sovereignty_zone** | Registered DCM artifact declaring geopolitical/regulatory boundary; rules reference zones by ID; inter-zone agreements declared explicitly | +| **STRIP_FIELD** | Governance matrix decision: remove named fields from payload and proceed; if stripped field is required → DENY_REQUEST | +| **REDACT** | Governance matrix decision: replace field value with ``; field presence preserved; receiver knows field exists but not its value | +| **Provider Type Registry** | Three-tier registry of approved provider types; each entry declares permissions, default_approval_method, enabled_in_profiles, capability_schema_ref | +| **registration_token** | Pre-issued by platform admin; scoped to provider_type/handle_pattern/zone; single_use; grants_auto_approval flag; value presented once only | +| **approval_method** | Registration approval: auto | reviewed | verified | authorized; resolved as most_restrictive(provider_type_default, profile_min, token_effect) | +| **Drift Reconciliation Component** | Control plane component; compares Discovered vs Realized State; produces drift records and events; never writes to Realized Store; DRC-001–005 | +| **drift_record** | Artifact produced by Drift Reconciliation; field-by-field comparison result with severity classification; unsanctioned flag; status tracking through resolution | +| **Placement Engine** | Six-step algorithm: sovereignty filter → accreditation filter → capability filter → reserve query → tie-breaking (policy/priority/affinity/cost/load/hash) → confirm; PLC-001–006 | +| **reserve_query** | Parallel capacity queries to all eligible provider candidates; PT5M capacity hold; non-responders and insufficient-capacity providers excluded | +| **consistent hash** | Final placement tie-breaker: SHA-256(request_uuid+resource_type+sorted_candidates); deterministic; never round-robin | +| **Lifecycle Constraint Enforcer** | Monitors TTL/expiry/max_execution_time; fires expiry actions through standard pipeline; grace period before action; Process Resources: immediate FAILED on breach; LCE-001–005 | +| **Search Index** | Non-authoritative queryable projection of GitOps stores; indexes key fields; returns git_path for full payload; max staleness PT5M; always rebuildable; SIX-001–004 | +| **Admin API** | Platform admin REST interface: Tenant lifecycle, provider review, accreditation approval, discovery trigger, orphan resolution, recovery decisions, quota management, Search Index rebuild, bootstrap operations | +| **PENDING_EXPIRY_ACTION** | Entity state when expiry action fails to execute; Lifecycle Constraint Enforcer retries per Recovery Policy; Platform Admin notified urgency: high | +| **data_classification** | First-class field metadata: public/internal/confidential/restricted/phi/pci/sovereign/classified; phi/sovereign/classified are immutable once set | +| **Accreditation** | Formal versioned attestation that a component satisfies a compliance framework; issued by an Accreditor; carries validity period; lifecycle: developing→proposed→active→expired/revoked | +| **Accreditor** | Entity that issues accreditations: government body, regulatory body, QSA, certification body, or internal audit team | +| **Accreditation Gap** | Missing, expired, or revoked accreditation required for an active interaction; always high/critical severity; Recovery Policy governs response | +| **Data/Capability Authorization Matrix** | Policy Group artifact (concern_type: data_authorization_boundary) declaring what data fields and capabilities are permitted across interaction boundaries given data classification and accreditation level | +| **zero_trust_posture** | Sixth Policy Group concern type; four levels: none/boundary/full/hardware_attested; profile defaults: minimal=none, dev/standard=boundary, prod/fsi=full, sovereign=hardware_attested | +| **Five-check boundary model** | Identity → Authorization → Accreditation → Matrix → Sovereignty; all five checks at every DCM interaction boundary; all produce audit records | +| **Federation tunnel** | Mutually authenticated, encrypted, scoped DCM-to-DCM channel; zero trust model; establishes secure transport only, not implicit trust; per-message signing; scoped non-transferable credentials | +| **hard_constraint** | Data/Capability Matrix declaration that cannot be overridden by any policy; sovereign/classified data never crossing federation boundaries is a hard_constraint | +| **STRIP_FIELD** | Matrix enforcement action: remove non-permitted field from payload and proceed; if stripped field is required → escalates to DENY_REQUEST | +| **DENY_REQUEST** | Matrix enforcement action: block entire interaction; entity enters PENDING_REVIEW; notification dispatched | +| **Request Orchestrator** | DCM control plane event bus; routes lifecycle events to Policy Engine; coordinates pipeline via event-condition-action; does not contain hardcoded pipeline logic | +| **Cost Analysis Component** | Internal DCM control plane component; three functions: pre-request estimation, placement input, ongoing attribution; not a billing system; not a provider type | +| **Module** | DCM capability extension adding new functions; distinct from Profile (which configures behavior) | +| **orchestration_flow** | Policy Group concern_type for static sequential flows; ordered: true; both static and dynamic flows compose through the same Policy Engine | +| **payload_type** | Closed vocabulary of event types the Request Orchestrator publishes; policies pattern-match on payload type + state | +| **OPA integration** | Reference implementation for Mode 3 External Policy Evaluators; DCM payload as OPA input document; built-in Rego functions provided by DCM | +| **Flow GUI** | Visual policy composer and orchestration manager; execution graph view, policy canvas, shadow mode dashboard, flow simulation | +| **__platform__** | Immutable system Tenant owning DCM control plane resources; created at bootstrap before Policy Engine comes online | +| **__transitional__** | Immutable system Tenant holding brownfield entities during INGEST phase | +| **bootstrap manifest** | Signed manifest declaring initial system Tenants, bootstrap admin, and initial profile; hash-verified at every DCM startup | +| **cross_tenant_authorization** | DCMGroup with this group_class formally grants one Tenant access to another's resources; has lifecycle (duration, renewal, revocation); revocation places active allocations in PENDING_REVIEW | +| **drift_criticality** | Field-level property in Resource Type Spec (low/medium/high/critical); combined with change magnitude to produce drift severity | +| **Ingress API** | Infrastructure-layer entry point for all inbound DCM requests; sets ingress block; routes to Consumer/Provider/Admin API surfaces | +| **Provider Catalog Item** | Provider-specific instantiation of a Resource Type Specification; what consumers actually request; distinct from the Resource Type Specification itself | +| **Recovery Policy** | Formal DCM policy type mapping trigger conditions (DISPATCH_TIMEOUT, PARTIAL_REALIZATION, etc.) to response actions; same authoring model as GateKeeper/Validation/Transformation | +| **recovery_posture** | Fifth Policy Group concern_type governing failure and ambiguity response; binds a recovery profile group to the deployment | +| **DRIFT_RECONCILE** | Recovery action: schedule discovery; let drift detection resolve actual state | +| **DISCARD_AND_REQUEUE** | Recovery action: best-effort cleanup; new request cycle created immediately | +| **NOTIFY_AND_WAIT** | Recovery action: notify human; wait for explicit decision up to declared deadline | +| **TIMEOUT_PENDING** | Infrastructure Resource Entity state: dispatch timeout fired; recovery policy evaluating | +| **LATE_REALIZATION_PENDING** | Entity state: provider responded after timeout; NOTIFY_AND_WAIT recovery decision pending | +| **INDETERMINATE_REALIZATION** | Entity state: state ambiguous; drift detection resolving | +| **COMPENSATION_FAILED** | Entity state: compound service rollback itself failed; orphan detection active | +| **orphan_candidate** | Resource discovered at provider with no corresponding Realized State record; surfaced to platform admin for human resolution | +| **Discovery Scheduler** | DCM control plane component maintaining priority queue of discovery requests; dispatches to provider discovery endpoints | +| **recovery-automated-reconciliation** | Built-in recovery profile: trust drift detection; accept late responses; appropriate for dev/standard | +| **recovery-notify-and-wait** | Built-in recovery profile: notify human; never act automatically; appropriate for prod/fsi/sovereign | +| **notification service** | Ninth DCM provider type; translates unified notification envelope to delivery channel; handles delivery, retry, dead letter, and delivery confirmation callbacks | +| **Notification Router** | DCM control plane component that resolves notification audiences and routes envelopes to notification services | +| **audience resolution** | Deriving notification recipients by traversing the entity relationship graph from the changed entity at event time | +| **notification_uuid** | Idempotency key on notification envelopes; notification services use this to deduplicate on retry | +| **audience_role** | owner / stakeholder / approver / observer — why this actor is in the notification audience | +| **stakeholder_reason** | Notification envelope field explaining which relationship caused the actor to be in the stakeholder audience | +| **Tier 1 / Tier 2 / Tier 3 notifications** | Mandatory system (non-suppressable) / Tenant defaults / Actor subscriptions — three subscription tiers that compose | +| **NOT-001 through NOT-008** | Notification model system policies | +| **write-once snapshot store** | Realized Store implementation model: each record is a complete immutable entity state snapshot; no event replay; direct point-in-time lookup; supersession chain links snapshots | +| **corresponding_requested_state_uuid** | Mandatory non-nullable field on every Realized State snapshot; traces every Realized State change to an authorized request | +| **Provider Update Notification** | Formal API for providers to report authorized state changes; DCM evaluates via Policy Engine; approved → new Requested State + Realized State; rejected → drift event | +| **notification_uuid** | Idempotency key on Provider Update Notifications; safe to resend on provider crash | +| **pre-authorized update** | Category of provider update pre-approved by GateKeeper policy; processed automatically without per-change human review | +| **Whole Allocation** | Ownership pattern: consumer owns the entire resource entity outright in their Tenant; no pool involved | +| **Allocation** | Ownership pattern: pool yields independently-owned sub-resources; consumer owns their allocation; AllocationRecord relationship links to pool | +| **Shareable** | Ownership pattern: one resource, multiple stakeholders; consumers hold stakes (relationships) only; no consumer owns any portion | +| **AllocationRecord** | Cross-tenant relationship from an allocation entity back to its source pool entity | +| **stake_strength** | Relationship property on shareable resource attachments: required (blocks decommission) / preferred / optional | +| **PENDING_REVIEW** | Formal Infrastructure Resource Entity lifecycle state for conflicts requiring human resolution (sovereignty, cross-tenant auth revocation, ownership transfer conflicts) | +| **Consumer API** | DCM REST API for consumers: catalog browsing, request submission, resource management, audit trail access | +| **Consumer Request Status** | Lifecycle: ACKNOWLEDGED → ASSEMBLING → AWAITING_APPROVAL → APPROVED → DISPATCHED → PROVISIONING → COMPLETED/FAILED/CANCELLED | +| **01-entity-types.md** | Entity type taxonomy: Infrastructure Resource, Composite Resource, Process Resource; sub-types and invariants | +| **04-examples.md** | Worked examples: VM end-to-end, IP allocation, VLAN sharing, brownfield ingestion, drift remediation; Git repo structure | +| **04b-ownership-sharing-allocation.md** | Authoritative ownership model: whole allocation, allocation, shareable; policies OWN-001 through OWN-008 | +| **federation routing** | Hub DCM applies placement engine logic at the DCM instance level; Regional DCMs are DCM Provider instances; sovereignty is a hard pre-filter; same tie-breaking hierarchy as provider selection | +| **independent_with_overlap** | Certificate rotation model: old cert valid P30D after new cert issued; allows peers to update trust stores without coordinated downtime | +| **alert_and_hold** | Federated drift detection response when peer DCM is unavailable: do not assume drift; hold state; escalate to platform admin after PT24H | +| **AUDIT_STORE_UNAVAILABLE** | Gap record inserted in Audit Store hash chain after recovery from Audit Store failure; timestamps the exact outage window; makes gap explicit and auditable | +| **confidence aggregation** | Per-entity endpoint computing overall confidence band (= lowest field band); identifies contested and stale fields; computed on demand never stored | +| **explicit_no_filter** | Composite group declaration suppressing the no member_type filter linting warning; confirms broad targeting is intentional | +| **certified profile** | DCM profile carrying formal third-party certification metadata (HIPAA assessor, FedRAMP JAB, PCI QSA); promoted to Tier 1; applies to artifact not deployment | +| **POLICY_PROVIDER_ELEVATED** | Audit action recorded when a External Policy Evaluator's mode level is elevated; always produced regardless of profile | +| **tier_certifications** | Certification metadata on Resource Type Specs or profiles from recognized certifying bodies; filter criterion, not structural tier boundary | +| **tier_3_to_tier_2_promotion** | PR-based pathway for organizations to promote internal Tier 3 Resource Types to Verified Community (Tier 2); requires production deployment, OSS license, named maintainer, migration path | +| **independent operation mode** | Resource Type Registry state when upstream registry is permanently unavailable; existing types continue; new community type adoption requires governance decision | +| **SCIM 2.0** | System for Cross-domain Identity Management; optional Auth Provider capability for automated actor provisioning from enterprise IdPs; provisions actors and group memberships; roles not SCIM-provisioned | +| **step-up MFA** | Additional MFA challenge at sensitive operations within an already-authenticated session; declared per operation by policy; step-up token TTL PT10M | +| **actor.type** | Audit record field: human / service_account / system; enables filtering between human-initiated and automated lifecycle operations in queries and dashboards | +| **system_actor** | Audit record block on system-initiated records: identifies DCM component, trigger event, and authorizing policy UUID | +| **Merkle root proof** | Federation-level audit integrity mechanism: Hub DCM computes Merkle root of all Regional DCM chain tips daily; any chain break detectable against stored root | +| **per-instance hash chain** | Each DCM instance (Hub/Regional/Sovereign) maintains its own independent hash chain; not merged cross-instance; cross-referenced via correlation_id | +| **AUTH-012 through AUTH-015** | Auth Provider gap policies: SCIM provisioning, failover handling, two-tier MFA, pluggable user store | +| **AUD-014 through AUD-017** | Universal Audit gap policies: hash chain verification modes, Commit Log capacity, system-initiated records, distributed hash chains | +| **Hub DCM** | Central/global instance; governance origin; authoritative registry | +| Ship | **Regional DCM** | Distributed regional instance; manages resources in its region | +| Enclave | **Sovereign DCM** | Air-gapped/compliance-isolated; signed bundle updates only | + +These map directly onto the DCM federation model (DCM-001 through DCM-008, doc 22). + +### 40.2 Cache Placement (Q1) +Caches placed closest to consumption point, subject to sovereignty constraints: +- **Layer + Catalog caches**: Regional DCM (closest to assembly); Hub DCM is authoritative origin +- **Information Provider caches**: co-located with source; never cross sovereignty boundary +- **Search Index**: Hub DCM primary; Regional DCM optional mirror; Sovereign DCM local index +- **Sovereign DCM caches**: always local static; populated from signed bundles; no live sync + +CACHE-001. + +### 40.3 Cache Synchronization (Q2) +**Hybrid push-pull model:** Pull on profile-governed schedule (minimal=P4H through fsi/sovereign=P5M); push invalidation via Message Bus for time-sensitive events (layer_updated, policy_activated, sovereignty_change). Pull failure → serve cached. Push failure → queue+retry within PT1H. Sovereign DCM: signed bundle import only. CACHE-002. + +### 40.4 Cache Authoritativeness (Q3) +GitOps stores and Event Streams always authoritative. Caches are derived projections — never authoritative. Cache divergence → rebuild from nearest authoritative store → CACHE_DIVERGENCE_DETECTED Audit event. Regional DCMs send cache state hash in heartbeats to Hub DCM; mismatch → forced refresh. CACHE-003, CACHE-004. + +### 40.5 Native Passthrough (Q5) +Core data model does not embed technology-specific data. `native_passthrough` field sanctioned for genuinely untranslatable provider-specific data. Always audit-logged (content if transparent; hash if opaque). Opaque passthrough blocked in fsi/sovereign profiles by default. DATA-001. + +### 40.6 Physical State Representation (Q6) +Intent/Requested → YAML in Git. Realized → Event Stream → Realized Store. Discovered → Discovered Store (ephemeral). Resolved by STO-001 through STO-005. + +### 40.7 Ingestion Model Gaps + +**Signal priority configurable (Q1):** Platform domain layer declares priority order. explicit_tenant_tag always first; default_tenant always last; middle signals reorderable. ING-012. + +**Bulk promotion (Q2):** Supported with profile-governed limits (minimal=unlimited → sovereign=25 per batch). Preview required. PT24H rollback. BULK_PROMOTE audit record. ING-013. + +**Max ingestion sources per entity (Q3):** Profile-governed (5 standard/prod; 3 fsi/sovereign). Warn or reject on exceed. ING-014. + +**Ingestion → Service Catalog (Q4):** Ingested entities promotable to catalog items. Bidirectional drift detection between entity and catalog item. ING-015. + +### 40.8 Deployment Redundancy Gaps + +**Bootstrap manifest verification (Q1):** GitOps stored + hash-verified at every startup. Tampering prevents startup. Operator-signed. RED-011. + +**Sovereign Kubernetes upgrades (Q2):** Pre-staged images via signed bundles. DCM maintenance mode during upgrade. Startup verification before resuming queued requests. RED-012. + +**Non-Kubernetes runtimes (Q3):** Kubernetes required for production. Podman/Docker Compose for dev/community only. DCM Operator is Kubernetes-native. RED-013. + +**Minimum hardware specs (Q4):** Declared as DCM Resource definitions per profile; enforced by placement engine. minimal=2cpu/4Gi/20Gi/1 replica → fsi/sovereign=32cpu/64Gi/500Gi/5 replicas. RED-014. + +**DCM self-hosted drift detection (Q5):** DCM is a DCM-managed resource — same drift detection. Bootstrap hash provides independent Operator verification. Audit hash chain breaks externally detectable. RED-015. + +--- + +## SECTION 41 — SECURITY, AUTH, AND AUDIT REFINEMENTS + +### 41.1 SCIM 2.0 User Provisioning (Auth Q1) + +SCIM 2.0 is an optional Auth Provider capability for enterprise deployments. Automates actor lifecycle from enterprise IdPs (Okta, Azure AD, Ping, JumpCloud). + +**What SCIM manages:** DCM actor records (create/update/deactivate), DCM group memberships from IdP groups. + +**What SCIM does NOT manage:** Roles — they require explicit DCM policy authorization. This prevents privilege escalation through the SCIM channel. + +**Deprovisioning:** `suspend` by default (reversible; sessions terminated; leases released; in-flight requests complete first). AUTH-012. + +### 41.2 Auth Provider Failover — In-Flight Requests (Auth Q2) + +**In-flight requests (already authenticated):** Continue to completion — session token carries resolved roles/groups/tenant scope; Auth Provider not needed for assembly. + +**New requests, provider down:** Follow declared failover chain. Sessions valid for declared TTL (PT8H default) regardless of provider availability. + +**Session expiry during outage:** Requires re-auth via available failover provider. All providers unavailable → reject new authentication with clear error. + +Failover chain: primary LDAP → OIDC backup → local users (last resort). AUTH-013. + +### 41.3 Two-Tier MFA — Per-Session and Step-Up (Auth Q3) + +**Per-session MFA:** Validated at login; captured in `ingress.actor.mfa_verified`. + +**Step-up MFA:** Additional challenge at sensitive operations even within a valid MFA session. Policy declares which operations require step-up: +- platform_policy_activate, provider_decommission, tenant_decommission +- sovereignty_zone_change, auth_provider_update, manual_rehydration (if entity requires hardware_token_mfa) + +Step-up token TTL: PT10M. Profile defaults: minimal/dev = no MFA; standard = recommended; prod = per-session required + destructive ops step-up; fsi = per-session + all policy changes; sovereign = hardware token + all admin ops. AUTH-014. + +### 41.4 Built-In Auth Provider Storage Backend (Auth Q4) + +Pluggable storage backend following the data store model. SQLite (minimal/dev) → PostgreSQL (standard+). FSI/sovereign require encryption at rest. Local store should contain bootstrap users, service accounts, API keys only — not enterprise users. AUTH-015. + +### 41.5 Hash Chain Verification Modes (Audit Q1) + +Three independent levels: +- **Continuous write:** Hash computed on every write — this IS chain construction; always active +- **Scheduled sweep:** Background verification: standard=weekly, prod=daily, fsi/sovereign=every 6 hours +- **On-demand:** Operator-triggered for any time range (max P365D per run) + +Failure: security alert + integrity incident; new writes continue (halting writes is itself a security risk). AUD-014. + +### 41.6 Commit Log Capacity and Overflow (Audit Q2) + +Configurable max capacity (default 10Gi) with declared overflow policy: +- `alert_and_continue` (minimal/dev/standard/prod) — availability priority +- `reject_new_ops` (fsi/sovereign) — audit completeness priority; operating unaudited is a compliance violation + +Backpressure: alert at 75%, urgent at 90%. P7D max age triggers escalation regardless of capacity. AUD-015. + +### 41.7 System-Initiated Audit Records (Audit Q3) + +`actor.type` field on all audit records: `human | service_account | system` + +System records include `system_actor` block: component + trigger + authorizing_policy_uuid. Full audit records — appear in all queries and compliance reports. Enables dashboard filtering: "show only human-initiated changes" vs "show only automated lifecycle operations". AUD-016. + +### 41.8 Distributed Hash Chain Integrity (Audit Q4) + +Per-instance hash chains — each DCM instance (Hub, Regional, Sovereign) has its own independent chain. Federation-level integrity via daily Merkle root proof at Hub DCM. Cross-instance queries: parallel chains with cross-references via `correlation_id` — not merged. Per-instance verification always local; federation verification requires Hub DCM connectivity. AUD-017. + +--- + +## SECTION 42 — POLICY AND REGISTRY REFINEMENTS + +### 42.1 Community Profile and Group Submissions (Policy Q1) + +Organizations submit custom profiles and policy groups to the DCM community registry via same PR-based workflow as Resource Types — Tier 2. Requirements: documented use case, at least one production deployment reference, test results, named maintainer. Same lifecycle as Resource Types (shadow validation before active, deprecation, sunset). PROF-005. + +### 42.2 Certified Profile Program (Policy Q2) + +Certified profiles carry third-party certification metadata (HIPAA assessor, FedRAMP JAB, PCI QSA). Certified profiles promoted to Tier 1 (DCM Core). Certification applies to the profile artifact only — not to any specific deployment. Community-contributed Tier 2 profiles that obtain certification can be promoted to Tier 1. PROF-006. + +### 42.3 External Policy Evaluator Trust Elevation Approval (Policy Q3) + +Trust elevation (increasing mode level) requires formal approval workflow. Profile-governed approver requirements: +- standard: 1 platform_admin +- prod: platform_admin + security_owner (min 2) +- fsi: platform_admin + security_owner + compliance_officer (dual approval) +- sovereign: 3 approvers + change control ticket + +P7D shadow period after elevation before outputs become binding. POLICY_PROVIDER_ELEVATED audit record. PROF-007. + +### 42.4 Dev Profile Resource TTL Configurability (Policy Q4) + +Default TTL declared in system domain layer; overridable at platform domain level. Per-resource-type overrides supported (VMs: P7D, Storage: P14D, DNS: P3D). on_expiry action configurable: notify (consumers can extend) vs destroy. PROF-008. + +### 42.5 Air-Gapped External Policy Evaluator Delivery (Policy Q5) + +Signed bundle model — same as registry bundles. Mode 3 bundles include OPA Rego files. Mode 4 in sovereign profiles: endpoint must be within sovereignty boundary — external AI service calls blocked by BBQ-001 sovereignty check. PROF-009. + +### 42.6 No Fourth Registry Tier (Registry Q1) + +Certification metadata within existing tier structure — no structural fourth tier. filter: tier_certifications provides equivalent discovery. REG-008. + +### 42.7 Tier 3 to Tier 2 Promotion (Registry Q2) + +PR-based promotion pathway with additional requirements: production deployment + OSS-compatible license + named community maintainer + documented migration path from Tier 3 handle. Current Tier 3 users notified. REG-009. + +### 42.8 Upstream Registry Permanently Unavailable (Registry Q3) + +Organization Registry mirror is self-sufficient — upstream loss is a governance decision, not an operational crisis. Three options: designate community mirror as new upstream / fork the registry / continue as independent installation. Existing operations never interrupted. REG-010. + +### 42.9 Provider Cost Metadata Source (Registry Q4) + +Static declaration or dynamic Cost Analysis sourcing; hybrid recommended (Cost Analysis preferred, static fallback, fallback_max_age: PT24H). Placement engine cost analysis step uses freshest available source — no changes to tie-breaking model. REG-011. + +--- + +## SECTION 43 — FEDERATION, OBSERVABILITY, AND FINAL REFINEMENTS + +### 43.1 DCM-to-DCM Certificate Rotation (Federation Q1) + +Independent rotation per instance with P30D overlap period. Peers notified via Message Bus 60 days before expiry. Automatic renewal triggers 90 days before expiry. Overlap allows peers to update trust stores without coordinated downtime. DCM-009. + +### 43.2 Federation Routing — Full Placement Engine at DCM Level (Federation Q2 — Extended) + +**Hub DCM federation routing follows the same placement engine logic as provider selection.** Regional DCMs are treated as DCM Provider instances. + +**Sovereignty is a hard pre-filter — not a tie-breaker:** +- Filter eligible Regional DCMs by sovereignty compatibility before the placement loop +- No eligible Regional DCMs after filter → reject with clear error + +**Tie-breaking hierarchy at the DCM instance level (same as provider selection):** +1. Policy preference (policy declares preferred Regional DCM) +2. Federation priority (numeric priority on DCM Provider registration) +3. Tenant affinity (Tenant's resources prefer a specific Regional DCM) +4. Sovereignty match quality (exact over partial match) +5. Geographic affinity (closest regional to consumer) +6. Least loaded (capacity utilization) +7. Consistent hash (deterministic tiebreaker) + +**Sub-regional routing:** Regional DCM acts as Hub for its children — same logic recursive within federation depth limit. Load balancing is step 6 (least loaded) — not a primary strategy. + +DCM-010. + +### 43.3 Federated Drift Detection Ownership (Federation Q3) + +Provider-side DCM discovers; consumer-side DCM compares against its Requested State. Discovered State events published via federation Message Bus with correlation_id + consumer_dcm_uuid tag. Peer DCM unavailable = alert-and-hold (not assumed drift); max hold PT24H then escalate. DCM-011. + +### 43.4 Cross-DCM Audit Correlation (Federation Q4) + +No full synchronization. correlation_id reference model — each DCM keeps its own authoritative audit trail. On-demand pull for compliance investigations requires platform admin auth + sovereignty check + peer DCM authorization. DCM-012. + +### 43.5 Maximum Federation Depth (Federation Q5) + +Profile-governed: minimal/dev=5, standard/prod=3, fsi/sovereign=2. Measured as hops from deepest instance to Hub DCM. Depth 3 covers Hub → Regional → Sub-Regional → Edge. DCM-013. + +### 43.6 Audit Provenance Scattered Resolutions + +- **Q1 (Audit Store architecture):** Already resolved as STO-004 — specialized PostgreSQL store contract; see doc 11. +- **Q2 (Air-gapped replication):** Live sync for Regional DCMs; signed bundle export for Sovereign DCMs; sovereignty check required; hash chain preserved; AUD-018. +- **Q3 (Default dashboard):** Grafana bundled for minimal/dev/standard; enterprise integration recommended for prod; required for fsi; local-only for sovereign; OBS-002. +- **Q4 (Failing Data Store):** Two-stage model handles it — Commit Log (etcd) independent of data stores; AUDIT_STORE_UNAVAILABLE gap record on recovery; hash chain makes gap explicit; AUD-019. + +### 43.7 Universal Groups — Composite Linting (Q1) + +Linting warning (not error) when composite group policy targeting has no member_type filter. Operator may suppress with explicit_no_filter: true. GRP-016. + +### 43.8 Information Provider — Confidence Aggregation (Q2) and Override Notifications (Q3) + +**Confidence aggregation API:** GET /api/v1/entities/{uuid}/confidence — overall band = lowest field band (conservative); computed on demand; identifies contested and stale fields. INF-010. + +**Override notifications:** Provider opt-in via conflict_notification in registration; webhook or Message Bus; overriding value may be redacted by policy for confidentiality. INF-011. + +--- + +## SECTION 44 — FOUNDATIONAL CAPABILITIES MATRIX + +### 44.1 Overview + +DCM has 95 foundational capabilities across 15 domains. Each capability has a unique ID (domain prefix + sequence number), three perspectives (Consumer, Producer, Platform/Admin), and declared dependencies. This matrix drives Jira ticket creation and implementation planning. + +### 44.2 Minimum Viable Capability Set (21 capabilities for end-to-end demo) + +IAM-001 → IAM-002 → IAM-003 → IAM-007 → CAT-001 → REQ-001 → REQ-002 → REQ-003 → REQ-004 → REQ-005 → REQ-006 → REQ-007 → PRV-001 → PRV-002 → PRV-003 → PRV-004 → PRV-005 → LCM-001 → DRF-001 → DRF-002 → AUD-001 + +### 44.3 Domain Summary + +| Prefix | Domain | Count | +|--------|--------|-------| +| IAM | Identity and Access Management | 7 | +| CAT | Service Catalog | 7 | +| REQ | Request Lifecycle Management | 10 | +| PRV | Provider Contract and Realization | 9 | +| LCM | Resource Lifecycle Management | 7 | +| DRF | Drift Detection and Remediation | 5 | +| POL | Policy Management | 7 | +| LAY | Data Layer Management | 5 | +| INF | Information and Data Integration | 6 | +| ING | Ingestion and Brownfield Management | 4 | +| AUD | Audit and Compliance | 5 | +| OBS | Observability and Operations | 5 | +| STO | Storage and State Management | 6 | +| FED | DCM Federation and Multi-Instance | 5 | +| GOV | Platform Governance and Administration | 7 | +| **Total** | | **95** | + +### 44.4 Perspectives + +- **Consumer** — what the end user / application team experiences or can do +- **Producer** — what the Service Provider or platform component must implement +- **Platform/Admin** — what the platform engineer or SRE must configure or operate + +Empty perspective = that capability does not have a direct touchpoint for that role. + +### 44.5 Key Dependency Chain + +``` +IAM-001 (Auth) → IAM-003 (RBAC) → CAT-001 (Catalog) → REQ-001 (Submit) + → REQ-003 (Layers) → REQ-004 (Policy) → REQ-005 (Placement) → REQ-007 (Dispatch) + → PRV-001 (Provider Reg) → PRV-003 (Realization) → PRV-005 (Realized State) + → LCM-001 (State Transitions) → DRF-001 (Discovery) → DRF-002 (Drift) +``` + +### 44.6 Resources +- Interactive map: DCM-Capabilities-Map.html +- CSV for Jira import: DCM-Capabilities-Matrix.csv +- Markdown reference: DCM-Capabilities-Matrix.md +- Taxonomy: DCM-Taxonomy.md + +--- + +## SECTION 45 — DCM TAXONOMY + +The DCM Taxonomy is the authoritative vocabulary for all DCM work — code, documentation, Jira tickets, design discussions. Four parts: + +### 45.1 Core Vocabulary (key terms) +- **Service Provider** — provisions/configures/manages infrastructure; implements naturalization, realization, denaturalization, capacity reporting, sovereignty maintenance. *NOT "producer."* +- **Hub DCM** — central/global instance; authoritative registry origin. *NOT "Shore."* +- **Regional DCM** — distributed regional instance; treated as DCM Provider by Hub placement engine. *NOT "Ship."* +- **Sovereign DCM** — air-gapped/compliance-isolated; signed bundle updates only. *NOT "Enclave."* +- **Layer** — passive data (what values should fields have); distinct from Policy (executable logic) +- **Policy** — executable rule evaluating assembled payload; distinct from Layer +- **Confidence Descriptor** — primary data model for Information Provider confidence: authority_level + corroboration + source_trust + last_updated_at (stored); score + band derived at query time +- **Rehydration** — replaying Intent State to new provider; UUID always preserved +- **Targeted Delta** — post-realization field update; does not re-run layer assembly chain +- **Fulfillment** — complete process from consumer submission to Service Provider realization +- **Reserve Query** — placement engine asking providers "can you fulfill this right now?" + +### 45.2 Anti-Vocabulary (terms to avoid) +| Avoid | Use Instead | +|-------|-------------| +| Producer | **Service Provider** | +| Shore / Ship / Enclave | **Hub DCM** / **Regional DCM** / **Sovereign DCM** | +| Realize / Realization | **Provision** / **Install** / **Fulfill** | +| Widgets | Specific resource type name | +| Data Center | **Region** / **Zone** | +| User (generic) | **Developer**, **Application Owner**, **Platform Engineer** | +| Service (unqualified) | **Catalog Item** / **Resource Type** / **Service Provider** | +| Manage (unqualified) | **Provision** / **Configure** / **Monitor** / **Decommission** | + +### 45.3 Roles and Personas +Developer/Application Owner (consumer), Platform Engineer (platform ops), Policy Owner (governance), Platform Admin (highest-privilege ops), SRE (operational health), Tenant Admin (tenant management), Service Provider Team (provider integration). + +### 45.4 Capability Domain Prefixes +IAM, CAT, REQ, PRV, LCM, DRF, POL, LAY, INF, ING, AUD, OBS, STO, FED, GOV — see Section 44. + +--- + +## SECTION 46 — GROUP 1: MISSING DOCUMENTS + +### 46.1 01-entity-types.md — Entity Types Taxonomy + +Three primary entity types in DCM: + +**Infrastructure Resource Entity** — persistent, full lifecycle (REQUESTED → PENDING → PROVISIONING → REALIZED → OPERATIONAL → SUSPENDED → DECOMMISSIONED). Owned by exactly one Tenant. Drift detection active. TTL management. `PENDING_REVIEW` is a valid state for sovereignty/tenancy conflicts during rehydration — not an error state; requires human resolution. + +**Composite Resource Entity** — Meta Provider composition of multiple Infrastructure Resource Entities. Owns its UUID; constituents own theirs. `lifecycle_state` reflects aggregate health — OPERATIONAL only when all required constituents OPERATIONAL. Two-level drift detection (composite + constituent). Staged decommission (composite first, then constituents in reverse dependency order). `composition_visibility: opaque|transparent|selective`. + +**Process Resource Entity** — ephemeral execution (automation jobs, playbooks, pipelines). Short lifecycle (REQUESTED → INITIATED → EXECUTING → terminal). No SUSPENDED state. No PENDING_REVIEW. `max_execution_time` mandatory — no default. Must record `affected_entity_uuids` if any infrastructure modifications made. + +Entity sub-types: Shared Resource Entity (`ownership_model: shareable`), Allocatable Pool Resource (pool entity), Allocation entity (`ownership_model: allocation`). + +Entity identity invariants: UUID never changes (including rehydration); single Tenant ownership always; provider entity ID is separate from DCM UUID; audit records preserved per retention policy. + +### 46.2 04b-ownership-sharing-allocation.md — Ownership, Sharing, and Allocation + +**The three ownership patterns — use these terms precisely:** + +**Whole Allocation** — consumer receives entire resource entity; owns it outright in their Tenant; no pool involved; full lifecycle control; decommission is straightforward. + +**Allocation** — pool resource (owned by platform Tenant) yields new independently-owned sub-resources. Consumer owns their allocation outright. AllocationRecord relationship links allocation → pool. Decommissioning the allocation releases it back to the pool. Pool entity unaffected. Example: IPAddressPool → IPAddress entities. + +**Shareable** — single resource owned by one Tenant; multiple consumers hold stakes (relationships) but own nothing. No new entity created per consumer. Decommission deferred while required stakes active. Consumer holds an `attached_to` or `depends_on` relationship with declared `stake_strength: required|preferred|optional`. Example: VLAN shared by multiple VMs. + +**Critical distinction:** Shareable = one resource, multiple stake-holders. Allocation = one pool, multiple independently-owned sub-resources. Never confuse these. + +Hybrid case: an allocation from a shareable pool. Consumer owns their /28 subnet (allocation). The parent /16 is shareable (NetworkOps owns it, multiple /28s have stakes in it). + +OWN-001 through OWN-008 policies govern these patterns. + +### 46.3 04-examples.md — Worked Examples and Git Repository Structure + +**Git repository structure (resolves Q54 deferred item):** +- Intent Store: `intent-store/{tenant-uuid}/{resource-type-category}/{resource-type}/{entity-uuid}/intent.yaml` +- Requested Store: `requested-store/{tenant-uuid}/{resource-type-category}/{resource-type}/{entity-uuid}/` with: `requested.yaml`, `assembly-provenance.yaml`, `placement.yaml`, `dependencies.yaml` +- Provider selection is in `placement.yaml` — not encoded in directory structure (Q54 resolved) + +**Five worked examples:** +1. VM provision end-to-end (layer assembly, policy evaluation, placement, all four states) +2. IP Address allocation (pool → consumer-owned allocation entity, AllocationRecord relationship) +3. VLAN attachment (shareable ownership — stake relationship, decommission deferral) +4. Brownfield ingestion (INGEST → ENRICH → PROMOTE with CMDB Information Provider) +5. Drift detection and remediation (unsanctioned memory change, severity, ESCALATE, UPDATE_DEFINITION resolution) + +### 46.4 consumer-api-spec.md — Consumer API Specification + +Consumer API base URL: `/api/v1/`. Three ingress surfaces: REST API (this spec), Web UI, Git PR. + +**Authentication:** Bearer token from `/api/v1/auth/token`. Tenant context via `X-DCM-Tenant` header — always required when actor has multiple Tenants. Step-up MFA via `X-DCM-StepUp-Token` for sensitive operations. + +**Service Catalog:** `GET /api/v1/catalog` (list, filtered by RBAC), `GET /api/v1/catalog/{uuid}` (describe with full schema, constraints, cost estimate), `GET /api/v1/catalog/search`. + +**Request submission:** `POST /api/v1/requests` → 202 with entity_uuid and status_url. Consumer request status lifecycle: ACKNOWLEDGED → ASSEMBLING → AWAITING_APPROVAL → APPROVED → DISPATCHED → PROVISIONING → COMPLETED|FAILED|CANCELLED. `DELETE /api/v1/requests/{uuid}` for cancellation (only before PROVISIONING). + +**Resource management:** `GET /api/v1/resources` (list), `GET /api/v1/resources/{uuid}` (describe with confidence scores, drift status, editable flags), `PATCH /api/v1/resources/{uuid}` (targeted delta for editable fields), `POST /suspend`, `DELETE` (decommission with deferred response if stakes active), `POST /rehydrate`. + +**Audit:** `GET /api/v1/resources/{uuid}/audit` with chain_integrity field, `GET /api/v1/audit/correlation/{id}` for cross-state timeline. + +Three conformance levels: Level 1 (read-only), Level 2 (standard), Level 3 (full including rehydration and audit). + +### 46.5 Context-and-Purpose Fix + +Section 5 subsections were incorrectly numbered 3.1/3.2/3.3 — corrected to 5.1/5.2/5.3. Q6 garbled row in open questions table — corrected. + +### 46.6 Q54 Resolution + +Git repository structure is independent of provider selection. Provider selection is stored in `placement.yaml` within the entity's directory. The deferred note in the four states doc has been updated to reference the worked examples document for the complete layout. + +--- + +## SECTION 47 — STORE ARCHITECTURE: INTENT, REQUESTED, REALIZED, DISCOVERED + +### 47.1 The Four Stores — Corrected Model + +| Store | Type | Implementation | Why | +|-------|------|---------------|-----| +| Intent | GitOps (required) | GitHub/GitLab/Gitea | PR workflow is first-class feature, not implementation detail | +| Requested | Write-once store (PostgreSQL) | GitOps (reference); PostgreSQL (production scale) | Machine-generated; no PR benefit; Git degrades at scale | +| Realized | Write-once Snapshot Store | PostgreSQL; CockroachDB | Snapshot-based (not event stream); request-traceable only | +| Discovered | Ephemeral Snapshot Stream | Kafka; EventStoreDB | High-frequency; never a rehydration source; ephemeral | + +**Intent Store must be GitOps** — the PR workflow, branch-per-request, and human review are architectural features. + +**Requested Store should NOT be GitOps at production scale** — Git throughput degrades under high-frequency machine writes; PR mechanics add latency with no benefit for machine-generated content. Write-once document store with hash-chain integrity satisfies the contract. + +**Realized Store is NOT an event stream** — it is a write-once snapshot store. Each record is a complete entity state, not a field-level event. This makes rehydration a direct lookup (not a replay) and makes point-in-time queries trivial. + +**Discovered Store remains an event stream** — high-frequency, machine-generated, ephemeral; never a rehydration source. + +### 47.2 The Fundamental Realized Store Constraint + +> **Realized State only changes when an authorized request produces a corresponding Requested State record. No exceptions.** + +Three write sources — all require `corresponding_requested_state_uuid` (non-nullable): +1. `initial_realization` — provider confirms first provisioning +2. `consumer_update` — consumer targeted delta approved and confirmed +3. `provider_update` — DCM approves a Provider Update Notification + +**What does NOT write to Realized Store:** +- Drift detection (reads only) +- Discovery cycles (writes to Discovered Store only) +- Unsanctioned provider changes (become drift events) +- Direct admin writes (bypassing the request pipeline is forbidden) + +**Drift is always unsanctioned** — there are no "legitimate drift events." Every authorized change goes through a request and produces a Requested State record. If Discovered State differs from Realized State without a corresponding Requested State record, it is drift. + +### 47.3 Provider Update Notification + +Formal mechanism for providers to report authorized state changes (auto-scaling, auto-healing, maintenance). Not drift — the provider is asserting the change was authorized. + +**DCM processing pipeline:** +``` +Provider submits POST /api/v1/provider/entities/{uuid}/update-notification + → Authentication (provider mTLS) + → Policy Engine evaluates (pre-authorized? requires consumer approval?) + → APPROVED: create provider_update Requested State → write Realized State snapshot + → REQUIRES_APPROVAL: entity enters PENDING_REVIEW; consumer notified + → REJECTED: Realized State unchanged; discrepancy becomes drift +``` + +**Pre-authorization:** Providers declare update capabilities at registration. Organizations pre-authorize categories of updates via GateKeeper policy (e.g., auto-scale within 2× bounds). Pre-authorized updates are processed automatically. + +**Consumer approval API:** `GET /api/v1/resources/{uuid}/provider-notifications` and `POST /approve` or `/reject`. On approval → new Requested State + Realized State. On rejection → drift event. + +**Idempotency:** `notification_uuid` is the idempotency key. Safe to resend on provider crash. + +**Level 2 conformance requirement** in the Operator Interface Specification for providers implementing auto-scaling, auto-healing, or provider-side maintenance. + +### 47.4 Realized State Snapshot Structure + +```yaml +realized_state_snapshot: + realized_state_uuid: + entity_uuid: + realized_at: + source_type: initial_realization | consumer_update | provider_update + corresponding_requested_state_uuid: # mandatory, not nullable + supersedes_realized_state_uuid: + superseded_by_realized_state_uuid: + fields: { # complete entity state } +``` + +### 47.5 Rehydration from Realized State + +Rehydration picks a specific snapshot — direct lookup by `realized_state_uuid` or by timestamp. Not a replay. Not a projection. A complete entity state that was explicitly authorized through DCM's governance pipeline. The supersession chain enables historical rehydration ("rehydrate as of March 15"). + +### 47.6 New Policies + +- `STO-007`: Realized Store is write-once snapshot; every write requires non-nullable `corresponding_requested_state_uuid`; enforcement at store API level +- `STO-008`: Intent Store requires GitOps; Requested Store requires write-once semantics (GitOps reference impl; write-once document stores supported at scale) +- `RSE-010`: Realized State only changes via authorized request; drift detection never writes to Realized Store +- `RSE-011`: Provider Update Notifications evaluated by Policy Engine before any Realized State change +- `RSE-012`: Categories of provider updates may be pre-authorized via GateKeeper policy +- `RSE-013`: Provider updates requiring consumer approval place entity in PENDING_REVIEW + +--- + +## SECTION 48 — NOTIFICATION MODEL + +### 48.1 Core Principle: Relationship Graph Determines Audience + +The audience for every notification is derived from the **entity relationship graph at event time** — not from a manually maintained subscriber list. When VLAN-100 is decommissioned, every VM attached to it gets notified automatically through their relationship edges. No subscription management required. + +### 48.2 notification service — Ninth Provider Type + +| # | Type | +|---|------| +| 1-8 | (existing providers) | +| **9** | **notification service** — translates DCM unified envelope to delivery channel (Slack, PagerDuty, email, ServiceNow, webhook, SMS); handles delivery, retry, dead letter; reports delivery status back to DCM | + +notification services register with DCM declaring supported channels, sovereignty, and delivery guarantees. Organizations configure which channel to use per subscription. + +### 48.3 Three Subscription Tiers + +**Tier 1 — Mandatory system notifications (non-suppressable):** Security events, sovereignty violations, audit chain breaks. Always delivered to Security Team + Platform Admin. Cannot be filtered. + +**Tier 2 — Tenant defaults:** Tenant admin configures baseline for all resources in Tenant — which event categories fire, which channels, urgency routing. + +**Tier 3 — Actor subscriptions:** Individual actors subscribe to specific events on specific resources or resource types. + +Tiers compose: Tier 1 always fires; Tier 2 applies to all Tenant resources; Tier 3 adds specifics. Actor subscriptions can add channels but cannot suppress Tier 1. + +### 48.4 Audience Resolution Algorithm (6 steps) + +1. Direct owner of changed entity (role: owner) +2. Traverse relationship graph — for each relationship: check event relevance, check min stake_strength, resolve related entity's owner (role: stakeholder) +3. Approval requirements — add approvers (role: approver) +4. Mandatory system audiences (Security Team, Platform Admin for security events) +5. Actor subscription overrides (can add; cannot remove mandatory) +6. Deduplicate; same actor via multiple paths → one notification with all roles listed + +### 48.5 Event Taxonomy (closed vocabulary — 7 categories) + +1. **Request lifecycle:** acknowledged, requires_approval, approved, dispatched, completed, failed, cancelled, gatekeeper_rejected +2. **Resource lifecycle:** realized, state_changed, ttl_warning, ttl_expired, suspended, resumed, decommissioning, decommissioned, decommission_deferred, ownership_transferred, pending_review +3. **Drift and discovery:** drift.detected, drift.severity_escalated, drift.resolved, drift.escalated, unsanctioned_change.detected +4. **Provider update:** submitted, requires_approval, approved, rejected, auto_approved +5. **Dependency and relationship:** dependency.state_changed, stakeholder.resource_decommissioning, allocation.pool_capacity_low, cross_tenant_auth.revoked +6. **Governance:** policy.activated, external_policy_evaluator.trust_elevated, profile.changed, catalog_item.deprecated +7. **Security/system (mandatory):** audit.chain_integrity_alert, sovereignty.violation, federation.tunnel_degraded, security.unsanctioned_provider_write + +### 48.6 Notification Envelope (unified — all channels) + +Key fields: notification_uuid (idempotency), correlation_id (links to audit record), event_type, urgency (critical/high/medium/low), entity info, audience role + stakeholder_reason (WHY this actor is in audience), context (previous/new state, changed_fields), action (type/url/deadline for approvals), deep links. + +### 48.7 Provider Update + Notifications Integration + +`provider_update.requires_approval` fires → consumer receives notification with `action.type: approve`, `action.deadline` (default PT24H). Approved → `provider_update.approved` + `entity.state_changed` to stakeholders. Rejected → `provider_update.rejected` + drift event. + +### 48.8 Webhooks Are Now a Notification Channel + +Outbound webhooks (doc 18) are superseded by the Notification Model. Webhooks are one channel type within a notification service. Existing webhook registrations are auto-converted to actor-level subscriptions with a webhook-type notification service — no migration needed. + +### 48.9 Delivery Pipeline + +Event → Audit record → Notification Router resolves audience → Subscription resolution → Envelope generation per actor → Route to notification service(s) → Provider delivers → Delivery confirmation → NOTIFICATION_DISPATCHED audit record. + +### 48.10 Policies + +NOT-001 through NOT-008. Key: audience derived from relationship graph (NOT-001); mandatory notifications never suppressable (NOT-002); cross-tenant notifications sovereignty-checked (NOT-003); every dispatch is audited (NOT-004); notification service must be registered for external delivery (NOT-007); event taxonomy is closed vocabulary (NOT-008). + +REL-022 through REL-024: traversal depth declared in Resource Type Spec; default depth 1; sovereignty respected; same actor via multiple paths → one notification with all roles. + +--- + +## SECTION 49 — GROUP 2: OPERATIONAL MODELS + +### 49.1 Three Timeout Scopes + +All three independently configurable and audited: +- **Assembly timeout** — max time for Request Payload Processor nine-step assembly (standard: PT3M; prod: PT2M) +- **Dispatch timeout** — max time waiting for provider realization after dispatch (standard/prod: PT30M-PT1H; resource-type overrides for legitimately long types) +- **Reserve-query timeout** — max time for a single provider to respond to reserve query (prod: PT5S); on timeout: skip that candidate, continue placement loop + +### 49.2 Cancellation — Three Scenarios + +1. **Before dispatch:** Clean cancel; no provider interaction; entity → CANCELLED +2. **After dispatch, provider not started:** DCM sends cancellation; provider confirms; entity → CANCELLED +3. **During PROVISIONING:** Provider capability-dependent: + - Supports cancellation: send cancel; provider attempts rollback; outcome → Recovery Policy + - No cancellation support: CANCEL_PENDING; wait for completion; LATE_RESPONSE_RECEIVED fires + +Cancellation is always best-effort — never guaranteed. Provider declares `supports_cancellation` and `partial_rollback_possible` at registration. + +### 49.3 Discovery Scheduling — Three Trigger Types + +1. **Scheduled (cron):** Each Resource Type Spec declares discovery interval; profile overrides; profile_min=PT4H minimal, PT5M fsi/sovereign +2. **Event-triggered:** After entity.realized (PT30S delay), drift.resolved (PT60S), provider.degraded (immediate), TIMEOUT_PENDING (PT5M orphan detection), COMPENSATION_FAILED (immediate) +3. **On-demand:** `POST /api/v1/admin/discovery/trigger` by platform admin; also used by CI/CD pre-validation and brownfield ingestion + +Discovery Scheduler component maintains priority queue (Critical → High → Standard → Background). Queue depth bounded per profile. + +### 49.4 Recovery Policy Model — The Unified Framework + +Recovery Policies are a formal DCM policy type (alongside GateKeeper, Validation, Transformation). Same authoring, GitOps store, shadow mode, activation workflow, and audit trail. + +**Trigger vocabulary (closed):** ASSEMBLY_TIMEOUT, DISPATCH_TIMEOUT, RESERVE_QUERY_ALL_EXHAUSTED, LATE_RESPONSE_RECEIVED, CANCELLATION_SENT, CANCELLATION_CONFIRMED, CANCELLATION_FAILED, PARTIAL_REALIZATION, COMPENSATION_IN_PROGRESS, COMPENSATION_FAILED + +**Action vocabulary (closed):** DRIFT_RECONCILE, DISCARD_AND_REQUEUE, DISCARD_NO_REQUEUE, ACCEPT_LATE_REALIZATION, COMPENSATE_AND_FAIL, NOTIFY_AND_WAIT (with deadline + on_deadline_exceeded), ESCALATE, RETRY (with backoff + max_attempts + on_exhaustion) + +### 49.5 Four Built-in Recovery Profile Groups + +| Group | Posture | Profile Default | +|-------|---------|----------------| +| `recovery-automated-reconciliation` | Trust drift detection to converge | minimal/dev/standard | +| `recovery-discard-and-requeue` | Clean up and restart on ambiguity | (opt-in) | +| `recovery-notify-and-wait` | Notify human; never act automatically | prod/fsi/sovereign | +| `recovery-aggressive-retry` | Retry everything before giving up | (opt-in) | + +Binding hierarchy: resource-type override > Tenant override > profile default > system default (automated-reconciliation). + +`recovery_posture` is a Policy Group concern_type (alongside security, compliance, operational, zero_trust_posture, data_authorization_boundary, orchestration_flow). + +### 49.6 Late Response Pipeline + +Provider responds after DCM timeout: +1. Late Response Handler activates (entity in TIMEOUT_PENDING state) +2. Cancel the pending cancellation if not yet sent +3. Write realized payload to Realized Store +4. Entity → LATE_REALIZATION_PENDING (if NOTIFY_AND_WAIT) or action per policy (if DRIFT_RECONCILE or DISCARD_AND_REQUEUE) + +NOTIFY_AND_WAIT consumer interface: `GET /api/v1/resources/{uuid}/recovery-decisions` and `POST` with chosen action. Platform admin can resolve any entity's pending decision via Admin API. + +### 49.7 Compound Service Compensation + +Declared per component in service definition: +- `required_for_delivery: atomic` — failure triggers full compensation rollback +- `required_for_delivery: partial` — failure → DEGRADED (not FAILED); no compensation triggered +- `compensation_on_failure: decommission_immediately | release_allocation | skip | notify` +- `compensation_order: ` — reverse order = first-decommissioned; lowest compensation_order runs last in reverse + +Partial delivery policy: `min_required_components` declares minimum for DEGRADED delivery; `auto_retry_optional_components` retries failed optional components. + +### 49.8 Five New Lifecycle States + +| State | Entry | Recovery Trigger | +|-------|-------|-----------------| +| TIMEOUT_PENDING | Dispatch timeout fired | DISPATCH_TIMEOUT | +| LATE_REALIZATION_PENDING | Late response received + NOTIFY_AND_WAIT | LATE_RESPONSE_RECEIVED | +| INDETERMINATE_REALIZATION | DRIFT_RECONCILE action taken | — | +| COMPENSATION_IN_PROGRESS | Compound rollback underway | — | +| COMPENSATION_FAILED | Rollback itself failed | COMPENSATION_FAILED | + +### 49.9 Orphan Detection Pipeline + +Triggers: timeout with cancellation sent, cancellation failed, compensation failed, DISCARD_NO_REQUEUE. Queries provider for resources matching Requested State characteristics in the provisioning time window, excluding known Realized State UUIDs. Creates ORPHAN_CANDIDATE records; notifies platform admin (urgency: high); human resolves (manual decommission, adopt into DCM, or mark false positive). + +### 49.10 Policies + +OPS-010 through OPS-019. Key: cancellation always best-effort (OPS-011); recovery policies are formal DCM policy type (OPS-014); four built-in recovery profiles (OPS-015); binding hierarchy resource-type > Tenant > profile (OPS-016); compensation in reverse dependency order (OPS-017); orphan detection on any uncertain cleanup (OPS-018); NOTIFY_AND_WAIT deadline always has on_deadline_exceeded action (OPS-019). + +--- + +## SECTION 50 — GROUPS 3, 4, AND 5: FINAL ARCHITECTURE GAPS + +### 50.1 Cost Analysis — Information Provider Model (Group 3) + +Cost Analysis is an **Information Provider** — not a built-in DCM component. DCM does not calculate costs; it provides input data and consumes cost signals. Integration target: Red Hat Cost Management or any external cost management platform. + +**DCM provides to Cost Analysis:** entity lifecycle events (realized/suspended/decommissioned with billing_state), provider catalog item declared costs, provider capacity utilization, request payload previews for pre-request estimates. + +**Cost Analysis provides to DCM:** pre-request cost estimates (pulled by service catalog and CI pipeline), placement cost signals (pulled during placement Step 4), cost actuals (pushed after billing period), budget alerts (pushed when thresholds approached). + +**Fallback chain:** Cost Analysis provider → static declared cost (provider registration) → resource type default estimate → no estimate. Staleness thresholds govern fallback (PT24H standard; PT1H sovereign). + +CMP-001, CMP-002. + +### 50.2 Orchestration — Reconciled Model (Replaces Conflicting Earlier Statements) + +DCM orchestration operates at two levels that compose through the same Policy Engine and event bus: + +**Level 1 — Named Workflow Artifacts (explicit, visible, auditable):** +An Orchestration Flow Policy with `concern_type: orchestration_flow` and `ordered: true` is a named workflow. It declares steps in explicit sequence using the closed payload type vocabulary as step identifiers. Named workflows are first-class Data artifacts — versioned, GitOps-managed, profile-bound, same lifecycle as all other artifacts. The request lifecycle pipeline is a built-in system Orchestration Flow Policy that cannot be deactivated but can be extended. Workflows are triggered: by events on the Request Orchestrator, by schedule (via Discovery Scheduler pattern), manually via Admin API, or by output of another policy. + +**Level 2 — Dynamic Policies (conditional, inline):** +GateKeeper, Transformation, Recovery, Governance Matrix, and Lifecycle Policies fire when their match conditions are satisfied — within or alongside workflow steps. They are not declared in workflow artifacts; they evaluate whenever payload state matches their conditions. + +**How they compose:** A named workflow step fires when its declared payload type event occurs. Dynamic policies also fire on the same event if their conditions match. Both are evaluated by the same Policy Engine. Both are triggered by events on the Request Orchestrator event bus. The workflow provides the explicit sequence skeleton; dynamic policies provide conditional behavior within it. + +**The "Orchestrator" term** in earlier sections refers to the combination of: Request Orchestrator (event bus) + Orchestration Flow Policy evaluation (named workflows) + Policy Engine (dynamic policy evaluation). There is no separate "Orchestrator" component — the Request Orchestrator is the event bus, and workflows are Policies. + +**Adding an explicit pipeline step** = add a step to an Orchestration Flow Policy artifact. +**Adding conditional behavior** = write a GateKeeper, Transformation, or Recovery policy. +**Both are Data artifacts evaluated by the Policy Engine.** + +### 50.3 Ingress API vs Consumer API (Group 5 fix) + +The **Ingress API** is the network infrastructure layer (API Gateway) — TLS termination, auth validation, rate limiting, ingress block population, routing. It routes to three logical API surfaces on distinct path prefixes: +- `/api/v1/` → **Consumer API** (catalog, requests, resource management, audit) +- `/api/v1/provider/` → **Provider API** (callbacks, update notifications, cancellation) +- `/api/v1/admin/` → **Admin API** (discovery triggers, orphan review, tenant management) + +The Ingress API is not a separate service — it is the API Gateway component. CMP-007. + +### 50.4 Consumer Rate Limiting and Quota Model (Group 5 fix) + +**Request rate quotas** — enforced at Ingress API level per actor; returns 429 with Retry-After. Configured in platform-domain layer. + +**Resource quotas** — enforced by GateKeeper policies at Step 5 (pre-placement). No hardcoded mechanism — quotas are declared policies. Quota exceeded → QUOTA_EXCEEDED GateKeeper rejection. Quota increase requests submitted via `Process.QuotaIncreaseRequest` catalog item → Orchestrator routes to platform admin for approval → GateKeeper policy updated. + +CMP-006. + +### 50.5 Drift Severity — Three-Tier Classification (Group 4 fix) + +**Tier 1 — Field criticality** (declared in Resource Type Spec): `drift_criticality: minor|significant|critical` per field. + +**Tier 2 — Magnitude thresholds** (system layer, overridable at platform/tenant): >50% change on significant field upgrades to critical; 10+ changed items upgrades minor to significant. + +**Tier 3 — Provider and consumer injection:** Providers suggest severity in update notifications (raise only). Consumers override sensitivity on specific entities (raise or lower — entity owner controls their resource's sensitivity). + +**Resolution:** highest severity from all three tiers wins. + +### 50.6 Cross-Tenant Authorization Lifecycle (Group 4 fix) + +`cross_tenant_authorization` is a DCMGroup with `group_class: cross_tenant_authorization`. Created by: granting Tenant admin (standard), Platform Admin (emergency), or pre-authorization policy (automated). Has declared duration or perpetual. On revocation: all active allocations/stakes under that authorization enter PENDING_REVIEW; notifications to both Tenant admins and affected resource owners; PT72H default resolution deadline; on_deadline_exceeded recovery policy fires. CTX-001 through CTX-004. + +### 50.7 Bootstrap Tenant Creation Sequence (Group 4 fix) + +Three foundation Tenants created during bootstrap (declared in bootstrap manifest, cannot be decommissioned): +- `__platform__` — owns DCM's own control plane resources +- `__transitional__` — holds brownfield entities during ingestion +- `__system__` — owns system-level artifacts + +Bootstrap sequence: verify manifest → initialize storage → create foundation Tenants → create initial Platform Admin actor → activate system layers/policies/recovery profiles → register built-in providers → ready. RED-016. + +### 50.8 Catalog Item vs Resource Type Clarification (Group 5 fix) + +**Resource Type** — classification category; vendor-neutral; declares field schema expectations; groups catalog items for portability. + +**Resource Type Specification** — versioned formal definition in registry; providers implement against this. + +**Provider Catalog Item** — what a specific provider offers to consumers: specific options, cost, availability, SLAs, linked to a Resource Type Specification version. Can be a resource allocation OR a process (automation job, playbook, pipeline). *Consumers request by Resource Type; DCM resolves to a catalog item.* + +Anti-vocabulary: never say "catalog item" when you mean "resource type specification." Never say "resource type" when you mean a specific offering. + +### 50.9 BBQ-001 and Federation Routing Reconciliation (Group 5 fix) + +These operate at different scopes — complementary not conflicting: +- **DCM-010 sovereignty pre-filter (Hub level):** Which Regional DCMs are eligible for this request? +- **BBQ-001 check (Regional DCM level):** Is this Mode 4 External Policy Evaluator endpoint within my sovereignty boundary? + +Hub selects Regional DCM using DCM-010. Regional DCM applies BBQ-001 for its own Mode 4 queries. Hub sovereignty pre-filter does NOT bypass Regional DCM's BBQ-001 check. + +--- + +## SECTION 51 — ACCREDITATION, DATA AUTHORIZATION MATRIX, AND ZERO TRUST + +### 51.1 Three Interconnected Models + +Three models compose to govern trust and data handling across all DCM boundaries: +1. **Accreditation** — is this component certified to handle this data type? +2. **Data/Capability Authorization Matrix** — given certification, what data/capabilities are permitted across this boundary? +3. **Zero Trust** — is this specific call, right now, from who it claims to be, permitted to do what it's attempting? + +All three checks run at every interaction boundary. All five boundary checks (identity → authorization → accreditation → matrix → sovereignty) produce audit records regardless of outcome. + +### 51.2 Data Classification — First-Class Field Metadata + +Eight classification levels: `public | internal | confidential | restricted | phi | pci | sovereign | classified` + +Carried as `data_classification` on every field in every DCM payload. Declared in: Resource Type Specification (default per field), Data Layer (domain-wide override), explicit field instance (highest precedence). `phi`, `sovereign`, `classified` are **immutable once set** — no layer or policy may downgrade them (ACC-003). Default for unclassified fields: `internal`. + +### 51.3 Accreditation Model + +First-class versioned artifacts. Seven types (ascending trust): `self_declared`, `first_party`, `third_party`, `qsa_assessment`, `baa`, `regulatory_certification`, `sovereign_authorization`. Lifecycle: developing → proposed → active → deprecated → retired. Renewal warning P90D before expiry. On expiry/revocation: **Accreditation Gap** record created; Recovery Policy evaluates response; affected entities potentially blocked. + +Accreditations cover: `data_classifications`, `capabilities`, `geographic_scope`. DCM deployments themselves carry accreditations (enabling federated trust verification). Providers declare accreditations via `POST /api/v1/provider/accreditations` → proposed → platform admin activates. + +### 51.4 Data/Capability Authorization Matrix + +Policy Group artifact with `concern_type: data_authorization_boundary`. Activated as part of compliance domain group (HIPAA domain → HIPAA boundary matrix). Three sections: + +**Outbound data permissions:** `data_classification × required_accreditation_type → ALLOW | STRIP_FIELD | DENY_REQUEST | WARN_AND_ALLOW`. PHI requires BAA — no BAA → DENY_REQUEST. Restricted requires third_party — no third_party → STRIP_FIELD. + +**Capability permissions:** STORE_AT_REST on PHI requires BAA. REPLICATE_CROSS_REGION on PHI requires BAA + replication target also has BAA. EXPORT_TO_EXTERNAL_SYSTEM on PHI/restricted/sovereign requires regulatory_cert. + +**Inbound data permissions:** What provider may return; which partition stores it; consumer visibility requirements. + +**Federation boundary matrix:** `sovereign` and `classified` data = `hard_constraint: true` → NEVER crosses any federation boundary regardless of accreditation. This cannot be overridden by any policy. + +**Enforcement pipeline:** Classification inventory → Accreditation resolution → Matrix evaluation per field → ALLOW/STRIP/DENY/WARN → Audit record. + +### 51.5 Zero Trust Interaction Model + +**Network position grants zero trust.** Five checks at every boundary: +1. Identity verification (mTLS mutual; certificate pinning; hardware attestation for sovereign) +2. Authorization verification (explicit permission; scoped credential; not revoked) +3. Accreditation check (target holds required cert; current; in-scope) +4. Data/Capability Matrix check (fields and capabilities permitted) +5. Sovereignty check (BBQ-001; endpoint within boundary) + +All five produce audit records on pass AND fail. + +**Credentials:** Scoped (minimum necessary operation), short-lived (PT15M for fsi/sovereign; PT30M prod; PT1H standard), non-transferable. Bound to specific entity + provider + operation type. + +### 51.6 Zero Trust Posture — Sixth Policy Group Concern Type + +Four levels: `none` (minimal) → `boundary` (dev/standard; external boundaries only) → `full` (prod/fsi; everywhere including internal) → `hardware_attested` (sovereign; TPM/HSM required). + +Profile defaults: minimal=none, dev/standard=boundary, prod/fsi=full, sovereign=hardware_attested. + +### 51.7 Federation Tunnel Zero Trust + +Federation tunnels = secure transport, not implicit trust. Structure: mTLS with certificate pinning + per-message signing (ed25519) + replay protection (nonce + PT5M window). Federation credentials scoped to specific operation + specific tunnel + specific resource types. Non-transferable. + +Hub-spoke: Hub presents its own credential to Regional DCMs. Regional DCM credentials are never relayed. Each DCM instance verifies the Hub's accreditation before accepting federation messages. + +Data boundary: sovereign/classified NEVER crosses federation tunnel (hard_constraint). fsi: max classification = restricted within same jurisdiction. sovereign: internal only, same instance. + +### 51.8 Policies + +ZT-001 through ZT-005 (zero trust) + ACC-001 through ACC-006 (accreditation). Key: +- ZT-001: network position = zero trust +- ZT-003: sovereign/classified never crosses any boundary (hard constraint) +- ZT-004: federation tunnel = secure transport, not trust +- ACC-003: phi/sovereign/classified classification is immutable +- ACC-004: matrix enforced at every outbound boundary before dispatch +- ACC-006: zero_trust_posture is the sixth Policy Group concern type + +--- + +## SECTION 52 — PERSONAS + +| Persona | Primary Concern | +|---------|----------------| +| **Consumer** | Self-service access to resources and services | +| **Service Provider** | Exposing services through DCM catalog | +| **Auditor** | End-to-end transaction review and validation | +| **Policy Creator** | Defining and maintaining governance policies | +| **SRE** | Stability, uptime, drift reconciliation, brownfield management | +| **CTO** | Accelerate innovation, reduce risk, enable sovereignty | +| **CIO/MD** | Lifecycle management, agility, IT investment maximization | +| **CISO/CCO** | Sovereignty enforcement, compliance, risk reduction | +| **Application Owner** | Focus on application code, not infrastructure specifics | +| **Line of Business** | Business outcomes — new products, revenue, compliance | + +--- + +## SECTION 53 — GOVERNANCE MATRIX, REGISTRATION, AND DRIFT RECONCILIATION + +### 53.1 Unified Governance Matrix (doc 27) + +The **single enforcement point** for all cross-boundary data and capability decisions in DCM. Supersedes the Data/Capability Authorization Matrix in doc 26 Section 4. Evaluates every interaction using four axes: + +**Axis 1 — Subject (who):** actor | service_provider | information_provider | auth_provider | peer_dcm | process_provider | system. With identity (specific UUID or trust_posture or accreditation_level) and tenant scope. + +**Axis 2 — Data (what):** classification (exact/in/minimum/maximum), resource_type, field_paths (allowlist/blocklist/any with dot-notation paths including wildcards `fields.phi_*`), capability (read/write/store/replicate/export/notify/execute/discover/query/federate). + +**Axis 3 — Target (where):** type, specific provider/peer UUID, sovereignty_zone (match/not_in), jurisdiction (includes/excludes/intersects country codes), trust_posture (minimum), accreditation_held (includes/not_includes). + +**Axis 4 — Context (under what conditions):** profile (posture/compliance_domains), zero_trust_posture (minimum level), tls_mutual, hardware_attestation, federated, cross_jurisdiction, cross_tenant. + +**Decisions:** ALLOW | DENY | ALLOW_WITH_CONDITIONS | STRIP_FIELD | REDACT | AUDIT_ONLY + +**Hard vs soft enforcement:** Hard rules cannot be relaxed by any downstream rule — ever. GMX-004: sovereign/classified data DENY to all external targets is always hard regardless of profile. Soft rules can be tightened by more-specific domain rules. + +**Field-level granularity:** allowlist mode (only named fields cross boundary), blocklist mode (named fields stripped/redacted), passthrough. STRIP_FIELD removes field; REDACT replaces value with ``; if stripped field is required → escalates to DENY_REQUEST (GMX-010). + +**Profile-bound defaults:** minimal (pass public/internal; hard DENY sovereign/classified), dev (add confidential with TLS), standard (restricted requires third_party accreditation; PHI denied by default), prod (verified peers only for confidential+; notification fields stripped for restricted), fsi (cross-jurisdiction hard DENY for regulated data; PHI requires BAA+verified+full-ZT), sovereign (no sensitive data in federation; hardware attestation required for all federation). + +**Compliance domain rules:** Automatically added when domain active. HIPAA: minimum_necessary principle; PHI audit all interactions; no export without regulatory cert. GDPR: EU residency hard rule; personal identifier fields stripped outside EU zones. + +**Sovereignty zones:** Registered artifacts declaring jurisdictions, regulatory frameworks, inter-zone agreements, and required provider accreditation. Rules reference zones by ID, not raw country codes. + +**Evaluation algorithm:** Hard DENY first → any hard DENY = terminal DENY. Soft constraints by domain precedence (entity > resource_type > tenant > platform > system); DENY > STRIP_FIELD > ALLOW. Conditions evaluated for ALLOW_WITH_CONDITIONS. Field permissions applied. Audit record always written. GMX-001 through GMX-010. + +### 53.2 Registration Specification (dcm-registration-spec.md) + +**Provider Type Registry:** Three-tier (Core/Community/Organization). Each entry declares permissions, default_approval_method, default_trust_level, enabled_in_profiles, capability_schema_ref. Five provider types: service_provider (reviewed), information_provider (reviewed), auth_provider (verified), peer_dcm (verified), process_provider (reviewed). + +**Registration token model:** Pre-issued by platform admin (POST /api/v1/admin/registration-tokens). Scoped to provider_type, handle_pattern, sovereignty_zone. single_use. grants_auto_approval flag. Token value presented once — never retrievable. Max trust level bounded by token scope. + +**Approval method resolution:** most_restrictive(provider_type_default, profile_min_method, token_grants_auto). Profile can only tighten. Token can relax to auto ONLY if profile.allow_token_auto_approval=true. Committee approval cannot be relaxed by token. + +**Profile defaults:** minimal/dev → reviewed, token auto-approval enabled. standard → reviewed, token auto-approval enabled (max trust: standard). prod → reviewed; high-trust types require verified; no token auto-approval. fsi → verified everything; minimum_accreditation: third_party. sovereign → authorized everything; minimum_accreditation: regulatory_certification; hardware_attestation required. + +**Registration pipeline:** SUBMITTED → VALIDATING (8 automated checks: provider type enabled, governance matrix pre-check, registration token, certificate, sovereignty declaration, capability consistency, health endpoint, accreditation) → PENDING_APPROVAL → ACTIVE. Approval methods: auto (immediate), reviewed (one admin), verified (two independent admins), authorized (DCMGroup quorum). + +**Per-type capability schemas:** service_provider (resource types, capacity model, cancellation support, discovery, naturalization format, cost metadata), information_provider (data domains, authority level, query capacity, confidence model), auth_provider (auth modes, MFA methods, RBAC model, token lifetime), peer_dcm (federation scope, trust level, mTLS certificate), process_provider (workflow types, execution engine, callback pattern). + +**Federated trust postures:** verified (manually approved; full scope), vouched (Hub-introduced; bounded scope), provisional (crypto-verified; catalog_query only if profile permits). Approval: dev auto-promotes provisional; standard reviewed for verified; prod/fsi verified; sovereign authorized+hardware-attestation. Profile federation_policy block declares all parameters. + +**Ongoing lifecycle:** health monitoring (polling; degraded → reduced routing; failure_threshold → UNAVAILABLE; 2×threshold → drift triggered), certificate rotation (P90D default; P14D warning; P7D transition window), capability amendments (simplified flow), graceful deregistration (entity migration plan required), forced deregistration (verified/authorized; entities → INDETERMINATE_REALIZATION; Recovery Policy fires). + +### 53.3 Drift Reconciliation Component (doc 25 Section 7) + +Control plane component that compares Discovered State vs Realized State. Read-only — never writes to Realized Store. Produces drift records and events into Request Orchestrator. + +**Algorithm:** discovery.cycle_complete event → field-by-field comparison per entity → field criticality (from Resource Type Spec) × change magnitude (profile-governed thresholds) → severity matrix (minor/significant/critical) → unsanctioned check (no corresponding Requested State? → elevate one level; fire unsanctioned_change.detected) → drift_record created → drift.detected event → Policy Engine evaluates response. + +**Drift record:** entity_uuid, discovery_snapshot_uuid, realized_state_uuid, overall_severity, unsanctioned flag, drifted_fields (field_path, realized_value, discovered_value, field_criticality, change_magnitude, field_severity, elevated_for_unsanctioned), status (open/acknowledged/resolved/escalated). + +**Resolution tracking:** Drift record status updated when REVERT (next discovery shows clean) or UPDATE_DEFINITION (new Realized State written) or ACCEPT or entity DECOMMISSIONED. drift.resolved event published. + +**Governance matrix integration:** Checks if a governance matrix rule permits the provider to make this type of change. If yes: warning (provider should have submitted update notification). Still treated as drift — provider must use the Provider Update Notification API. + +DRC-001 through DRC-005. Nine control plane components now fully defined in doc 25. + +--- + + + +## SECTION 54 — FEDERATED CONTRIBUTION MODEL (doc 28) + +### Core Principle +DCM defaults to a federated model for data creation, import, usage, and lifecycle. Every authorized actor type can contribute Data artifacts within their permitted domain scope. The same GitOps PR flow and lifecycle (developing → proposed → active → deprecated → retired) applies to all contributors. Profile-bound auto-approval governs what requires human review. + +### Four Contributor Types +1. **Platform Admin** — all artifact types, all domains, no restrictions +2. **Consumer/Tenant** — tenant-domain policies, resource groups, notification subscriptions, webhook registrations, cross-tenant authorization records, request layers +3. **Service Provider** — resource type specs (types they offer), provider catalog items, service layers, provider-domain policies +4. **Peer DCM** — registry entries, policy templates, service layers (via federation channels, scoped by trust posture) + +### Contributor Permission Boundaries (hard DENY — Governance Matrix enforced) +- Consumers cannot contribute system or platform domain policies +- Providers cannot contribute specs for resource types they don't offer +- Provisional peers: registry entries only (no policies; authorized approval) +- Vouched peers: registry entries + service layers only (reviewed always) +- Verified peers: registry entries + policy templates + service layers (reviewed standard+; auto dev) + +### Universal Contribution Pipeline +Submit → Governance Matrix evaluates contributor permissions → proposed status (shadow mode for policies) → review flow (auto / reviewed / verified / authorized per profile + artifact type + contributor) → active → lifecycle by contributor (deprecate/retire) → platform admin override at any time + +### Contribution Artifact Types by Contributor +- Consumer: tenant policies (all 7 types), resource groups, notification subs, webhooks, cross-tenant auth records, request layers +- Provider: Resource Type Specs (their types), catalog items, service layers, provider-domain GateKeeper/Validation policies +- Peer DCM: registry entries, policy templates (verified peers), service layers (verified/vouched) + +### Contribution Store Directory Structure +`dcm-policy-store/system/` (platform admin) · `platform/` (platform admin) · `tenant//` (consumer) · `provider//` (provider) · `federated//` (peer DCM) +`dcm-registry/core/` (DCM project) · `community//` (community) · `organization//` (org) + +Every artifact includes `contributed_by` block: contributor_type, actor/tenant/provider/peer_dcm UUID, contribution_method (api/flow_gui/git_pr/federation_push), pr_url, reviewed_by. Immutable once set. + +### Profile-Governed Auto-Approval +- minimal/dev: most contributions auto-approved; shadow optional +- standard: consumer/provider policies → reviewed; shadow default on, P7D review period +- prod: governance matrix rules → verified; provider specs → reviewed; shadow P14D +- fsi: all consumer/provider contributions → verified; shadow P30D; must review all divergence cases +- sovereign: all → authorized; shadow P30D; orphaned artifacts auto-retire + +### Consumer API Contribution Endpoints (Section 9) +`POST /api/v1/contribute/policy` (generates PR, activates shadow mode) · `POST /api/v1/contribute/resource-group` (activates immediately) · `GET /api/v1/contribute` (list contributions) · `DELETE /api/v1/contribute/{uuid}` (withdraw, closes PR) + +### Organization Sub-Tiers (Registry) +Three-tier model extended to all artifact types: `organization/platform` (platform admin authored), `organization/provider` (provider authored, scoped to their types), `organization/tenant` (consumer authored, scoped to their Tenant). Lower sub-tier = lower inherent trust = may require additional review. + +### FCM-001 through FCM-008 System Policies +FCM-001: contributor recorded in contributed_by; immutable. FCM-002: domain scope violations = hard DENY. FCM-003: all contributions via GitOps PR (except auto-approve). FCM-004: policies enter shadow mode by default. FCM-005: platform admin override always available; audited. FCM-006: orphaned artifacts don't auto-deactivate (except sovereign). FCM-007: federation contribution scoped by trust posture. FCM-008: contributor scope limits absolute. + +--- + +## SECTION 55 — TERMINOLOGY GLOSSARY + +| Term | Definition | +|------|-----------| +| **DCM** | Data Center Management — the framework itself | +| **Sovereign Execution Posture** | Target state where all operations are governed, auditable, and sovereignty-compliant | +| **Hard Tenancy** | Strict isolation between tenants at the infrastructure level | +| **UDM** | Unified Data Model — the centralized data schema and single source of truth | +| **Naturalization** | Converting UDM format to provider-specific format for execution | +| **Denaturalization** | Converting provider-specific results back to UDM format | +| **Greening the Brownfield** | Bringing existing unmanaged resources under DCM lifecycle management | +| **Intent Portability** | Replaying an Intent State through current policies to produce a new Requested State | +| **Resource Type** | Abstract, vendor-neutral definition of a class of resource | +| **Provider Catalog Item** | Concrete provider implementation of a Resource Type Specification | +| **Portability-Breaking** | A field or operation that ties a request to a specific provider | +| **CMDB** | Configuration Management Database — DCM aims to replace the fragmented multi-CMDB problem | +| **GRC** | Governance, Risk, and Compliance | +| **TTR** | Time to Recovery — key metric for rehydration use case | +| **MTTD** | Mean Time to Deploy or Detect | +| **MTTR** | Mean Time to Recovery/Repair | +| **FSI** | Financial Services Institution | +| **IaC** | Infrastructure as Code | +| **GitOps** | Managing infrastructure definitions through Git workflows | +| **CRUD** | Create, Read, Update, Delete — full lifecycle operations | +| **IPU** | In-Place Upgrade | +| **EOL** | End of Life | +| **Field-Level Provenance** | Structural mechanism carrying data lineage within each data object | +| **Data Lineage** | Complete chain of custody of any field value from origin through all modifications | +| **Base Layer** | Foundation entity for a resource — every layer chain starts here | +| **Core Layer** | Type-agnostic data layer carrying organizational and infrastructure context | +| **Service Layer** | Type-scoped data layer carrying service-specific configuration — must declare Resource Type scope | +| **Request Layer** | Consumer's declared intent — becomes Intent State on submission | +| **Layer Chain** | Ordered sequence of layers merged to produce an assembled payload | +| **Assembly Process** | Seven-step process by which the Request Payload Processor builds a Requested State payload | +| **GateKeeper Policy** | Highest-authority policy that can override any field including consumer input | +| **Transformation Policy** | Policy that enriches or modifies payload fields — all changes recorded in provenance | +| **Validation Policy** | Policy that checks payload against rules — pass/fail, no field modification | +| **Resource/Service Request** | What a consumer submits to DCM — declared intent to consume a resource or service | +| **Resource/Service Entity** | The "thing" produced by a provider fulfilling a request — the allocation made real | +| **DCM Tenant** | Mandatory first-class ownership boundary for all Resource/Service Entities | +| **Allocation Model** | Provider retains infrastructure ownership; consumer owns the Entity allocation | +| **Whole Allocation Model** | Entire resource allocated as indivisible unit; provider retains ownership | +| **Full Transfer Model** | Provider transfers complete ownership of underlying resource to consumer Tenant | +| **Hybrid Transfer Model** | Ownership can transfer multiple times; always exactly one owning Tenant | +| **Process Resource Entity** | Ephemeral execution resource — playbook, pipeline, workflow. Permanent execution record. | +| **Dependency Graph** | Complete map of all resources required to fulfill a request including transitive dependencies | +| **Type-Level Dependency** | Portable, provider-agnostic dependency declared at Resource Type Specification level | +| **Provider-Specific Dependency** | Additional dependency declared at Provider Catalog Item level — must be marked portability-breaking | +| **Resource Group** | Flexible composable grouping entity — functions like a structured tag | +| **Custom Resource Group** | Implementor-defined grouping entity with full parity to DCM Default Resource Group | +| **Tenant Advocate** | DCM's role in protecting Tenant interests in all provider interactions | +| **DCM System Policy** | Non-overridable policy built into DCM — cannot be disabled or overridden by organizational policy | +| **Webhook** | Push-based outbound notification from DCM to an external system triggered by a DCM event | +| **Commit Log** | Stage 1 audit store — minimal record, Raft consensus quorum write, sub-millisecond; Audit Forward Service reads from it to produce full audit records | +| **Audit Forward Service** | DCM component that enriches Commit Log entries into full audit_record structures and delivers them to the Audit Store asynchronously with retry | +| **Self-Hosting** | DCM's own deployment is a DCM resource; DCM manages itself through the same model used for customer infrastructure | +| **dcm_deployment** | The DCM resource declaring DCM's own deployment — profile, replica counts, store implementations, redundancy configuration | +| **Bootstrap manifest** | The minimal configuration outside DCM's management scope used to bootstrap DCM before it can manage itself | +| **Redundancy by Default** | DCM architectural principle: every component and store has a redundancy model; `minimal` profile sets replicas: 1; all others set replicas >= 3 | +| **Quorum Write** | Write confirmed durable only when a majority of replicas acknowledge it; used by Commit Log and all durable stores in standard+ profiles | +| **Raft** | Consensus protocol used by Commit Log (etcd) for quorum writes; guarantees durability even if minority of replicas fail | +| **DCMGroup** | Universal group entity — all grouping constructs in DCM expressed as DCMGroup with group_class | +| **group_class** | Determines system behavior of a DCMGroup — closed built-in set: tenant_boundary, resource_grouping, policy_collection, policy_profile, layer_grouping, composite, federation | +| **Cost Analysis Information Provider** | Specialized Information Provider supplying cost estimates, placement cost signals, cost actuals, and budget alerts; DCM provides input data; provider performs calculations | +| **Orchestration Flow Policy** | Named workflow artifact: Orchestration Flow Policy with `ordered: true`; declares explicit step sequence using payload type vocabulary; first-class Data artifact; versioned, GitOps-managed, profile-bound | +| **Request Orchestrator** | Runtime event bus; routes lifecycle events to Policy Engine; has no pipeline logic; both named workflows and dynamic policies are evaluated through it | +| **orchestration (DCM)** | Two-level composable model: Level 1 = named Orchestration Flow Policies (explicit sequence); Level 2 = dynamic policies (conditional, inline); both evaluated by Policy Engine; adding a step = adding to a workflow Policy; adding conditional behavior = writing a dynamic policy | +| **Provider Catalog Item** | What a specific Service Provider offers consumers: specific resource allocation or process with cost, availability, SLAs; linked to Resource Type Specification version | +| **cross_tenant_authorization** | DCMGroup with group_class: cross_tenant_authorization; grants one Tenant permission to reference/allocate/stake another Tenant's resources; revocation places active allocations in PENDING_REVIEW | +| **foundation Tenants** | Three system Tenants created at bootstrap: __platform__, __transitional__, __system__; cannot be decommissioned; declared in bootstrap manifest | +| **QUOTA_EXCEEDED** | GateKeeper rejection code when resource quota policy fires at Step 5 (pre-placement) | +| **Federated Contribution Model** | DCM defaults to federated data creation — all authorized actor types (platform admin, consumer/tenant, service provider, peer DCM) can contribute Data artifacts within their domain scope via the GitOps PR model; see doc 28 | +| **contributor** | Actor type that authored a Data artifact; recorded in artifact_metadata.contributed_by; determines review requirements; platform_admin / consumer / service_provider / peer_dcm | +| **contributed_by** | Artifact metadata block recording contributor_type, actor UUID, contribution_method, pr_url, reviewed_by; immutable once set | +| **DPO-001–006** | Design Priority system policies. DPO-001: security properties present in all profiles (not controlled by profiles). DPO-002: every security requirement needs an ease-of-use mechanism. DPO-005: minimal profile = "security with minimal overhead" not "minimal security". DPO-006: when security and ease conflict, redesign ease-of-use, not security. | +| **FCM-001–008** | Federated Contribution Model system policies; key: FCM-002 (domain scope violations = hard DENY), FCM-003 (GitOps PR for all), FCM-008 (contributor scope limits absolute) | +| **Unified Governance Matrix** | Single enforcement point for all cross-boundary decisions; four axes (subject/data/target/context); hard vs soft enforcement; field-level granularity (allowlist/blocklist/paths); profile-bound defaults; GMX-001–010 | +| **governance_matrix_rule** | Artifact declaring match conditions across four axes and a decision (ALLOW/DENY/ALLOW_WITH_CONDITIONS/STRIP_FIELD/REDACT/AUDIT_ONLY) with hard or soft enforcement | +| **sovereignty_zone** | Registered DCM artifact declaring geopolitical/regulatory boundary; rules reference zones by ID; inter-zone agreements declared explicitly | +| **STRIP_FIELD** | Governance matrix decision: remove named fields from payload and proceed; if stripped field is required → DENY_REQUEST | +| **REDACT** | Governance matrix decision: replace field value with ``; field presence preserved; receiver knows field exists but not its value | +| **Provider Type Registry** | Three-tier registry of approved provider types; each entry declares permissions, default_approval_method, enabled_in_profiles, capability_schema_ref | +| **registration_token** | Pre-issued by platform admin; scoped to provider_type/handle_pattern/zone; single_use; grants_auto_approval flag; value presented once only | +| **approval_method** | Registration approval: auto | reviewed | verified | authorized; resolved as most_restrictive(provider_type_default, profile_min, token_effect) | +| **Drift Reconciliation Component** | Control plane component; compares Discovered vs Realized State; produces drift records and events; never writes to Realized Store; DRC-001–005 | +| **drift_record** | Artifact produced by Drift Reconciliation; field-by-field comparison result with severity classification; unsanctioned flag; status tracking through resolution | +| **Placement Engine** | Six-step algorithm: sovereignty filter → accreditation filter → capability filter → reserve query → tie-breaking (policy/priority/affinity/cost/load/hash) → confirm; PLC-001–006 | +| **reserve_query** | Parallel capacity queries to all eligible provider candidates; PT5M capacity hold; non-responders and insufficient-capacity providers excluded | +| **consistent hash** | Final placement tie-breaker: SHA-256(request_uuid+resource_type+sorted_candidates); deterministic; never round-robin | +| **Lifecycle Constraint Enforcer** | Monitors TTL/expiry/max_execution_time; fires expiry actions through standard pipeline; grace period before action; Process Resources: immediate FAILED on breach; LCE-001–005 | +| **Search Index** | Non-authoritative queryable projection of GitOps stores; indexes key fields; returns git_path for full payload; max staleness PT5M; always rebuildable; SIX-001–004 | +| **Admin API** | Platform admin REST interface: Tenant lifecycle, provider review, accreditation approval, discovery trigger, orphan resolution, recovery decisions, quota management, Search Index rebuild, bootstrap operations | +| **PENDING_EXPIRY_ACTION** | Entity state when expiry action fails to execute; Lifecycle Constraint Enforcer retries per Recovery Policy; Platform Admin notified urgency: high | +| **data_classification** | First-class field metadata: public/internal/confidential/restricted/phi/pci/sovereign/classified; phi/sovereign/classified are immutable once set | +| **Accreditation** | Formal versioned attestation that a component satisfies a compliance framework; issued by an Accreditor; carries validity period; lifecycle: developing→proposed→active→expired/revoked | +| **Accreditor** | Entity that issues accreditations: government body, regulatory body, QSA, certification body, or internal audit team | +| **Accreditation Gap** | Missing, expired, or revoked accreditation required for an active interaction; always high/critical severity; Recovery Policy governs response | +| **Data/Capability Authorization Matrix** | Policy Group artifact (concern_type: data_authorization_boundary) declaring what data fields and capabilities are permitted across interaction boundaries given data classification and accreditation level | +| **zero_trust_posture** | Sixth Policy Group concern type; four levels: none/boundary/full/hardware_attested; profile defaults: minimal=none, dev/standard=boundary, prod/fsi=full, sovereign=hardware_attested | +| **Five-check boundary model** | Identity → Authorization → Accreditation → Matrix → Sovereignty; all five checks at every DCM interaction boundary; all produce audit records | +| **Federation tunnel** | Mutually authenticated, encrypted, scoped DCM-to-DCM channel; zero trust model; establishes secure transport only, not implicit trust; per-message signing; scoped non-transferable credentials | +| **hard_constraint** | Data/Capability Matrix declaration that cannot be overridden by any policy; sovereign/classified data never crossing federation boundaries is a hard_constraint | +| **STRIP_FIELD** | Matrix enforcement action: remove non-permitted field from payload and proceed; if stripped field is required → escalates to DENY_REQUEST | +| **DENY_REQUEST** | Matrix enforcement action: block entire interaction; entity enters PENDING_REVIEW; notification dispatched | +| **Request Orchestrator** | DCM control plane event bus; routes lifecycle events to Policy Engine; coordinates pipeline via event-condition-action; does not contain hardcoded pipeline logic | +| **Cost Analysis Component** | Internal DCM control plane component; three functions: pre-request estimation, placement input, ongoing attribution; not a billing system; not a provider type | +| **Module** | DCM capability extension adding new functions; distinct from Profile (which configures behavior) | +| **orchestration_flow** | Policy Group concern_type for static sequential flows; ordered: true; both static and dynamic flows compose through the same Policy Engine | +| **payload_type** | Closed vocabulary of event types the Request Orchestrator publishes; policies pattern-match on payload type + state | +| **OPA integration** | Reference implementation for Mode 3 External Policy Evaluators; DCM payload as OPA input document; built-in Rego functions provided by DCM | +| **Flow GUI** | Visual policy composer and orchestration manager; execution graph view, policy canvas, shadow mode dashboard, flow simulation | +| **__platform__** | Immutable system Tenant owning DCM control plane resources; created at bootstrap before Policy Engine comes online | +| **__transitional__** | Immutable system Tenant holding brownfield entities during INGEST phase | +| **bootstrap manifest** | Signed manifest declaring initial system Tenants, bootstrap admin, and initial profile; hash-verified at every DCM startup | +| **cross_tenant_authorization** | DCMGroup with this group_class formally grants one Tenant access to another's resources; has lifecycle (duration, renewal, revocation); revocation places active allocations in PENDING_REVIEW | +| **drift_criticality** | Field-level property in Resource Type Spec (low/medium/high/critical); combined with change magnitude to produce drift severity | +| **Ingress API** | Infrastructure-layer entry point for all inbound DCM requests; sets ingress block; routes to Consumer/Provider/Admin API surfaces | +| **Provider Catalog Item** | Provider-specific instantiation of a Resource Type Specification; what consumers actually request; distinct from the Resource Type Specification itself | +| **Recovery Policy** | Formal DCM policy type mapping trigger conditions (DISPATCH_TIMEOUT, PARTIAL_REALIZATION, etc.) to response actions; same authoring model as GateKeeper/Validation/Transformation | +| **recovery_posture** | Fifth Policy Group concern_type governing failure and ambiguity response; binds a recovery profile group to the deployment | +| **DRIFT_RECONCILE** | Recovery action: schedule discovery; let drift detection resolve actual state | +| **DISCARD_AND_REQUEUE** | Recovery action: best-effort cleanup; new request cycle created immediately | +| **NOTIFY_AND_WAIT** | Recovery action: notify human; wait for explicit decision up to declared deadline | +| **TIMEOUT_PENDING** | Infrastructure Resource Entity state: dispatch timeout fired; recovery policy evaluating | +| **LATE_REALIZATION_PENDING** | Entity state: provider responded after timeout; NOTIFY_AND_WAIT recovery decision pending | +| **INDETERMINATE_REALIZATION** | Entity state: state ambiguous; drift detection resolving | +| **COMPENSATION_FAILED** | Entity state: compound service rollback itself failed; orphan detection active | +| **orphan_candidate** | Resource discovered at provider with no corresponding Realized State record; surfaced to platform admin for human resolution | +| **Discovery Scheduler** | DCM control plane component maintaining priority queue of discovery requests; dispatches to provider discovery endpoints | +| **recovery-automated-reconciliation** | Built-in recovery profile: trust drift detection; accept late responses; appropriate for dev/standard | +| **recovery-notify-and-wait** | Built-in recovery profile: notify human; never act automatically; appropriate for prod/fsi/sovereign | +| **notification service** | Ninth DCM provider type; translates unified notification envelope to delivery channel; handles delivery, retry, dead letter, and delivery confirmation callbacks | +| **Notification Router** | DCM control plane component that resolves notification audiences and routes envelopes to notification services | +| **audience resolution** | Deriving notification recipients by traversing the entity relationship graph from the changed entity at event time | +| **notification_uuid** | Idempotency key on notification envelopes; notification services use this to deduplicate on retry | +| **audience_role** | owner / stakeholder / approver / observer — why this actor is in the notification audience | +| **stakeholder_reason** | Notification envelope field explaining which relationship caused the actor to be in the stakeholder audience | +| **Tier 1 / Tier 2 / Tier 3 notifications** | Mandatory system (non-suppressable) / Tenant defaults / Actor subscriptions — three subscription tiers that compose | +| **NOT-001 through NOT-008** | Notification model system policies | +| **write-once snapshot store** | Realized Store implementation model: each record is a complete immutable entity state snapshot; no event replay; direct point-in-time lookup; supersession chain links snapshots | +| **corresponding_requested_state_uuid** | Mandatory non-nullable field on every Realized State snapshot; traces every Realized State change to an authorized request | +| **Provider Update Notification** | Formal API for providers to report authorized state changes; DCM evaluates via Policy Engine; approved → new Requested State + Realized State; rejected → drift event | +| **notification_uuid** | Idempotency key on Provider Update Notifications; safe to resend on provider crash | +| **pre-authorized update** | Category of provider update pre-approved by GateKeeper policy; processed automatically without per-change human review | +| **Whole Allocation** | Ownership pattern: consumer owns the entire resource entity outright in their Tenant; no pool involved | +| **Allocation** | Ownership pattern: pool yields independently-owned sub-resources; consumer owns their allocation; AllocationRecord relationship links to pool | +| **Shareable** | Ownership pattern: one resource, multiple stakeholders; consumers hold stakes (relationships) only; no consumer owns any portion | +| **AllocationRecord** | Cross-tenant relationship from an allocation entity back to its source pool entity | +| **stake_strength** | Relationship property on shareable resource attachments: required (blocks decommission) / preferred / optional | +| **PENDING_REVIEW** | Formal Infrastructure Resource Entity lifecycle state for conflicts requiring human resolution (sovereignty, cross-tenant auth revocation, ownership transfer conflicts) | +| **Consumer API** | DCM REST API for consumers: catalog browsing, request submission, resource management, audit trail access | +| **Consumer Request Status** | Lifecycle: ACKNOWLEDGED → ASSEMBLING → AWAITING_APPROVAL → APPROVED → DISPATCHED → PROVISIONING → COMPLETED/FAILED/CANCELLED | +| **01-entity-types.md** | Entity type taxonomy: Infrastructure Resource, Composite Resource, Process Resource; sub-types and invariants | +| **04-examples.md** | Worked examples: VM end-to-end, IP allocation, VLAN sharing, brownfield ingestion, drift remediation; Git repo structure | +| **04b-ownership-sharing-allocation.md** | Authoritative ownership model: whole allocation, allocation, shareable; policies OWN-001 through OWN-008 | +| **federation routing** | Hub DCM applies placement engine logic at the DCM instance level; Regional DCMs are DCM Provider instances; sovereignty is a hard pre-filter; same tie-breaking hierarchy as provider selection | +| **independent_with_overlap** | Certificate rotation model: old cert valid P30D after new cert issued; allows peers to update trust stores without coordinated downtime | +| **alert_and_hold** | Federated drift detection response when peer DCM is unavailable: do not assume drift; hold state; escalate to platform admin after PT24H | +| **AUDIT_STORE_UNAVAILABLE** | Gap record inserted in Audit Store hash chain after recovery from Audit Store failure; timestamps the exact outage window; makes gap explicit and auditable | +| **confidence aggregation** | Per-entity endpoint computing overall confidence band (= lowest field band); identifies contested and stale fields; computed on demand never stored | +| **explicit_no_filter** | Composite group declaration suppressing the no member_type filter linting warning; confirms broad targeting is intentional | +| **certified profile** | DCM profile carrying formal third-party certification metadata (HIPAA assessor, FedRAMP JAB, PCI QSA); promoted to Tier 1; applies to artifact not deployment | +| **POLICY_PROVIDER_ELEVATED** | Audit action recorded when a External Policy Evaluator's mode level is elevated; always produced regardless of profile | +| **tier_certifications** | Certification metadata on Resource Type Specs or profiles from recognized certifying bodies; filter criterion, not structural tier boundary | +| **tier_3_to_tier_2_promotion** | PR-based pathway for organizations to promote internal Tier 3 Resource Types to Verified Community (Tier 2); requires production deployment, OSS license, named maintainer, migration path | +| **independent operation mode** | Resource Type Registry state when upstream registry is permanently unavailable; existing types continue; new community type adoption requires governance decision | +| **SCIM 2.0** | System for Cross-domain Identity Management; optional Auth Provider capability for automated actor provisioning from enterprise IdPs; provisions actors and group memberships; roles not SCIM-provisioned | +| **step-up MFA** | Additional MFA challenge at sensitive operations within an already-authenticated session; declared per operation by policy; step-up token TTL PT10M | +| **actor.type** | Audit record field: human / service_account / system; enables filtering between human-initiated and automated lifecycle operations in queries and dashboards | +| **system_actor** | Audit record block on system-initiated records: identifies DCM component, trigger event, and authorizing policy UUID | +| **Merkle root proof** | Federation-level audit integrity mechanism: Hub DCM computes Merkle root of all Regional DCM chain tips daily; any chain break detectable against stored root | +| **per-instance hash chain** | Each DCM instance (Hub/Regional/Sovereign) maintains its own independent hash chain; not merged cross-instance; cross-referenced via correlation_id | +| **AUTH-012 through AUTH-015** | Auth Provider gap policies: SCIM provisioning, failover handling, two-tier MFA, pluggable user store | +| **AUD-014 through AUD-017** | Universal Audit gap policies: hash chain verification modes, Commit Log capacity, system-initiated records, distributed hash chains | +| **Hub DCM** | Central/global DCM instance; authoritative registry origin; governance authority; replaces "Shore" terminology | +| **Regional DCM** | Distributed regional DCM instance; manages resources in its region; caches from Hub DCM; replaces "Ship" terminology | +| **Sovereign DCM** | Air-gapped or compliance-isolated DCM instance; local static caches from signed bundles; replaces "Enclave" terminology | +| **native_passthrough** | Sanctioned field for provider-specific data that cannot be expressed in the unified model; always audit-logged; opaque mode blocked in fsi/sovereign | +| **BULK_PROMOTE** | Audit action for bulk entity promotion; single audit record with full member list; requires preview + approval in prod+ profiles | +| **CACHE policies** | CACHE-001 through CACHE-004 — cache placement, sync, authoritativeness, consistency | +| **DATA-001** | Policy: core data model does not embed technology-specific data; native_passthrough sanctioned with governance | +| **RED-011 through RED-015** | Deployment redundancy gap policies: bootstrap verification, K8s upgrades, runtime support, hardware specs, self-hosted drift | +| **ING-012 through ING-015** | Ingestion gap policies: signal priority configurable, bulk promotion, max sources, catalog promotion | +| **rehydration_history** | Immutable record on entity of all rehydration events: trigger, from/to provider, from/to provider-side IDs, actor, state refs | +| **rehydration_lease** | Exclusive time-bounded lock per entity during rehydration; prevents concurrent rehydrations; TTL prevents orphans | +| **min_auth_level** | Entity rehydration constraint declaring minimum actor authentication level required; profile governs enforcement | +| **allow_delegated_rehydration** | Entity flag permitting DCM service accounts to rehydrate automatically; requires platform admin authorization audit trail | +| **REHYDRATION_BLOCKED** | Audit event recorded when a concurrent rehydration attempt is rejected due to active lease | +| **hybrid retention mode** | Discovered State retention: minimum window + retain until drift resolved + hard maximum ceiling | +| **rolling_window retention** | Discovered State retention: keep last N days regardless of drift status | +| **override: allow/constrained/immutable** | Layer field metadata declaring override intent; enforced by Request Payload Processor at Step 3; immutable prevents lower-authority overrides only; GateKeeper may additionally lock | +| **constraint_visibility** | Policy-governed disclosure level for constrained fields: full (constraint+bounds+reason+suggestions), summary (bounds only), hidden (silently enforced) | +| **editable** | Resource Type Spec field declaration: can this field be modified post-realization via a targeted delta update (true) or only via reprovisioning (false) | +| **edit_constraints** | Bounds declared on editable fields: range, list, enum; validated at update time; same constraint types as assembly-time constraints | +| **requires_restart** | Editable field flag indicating whether a provider restart action is needed to apply the update | +| **targeted delta** | Update mechanism for editable fields: applies only changed fields to Realized State; does NOT re-run the layer assembly chain | +| **non_editable_reason** | Human-readable explanation on non-editable fields explaining why reprovisioning is required to change them | +| **confidence_descriptor** | Primary confidence data model: authority_level (from registration), corroboration (from ingestion), source_trust (from trust system), last_updated_at (from push) — stored fields | +| **freshness** | Derived confidence field computed at query time from (now - last_updated_at) vs thresholds: high (<1h), medium (<1d), low (<7d), stale (>7d) | +| **corroboration** | Confidence descriptor field: confirmed (2+ sources agree), single_source, contested (sources disagree) | +| **source_trust** | Confidence descriptor field maintained by trust scoring system: verified (score ≥80), degraded (60-79), suspended (<60) | +| **OBS-001** | Policy: DCM publishes curated Observability Event Stream via Message Bus; policy-filtered; raw metrics opt-in; does not replace audit records | +| **AUD-013** | Policy: Audit and Observability are definitively separate components — opposite trade-offs; cannot be combined | +| **group_subclass** | Advisory label on a DCMGroup — no system behavior; used for organization-specific semantics (e.g., cost_center, business_unit) | +| **composite group** | DCMGroup with group_class: composite — permits cross-type membership (resources, policies, layers, groups) | +| **federation group** | DCMGroup with group_class: federation — peer association of tenant_boundary groups; enables shared policies and consolidated reporting | +| **nested Tenant** | A tenant_boundary group with parent_group_uuid pointing to another tenant_boundary group | +| **federated Tenant** | A tenant_boundary group that is a member of a federation group | +| **former_group_membership** | Permanent provenance record retained by a member after group destruction or membership expiry | +| **group_destruction_record** | Permanent Audit Store record of a destroyed group including its full member list at destruction time | +| **member_type_filter** | Policy targeting declaration narrowing scope within a composite group to specific member types | +| **most_restrictive_wins** | Governance inheritance principle for nested Tenants — most restrictive policy at any level in the hierarchy applies | +| **Nested Tenant** | tenant_boundary group with parent_group_uuid — child maintains isolation; parent has governance overlay and cost rollup | +| **GRP-INV** | Universal group structural invariants — non-overridable regardless of enforcement_model or Profile | +| **Universal Audit Record** | Uniform audit record produced by every DCM component for every change — date/time, who, what, action | +| **Composite Actor Chain** | The who in an audit record — immediate actor + authorized_by human chain + originating request/policy | +| **Action Vocabulary** | Closed set of audit record action values — free text rejected at write time (AUD-007) | +| **Reference-Based Retention** | Audit records retained while any referenced entity is live — not fixed time schedule | +| **Write-Ahead Log (WAL)** | Local audit delivery buffer — change + audit record written atomically; Audit Store delivery async with retry; WAL cleared after Audit Store confirms | +| **Hash Chain** | Per-entity tamper-evident chain: record_hash + previous_record_hash; chain breaks detectable and trigger security alerts | +| **Mode 4 External Policy Evaluator** | Black box query-enrichment policy provider — DCM sends query, external system evaluates and/or enriches, returns structured result; logic is opaque to DCM | +| **Black Box Query-Enrichment** | Mode 4 operation where an external system simultaneously evaluates request data and injects enrichment fields into the payload | +| **audit_token** | Provider-issued reference in Mode 4 responses enabling cross-system audit correlation between DCM audit trail and provider's internal logs | +| **data_request_spec** | Mode 4 registration declaration of which fields the provider is authorized to receive, with classification ceiling per field | +| **Policy Naturalization** | Translation of external policy schemas (OSCAL, XCCDF, CIS JSON) into DCM policy format — Mode 3 External Policy Evaluator mechanism | +| **Policy Group** | Cohesive versioned collection of policies addressing a single identifiable concern — the unit of policy reuse | +| **Policy Profile** | Complete DCM configuration for a specific use case — composed of Policy Groups | +| **External Policy Evaluator** | Fifth DCM provider type — external authoritative source supplying policies into DCM | +| **concern_type** | Policy Group classification: technology, compliance, sovereignty, business, operational, security | +| **minimal profile** | Least restrictive built-in profile — advisory enforcement, auto-tenant, home lab / evaluation | +| **sovereign profile** | Most restrictive built-in profile — hard tenancy, deny_all cross-tenant, maximum sovereignty | +| **Lifecycle Constraint Enforcer** | DCM control plane component monitoring realized entities against time constraints | +| **cross_tenant_authorization** | Explicit authorization record for cross-tenant relationship — specifies who, what, when, where | +| **explicit_only** | Default cross-tenant hard tenancy setting — ALL cross-tenant requires explicit authorization | +| **PENDING_REVIEW** | Entity state during paused rehydration tenancy conflict — awaiting resolution | +| **Policy Gap Record** | Audit record for fields absent from reserve query with no applicable policy — records implicit_approval | +| **Shared Resource** | An entity within a single Tenant with active relationships from multiple parent entities; governed by sharing_model and reference counting | +| **sharing_model** | Entity-level declaration of shareability, active_relationship_count, and on_last_relationship_released behavior | +| **active_relationship_count** | DCM-maintained count of active constituent/operational relationships on a shared resource | +| **save_overrides_destroy** | The lifecycle action hierarchy rule: retain > notify > suspend > detach > cascade > destroy; most conservative action always wins | +| **lifecycle_conflict_record** | Audit record created when multiple lifecycle action recommendations differ; carries severity, resolved action, and resolution rule | +| **deferred_destruction_record** | Audit record created when a destructive lifecycle action is deferred because active_relationship_count is above minimum | +| **Ingestion Model** | Unified DCM mechanism for bringing entities outside lifecycle control into DCM governance — covers V1 migration, brownfield discovery, and manual import | +| **Ingestion Record** | Provenance record on every ingested entity — source, confidence, assignment method, enrichment history, promotion timestamp | +| **`__transitional__` Tenant** | System-managed holding Tenant for unassigned ingested entities — cannot be deleted, renamed, or used for new provisioning | +| **Ingestion Confidence** | `high | medium | low` — quality signal for auto-assignment; reflects how reliable the Tenant assignment is | +| **Brownfield** | Existing infrastructure not yet under DCM lifecycle management — brought in via brownfield ingestion | +| **V1 Migration** | Migration of pre-Tenant DCM V1 entities to V2 using the ingestion model | +| **INGESTED state** | First ingestion lifecycle state — entity in DCM, minimal metadata, Tenant may be __transitional__ | +| **ENRICHING state** | Second ingestion lifecycle state — Tenant assigned, metadata and relationships being completed | +| **PROMOTED state** | Final ingestion lifecycle state — all requirements met, DCM assumes full lifecycle ownership | +| **DCM Event Type** | A versioned, typed event that DCM can emit — follows universal versioning model | +| **Event Type Registry** | DCM-maintained registry of standard event types — extensible like the Resource Type Registry | +| **Webhook Registration** | Declaration by a consumer, provider, or external system of which DCM events they want to receive and where | +| **Discussion Topics** | Living document (DISCUSSION-TOPICS.md) capturing unresolved design decisions and topics requiring further discussion | +| **Override Preference** | Level 2 simple override declaration — single `override: allow|constrained|immutable` on a field | +| **Override Matrix** | Level 3 per-actor permission matrix for fields requiring nuanced governance | +| **Field Override Control** | Graduated mechanism (Levels 1-3) governing who can change what field, under what conditions | +| **Structural Layer Rules** | Non-configurable rules enforced by the Request Payload Processor — layer immutability, precedence order, chain integrity | +| **Business Override Rules** | Configurable override control rules enforced by the Policy Engine via override metadata | +| **Trusted Grant** | Explicit expansion of override permissions issued by a higher-authority actor to a specific entity UUID | +| **Actor Registry** | Extensible registry of override actors — built-in (policy.global, consumer_request, sre_override, etc.) plus custom actors | +| **Basis for Value** | Field metadata documenting why a particular value was set | +| **Baseline Value** | Field metadata recording the original default value before any override was applied | +| **Entity Relationship** | Universal bidirectional relationship between any two entities — internal or external | +| **Entity Relationship Graph** | Complete traversable graph of all entity relationships in DCM | +| **Relationship UUID** | UUID identifying a specific relationship — same on both sides of the bidirectional record | +| **Relationship Type** | Fixed vocabulary describing the nature of a relationship (requires, depends_on, contains, references, peer, manages) | +| **Relationship Role** | Semantic label describing the function a related entity serves (compute, storage, networking, business_unit, etc.) | +| **Relationship Nature** | Structural character of a relationship — constituent, operational, or informational | +| **Lifecycle Policy** | Declares what happens to an entity when its related entity changes state | +| **Bundled Declaration Expansion** | Processor mechanism expanding bundled fields (e.g., storage in VM request) into first-class entities and relationships | +| **Information Provider** | DCM provider type serving authoritative external data DCM references but does not own | +| **External Entity Reference** | Stable pointer record DCM uses to reference data in an external system | +| **Standard Data** | DCM-defined fields on an information type — used for lookups and operational decisions | +| **Extended Data** | Organization-defined fields added to an information type — carried in payload but not used for DCM core operations | +| **Information Type** | Registry entry for a category of external data (Business.BusinessUnit, Identity.Person, etc.) | +| **Stable External Key** | The external system's UUID used as the primary lookup anchor for an external entity reference | +| **Trust But Verify** | DCM's approach to external references — trusts external data is correct, verifies references remain valid | +| **DCM Operator Interface Specification** | The formal technical contract defining how Kubernetes operators integrate with DCM as Service Providers | +| **DCM Operator SDK** | Go library implementing the Operator Interface Specification — enables Level 1 conformance in one day | +| **Conformance Level** | The level of DCM integration an operator implements — Level 1 (basic), Level 2 (standard), Level 3 (full) | +| **Naturalization (Kubernetes)** | Translating DCM Requested State into a Kubernetes CR | +| **Denaturalization (Kubernetes)** | Translating Kubernetes CR status back into DCM Realized State format | +| **Unsanctioned Change** | A change to a DCM-managed CR that did not originate from a DCM request — detected via missing DCM request annotation | +| **Operator Adapter** | A component implementing the DCM Service Provider API on behalf of an operator that cannot be modified directly | +| **CNCF Sandbox** | The initial CNCF maturity level — target for initial DCM project submission | +| **Conformance Test Suite** | The test suite that validates an operator's implementation against the DCM Operator Interface Specification | +| **Intent State** | The immutable record of a consumer's original declaration — captured before any assembly or policy evaluation | +| **Requested State** | The fully assembled, policy-processed, provider-ready payload — the authoritative record of what DCM instructed a provider to build | +| **Realized State** | The provider-confirmed record of what was actually built — append-only event stream keyed by entity UUID | +| **Discovered State** | What DCM observes actually existing through active discovery — ground truth for drift detection | +| **Data Store Contract** | The interface specification through which DCM defines persistence requirements — implemented by PostgreSQL | +| **GitOps Store** | PostgreSQL store contract for Intent and Requested State — branch, PR, merge, CI/CD hook semantics | +| **Event Stream Store** | PostgreSQL store contract for Realized and Discovered State — append-only, entity-keyed, replayable | +| **Search Index** | Queryable projection of GitOps stores — explicitly non-authoritative, rebuilt from Git on demand | +| **Provider-Portable Rehydration** | Rehydration with provider selection re-evaluated through current placement policies | +| **Faithful Rehydration** | Rehydration honoring the original provider selection from the source record | +| **Pinned Policy Version** | Rehydration using policies as of a specific historical timestamp — requires elevated authorization | +| **Audit Component** | Separate DCM component aggregating provenance events from all stores — compliance-grade, long-retention | +| **Observability Store** | Time-series metrics, traces, and logs — operational, not compliance-grade | +| **Third Rail** | Direct API ingress path — bypasses PR workflow, never bypasses governance | +| **Layer Domain** | Organizational and architectural home of a layer — system, platform, tenant, service, provider | +| **Layer Handle** | Human-readable stable identifier for a layer — format: domain/layer_type/name | +| **Priority Schema** | Hierarchical dotted-notation priority system for deterministic layer conflict resolution | +| **Priority Value** | Numeric dotted-notation priority — higher value wins; no ceiling, infinitely insertable in both directions | +| **Immutable Ceiling** | `immutable_ceiling: absolute` — explicit declaration that a field lock cannot be overridden by any future higher-priority policy; the nuclear option for true non-negotiables | +| **Priority Label** | Semantic context for a priority value — human-readable, does not affect ordering | +| **Reference Priority Taxonomy** | DCM's advisory priority category ranges — not enforced, organizations adopt/adapt/ignore | +| **Artifact Metadata** | Universal metadata block on every DCM artifact — identity, ownership, creation, modification history, contact | +| **created_by** | Artifact metadata field — the audit record of who physically submitted the artifact | +| **owned_by** | Artifact metadata field — the accountability record of who is responsible and receives notifications | +| **created_via** | Artifact metadata field — ingestion path: pr, api, migration, system | +| **Proposed Shadow Execution** | Policy artifact in proposed status executing against real traffic — output captured, never applied | +| **Proposed Evaluation Record** | Shadow output record for a proposed policy — what it would have done on a real request | +| **Validation Dashboard** | Review interface showing aggregate shadow output for proposed policies before activation | + +--- + +## SECTION 56 — COMMUNITY QUESTIONS RESOLVED + +All 21 previously open community/implementation questions are now resolved. Key decisions: + +### Kubernetes Compatibility (5 resolved) +- **Namespace → Tenant mapping:** brownfield ingestion model handles pre-existing namespaces; each namespace maps to one DCM Tenant; resources without ownership go to `__transitional__` Tenant +- **Cluster boundary:** DCM manages across multiple clusters; `Platform.KubernetesCluster` is a resource type DCM provisions, not DCM's own boundary; Tenant is the boundary +- **Admission webhooks vs Policy Engine:** complementary layers — admission webhooks enforce cluster-native policy, DCM Policy Engine enforces DCM request policy; defense in depth, not duplication +- **Kubernetes Information Provider:** separately deployed Information Provider following the unified base contract; no built-in providers in DCM +- **Managed K8s (EKS/GKE/AKS):** managed clusters register as Service Providers of `Platform.ManagedKubernetesCluster`; DCM manages workloads within, not the control plane + +### CNCF Strategy (5 resolved) +- **Submission scope:** Operator Interface Specification as a CNCF specification project first; DCM project submission follows after Level 2 reference implementation +- **Named adopters:** minimum 2 named evaluators + 1 FSI design partner before submission; project team action item +- **TOC sponsor:** target App Delivery TAG and Runtime TAG; SIG engagement surfaces sponsors; project team action item +- **SIG engagement timing:** BEFORE Sandbox submission; SIG App Delivery and SIG Cluster Lifecycle; Cluster API overlap must be addressed pre-submission +- **Level 2 timeline:** scope is now formally defined (dispatch/cancel/discover + realized state + governance matrix + health check); team estimates timeline against defined scope + +### Operator Interface Specification (6 resolved) +- **CNCF submission:** specification project (not sandbox project requiring implementation); SIG engagement first +- **Conformance certification:** self-certified via automated test suite (low friction gate) + optional DCM Verified badge via project review +- **Cluster-scoped resources — two models:** (A) **Cluster as a catalog item (example):** When a Service Provider offers Kubernetes clusters as a resource type, a Tenant that owns a provisioned cluster entity owns all cluster-scoped resources within it — the cluster entity is the ownership boundary; (B) **Shared cluster infrastructure:** cluster-scoped resources governing shared multi-tenant cluster infrastructure belong to `__platform__` Tenant. Note: Cluster-as-a-Service is an example Service Provider implementation, not a DCM architectural feature — DCM treats the cluster as any other resource entity +- **Non-Go frameworks:** spec is language-agnostic; Go SDK is reference implementation; community Java/Python SDKs encouraged; not maintained by DCM project in v1 +- **Cluster API as an example Service Provider:** A CAPI-based operator can register as a Service Provider for a `Platform.KubernetesCluster` resource type — this is an example of what DCM's Provider model enables, not a special architectural feature. DCM has no built-in knowledge of Kubernetes; a CAPI Service Provider is structurally identical to any other Service Provider. Once provisioned, the cluster entity can optionally register as a nested Service Provider for workload resources (the Meta Provider pattern — composing compute + network + storage + DNS + credentials) +- **Level 0:** exists — label-based passive discovery, no operator code changes; DCM discovers and tracks but does not control; lowest adoption friction + +### Operator SDK (5 resolved) +- **Language-agnostic adapter:** not needed — spec is language-agnostic; Go SDK is reference only +- **DCM unavailability:** local durable queue (SQLite); replay on reconnect; DEGRADED mode on overflow with QUEUE_OVERFLOW audit + alert; never drop silently +- **Dynamic field resolution:** Information Provider reference in field mapping; DCM resolves during layer assembly; keeps logic in Policy Engine with full provenance +- **Testing framework:** mock DCM test harness ships as first-class SDK component; configurable failure/delay injection; required for Level 2 conformance +- **Prometheus metrics:** mandatory; 6 standard metrics (registration_status, event_delivery_total, event_delivery_duration, queue_depth, dispatch_duration, discovery_cycle_duration); required for Level 2 conformance + +**Zero remaining unresolved architectural questions.** Remaining open items are project team action items (named adopters, TOC sponsor, KubeVirt timeline). + +--- + +## SECTION 57 — PREVIOUSLY OPEN QUESTIONS (NOW CLOSED) + +These items are explicitly unresolved. Do not make assumptions about them — flag them and ask for guidance. + +| # | Question | Area | +|---|----------|------| +| 1 | Where should data caches live? (Hub DCM, Regional DCM, Sovereign DCM, all?) | Data Model | +| 2 | Should cache synchronization be push, pull, or both? | Data Model | +| 3 | Which cache is authoritative when caches diverge? | Data Model | +| 4 | What mechanism maintains consistency across distributed caches? | Data Model | +| 5 | Should the data model allow embedded target-technology-specific data bundles? | Data Model | +| 6 | How are the four states represented physically? | Data Model | +| 7 | Performance impact of field-level provenance at scale — optimization strategies? | Data Model | +| 8 | Should provenance metadata be inline or in a linked provenance document? | Data Model | +| 9 | What is the governance model for proposing new Resource Types to the registry? | Catalog | +| 10 | Should the registry support a formal review/approval workflow? | Catalog | +| 11 | What is the minimum sunset period for deprecated definitions? | Catalog | +| 12 | Should version constraints in requests be strictly enforced or advisory? | Catalog | +| 13 | How are conflicts resolved when multiple providers satisfy all narrowing criteria equally? | Catalog | +| 14 | Should the registry be distributed or centralized? Sovereignty implications? | Catalog | +| 15 | Trust validation mechanism for provider certification | Providers | +| 16 | Audit vs. Observability — are these truly separate components? | Control Plane | +| 17 | Message Bus — should it be exposed as consumer ingress or egress only? | Control Plane | +| 18 | GateKeeper vs. Validation policy distinction — needs better examples | Policy Engine | +| 19 | How are conflicting Service Layers at the same precedence level resolved? | Data Layers | ✅ Resolved — priority schema + ingestion conflict detection | +| 20 | Should Core Layers be ordered within their precedence level? | Data Layers | ✅ Resolved — priority schema provides deterministic ordering | +| 21 | Can a consumer explicitly exclude a layer from their request? | Data Layers | +| 22 | How are Service Layers registered and versioned relative to their Service Provider registration version? | Data Layers | +| 23 | Should assembly support conditional layer inclusion — a layer only applied if a specific field value is present? | Data Layers | +| 24 | How does the layer chain interact with service dependencies — does each dependent service get its own chain? | Data Layers | +| 25 | For Hybrid Transfer — what is the maximum number of ownership transfers allowed? | Entities | +| 26 | For Whole Allocation of bare metal — how is indivisibility enforced at the provider level? | Entities | +| 27 | Should capacity confidence ratings trigger automatic actions (e.g., LOW triggers Mode 1 query)? | Entities | +| 28 | For Process Resources — should there be a maximum execution time before DCM escalates? | Entities | +| 29 | How does SUSPENDED state interact with cost analysis — is a suspended Entity still billable? | Entities | +| 30 | How are dependency graphs versioned relative to catalog item versions? | Dependencies | +| 31 | Should the dependency graph be stored as a separate entity or embedded in the request payload? | Dependencies | +| 32 | How are cross-tenant dependencies handled? | Dependencies | ✅ Resolved — governed by REL-010/011/012; DEP-001/002/003 for dependency graph specifics; explicit_only default; cross_tenant_authorization required | +| 33 | Should there be a maximum dependency graph depth? | Dependencies | +| 34 | How does the dependency graph interact with the Meta Provider model? | Dependencies | +| 35 | Should DCM maintain a registry of well-known custom group types? | Grouping | +| 36 | How does group membership interact with sovereignty — can a group span sovereignty boundaries? | Grouping | +| 37 | When a Tenant is decommissioned, what happens to its resources and group memberships? | Grouping | +| 38 | Should Resource Groups support time-bounded membership? | Grouping | +| 39 | How are group-level policies inherited by nested child groups — opt-in or opt-out? | Grouping | +| 40 | Webhook registration model — Consumer API, Provider Registration, or dedicated Webhook API? | Webhooks | +| 41 | Full DCM event taxonomy and whether it should be a versioned registry | Webhooks | +| 42 | Webhook payload format — full state payload vs reference + event type | Webhooks | +| 43 | Webhook authentication model for outbound calls | Webhooks | +| 44 | Webhook retry and reliability obligations | Webhooks | +| 45 | Webhook ordering guarantees | Webhooks | +| 46 | Relationship between webhooks and the Message Bus | Webhooks | +| 47 | Should provider webhook support be mandatory in the Provider Contract? | Webhooks | +| 48 | Tenant vs platform-level webhook scoping | Webhooks | +| 49 | Should webhook registrations declare which payload schema version they expect? | Webhooks | +| 50 | Should override_preference be declarable in layer definitions as a hint to the Policy Engine? | Override Control | +| 51 | When immutable is set by a Global policy, can a higher-priority Global policy still override it? | Override Control | ✅ Resolved — execution order makes default immutable effectively absolute; immutable_ceiling: absolute provides explicit forward-looking protection | +| 52 | Should constraint_schema on a constrained field be visible to consumers in the Service Catalog UI? | Override Control | +| 53 | Enhancement gaps: storage/networking bundling vs. dependency model — V1 simplification or new concept needed? | Enhancements | +| 54 | Enhancement gaps: selected_provider as policy output vs. placement component concern | Enhancements | ✅ Resolved — Placement Engine is a distinct named component; nine-step assembly; reserve query; placement loop with policy phases; policy_gap_record for implicit approval; post-placement policy pass | +| 55 | Enhancement gaps: migration path from V1 (no Tenant) to Tenant-mandatory | Enhancements | ✅ Resolved — unified ingestion model; __transitional__ Tenant; three-step ingest/enrich/promote; ING-001 through ING-007; also covers brownfield ingestion | +| 56 | Enhancement gaps: should editable field concept from Catalog Item Schema be incorporated into Resource Type Spec? | Enhancements | +| 57 | How are relationship conflicts resolved — two policies declare different lifecycle policies for the same relationship? | Entity Relationships | ✅ Resolved — standard Policy Engine authority hierarchy; lifecycle policy fields are just fields; no special case; REL-008 and REL-009 | +| 58 | Should relationship roles be validated against the role registry at request time, or is validation advisory? | Entity Relationships | +| 59 | How does the relationship graph interact with multi-tenant scenarios — can a relationship cross Tenant boundaries? | Entity Relationships | ✅ Resolved — nature governs; constituent never; operational with dual auth; informational unless deny_all; REL-010/011/012; allocated resource model | +| 60 | Should there be a maximum relationship graph depth? | Entity Relationships | +| 61 | How are shared entities represented — an entity required by multiple parents? | Entity Relationships | ✅ Resolved — sharing_model with active_relationship_count; save_overrides_destroy hierarchy; lifecycle_conflict_record; REL-015 through REL-019 | +| 62 | How are conflicting Information Provider push events handled — two providers claim authority for the same record? | Information Providers | +| 63 | Should Information Providers support write-back — DCM updating external records via the provider? | Information Providers | +| 64 | How is the extended schema versioned when a provider adds or removes extended fields? | Information Providers | +| 65 | Should DCM maintain a registry of well-known Information Providers to simplify onboarding? | Information Providers | +| 66 | How does Information Provider verification interact with air-gapped environments? | Information Providers | +| 67 | Should CNCF submission be for DCM as a whole or for the Operator Interface Specification as a standalone standard? | CNCF Strategy | +| 68 | Which FSI consortium members will be named as public adopters in the CNCF submission? | CNCF Strategy | +| 69 | How does the Namespace-to-Tenant mapping work for clusters with pre-existing namespaces? | Kubernetes Compatibility | +| 70 | How does DCM interact with Kubernetes admission webhooks — duplicate or complement Policy Engine? | Kubernetes Compatibility | +| 71 | Should the Kubernetes Information Provider be a built-in DCM component or separately deployed? | Kubernetes Compatibility | +| 72 | How does DCM interact with managed Kubernetes services (EKS, GKE, AKS) where cluster management is outside user control? | Kubernetes Compatibility | +| 73 | Should the SDK support non-Go operator frameworks via a language-agnostic REST adapter? | SDK Design | +| 74 | How should the SDK handle DCM endpoint unavailability — queue events locally or drop? | SDK Reliability | +| 75 | Should the entity UUID be preserved or regenerated on rehydration? | Four States | +| 76 | For pinned policy version rehydration — what is the minimum authorization level? | Four States | +| 77 | How are concurrent rehydration requests for the same entity handled? | Four States | +| 78 | Should the Discovered Store retain full history or only a configurable window? | Four States | +| 79 | Git repository structure for Intent and Requested stores — deferred pending Q54 | Storage | +| 80 | Should data stores support multi-region replication as a declared capability? | Storage | +| 81 | How are store failures handled — failover, queuing, or rejection? | Storage | +| 82 | Should the Search Index be a separate store contract or bundled with the primary store? | Storage | +| 83 | Should the Audit Store be a specialized store contract or a general event stream? | Audit | +| 84 | Should DCM provide a default observability dashboard or only the telemetry? | Observability | +| 85 | Should the background conflict validation job run on schedule or be event-triggered? | Data Layers | +| 86 | What is the minimum validation review period for a proposed policy before activation? | Policy Engine | +| 87 | Should the proposed shadow evaluation record be stored in the Audit Store or a separate validation store? | Storage | +| 88 | Should organizations be able to define their own artifact status extensions beyond the five standard statuses? | Artifact Metadata | + +--- + +## SECTION 58 — EXAMPLES AND USE CASES (dcm-examples.md) + +### Orchestration Examples (8 scenarios) + +**1.1 Basic request lifecycle** — submit → layers_assembled (GateKeeper + Transformation fire) → placement (6-step) → dispatch → realized. Shows named workflow + dynamic policies composing on same events. + +**1.2 Human approval gate** — GateKeeper with `requires_approval: true` flag inserts AWAITING_APPROVAL step without modifying named workflow. Manager approves via API → pipeline resumes. + +**1.3 Policy-gated hard block** — GateKeeper denies unsupported OS. Consumer receives clear error with policy_uuid and suggestion. No requires_approval flag → terminal FAILED. + +**1.4 Compound service (Meta Provider)** — VM + IP + DNS + LoadBalancer. Dependency-ordered execution (parallel where no deps). DNS fails (partial delivery) → DEGRADED state. Recovery: NOTIFY_AND_WAIT. Consumer chooses: accept degraded or trigger DNS retry. + +**1.5 Drift detection + remediation** — Discovery finds memory_gb changed (unsanctioned). Drift: significant + unsanctioned → critical. Policy: ESCALATE. Consumer submits REVERT → new request cycle → next discovery clean. + +**1.6 Dispatch timeout + late response** — Provider silent for PT30M → TIMEOUT_PENDING → Recovery: NOTIFY_AND_WAIT (prod profile). Provider responds at T+45M → LATE_RESPONSE_RECEIVED. Consumer chooses DISCARD_AND_REQUEUE. + +**1.7 Federation-routed request** — Local providers at capacity. Placement queries Hub DCM (Peer DCM provider). Hub routes to Regional DCM B. Governance Matrix checked at each hop. Realized State flows back chain. entity_uuid preserved. + +**1.8 Brownfield ingestion** — Discovery finds unmanaged VM. Orchestration Flow Policy: discover → INGEST → ENRICH (CMDB query) → await operator → PROMOTE to tenant. Drift detection activated post-promotion. + +### Provider Examples (4 scenarios) + +**2.1 Service Provider dispatch cycle** — Full payload showing DCM unified format → naturalize to OpenStack Nova → execute → denaturalize back. Shows provenance on injected fields (monitoring_endpoint from policy). + +**2.2 Information Provider enrichment** — CMDB query during layer assembly. Response with confidence descriptor. Fields injected with source_type: information_provider and source_uuid. + +**2.3 External Policy Evaluator Mode 3 (OPA sidecar)** — Exact OPA HTTP API call format, input document structure, response parsing. + +**2.4 notification service delivery** — VLAN decommission event. Audience: owner (NetworkOps) + 2 stakeholders (required stakes) + 1 observer (optional stake). Per-actor envelopes with stakeholder_reason field. Slack message format. + +### Consumer API Examples (2 scenarios) + +**3.1 Complete request lifecycle** — catalog browse → describe (see constraints) → submit → poll status sequence → get realized resource with confidence scores. + +**3.2 Provider update approval** — Provider submits auto-scale notification → REQUIRES_CONSUMER_APPROVAL → consumer reviews pending notifications → approve → new Realized State. + +### Admin API Examples (2 scenarios) + +**4.1 Provider registration review** — List pending registrations (with validation results) → approve with review notes. + +**4.2 Orphan resolution** — List orphan candidates → investigate → adopt_into_dcm → entity promoted to full lifecycle. + +### Registration Flow Example (1 scenario) + +**5.1 Complete provider onboarding** — Admin issues registration token → provider submits registration payload (mTLS + token) → 8 automated validation checks shown → PENDING_APPROVAL → admin reviews → ACTIVE. Full capability declaration structure for Service Provider. + +--- + +## SECTION 59 — CAPABILITIES MATRIX (167 capabilities, 26 domains) + +The DCM Capabilities Matrix contains 167 capabilities across 26 domains. Each capability row specifies what consumers, service providers, and platform admins can do, along with dependencies. + +**Current domain count: 26** +IAM, CAT, REQ, PRV, LCM, DRF, POL, LAY, INF, ING, AUD, OBS, STO, FED, GOV, ACC, ZTS, GMX, DRC, FCM, SMX, MPX, CPX, DPO, ATM, EVT, VER (26 domain prefixes; see taxonomy for full names) + +**Recent additions (docs 29–34):** +- SMX (Scoring Model, doc 29): risk scoring, approval routing, signal weights, governance matrix +- MPX (Meta Provider, doc 30): compound service definition, constituent orchestration via dependency graph +- CPX (credential management service, doc 31): credential lifecycle, rotation, revocation, profile-governed security +- ATM (Authority Tier, doc 32): dynamic ordered tier list, custom tiers, degradation gate, impact detection +- EVT (Event Catalog, doc 33): 82 event types, base envelope, payload schemas, EVT-001–007 +- VER (API Versioning, doc 34): breaking change definition, deprecation lifecycle, version discovery + +**SMX-008 hard constraint:** auto_approve_below ≤ 50 in ALL profiles +**ATM-002 hard constraint:** auto tier max_score ≤ 50 in ALL profiles +**CPX-001 absolute:** credential values NEVER in DCM stores in ANY profile +**EVT-007:** audit.* critical events are non-suppressable + + +## SECTION 60 — DOCUMENTATION STRUCTURE + +DCM documentation follows a hierarchical structure: + +``` +dcm-docs/ (internal working docs) +├── README.md +├── DCM-AI-PROMPT.md # This file +├── DISCUSSION-TOPICS.md +├── data-model/ # → website: /docs/architecture/data-model/ +│ ├── 00-context-and-purpose.md ✅ +│ ├── 02-four-states.md ✅ +│ ├── 03-layering-and-versioning.md ✅ +│ ├── 05-resource-type-hierarchy.md ✅ +│ ├── 06-resource-service-entities.md ✅ +│ ├── 07-service-dependencies.md ✅ +│ ├── 08-resource-grouping.md ✅ +│ ├── 09-entity-relationships.md ✅ +│ ├── 10-information-providers.md ✅ +│ ├── 11-storage-providers.md ✅ +│ └── 12-audit-provenance-observability.md ✅ +└── specifications/ # → website: /docs/architecture/specifications/ + ├── dcm-operator-interface-spec.md ✅ + ├── 11-kubernetes-compatibility.md ✅ + ├── dcm-operator-sdk-api.md ✅ + └── cncf-strategy.md ✅ + +Website structure (Hugo / Hextra): +content/ +├── _index.md # Homepage — 4 bottom cards +└── docs/ + ├── _index.md # Docs index + ├── architecture/ + │ ├── _index.md # Architecture section — 3 cards + │ ├── overview.md # High Level Design ✅ + │ ├── data-model/ + │ │ ├── _index.md # Data Model section — 11 cards + │ │ └── (11 data model docs) ✅ + │ └── specifications/ + │ ├── _index.md # Specifications section — 4 cards + │ └── (4 specification docs) ✅ + └── enhancements/ + ├── _index.md + └── (existing enhancement stubs — unchanged) +``` + +--- + +## SECTION 61 — SCORING MODEL (doc 29) + +### Governing Principle +Questions of fact use boolean gates. Questions of degree use scoring. Secondary test: "Can a regulator accept 'the score was below threshold' as a complete explanation?" If not — boolean. + +### GateKeeper enforcement_class (required field) +- `compliance` — boolean deny gate. Default and fail-safe if omitted. Used for: regulatory requirements (PHI→BAA, sovereign data), security hard requirements, anything where score-around creates legal liability. +- `operational` — contributes `risk_score_contribution` (weight 1–100) to request risk score. Used for: cost ceilings, size limits, quota pressure, off-hours context, business rule preferences. + +### Validation output_class (required field) +- `structural` — boolean pass/fail. Default and fail-safe. Missing required fields, type errors, broken references. +- `advisory` — completeness score contribution + warning list. Never blocks. Recommended fields absent, unusual values, low confidence. + +### The Five Scoring Signals (aggregate → request_risk_score 0–100) +1. **Operational GateKeeper score** (weight: 0.45 standard) — sum of risk_score_contribution from all fired operational GateKeepers, capped at 100 +2. **Completeness score** (weight: 0.15) — sum of advisory Validation contributions +3. **Actor risk history score** (weight: 0.20) — decay-weighted (λ=0.1, half-life 7 days) history of actor's previous request outcomes; events: validation_failure(5), gatekeeper_deny(10), compliance_deny(20), policy_override(8), drift_caused(15), forced_decommission(12) +4. **Quota pressure score** (weight: 0.10) — zero below 75% utilization; max(0, (util - 0.75) / 0.25) × 100 above +5. **Provider accreditation richness** (weight: 0.10, inverse) — weighted portfolio sum; higher richness = lower provider risk contribution + +### Profile-Governed Thresholds → Approval Routing +auto_approve (threshold) +Default per profile: minimal(<45), dev(<40), standard(<25), prod(<15), fsi(<10), sovereign(<5) +SMX-008 applies to ALL profiles including minimal — auto_approve_below may never exceed 50 +**SMX-008: auto_approve_below may never exceed 50 in any profile, including minimal.** minimal achieves higher effective auto-approval through lower signal weights, not higher thresholds. +Signal weights must sum to 1.00 (validated at profile activation). + +### Profile Enforcement Class Overrides +Profiles can promote operational→compliance or demote compliance→operational (non-regulatory only). +SMX-003: policies with `regulatory_mandate: true` cannot be demoted. Set by platform admins. Audited. +Threshold and override changes take effect immediately. Score Records are immutable — no retroactive changes. + +### Pipeline Sequence (doc 29, Section 8) +1. Evaluate all policies +2. Compliance GateKeeper fires → HALT (boolean deny, no score) +3. Structural Validation fails → HALT (boolean fail, no score) +4. Governance Matrix DENY → HALT (always boolean, never scored — SMX-004) +5. Collect operational GateKeeper contributions → Signal 1 +6. Collect advisory Validation contributions → Signal 2 +7. Fetch actor risk history → Signal 3 +8. Calculate quota pressure → Signal 4 +9. Calculate provider accreditation richness → Signal 5 +10. Aggregate with profile weights → request_risk_score +11. Apply profile thresholds → routing_decision +12. Write Score Record to Audit Store (SMX-010: required for every scored request) +13. Route: auto_approve | queue_for_review | queue_dual | queue_committee + +### What Is NEVER Scored +Governance Matrix (SMX-004) · authentication · authorization · five-check boundary enforcement · lifecycle state transitions · unsanctioned change flag · TTL expiry · request status states + +### Score Exposure +Consumer: risk_score, routing_decision, score_drivers (top 3, human-readable), advisory_warnings +Platform admin: full Score Record — all signal breakdowns, weights, actor risk history detail +Actor risk history never exposed to other consumers (privacy — SMX-007) + +### Score Record (immutable, Audit Store) +score_record_uuid, request_uuid, entity_uuid, request_risk_score, routing_decision, routing_threshold_applied, profile_uuid, signal_breakdown (per signal: score, weight, weighted_contribution, fired_policies) + +### New API Endpoints +Consumer: risk_score + advisory_warnings on POST /api/v1/requests response and GET status +Admin: GET/PATCH /api/v1/admin/profiles/{name}/scoring · POST overrides · GET/POST /actors/{uuid}/risk-history · GET /scoring/audit +Flow GUI: GET /flow/api/v1/graph/scoring-overlay · POST /flow/api/v1/simulate/score · Threshold slider in Profile Management view · Score breakdown panel in Simulation + +### SMX-001–010 System Policies +SMX-001: GateKeeper must declare enforcement_class (compliance default). SMX-002: Validation must declare output_class (structural default). SMX-003: regulatory_mandate:true = no profile demotion. SMX-004: Governance Matrix always boolean. SMX-005: signal weights must sum to 1.00. SMX-006: Score Records immutable. SMX-007: actor risk history not exposed to other consumers. SMX-008: auto_approve_below ≤ 50. SMX-009: scoring_weight 1–100; aggregate capped at 100 before weighting. SMX-010: Score Record required for every scored request. + +### Capabilities +SMX-001 through SMX-008 in Capabilities Matrix Domain 21. Total: 167 capabilities, 26 domains. + +--- + +## SECTION 62 — META PROVIDER COMPOSABILITY MODEL (doc 30) + +### What a compound service definition Is (formerly "Meta Provider") + +> **Architecture update (April 2026):** "Meta Provider" is no longer a separate provider type. The concept is now **Compound Resource Type Specifications** — a Data concept in the Resource Type Registry, orchestrated by the Control Plane. Individual constituents are fulfilled by standard Service Providers. References to "Meta Provider" in earlier sections are historical. The authoritative model is 5 provider types: service_provider, information_provider, auth_provider, peer_dcm, process_provider. +A **compound Service Provider** that uses other providers in the DCM catalog to fulfill a higher-order service. Its primary contribution is a **compound service definition** declaring constituent resource types, dependencies, and delivery requirements so DCM can place, sequence, and govern the constituents. The Meta Provider is NOT an orchestrator — it is a compound service definition plus a standard Service Provider for its own resource types. + +### Key Principle +The Meta Provider declares the dependency graph. DCM executes it. Parallelism emerges from the graph — constituents with no unresolved dependencies dispatch concurrently. The Meta Provider does not manage sequencing, external placement, failure handling, or compensation. + +### provided_by (critical field on each constituent) +- `self` — Meta Provider handles this constituent via standard Services API (naturalize/execute/denaturalize — same as any Service Provider) +- `external` — DCM places with best available provider via Placement Engine (all sovereignty/accreditation/trust checks apply) +- `` — DCM dispatches to specific named provider + +### depends_on → Execution Order +Each constituent declares `depends_on: [component_id, ...]`. DCM reads this graph and dispatches in order — no dependencies first, then those whose dependencies are REALIZED. Parallelism within a round emerges from the graph. Meta Provider does NOT manage this. + +### required_for_delivery +- `required` — failure triggers Recovery Policy; unrealized constituents cancelled +- `partial` — failure noted; compound continues; composite status may be DEGRADED +- `optional` — failure noted; execution continues unaffected + +### Division of Responsibility (authoritative — see doc 30 Section 4) +**DCM:** catalog, layer assembly, policy/scoring, external placement (Placement Engine), dependency-ordered dispatch, failure handling (Recovery Policy), compensation (dependency-reverse decommission), composite Realized State assembly, drift detection, audit, lifecycle. +**Meta Provider:** declares compound service definition; executes `self` constituents as standard Service Provider; implements standard decommission for `self` constituents. + +### Composite Entity Four States +- **Intent:** Compound request stored as-is; no constituent expansion +- **Requested:** DCM expands using compound service definition; Placement Engine resolves `external` providers; constituent blocks have component_id, provided_by, depends_on, required_for_delivery +- **Realized:** DCM assembles from all constituent realized payloads; composite_status; synthesized composite_fields +- **Discovered:** Via Meta Provider endpoint (opaque/selective) or per-constituent providers (transparent) + +### Composite Status — determined by DCM +- `REALIZED` — all required constituents REALIZED +- `DEGRADED` — required REALIZED; partial(s) failed; accepted if profile permits +- `FAILED` — required constituent(s) failed → Recovery Policy → compensation in dependency-reverse order + +### Composition Visibility +- `opaque` — top-level entity only; discovery via Meta Provider endpoint +- `transparent` — all constituents as DCM entities; UUIDs = deterministic(parent_uuid + component_id); per-constituent drift detection +- `selective` — declared sub-set as DCM entities + +### Rehydration +Primary use case for dependency graph declaration. DCM sequences rehydration from `depends_on` graph in same order as provisioning. `external` constituents re-placed by Placement Engine. `self` constituents return to same Meta Provider. + +### Scoring +Operational GateKeepers fire on compound payload (not per-constituent). Signal 5 (accreditation richness) = lowest richness score among required-constituent providers. + +### Nested Meta Providers +Max depth 3 enforced by DCM at placement. Nested Meta Provider has no awareness it is a constituent — receives and responds with standard payloads. + +### MPX-001–MPX-008 System Policies +MPX-001: self constituents use standard Services API. MPX-002: DCM derives ordering from depends_on. MPX-003: parallelism from graph, not Meta Provider. MPX-004: composite status determined by DCM. MPX-005: Recovery Policy governs failure/compensation. MPX-006: external placement by Placement Engine only. MPX-007: transparent UUIDs are deterministic. MPX-008: max nesting depth 3 enforced at placement. + +### Capabilities: MPX-001–MPX-007 (Domain 22 — 141 total across 23 domains) + +## SECTION 63 — CREDENTIAL PROVIDER MODEL (doc 31) + +### Two Credential Categories +1. **DCM Interaction Credentials** — short-lived (PT15M–PT1H profile-governed), scoped to specific operation+entity+provider. Issued before every provider dispatch. Implements ZTS-002. Never stored beyond use. +2. **Consumer-Facing Resource Credentials** — SSH keys, API keys, kubeconfigs, service account tokens, database passwords, x509 certificates. Issued as part of resource realization; delivered via Consumer API. + +### CPX-001 (most important): Values NEVER in DCM stores +Credential values are never written to GitOps stores, Realized State Store, or Audit Store. DCM stores only metadata (UUID, type, scope, expiry, status). Values held by credential management service; retrieved via authenticated `value_retrieval_endpoint`. + +### Credential Record Fields +credential_uuid, credential_type, status (active/rotating/revoked/expired), issued_at, valid_until, issued_to (actor/entity/component/provider UUID), scope (operations[], resource_types[], tenant_uuid), non_transferable:true, bound_to_ip (fsi/sovereign), value_retrieval_endpoint, value_retrieval_auth, rotation_of (parent UUID if rotation), service_provider_uuid, entity_uuid + +### Issuance Flows +- **Resource credential:** after VM/resource realized → DCM sub-request to credential management service → metadata stored in Realized State → consumer retrieves value via authenticated endpoint +- **Interaction credential:** before each provider dispatch → credential management service issues scoped cred → included in dispatch → expires PT15M regardless; new cred issued for each interaction +- **Bootstrap:** special mechanism before credential management service is registered; see doc 17 + +### Rotation Protocol +Trigger types: pre_expiry (default), scheduled, security_event, actor_request, provider_initiated. Standard flow: issue new cred → transition window (both valid) → revoke old at window end → notify consumer. Window: P1D consumer creds; PT5M dcm_interaction; P7D x509. Emergency rotation (security_event): NO transition window — old revoked immediately; fastest-channel delivery of new. + +### Revocation Model +Revocation Triggers: actor_deprovisioned, entity_decommissioned, security_event, provider_deregistered, actor_request, ttl_expired. Propagation: credential record → status:revoked → publish credential.revoked to Message Bus → all components refresh revocation cache within SLA (PT5M standard; PT1M fsi/sovereign) → credential management service invalidates stored value within SLA. + +### Use-Time Validation (CPX-002 enforcement) +Providers must validate at use time (not just receipt): check revocation cache, verify valid_until, verify operation within scope, verify IP binding. Reject with 403 if any check fails. Cache refresh: ≤ PT1M standard; ≤ PT30S fsi/sovereign. + +### CPX-006: Actor Deprovisioning +Triggers immediate revocation of ALL credentials issued to actor. Revocation events published to Message Bus BEFORE deprovisioning acknowledged. +CPX-007: Entity decommissioning triggers revocation of all entity-scoped credentials before decommission confirmed. Decommission that cannot revoke enters COMPENSATION_IN_PROGRESS. + +### Consumer API Endpoints +GET /api/v1/resources/{entity_uuid}/credentials — list credential metadata +GET /api/v1/credentials/{uuid}/value — retrieve value (step_up_mfa if required); every retrieval audited with retrieval_uuid +POST /api/v1/credentials/{uuid}/rotate — request rotation; returns old/new UUIDs + transition_window_ends + +### credential management service API Contract +POST {issue_endpoint} · POST {rotate_endpoint} · DELETE {revoke_endpoint}/{uuid} · POST {validate_endpoint} (use-time check) · GET {list_endpoint}?entity_uuid= + +### Profile-Governed Credential Configuration (doc 31 Section 12) +credential_profile block controls: permitted_credential_types (homelab: api_key/x509/ssh; sovereign: hsm_backed_key only) · max_lifetime per credential type per profile · scheduled_rotation_required (ALL profiles: true; minimal/dev allow manual trigger and P365D/P180D max intervals) · min_transition_window (minimal: PT0S; standard+: P1D) · value_retrieval_auth_required (minimal: bearer_token; prod: step_up_mfa; sovereign: mtls) · audit_every_retrieval (minimal: false; standard+: true) · idle_detection_threshold (minimal: P30D; dev: P14D; standard: P7D; prod: P3D; fsi: P1D; sovereign: PT12H — NEVER null) · ip_binding_required (minimal-prod: false; fsi/sovereign: true) · fips_140_level_required (minimal: 0; fsi: Level 2; sovereign: Level 3) · approved_algorithms (minimal: forbidden_algorithms list [MD5,SHA-1,DES,3DES,RSA<2048]; standard: Ed25519/ECDSA-P-384; fsi: FIPS-only; sovereign: HSM-generated only) · revocation_check_frequency (minimal: PT5M; fsi: PT30S; sovereign: PT15S) · revocation_sla (minimal: PT10M; sovereign: PT30S) + +### Compliance Domain Overlays (additive, never relaxing) +hipaa: audit_every_retrieval:true, idle_detection:P7D, max rotation api_key:P90D +pci_dss: max_rotation_interval:P90D (mandatory — req 8.3.9), min_password_complexity:12+4-classes +fedramp_moderate: fips_level:1 · fedramp_high: fips_level:2, ip_binding:true · dod_il4: fips:2, ip_binding:true + +### AAL Mapping (NIST 800-63B) +minimal/dev=AAL1 · standard=AAL2 (MFA for sensitive types) · prod=AAL2 (MFA all) · fsi=AAL2+ (hardware MFA, FIPS L2) · sovereign=AAL3 (hardware-bound, FIPS L3, tamper evidence) + +### New Fields on Credential Record +algorithm (Ed25519/ECDSA-P-384/RSA-4096/HS256/etc.) · key_usage [authentication|signing|encryption] · retrieved_count_threshold (hours; idle alert threshold) + +### CPX-001–CPX-012 System Policies +CPX-001: values never in DCM stores. CPX-002: every provider interaction must present scoped credential. CPX-003: revocation propagation within declared SLA. CPX-004: emergency rotation has no transition window. CPX-005: every value retrieval audited. CPX-006: actor deprovisioning revokes all actor credentials. CPX-007: entity decommission blocks on credential revocation. CPX-008: fsi/sovereign credentials must be IP-bound or hardware-attested. + +### Capabilities: CPX-001–CPX-007 (Domain 23 — 148 total across 23 domains) + +--- + +## SECTION 64 — AUTHORITY TIER MODEL (doc 32 — 32-authority-tier-model.md) + +> **Full specification:** [32-authority-tier-model.md](data-model/32-authority-tier-model.md) — ordered tier list, custom tier contribution, dynamic threshold format, impact detection pipeline, ATM-001–ATM-012. + +### Core Model +Authority tiers are a **named, ordered list**. Names are stable references; numeric weight is derived from list position at evaluation time — never hardcoded. Organizations can insert custom tiers between existing ones without breaking any existing name references. + +### Default Tier List (ordered) +``` +auto → reviewed → verified → authorized +``` +Position determines weight: auto=1, reviewed=2, verified=3, authorized=4. +If org inserts `compliance_reviewed` after `verified`: auto=1, reviewed=2, verified=3, compliance_reviewed=4, authorized=5. +All existing references to `authorized` still resolve correctly. + +### decision_gravity (stable severity vocabulary) +- `none` → auto (automated; no human judgment) +- `routine` → reviewed (standard authority; one qualified reviewer) +- `elevated` → verified (elevated authority; separation of duties; two distinct reviewers) +- `critical` → authorized (highest authority weight; DCMGroup + quorum required) + +decision_gravity is stable and position-independent. Custom tiers must declare consistent gravity. + +### Dynamic Threshold Format +Profile thresholds are a named-tier list, NOT fixed column keys: +```yaml +approval_routing: + - { tier: auto, max_score: 24 } # ATM-002: never exceed 50 + - { tier: reviewed, max_score: 59 } + - { tier: verified, max_score: 79 } + - { tier: authorized, max_score: 100 } +``` +Custom tiers insert into this list. Existing tier names and ranges shift only for the affected range. + +### Custom Tiers +Contributed via standard contribution pipeline; require `verified` tier approval (ATM-004). Must declare decision_gravity consistent with position (ATM-003). Cannot alter dcm_gate semantics of existing DCM system tiers (ATM-005). + +### Authorized Tier (dcmgroup_required: true) +Requires a declared DCMGroup and quorum threshold. Organization defines: group composition (CTO, CISO, board, single delegate — any structure), how members deliberate, what external tools they use. DCM enforces that N members of the declared DCMGroup recorded decisions via Admin API. Organization provides everything else. + +### Tier Registry Change Impact Detection (doc 32 Section 7) +When tier registry changes, DCM computes a **tier_impact_diff** — a structured comparison of proposed vs current ordered list — before activation: +- **SECURITY_DEGRADATION**: tier's gravity or position decreased → blocks activation until reviewed and accepted (ATM-009) +- **BROKEN_REFERENCE**: tier name removed but still referenced → blocks activation until resolved (ATM-010) +- **PROFILE_GAP**: new tier inserted but profile threshold list not updated → warning, does not block (ATM-012) +- **SECURITY_UPGRADE / STALE_WEIGHT**: informational, does not block + +Degradation review gate: each SECURITY_DEGRADATION must be accepted via `POST /api/v1/admin/tier-registry/{change_uuid}/accept-degradation` by a `verified` or `authorized` tier reviewer before activation. + +Impact report (ATM-011) stored in Audit Store for every registry change, at proposal and at activation. + +Admin API: POST /api/v1/admin/tier-registry/changes (propose) · GET .../impact (report) · POST .../accept-degradation · POST .../activate + +### ATM-001–ATM-012 System Policies +ATM-001: tiers identified by name; weight derived from position. ATM-002: auto tier max_score ≤ 50. ATM-003: custom gravity consistent with position. ATM-004: custom tiers require verified-tier approval. ATM-005: custom tiers cannot change existing tier dcm_gate semantics. ATM-006: dcmgroup_required tiers must have DCMGroup declared before use. ATM-007: four gravity values are DCM vocabulary (org cannot add gravity values). ATM-008: approval records store weight at creation time for point-in-time audit. + +### Federation Tier Resolution +Peer DCM instances may have different custom tier lists. Resolution strategy: `gravity_match` — match by decision_gravity, not tier name. Unknown peer tiers escalate to their declared gravity level. + +--- + +## SECTION 65 — EVENT CATALOG (doc 33 — 33-event-catalog.md) + +> **Full specification:** [33-event-catalog.md](data-model/33-event-catalog.md) — authoritative source for all 82 DCM event types, payload schemas, urgency levels, EVT-001–EVT-007 system policies. + +### Base Envelope (all events share this) +event_uuid (idempotency key — EVT-002: consumers must treat duplicates as already-processed) · event_type · event_schema_version · timestamp (from Commit Log — authoritative) · dcm_version · dcm_instance_uuid · subject (entity_uuid, entity_type, entity_handle, tenant_uuid, actor_uuid) · urgency (critical/high/medium/low/info) · payload (event-specific) · links (self, audit_record) + +### Event Domains (82 total across 26 domains) +request.* (14): submitted → intent_captured → layers_assembled → policies_evaluated → requires_approval → approved → placement_complete → dispatched → compound_assembled → dependencies_resolved → realized/failed/gatekeeper_rejected/cancelled +entity.* (13): realized, state_changed, modified, ttl_warning, ttl_expired, suspended, resumed, decommissioning, decommissioned, decommission_deferred, ownership_transferred, pending_review, expired +drift.* (4): detected, severity_escalated, resolved, escalated +provider.* (5): registered, deregistered, healthy, unhealthy, degraded +provider_update.* (5): submitted, requires_approval, approved, rejected, auto_approved +rehydration.* (5): started, paused, interrupted, completed, blocked +policy.* (4): activated, deactivated, evaluated, shadow_result +credential.* (4): rotating, revoked, idle, expired +approval.* (4): decision_recorded, quorum_reached, window_expiring, expired +tier_registry.* (4): proposed, impact_assessed, degradation_detected, activated +audit.* (3): chain_integrity_alert, chain_break, forward_failed +dependency.* (2): state_changed + stakeholder.resource_decommissioning +allocation.* (2): pool_capacity_low, released +ingestion.* (3): transitional_created, enriched, promotion_approved +governance.* (3): catalog_item_deprecated, profile_changed, policy_trust_elevated +security.*/sovereignty.*/federation.*/auth.*: unsanctioned_provider_write, sovereignty.violation, sovereignty.migration_required, federation.tunnel_degraded, auth.provider_failover + +### Urgency Levels +critical (push + page if configured) · high (push) · medium (standard) · low (standard) · info (batch/webhook only) +critical + audit.* events: NON-SUPPRESSABLE — EVT-005 and EVT-007 + +### Schema Versioning +event_schema_version increments ONLY on breaking changes (removing fields, changing types/semantics). Adding optional fields is NOT breaking. EVT-004. + +### Non-Standard Events +Providers/extensions may publish non-standard events using reverse-DNS prefix (e.g. com.acme.custom_event). EVT-006. + +### EVT-001–EVT-007 System Policies +EVT-001: all events must include base envelope. EVT-002: event_uuid is idempotency key. EVT-003: timestamp from Commit Log. EVT-004: schema version increments on breaking changes only. EVT-005: critical urgency → push delivery. EVT-006: non-standard events use reverse-DNS prefix. EVT-007: audit.* critical events are non-suppressable. + +--- + +## SECTION 66 — API VERSIONING STRATEGY (doc 34 — 34-api-versioning-strategy.md) + +> **Full specification:** [34-api-versioning-strategy.md](data-model/34-api-versioning-strategy.md) — breaking change definition, deprecation lifecycle, version discovery, sunset behavior, VER-001–VER-009. + +### Versioning Model +URL path versioning: `/api/v1/`, `/api/v2/`, etc. Version is per-API surface (Consumer, Admin, Provider/OIS, Flow GUI) — NOT per-endpoint. All endpoints in a surface share the same major version. Non-breaking changes do not change the URL. + +### Breaking Change Definition (VER-002) +**Breaking:** removing fields/endpoints, changing field types, changing URL structure, tightening validation, changing HTTP status semantics, removing enum values, changing HTTP method. +**NOT breaking:** adding optional fields, adding endpoints, expanding enums, relaxing validation, adding error codes, performance changes. When in doubt → treat as breaking. + +### Deprecation Lifecycle +Profile-governed support windows: +- minimal: 90 days notice, 180 days deprecated support +- standard: 180 days / 365 days +- prod: 365 days / 730 days (2 years) +- fsi: 18 months / 3 years +- sovereign: 2 years / 4 years + +Deprecated versions: fully functional until sunset. Bugs fixed; features not backported. `Deprecation`, `Sunset`, `Link` headers on every response (RFC 8594/RFC 9745). VER-003. + +### Version Discovery +`GET /.well-known/dcm-api-versions` — lists all API surfaces, current/supported/deprecated versions, base URLs, changelog URLs. +Per-version changelog: `GET /api/v{N}/changelog` +Machine-readable migration guide (required by VER-008): `GET /api/v{N}/migration-guide` + +### Sunset Behavior +After sunset: `410 Gone` with successor_version, migration_guide_url, sunset_date. +Three events: `governance.api_version_deprecated`, `governance.api_version_sunset_warning` (30 days before), `governance.api_version_sunset`. + +### Version Negotiation +URL path is authoritative. Optional `DCM-API-Version: v1` header for explicit pinning (returns 406 if sunsetted). +`/api/latest/` alias exists but NOT for production — pin to specific version. + +### Preview Endpoints +`/api/v{N}/preview/` — no stability commitment; may change without major version increment; not for production automation. + +### OIS (Provider API) Versioning +Providers declare `ois_version` in capability registration. DCM maintains dispatch compatibility with all supported OIS versions during deprecation window. VER-009. + +### VER-001–VER-009 System Policies +VER-001: URL path versioning only. VER-002: breaking change definition. VER-003: deprecation headers required. VER-004: deprecated versions fully functional until sunset. VER-005: support windows profile-governed. VER-006: latest alias not for production. VER-007: preview endpoints not stable. VER-008: migration guide required per new major version. VER-009: OIS dispatch compatibility during deprecation window. + +--- + +## SECTION 67 — SESSION TOKEN REVOCATION (doc 35 — 35-session-revocation.md) + +> **Full specification:** [35-session-revocation.md](data-model/35-session-revocation.md) — session lifecycle, revocation triggers, revocation registry, token introspection, AUTH-016–AUTH-022. + +### Session Record +session_uuid · actor_uuid · auth_provider_uuid · auth_method · mfa_verified · created_at · expires_at · status (active/refreshing/revoked/expired) · revocation_reason · revoked_at · revoked_by + +### Session Store +Fast-queryable operational store (not GitOps-backed). Redis/Postgres (standard+) or in-memory (minimal/dev). Profile-governed TTLs: minimal PT8H → sovereign PT15M. Concurrent session limits: unlimited(minimal) → 1(sovereign). + +### Revocation Triggers +actor_logout (single session, self) · actor_logout_all (all sessions, self) · actor_deprovisioned (all sessions — parallel with CPX-006) · actor_suspended · security_event (emergency, no grace period) · concurrent_limit_exceeded (oldest session evicted) · auth_provider_deregistered · credential_compromised · admin_forced_logout + +### Session Revocation Registry +Fast-queryable store of revoked-but-not-yet-expired session UUIDs. ALL components that accept bearer tokens MUST check this on every request. Cache age: PT5M(minimal) → no cache(sovereign). AUTH-018. + +### Actor Deprovisioning (AUTH-016) +Session revocation and credential revocation (CPX-006) are PARALLEL operations. Deprovisioning not acknowledged until BOTH complete. Neither blocks the other. + +### Emergency Revocation (AUTH-019) +security_event trigger: immediate, no grace period. auth.security_session_revoked event: urgency critical, non-suppressable. SLA: PT30S(standard) → PT5S(sovereign). + +### Token Introspection +POST /api/v1/auth/introspect (RFC 7662). Returns {active: true/false, session_uuid, actor_uuid, expires_at, mfa_verified, roles}. Requires introspection scope. AUTH-020. + +### Consumer API Session Endpoints +DELETE /api/v1/auth/session (logout single) · DELETE /api/v1/auth/sessions (logout all) · GET /api/v1/auth/sessions (list active) · DELETE /api/v1/auth/sessions/{uuid} (revoke specific) +Admin: POST /api/v1/admin/actors/{uuid}/revoke-sessions (force revoke, requires reason) + +### AUTH-016–AUTH-022 System Policies +AUTH-016: deprovisioning fires session + credential revocation in parallel. AUTH-017: revocation SLA PT5M(minimal) → PT5S(sovereign). AUTH-018: all components check revocation registry. AUTH-019: emergency revocation = critical urgency, non-suppressable. AUTH-020: introspection endpoint requires introspection scope. AUTH-021: oldest session evicted at concurrent limit. AUTH-022: refresh tokens invalidated when parent session revoked. + +--- + +## SECTION 68 — INTERNAL COMPONENT AUTHENTICATION (doc 36 — 36-internal-component-auth.md) + +> **Full specification:** [36-internal-component-auth.md](data-model/36-internal-component-auth.md) — component identity, Internal CA, bootstrap tokens, communication graph, ICOM-001–ICOM-009. + +### Two-Layer Enforcement +Mesh layer (Istio/mTLS): prevents impersonation at transport. Application layer (DCM): enforces what each component is permitted to do. BOTH required. Network position grants zero trust — internal calls receive same boundary checks as external. + +### Component Identity +Each component has: component_uuid · component_type · mTLS certificate (from Internal CA) · service_account_uuid · allowed_operations · allowed_targets list. + +### Component Types +api_gateway · policy_engine · placement_engine · request_orchestrator · scoring_engine · drift_reconciler · lifecycle_enforcer · notification_router · audit_store · session_store · message_bus · service_provider_proxy + +### Communication Graph (enforced, not advisory) +Consumer/Admin → API Gateway → Request Orchestrator → Policy Engine / Placement Engine / Scoring Engine +All components → Session Store (revocation check) + credential management service Proxy (interaction creds) +ICOM-004: components may ONLY call declared allowed_targets. Unauthorized source → 403 + ICOM_UNAUTHORIZED_SOURCE audit (urgency: high). ICOM-003. + +### Every Internal Call Requires +1. mTLS certificate from Internal CA (transport identity) +2. ZTS-002 scoped interaction credential (operation authorization) — scoped to specific operation + target component, valid PT5M max +3. Correlation ID + +### Internal CA +Per-deployment CA. Certificates: ECDSA-P-384, P90D lifetime, auto-renew P14D before expiry. CRL + OCSP endpoints. Internal CA root cert installed in ALL component trust stores at deploy time. ICOM-006, ICOM-009. + +### Bootstrap Protocol (ICOM-007) +New component has no cert yet. Platform admin generates one-time bootstrap token (PT1H max). Component uses bootstrap token → gets first cert from Internal CA → token invalidated immediately. Kubernetes: token injected as Secret, deleted by component after cert acquisition. Unused tokens auto-expire at PT1H. + +### Certificate Compromise (ICOM-008) +Compromised cert → added to Internal CA CRL immediately → all components refresh CRL within profile SLA (PT15S sovereign, PT1M standard) → ICOM_CERT_COMPROMISED audit (urgency: critical) → platform admin notified → new cert issued. + +### ICOM-001–ICOM-009 System Policies +ICOM-001: mTLS required ALL internal calls, no exceptions. ICOM-002: interaction credential required IN ADDITION to mTLS. ICOM-003: unauthorized source → 403 + high-urgency audit. ICOM-004: components only call declared allowed_targets. ICOM-005: all internal calls audited. ICOM-006: component certs max P90D, Internal CA only. ICOM-007: bootstrap tokens one-time-use PT1H max. ICOM-008: compromised certs → CRL immediately. ICOM-009: Internal CA root in all trust stores; no external CA certs for internal comms. + +--- + +## SECTION 69 — SCHEDULED AND DEFERRED REQUESTS (doc 37 — 37-scheduled-requests.md) + +> **Full specification:** [37-scheduled-requests.md](data-model/37-scheduled-requests.md) — scheduling model, dual policy evaluation, maintenance windows, Request Scheduler component, SCH-001–SCH-006. + +### Scheduling Model +schedule.dispatch: immediate (default) | at (specific time with not_before/not_after) | window (maintenance window reference) | recurring (cron expression). Added as optional field on POST /api/v1/requests — no new submission endpoint. + +### SCHEDULED Status +Request enters SCHEDULED status in Intent State after passing declaration-time GateKeeper. Visible in GET /api/v1/requests?status=SCHEDULED. Cancellable via DELETE /api/v1/requests/{uuid} before dispatch. request.scheduled event (info urgency). + +### Dual Policy Evaluation (SCH-001) +GateKeeper runs at declaration time (fail fast) AND at dispatch time (validate against current state). Dispatch-time rejection → FAILED with schedule_policy_rejection (SCH-003). Data, quotas, policies may all change between declaration and dispatch. + +### Deadline Enforcement (SCH-005) +not_after: if passed without dispatch → FAILED with schedule_deadline_missed. No retry. request.failed event (medium urgency). + +### Maintenance Windows +Reusable named recurrence artifacts. Platform admin creates; consumers reference by window_uuid in schedule. Admin: POST /api/v1/admin/maintenance-windows. Consumer: GET /api/v1/maintenance-windows. + +### New Events (added to doc 33) +request.scheduled · request.schedule_cancelled · request.schedule_deadline_missed (17 total in request.* domain) + +--- + +## SECTION 70 — REQUEST DEPENDENCY GRAPH (doc 38 — 38-request-dependency-graph.md) + +> **Full specification:** [38-request-dependency-graph.md](data-model/38-request-dependency-graph.md) — consumer-declared cross-request ordering, field injection, PENDING_DEPENDENCY status, RDG-001–RDG-006. + +### What This Is +Consumer-declared ordering of INDEPENDENT requests. Distinct from: type-level deps (doc 07, resolved automatically) and Meta Provider composition (doc 30, platform team defines). Use when no Meta Provider exists for the compound deployment. + +### Request Dependency Group +POST /api/v1/request-groups — submit multiple requests with depends_on declarations using local refs. Response includes group_uuid and per-request entity_uuids. GET /api/v1/request-groups/{uuid} for group status. DELETE to cancel. + +### PENDING_DEPENDENCY Status +Dependent request waits in PENDING_DEPENDENCY until dependency reaches wait_for: acknowledged|approved|dispatched|realized (default: realized). Quota counted at group submission, not at dispatch. RDG-004. + +### Field Injection +inject_fields: pass realized output fields from dependency (e.g. IP address) into dependent request's fields automatically at dispatch time. Subject to Transformation policies. RDG-003. + +### Failure Handling +on_failure: cancel_remaining (dependents → CANCELLED with dependency_failed) | continue (only directly-dependent requests fail). Group timeout: all non-terminal → FAILED with group_timeout. RDG-005. + +### Constraints (RDG-001, RDG-002, RDG-006) +Circular deps rejected at submission (422) — must be a DAG. Max 50 requests per group. Request may belong to at most ONE group (409 on second add). + +### New Events (added to doc 33) +request.pending_dependency · request.dependency_met · request.group_completed · request.group_failed + +--- + +## SECTION 71 — DCM SELF-HEALTH ENDPOINTS (doc 39 — 39-dcm-self-health.md) + +> **Full specification:** [39-dcm-self-health.md](data-model/39-dcm-self-health.md) — liveness, readiness, component health, Prometheus metrics, HLT-001–HLT-006. + +### Three Endpoints +GET /livez (liveness — PT5S max, no external calls, unauthenticated, Kubernetes restarts on fail) · +GET /readyz (readiness — checks Session Store + Audit Store + Policy Engine + Message Bus + Auth Provider, unauthenticated, Kubernetes removes from LB on fail) · +GET /api/v1/admin/health (per-component detail, admin auth required) + +### Liveness (/livez) +{status: pass|fail}. Fail if: deadlocked, Internal CA unreachable. Responds within PT5S. No DB reads, no external calls. HLT-002. + +### Readiness (/readyz) +{status: pass|warn|fail, checks: {session_store, audit_store, policy_engine, message_bus, auth_provider}}. Fail if ANY core dependency unreachable. Warn if optional component degraded. Used for startup probe (failureThreshold 30 × 10s = 300s startup allowance). HLT-003, HLT-006. + +### Component Health (/api/v1/admin/health) +Full per-component status: api_gateway, request_orchestrator, policy_engine, placement_engine, scoring_engine, request_scheduler, drift_reconciler, lifecycle_enforcer, discovery_scheduler, notification_router, session_store, audit_store, message_bus, internal_ca. Plus providers{registered/healthy/degraded/unhealthy} and auth_providers summary. + +### Prometheus Metrics (/metrics) +dcm_requests_total · dcm_request_duration_seconds · dcm_requests_pending_dependency_total · dcm_policy_evaluations_total · dcm_sessions_active_total · dcm_session_revocations_total · dcm_drift_open_records_total · dcm_providers_healthy_total · dcm_internal_ca_certificates_active. HLT-005. + +### Kubernetes Manifest +livenessProbe: /livez PT5S timeout, 10s period, 3 failures. +readinessProbe: /readyz PT10S timeout, 5s period, 6 failures. +startupProbe: /readyz PT10S timeout, 10s period, 30 failures (allows 300s startup). + +--- + +## SECTION 72 — STANDARDS AND COMPLIANCE CATALOG (doc 40 — 40-standards-catalog.md) + +> **Full specification:** [40-standards-catalog.md](data-model/40-standards-catalog.md) — authoritative source for all RFCs, protocols, cryptographic standards, CNCF projects, and compliance frameworks used in DCM. + +### Internet Standards (IETF RFCs) — Normative +Auth/AuthZ: RFC 7519 (JWT) · RFC 7517 (JWK) · RFC 7662 (Token Introspection) · RFC 6749 (OAuth 2.0) · RFC 4511 (LDAP) · RFC 7643/7644 (SCIM 2.0) +Transport: RFC 8446 (TLS 1.3, preferred) · RFC 5246 (TLS 1.2, minimum) · RFC 5280 (X.509/CRL) · RFC 6960 (OCSP) +Certificate enrollment: RFC 7030 (EST, preferred) · RFC 8555 (ACME) · RFC 8894 (SCEP, optional) · RFC 4210 (CMP, optional) +API lifecycle: RFC 8594 (Sunset header, VER-003) · RFC 9745 (Deprecation header, VER-003) +Health/discovery: RFC 8615 (Well-Known URIs, /livez /readyz /.well-known/dcm-api-versions) +Data: ISO 8601 (all timestamps and durations) · RFC 8259 (JSON, all API bodies) + +### Cryptographic Standards +Permitted algorithms: ECDSA P-384 (Internal CA, all profiles), AES-256-GCM, SHA-256 minimum, SHA-384/512 for fsi+ +RSA permitted only ≥ 2048 bits +TLS: 1.3 preferred, 1.2 minimum — TLS 1.0/1.1 strictly prohibited in ALL profiles +FIPS 140-2 Level 1+ (standard/prod), Level 2+ (fsi/fedramp), Level 3 (sovereign/dod_il4) +FORBIDDEN (all profiles, no exceptions): MD5, SHA-1, DES, 3DES, RC4, RSA < 2048 + +### Authentication Assurance Levels (NIST SP 800-63B) +minimal/dev: AAL1 (single factor OK) · standard/prod: AAL2 (MFA required) · fsi: AAL2+ (phishing-resistant) · sovereign: AAL3 (hardware authenticator) + +### Compliance Frameworks and DCM Profile/Overlay Mapping +HIPAA → fsi profile + hipaa overlay · PCI DSS → pci_dss overlay (P90D max rotation, 12-month audit) · FedRAMP Moderate/High → fedramp_moderate/fedramp_high overlays (NIST 800-53) · DoD IL4 → dod_il4 overlay (FIPS 140-2 Level 2, hardware attestation) · GDPR → sovereignty constraints + data classification · ISO 27001 → all profiles (risk-based approach) · SOC 2 → standard+ (Type II audit trail) · NIST SP 800-53 → FedRAMP profiles + +### CNCF Ecosystem (Graduated Projects) +Kubernetes (deployment, CRD operator, resource model) · OPA/Open Policy Agent (policy engine backend, Rego) · Prometheus (metrics, /metrics endpoint) · OpenTelemetry (tracing, correlation IDs) · Istio (internal mTLS service mesh) · Argo CD / Flux (GitOps delivery) · SPIFFE (workload identity concept — inspiration for ICOM component identity model) + +### Operational Standards (Normative) +W3C SSE / Server-Sent Events (GET /api/v1/requests/{uuid}/stream — live status without polling) · OpenAPI 3.1 (REST API spec format — consumer, admin, OIS specs) · Unix cron / POSIX (recurring schedule expressions in doc 37) · IANA health+json (RFC 8615 — /livez /readyz health response format) · GitOps / OpenGitOps v1.0 (all DCM artifacts in Git; PR-based contribution) + +### External CA credential management services (Optional) +HashiCorp Vault PKI (native API + EST/ACME; recommended enterprise PKI for fsi/sovereign; operates as subordinate CA) · Venafi TLS Protect (ACME/EST/REST) · EJBCA (ACME/CMP/SCEP) — all implemented as x509_certificate credential management services per doc 31; NOT Auth Providers + +### Policy Family → Standards Mapping (doc 40 Section 9) +AUTH → RFC 6749/7519/7662/OIDC/SCIM · CPX → FIPS 140/RFC 5280/8555/7030/8894/4210 · ICOM → RFC 8446/5280/SPIFFE/FIPS 140 · VER → RFC 8594/9745 · SES → RFC 7662/7009 · HLT → RFC 8615/Kubernetes probes · ZTS → NIST SP 800-207/800-63B · SCH/RDG → industry scheduling/DAG patterns · SMX/ATM → organizational risk governance + +--- + +## SECTION 73 — OPERATIONAL REFERENCE (doc 41 — 41-operational-reference.md) + +> **Full specification:** [41-operational-reference.md](data-model/41-operational-reference.md) — GitOps store partitioning, store migration playbook, disaster recovery scenarios, OPS-001–007. + +### GitOps Store Partitioning (Section 1) +Three strategies: tenant-shard (hash(tenant_uuid) % N shards — recommended), per-tenant (one repo per tenant — MSP/strict isolation), time-based archiving (active vs cold archive repos). Triggers: >50k entities, clone time >PT30S, >500 tenants, repo >10GB. Layer Store partitioned by resource domain (Compute.*, Network.*, etc.). Shallow clones for read-only consumers; read mirrors for audit/drift. + +### Store Migration Playbook (Section 2) +5-phase pattern: Prepare → Backfill → Validate → Cutover → Decommission (after burn-in). Dual-write mode during migration. Audit chain must be unbroken across cutover (OPS-002). Source stays read-only for burn-in period — DO NOT decommission early (OPS-003). Profile-governed burn-in: P7D(minimal) → P90D(fsi/sovereign). Rollback available during burn-in by re-pointing to source. Common paths: SQLite→PostgreSQL (evaluation→standard), PostgreSQL→CockroachDB (HA requirement), GitOps repo restructuring (monorepo→shards via git filter-repo). + +### Disaster Recovery Scenarios (Section 3) +5 scenarios with RTO/RPO per profile: +- S1 Component failure: pod restart; RTO PT1M(sovereign)–PT15M(minimal); RPO 0 (stateless) +- S2 Store failure: failover/restore; RTO PT5M(fsi)–PT2H(minimal); RPO 0(GitOps)–PT15M(Audit) +- S3 Full control plane loss: redeploy + store reconnect; RTO PT5M(fsi)–PT30M(minimal); RPO 0 +- S4 Partial region loss: federation reroutes; sovereignty-scoped may block until region recovers +- S5 Repave (complete loss, Git intact): bootstrap from Git, restore operational stores from backup, rehydrate managed resources; RTO PT2H(fsi)–PT24H(minimal) + +Post-recovery validation checklist (Section 3.6): /livez pass, /readyz pass, audit chain verify, store validate, drift scan, cert status, write post-incident audit record. OPS-005. + +### OPS-001–007 System Policies +OPS-001: partitioning declared in deployment manifest. OPS-002: audit chain continuity across migration. OPS-003: source read-only during burn-in; do not decommission early. OPS-004: RTO must be met per profile. OPS-005: post-recovery checklist written to audit store. OPS-006: Audit Store minimum P365D retention all profiles. OPS-007: Git remotes must have push access from 2+ geographically separated locations. + +--- + +## SECTION 74 — WEB INTERFACE SPECIFICATIONS (3 specs) + +> **Consumer GUI:** [dcm-consumer-gui-spec.md](specifications/dcm-consumer-gui-spec.md) — Consumer Portal wrapping all 16 Consumer API sections, bounded by tenancy +> **Admin GUI:** [dcm-admin-gui-spec.md](specifications/dcm-admin-gui-spec.md) — Admin Panel wrapping all Admin API sections, role-gated +> **Provider GUI:** [dcm-provider-gui-spec.md](specifications/dcm-provider-gui-spec.md) — Provider management shell + 11 type-specific extension sets + +### Unified Shell Architecture (GUI-010) +ONE application, THREE role-gated surfaces: Consumer Portal (all actors) + Admin Panel (platform_admin/sre/auditor/security/policy_owner/finops) + Provider Management (provider_owner role). One login, one session token — navigation adapts to highest privilege level. Flow GUI (policy authoring) is linked/embeddable within Admin Panel. + +### Consumer Portal (dcm-consumer-gui-spec.md — 20 sections) +Tenancy: X-DCM-Tenant header; ContextSelector in masthead (hidden for single-tenant actors). +Navigation (PatternFly grouped left nav): Service Catalog | MY WORK (Requests, Resources, Dependency Groups) | Approvals [badge] | GOVERNANCE (Cost & Quota, Audit Trail, Contributions) | SETTINGS (Notifications, Sessions). **Hide, not disable** — unavailable items hidden entirely. +Key capability — Live Status (GUI-002): SSE stream (GET /api/v1/requests/{uuid}/stream); events: status_change, progress_updated, approval_required, approval_recorded, heartbeat; constituent_status array for compound/Meta Provider requests; fallback to polling. +Key capability — Request form: rendered from catalog item schema; live cost estimate; scheduling section (at/window/recurring); dependency group linking with field injection declaration. +Key capability — ITSM Bridge (GUI-014, section 8): ITSM references (ServiceNow/Jira change records, CMDB CIs) displayed on entity Overview tab; ITSM notification service translates DCM events → ITSM records; ITSM systems call Admin API to record approval votes (DCM records decision, ITSM runs CAB process); CMDB sync is one-way DCM→CMDB via notification service subscription. +Key capability — Consumer Audit Trail (section 11): own resource audit trail with Correlation ID trace through full pipeline; filterable by operation/type/date; export CSV. +Key capability — Drift Report (section 6.3): cross-resource drift view with severity sorting; Revert All Critical bulk action; export drift report. +Security: MFA step-up inline (no page leave); strict CSP (connect-src self + DCM API origin only; no inline scripts). + +### Admin Panel (dcm-admin-gui-spec.md) +Platform health dashboard: component health grid, provider health summary, pending approvals count — all from GET /api/v1/admin/health. +Tier registry editor: drag-and-drop reorder; impact report shows SECURITY_DEGRADATION(red)/BROKEN_REFERENCE(orange)/PROFILE_GAP(yellow)/SECURITY_UPGRADE(green); activate blocked until all blocking items resolved. +Scoring editor: visual slider with hard-stop — auto_approve_below slider maximum is 50 (SMX-008/ATM-002); signal weights must sum to 100%. +Flow GUI accessible via link or embedded iframe for policy_owner/sre roles. + +### Provider Management (dcm-provider-gui-spec.md) +Common shell for ALL 5 provider types: overview, config, health history, audit trail, notifications. +Provider ownership: declared at registration (owner_team_uuid); provider_owner role scoped per provider UUID. +Service Provider extensions: capacity management (manual override for emergencies), managed entities with drift indicators, naturalization mapping viewer, test naturalization tool, interim status config. +credential management service extensions: inventory (metadata only — NEVER values), rotation management, revocation registry search, external CA config (protocol, chain, CRL/OCSP status), algorithm compliance view (forbidden algorithms must be zero). +Auth Provider extensions: connection status with failover chain, SCIM sync status, session statistics by auth method. + +### Security Model (all GUI surfaces) +Navigation: hide not disable. Roles from session token; re-checked on token refresh. Tenancy: X-DCM-Tenant enforced UI + API (defense in depth). Step-up MFA: inline prompt without page navigation; cached PT10M. CSP: connect-src self + DCM API; no inline scripts; no eval(). + +### RHDH Integration (dcm-rhdh-integration-spec.md) +PRIMARY deployment model. 6 plugin packages as Dynamic Plugins (no RHDH rebuild): @dcm/backstage-plugin (frontend) · @dcm/backstage-plugin-backend (proxy + SSE relay + auth) · @dcm/backstage-plugin-catalog-backend (entity provider: DCMService + DCMResource) · @dcm/backstage-plugin-scaffolder-backend (dcm:request:submit, dcm:request:wait with live log, dcm:request:group, dcm:catalog:refresh) · @dcm/backstage-permission-policy (DCM roles → Backstage permissions) · @dcm/backstage-plugin-auth-backend (RHDH as DCM OIDC provider). +Auth: RHDH/Keycloak → OIDC token exchange → DCM session token (cached in RHDH backend by backstage user ref). Tenancy: RHDH Group context → X-DCM-Tenant header (via dcm-tenant-{uuid} group naming convention). +Entity model: DCMService kind (catalog items, namespace=dcm-catalog) auto-generated from DCM API every PT5M. DCMResource kind (realized resources, namespace=dcm-tenant-uuid) auto-synced from Realized State. Both search-indexed in RHDH. +Scaffolder = request form: each DCM catalog item generates one Backstage Software Template from its JSON Schema. Template wizard → dcm:request:submit → dcm:request:wait (live log: step N/M, constituent status) → dcm:catalog:refresh → entity appears in catalog. NO separate request form UI needed. +PatternFly nav: NavGroup(MY WORK > Requests/Resources/Groups) · NavItem with NotificationBadge (Approvals [count]) · NavGroup(GOVERNANCE > Cost&Quota/Contributions) · NavGroup(SETTINGS > Notifications/Sessions). Tenant via RHDH ContextSelector in masthead. +Pre-built RHDH value: RHSSO/Keycloak auth (no auth code) · RBAC plugin (no-code role management) · TechDocs (DCM docs in-portal) · ArgoCD plugin (layer store visibility) · AAP plugin (Ansible provider job status) · OCM plugin (cluster management alongside DCM). +Migration: Standalone SPA → RHDH via Dynamic Plugin loading only; no data migration, no API changes; 5-phase migration ending in standalone SPA decommission. + +--- + +## SECTION 75 — ITSM INTEGRATION (doc 42 — 42-itsm-integration.md) + +> **Full specification:** [42-itsm-integration.md](data-model/42-itsm-integration.md) — 12th provider type, 8th policy type, 6 example policies, ITSM-001–007, ITSM-POL-001–004. + +### Design Principle +DCM replaces the infrastructure ticket as the provisioning mechanism. ITSM integration is ADDITIVE — never required for DCM to function. Non-blocking by default. Organizations opt into blocking gates explicitly. + +### ITSM Integration (via process_provider) +Bidirectional: outbound (DCM events → ITSM records) + inbound (ITSM approvals → DCM votes). Implements full base Provider contract (PRV-001). Supported systems: ServiceNow, Jira Service Management, BMC Remedy/Helix, Freshservice, PagerDuty, Opsgenie, ManageEngine, Cherwell, TOPdesk, generic_rest. +Key capabilities: create/update/close change_request, create/update/close incident, create/update/retire cmdb_ci, create service_request, inbound_approval, inbound_request_initiation. +Credentials: auth via credential management service (ITSM-001). Inbound webhooks: HMAC-SHA256 verified (ITSM-003). + +### ITSM Action Policy (8th policy type) +Side-effect policy — fires on DCM events, triggers ITSM action, does NOT block pipeline by default. +Output schema: itsm_provider_uuid, action, action_payload (template expressions: {{ field }}), store_reference_on_entity, block_until_created, block_timeout, on_failure. +ITSM-005: block_until_created REQUIRES block_timeout — pipeline never permanently stalled. +ITSM-POL-002: NOT a GateKeeper substitute except via explicit block_until_created mechanism. +ITSM-POL-004: Multiple ITSM Policies on same event fire INDEPENDENTLY. + +### 6 Policy Examples +1. create_change_request on request.dispatched (ServiceNow) — log and continue +2. block_until_created for PCI-scope tenants (compliance gate with PT30M timeout) +3. create_cmdb_ci on entity.realized — with IP address from realized_fields +4. create_incident on drift.detected (significant/critical severity, Jira) +5. retire_cmdb_ci on entity.decommissioned +6. close_change_request on request.realized + +### ITSM Entity References +itsm_references[] on entity business data: system, record_type, record_id, record_url, status, last_synced_at. Preserved through entity lifecycle. In audit records. + +### recorded_via Field +Already on approval vote API: dcm_admin_ui | servicenow | jira | slack_bot | api_direct | other. ITSM integration populates this on inbound approvals. + +### Event Catalog Additions +itsm.record_created, itsm.record_updated, itsm.record_failed — new itsm.* domain (21st domain; 85 total events). + +### ITSM-001–007 + ITSM-POL-001–004 +ITSM-001: base Provider contract applies. ITSM-002: non-blocking by default. ITSM-003: inbound webhook HMAC auth. ITSM-004: references persist through lifecycle. ITSM-005: block_until_created mandatory timeout. ITSM-006: unmapped resource types silently skipped for CMDB sync. ITSM-007: missing template fields → warning + empty string (no block). + +--- + +## SECTION 76 — PROVIDER CALLBACK AUTHENTICATION (doc 43 — 43-provider-callback-auth.md) + +**Purpose:** Specifies how Service Providers authenticate inbound calls to the DCM control plane (the Provider Callback API). Resolves the gap between the outbound credential model (DCM → Provider) and the inbound model (Provider → DCM). + +**Two-layer model:** +- **Layer 1 — mTLS:** Provider presents its registered certificate on every TLS connection. DCM validates the chain against the registered CA and the stored certificate fingerprint for that provider_uuid. Proves transport-level identity. +- **Layer 2 — Provider Callback Credential:** A `dcm_interaction` credential issued by the credential management service at provider activation time. Scoped to `provider_uuid` + `allowed_operations`. Short-lived (PT15M fsi/sovereign; PT1H standard). Presented as `Authorization: Bearer` on every callback call. Proves operation-level authorization. +- Both layers are required. mTLS alone does not prove authorization. The credential alone cannot establish the connection. + +**Entity-level authorization (per call, independent of credential):** +- `realized_state_push`: DCM verifies `credential.provider_uuid` matches the `provider_uuid` in the Requested State record for that `resource_id`. Prevents a provider from pushing state for entities it was not dispatched to. +- `update_notification`: DCM verifies the calling provider is the current Realized State provider for the entity AND the `notification_type` was declared in the provider's registration. +- `lifecycle_event`: DCM verifies the calling provider is the provider on record for the resource. + +**Credential lifecycle:** +- Issued at provider activation; delivered via the activation response (retrieved via credential management service) +- Rotated automatically by DCM before expiry (DCM initiates rotation; provider must implement refresh) +- Transition window (50% of credential lifetime) during which both old and new credentials are accepted +- Revoked immediately on: provider deregistration, 5+ scope violations in PT1H, admin explicit revocation, certificate expiry without rotation + +**Registration special case:** +- Initial registration (`POST /api/v1/providers`) uses a registration token (single-use, admin-issued) not the callback credential — no callback credential exists until activation +- Re-registration (same name, updating version/capabilities) uses the active callback credential +- Re-registration that changes sovereignty declaration requires a new registration token and triggers a new approval pipeline + +**System policies:** PCA-001 through PCA-010. Key: PCA-003 (entity-level ownership check is independent of credential), PCA-004 (5 scope violations → auto-suspend), PCA-010 (all inbound calls produce audit records including rejected ones — no silent failures). + + +## SECTION 77 — AEP API ALIGNMENT + +DCM's four OpenAPI specifications follow AEP (API Enhancement Proposals — aep.dev) conventions in three specific areas: + +**1. Custom methods (AEP-136):** Actions on resources use colon syntax. `POST /resources/{id}:suspend` not `POST /resources/{id}/suspend`. Applies to all state-transition and action operations in Consumer and Admin APIs (30 path conversions total: :suspend, :resume, :rehydrate, :rotate, :extend-ttl, :transfer, :bulk-decommission, :acknowledge, :revert, :approve, :reject, :reinstate, :trigger, :rebuild, :activate, :vote, :revoke-sessions, :accept-degradation, :rotate-credential, :read-all, and others). + +**2. Long-Running Operations (AEP-151):** Async operations that produce a trackable result return an `Operation` resource (not `202 Accepted` with no body). The `Operation` has: `name` (stable poll URL), `done` (boolean), `metadata` (stage/progress/resource_uuid), `response` (present when done=true, success), `error` (present when done=true, failure). 14 consumer-facing operations return Operation: submitRequest, createRequestGroup, updateResource, decommissionResource, suspendResource, resumeResource, rehydrateResource, initiateOwnershipTransfer, bulkDecommission, revertDrift, rotateCredential, contributePolicy, contributeResourceGroup (consumer API) + decommissionTenant (admin API). Fire-and-forget operations (capacity reports, interim status, lifecycle events, discovery triggers) retain `202 Accepted` — no Operation body. + +**3. Pagination (AEP-158):** `page_size` and `page_token` query parameters (not `limit`/`cursor`). Responses include `next_page_token`. + +**Deliberately NOT aligned:** Resource names (DCM retains bare UUIDs — immutability across ownership transfers is more important than AEP naming), timestamp field names (`created_at` not `create_time` — would require data model change with no functional benefit). + +**Operator API note:** The operator-facing Services API (dcm-operator-api.yaml) uses the callback pattern — operators are NOT consumer-facing LRO callers. `updateResource` in the operator spec uses `202 Accepted` (async with callback) + `200 OK` (sync with RealizedStatePayload), not an Operation resource. + +## SECTION 78 — KESSEL INTEGRATION EVALUATION (doc 44 — 44-kessel-integration-evaluation.md) + +**Status:** Pre-implementation evaluation. Discussion with Kessel team required before any implementation. No DCM architecture changes should be made based on this document until alignment is confirmed. + +**What Kessel is:** +- **Kessel Relations:** Authorization service built on SpiceDB (Google Zanzibar / ReBAC). `CheckPermission(subject, permission, resource)` traverses relationship graph. Zookie consistency tokens for "read your own writes" guarantee. +- **Kessel Asset Inventory:** Hybrid cloud resource state tracking (current-state snapshot store, gRPC streaming API). Integrates with Kessel Relations for auth-filtered inventory queries. + +**Integration Option A — Kessel Relations as DCM Auth Provider:** +- Handles checks 1 and 2 of DCM's five-check boundary model (identity + authorization). Checks 3-5 (accreditation, data matrix, sovereignty) remain in DCM's Policy Engine — cannot be delegated. +- DCM's entity relationship graph (operational: requires/constituent/shareable) must NOT go in Kessel Relations — only access-control relationships. +- Approval gate quorum stays in DCM (Kessel answers "is actor authorized to vote?"; DCM tracks "how many have voted"). +- Integration path: new `auth_mode: kessel_rebac` Auth Provider registration. +- Zookie tokens must be threaded through DCM request context for consistency. + +**Integration Option B — Kessel Inventory as Discovered State Store:** +- Discovered State only (most ephemeral store, current-state snapshot). Intent, Requested, Realized stores remain in DCM — not replaceable by Kessel Inventory. +- Drift detection logic stays in DCM's DRC component regardless — Kessel Inventory is a data source, not a drift engine. +- Field-level provenance, lifecycle state machine, audit chain — all stay in DCM. +- Integration path: snapshot store backed by Kessel Inventory. No data model changes. + +**Pros (Relations):** SpiceDB production-grade, Zanzibar consistency, scalable graph traversal, shared source of truth across Red Hat products, single CheckPermission call replaces multi-step group-lookup + policy-evaluation. + +**Pros (Inventory):** Feeds same discovered state to ACM/Insights/HCC, reduces DCM's operational burden for ephemeral store, gRPC streaming fits provider push pattern. + +**Cons (Relations):** Hard runtime dependency (unavailability = safe-deny mode), schema coupling requires coordinated evolution, quorum model doesn't fit, entity relationship graph stays in DCM anyway. + +**Cons (Inventory):** Four-state model mismatch (Kessel is upsert/current-state only), no field-level provenance, no drift detection, schema extensibility for DCM-specific types unconfirmed, project maturity risk. + +**18 questions for Kessel team documented in Section 6. 10 blocking items in Section 10.** + +**KESSEL-001 through KESSEL-007** are the proposed system policies — not active until Kessel alignment is complete. +## SECTION 79 — CONSISTENCY REVIEW (doc 45 — 45-consistency-review.md) + +Full consistency review completed 2026-03. Key findings and fixes: + +**Fixed:** AEP colon paths applied to admin-api-spec.md (13 paths) and consumer-api-spec.md (4 remaining); stale entity_type values corrected (`allocated_resource`→`infrastructure_resource`, `resource_entity`→`infrastructure_resource`); stale scoring threshold keys replaced with named-tier format comments; `provider_id`→`provider_uuid` in DCM API endpoint paths; Operation (LRO) polling section added to consumer spec. + +**Canonical rules:** entity_type has exactly three values (infrastructure_resource, composite_resource, process_resource). Custom method paths use colon syntax in ALL docs. `provider_uuid` in DCM APIs; `resource_id` (operator-assigned) in callback APIs — these are intentionally distinct. Lifecycle state UPPERCASE in YAML, lowercase in prose — by design. + +**Three implementation decisions still needed:** (1) resource_type field: accept FQN string or require UUID at dispatch? (2) Operation polling: same endpoint as request status or separate? (3) API Gateway must map resource_id → entity_uuid at callback boundary. + + +## SECTION 80 — IMPLEMENTATION DECISIONS RESOLVED (doc updates 2026-03) + +Three open implementation decisions have been resolved: + +**DECISION 1 — resource_type field: accept both FQN and UUID** +- Consumers may supply resource_type as either FQN string (`Compute.VirtualMachine`) or Registry UUID +- DCM resolves either form to the canonical (resource_type_uuid, resource_type_name) pair at request assembly time in the Request Payload Processor +- FQN recommended for consumer use (stable across deployments; returned by service catalog) +- UUID accepted for programmatic use where UUID was obtained from catalog API +- Unresolvable references rejected at validation time: 422 + code RESOURCE_TYPE_NOT_FOUND +- Dispatch payloads (CreateRequest/UpdateRequest) to operators ALWAYS carry both: resource_type_uuid + resource_type_name +- Added to: dcm-common.json (resource_type_ref oneOf type), CatalogItem schema (oneOf in consumer YAML), doc 05, consumer spec, operator spec + +**DECISION 2 — operation.name → /api/v1/operations/{uuid} (separate AEP endpoint)** +- operation_uuid == request_uuid — the same UUID serves both endpoints +- GET /api/v1/operations/{uuid} → Operation schema (AEP-standard: done, metadata, response/error) +- GET /api/v1/requests/{uuid}/status → RequestStatus schema (DCM-native: pipeline_stage, full history) +- Both endpoints reflect the same underlying operation state +- Operation.metadata includes request_uuid field so AEP clients can navigate to the rich view if needed +- POST /api/v1/requests returns Operation with name = /api/v1/operations/{request_uuid} +- Added to: consumer YAML (new GET /api/v1/operations/{operation_uuid} path, 61 paths total), Operation schema (request_uuid in metadata, dual-endpoint description), consumer spec (Operations polling section with dual-endpoint table) + +**DECISION 3 — resource_id → entity_uuid mapping (RESOLVED by existing data model)** +- Not an open decision — already solved by the schema +- DCM sends dcm_entity_uuid in every CreateRequest dispatch; operator echoes it in every response and callback +- DCM uses entity_uuid for all routing; resource_id is operator's own correlation handle stored opaquely +- API Gateway validates dcm_entity_uuid in each callback matches the entity under the calling provider's credential +- Documented explicitly in operator interface spec + + +## SECTION 81 — SPEC COMPLETION: K8S + OPERATOR SDK (2026-03) + +**11-kubernetes-compatibility.md** — Completed: +- Document status header and related docs cross-references added +- Section 2 intro: explains the superset relationship explicitly +- Section 4 summary table: 7 DCM capabilities vs Kubernetes gaps, side-by-side +- Comment lines cleaned from section 3a (code comment artifacts removed) +- AEP alignment note added +- All 5 open questions were already resolved; Resolution Notes complete +- Status: ✅ Complete + +**dcm-operator-sdk-api.md** — Completed: +- Document status header, related docs, AEP alignment note added +- New Section 11: Callback Credential Management — automatic rotation, entity_uuid vs resource_id contract, DCM validation rule +- resource_type_name field annotated: "FQN — always present alongside resource_type_uuid" +- All 5 open questions were already resolved; Q10 header annotated +- Status: ✅ Complete + +**All 14 specifications now have:** AEP alignment notes, document status headers, no stale slash-verb paths, consistent field naming. + + +## SECTION 82 — PDF VALIDATION + THREE NEW ADDITIONS (2026-03) + +**PDF (Miro board) was validated against current architecture.** Core finding: PDF = original design intent; current docs = evolved, more detailed implementation. Structurally aligned. Three gaps identified and filled: + +**GAP 1 — Static Replace use case (dcm-examples.md sections 1.9 + 1.10):** +- Static Replace = re-provision using existing Requested State verbatim, no layer enrichment, no policy re-evaluation. Deterministic rebuild. +- Distinct from Rehydration (mode: intent) which replays original Intent State through current policies and layers. +- `POST /api/v1/resources/{uuid}:rehydrate` with `mode: static` (same endpoint, different mode). +- Precondition: application data on separate partition (VM OS/config is what gets rebuilt). +- Orchestration Flow Policy specified (4-step: validate → decommission → dispatch original requested state → restore operational). +- In-Place Upgrade (Leapp/IPU) also documented as Section 1.10 — upgrades OS in-place, preserves entity UUID, creates new Realized State delta record. + +**GAP 2 — Workload Analysis (new doc 46-workload-analysis.md):** +- New capability: actively classifies discovered resources by operational characteristics. +- Answers: "what is this resource?", "is it migratable?", "what lifecycle model applies?" +- WorkloadProfile = process_resource_entity of type Analysis.WorkloadProfile. +- Fires automatically as part of brownfield ingestion pipeline (DRC → WLA → Ingestion). +- MTA (Migration Toolkit for Applications) is the reference Information Provider implementation. +- 6 system policies WLA-001 through WLA-006. +- API: GET /api/v1/resources/{uuid}/workload-profile, POST /api/v1/resources/{uuid}/workload-profile:analyze +- Added to Capabilities Matrix as Domain 36 (6 capabilities: WLA-001 to WLA-006). + +**GAP 3 — Per-provider monitoring contract (registration spec + self-health doc):** +- 7 Provider Readiness Gates (GATE-SP-01 through GATE-SP-07) added to registration spec Section 7.2. +- Gates 1-3 required for all profiles; Gates 4-7 required for standard+. +- GATE-SP-01: OpenAPI spec declared and reachable. +- GATE-SP-02: Healthy API at activation. +- GATE-SP-03: State Management callback implemented. +- GATE-SP-04: Tenant Metadata endpoint (usage by tenant, quota consumed). +- GATE-SP-05: Prometheus metrics (4 required families: dispatches_total, dispatch_duration_seconds, realizations_total, health_status). +- GATE-SP-06: AEP.DEV linting passes (no errors in provider OpenAPI spec). +- GATE-SP-07: Multi-tenant dispatch (accepts tenant_uuid). +- Self-health doc (39-dcm-self-health.md) Section 7: Per-Provider Metrics Contract added. +- Added to Capabilities Matrix as PRR-001 through PRR-007 in Provider Contract domain. +- Matrix now: 37 domains / 287 capabilities. + +**PDF naming differences documented (not errors — terminology evolution):** +Widget → Resource/Entity; Requested/Realized/Discovered Widget Store → State Stores; +Widget Discovery → Discovery Scheduler; Interoperability API → Operator Interface; +Provisioned Store → Realized State Store; Job Queue → Message Bus; Rules Engine → Policy Engine. + + +## SECTION 83 — ACCREDITATION MONITOR (doc 47 — 47-accreditation-monitor.md) + +**Purpose:** Continuously verifies registered accreditations against authoritative external sources. Answers: "Is this accreditation still valid according to the issuing authority — not just the expiry date we were told?" + +**Four verification tiers (by automation depth):** +- **Tier 1 — External Registry API (full automation):** FedRAMP (marketplace.fedramp.gov/api), CMMC 2.0 (cyberab.org/catalog), StateRAMP, ISO 27001 (iaf.nu CertSearch). Queries by external_registry_id; detects status changes including mid-cycle revocations. +- **Tier 2 — Document Currency (partial automation):** SOC 2, PCI DSS AoC. Fetches document from certificate_ref/audit_report_ref, extracts date via PDF metadata or header parsing, validates against max_age threshold (default P365D). +- **Tier 3 — Contract Webhook (event-driven):** HIPAA BAA, DoD IL. Inbound webhook from DocuSign/Ironclad/etc fires when BAA is signed, amended, or terminated. DCM processes: signed→active, amended→pending_review, terminated→revoked. +- **Tier 4 — Expiry-Only (no external check):** Self-declared, internal, HIPAA BAA without contract system. Monitors declared valid_until only. + +**Key flows:** +- Tier 1 poll detects Revoked status → immediate accreditation revocation → Accreditation Gap triggered (no admin confirmation required for revocations) +- Tier 1 poll detects status change to non-revoked (e.g., Authorized → In Process) → status: pending_review → admin confirms +- Registry unreachable → increment failure_count; no status change; fires verification_stale at threshold +- stale_after exceeded → stale_action: warn/suspend/escalate (profile-governed: warn for dev/standard, suspend for prod, escalate for fsi/sovereign) + +**Accreditation record additions (doc 26 Section 3.3):** `verification` block with tier, registry_api/document_check/contract_webhook sub-blocks, stale_after, stale_action, verification_failure_count. `status` gains `pending_review` state. `gap_type` gains `verification_stale`. + +**Scoring Model addition (doc 29 Signal 5):** `verification_multipliers` — accreditation weight discounted based on verification currency: external_registry verified today = 1.0, expiry_only = 0.7, stale = 0.4, failed threshold = 0.1. + +**New events (doc 33 Section 20):** accreditation.verified, accreditation.status_changed, accreditation.registry_mismatch, accreditation.verification_stale, accreditation.document_expired, accreditation.contract_event, accreditation.expiry_approaching. + +**8 system policies: ACM-001 through ACM-008.** Key: ACM-002 (status change → pending_review except Revoked which is immediate), ACM-003 (registry unreachable does NOT revoke), ACM-004 (fsi/sovereign must use tier ≥ document_currency), ACM-007 (all verifications produce audit records — no silent checks). + +**Air-gapped mode:** Tiers fall back to expiry_only for unreachable registries. Retries after air_gapped_retry_interval (default P30D). Manual update of last_verified_at permitted with required justification. + +**Matrix:** Domain 37, ACM-001 through ACM-007. Total: 37 domains / 287 capabilities. + + +## SECTION 84 — COMPREHENSIVE USE CASE EXAMPLES (dcm-use-case-examples.md) + +New specification: `specifications/dcm-use-case-examples.md` — 1,853 lines, 5 sections. + +**Shared context:** All examples use consistent fictitious actors (alice@corp, bob@corp, svc-pipeline@corp), tenants (payments-bu, web-platform-bu, platform-team), and providers (vmware-prod, netbox-prod, vault-prod, freeipa-prod, ceph-prod, rabbitmq-prod, servicenow-prod, webapp-meta) for cross-example coherence. + +**Section 1 — Data Model Examples (12 examples):** +- 1.1 Four States: VM intent → layer assembly → requested state → realized state → discovered state → decommission +- 1.2 Layer Assembly: 6-layer compose (base/DC/zone/BU/service/request) with full provenance +- 1.3 Governance Matrix: PHI request denied — four-axis evaluation, HIPAA BAA missing on provider +- 1.4 Scoring Model: 5-signal risk score (0-100), placement tie-breaking by accreditation richness + verification multiplier +- 1.5 Authority Tier: 200-VM bulk request → CRITICAL tier → 3-step approval chain +- 1.6 Entity Relationships: VM+IP+FirewallRule composite, decommission impact analysis +- 1.7 Universal Groups: tenant boundary, resource group, cross-tenant read authorization +- 1.8 Scheduled Requests: maintenance window + deferred OS patch via CI/CD +- 1.9 Request Dependency Graph: DB→App→LB 3-node chain with field injection between nodes +- 1.10 Workload Analysis: legacy VM discovered → classified (batch_processor, confidence:medium) → MTA score → ingested +- 1.11 Accreditation Monitor: FedRAMP daily verify + mid-cycle revocation → immediate gap trigger +- 1.12 Session Revocation: stolen laptop → all sessions revoked → in-flight request aborted safely + +**Section 2 — Provider Interaction Examples (6 new examples, 2.5–2.10):** +- 2.5 Auth Provider (FreeIPA): registration, LDAP auth flow, group mapping → DCM roles +- 2.6 Data Store: write-once snapshot registration, Realized State write +- 2.7 event routing service (RabbitMQ): topic exchange, routing key pattern, multi-subscriber routing +- 2.8 credential management service (Vault): AppRole registration, ephemeral bind-password fetch, dynamic DB creds, consumer SSH key retrieval with audit +- 2.9 Meta Provider: compound WebApp (VM+IP+FW+DNS) decomposition, parallel + sequential constituent ordering, field injection +- 2.10 ITSM integration (ServiceNow): incident creation on provider health change, field mapping, resolve on recovery + +**Section 3 — Registration Flow Examples (3 examples):** +- 3.1 Information Provider (NetBox): token issuance, mTLS registration, 6-check validation, approval, assembly enrichment +- 3.2 Auth Provider (Azure AD OIDC): secondary auth source for contractors, precedence ordering, TTL-scoped role mapping +- 3.3 Meta Provider: constituent validation, circular dependency check, activation + +**Section 4 — OPA Policy Integration (2 examples):** +- 4.1 Shadow mode: 30-day shadow evaluation, divergence reporting, admin review dashboard, promotion to active +- 4.2 Bundle delivery: sidecar registration, 5-minute pull cycle, hot reload, evaluation call with input/output + +**Section 5 — GUI Examples (3 examples):** +- 5.1 Consumer Portal: login → catalog → cost estimate → submit → progress polling → credential delivery +- 5.2 Admin GUI Policy Flow: node-by-node pipeline visualization with shadow indicators and red blocking paths +- 5.3 Admin GUI Drift Dashboard: severity summary, critical drift detail, revert/accept/investigate actions + + +## SECTION 85 — EXAMPLES EXPANSION (dcm-examples.md) + +dcm-examples.md expanded from 1,058 lines to 2,189 lines. Now covers all 10 provider types, all 3 remaining policy types, and 8 new lifecycle/model flows. Full section inventory: + +**Section 6 — Provider Type Examples (NEW):** +- 6.1 Data Store — state store write/read cycle (provenance emission, replica confirmation) +- 6.2 Auth Provider — OIDC cutover from GitHub OAuth (shadow evaluation, zero-downtime cutover) +- 6.3 credential management service — SSH key issuance post-VM-realization, 90-day TTL, auto-rotation at P45D +- 6.4 Meta Provider — three-tier WebApp stack (VM→VM→LB→DNS with field injection between tiers) +- 6.5 ITSM integration — ServiceNow Change Request lifecycle (create→approve→implement→close) +- 6.6 event routing service — Kafka event bridge (entity lifecycle events, dead letter handling) + +**Section 7 — Policy Type Examples (NEW):** +- 7.1 Transformation Policy — OS image auto-injection (immutable field, provenance annotation) +- 7.2 Placement Policy — PHI VM with HIPAA BAA requirement (require/prefer/exclude model, audit trail) +- 7.3 Shadow Execution — cost-cap policy rollout (parallel evaluation, divergence report, safe activation) + +**Section 8 — Lifecycle and Model Examples (NEW):** +- 8.1 Scheduled Request — deferred provisioning via maintenance window (deadline handling, cancellation) +- 8.2 Request Dependency Graph — three-tier app with realized field injection across tiers +- 8.3 Authority Tier Routing — sovereign decommission requiring sequential platform_admin + CISO approval +- 8.4 Rehydration (intent mode) — DR failover to new datacenter (contrast with Static Replace) +- 8.5 Session Revocation — emergency security incident response (revoke-all, in-flight handling) +- 8.6 Workload Analysis — brownfield VM classification via MTA (port scan, process list, classification pipeline) +- 8.7 Scoring Model — placement tie-breaking with 5 signals (accreditation verification multiplier effect) +- 8.8 Accreditation Monitor — FedRAMP status change detection (mid-cycle downgrade, pending_review flow) + +**Previously covered (Sections 1-5, unchanged):** +Service Provider (dispatch cycle), Information Provider (assembly enrichment), External Policy Evaluator (OPA sidecar), +notification service (audience graph), Consumer API lifecycle, Admin API flows, Registration onboarding, +Brownfield ingestion, Static Replace, In-Place Upgrade. + +All 10 provider types: ✅ covered. All 7 policy output schemas: ✅ covered. All major model flows: ✅ covered. + + +## SECTION 86 — LOCATION TOPOLOGY LAYER MODEL (doc 48) + +**New doc:** 48-location-topology-layers.md — specifies where resources can be allocated, how location data flows into requests as Core Layers, and how consumers select locations. + +**Core concept:** Location is not a string field. It is a resolved chain of versioned Core Layers — one per level of the topology hierarchy. When a consumer selects "DC1 — Frankfurt Alpha", DCM assembles Country → Region → Zone → Site → Data Center layers into the request payload, injecting all structured location data at each level. + +**Standard hierarchy (9 levels, configurable names, standard types):** +- Level 1 Country (CTY) — jurisdiction, regulatory frameworks, ISO codes +- Level 2 Region (RGN) — interconnects, latency profile, failover region +- Level 3 Zone/AZ (AZ) — isolation boundary, HA peers, RPO/RTO +- Level 4 Site/Campus (SITE) — physical address, security tier, facilities contacts +- Level 5 Data Center (DC) — tier classification, power/cooling, PUE, certifications +- Level 6 Hall/Pod (HALL) — optional; network segment, cooling type +- Level 7 Cage/Enclosure (CAGE) — optional; tenant isolation, access control +- Level 8 Rack (RACK) — rack units, power circuits, ToR switch +- Level 9 Unit/Slot (UNIT) — optional; typically provider-managed + +**Authority model:** Each level has a designated owning authority (Data Center Operations, Network Operations, Facilities, Platform Governance). Changes require GitOps PRs approved by the owning authority. Upper levels (Country, Region, Zone) require platform_admin approval; lower levels (Hall, Cage, Rack) can be operator-approved. + +**Custom types:** Custom levels insertable anywhere using decimal level values (e.g., Fleet=3.5, Ship=4.5 in a Navy deployment). Navy example included in doc. + +**Priority bands:** Location layers occupy dedicated bands in the Core Layer priority space: Country=100.xx, Region=200.xx, Zone=300.xx, Site=400.xx, DC=500.xx, Hall=600.xx, Cage=700.xx, Rack=800.xx. Ensures specific always overrides general. + +**Consumer API (NEW):** +- GET /api/v1/locations — list available location nodes (entitlement-filtered, filterable by resource_type, catalog_item, level, classification) +- GET /api/v1/locations/{uuid} — full detail including hierarchy, sovereignty, compliance, capacity +- ServiceRequest now accepts location_uuid or location_handle +- Consumer selects at any level; Placement Engine refines to specific DC at dispatch + +**Admin API (NEW):** +- GET/POST /api/v1/admin/location-types — manage location type registry +- GET /api/v1/admin/locations — all location nodes (no entitlement filter) +- PATCH /api/v1/admin/locations/{uuid} — update mutable fields (e.g., rack_units_available) + +**OpenAPI updates:** Consumer API now 63 paths; Admin API now 44 paths. LocationList, LocationSummary, LocationDetail schemas added to consumer API. + +**Placement Engine integration:** Location layers feed LOC-005 sovereignty enforcement (max_data_classification per DC), Step 1 sovereignty pre-filter, Step 3 capability filter, and Placement Policy expressions (input.payload.location.jurisdiction, etc.). + +**9 system policies: LOC-001 through LOC-009.** + +**Matrix:** 38 domains / 294 capabilities. + + +## SECTION 87 — REFERENCE DATA LAYERS + LAYER-REFERENCED FIELD CONSTRAINTS + +**Core clarification applied:** Layer data is the source of allowed values for resource type fields. This is not new architecture — it is the `layer_reference` constraint type made explicit throughout the documentation. + +**Pattern:** A field in a Resource Type Specification can declare `constraint.type: layer_reference` with a `layer_type` name. At catalog item render time, DCM resolves the active instances of that layer type into the `allowed_values` list. The consumer selects from that list; DCM injects the full layer data into the assembled payload. + +**Standard reference data layer types:** +- `location.data_center` — where resources can be placed (hierarchy: Country→Region→Zone→Site→DC→Hall→Cage→Rack) +- `os_image` — approved OS images (Platform Security team) +- `vm_size` — approved VM size profiles (Platform Team) +- `network_zone` — available network zones (Network Operations) +- `environment` — deployment environments (Platform Governance) +- `storage_class` — storage tiers (Storage Operations) +- `gpu_profile` — GPU configurations (Platform Team) + +**Governance model:** Reference Data Layer instances have IDENTICAL lifecycle, controls, security, and governance as Resource Types — `developing → proposed → active → deprecated → retired`, GitOps workflow, versioned, owned by declared authority, immutable once active. Adding a new approved OS image = adding a new `os_image` layer. No Resource Type Specification change needed. + +**Changes made this session:** + +doc 03 (layering): New Section 3.7 — Reference Data Layers. Defines the pattern, governance model, and YAML format for `os_image` and `vm_size` reference data layers. Updated Core Layers cross-ref. + +doc 05 (resource types): New Section 2.1b — Layer-Referenced Field Constraints. Explains why `layer_reference` is preferred over static enums. Complete table of standard layer types with owning authorities. Shows what the catalog item field constraint looks like when resolved (full `allowed_values` with structured data). Adds `layer_reference` and `layer_reference_list` to the field constraint type vocabulary, including `filter`, `display_field`, `value_field` sub-fields. + +consumer-api-spec.md Section 3.2: Catalog item field schema now shows four field examples: + - `cpu_count` — static `enum` constraint (unchanged) + - `os_image` — `layer_reference` to `os_image` type (resolved `allowed_values` with image metadata) + - `location` — `layer_reference` to `location.data_center` type (resolved with DC name, zone, certifications, capacity_status) + - `size` — `layer_reference` to `vm_size` type (resolved with CPU/RAM defaults) + Section 3.3 submit request shows layer UUIDs in the fields object with comments explaining DCM's resolution. + +doc 48 (location): New Section 0 — Pattern Context. Explicitly frames location as one application of the Reference Data Layer pattern. Section 8 (Consumer Selection Model) updated: location selection is via the catalog item field constraint, not a separate /locations endpoint. Shows how filter clause on the layer_reference constraint controls which DCs appear per catalog item. + +API changes: Removed standalone /api/v1/locations and /api/v1/locations/{uuid} consumer endpoints (wrong model — location is a field constraint, not a separate API). Admin API keeps /api/v1/admin/location-types and /api/v1/admin/locations (correct — admin manages the Reference Data Layer registry). Consumer API back to 61 paths. + + +## SECTION 88 — RESOURCE TYPE AUTHORITY + UNIFIED LAYER MODEL CLARIFICATION + +**Core clarification:** All of DCM's data — Resource Type Specifications, Reference Data Layers, Service Layers, provider extension layers — is built on the same layer model with the same lifecycle, governance, ownership, and security model. This was architecturally correct but not explicitly stated. + +**What was already in the architecture (confirmed, no changes needed):** +- Three-tier registry (DCM Core / Verified Community / Organization) — doc 20 +- Provider as Resource Type Publisher (doc 28 section 6.1) +- Resource Type Specification vs Catalog Item distinction (doc 05 section 2.1a) +- Portability classification (universal/conditional/provider-specific/exclusive) — doc 05 section 4 +- Provider-specific extension fields must be marked portability-breaking — doc 05 section 4 +- Layer domain model (system/platform/tenant/service/provider) — doc 03 section 4.1 +- Service Layers contributed by providers — doc 28 section 6.3 +- GitOps workflow for resource type proposals — doc 20 section 3.1 + +**What was added (4 targeted additions):** + +**1. Resource Type Authority — Stewardship Model (doc 05 new Section 2.1c):** +Every Resource Type Specification has a declared Resource Type Authority — the team responsible for defining, maintaining, evolving, and deprecating it. Same `owned_by` governance model as all other DCM artifacts. Required approver for all future version PRs. Standard authority assignments by category: Compute→Platform Team, Network→Network Ops, Storage→Storage Ops, Security→CISO, etc. Three tiers: DCM maintainers (Tier 1), named community maintainers (Tier 2), org domain teams (Tier 3). + +**2. Three-Way Field Constraint Model (doc 05 new Section 3a):** +Resource Type Authorities choose per field: +- Option 1 — layer_reference: valid values are active instances of a named layer type (location, OS images, network zones). Portable. Value governance delegated to the layer type's authority. +- Option 2 — provider-declared constraint: ad-hoc enum/range in the Resource Type Spec or Catalog Item. CPU counts, memory ranges, protocol versions. Portable if values are vendor-neutral. +- Option 3 — no constraint: free-form or provider judgment. Names, descriptions, provider-internal IDs. +Decision guide table included. Key point: VM size is Option 2 or 3 by default — not layer-referenced unless the org explicitly wants a governed size catalog. This is an organizational decision, not an architectural mandate. + +**3. Provider Extension Layers (doc 05 Section 6.2 extended):** +Providers can contribute extension layers (domain: provider) alongside their catalog item declaration — `provider_extension_layer_handles` field. These inject provider-specific fields during payload assembly only when that catalog item is selected. Cannot override platform/tenant layers. Same portability_breaking: true semantics as inline extensions. Resource Type Authority may adopt popular extensions as conditional fields in a future spec version. + +**4. Unified Layer Model Statement (doc 03 Section 3.7 + Section 4.1):** +Explicit statement that Resource Type Specifications ARE data layer artifacts. Same lifecycle, same governance, same GitOps workflow, same authority model, same domain access control. The Resource Type Registry is a specialized layer store. All DCM data — type definitions, reference data, service configuration, provider extensions — lives in this one unified model. + +**Authority vs Publisher clarification (doc 28 Section 6.1):** Resource Type Authority defines the spec; Service Provider publishes the catalog item implementing it. Often the same team. May be different: platform team defines Compute.VirtualMachine; Nutanix, VMware, and bare metal teams independently register catalog items implementing it. + + +## SECTION 89 — SECTION 9 EXAMPLES: RESOURCE TYPE + LAYER LIFECYCLE (dcm-examples.md) + +dcm-examples.md expanded from 2,189 to 3,381 lines. Section 9 added — 8 subsections showing +the complete lifecycle of VM and WebApp resources from layer definition through rehydration. + +**9.1 Layer Definitions:** Full YAML for all foundational layers — OS image (Platform Security Team), +location/DC (Data Center Operations), network zone (Network Operations), zone and country ancestor layers. +Each with complete artifact_metadata, owned_by, domain, data blocks. + +**9.2 Resource Type Specifications:** Complete `Compute.VirtualMachine` v2.1.0 spec showing: +- Universal fields: cpu_count/memory_gb/storage_gb (range constraints — intrinsic), os_image/location/ + network_zone/environment (layer_reference — governed lists) +- Conditional fields: high_availability, gpu_profile (layer_reference) +- Extension point declaration for provider hypervisor config +- Application.WebApp v1.0.0 spec: app_name/tier_level (static enum — intrinsic), + environment/location (layer_reference), web_replica_count/db_engine (range/enum). + +**9.3 Provider Catalog Items:** Two providers implementing Compute.VirtualMachine: +- Nutanix EU-WEST: portable (portability_class: portable), narrows CPU/RAM enums, + contributes Service Layer with AHV hypervisor defaults, backup policy, monitoring agent. +- VMware EU-WEST: non-portable (portability_class: provider-specific), contributes + Provider Extension Layer with vsphere_resource_pool/datastore_cluster/vmware_tools_version. + +**9.4-9.5 VM Request and Processing Pipeline:** Full 8-step trace from consumer submission through +intent capture → layer reference resolution → layer assembly (showing each contributing layer and field +provenance) → policy evaluation (GateKeeper, Validation, Transformation each shown) → placement → +requested state write (with provenance on every field) → dispatch → realized state. + +**9.6 WebApp as a Service Request:** Meta Provider orchestration: DB first, then 3 web VMs with +db_host injected from DB realization, then LoadBalancer with backend_pool injected from VM IPs. +Tier 1 GateKeeper policies enforcing HA, minimum replicas, LTM requirement. Environment layer +injecting production defaults (backup, TTL=null, approval tier, log retention). + +**9.7 VM Rehydration (DR failover DC1→DC2):** Shows location override in placement_constraints, +fresh layer resolution against DC2 layer chain (different cluster_uuid in Nutanix Service Layer), +certification gap warning (SOC 2 not at AMS-DC2), hostname preserved / FQDN updated, entity_uuid +preserved across DC move. Static Replace vs Rehydration contrast explained in pipeline steps. + +**9.8 WebApp Rehydration (standards refresh — no incident):** Rolling replacement pattern for Tier 1. +Shows: retired OS image → auto-upgrade Transformation policy substitutes RHEL 9.5; environment layer +v1.2 injects new log_retention_days=365 and vulnerability scanning; new Tier 1 GateKeeper bumps +replica count 3→4; DB unchanged (no OS dependency). Full audit record showing every field change, +its source layer/policy, and version. + + +## SECTION 90 — DOCUMENTATION CLEANUP PASS (2026-03) + +No prior implementations exist — DCM is at v1. All migration/backward-compat/update language +removed from documentation. Every doc is the authoritative first-version spec. + +**Removed entirely:** +- All 38 Active Development Notice blockquotes across every data model and specification doc +- `## 7. V1 Migration` section from doc 13 — V1 concept has no basis in first implementation; `v1_migration` ingestion_source type renamed `legacy_import`; `v1_identifier` → `legacy_identifier` +- `## 7. Migration from Current Constructs` section from doc 15 (universal groups) +- `## 9. Migration Path — Standalone SPA → RHDH` from RHDH spec — replaced with clean "Deployment Options" section (standalone_spa vs rhdh are configuration choices, not migration paths) + +**Reframed (concept kept, backward-looking framing removed):** +- doc 06 section 7a.6: "Updated table (supersedes 7.2)" → "Provider Lifecycle Events" +- doc 07: "dependency graph superseded by entity relationships" → scope cross-reference +- doc 08: "superseded by Universal Group Model" → "Related: see Universal Group Model" +- doc 09: "This document supersedes dependency graph concept" → plain cross-reference +- doc 13: "V1 migration and brownfield ingestion are the same" → "same ingestion model for all sources" +- doc 18: "outbound webhook superseded by Notification Model" → "one delivery channel within Notification Model" +- doc 23: "This model supersedes standalone webhooks" → "outbound webhooks are one delivery channel" +- doc 26: "Section 4 superseded by Governance Matrix" → scope statement +- doc 27: "This document supersedes Section 4 of doc 26" → cross-reference +- doc 11 (k8s): "Migration Path — Kubernetes-Native to DCM-Managed" → "Incremental Adoption" +- doc 34: "Client Migration Path" → "Version Upgrade Path"; "backward compat" → "version-compatible"; "deprecation window" → "until version is sunset" +- doc 15: "preserve backward compatibility with existing API consumers" → "for convenience" +- OIS spec: "during the deprecation window" → "until the OIS version is sunset" + +**Verification:** 12 stale patterns — all clean after cleanup pass. + +--- + +## SECTION 91 — SYNC AUDIT AND FULL RESYNC (2026-03) + +Full Hugo content sync performed. State before sync: +- 5 data model docs missing from Hugo entirely (43-provider-callback-auth, 44-kessel-evaluation, 45-consistency-review, 46-workload-analysis, 47-accreditation-monitor) +- 44 of 53 data model docs stale (primarily -5 line delta from Active Development Notice removal) +- 7 specs stale (dcm-examples, dcm-registration-spec, dcm-operator-interface-spec, dcm-rhdh-integration-spec, 11-kubernetes-compatibility, consumer-api-spec, dcm-operator-sdk-api) +- Capabilities Matrix and DISCUSSION-TOPICS stale +- AI prompt had one stale matrix reference: "36 domains / 281 capabilities" (from Provider Readiness Gates session before Accreditation Monitor domain was added) + +**After sync:** All 53 data model docs × 2 Hugo locations: in sync. All 14 specs: in sync. All top-level docs: in sync. AI prompt matrix refs updated to 37/287. + +--- + +## SECTION 92 — ARCHITECTURE GAPS ANALYSIS (2026-03) + +Systematic scan of all docs, schemas, and capabilities matrix. Summary: + +**Tier 1 — Spec gaps (documented capability, API endpoints missing):** +- Federation Admin API (doc 22 architecture complete; no OpenAPI paths for tunnel management, peer listing, trust posture) +- Scheduled/Deferred Requests maintenance-windows endpoints (doc 37 specifies them; not in admin YAML) +- Workload Analysis endpoints (doc 46 specifies GET /workload-profile and :analyze; not in consumer YAML) +- Accreditation Monitor contract-event and :configure-webhook endpoints (doc 47; not in admin YAML) + +**Tier 2 — Implementation gaps (no specification exists):** +- Cost Analysis component internal model (376 refs; no spec for how it calculates) +- Cross-region data replication model (multi-region assumed; consistency + sovereignty enforcement at replication layer unspecified) +- Secret zero / initial credential bootstrap (day-0 sequence has no spec; chicken-and-egg problem every deployer hits) +- Multi-tenancy at storage layer (row-level security, tenant-scoped encryption — not specified) +- Rate limiting implementation (policy references it; enforcement mechanics unspecified) +- Audit log hash chain implementation (tamper evidence concept documented; implementation not specified) + +**Tier 3 — Security posture gaps:** +- No threat model document (attack surfaces, adversary profiles, STRIDE analysis) +- No supply chain security spec (SBOM, provider package signing, operator container provenance) +- No secrets scanning spec for GitOps stores + +**Tier 4 — Experience gaps:** +- New tenant onboarding flow not specified +- Pre-request cost estimation UX not specified end-to-end +- Provider sandbox/test mode not specified +- Capacity forecasting model not specified +- SLA/SLO tracking not specified + +**Two ownership ambiguities:** +- Who issues operation_uuid — API Gateway or Request Orchestrator? +- Who owns the Credential Revocation Registry? + +--- + +## SECTION 93 — LIGHTSPEED INTERFACE CONCEPT + DISCUSSION TOPICS (2026-03) + +**DISCUSSION-TOPICS.md updated:** Item 6 added — "Universal Lightspeed Interface for Operations." + +Concept: a universal, high-velocity operational surface for all DCM actions regardless of provider, resource type, or lifecycle stage. Operations that currently require multiple tool hops, context switching, and approval interruptions should be expressible and executable in a single interaction. + +**Key design questions captured for future roadmap:** +- New GUI surface, CLI, AI agent interface, or all three? +- How does it relate to existing Web UI spec and Flow GUI spec? +- What does "lightspeed" mean operationally — sub-second execution, zero-confirmation for pre-approved patterns, predictive pre-staging? +- How does it interact with Authority Tier model — can it auto-route approval gates without interrupting operator flow? +- Is this the primary interface for the AIOps layer referenced in the README? + +**Status:** Concept only — no design work started. Future roadmap item. + + +## SECTION 94 — ALL ARCHITECTURE GAPS ADDRESSED (2026-03) + +All gaps from Section 92 gap analysis resolved. + +**Tier 1 — API Endpoint Gaps (all closed):** +- Federation Admin API: 6 new admin YAML paths including GET/POST /api/v1/admin/federation/peers, :set-trust-posture, :suspend, /routed-requests, GET/PATCH /api/v1/admin/federation/config. Schemas: FederationPeer, FederationPeerRegistration, FederationConfig. +- Scheduled Requests: 3 new admin paths for maintenance windows: GET/POST /maintenance-windows, GET/PATCH/DELETE /maintenance-windows/{uuid}, GET /maintenance-windows/{uuid}/scheduled-requests. Schemas: MaintenanceWindow, MaintenanceWindowCreate, MaintenanceWindowPatch. +- Workload Analysis: 2 new consumer paths — GET /resources/{uuid}/workload-profile, POST /resources/{uuid}/workload-profile:analyze. WorkloadProfile schema added. +- Accreditation Monitor: 3 new admin paths — :verify, :configure-webhook, /contract-event. +- Final counts: consumer 63 paths / 33 schemas; admin 57 paths / 27 schemas. + +**Tier 2 — Implementation Gaps (doc 49 — 49-implementation-specifications.md, 728 lines):** +- Rate Limiting (sec 2): Token bucket at API Gateway. Per-actor bucket, profile-governed parameters (60/min minimal to 600/min prod), PT120S TTL on state. 5 policies RLM-001 through RLM-005. +- Audit Hash Chain (sec 3): SHA-256 with canonical concatenation (0x1F separator). GENESIS anchor for chain start. Continuous per-write verification + periodic sweep (PT1H sovereign, PT12H prod). Chain resealing endpoint: POST /api/v1/admin/audit/entities/{uuid}:reseal-chain. +- Multi-Tenancy at Storage Layer (sec 4): GitOps=directory namespace, EventStream=per-tenant stream, Snapshot=PostgreSQL RLS + AES-256-GCM per-tenant key, Search=index namespace. Cryptographic tenant deletion via key revocation. 4 policies STI-001 through STI-004. +- Cross-Region Replication (sec 5): Per-store replication model. Sovereignty-aware routing enforced at replication layer. Lag monitoring with degraded/unavailable state transitions. Last-write-wins + vector clocks for conflict resolution. +- Secret Zero Bootstrap (sec 6): Bootstrap manifest with internal CA + pre-shared component credentials replaced by mTLS within PT5M of CA startup. credential management service takes CA key ownership on registration. Air-gapped options: embedded vault / operator passphrase / HSM. 4 policies BOOT-001 through BOOT-004. + +**Ownership Ambiguities (sec 7) — Both resolved:** +- operation_uuid: issued by API Gateway at ingress (operation_uuid == request_uuid). API Gateway writes initial Operation; Request Orchestrator updates via shared fast store. +- Credential Revocation Registry: owned by credential management service. Key: credential_uuid to revocation metadata. TTL: max(credential_ttl, P90D). Session Revocation Registry is separate — owned by Auth component. + +**Tier 3 — Security Posture (sec 8):** +- Threat model: 5 boundaries (Consumer Ingress, Provider Interface, Admin, Internal Component, Storage). Highest-risk: credential management service compromise and Internal CA compromise — mitigated by air-gapped credential management service and HSM-backed CA for sovereign profiles. +- Supply chain: provider OpenAPI spec signing (mTLS private key; rejected at GATE-SP-01 if unsigned), container image provenance via Sigstore/Cosign, GitOps secrets scanning, SBOM mandatory for fsi/sovereign. 5 policies SEC-001 through SEC-005. + +**Tier 4 — Experience Gaps (sec 9):** +- New Tenant Onboarding (sec 9.1): full sequence — entity, groups, quota, admin actor, Git namespace, audit stream, member invitations, onboarding_complete event. +- Pre-Request Cost Estimation UX (sec 9.2): catalog display then POST /cost/estimate with fields then dry_run: true for placement preview then actual submit. +- Provider Sandbox/Test Mode (sec 9.3): sandbox_mode: true in registration, excluded from production placement, explicit targeting via _test_context, graduation path to production. +- SLA/SLO Tracking (sec 9.4): SLO declared in Resource Type Specification (time_to_operational, uptime, drift_detection_latency), continuous measurement from audit records, breach events, consumer status endpoint, admin aggregate report endpoint. + + +## SECTION 95 — CONTINUED: MATRIX + SPEC CLEANUP (2026-03) + +**Capabilities Matrix updated:** 38 domains / 299 capabilities (was 294). +New capabilities added from doc 49 implementation specifications: +- OBS-006: SLA/SLO Declaration (Resource Type Spec + consumer status endpoint + admin report) +- OBS-007: SLO Breach Detection and Notification +- STO-007: Cross-Region Sovereignty-Aware Replication (routing enforced at replication layer) +- STO-008: Tenant-Scoped Storage Isolation (RLS + per-tenant stream + index namespace) +- STO-009: Tenant-Scoped Encryption for fsi/sovereign (AES-256-GCM, credential management service managed) +- ZTS-007: Provider OpenAPI Spec Signing (SEC-001, mandatory at GATE-SP-01) +- ZTS-008: GitOps Secrets Scanning (SEC-002, SECRETS_DETECTED rejection) +- ZTS-009: SBOM Declaration (SEC-003, mandatory fsi/sovereign) +- PRV-010: Provider Sandbox/Test Mode +- GOV-008: Tenant Onboarding Workflow + +**Admin API:** Added `GET /api/v1/admin/workload-analysis` — aggregate workload profile view across all tenants with archetype/confidence/resource_type filtering and archetype_distribution histogram. Admin API now: 57 paths / 27 schemas. + +**All 15 specification docs now have Document Status headers.** 7 specs updated: cncf-strategy, consumer-api-spec, dcm-admin-api-spec, dcm-flow-gui-spec, dcm-opa-integration-spec, dcm-operator-interface-spec, dcm-registration-spec. + +**Open markers: 0.** No TODO/FIXME, no Active Dev Notices, no superseded-by language, no V1 Migration references anywhere in the corpus. + +**Current state:** 55 data model docs / 15 specifications / 4 OpenAPI schemas / 38 domains / 299 capabilities / 97 prompt sections. + + +## SECTION 96 — PROJECT OVERVIEW DOCUMENT (project-overview.md) + +New canonical document added: `project-overview.md` (located at dcm-docs root and Hugo /docs/project-overview). + +**Purpose:** Single authoritative description of DCM for any audience — engineers, business stakeholders, executives, or community members encountering the project for the first time. Referenced from README and Hugo navigation as the first document to read. + +**Content:** +- What DCM Is: governing control plane above provisioning tools; not a deployment tool; the management plane that connects existing automation +- Architecture in one sentence + event loop diagram +- The Problem DCM Solves: 5 specific problems (fragmented ops, long TTM, private cloud gap, unreliable data, compliance overhead) +- What DCM Does: three abstractions in full — Data (4 states), Policy (7 types with table), Provider (11 types with table) +- What this enables: self-service consumer experience; standards enforced structurally +- Who Benefits: Consumers, Platform Engineers, Security/Compliance, SRE, Auditors, FinOps — each with specific value statement +- Where DCM Operates: deployment topology table (single-region/federated/hub-regional/sovereign), data sovereignty model, target environments with compliance frameworks listed +- Key Facts table: accurate current counts (55 docs, 15 specs, 299 caps/38 domains, 63 consumer paths, 61 admin paths) +- 9 Core Design Principles + +**README.md updated:** Accurate counts, new project-overview link as first doc in foundation table, all doc ranges updated (55 data model docs, 15 specs), AI prompt described as 98 sections. + +**Hugo updated:** +- Root _index.md: replaced thin 'About DCM' with full What/Who/Three Abstractions/Benefits layout; Active Dev Notice removed +- docs/_index.md: replaced Active Dev Notice with navigation cards including project-overview link +- architecture/overview.md: Active Development Notice removed +- /docs/project-overview.md: new Hugo page from project-overview.md + + +## SECTION 97 — HOW + ETHOS SECTIONS ADDED + +**project-overview.md expanded** from 164 to 327 lines. Two new sections added: + +**## How DCM Works** (5 subsections): +- The Event Loop: policy-driven event loop diagram showing event → Policy Engine → typed outputs → Providers/Data → new events +- The Request Lifecycle: 5-step numbered sequence from intent declaration through layer assembly, policy evaluation, dispatch, and ongoing lifecycle +- How Policy Replaces Hard-Coded Logic: why every business rule is a Policy artifact and what that means operationally +- How Providers Integrate: base contract + capability extension model; organizations wrap existing automation, not replace it +- How Data Sovereignty Is Enforced: structural property evaluated at every boundary; Governance Matrix always boolean; no scoring override + +**## Ethos** (5 subsections): +- Security Is the Baseline, Not a Feature: minimal profile = security with minimal overhead, not minimal security; secure path must also be easy path +- The Governed Path Must Also Be the Easy Path: self-service is the delivery mechanism for governance; if governed path is harder, teams route around it +- Compliance Is Constructed, Not Audited: audit evidence and provenance are structural products of operations, not reconstructed post-hoc +- The Architecture Should Be Easy to Implement and Extend: three-abstraction test; no core changes for new capabilities that fit Data/Provider/Policy +- No Silent Behavior: every operation produces an observable artifact; every state transition audited; every decision has typed output + +**README.md updated** with condensed How + Ethos sections: event loop summary, request lifecycle in one paragraph, four design priority ethos statements, links to full sections in project-overview.md. + + +## SECTION 98 — SUMMIT 2026 ROADMAP ALIGNMENT (2026-03) + +Source: DCM Technical Roadmap Summit 2026 presentation (Red Hat FlightPath Team). + +**Origin story from roadmap:** +- Initial inspiration: US Navy warships disconnected for months, needing consistent updates in short windows +- Summit 2025: Market gap identified for private cloud framework 'from Idea to Infrastructure' +- June 2025: Banking consortium workshop identified three needs: automated workload placement, automated DC rehydration, standards-based APIs +- Summit 2026: First public showcase of DCM Control Plane + +**Three Summit demo use cases:** +1. Datacenter Rehydration — show rehydration meta process (CIO persona: restore after ransomware/90-day redeploy cycle) +2. Intelligent Placement — show business logic via policies (CTO persona: metadata-driven infrastructure placement) +3. Application as a Service — show Meta Service Provider (App Owner persona: full execution lifecycle from code) + +**Three post-Summit use cases:** +1. Greening the Brownfield — discover unmanaged resources via Service Providers or CMDB import; lifecycle ownership transfer +2. New Data Center Deployment — bootstrap new DC; automated workload migration from legacy DC +3. Application Modernization — MTA integration; platform migration + code enhancement recommendations + +**MVP Roadmap phases:** +- March 1: API gateway + Catalog API + policy in pipeline + data stores + initial rules (control plane core only) +- April 1: Service Catalog Web UI + Service Providers (VM, OCP Cluster, Web App, X2Ansible, Networking, Firewall) + Rehydration trigger + Placement + Consumer API + SP integration specs +- May 1: On-prem deploy + external message bus + observability + 3rd party module integration + long-term storage + IDM integration + +**Demo pipeline (slide 17) maps exactly to DCM architecture:** +- App Request = Consumer POST /api/v1/requests +- Requested Store = Intent State (GitOps) +- Customize Region layer = Transformation Policy (Core Layer injection) +- Tier Region Policy (OPA Rego) = GateKeeper/Validation Policy (Mode 3 OPA) +- App Declaration = Requested State (assembled payload) +- Declared Store = Requested State Store +- Egress = Service Provider dispatch via Operator Interface +- VM Service Provider Mock API = mock Service Provider for demo + +**Architecture coverage: FULL.** All three demos and all three MVP phases are architecturally specified. + +**Outstanding implementation decisions (ordered by urgency):** +- Q6 (blocking): resource_uuid vs entity_uuid in Operation.metadata — must decide before code +- Q7 (blocking): expires_at (26 refs) vs valid_until (canonical) — fix before session model implementation +- Q1 (pre-March): Technology stack — recommended: NATS JetStream / custom Go API gateway / OPA sidecar / Gitea / PostgreSQL+Alembic +- Q8 (pre-March): Intelligent Placement is best March 1 demo candidate (pipeline-only, mock SP) +- Q2 (pre-April): Mock providers confirmed by slides (VM Service Provider Mock API explicit) +- Q3 (pre-April): ACM — treat as Information Provider (feeds cluster inventory), not Service Provider +- Q4 (pre-April): X2Ansible — needs resource type spec for AAP job template/workflow as DCM resource +- Q5 (pre-April): Firewall — handle as FirewallRule sub-resource of Networking Meta Provider +- Q9 (pre-April): RHDH vs Consumer GUI — RHDH is likely Summit target (Red Hat branding on slides) +- Q10 (pre-April): 'Data Center Pipeline' = RHDH scaffolding sitting above DCM Consumer API; consistent with RHDH integration spec + + +## SECTION 99 — IMPLEMENTATION DECISIONS + EXAMPLE #1 (2026-03) + +**Field name decisions (final):** +- resource_uuid: KEEP (AEP standard) — Operation.metadata already correct; entity_uuid is correct everywhere else in the model +- expires_at: KEEP (valid_until replaced) — 77 refs across 20 files + 1 OpenAPI schema replaced; valid_until is now gone from corpus + +**Technology stack (Red Hat sanctioned open source):** +- Event bus: AMQ Streams (Apache Kafka on OpenShift) — Kafka CR via AMQ Streams operator +- API Gateway: Custom Go service — thin, controllable for demo; Service Mesh handles mTLS +- Policy engine: OPA (Open Policy Agent) — Mode 3 sidecar + standalone Deployment for complex policies +- Git server: GitLab CE (self-hosted) — Intent Store, Requested Store, Policy Store +- Database: PostgreSQL — CrunchyData PGO operator +- Service Mesh / mTLS: OpenShift Service Mesh (Istio/Envoy) — STRICT PeerAuthentication across all namespaces +- Secret Management: HashiCorp Vault + External Secrets Operator +- Auth / IDM: Keycloak (Red Hat SSO) — OIDC; DCM Auth Provider implementation +- Observability: OpenTelemetry + Prometheus + Grafana (OpenShift Monitoring stack) +- Front End: RHDH (Red Hat Developer Hub / Backstage) + DCM plugin +- Deployment automation: Ansible Automation Platform +- Certificate management: cert-manager + +**Provider decisions:** +- Real providers (not mocks) — exercises architecture portability +- VM as a Service: DCM → AAP → KVM/libvirt or OpenStack Nova +- Network Port: DCM → Netbox (IPAM) or OpenStack Neutron +- OCP Cluster: DCM → ACM Shim (Go service) → ACM API → ClusterDeployment (Hive) +- ACM also as Information Provider: feeds cluster inventory/capacity into placement engine +- Web App: Meta Provider composing VM + Network Port + OCP Cluster (optional) +- X2Ansible: deferred. Firewall: deferred. + +**New document: implementations/example-1-summit/IMPLEMENTATION.md** (1029 lines) +Contents: +- Purpose and scope (3 Summit demos + portability validation goal) +- Technology stack table (all Red Hat sanctioned choices) +- Architecture overview ASCII diagram (3 namespaces: dcm-system, dcm-providers, dcm-infra) +- Provider specs: VM, Network Port, OCP Cluster (ACM Shim), Web App Meta Provider +- All 3 demo use case flows: Intelligent Placement, DC Rehydration, App as a Service +- Namespace + Pod design (API Gateway with OPA sidecar, Orchestrator, Policy Engine, Provider pods) +- Full OpenShift file structure (ansible/ + openshift/ + config/ directories) +- Key Kubernetes resources: Kafka topics, Istio mTLS PeerAuthentication, ExternalSecret CRs +- Configuration files: dev profile YAML, Tier Region Policy Rego (matches slide 17 exactly), VM catalog item YAML +- Ansible playbooks: site.yml master, group_vars, Kafka role task, provider registration task +- Portability notes: what changes vs what doesn't when providers are replaced +- Demo seed data: tenants, users, resource types, catalog items, Core Layers, sample policies + +**Namespaces:** dcm-system (control plane), dcm-providers (all providers), dcm-infra (supporting infra) +**Deployment:** Ansible site.yml → prerequisites → infra → dcm-control-plane → dcm-providers → dcm-demo-data + +## SECTION 100 — EXAMPLE IMPLEMENTATION #1 — SUMMIT DEMO (2026-03) + +New artifact: `implementations/example-01-summit-demo/` — 57 files / 5,683 lines. +Labeled as Example Implementation #1. Isolated from core architecture docs. +Purpose: validate DCM architecture/data model and demonstrate portability. +Providers built here may be replaced — this is an exercise in the Provider contract. + +**Implementation decisions applied:** +- `resource_uuid` in Operation.metadata (AEP convention — kept as-is, already correct) +- `expires_at` for token/session expiry (already canonical in schemas — 0 occurrences of valid_until) +- Technology: AMQ Streams (Kafka), GitLab CE, PostgreSQL (CrunchyData PGO), OPA sidecar, Keycloak, Vault, OpenShift Service Mesh, RHDH +- All Red Hat sanctioned OSS where possible + +**Directory structure:** +- `openshift/` — 27 YAML manifests: namespace, RBAC, all control plane deployments, all provider deployments, storage, auth, vault, service mesh, RHDH, monitoring +- `ansible/` — 12 files: site.yml master playbook + inventory + 10 roles (operators, storage, auth, vault, service-mesh, control-plane, providers, rhdh, seed-data) +- `config/` — 13 files: PostgreSQL schema (RLS + hash chain tables), OPA policies (Rego), Keycloak realm, DCM profile/layers/catalog-items +- `docs/` — 5 files: README, ARCHITECTURE mapping, deployment guide, demo script, provider dev guide + +**Control plane (8 Go services, each a separate container/Deployment):** +dcm-api-gateway, dcm-request-orchestrator, dcm-policy-engine, dcm-placement-engine, dcm-request-processor, dcm-audit, dcm-catalog, dcm-discovery + +**Storage stack:** PostgreSQL (Snapshot Store + Operations + Audit), GitLab CE (GitOps Store), AMQ Streams/Kafka (Event Stream), OpenSearch (Search Index) + +**Service Providers (5):** +- dcm-provider-vm: wraps AAP/Ansible for VM provisioning (OIS Level 2) +- dcm-provider-ocp-cluster: wraps RHOCP/CAPI (OIS Level 2) +- dcm-provider-network: wraps AAP for network ports/VLANs (OIS Level 2) +- dcm-provider-acm-shim: shim wrapping ACM API as standard DCM provider; ClusterRole grants ACM ManagedCluster/Placement/ManifestWork access +- dcm-provider-webapp: Meta Provider composing VM + Network + OCP Cluster (OIS Level 1; sequential with rollback) + +**OPA Rego policies (Summit demo):** +- tier-region.rego: GateKeeper (compliance) — enforces zone ⊆ tier allowed zones. Matches slide 17 demo exactly. +- vm-sizing.rego: Validation (structural) — CPU/RAM/storage bounds by environment +- placement-weights.rego: Transformation — 5-signal scoring (capacity 35%, affinity 10%, cost 20%, perf 20%, risk 15%) + +**Security posture:** +- STRICT mTLS via OpenShift Service Mesh across all components (Boundary 4) +- Per-component ServiceAccounts with minimum RBAC +- Vault: per-provider policies (vm provider can only read dcm/data/providers/vm/*) +- PostgreSQL RLS: dcm_app role cannot query across tenant boundaries +- Keycloak PKCE for RHDH consumer authentication +- Audit records: append-only enforced by SQL trigger + REVOKE UPDATE/DELETE + +**Demo script covers 3 use cases:** +1. Intelligent Placement (March 1): tier-region.rego enforces zone policy; pipeline visible in RHDH +2. Datacenter Rehydration (April 1): simulate DC loss; :rehydrate endpoint replays Intent through current policies +3. Application as a Service (April 1): single request → webapp Meta Provider sequences Network + VM + OCP Cluster + +**Deploy single command:** `cd ansible && ansible-playbook site.yml -i inventory/hosts.yml` +**BOOT-002:** Bootstrap admin password CHANGE_ME_BOOT002 must be rotated on first login. + + +## SECTION 101 — FULL SYNC COMPLETE (2026-03) + +Complete documentation sync performed. All artifacts now in sync. + +**Current corpus state:** +- Data model: 55 documents (00-foundations through 49-implementation-specifications + A-provider-contract + B-policy-contract) +- Specifications: 15 documents (all with Document Status headers) +- OpenAPI schemas: consumer 63 paths/33 schemas, admin 57 paths/27 schemas, operator 5 paths/12 schemas, callback 7 paths/11 schemas +- Capabilities Matrix: 38 domains / 299 capabilities (verified: summary count matches row count) +- AI Prompt: 103 sections, 0 duplicates +- Open markers (TODO/FIXME/Active Dev Notice/superseded-by/V1 Migration): 0 +- Example Implementation #1: 57 files / 5,683 lines + +**Hugo website state (fully synced):** +- /docs/data-model: 55 files +- /docs/architecture/data-model: 109 files (55 source + 54 duplicates from earlier build — source is /docs/data-model) +- /docs/architecture/specifications: 24 files (15 specs + 4 OpenAPI YAMLs + indexes) +- /docs/implementations/example-01-summit-demo: 5 pages (README, architecture mapping, deployment guide, demo script, provider dev guide) +- Active Development Notices: removed from all files (3 remaining instances cleared in this sync) +- Navigation: root _index, docs _index, architecture _index, implementations _index all updated with current content + +**README.md (135 lines):** accurate counts, implementation section added, AI prompt section count updated to 103. + +**project-overview.md (328 lines):** accurate counts, reference implementation entry added to Key Facts table. + +**DISCUSSION-TOPICS.md:** 6 items including Lightspeed Interface concept (item 6) and Kessel evaluation (item 4). + +**Implementation status:** Example #1 fully specified. Architecture fully ready for implementation. Remaining pre-coding decisions: event bus tech, API gateway tech, policy engine runtime, Git server — all documented in prompt section 99 (Summit Roadmap Alignment). All field names resolved (resource_uuid per AEP, expires_at for token expiry). + + +## SECTION 102 — WORKING INSTRUCTIONS FOR AI MODELS + +When working on this project, apply these instructions in addition to the numbered guidance in SECTION 60 (Documentation Structure): + +172. **DCM defaults to federated data creation** — platform admins are not the only contributors; consumers author tenant-domain policies; providers publish resource type specs and service layers; peer DCMs contribute registry entries; all via GitOps PR with profile-governed review +173. **Contributor domain scope is hard DENY at submission** — consumers cannot contribute system/platform policies regardless of declared domain; providers cannot contribute specs for types they don't offer; enforced by Governance Matrix at contribution time (FCM-002) +174. **All contributed policies enter shadow mode by default** — proposed status with shadow evaluation before activation; shadow_review_period is profile-governed (P7D standard → P30D fsi/sovereign); platform admin reviews divergence cases before promoting +175. **Orphaned artifacts do not auto-deactivate** — when contributor's access is revoked, their active artifacts remain active until platform admin assigns new owner or explicitly retires; exception: sovereign profile auto-retires orphaned artifacts (FCM-006) +176. **GateKeeper enforcement_class is required and fail-safe** — if omitted, treated as compliance (boolean deny). Operational-class GateKeepers never halt the request; they contribute a weighted risk_score_contribution to the aggregate. The aggregate risk score determines approval routing, not individual policy outcomes. +177. **Validation output_class is required and fail-safe** — if omitted, treated as structural (boolean halt). Advisory-class Validations never halt requests; they accumulate completeness score and warning list surfaced to the consumer. +178. **Governance Matrix is always boolean — never scored** — SMX-004 is absolute. Scoring cannot be used to route around data sovereignty or regulatory boundaries. The Governance Matrix evaluates before the scoring pipeline runs. +179. **Profile thresholds determine routing, not individual policies** — the approval routing decision (auto/review/dual/authorized) emerges from the aggregate risk score crossing profile-configured thresholds, not from individual policy flags. Changing governance sensitivity = adjusting thresholds in the profile. +180. **SMX-008 is a hard system constraint** — auto_approve_below may never exceed 50 in any profile. Platform admins cannot override this. Profiles submitted with auto_approve_below > 50 fail validation. +181. **Meta Provider is a compound service definition + standard Service Provider** — not an orchestrator. It declares the dependency graph so DCM can place, sequence, and govern constituents. For `self` constituents it executes as any Service Provider does. DCM handles all orchestration, placement, failure, and compensation. +182. **Composite Entity has ONE entity UUID** that links Intent, Requested, Realized, and Discovered states; the UUID is assigned at Intent creation and is stable throughout the lifecycle including rehydration +183. **DEGRADED is a valid terminal state** — not an error; a DEGRADED entity enters standard OPERATIONAL lifecycle; profile governs whether degraded delivery is accepted; Recovery Policy governs failure/compensation decisions +184. **Parallelism emerges from the dependency graph** — constituents with no unresolved dependencies dispatch concurrently within DCM's pipeline; the Meta Provider does not manage this +186. **Credential values are NEVER stored in DCM** (CPX-001) — only metadata is stored; values are held by the credential management service; retrieved via authenticated endpoint; this applies to ALL credential types including dcm_interaction credentials +187. **Every provider dispatch requires a scoped interaction credential** (CPX-002) — issued before dispatch, scoped to the specific operation+entity+provider, expires PT15M; provider must validate at use time not just receipt; check revocation cache on each use +189. **Security properties are present in ALL profiles — minimal profile is "security with minimal operational overhead" not "minimal security"** — rotation required in all profiles (minimal: P365D max, manual OK); idle detection on in all profiles (minimal: P30D); algorithm baseline in all profiles (minimal: forbidden list); CPX-001 (values never in DCM stores) is absolute — homelab (minimal) uses bearer_token retrieval, no scheduled rotation, no FIPS; sovereign uses mtls+hardware attestation, FIPS Level 3, PT15S revocation cache; same API contract, same data model, same CPX-001 (values never in DCM stores) +196. **API versioning is per-surface not per-endpoint** (VER-001) — Consumer, Admin, Provider/OIS, Flow GUI each have their own major version; all endpoints within a surface share the version; when in doubt whether a change is breaking, it is (VER-002); prod support window is 2 years deprecated after 1 year notice; sovereign is 4 years deprecated after 2 years notice; deprecated versions return Deprecation + Sunset headers (RFC 8594/RFC 9745) +197. **Consumer API has 16 sequential sections (reorganized)** — sections were renumbered 1–16 in logical order: Auth(2), Catalog(3), Requests(4), Resources(5), Drift(6), Groups(7), Approvals(8), Cost(9), Notifications(10), Search(11), Audit(12), Errors(13), Contributions(14), Credentials(15), Conformance(16); old 5b/5c/6b/7b numbering is gone +198. **Admin API base URL is /api/v1/admin/ (version-first)** — NOT /admin/api/v1/; all 40 admin endpoints use /api/v1/admin/; this is the authoritative form used everywhere in the specs and data model docs +199. **Consumer API has idempotency (1.5), rate limiting (1.6), request IDs (1.7), and standard envelope (1.8)** — POST requests support Idempotency-Key header (PT24H retention); rate limits are profile-governed (60/min minimal → 600/min sovereign) with Retry-After on 429; all list responses use {"items":[...],"total":N,"next_cursor":"..."} envelope; X-DCM-Request-ID and X-DCM-Correlation-ID on all responses +200. **OIS health check response is normative (not optional)** — providers MUST return {status: pass|warn|fail, version, dcm_registration_status}; missing/malformed body = warn; 3 consecutive non-200 = provider.unhealthy event; response format follows RFC 8615 / IANA health+json +201. **Doc 35 (35-session-revocation.md) is complete** — session lifecycle: intent→requested→realized store model; AUTH-016 (deprovisioning revokes sessions AND credentials in parallel); AUTH-017 (revocation SLA: PT5M minimal → PT5S sovereign); AUTH-018 (ALL components check revocation registry on every bearer token request — no exceptions); AUTH-019 (emergency revocation = critical urgency, non-suppressable); AUTH-020 (introspection endpoint authenticated); AUTH-021 (oldest session revoked on concurrent limit breach); AUTH-022 (refresh tokens invalidated on parent session revocation); session endpoints: DELETE /api/v1/auth/session, DELETE /api/v1/auth/sessions, GET /api/v1/auth/sessions, DELETE /api/v1/auth/sessions/{uuid}; admin: POST /api/v1/admin/actors/{uuid}/revoke-sessions +202. **Doc 36 (36-internal-component-auth.md) is complete** — ICOM-001 (all internal calls mTLS); ICOM-002 (scoped interaction credential required IN ADDITION to mTLS on every call); ICOM-003 (unauthorized source → 403 + audit); ICOM-004 (components may only call declared allowed_targets); ICOM-005 (all internal calls audited); ICOM-006 (P90D max cert validity); ICOM-007 (bootstrap tokens one-time-use, PT1H max); ICOM-008 (compromised cert → CRL immediately); ICOM-009 (Internal CA root in all trust stores at deploy); component communication graph is declared and enforced — not implicit; every call: mTLS cert (transport identity) + ZTS-002 interaction credential (operation authorization) +203. **Session revocation and internal component auth complete the zero trust model** — external boundary: provider↔DCM uses mTLS + scoped credentials (CPX-001–CPX-012); internal boundary: component↔component uses Internal CA mTLS + ZTS-002 interaction credentials (ICOM-001–ICOM-009); actor sessions: Auth Provider issues tokens; Session Revocation Registry checked on every request (AUTH-018); credentials: credential management service manages values that never touch DCM stores (CPX-001); together these four surfaces cover the complete trust boundary + +201. **35-session-revocation.md (AUTH-016–AUTH-022)** — session revocation and credential revocation are PARALLEL on actor deprovisioning (not sequential); revocation registry must be checked on EVERY request by ALL components; sovereign profile: no revocation registry cache; emergency revocation (security_event) is critical urgency + non-suppressable; refresh tokens are invalidated when parent session is revoked (AUTH-022) +202. **36-internal-component-auth.md (ICOM-001–ICOM-009)** — network position grants ZERO trust for internal calls — same five-check boundary model as external; every internal call requires BOTH mTLS cert AND ZTS-002 interaction credential; bootstrap tokens are one-time-use PT1H max; unauthorized source component → 403 + high-urgency audit; component certs from Internal CA only, max P90D, never external CA +203. **SES and ICOM domains added to capabilities matrix** — matrix is now 177 capabilities across 28 domains; SES-001–SES-005 (session lifecycle, deprovisioning, emergency revocation, introspection, concurrent enforcement); ICOM-001–ICOM-005 (mTLS, bootstrap, call authorization, interaction credentials, cert revocation) +204. **Domain prefix totals now 28** — IAM CAT REQ PRV LCM DRF POL LAY INF ING AUD OBS STO FED GOV ACC ZTS GMX DRC FCM SMX MPX CPX DPO ATM EVT VER SES ICOM; README and taxonomy both updated to 177/28 +205. **Doc 37 (Scheduled Requests): dual policy evaluation** — GateKeeper runs at declaration AND at dispatch; dispatch-time rejection = FAILED not retried; schedule field is optional addition to existing POST /api/v1/requests body; SCHEDULED status is cancellable; not_after deadline miss = terminal FAILED (SCH-005) +206. **Doc 38 (Request Dependency Graph): distinct from type-level and Meta Provider deps** — consumer-declared ad-hoc ordering for independent requests; POST /api/v1/request-groups; PENDING_DEPENDENCY status counts against quota at submission not dispatch; max 50 requests per group; circular deps → 422 at submission; field injection passes realized outputs into dependent request fields automatically +207. **Doc 39 (DCM Self-Health): three endpoints, different purposes** — /livez (liveness, PT5S max, no external calls, Kubernetes restarts pod on fail) vs /readyz (readiness, checks 5 core dependencies, Kubernetes removes from LB) vs /api/v1/admin/health (per-component detail, admin auth required, Prometheus metrics at /metrics); all follow RFC 8615 / IANA health+json +208. **Capabilities matrix now 189 across 31 domains** — SES(5) ICOM(5) SCH(4) RDG(4) HLT(4) added; domain prefixes: IAM CAT REQ PRV LCM DRF POL LAY INF ING AUD OBS STO FED GOV ACC ZTS GMX DRC FCM SMX MPX CPX DPO ATM EVT VER SES ICOM SCH RDG HLT (31 total) +209. **40-standards-catalog.md is the authoritative source for all DCM standards** — forbidden algorithms (MD5, SHA-1, DES, 3DES, RC4, RSA<2048) are prohibited in ALL profiles with no exceptions; TLS 1.0/1.1 prohibited in ALL profiles; ECDSA P-384 is the mandated algorithm for Internal CA certs; AAL mapping: minimal/dev=AAL1, standard/prod=AAL2, fsi=AAL2+, sovereign=AAL3; doc 40 Section 8 maps every standard to the docs that use it +209. **External CAs belong in the credential management service (NOT Auth Provider)** — Auth Provider authenticates identity; credential management service manages credential lifecycle; External CAs (Vault PKI, Venafi, EJBCA, AWS ACM PCA) are x509_certificate credential management services using ACME(RFC 8555)/EST(RFC 7030)/SCEP/CMP protocols; ICOM-009 updated — trust anchor is any registered CA root, not just built-in Internal CA; doc 36 profile table: cert lifetime P180D(minimal) → P14D(sovereign); sovereign requires HSM-backed certs if hardware_attested posture +210. **Live updates: SSE stream + OIS interim status** — GET /api/v1/requests/{uuid}/stream (text/event-stream, closes on terminal status) for browser/CLI without polling; events: status_change, progress_updated, approval_required, approval_recorded, heartbeat(30s); OIS providers POST /api/v1/provider/entities/{uuid}/status for interim progress with step_current/step_total/constituent_status; request.progress_updated event added to doc 33; rate-limited: max 1 interim status per 10s per entity +211. **Profile coverage added to docs 36-39** — doc36 cert lifetime table(P180D minimal→P14D sovereign), algorithm min table; doc37 max scheduling horizon(P365D minimal→P7D sovereign), concurrent scheduled limit, maintenance window approval tier; doc38 max group size(100 minimal→5 sovereign), group timeout max, field injection validation strictness, nesting depth; doc39 metrics scraping restrictions(sovereign internal only), /api/v1/admin/health MFA requirements(fsi/sovereign) +212. **40-standards-catalog.md is the authoritative standards reference** — 19 RFCs, 3 cryptographic standards tables (permitted algorithms, forbidden algorithms, FIPS levels), 6 compliance frameworks, 7 CNCF ecosystem projects, W3C SSE, OpenAPI 3.1, SPIFFE (informative), HashiCorp Vault PKI / Venafi / EJBCA as External CA credential management service backends (NOT Auth Providers); Section 9 maps all 17 policy families to their standards basis; usage map tracks which standards appear in which documents +213. **41-operational-reference.md covers the three operational gaps** — GitOps partitioning (3 strategies; tenant-shard recommended; trigger thresholds table), store migration (5-phase dual-write playbook; never decommission source before burn-in; audit chain continuity required OPS-002), DR (5 scenarios; post-recovery validation checklist mandatory OPS-005; Audit Store minimum P365D retention all profiles OPS-006); RTO: PT1M sovereign component → PT24H minimal repave +214. **Three GUI specs define the unified DCM web application** — one application with role-gated surfaces: Consumer Portal (all actors — catalog, SSE live status with constituent tracking, resources, approvals, cost, sessions), Admin Panel (platform roles — health dashboard, tier registry drag-drop editor with hard-stops, scoring slider max 50), Provider Management (provider_owner role — 5 provider types with common shell + type-specific tabs; Credential management shows metadata never values; algorithm compliance must show zero forbidden algorithm violations) +215. **Doc 42 adds 12th provider type (ITSM integration) and 8th policy type (ITSM Action Policy)** — ITSM is ADDITIVE (DCM never requires it); ITSM Action Policy is side-effect only (non-blocking default); block_until_created requires block_timeout (never permanently stalls pipeline, ITSM-005); recorded_via field already existed on approval vote — ITSM inbound approvals use it; 6 policy examples covering ServiceNow + Jira for provisioning, CMDB sync, incident on drift, decommission; itsm.* event domain adds 3 events (85 total, 21 domains) +215. **Consumer GUI ITSM bridge (GUI-011, section 8 of dcm-consumer-gui-spec.md)** — ITSM is a CONSUMER of DCM events, not a source of truth; DCM is the system of record; CMDB sync is one-way DCM→CMDB via notification service subscription to entity.* events; ITSM approval votes call POST /api/v1/admin/approvals/{uuid}/vote — the CAB process happens in ITSM, DCM just records the outcome; ITSM references stored as business data fields on entities; Audit Trail (section 11) is consumer-scoped own-resource view; cross-tenant audit is Admin Panel only +215. **RHDH/Backstage is the PRIMARY consumer GUI deployment model** — 6 Dynamic Plugin packages (no RHDH rebuild); Scaffolder IS the request form (auto-generated templates from catalog item JSON Schema); DCMService + DCMResource entity kinds sync to RHDH catalog every PT5M; tenancy via RHDH Group context → X-DCM-Tenant; OIDC token exchange for auth delegation; PatternFly NavGroup/NavItem/NotificationBadge for sidebar; Approvals shows live pending count badge; all compliance enforced by DCM control plane — RHDH is a client +195. **33-event-catalog.md is the SINGLE authoritative source for all DCM event types** — 82 events across 26 domains; all events share the base envelope (event_uuid, event_type, event_schema_version, timestamp from Commit Log, urgency, payload, links); consumers implement idempotency using event_uuid; critical urgency events are non-suppressable; non-standard events use reverse-DNS prefix; event_schema_version only increments on breaking changes +194. **Tier registry changes are gated by impact detection** — any change that creates a SECURITY_DEGRADATION (tier gravity or position decreased) blocks activation until each degradation is explicitly accepted by a verified-tier or above reviewer via Admin API; BROKEN_REFERENCE also blocks; PROFILE_GAP is a warning that does not block; all changes produce an impact report in the Audit Store (ATM-009–012) +193. **Authority tiers are named positions in an ordered list — not fixed enum values** — tier weight derived from list position at evaluation time; organizations insert custom tiers between existing ones without breaking existing name references; 'authorized' tier always means 'highest current gravity' regardless of what's been inserted before it; ATM-001: never hardcode tier weights +192. **DCM provides the approval gate and audit trail — the review process is the organization's responsibility** — for authorized tier: DCM tracks quorum of a DCMGroup; the authorized deliberation and vote collection happen outside DCM; external systems (ServiceNow, Jira, Slack bots) can call Admin API to record votes; DCM does NOT build authorized management; for reviewed and verified: same principle — DCM holds the pipeline until the API receives the required decisions +191. **The priority order is a decision framework, not a suggestion** — when security and ease of use conflict, security wins AND you must design an easy mechanism for the secure path; "it's too complex" is a reason to improve the ease-of-use design, not to reduce security; "minimal profile" means minimal overhead, never minimal security (DPO-005, DPO-006) +190. **key_usage is declared at issuance and validated at use** (CPX-009) — a credential issued for authentication cannot be used for signing; credential management service must validate this at the validate endpoint; prevents algorithm confusion attacks +188. **Actor deprovisioning and entity decommissioning trigger immediate credential revocation** (CPX-006, CPX-007) — deprovisioning publishes revocation events before the deprovisioning is acknowledged; decommission is blocked until all entity-scoped credentials are revoked +185. **provided_by: external constituents are placed by DCM's Placement Engine** — all governance controls (sovereignty, accreditation, trust) apply; the Meta Provider has no influence over external constituent provider selection + +--- + +## Session: Architecture Consistency and Policy Model (April 2026) + +**Summary:** Infrastructure consolidated to 1 required dependency (PostgreSQL). Auth, secrets, and events handled internally by default with optional external delegation. Policy evaluation model expanded with Evaluation Context, Constraint Type Registry, and Policy Templates. Full consistency pass across all 58 data model docs and 15 specifications. + +216. **One required infrastructure: PostgreSQL** — Authentication (local accounts + argon2id + DCM-issued JWT), secrets (envelope encryption with AES-256-GCM + KEK from environment), and event routing (LISTEN/NOTIFY) are all handled internally. External systems (OIDC IdP, Vault, Kafka, Redis, Git, service mesh) are optional deployment enhancements registered through the standard provider contract. + +217. **Internal/External pattern applied three times** — Policy evaluation (Internal: OPA evaluates; External: external provider evaluates). Secrets management (Internal: PostgreSQL secrets table + envelope encryption; External: Vault-compatible API). Authentication (Internal: actors table + argon2id + JWT; External: auth_provider — OIDC, SAML, LDAP). Same pattern, consistent across all three domains. + +218. **Data-driven policy matching** (doc B §2) — A policy fires when the data says it should fire. Any field from three sources (request payload, evaluation context, entity metadata) can be a match trigger. No pre-assignment of policies to resource types. Specificity ranges from universal (no conditions) to fully scoped (VMs in DMZ in zone A for application UUID XXXXX). + +219. **Evaluation Context with multi-pass convergence** (doc B §7) — Transient constraint space scoped to one request evaluation. Three phases per pass: collect constraints, resolve conflicts, apply and validate. Hard/soft constraint binding. Auto-resolution via declared `on_conflict` strategies. Maximum 3 passes (configurable). Unresolvable hard-hard conflicts escalate to human. Per-pass audit captures complete context snapshots. + +220. **Constraint Type Registry** (doc B §8) — Shared vocabulary for the Evaluation Context. Each constraint type has a canonical name, OpenAPI v3 schema, emittable_by/consumable_by declarations. 10 built-in types (zone_restriction, distribution_requirement, cost_ceiling, network_restriction, resource_limits, compliance_requirement, sovereignty_boundary, approval_requirement, scheduling_constraint, provider_restriction). Organizations register custom types. Validation at policy activation catches vocabulary mismatches at authoring time. + +221. **Policy Templates** (doc B §9) — Gatekeeper ConstraintTemplate pattern adapted for DCM. Templates define reusable Rego logic with parameter schemas and declared emitted/consumed constraint types. Policy artifacts are instances binding parameters and match conditions. DCM auto-generates `data.dcm.constraint_types` Rego library from the Constraint Type Registry — constructor functions enforce schemas at compile time. + +222. **Doc 11 rewritten** as "Data Store Contracts" (791→154 lines) — Enforcement rules for the four PostgreSQL data domains (append-only, RLS, hash chain, REVOKE grants). No storage provider abstraction. + +223. **Doc 31 rewritten** as "Credential Management" (979→154 lines) — Internal secrets via PostgreSQL envelope encryption (default). External via Vault-compatible API (optional). Consumer-facing credentials handled by service_provider with Credential.* resource types. + +224. **Doc A §7 rewritten** as 5 provider types (556→387 lines) — Removed Storage, Policy, Credential, Notification, Message Bus, Registry, ITSM as separate types. Added Process Provider (ephemeral workflow execution). Service Provider description updated to cover Credential.*/Notification.*/ITSM.* resource types. + +225. **Doc 14 §4 rewritten** — "External Policy Evaluators" (4 modes) → "Policy Evaluation Modes" (Internal/External). BBQ-001–009 governance preserved for External mode. Mode numbering removed. + +226. **Doc 02 §4 rewritten** — "data store Model" (old terminology) → "Data Domain Model" with PostgreSQL table enforcement. Git as optional ingress adapter, not a state store. + +227. **Capabilities matrix updated** — 309→331 capabilities. Added: POL-008 (Constraint Type Registry), POL-009 (Evaluation Context), POL-010 (Policy Templates), POL-011 (DCM Constraint Types Library), POL-012 (Data-Driven Matching), STO-010 (Internal Secrets), STO-011 (Pipeline Event Routing), STO-012 (Internal Authentication). Domain 13 (Storage) rewritten for PostgreSQL. Domain 23 renamed to Credential Management. + +228. **Bulk consistency pass** — Zero old provider type references remain outside AI prompt historical sections. Zero old store model references. Zero old policy mode references (Mode 3/4) in policy contexts (information provider retrieval modes correctly retained). All "3 required infrastructure" / "Vault required" / "Keycloak required" references updated. + +229. **Unified audit and payload integrity via Merkle tree** (doc 16 §8) — RFC 9162 (Certificate Transparency v2.0) pattern. Each audit record is a Merkle leaf with input/output payload hashes, service signature, and tree position. Inclusion proofs (O(log n)), consistency proofs, and request chain verification unify audit trail integrity and payload chain of custody into one mechanism. Satisfies NIST 800-53 AU-9(3), AU-10(2/3/5), SI-7; FIPS 186-5 (Ed25519); FedRAMP High AU-10. + +230. **Configurable audit granularity** (doc 16 §8.1) — Three levels: `stage` (~6 leaves/request — one per pipeline stage), `mutation` (~15-30 leaves — one per layer merge, policy evaluation, constraint resolution), `field` (mutation + per-field old/new value hashes). Profile defaults: minimal/dev → stage, standard/prod → mutation, fsi/sovereign → field (minimum, cannot downgrade). Inter-stage verification: synchronous (fsi/sovereign required), asynchronous, or disabled (homelab). + +231. **Lifecycle-scoped policy evaluation** (doc B §2.2–2.3) — 10 lifecycle operation types: initial_provisioning, update, scale, rehydration, decommission, ownership_transfer, subscription_renewal, drift_remediation, provider_migration, compliance_rescan. Each policy declares `lifecycle_scope` specifying which operations trigger it. `changed_field_filter` enables policies to fire only when specific fields change on update/scale operations (e.g., placement policies skip memory-only changes, sovereignty fires on zone/provider changes). Profile-governed minimums: fsi/sovereign require sovereignty and GateKeeper policies on ALL lifecycle operations — cannot be scoped down. + +232. **Policy Override Model** (doc B §18) — Five override mechanisms organized by severity: Override Policy (planned exceptions, full lifecycle, cannot target hard policies), Exception Grant (pre-authorized time-bounded waiver with compensating controls, dual-approval for hard), Manual Override (immediate single-request authorization, dual-approval for hard), Dual-Approval Escalation (required modifier for hard policy overrides — two individuals from different roles), Compensating Control Substitution (satisfy policy intent through different mechanisms without actually overriding). Every override produces a Merkle tree audit leaf. Profile-governed: fsi/sovereign require dual-approval on ALL overrides. + +233. **Test Framework Specification** (doc 52) — Automated self-reflecting test framework for data model and architecture validation. Generate→Execute→Verify→Analyze→Enhance loop. Machine-readable architecture summary (YAML) covering all fundamentals: 4 data domains, 5 provider types, 10 lifecycle operations, 8 policy types, 5 override mechanisms, 3 audit granularity levels. 56 named invariants across 7 categories: MATCH (7), EVAL (8), OVRD (9), LSCOPE (4), DATA (3), MRKL (6), RLS (3), PROV (7). Edge case categories: policy interaction (7 scenarios), provider (5), data integrity (5), lifecycle (5). Framework outputs enhancement proposals when gaps are discovered. + +234. **Override Approval Flow** (doc B §18.8) — When a policy blocks a request and no automatic resolution exists (no active override policy, exception grant, or compensating control), the request enters PENDING_OVERRIDE status. DCM publishes `override.required` event. Notification routing delivers to eligible approvers via internal (LISTEN/NOTIFY → Consumer Portal) and external (webhook to ServiceNow/Jira/Slack) channels. Approver(s) act via Admin API (`POST /api/v1/admin/overrides/{request_uuid}/approve`). For dual-approval: first approval recorded, second notification sent, pipeline held until both arrive or timeout. On approval: pipeline resumes from blocked stage with override injected into Evaluation Context. On timeout: request fails with OVERRIDE_TIMEOUT. Timeout is profile-governed (minimal/dev: PT24H, standard/prod: PT4H, fsi/sovereign: PT1H). `override_requests` table (18th SQL table) stores the complete approval record. + +235. **Override events and API** — 5 new events (override.required, override.first_approval, override.approved, override.rejected, override.expired) bring total to 109 event payloads across 23 domains. 4 new Admin API endpoints (list/get/approve/reject) bring admin paths to 61. + +236. **Policy Block Resolution** (doc B §18.8) — When a policy blocks a request and no automatic resolution exists, DCM does NOT silently enter an override queue. The request enters POLICY_BLOCKED status and the consumer is notified with actionable guidance: what blocked the request, why, compliant values to fix it, and four resolution options (modify request to be compliant, request override, cancel request, escalate to platform admin). Resolution guidance includes per-field suggestions derived from the blocking policy's constraint output. Consumer acts via `POST /api/v1/requests/{id}:resolve`. Only the "request override" option triggers the override approval flow (§18.9). This is the consumer-facing experience — §18.9 is the admin-facing approval mechanism. + +237. **Override approval is one resolution option, not the default** — The pipeline flow is: POLICY_EVALUATION → POLICY_BLOCKED (consumer notified with options) → consumer chooses action → if modify: re-enters pipeline from assembly → if request_override: PENDING_OVERRIDE → approval flow → if cancel: CANCELLED → if escalate: platform admin notified. Two separate timeouts: block timeout (how long before auto-cancel) and override timeout (how long approvers have). Both profile-governed. + +## SECTION 103 — APRIL 2026 SESSION: ADRs, WALKTHROUGH, PATTERN CATALOG, REQUIREMENTS (2026-04) + +### 103.1 Architecture Decision Records (Rewritten) + +16 ADRs in `architecture/adr/`, rewritten to answer "why does this exist?" not "how did we implement it?": + +| ADR | Title | One-Line | +|-----|-------|---------| +| 001 | Why DCM Exists | Unified management plane for on-prem — governance layer above provisioning tools | +| 002 | Three Foundational Abstractions | Everything is Data, Provider, or Policy | +| 003 | Four Lifecycle States | Intent → Requested → Realized → Discovered, immutable, linked by entity_uuid | +| 004 | Service Catalog & Consumer UX | Four-level hierarchy; consumers declare what, not how | +| 005 | Provider Abstraction | 6 types with naturalization/denaturalization; any platform, same interface | +| 006 | Policy Engine | Policy-as-code on every request; 8 types; multi-pass convergence | +| 007 | Placement Engine | Multi-stage scoring: sovereignty pre-filter → capability → capacity → policy | +| 008 | Dependency Resolution | Type-level deps → automatic sub-requests; binding fields inject runtime values | +| 009 | API Gateway & Control Plane | Single entry point; 9 services; deterministic pipeline | +| 010 | Audit & Tamper Evidence | Merkle tree (RFC 9162); configurable granularity; provable integrity | +| 011 | Sovereignty & Data Residency | First-class enforcement on every lifecycle op; dual-approval for overrides | +| 012 | Data Assembly & Layering | Organizational data merges with consumer requests; field-level provenance | +| 013 | Override & Exception Governance | 5 mechanisms from planned exceptions to dual-approval | +| 014 | Multi-Tenancy & Isolation | PostgreSQL RLS at database layer | +| 015 | Minimal Infrastructure | PostgreSQL only required dep; Internal/External delegation for everything else | +| 016 | Application Definition Language | **OPEN** — How should consumers define multi-resource apps? 4 options under evaluation | + +ADR-016 is the key open design question raised by the engineering team (Ondra/machacekondra). Options: API-only, YAML manifests, external DSL (Bicep/CEL), API composition. Comparison to Radius and KRO included. Decision pending team discussion. + +### 103.2 End-to-End Walkthrough + +`architecture/WALKTHROUGH.md` — 581 lines, 11 stages tracing a VM provision including IP dependency resolution: + +1. Consumer submits intent (6 fields) +2. Layer assembly (5 layers merge → 10+ fields with provenance) +3. Policy evaluation (GateKeeper, Validation, Transformation — 4 policies) +4. Dependency resolution (VM requires Network.IPAddress → sub-request created) +5. IP policy evaluation (sovereignty, subnet isolation, pool selection — 4 policies) +6. IP realization (InfoBlox IPAM allocates 10.1.45.23) +7. Dependency injection + VM placement (IP injected, providers scored) +8. VM dispatch (naturalization to OpenStack Nova with pre-allocated IP) +9. VM callback (denaturalization, realized state) +10. Discovery (both VM and IP independently polled, drift comparison) +11. Audit trail (17 Merkle leaves across 2 entities) + +### 103.3 Deployment Pattern Catalog + +Patterns are compound Resource Type Specifications — reusable, provider-agnostic blueprints that define collections of resources with dependencies and binding fields. The Pattern Catalog is NOT a new architectural component — it is a curated view of the Resource Type Registry filtered to compound types. All existing DCM machinery (Meta Provider, dependency graphs, binding fields, placement) executes patterns. + +Example patterns: Standard Web Application (6 constituents), Secure Data Pipeline, Developer Sandbox, Regulated Database Service, Edge Compute Node. + +Pattern interaction with DCM features: each constituent independently policy-evaluated, independently placed, independently discoverable for drift, independently auditable. Decommission reverses dependency order. Rehydration rebuilds in dependency order with current policies. + +### 103.4 Platform Requirements Document + +`dcm-platform-requirements.md` — 700 lines following enterprise requirements template: +- 5 personas (Consumer, Platform Engineer, Operator, Policy Owner, Administrator) +- 31 use cases in summary table, 23 with detailed descriptions +- Organized by lifecycle: Day 0 (5 UCs), Day 1 (5 UCs), Day 2 (4 UCs), Governance (2 UCs), Platform Ops (3 UCs), Federation (1 UC), Integration (1 UC), Pattern Catalog (1 UC + full overlay) +- Architecture principles, success outcomes, priority summary (16 P0, 9 P1, 6 P2) +- 4 open design questions documented + +### 103.5 PR Reviews + +**PR #50 (selrahal — Nova OpenAPI spec):** Architecture covers via Naturalization/Denaturalization. Nova spec belongs in dcm-examples. OpenStack Nova provider added with registration YAML, deployment manifest, and Go service. + +**PR #18 (Fale — Interoperability API + object design RFC):** Superseded by current architecture. Conventions conflict with team's established patterns (camelCase vs snake_case, YAML vs JSON, K8s structure vs flat REST, OpenAPI 3.0.4 vs 3.1.0). Spectral AEP linter worth adopting after 3.1 compatibility testing. Added as DISCUSSION-TOPICS item 7. + +### 103.6 Escalation Routing Fix + +Doc B §18.8 updated: policy block escalation routes to the responsible policy domain owner (sovereignty admin, security admin, cost admin) — not generic "platform admin." Routing configurable per policy domain and profile. Fixed in doc B, walkthrough, HTML presentation, and requirements document. + +### 103.7 Documentation Review Strategy + +Engineering team feedback incorporated. Key decisions: +- ADRs rewritten outward-facing per Ondra's feedback ("why does this exist, not how we implemented it") +- NotebookLM recommended over raw AI prompt for reproducible onboarding (Piotr's point) +- Domain-split PRs with reviewer assignments (Gabriel's point) +- Use cases drive architecture and priorities (Piotr and Ygal's point) +- Reading guide (proposal 1), session changelogs (proposal 7) still needed + +### 103.8 Meta Provider Removal + +**Decision:** `meta_provider` removed as a provider type (6 → 5 types). Compound service composition is a **Data** concept (Compound Resource Type Specifications in the Resource Type Registry) orchestrated by the **Control Plane** (Request Processor, Request Orchestrator). Individual constituents are fulfilled by standard **Service Providers**. + +**What changed:** +- Provider type count: 6 → 5 (service_provider, information_provider, auth_provider, peer_dcm, process_provider) +- Doc 30 retitled: "Meta Provider Model" → "Compound Resource Type Specifications" +- `provided_by: self` → `provided_by: ` (a named service provider) +- Pattern Catalog uses compound specs, not a meta provider type +- SQL CHECK constraint updated (meta_provider removed from provider_type enum) +- All ADRs, walkthrough, requirements, pattern overlay, examples repo updated +- Historical sections in AI prompt retain "Meta Provider" references with architectural note + +**What didn't change:** +- The compound Resource Type Specification YAML format — identical +- Consumer experience — still requests a catalog item, gets a composed application +- Dependency graphs, binding fields, compensation — all still work identically +- Platform engineer authoring — still defines compound specs the same way + +### 103.9 Authoritative Counts Update + +| Metric | Value | +|--------|-------| +| Data model docs | 58 | +| Specifications | 15 | +| ADRs | 16 (+ README) | +| OpenAPI schemas | 4 | +| Capabilities | 331 across 39 domains | +| SQL tables | 18 | +| Consumer API paths | 74 | +| Admin API paths | 61 | +| Event payloads | 109 across 23 domains | +| Provider types | 5 | +| Policy evaluation modes | 2 | +| Control plane services | 9 | +| Required infrastructure | 1 (PostgreSQL) | +| Test invariants | 60 | +| Use cases (Jira) | 30 epics, 249 stories | +| Use cases (requirements doc) | 31 total, 23 detailed | +| Prompt sections | 104 | + diff --git a/content/docs/architecture/data-model/00-context-and-purpose.md b/content/docs/architecture/data-model/00-context-and-purpose.md new file mode 100644 index 0000000..4bac891 --- /dev/null +++ b/content/docs/architecture/data-model/00-context-and-purpose.md @@ -0,0 +1,379 @@ +# DCM Data Model — Context and Purpose + + + +**Document Status:** ✅ Complete +**Related Documents:** [Entity Types](01-entity-types.md) | [Ownership, Sharing, and Allocation](04b-ownership-sharing-allocation.md) | [Four States](02-four-states.md) | [Layering and Versioning](03-layering-and-versioning.md) | [Examples](04-examples.md) + +> **Foundation Document Reference** +> +> This document is a detailed reference for a specific domain of the DCM architecture. +> The three foundational abstractions — Data, Provider, and Policy — are defined in +> [00-foundations.md](00-foundations.md). All concepts in this document map to one or +> more of those three abstractions. +> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) +> +> **This document maps to: DATA** +> +> The Data abstraction — foundational data model, provenance, four lifecycle states + + + +--- + +## 1. Purpose + +The DCM Data Model is the foundational layer upon which the entire DCM framework operates. It is not a storage mechanism or a database schema — it is the **lingua franca of DCM**. Every component in the DCM architecture communicates through the data model in some form, whether reading, writing, validating, enriching, transforming, or comparing data. + +The data model exists to solve a problem that is endemic to enterprise IT: **there is no single, trustworthy, consistent representation of infrastructure state**. Tools proliferate, CMDBs diverge, and the result is that no one knows with confidence what exists, what was requested, what was provisioned, or whether the current state matches the intended state. + +The DCM Data Model establishes a **unified, versioned, declarative single source of truth** for all infrastructure state across the full lifecycle of every resource DCM manages. + +--- + +## 2. Role in the DCM Architecture + +The data model is not owned by any single component — it is the contract between all components. Every major DCM capability acts on data in a specific and well-defined way: + +| Component | Relationship to Data | +|-----------|---------------------| +| Request Payload Processor | Assembles and enriches data into a complete request payload | +| Policy Engine | Reads, validates, transforms, and gates data based on policy definitions | +| Service Provider | Consumes data (via Naturalization) and returns data (via Denaturalization) | +| Orchestration | Coordinates component interactions based on data state and dependencies | +| Audit | Records data at every state transition for compliance evidence | +| Drift Reconciliation | Compares versions of data across states to detect and remediate drift | +| Cost Analysis | Derives cost information from data throughout the resource lifecycle | +| Resource Discovery | Produces data representing the current discovered state of resources | +| IDM / IAM | Gates access to data and operations based on identity and role | +| Service Catalog | Exposes available services based on data definitions and RBAC policy | + +This means the data model is effectively the **API between all DCM components** — even components that do not communicate directly are coupled through the data model. A well-designed data model makes every component easier to build, test, and evolve independently. + +--- + +## 3. Universal Identity Requirement + +Every data object in DCM must have a **UUID (Universally Unique Identifier)**. This is not optional — it is a foundational requirement that applies to every entity in the data model without exception. + +UUIDs serve several critical functions: + +- **Unambiguous reference** — any component, policy, layer, catalog item, or process that touches a data object can reference it precisely and without ambiguity +- **Provenance anchoring** — every change recorded in a data object's lineage references the UUID of the entity that caused the change +- **Dependency mapping** — relationships between resources, services, and components are expressed as UUID references, never by name alone +- **Audit fidelity** — audit records reference UUIDs, ensuring that even if names or labels change, the audit trail remains accurate and traceable +- **Cross-state correlation** — the same resource across Intent, Requested, Realized, and Discovered states can be correlated via UUID chains + +This applies to all entities including but not limited to: resource definitions, catalog items, data layers, policies, policy sets, components, service providers, consumers, and requests. + +--- + +## 4. Field-Level Provenance and Data Lineage + +One of the most critical requirements of the DCM Data Model is the ability to trace the complete lineage of any piece of data at any stage of the pipeline. This is not a logging concern — it is a **structural requirement of the data model itself**. + +### 4.1 The Requirement + +At any point in the DCM pipeline, for any field in any data object, it must be possible to answer: + +- What is the current value of this field? +- Where did this value originate? (catalog item, base layer, intermediate layer, policy, consumer input, discovery) +- Has this value been modified since origination? +- If modified: + - What is the complete history of modifications? + - Which entity caused each modification? (identified by UUID) + - What type of entity caused it? (policy, layer, component, provider) + - When did each modification occur? + - What was the value before each modification? + - Why was the modification made? (enrichment, validation, transformation, gatekeeping) +- What is the complete chain of custody of this field from origin to current value? + +### 4.2 Why Field-Level Lineage Matters + +Document-level versioning alone is insufficient for DCM's requirements. Consider a resource request flowing through the pipeline: + +1. Consumer selects a catalog item — catalog item UUID recorded +2. Base resource definition layer applied — base layer UUID recorded, fields established +3. Intermediate layers applied — each layer UUID recorded, field overrides recorded +4. Policy Engine validates — policy UUID recorded, validation outcome recorded +5. Policy Engine enriches — policy UUID recorded, enriched field values and their source recorded +6. Policy Engine transforms — policy UUID recorded, transformation recorded with before/after values +7. Request payload submitted — complete provenance chain intact across all fields + +Without field-level provenance, it is impossible to determine after the fact whether a specific field value came from a consumer request, a data layer, a security policy, or a business rule. This ambiguity is unacceptable in a governed, auditable system. + +### 4.3 Provenance as a Structural Element + +Field-level provenance must be carried within the data object itself — not in an external log. This ensures that: + +- The data and its lineage are always co-located and cannot be separated +- Any consumer of the data can inspect its lineage without querying an external system +- Provenance survives data export, migration, and portability scenarios +- The audit capability reads provenance that is intrinsic to the data, not reconstructed from logs + +### 4.4 Provenance Metadata Structure + +Every field that can be created or modified by any DCM process carries provenance metadata alongside its value. The conceptual structure is: + +```yaml +field_name: + value: + metadata: + # Simple override declaration (Level 2) — most fields only need this + override: + # OR matrix declaration (Level 3) — for actor-specific governance + override_matrix: + default: + actors: + trusted_grants: + + # Always present regardless of level + basis_for_value: + baseline_value: + locked_by_policy_uuid: + locked_at_level: + constraint_schema: + + provenance: + origin: + value: + source_type: + source_uuid: + timestamp: + modifications: + - sequence: 1 + previous_value: + modified_value: + source_type: + source_uuid: + operation_type: + actor: + timestamp: + reason: +``` + +**Note:** The `metadata` block is set exclusively by the Policy Engine. Data layers and the Request Payload Processor never set override control. `operation_type: lock` is used when a GateKeeper policy sets `override: immutable`. `operation_type: grant` is used when a trusted_grant is issued. The three levels of override control are: Level 1 (no declaration — fully overridable), Level 2 (simple `override:` property), Level 3 (full `override_matrix:` with per-actor permissions). See the Layering and Assembly document Section 5a for the complete model. + +### 4.5 Provenance Obligations + +Every DCM component that reads and modifies data carries a provenance obligation: + +| Component | Provenance Obligation | +|-----------|----------------------| +| Request Payload Processor | Record source UUID and type for every field assembled from layers and catalog items | +| Policy Engine | Record policy UUID, operation type, and reason for every field it enriches, transforms, or validates | +| Service Provider (Denaturalization) | Record provider UUID and timestamp for every field returned in the realized payload | +| Resource Discovery | Record provider UUID, discovery timestamp, and interrogation method for every field in the discovered payload | +| Data Layers | Each layer must declare its UUID so downstream provenance records can reference it | +| Catalog Items | Each catalog item must declare its UUID so downstream provenance records can reference it | + +Provenance recording is **not optional** for any component that modifies data. A component that modifies data without recording provenance violates the data model contract. + +### 4.6 Relationship to Audit + +The Audit capability in DCM reads provenance data that is intrinsic to every data object. This means: + +- Audit does not reconstruct history from logs — it reads lineage that was recorded at the point of change +- Any data object can be audited at any time, in any state, by any authorized persona +- The audit trail is as durable and immutable as the data itself +- Compliance evidence is produced from the data, not from a separate audit system that could diverge from the data + +--- + +## 5. Foundational Constraints + +The DCM Data Model is governed by three foundational constraints that apply universally and without exception: + +### 5.1 Declarative + +Data in DCM describes **what something is or should be**, not how to achieve it. Every entity in the data model is a complete, self-describing statement of state. The procedures required to achieve that state are the concern of the Service Provider, not the data model. + +This means: +- A resource definition declares its desired configuration, not the steps to configure it +- A policy declares its conditions and outcomes, not its execution logic +- A layer declares its overrides, not the merge algorithm used to apply them + +### 5.2 Idempotent in Operation + +Applying the same data to the same system multiple times must always produce the same result. No operation on DCM data should have different outcomes based on how many times it has been applied. + +This is critical for: +- **Drift reconciliation** — reapplying desired state to a drifted resource must produce correct results +- **DC rehydration** — replaying the full set of declared states must reconstruct the environment correctly +- **Retry scenarios** — failed operations can be safely retried without risk of inconsistent state +- **Audit and compliance** — the same data, applied by anyone at any time, produces the same verifiable outcome + +### 5.3 Immutable if Versioned + +Once a version of any entity is published, it cannot be modified. If a change is required, a new version must be created. The previous version remains intact and accessible. + +This constraint is what makes the following capabilities trustworthy: +- **Audit trails** — every state at every point in time is preserved and verifiable +- **Drift detection** — comparison between states is meaningful because neither state can change retroactively +- **Intent portability** — a previously declared intent can be replayed against current policies with confidence that the original intent is unchanged +- **Rollback** — reverting to a previous version is always possible because previous versions are never destroyed +- **Chain of trust** — the provenance of any configuration can be traced through an unbroken chain of immutable versions + +--- + +## 5a. Artifact Metadata Standard + +Every DCM artifact — layers, policies, resource types, catalog items, provider registrations, entity definitions, and all other defined or stored objects — carries a universal **Artifact Metadata** block. This is a structural requirement that applies to all artifacts without exception. + +### The Purpose + +Artifact metadata answers: **who created this, when, who owns it, what changed, and how do we contact them?** It is the identity and accountability record for the artifact itself — distinct from field-level provenance which tracks data value lineage. + +### The Five Artifact Statuses + +All DCM artifacts follow a five-status lifecycle: + +| Status | Meaning | Key Behavior | +|--------|---------|-------------| +| `developing` | In active development | Dev mode / dev pipeline only. Not applied in production. | +| `proposed` | Submitted for validation | Shadow execution for policies — output captured, not applied. In PR review for data artifacts. | +| `active` | Live and governing | Applied to all relevant requests. | +| `deprecated` | Being phased out | Still works, replacement available, warning on use. | +| `retired` | End of life | Cannot be used. Terminal status. | + +### Key Design Decisions + +**created_by vs owned_by:** Deliberately separate. The creator is the audit record — who physically submitted the artifact. The owner is the accountability record — who is responsible and receives notifications for conflicts, deprecation warnings, and policy violations. + +**Contact info — two modes:** When an Identity Provider is registered, the `uuid` field links to the IdP record and `display_name` is a non-authoritative display cache. In standalone/air-gapped mode, `uuid` is absent and `display_name` + `email` are the primary identity fields. Both modes are fully supported. + +**created_via:** Declares the ingestion path — `pr` (full GitOps review history), `api` (direct submission), `migration` (imported, limited provenance), `system` (DCM-created). Makes audit quality transparent. + +**Proposed shadow execution:** Policy artifacts in `proposed` status execute in shadow mode against real traffic — output is captured and reported but never applied. Enables safe validation before activation. + +See [Data Layers and Assembly — Section 4b](03-layering-and-versioning.md) for the complete artifact metadata structure and all field definitions. + +--- + +## 6. The Four States + +DCM tracks the lifecycle of every resource through four distinct states. Together, these four states provide complete visibility into the gap between what was wanted, what was asked for, what was built, and what actually exists. + +### 6.1 Intent State + +The **Intent State** represents what a consumer wants to happen. It is the declared desire, captured at the moment a consumer initiates a request, before any processing, validation, or enrichment has occurred. + +- **When it is created:** When a consumer submits a request via the Web UI or Consumer API +- **Where it is stored:** Intent Store +- **Key characteristic:** Captures the consumer's raw intent — what they asked for in their own terms +- **Primary use:** Source for Intent Portability — replaying an intent through current policies to produce a new request for a different environment or provider + +### 6.2 Requested State + +The **Requested State** represents the fully processed, policy-validated, and enriched payload that has been submitted to a Service Provider for execution. It is the output of the Request Payload Processor after all policies have been applied and all data layers have been merged. + +- **When it is created:** When the Request Payload Processor completes processing and submits to the API Gateway +- **Where it is stored:** Request Store +- **Key characteristic:** Represents a complete, validated, provider-ready declaration of desired state +- **Primary use:** Record of what was formally requested; input to audit and drift processes + +### 6.3 Realized State + +The **Realized State** represents what was actually provisioned or executed by a Service Provider, returned to DCM in unified data model format via Denaturalization. It is the ground truth of what was built. + +- **When it is created:** When a Service Provider completes execution and returns the realized payload to the API Gateway +- **Where it is stored:** Realized Store +- **Key characteristic:** Must be a complete representation of the provisioned resource in DCM unified format — not a status code, but a full state description +- **Primary use:** Baseline for drift detection; source of truth for audit and reporting; input to cost analysis + +### 6.4 Discovered State + +The **Discovered State** represents what actually exists in the environment as interrogated by a Service Provider during a discovery operation. It is an independent observation of reality, not derived from any previous DCM state. + +- **When it is created:** When a Service Provider completes a discovery cycle and returns the discovered payload to DCM +- **Where it is stored:** Discovered Store +- **Key characteristic:** Produced independently of the Realized State — it is what is actually there, regardless of what was supposed to be there +- **Primary use:** Drift detection (compared against Realized State); brownfield ingestion (pathway to lifecycle ownership of unmanaged resources) + +### 6.5 State Relationships and Lifecycle Flow + +The four states relate to each other as follows: + +``` +Consumer Request + │ + ▼ +┌─────────────┐ +│ INTENT │ ◄── What the consumer wants +│ STATE │ +└──────┬──────┘ + │ Policy Engine processes, enriches, validates + ▼ +┌─────────────┐ +│ REQUESTED │ ◄── What was formally submitted to the provider +│ STATE │ +└──────┬──────┘ + │ Service Provider executes + ▼ +┌─────────────┐ +│ REALIZED │ ◄── What was actually built +│ STATE │ +└──────┬──────┘ + │ + │ ┌─────────────┐ + │ Compare ◄─────────│ DISCOVERED │ ◄── What actually exists now + │ │ STATE │ + ▼ └─────────────┘ + Drift Detection +``` + +**Key operations across states:** +- **Drift Detection:** Discovered State vs. Realized State +- **Request Validation:** Requested State vs. Policy definitions +- **Intent Portability:** Intent State → re-process through current policies → new Requested State +- **Brownfield Ingestion:** Discovered State → enrichment → Realized State (lifecycle ownership) + +--- + +## 7. Data as the Provider Contract Boundary + +The data model defines the boundary between DCM and its Service Providers. DCM is explicitly **not concerned with how a provider accomplishes its work** — only with the data that crosses the boundary in both directions. + +This means: +- Providers are interchangeable as long as they honor the data contract +- New providers can be added without changing DCM's core data model +- Provider implementation can evolve independently of DCM +- The contract is enforced at the data level — conformant data in, conformant data out + +The two mechanisms that enforce this boundary are: +- **Naturalization** — the provider's responsibility to transform DCM unified data into its own tool-specific format for execution +- **Denaturalization** — the provider's responsibility to transform its tool-specific result data back into DCM unified format for return to the control plane + +This separation of concerns is what makes DCM technology-agnostic while maintaining a consistent and trustworthy data model across all providers. + +--- + +## 8. Open Questions + +The following questions remain unresolved and require decisions before the data model specification can be considered complete: + +| # | Question | Impact | Status | +|---|----------|--------|--------| +| 1 | Where should data caches live? | Cache architecture, latency, sovereignty | ✅ Resolved — caches at Regional DCM level; Info Provider caches co-located with source; Sovereign DCM uses local static from signed bundles (CACHE-001) | +| 2 | Should cache synchronization be push, pull, or both? | Consistency model | ✅ Resolved — hybrid push-pull; pull on schedule; push for time-sensitive events via Message Bus (CACHE-002) | +| 3 | Which cache is authoritative when caches diverge? | Conflict resolution | ✅ Resolved — GitOps stores always authoritative; caches are projections; divergence triggers rebuild from authoritative store (CACHE-003) | +| 4 | What mechanism maintains consistency across distributed caches? | Data integrity | ✅ Resolved — hash-based heartbeat divergence detection + push invalidation; PT2H staleness alert; signed bundles for Sovereign DCM (CACHE-004) | +| 5 | Should the data model allow embedded target-technology-specific data bundles? | Portability | ✅ Resolved — native_passthrough field sanctioned; always audit-logged; opaque mode blocked in fsi/sovereign (DATA-001) | +| 6 | How are the four states represented physically? | Physical model | ✅ Resolved — Intent/Requested in Git; Realized in Event Stream; Discovered in Discovered Store (STO-005) | +| 7 | What is the performance impact of field-level provenance at scale? What optimization strategies are acceptable? | Scalability, storage cost | ✅ Resolved — three configurable provenance models: full_inline, deduplicated (Model B recommended), tiered; profile-appropriate Policy Groups; see docs 03 and 06 (OPS-001) | +| 8 | Should provenance metadata be stored inline with field data or in a linked provenance document? | Data model structure, query performance | ✅ Resolved — three-level structure: implicit chain ref, inline delta, linked history document; all reconstructable from stored facts; see doc 03 (OPS-002) | + +--- + +## 9. Related Concepts + +- **Sovereign Execution Posture** — the target end state the data model enables by providing a verified, auditable chain of custody through the full resource lifecycle +- **CMDB Replacement** — DCM's four-state model is intended to replace the fragmented multi-CMDB problem by becoming the singular resource domain +- **GitOps** — all entities in the data model are stored in Git, enabling version control, change tracking, and standard software lifecycle practices +- **Data Lineage** — the complete chain of custody of any field value from its origin through every modification, recorded within the data object itself +- **Field-Level Provenance** — the structural mechanism by which data lineage is captured, carried, and made available for audit and compliance purposes +- **UUID** — the universal identity mechanism that makes provenance references, dependency mapping, and cross-state correlation unambiguous and durable + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* diff --git a/content/docs/architecture/data-model/00-design-priorities.md b/content/docs/architecture/data-model/00-design-priorities.md new file mode 100644 index 0000000..db26240 --- /dev/null +++ b/content/docs/architecture/data-model/00-design-priorities.md @@ -0,0 +1,312 @@ +# DCM Design Priorities + +**Document Status:** ✅ Stable — Foundational reference +**Document Type:** Architecture Reference — Design Philosophy +**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Policy Profiles](14-policy-profiles.md) | [Scoring Model](29-scoring-model.md) | [credential management service Model](31-credential-provider-model.md) + +> **This document maps to: DATA + PROVIDER + POLICY** +> +> Design priorities govern every decision across all three abstractions. They are not guidelines — they are the decision framework used when priorities conflict. Every contributor, implementer, and reviewer should apply this framework. +> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) + +--- + +## The Four Priorities + +Every design decision in DCM is evaluated against this hierarchy. When priorities conflict, higher priorities win. When there is no conflict, all four apply simultaneously. + +--- + +### Priority 1 — Industry Best Practices for Security + +Security is not a feature, a profile option, or a compliance checkbox. It is the baseline that every other design decision must respect. + +**What this means:** + +Security properties — value separation, rotation, audit trails, idle detection, algorithm baselines, scoped credentials, revocation propagation, shadow mode evaluation — are **architecturally present in every profile**. What profiles control is enforcement strictness, threshold values, and automation level — not whether the security property applies. + +**The `minimal` profile is "security with minimal operational overhead" — not "minimal security."** + +A `minimal` profile deployment: +- Rotates credentials (at longer intervals with manual triggers acceptable — not never) +- Detects idle credentials (at a generous P30D threshold — not never) +- Requires algorithm baselines (via forbidden list — not null) +- Runs shadow mode on contributed policies (always — not optionally) +- Audits first credential retrieval (always — not sometimes) +- Maintains revocation registry (at PT5M cache TTL — not disabled) + +The security model is present and correct. The enforcement strictness and automation burden are reduced. + +**When security and convenience conflict, security wins** — but the design must find a way to make the secure option easy. A security model that is routinely bypassed because it is too burdensome has failed at both security and usability. The profile system is the mechanism: the right profile makes secure behavior automatic, not effortful. + +**Security properties that are non-negotiable in all profiles:** + +| Property | Rule | Reference | +|----------|------|-----------| +| Credential values never in DCM stores | CPX-001 — absolute, no profile exception | doc 31 | +| Governance Matrix always boolean | SMX-004 — scoring never applies to boundary decisions | doc 29 | +| Every provider dispatch requires scoped interaction credential | CPX-002 | doc 31 | +| Shadow mode on all contributed policies | FCM-004 | doc 28 | +| auto_approve_below ≤ 50 in all profiles | SMX-008 | doc 29 | +| First credential retrieval always audited | CPX-005 | doc 31 | +| Forbidden algorithm baseline always enforced | CPX-009 (no null approved_algorithms) | doc 31 | +| Revocation registry always maintained | CPX-003 | doc 31 | + +--- + +### Priority 2 — Ease of Use + +DCM exists to enable self-service for application teams. If the right path is also the hard path, teams will find other paths — and those other paths are ungoverned. + +**What this means:** + +The secure path must also be the easy path. Profile defaults should work for most deployments without customization. Ordinary requests should auto-approve without human intervention. Policy authoring should not require Rego expertise for common patterns. The Flow GUI, scoring model, and consumer contribution endpoints all exist to make governed behavior less operationally burdensome. + +**Ease of use serves security.** An organization that finds DCM too cumbersome and routes requests outside DCM has eliminated all of DCM's security benefits. A homelab team that circumvents credential management because it's too complex has no credential management. + +**The design principle:** When implementing a security requirement, simultaneously design the ease-of-use mechanism that makes it effortless to comply with. The scoring model's auto-approval threshold (not making every request require human review) is ease of use in service of security. + +**Things that should be easy in all profiles:** +- Requesting a standard resource (auto-approve for clean requests) +- Authoring a common policy without Rego expertise (visual condition builder) +- Retrieving a credential after resource provisioning (direct API call) +- Understanding why a request was scored a certain way (score_drivers field) +- Contributing a policy (API endpoint, not manual GitOps PR) + +--- + +### Priority 3 — Extensibility and Capability Grouping + +The profile system, compliance domain overlays, policy groups, capability extensions, and registry governance make DCM adaptable to arbitrary organizational requirements without code changes. + +**What this means:** + +New compliance requirements should be expressible as policy additions within the existing framework. New provider types should fit the existing Provider base contract. New deployment contexts should be addressable through profile configuration. A platform that requires modifying source code for each new deployment context is not a platform — it is a template. + +**Grouping is the mechanism for extensibility.** Compliance domain overlays compose with base profiles. Policy groups compose with profile policies. Capability extensions compose with base provider contracts. The three-abstraction model (Data, Provider, Policy) is the foundation that makes all of this compositional. + +**Extensibility must not compromise security or usability.** An extension mechanism that allows downstream users to disable security properties (rather than scale them) fails priority 1. An extension mechanism that requires expertise to configure fails priority 2. The profile system's hard constraints (SMX-008: auto_approve ≤ 50; CPX-001: no values in DCM stores) are precisely the boundaries that prevent extensibility from undermining security. + +--- + +### Priority 4 — Fit for Purpose (Always Required) + +DCM must manage data center infrastructure lifecycle. All of the above is in service of this purpose. An architecturally beautiful system that cannot provision a VM, track its drift, and decommission it cleanly has failed at its reason for existing. + +**What this means:** + +Design decisions that serve priorities 1–3 but break the end-to-end lifecycle (request → provision → operate → decommission) are not acceptable. Every capability added must have a clear answer to "how does this serve the lifecycle management mission?" + +Fit for purpose is not a fourth priority that can be traded against the first three — it is a precondition. If a design cannot fulfill its stated purpose, priorities 1–3 become irrelevant. This is why it is listed fourth rather than first: it is assumed, not aspirational. + +--- + +## Applying the Priorities — Decision Framework + +When facing a design decision where priorities seem to conflict, apply this sequence: + +``` +1. Does this design decision compromise a non-negotiable security property? + YES → redesign until it does not. No exceptions. + +2. Does the secure option create significant operational burden? + YES → design the ease-of-use mechanism simultaneously. + The secure path must also be the easy path. + If you cannot make it easy enough, reconsider whether the + security property is correctly scoped. + +3. Can this behavior be expressed through the existing profile/policy/extension system? + YES → use it. Do not add new mechanisms when existing ones suffice. + NO → extend the existing mechanism before creating a new one. + +4. Does this design decision support the complete lifecycle? + NO → do not proceed until it does. +``` + +### Common Misapplications + +**"We can disable X in the minimal profile for simplicity."** +Wrong application. The minimal profile scales down operational burden, not security properties. The question is: what is the minimum viable implementation of X that requires no operational overhead? That is what minimal profile gets. + +**"Security is too complex for our users, so we'll make it optional."** +Wrong application. If security is too complex, the design of the security mechanism needs to improve (priority 2). Making security optional removes it — that fails priority 1. Design a simpler mechanism that achieves the same security outcome. + +**"We need a new mechanism for this capability."** +Wrong starting point (priority 3 failure). The question is: can this be expressed through profiles, policies, provider capability extensions, or compliance overlays? Usually yes. If genuinely not, extend the nearest existing mechanism rather than creating a new one. + +**"This edge case isn't part of the lifecycle."** +Wrong framing (priority 4). Every edge case in the lifecycle — partial realization, compensation, drift remediation, credential revocation on decommission — is part of the lifecycle. Fit for purpose means handling the complete lifecycle, not just the happy path. + +--- + +## Profile Scaling Model + +The profile system is the primary mechanism for expressing priorities 1–3 simultaneously. Understanding what profiles control — and what they do not — is essential to applying the priority order correctly. + +**Profiles control:** +- Enforcement strictness (how strictly a security property is enforced) +- Threshold values (how long, how often, how many) +- Automation level (automated vs manual trigger) +- Approval tier (auto-approve vs human review vs verified vs authorized) +- Review periods (how long shadow mode runs before promotion) + +**Profiles do not control:** +- Whether a security property is present (it always is) +- Which non-negotiable constraints apply (CPX-001, SMX-004, SMX-008, etc.) +- Whether the audit trail is maintained (always maintained; retention varies) +- Whether the data model is valid (schema conformance is not profile-dependent) + +### Profile Scaling Table + +This table shows how representative security properties scale across profiles. "Present" means the property is architecturally required — what varies is the configuration. + +| Security Property | minimal | dev | standard | prod | fsi | sovereign | +|------------------|---------|-----|----------|------|-----|-----------| +| Credential rotation | Required; P365D max; manual OK | Required; P180D max; manual OK | Required; automated | Required; strict interval | Required; P90D max | Required; hardware-triggered | +| Idle detection threshold | P30D | P14D | P7D | P3D | P1D | PT12H | +| Algorithm baseline | Forbidden list | Forbidden list | Approved list | Approved list | FIPS-only | HSM-generated only | +| Shadow mode on contribution | Always on | Always on | Always on | Always on | Always on | Always on | +| First retrieval audit | Always | Always | Always | Always | Always | Always | +| Revocation cache TTL | PT5M | PT5M | PT1M | PT1M | PT30S | PT15S | +| Auto-approve threshold | ≤ 45 | ≤ 40 | ≤ 25 | ≤ 15 | ≤ 10 | ≤ 5 | +| Step-up MFA for credentials | Optional | Optional | Sensitive types | All types | Hardware MFA | mTLS | +| FIPS level | None required | None required | None required | Level 1 | Level 2 | Level 3 | +| IP binding | Not required | Not required | Not required | Not required | Required | Required | +| Hub contribution auto-approve | Yes | Yes | Yes | No (human review) | No (verified) | No (authorized) | + +--- + +--- + +## Approval Tier Model + +DCM defines four approval tiers that apply to requests, policy contributions, provider registrations, and any pipeline decision requiring human authorization. Understanding the model is critical: **DCM provides the gate and the audit trail. The review process is the organization's responsibility.** + +> **Full specification:** See [Authority Tier Model](32-authority-tier-model.md) for the complete ordered tier list, custom tier contribution model, dynamic threshold format, and ATM-001–ATM-008 system policies. + +### What DCM Does vs What Organizations Provide + +| Tier | Required authority level | DCM provides | Organization provides | DCM gate condition | +|------|-------------------------|-------------|----------------------|-------------------| +| `auto` | None — `decision_gravity: none`; system confidence sufficient | Structural and governance validation; automatic activation on pass | Nothing — fully automated | All validation checks pass | +| `reviewed` | Standard authority — `decision_gravity: routine`; one qualified reviewer in the relevant domain | Approval record; eligible reviewer notification via notification service; pipeline hold; decision recording via Admin API; activation or rejection | Who constitutes a qualified reviewer; the review process; recording the decision via DCM API or an external system that calls it | One actor with reviewer role records a decision via the Admin API | +| `verified` | Elevated authority — `decision_gravity: elevated`; independent confirmation required; separation of duties | Approval record requiring two independent decisions; enforces distinct actors (same actor cannot provide both); eligible reviewer notification; pipeline hold | Who constitutes qualified reviewers; both review processes; may use external workflow tools that call the DCM API | Two distinct actors with reviewer role each record a decision via the Admin API | +| `authorized` | Senior/governing authority — `decision_gravity: critical`; highest organizational weight; most consequential decisions | Approval record specifying the required DCMGroup and threshold (N of M); group member notification; pipeline hold; individual decision tracking via Admin API; threshold evaluation; activation when N reached | Who constitutes the authority group (one person with delegated authority, a CTO, a CISO and legal counsel, a change board — the organization decides); how they deliberate; what external tools they use; DCM records decisions, not deliberation | N members of the declared DCMGroup record decisions via the Admin API within the declared window | + +### Tier Extensibility + +The four default tiers (`auto`, `reviewed`, `verified`, `authorized`) are DCM system defaults. Organizations can add custom tiers by inserting them into the ordered list between existing tiers. The tier name is stable; numeric weight is derived from list position at evaluation time. + +Example: An organization adds `compliance_reviewed` between `verified` and `authorized`: +``` +auto → reviewed → verified → compliance_reviewed → authorized +``` +All existing references to `authorized` continue to work. Only the threshold ranges in the affected profile need updating. See [Authority Tier Model](32-authority-tier-model.md). + +### The `authorized` Tier — What DCM Builds vs What It Does Not Not + +DCM does **not** build an authorized group management system. It does not track deliberation, run voting sessions, manage agendas, or coordinate review meetings. + +DCM builds: + +1. **DCMGroup membership management** — which actors constitute the authorized group; configurable by platform admins +2. **Quorum declaration** — `N of M` threshold declared in the profile or per-decision configuration +3. **Notification routing** — when a decision enters `pending_authorized` state, the notification service fires to all DCMGroup members +4. **Vote recording API** — the Admin API endpoint that authorized group members (or external systems acting on their behalf) call to record `approve` or `reject` +5. **Quorum tracking** — DCM counts votes and advances the pipeline when N is reached +6. **Audit trail** — every vote is audited with actor UUID, timestamp, decision, and the system that recorded it + +External systems (ServiceNow, Jira, email workflows, Slack bots) connect to DCM by calling the vote recording API. A Slack bot that collects emoji reactions from group members and then calls DCM's Admin API is a valid implementation — DCM doesn't care how the organization collected the vote, only that an authorized group member recorded it. + +### Admin API as the Integration Point + +The Admin API approval endpoint is designed to be called by external systems, not only by humans in a DCM UI: + +``` +POST /api/v1/admin/approvals/{approval_uuid}:vote +Authorization: Bearer # any actor who is a member of the required DCMGroup + +{ + "decision": "approve | reject", + "reason": "", + "recorded_via": "dcm_admin_ui | servicenow | jira | slack_bot | api_direct | other", + "external_reference": "" +} + +Response 200: +{ + "approval_uuid": "", + "voter_uuid": "", + "decision": "approve", + "votes_recorded": 2, + "quorum_required": 3, + "quorum_reached": false, + "pipeline_status": "pending_authorized" +} + +# When quorum is reached: +{ + "approval_uuid": "", + "voter_uuid": "", + "decision": "approve", + "votes_recorded": 3, + "quorum_required": 3, + "quorum_reached": true, + "pipeline_status": "activating" +} +``` + +The `recorded_via` field provides the audit trail provenance — DCM knows whether the vote came through its own UI, a ServiceNow integration, a Jira plugin, or a direct API call. This is not enforced — it is informational for audit purposes. + +### Deadline and Escalation + +DCM manages the approval window (the time within which a decision must be reached) and fires escalation notifications when the window is approaching: + +```yaml +approval_window: + reviewed: PT72H # 3 days; configurable per profile + verified: PT72H + authorized: P7D # 7 days for deliberation; configurable + on_expiry: + reviewed: escalate # escalate to platform admin + verified: escalate + authorized: reject # authorized tier that cannot reach threshold in window → reject +``` + +When the window expires without a decision, DCM fires an escalation notification and either rejects (for authorized) or escalates to the next approval tier (for reviewed and verified). The organization can configure these windows to match their actual governance processes. + +### DPO Alignment + +The approval tier model directly implements all four design priorities: + +1. **Security:** Approval tiers are the enforcement mechanism for governance. The gate is DCM's responsibility — it cannot be bypassed, and every decision is audited. +2. **Ease of use:** The Admin API as integration point means organizations use whatever workflow tools they already have. DCM does not require them to adopt a new process tool. +3. **Extensibility:** The `recorded_via` field and `external_reference` field make the approval tier model composable with arbitrary external systems without DCM needing to integrate with each one. +4. **Fit for purpose:** The tier model enables governance of every pipeline decision (request approval, policy contribution, provider registration, federation contribution) through a single consistent mechanism. + +## Documentation Requirements + +Every document in the DCM data model should: + +1. **Reference the priority order** where design decisions are made that involve tradeoffs +2. **Explain non-negotiable security properties** with clear rationale +3. **Document what profiles control vs what they do not** for each security-relevant configuration +4. **Identify the ease-of-use mechanism** that accompanies every security requirement +5. **State fit-for-purpose scope** explicitly — what lifecycle operations does this document govern? + +--- + +## System Policies + +| Policy | Rule | +|--------|------| +| `DPO-001` | Security properties are architecturally present in all profiles. Profiles control enforcement strictness, thresholds, and automation level — not whether the property exists. | +| `DPO-002` | Every security requirement must be accompanied by an ease-of-use mechanism that makes compliance effortless for the common case. A security model routinely bypassed because of complexity has failed. | +| `DPO-003` | New capabilities should be expressed through the existing profile/policy/provider extension system before creating new mechanisms. Extensibility is achieved through composition, not proliferation. | +| `DPO-004` | Fit for purpose is a precondition, not a priority. All four priorities apply only within the constraint that the system can fulfill its lifecycle management mission. | +| `DPO-005` | The `minimal` profile is "security with minimal operational overhead" — not "minimal security." Design decisions that disable security properties rather than scaling them violate DPO-001. | +| `DPO-006` | When security and ease of use conflict, redesign the ease-of-use mechanism — not the security requirement. The secure path must also be the easy path. | + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* diff --git a/content/docs/architecture/data-model/00-foundations.md b/content/docs/architecture/data-model/00-foundations.md new file mode 100644 index 0000000..5a7deda --- /dev/null +++ b/content/docs/architecture/data-model/00-foundations.md @@ -0,0 +1,301 @@ +# DCM — Foundational Abstractions + + +**Document Status:** ✅ Complete +**Document Type:** Architecture Foundation — Read This First +**Related Documents:** [Data Model Context](00-context-and-purpose.md) | [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) + +--- + +## 1. The Three Abstractions + +DCM is built on three foundational abstractions. Every concept in the architecture is an instance of one of these three — or a combination of them. There is no fourth. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ DATA │ +│ │ +│ Everything that exists, is stored, has a lifecycle, and flows │ +│ through the system. Entities, layers, policies, accreditations, │ +│ audit records, groups, relationships — all Data. │ +└──────────────────────────┬──────────────────────────────────────┘ + │ flows through + ┌────────────┴────────────┐ + ▼ ▼ +┌─────────────────────┐ ┌─────────────────────────────────────┐ +│ PROVIDER │ │ POLICY │ +│ │ │ │ +│ Every external │ │ Every rule that fires on Data, │ +│ component DCM │ │ decides what happens, transforms │ +│ calls or that │ │ values, or enforces constraints. │ +│ calls DCM. │ │ │ +│ Twelve typed │ │ Eight typed output schemas. │ +│ capability │ │ One evaluation algorithm. │ +│ extensions. │ │ Same lifecycle for all. │ +│ One base contract. │ │ │ +└─────────────────────┘ └─────────────────────────────────────┘ +``` + +**The runtime that connects them:** + +``` +Event (Data state change) + → Policy Engine evaluates all matching Policies + → Policies produce decisions / mutations / actions + → Actions invoke Providers or produce new Data + → New Data triggers new Events + → Repeat +``` + +This is the complete DCM operational model. Everything else is a typed specialization of these three abstractions operating through this loop. + +--- + +## 2. DATA — Everything That Exists + +**Definition:** Data is any structured artifact in DCM with a type, fields, classification, provenance, and lifecycle state. Data is always versioned, always identified by UUID, and always carries provenance describing where each field value came from. + +**The universal properties of all Data:** +- **UUID** — every Data artifact has a universally unique identifier, stable across its full lifecycle +- **Type** — every Data artifact has a declared type that determines its schema and valid field set +- **Lifecycle state** — every Data artifact is in exactly one lifecycle state at any moment +- **Artifact metadata** — every Data artifact carries a standard metadata block (handle, version, status, owned_by, created_by, created_via) +- **Provenance** — every field in every Data artifact carries lineage metadata describing its origin and all modifications +- **Data classification** — every field carries a classification (public → classified) governing what may cross interaction boundaries +- **Immutability if versioned** — once a version is published, it cannot be modified; changes produce new versions +- **Contributor identity** — every Data artifact records who contributed it (platform admin, consumer/tenant, service provider, or peer DCM) and what review it received before activation. DCM defaults to a federated contribution model — all authorized actor types can create Data within the bounds their role permits. See [Federated Contribution Model](28-federated-contribution-model.md). + +**The complete Data taxonomy:** + +| Data Type | Description | Storage | +|-----------|-------------|---------| +| **Resource Entity** | A realized infrastructure resource; the primary managed thing | Realized Store | +| **Process Entity** | An ephemeral execution (job, playbook, pipeline) | Realized Store | +| **Composite Entity** | A Meta Provider composition of Resource Entities | Realized Store | +| **Intent State** | Consumer's raw declaration before processing | Intent Store (GitOps) | +| **Requested State** | Fully assembled, policy-validated provider payload | Requested Store | +| **Discovered State** | What actually exists per discovery observation | Discovered Store | +| **Data Layer** | A versioned artifact contributing fields to assembly | Layer Store (GitOps) | +| **Resource Type Specification** | Schema definition for a resource type | Registry | +| **Provider Catalog Item** | Provider-specific instantiation of a Resource Type Spec | Registry | +| **Policy** | A rule artifact with match conditions and output schema | Policy Store (GitOps) | +| **Policy Group** | A collection of policies grouped by concern_type | Policy Store (GitOps) | +| **Policy Profile** | A composition: one posture + zero or more compliance domains | Policy Store (GitOps) | +| **Accreditation** | A compliance certification artifact | Accreditation Store | +| **Sovereignty Zone** | A geopolitical/regulatory boundary artifact | Config Store | +| **Registration Token** | A scoped authorization artifact for provider registration | Token Store | +| **DCMGroup** | A grouping artifact (tenant_boundary, resource_grouping, etc.) | Config Store | +| **Drift Record** | A comparison result artifact | Operational Store | +| **Audit Record** | An immutable event record | Audit Store | +| **Governance Matrix Rule** | A boundary control rule artifact | Policy Store (GitOps) | +| **Orphan Candidate** | A potentially untracked resource artifact | Operational Store | + +**How Data flows — the four lifecycle stages:** + +Every Resource Entity flows through four stages. These are not four separate things — they are the same entity at four different lifecycle stages, stored as four data domains in DCM's PostgreSQL database, each with distinct immutability and access patterns: + +``` +Consumer Intent + │ raw consumer declaration + ▼ +Intent State ──────────────────────────────────── intent_records (append-only) + │ layer assembly + policy evaluation + ▼ +Requested State ────────────────────────────────── requested_records (append-only) + │ provider execution + ▼ +Realized State ─────────────────────────────────── realized_entities (versioned snapshots) + │ independent observation + ▼ +Discovered State ───────────────────────────────── discovered_records (ephemeral) +``` + +**How Data is composed — the layering model:** + +Data fields are assembled from multiple contributing layers in a deterministic precedence order. See [Data Model Context](00-context-and-purpose.md) and [Layering and Versioning](03-layering-and-versioning.md) for the complete assembly algorithm. + +--- + +## 3. PROVIDER — Everything External + +**Definition:** A Provider is any external component that DCM interacts with through a defined contract. Providers receive Data from DCM, act on it, and return Data to DCM. The contract governs how this exchange happens — not what the Provider does internally. + +**The universal properties of all Providers:** +- **Registration** — every Provider registers with DCM, declaring its capabilities, sovereignty, and accreditation +- **Health check** — every Provider exposes a health endpoint; DCM monitors it continuously +- **Sovereignty declaration** — every Provider declares where it operates and what jurisdictions it covers +- **Accreditation** — every Provider declares its compliance certifications; DCM enforces these via the Governance Matrix +- **Governance Matrix enforcement** — every interaction with a Provider is subject to the Governance Matrix before data crosses the boundary +- **Zero trust** — every Provider interaction is authenticated and authorized; no implicit trust from network position +- **Lifecycle** — every Provider registration goes through a defined lifecycle (SUBMITTED → VALIDATING → ACTIVE → DEREGISTERED) + +**The complete Provider taxonomy:** + +| Provider Type | Capability | Data direction | +|--------------|-----------|---------------| +| **Service Provider** | Realizes infrastructure resources | DCM → Provider → DCM | +| **Information Provider** | Serves authoritative external data | DCM queries → Provider responds | +| **data store** | Persists DCM state | DCM reads/writes ↔ Provider | + +| **External Policy Evaluator** | Evaluates policies externally | DCM sends payload → Provider decides | +| **credential management service** | Manages secrets and credentials | DCM requests → Provider issues | +| **Auth Provider** | Authenticates identities | DCM verifies → Provider confirms | +| **notification service** | Delivers notifications | DCM sends envelope → Provider delivers | +| **event routing service** | Async event streaming | DCM publishes/subscribes ↔ Provider | +| **Resource Type Registry** | Serves the resource type registry | DCM pulls → Provider serves | +| **Peer DCM** | Another DCM instance (federation) | DCM ↔ DCM via federation tunnel | +| **ITSM integration** | Bidirectional integration with ITSM systems (ServiceNow, Jira, Remedy, etc.); creates/updates ITSM records from DCM events; routes ITSM approvals back to DCM | DCM → ITSM (outbound) / ITSM → DCM (inbound) | + +**The unified Provider base contract** is defined in [A-provider-contract.md](A-provider-contract.md). All twelve Provider types implement this base contract. What varies is the capability declaration — what operations the Provider exposes and what data flows in which direction. + +**Peer DCM as Provider:** A federated DCM instance is a typed Provider. The federation tunnel is the Provider's communication channel. Federation routing is policy-governed provider selection. There is no separate "federation abstraction" — federation is the Provider abstraction applied across DCM instances. + +--- + +## 4. POLICY — Everything That Decides + +**Definition:** A Policy is a rule artifact that fires when Data matches declared conditions, produces a typed output (decision, mutation, action, or directive), and is enforced according to a declared level. Policies govern every transition, transformation, and constraint in DCM. + +**The universal properties of all Policies:** +- **Match conditions** — every Policy declares when it fires, using the four governance matrix axes (subject, data, target, context) or payload type + field conditions +- **Typed output schema** — every Policy produces one of seven output types; the output type determines how the Policy Engine applies the result +- **Enforcement level** — hard (cannot be overridden) or soft (can be tightened by more-specific policies) +- **Domain precedence** — policies at more-specific domains win within their concern type; system > platform > tenant > resource_type > entity +- **Lifecycle** — every Policy follows the standard artifact lifecycle (developing → proposed → active → deprecated → retired) +- **Shadow mode** — proposed Policies execute against real traffic without applying results; safe validation before activation +- **Audit** — every Policy evaluation produces an audit record regardless of outcome + +**The complete Policy taxonomy:** + +| Policy Type | Fires on | Output | +|-------------|---------|--------| +| **GateKeeper** | Request payload | `allow` or `deny` with reason | +| **Validation** | Request payload | `pass` or `fail` with field-level details | +| **Transformation** | Request payload | `mutations[]` — field additions, changes, locks | +| **Recovery** | Failure/timeout trigger condition | `action` + parameters (DRIFT_RECONCILE, DISCARD_AND_REQUEUE, etc.) | +| **Orchestration Flow** | Payload type events | `flow_directive` — sequence ordering for pipeline steps | +| **Governance Matrix Rule** | Any cross-boundary interaction | `ALLOW / DENY / ALLOW_WITH_CONDITIONS / STRIP_FIELD / REDACT / AUDIT_ONLY` | +| **Lifecycle Policy** | Relationship events | `action` on the related entity (save, destroy, notify, cascade) | +| **ITSM Action** | DCM events (state transitions, drift, realization) | `itsm_action` — create/update/close ITSM records; non-blocking by default | + +**The unified Policy base contract** is defined in [B-policy-contract.md](B-policy-contract.md). All eight Policy types implement this base contract. What varies is the output schema. + +**Policies as orchestration — two levels that compose:** + +*Level 1 — Named Workflow Artifacts (explicit, visible, auditable):* +An Orchestration Flow Policy with `concern_type: orchestration_flow` and `ordered: true` is a named workflow. It declares steps in explicit sequence. Named workflows are first-class Data artifacts — versioned, GitOps-managed, profile-bound. Adding an explicit pipeline step = adding a step to a workflow Policy artifact. + +*Level 2 — Dynamic Policies (conditional, inline):* +GateKeeper, Transformation, Recovery, and Governance Matrix Policies fire when their match conditions are satisfied — within or alongside workflow steps, without being declared in the workflow. Adding conditional behavior = writing a dynamic policy. + +Both levels are evaluated by the same Policy Engine and triggered through the same Request Orchestrator event bus. They compose naturally: a named workflow provides the sequence skeleton; dynamic policies provide conditional behavior within it. + +**The Governance Matrix as Policy:** The Governance Matrix rules (doc 27) are typed Policies with the `boundary_control` output schema. They fire at every cross-boundary interaction. They follow the same match conditions, enforcement levels, and lifecycle as all other Policies. The governance matrix is not a separate system — it is the Policy abstraction applied at interaction boundaries. + +--- + +## 5. The Runtime — Connecting the Three + +The Request Orchestrator and Policy Engine are the runtime that connects the three abstractions. They are not a fourth abstraction — they are the implementation machinery. + +``` +┌─────────────────────────────────────────────────────────┐ +│ Request Orchestrator │ +│ (event bus — not a sequencer) │ +│ │ +│ Receives events → routes to Policy Engine │ +│ Policy Engine evaluates all matching Policies │ +│ Results: invoke Providers OR produce new Data │ +│ New Data → new events → new Policy evaluations │ +└─────────────────────────────────────────────────────────┘ +``` + +**Key runtime properties:** +- The Request Orchestrator contains no pipeline logic — Policies define what happens +- Every pipeline step is a Policy firing on a payload type event +- Parallel execution: Policies with no data dependencies evaluate concurrently +- Static flows: Orchestration Flow Policies with `ordered: true` +- Dynamic flows: conditional Policies that fire based on payload state + +**Control plane components as runtime specializations (nine total):** + +The components in [Control Plane Components](25-control-plane-components.md) are specialized runtime implementations, not separate abstractions: + +| Component | Abstraction it implements | +|-----------|--------------------------| +| Request Orchestrator | The runtime event bus | +| Policy Engine | The runtime Policy evaluator | +| Placement Engine | Policy evaluation specialized for provider selection | +| Cost Analysis | Information Provider (internal; data derivation) | +| Lifecycle Constraint Enforcer | Scheduled Recovery Policy trigger | +| Discovery Scheduler | Scheduled Provider invocation | +| Notification Router | Transformation Policy + notification service invocation | +| Drift Reconciliation | Data comparison producing new Data (drift records) | +| Search Index | PostgreSQL store contract (queryable projection) | + +--- + +## 6. Extension Points + +DCM is designed to be extended without modifying the core. Every extension fits within the three abstractions: + +**Extending Data:** New entity types, new artifact types, new resource types, new group classes — all are typed extensions of the Data abstraction. Register them in the Resource Type Registry or DCMGroup registry. + +**Extending Providers:** New provider types (a Billing Provider, a CMDB Provider, an AI/ML Provider) — implement the unified Provider base contract with a new capability declaration extension. Register in the Provider Type Registry. + +**Extending Policies:** New policy types, new governance matrix rules, new orchestration flows — implement the unified Policy base contract with a new output schema. Register in the Policy Store via GitOps. + +**The extension principle:** If you can express it as Data, Provider, or Policy — it belongs in DCM. If you cannot express it within these three abstractions, it is either a runtime implementation detail or a genuinely novel concept that should be explicitly identified and documented as such. + +--- + +## 7. The Core Ethos + +These three abstractions serve DCM's core ethos: + +**Effective at the core mission** — managing the lifecycle of infrastructure resources across a sovereign private cloud. The Data abstraction ensures every resource is tracked, versioned, and auditable. The Provider abstraction ensures every external integration is governed and trustworthy. The Policy abstraction ensures every decision is declared, reproducible, and auditable. + +**Easy to use** — consumers interact with Data (submit an intent, receive a resource). Policies govern what happens without consumers needing to understand them. Providers handle the implementation details. + +**Easy to implement** — implementors implement one base contract (Provider) with a typed capability extension. The Policy Engine handles all policy evaluation. The Data model handles all storage and provenance. + +**Easy to extend and integrate** — add a new provider type by implementing the base contract. Add a new policy type by defining an output schema. Add a new data type by defining a schema. No core changes required. + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* + + +--- + +## Design Priority Order + +> **Full specification:** See [Design Priorities](00-design-priorities.md) for the complete priority framework, decision framework, profile scaling table, and DPO-001–006 system policies. + + +Every design decision in DCM is evaluated against this priority order. When priorities conflict, higher priorities win. When there is no conflict, all four apply simultaneously. + +**1. Industry best practices for security** +Security is not a feature or a profile option. It is the baseline that every other design decision must respect. Where security and convenience conflict, security wins — but the design must find a way to make the secure path the easy path. A security model that is routinely bypassed because it is too burdensome has failed at both security and usability. + +*In practice:* Security properties — value separation, non-transferable credentials, scoped permissions, rotation, audit, revocation propagation — are architecturally present in every profile. What profiles control is the enforcement strictness, operational automation, and threshold values. A `minimal` profile does not disable security; it implements security with minimal operational overhead. + +**2. Ease of use** +DCM exists to enable self-service for application teams. If the right path is also the hard path, teams will find other paths. The goal is to make secure, governed, auditable infrastructure management the path of least resistance — not the path of compliance obligation. + +*In practice:* Profile defaults should eliminate configuration burden for common cases. The standard pipeline should auto-approve ordinary requests without human intervention. Policy authoring should not require Rego expertise for common patterns. The Flow GUI, scoring model, and contribution endpoints all serve this priority. + +**3. Extensibility and capability grouping** +The profile system, compliance domain overlays, policy groups, and registry governance exist to make DCM adaptable to arbitrary organizational requirements without code changes. This priority serves at scale — a platform that can only be configured by modifying source code is not a platform. + +*In practice:* New compliance requirements should be expressible as policy additions within the existing framework. New provider types should fit the existing Provider base contract. New deployment contexts should be addressable through profile configuration. + +**4. Fit for purpose (always required)** +DCM must actually manage data center infrastructure lifecycle. All of the above is in service of this purpose — not independent of it. An architecturally beautiful system that cannot provision a VM, track its drift, and decommission it cleanly has failed at its reason for existing. + +*In practice:* Design decisions that serve priorities 1–3 but break the end-to-end lifecycle (request → provision → operate → decommission) are not acceptable. Every capability added must have a clear answer to "how does this serve the lifecycle management mission?" + +--- + +**The implication for profiles:** A `minimal` profile is "security with minimal operational overhead" — not "minimal security." The security architecture is present and correct in every profile. What varies is how much automation, how strict the thresholds, and how much manual intervention is acceptable. This is the principle that makes DCM trustworthy in a homelab and in a sovereign government deployment using the same codebase. + diff --git a/content/docs/architecture/data-model/01-entity-types.md b/content/docs/architecture/data-model/01-entity-types.md new file mode 100644 index 0000000..fd62cb9 --- /dev/null +++ b/content/docs/architecture/data-model/01-entity-types.md @@ -0,0 +1,346 @@ +# DCM Data Model — Entity Types + + +**Document Status:** ✅ Complete +**Document Type:** Architecture Reference + +> **Foundation Document Reference** +> +> This document is a detailed reference for a specific domain of the DCM architecture. +> The three foundational abstractions — Data, Provider, and Policy — are defined in +> [00-foundations.md](00-foundations.md). All concepts in this document map to one or +> more of those three abstractions. +> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) +> +> **This document maps to: DATA** +> +> The Data abstraction — typed entity extensions (Infrastructure Resource, Composite, Process) + + +**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Four States](02-four-states.md) | [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Ownership, Sharing, and Allocation](04b-ownership-sharing-allocation.md) + +--- + +## 1. Purpose + +This document defines the complete taxonomy of entity types in DCM. Every resource, service, group, and process managed by DCM is an entity — and every entity belongs to one of the types defined here. The entity type determines the lifecycle state machine, the ownership model, the decommission behavior, and which data model fields are applicable. + +Understanding entity types is prerequisite to understanding: +- How lifecycle states are assigned and transition +- How ownership and allocation interact +- How drift detection operates at different levels +- How decommission cascades through dependent entities + +--- + +## 2. The Three Primary Entity Types + +DCM defines three primary entity types. Every entity is exactly one of these. + +### 2.1 Infrastructure Resource Entity + +An **Infrastructure Resource Entity** is a realized physical or virtual infrastructure resource that persists across time and has a full operational lifecycle. + +**Characteristics:** +- Persists after provisioning — it continues to exist and consume resources until explicitly decommissioned +- Owned by exactly one Tenant at any point in time +- Has a full bidirectional lifecycle including OPERATIONAL and SUSPENDED states +- Subject to drift detection — its Realized State is continuously compared against Discovered State +- Subject to TTL management — may declare an expiry after which decommission is triggered +- May have relationships to other entities — dependencies, attachments, allocations, business data +- Carries field-level provenance across its full lifecycle + +**Lifecycle State Machine:** + +``` + ┌─────────────────────────────────┐ + │ REQUESTED │ + │ (Intent State assembled, │ + │ Requested State committed) │ + └───────────────┬─────────────────┘ + │ Provider dispatch + ▼ + ┌─────────────────────────────────┐ + │ PENDING │ + │ (Awaiting provider capacity │ + │ or dependency resolution) │ + └───────────────┬─────────────────┘ + │ Provider begins work + ▼ + ┌─────────────────────────────────┐ + │ PROVISIONING │ + │ (Provider actively realizing) │ + └───────────────┬─────────────────┘ + │ Provider confirms realization + ▼ + ┌─────────────────────────────────┐ + │ REALIZED │ + │ (Provider-confirmed, DCM has │ + │ full Realized State record) │ + └───────────────┬─────────────────┘ + │ Passes health checks + ▼ + ┌─────────────────────────────────┐ ◄── Primary operational state + │ OPERATIONAL │ Drift detection active + │ (Active, healthy, in use) │ Cost analysis active + └───┬───────────────────┬─────────┘ Policy evaluation active + │ │ + Suspend │ │ Decommission request + request ▼ ▼ + ┌──────────────────┐ ┌──────────────────────┐ + │ SUSPENDED │ │ DECOMMISSIONING │ + │ (Paused, not in │ │ (Provider removing, │ + │ active use, │ │ dependencies │ + │ may be billed │ │ being released) │ + │ at reduced rate)│ └──────────┬───────────┘ + └────────┬─────────┘ │ + │ Resume │ Provider confirms removal + │ or decommission ▼ + │ ┌──────────────────────┐ + └─────────────► DECOMMISSIONED │ ◄── Terminal state + │ (Removed from infra, │ + │ audit records │ + │ preserved) │ + └──────────────────────┘ + +Any state except DECOMMISSIONED: + PROVISIONING_FAILED → rolls back to REQUESTED or terminal FAILED + PENDING_REVIEW → sovereignty/tenancy conflict during rehydration (see Section 2.1.2) +``` + +**Applicable to:** VirtualMachine, VLAN, IPAddress, StorageVolume, Container, LoadBalancer, DNSRecord, FirewallRule, NetworkPort, Subnet, and all other persistent infrastructure resource types. + +#### 2.1.1 Infrastructure Resource Entity Data Model + +```yaml +infrastructure_resource_entity: + # Universal artifact metadata + uuid: # stable across full lifecycle including rehydration + handle: # human-readable stable identifier + resource_type: # e.g., Compute.VirtualMachine + resource_type_spec_version: + lifecycle_state: + created_at: + updated_at: + + # Ownership + owned_by_tenant_uuid: # exactly one Tenant; mandatory + created_by_actor_uuid: + + # Ownership model — see doc 04b + ownership_model: + # whole_allocation: consumer owns this entity outright + # allocation: this entity is an allocation carved from a pool (consumer owns it) + # shareable: consumer has a stake; ownership remains with pool owner + + # If this is an allocation from a pool resource + allocated_from_pool_uuid: # UUID of the pool entity; null if not an allocation + allocation_ref_uuid: # UUID of the AllocationRecord relationship + + # If this is a shareable stake + shared_resource_uuid: # UUID of the shared resource; null if not a stake + + # Provider details (populated after REALIZED) + provider_uuid: + provider_entity_id: # provider's own identifier (e.g., "vm-12345") + provider_entity_id_history: [...] # history of provider IDs (rehydration changes these) + + # Lifecycle constraints + ttl: + ttl_expires_at: + on_expiry: + billing_state: + + # Rehydration + rehydration_constraints: + min_auth_level: + allow_delegated_rehydration: + rehydration_history: [...] + + # Drift tracking + last_discovered_at: + drift_status: + last_drift_severity: + + # Relationships (see doc 09) + relationships: [...] + + # Field-level provenance on all data fields (see doc 00, Section 4) + # [all resource-type-specific fields carry provenance metadata] +``` + +#### 2.1.2 PENDING_REVIEW State + +`PENDING_REVIEW` is a formal lifecycle state for Infrastructure Resource Entities (not Process Resources). An entity enters `PENDING_REVIEW` when an automated operation detects a conflict that requires human resolution before the operation can proceed: + +| Trigger | Description | +|---------|-------------| +| Rehydration sovereignty conflict | Rehydration discovers the target provider no longer satisfies the entity's sovereignty constraints | +| Cross-tenant authorization revoked | An authorization enabling a cross-tenant resource reference was revoked while the resource is still allocated | +| Ownership transfer conflict | An ownership transfer request conflicts with active relationships that prevent transfer | + +An entity in `PENDING_REVIEW`: +- Is not actively drifting from its Realized State (the underlying resource is unchanged) +- Has an active `pending_review_record` on the entity with trigger, timestamp, and resolution options +- Generates notifications to the entity owner, Tenant admin, and platform admin +- Remains in `PENDING_REVIEW` until a resolution action is taken (re_authorize, release, escalate, or manual override) +- Is never automatically resolved — all resolutions require explicit human or policy authorization + +### 2.2 Composite Resource Entity + +A **Composite Resource Entity** is produced by a compound resource type specification that orchestrates multiple constituent Infrastructure Resource Entities to deliver a higher-order service. The composite is a first-class entity — it has its own UUID, Tenant ownership, and lifecycle. Its constituents each retain their own entity identity. + +**Characteristics:** +- Represents the logical aggregate, not a physical resource +- Owned by exactly one Tenant (the Tenant that requested the composite service) +- Constituents may be owned by the same Tenant or may be allocations/stakes in pool resources owned by another Tenant +- Drift detection operates at two levels: the composite level (is the composite healthy as a whole?) and the constituent level (is each underlying resource still in its expected state?) +- Decommission is staged: composite decommissioned first, then constituents in reverse dependency order + +**Lifecycle state machine:** Same as Infrastructure Resource Entity. The composite's `lifecycle_state` reflects the aggregate health of all constituents — a composite is OPERATIONAL only when all required constituents are OPERATIONAL. + +**Constituent relationship:** Each constituent is recorded as a `constituent_of` relationship from the constituent to the composite. The composite holds `has_constituent` relationships to each constituent. The composite UUID is the correlation key across all constituent audit records. + +```yaml +composite_resource_entity: + uuid: + resource_type: # e.g., ApplicationStack.WebApp + lifecycle_state: + owned_by_tenant_uuid: + composition_visibility: + # opaque: consumers see composite only; constituents hidden + # transparent: consumers see composite and all constituents + # selective: policy declares which constituents are visible + + constituents: + - constituent_entity_uuid: + role: + required_for_composite_operational: + # If a required constituent fails, the composite enters DEGRADED + constituent_lifecycle_state: + composite_health: +``` + +### 2.3 Process Resource Entity + +A **Process Resource Entity** represents an ephemeral execution — an automation job, playbook, pipeline, workflow, or script execution. It does not persist after completion. Its lifecycle is terminal-focused: every Process Resource Entity ends in either COMPLETED, FAILED, or CANCELLED. + +**Characteristics:** +- Does not persist after reaching a terminal state — no ongoing Realized State to manage +- Must declare `max_execution_time` — mandatory, not optional +- If max_execution_time is exceeded, the process enters FAILED state and DCM generates a `PROCESS_TIMEOUT` event +- If the process modifies any Infrastructure Resource Entity, it must record the modified entity UUIDs in its provenance +- Owned by the Tenant that initiated the execution +- Subject to audit — every process execution produces a full audit trail + +**Lifecycle state machine:** + +``` +REQUESTED → INITIATED → EXECUTING → COMPLETED (terminal) + → FAILED (terminal) + → CANCELLED (terminal — requires explicit cancel request) +``` + +No SUSPENDED state. No PENDING_REVIEW state. Process Resources are ephemeral — they do not enter states that require ongoing management. + +```yaml +process_resource_entity: + uuid: + resource_type: # e.g., Automation.AnsiblePlaybook + lifecycle_state: + owned_by_tenant_uuid: + created_by_actor_uuid: + + max_execution_time: # mandatory + started_at: + completed_at: + execution_timeout_at: # computed: started_at + max_execution_time + + # Entities this process modified (mandatory if any modifications made) + affected_entity_uuids: [, ...] + + # Execution details + provider_uuid: # which automation provider executed this + provider_job_id: # provider's own job identifier + exit_status: + execution_log_ref: # reference to log store entry + + # Provenance on all execution parameters carries field-level lineage +``` + +--- + +## 3. Sub-Types and Specializations + +### 3.1 Shared Resource Entity (Infrastructure Resource sub-type) + +A **Shared Resource Entity** is an Infrastructure Resource Entity where multiple consumers hold stakes — references, attachments, or dependencies — without any consumer owning an allocation of the resource. The resource has a single owner (typically a platform or network operations Tenant). Consumers reference it through relationships. + +See [Ownership, Sharing, and Allocation](04b-ownership-sharing-allocation.md) for the complete model. + +**Examples:** VLAN, NetworkSegment, SharedStorageCluster, DNS Zone, NTP Server, Certificate Authority. + +**Key property:** `ownership_model: shareable` + +Decommission is deferred while any active stakeholder relationships exist. The `minimum_relationship_count` on the resource type spec declares the safe minimum — typically 0 (can be decommissioned when all stakes are released) but may be higher for infrastructure that must always have at least one consumer. + +### 3.2 Allocatable Pool Resource (Infrastructure Resource sub-type) + +An **Allocatable Pool Resource** is an Infrastructure Resource Entity that serves as a pool from which consumers receive owned allocations. The pool itself is owned by a platform Tenant. Each allocation request produces a new, independently owned Infrastructure Resource Entity carved from the pool. + +See [Ownership, Sharing, and Allocation](04b-ownership-sharing-allocation.md) for the complete model. + +**Examples:** IPAddressPool (allocates IPAddress entities), SubnetPool (allocates Subnet entities), VLANPool (allocates VLAN entities), StoragePool (allocates StorageVolume entities). + +**Key property:** `ownership_model: whole_allocation` on the pool entity; allocation products have `ownership_model: allocation`. + +The pool tracks available capacity. Allocation requests go through the placement engine like any other resource request. The produced allocation entity is owned by the requesting Tenant. + +--- + +## 4. Entity Identity Invariants + +These invariants apply to all entity types without exception: + +| Invariant | Rule | +|-----------|------| +| UUID stability | An entity's UUID never changes across its full lifecycle, including rehydration and provider migration | +| Single Tenant ownership | Every Infrastructure Resource Entity and Process Resource Entity is owned by exactly one Tenant at all times | +| Composite constituent ownership | A Composite Resource Entity's constituents are owned individually — the composite UUID does not override constituent Tenant ownership | +| Immutable Realized State | Realized State events are append-only; a new event is created for every state change | +| Audit trail preservation | Audit records for an entity are never destroyed while any related entity is active; preservation policy governs post-terminal retention | +| Provider ID separation | The entity UUID is the DCM stable identity; the provider entity ID is the provider's own reference. These are separate and the provider ID may change on rehydration | + +--- + +## 5. Entity Type to Resource Type Mapping + +Not all resource types produce the same entity type. The entity type is declared in the Resource Type Specification: + +```yaml +resource_type_spec: + fqn: Compute.VirtualMachine + entity_type: infrastructure_resource # infrastructure_resource | composite_resource | process_resource + ownership_model: whole_allocation # whole_allocation | allocation | shareable + allocatable_from_pool_type: null # if allocation: the pool resource type this comes from + pool_resource_type: null # if pool: declare this is a pool resource + shareable: false # if shareable: true +``` + +--- + +## 6. Related Policies + +| Policy | Rule | +|--------|------| +| `ENT-001` | Every Infrastructure Resource Entity must be owned by exactly one Tenant at all times | +| `ENT-002` | Process Resource Entities must declare max_execution_time — this field has no default and is not optional | +| `ENT-003` | Process Resource Entities must record all affected entity UUIDs if any infrastructure modifications are made during execution | +| `ENT-004` | Composite Resource Entity lifecycle_state reflects aggregate constituent health — OPERATIONAL only when all required constituents are OPERATIONAL | +| `ENT-005` | PENDING_REVIEW is a valid Infrastructure Resource Entity state requiring human resolution — it is never an error state and never automatically resolved | +| `ENT-006` | The entity UUID is immutable across the full entity lifecycle including rehydration, provider migration, and ownership transfer | + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* diff --git a/content/docs/architecture/data-model/02-four-states.md b/content/docs/architecture/data-model/02-four-states.md new file mode 100644 index 0000000..55223be --- /dev/null +++ b/content/docs/architecture/data-model/02-four-states.md @@ -0,0 +1,818 @@ +# DCM Data Model — The Four States + + + +**Document Status:** ✅ Complete +**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Entity Relationships](09-entity-relationships.md) | [data stores](11-storage-providers.md) | [Audit, Provenance, and Observability](12-audit-provenance-observability.md) + +> **Foundation Document Reference** +> +> This document is a detailed reference for a specific domain of the DCM architecture. +> The three foundational abstractions — Data, Provider, and Policy — are defined in +> [00-foundations.md](00-foundations.md). All concepts in this document map to one or +> more of those three abstractions. +> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) +> +> **This document maps to: DATA** +> +> The Data abstraction — four lifecycle stages and their storage models + + + +--- + +## 1. Purpose + +The four states are the foundational model for how DCM tracks the complete lifecycle of any resource or service. Every entity in DCM exists in one or more of these states simultaneously. The states are not sequential stages — they are parallel, independently maintained records that together provide a complete, auditable picture of what was requested, what was approved, what was built, and what actually exists. + +The four states answer four distinct questions: + +| State | Question Answered | Data Domain | +|-------|------------------|-------------| +| **Intent State** | What did the consumer ask for? | `intent_records` — append-only, immutable | +| **Requested State** | What was approved and dispatched to the provider? | `requested_records` — append-only, immutable | +| **Realized State** | What did the provider actually build? | `realized_entities` — versioned snapshots, `is_current` flag | +| **Discovered State** | What does DCM observe actually existing right now? | `discovered_records` — ephemeral, refreshed per discovery run | + +> **Infrastructure note (doc 51):** All four data domains are stored in a single PostgreSQL-compatible database. The logical distinctions (immutability rules, versioning model, query patterns) are preserved via table design, `REVOKE UPDATE/DELETE`, and RLS. Git, Kafka, and Redis are optional deployment enhancements, not architectural requirements. See [51-infrastructure-optimization.md](51-infrastructure-optimization.md). + +--- + +## 2. State Definitions + +### 2.1 Intent State + +The **Intent State** is the immutable record of a consumer's original declaration. It is captured at the moment a request is submitted — before any layer assembly, before any policy evaluation, before any provider selection. + +**Characteristics:** +- Immutable once created — the consumer's original intent is never modified +- Stored in a GitOps store — branched, reviewed, merged +- The CI/CD pipeline operates on the Intent State — policy pre-validation, cost estimation, sovereignty check, approval workflow +- Versioned via Git history — every revision of an intent is traceable +- Supports human review and debate via the PR mechanism +- The entity UUID is assigned at Intent State creation — it follows the entity through all subsequent states + +**When created:** Every request submission, every rehydration operation, every drift remediation authorization + +**Content:** The consumer's raw declaration in DCM Unified Data Model format — what they want, not what will be built + +### 2.2 Requested State + +The **Requested State** is the fully assembled, policy-processed, provider-ready payload. It is produced by the Request Payload Processor from the Intent State — after layer assembly, after all policy evaluation, after provider selection. + +**Characteristics:** +- Immutable once created — a new Requested State is created for each request cycle +- Stored in a GitOps store — committed, versioned, triggering CD pipeline +- The CD pipeline dispatches from the Requested State to the provider +- Contains the complete assembled payload with full field-level provenance +- Contains the results of all policy evaluations — which policies ran, what they did, what they locked +- Contains provider selection — which provider will realize this request +- Is the authoritative record of what DCM instructed a provider to build + +**When created:** After Intent State approval (merge), after successful policy processing + +**Content:** The complete assembled payload in DCM Unified Data Model format, with full provenance chain, policy evaluation results, provider selection, and override control metadata + +### 2.3 Realized State + +The **Realized State** is the provider-confirmed record of what was actually built. It is produced by the provider after successful realization — the denaturalized result of the provider's execution, translated back to DCM Unified Data Model format. + +**Characteristics:** +- Write-once complete snapshots — each Realized State record is a full entity state, never modified after writing +- Every Realized State record is traceable to exactly one Requested State record — no exceptions +- Stored in a realized_entities table keyed by entity UUID +- Contains provider-specific details not in the Requested State — assigned IPs, generated passwords, actual storage sizes, provider-internal IDs +- Is the authoritative record of what actually exists from DCM's perspective +- Drift is detected by comparing the most recent Realized State snapshot against Discovered State +- Carries a supersession chain — each snapshot knows which snapshot it superseded and which superseded it + +**Three write sources (all require a corresponding Requested State record):** + +| Source | Requested State record type | Example | +|--------|---------------------------|---------| +| Initial realization | `initial_realization` | Consumer provisions a new VM | +| Consumer update request | `consumer_update` | Consumer patches an editable field | +| Provider update notification | `provider_update` | Provider reports an authorized state change (auto-healing, maintenance) | + +**What does NOT write to the Realized Store:** +- Drift detection — drift only compares, never writes +- Discovery cycles — discovery writes to Discovered Store only +- Unsanctioned provider changes — these are drift events until DCM evaluates and explicitly approves them + +**When created:** When a provider confirms realization of any authorized request (initial, consumer update, or approved provider update notification) + +**Content:** Complete entity state snapshot in DCM Unified Data Model format, with provider-added fields, full field-level provenance including provider attribution, and supersession chain references + +### 2.4 Discovered State + +The **Discovered State** is what DCM observes actually existing through active discovery — polling providers, querying Kubernetes APIs, interrogating infrastructure. It is the ground truth of what physically exists, independent of what DCM thinks exists. + +**Characteristics:** +- Append-only snapshot stream — each discovery cycle produces a new snapshot +- Stored in an pipeline_events table (ephemeral) — recent history retained, older snapshots archived or discarded +- High-frequency and machine-generated — not appropriate for human review +- Used exclusively for drift detection — comparing against Realized State +- May contain resources DCM did not provision — brownfield resources discovered for ingestion + +**When created:** On every discovery cycle, on demand for specific entities + +**Content:** Raw discovered resource state in DCM Unified Data Model format, with discovery metadata (timestamp, discovery method, provider interrogated) + + +### 2.5 Recovery States + +Five additional states apply to Infrastructure Resource Entities when the normal provisioning lifecycle encounters timeouts, cancellation failures, or partial realization. These states are governed by Recovery Policies (see [Operational Models](24-operational-models.md) Section 5). + +| State | Meaning | Entry Trigger | +|-------|---------|--------------| +| `TIMEOUT_PENDING` | Dispatch timeout fired; cancellation sent to provider | `DISPATCH_TIMEOUT` recovery trigger | +| `LATE_REALIZATION_PENDING` | Provider responded after timeout; NOTIFY_AND_WAIT active | `LATE_RESPONSE_RECEIVED` recovery trigger | +| `INDETERMINATE_REALIZATION` | State ambiguous; drift detection resolving | `DRIFT_RECONCILE` recovery action | +| `COMPENSATION_IN_PROGRESS` | Compound service rollback underway | `PARTIAL_REALIZATION` trigger | +| `COMPENSATION_FAILED` | Rollback itself failed; orphaned resources possible | Compensation step failure | + +See [Operational Models](24-operational-models.md) for the complete recovery state machine and Recovery Policy model. + + +--- + +## 3. The Entity UUID — Universal Linking Key + +Every entity has a single UUID assigned at Intent State creation. This UUID is the universal key linking the entity across all four states and all stores: + +``` +Intent Store: file path includes entity_uuid, content declares entity_uuid +Requested Store: file path includes entity_uuid, content declares entity_uuid +Realized Store: event stream keyed by entity_uuid +Discovered Store: snapshot stream keyed by entity_uuid (matched via provider labels) +Audit Store: all provenance events indexed by entity_uuid +Search Index: entity_uuid → git_path mapping for Git stores +``` + +Given an entity UUID, DCM can reconstruct the complete history of that entity across its entire lifecycle — from the consumer's original intent through every state transition to the current discovered state. + +--- + +## 4. Physical Representation — Data Domain Model + +All four states are stored in DCM's PostgreSQL-compatible database as distinct data domains. Each domain has specific immutability rules, access patterns, and enforcement mechanisms — but they share a single infrastructure dependency. See [Infrastructure Requirements](51-infrastructure-optimization.md) for the prescribed infrastructure model and [Data Store Contracts](#41-data-store-contracts) below for the enforcement rules. + +Git is available as an optional ingress adapter — consumers who prefer PR-based workflows can submit intent via Git. But Git is an ingress path, not a state store. DCM's state lives in PostgreSQL. + +### 4.1 Data Store Contracts + +Each data domain enforces its contract through PostgreSQL-native mechanisms: + +| Domain | Table | Immutability | Enforcement | +|--------|-------|-------------|-------------| +| **Intent** | `intent_records` | Append-only — new intent creates a new row, previous intents never modified | `REVOKE UPDATE, DELETE` on table; RLS per tenant | +| **Requested** | `requested_records` | Append-only — each policy evaluation produces a new version with full provenance | `REVOKE UPDATE, DELETE` on table; RLS per tenant | +| **Realized** | `realized_entities` | Versioned snapshots — each state change creates a new row with `is_current` flag | Append-on-change semantics; `is_current` enforces single latest; RLS per tenant | +| **Discovered** | `discovered_records` | Ephemeral — each discovery run produces fresh snapshots; previous runs retained for trend analysis | Grouped by `discovery_run_uuid`; RLS per tenant | + +**Pipeline events** flow between control plane services via the `pipeline_events` table with PostgreSQL `LISTEN/NOTIFY` for real-time routing. For high-throughput deployments, Kafka can be added alongside as an optional enhancement. + +**Audit records** are stored in `audit_records` with a SHA-256 hash chain (each record's hash includes the previous record's hash), append-only enforcement (`REVOKE UPDATE, DELETE` + trigger-based immutability guard), and per-entity chain sequence numbers. + +### 4.2 Realized State Snapshot Model + +The Realized domain uses a **snapshot model** — each record is a complete entity state, not a delta. This makes rehydration a direct lookup rather than an event replay, and point-in-time queries ("what was the state on March 15?") are direct lookups. + +```yaml +realized_state_snapshot: + realized_uuid: # this snapshot's identity + entity_uuid: # stable entity identity across versions + realized_at: + + # Always traceable to a request — mandatory, not nullable + source_type: + request_uuid: + + # Versioning + version_major: + version_minor: + version_revision: + is_current: # only one current per entity_uuid + + # Complete entity state at this point — all fields, all provenance + fields: + # [full entity state in DCM Unified Data Model format] + + # Provider-added fields + provider_metadata: + provider_entity_id: + provider_reported_at: +``` + +**Why snapshots instead of events:** Rehydration requires a complete entity state, not a replay of field-level events. The Realized domain is written only when an authorized change completes — it does not need high-frequency write throughput. + +### 4.3 Query and Caching + +For read-heavy workloads (catalog browsing, placement lookups, resource listing), PostgreSQL materialized views provide derived projections optimized for specific query patterns. These views are explicitly non-authoritative — the base tables always win if a view and a table disagree. + +For deployments requiring geographically distributed read performance, Redis can be added as an optional caching layer in front of materialized views. + +--- + +## 5. Rehydration + +Rehydration is the process of using a previously stored state record as the starting point for a new request. It is not a shortcut around governance — **all relevant governance policies always apply regardless of rehydration source.** Rehydration is a new request that happens to start from a known prior state. + +### 5.1 Three Rehydration Sources + +**From Intent State:** +- The consumer's original declaration is replayed +- Full layer assembly runs — current layers applied +- All governance policies run — current policies applied +- Provider selection runs fresh +- Most likely to produce a different result than the original — policies and layers may have changed +- Use cases: upgrade resource to current standards, apply new sovereignty constraints, environment refresh + +**From Requested State:** +- The previously assembled, policy-processed payload is loaded +- Layer assembly is skipped — layers were already applied +- All governance policies run — current policies applied +- Provider selection: configurable via flag (see Section 5.3) +- Use cases: reproduce a resource as closely as possible to the approved specification + +**From Realized State:** +- The provider-confirmed realized payload is loaded +- Provider-specific fields are stripped — DCM unified format only +- Layer assembly is skipped +- All governance policies run — current policies applied +- Provider selection: configurable via flag +- Use cases: exact reproduction for disaster recovery, environment cloning, replacing a failed resource + +### 5.2 The Common Governance Pipeline + +Regardless of rehydration source, all requests flow through the same governance pipeline: + +``` +Rehydration source selected and loaded + │ + │ Source payload becomes the basis for a new Intent State record + │ New entity UUID assigned (or existing UUID preserved — policy decision) + │ Rehydration provenance recorded: source_store, source_record_uuid, + │ rehydration_reason, requested_by_uuid, rehydration_timestamp + ▼ +If source = Intent: + │ Full layer assembly runs (Steps 1-7) + │ Current layers applied + ▼ +If source = Requested or Realized: + │ Layer assembly skipped + │ Payload loaded as pre-assembled + │ If source = Realized: provider-specific fields stripped + ▼ +Placement evaluation + │ See Section 5.3 — configurable + ▼ +Policy Engine — ALL governance policies applied + │ Authorization policies: does this actor have permission to rehydrate? + │ Transformation policies: current enrichment applied + │ Validation policies: current constraints checked + │ GateKeeper policies: current field locks applied + │ Gatekeeping policies: is this resource type still permitted? + │ + │ Governance is NEVER skippable — not for any rehydration source, + │ not for any actor, not for any urgency claim + ▼ +New Requested State produced and stored + │ New record — never overwrites the source record + │ Source record remains immutable + │ Provenance chain links to source record + ▼ +Provider dispatch + │ Dispatched to selected provider + ▼ +New Realized State events produced + │ New event stream or continuation of existing stream + │ Provenance links to rehydration Requested State +``` + +### 5.3 Placement Flag — Provider-Portable Rehydration + +When rehydrating from Requested State or Realized State, provider selection is configurable via an explicit flag in the rehydration request: + +```yaml +rehydration_request: + uuid: + source_store: + source_record_uuid: + + placement: + re_evaluate: false + # false (default): honor provider selection from source record + # Use when: original provider is available and appropriate + # Result: resource reproduced on same provider + # + # true: strip provider selection, run placement policies fresh + # Use when: original provider unavailable, decommissioned, + # at capacity, or no longer sovereign-compliant + # Result: placement policies select provider from current landscape + # Named concept: Provider-Portable Rehydration + + placement_constraints: + # Optional — additional constraints for re-evaluation + # Only applicable when re_evaluate: true + exclude_provider_uuids: [, ...] + require_region: + require_sovereignty_capability: + + governance: + apply_all_policies: true + # Always true — governance is never skippable + # Included explicitly for auditability — the rehydration record + # must declare that governance was applied + + policy_version: current + # current (default): apply today's policies + # pinned: apply policies as of a specific timestamp + # Use when: exact historical reproduction required + # (audit evidence, regulatory examination, environment reconstruction) + # Requires elevated authorization — bypasses current GateKeeper policies + # Only SRE and Admin actors may use pinned policy version + + pinned_timestamp: + # Required when policy_version: pinned + + rehydration_reason: + requested_by_uuid: +``` + +### 5.4 The Four Rehydration Modes + +Two independent axes — placement and policy version — produce four distinct rehydration configurations: + +| Mode | re_evaluate | policy_version | Use Case | +|------|-------------|----------------|----------| +| **Faithful** | false | current | Same provider, current governance | +| **Provider-Portable** | true | current | New provider, current governance | +| **Historical Exact** | false | pinned | Same provider, historical governance (audit evidence) | +| **Historical Portable** | true | pinned | New provider, historical governance | + +Historical modes require elevated authorization. All modes run governance — the difference is whether governance uses current or pinned policies. + +### 5.5 Rehydration Tenancy and Sovereignty Controls + +**Tenancy controls, sovereignty directives, and cross-tenant authorizations are always evaluated against current policies during rehydration — they cannot be pinned to historical versions.** + +The `policy_version: pinned` setting governs resource configuration policies only. It does not apply to: +- Tenancy boundary enforcement +- Sovereignty constraints +- Cross-tenant authorization requirements + +```yaml +rehydration: + policy_version: pinned # governs resource configuration policies + # The following ALWAYS use current policies — cannot be pinned: + tenancy_controls: always_current + sovereignty_controls: always_current + cross_tenant_authorizations: always_current +``` + +**When rehydration conflicts with current tenancy controls:** + +If the current policy environment produces a tenancy or sovereignty constraint that conflicts with a cross-tenant allocation valid at original request time — for example, the consuming Tenant's authorization was revoked since the original request — the rehydration is **paused**, not failed or silently bypassed: + +``` +Rehydration detects cross-tenant authorization conflict + │ + ▼ +Entity enters PENDING_REVIEW state + │ Allocation is not automatically released + │ Rehydration_tenancy_conflict_record created + ▼ +Notifications dispatched: + │ entity owner, owning Tenant admin, + │ consuming Tenant admin, platform admin + ▼ +Resolution options: + re_authorize → issue new cross_tenant_authorization for this allocation + release → release the allocation, entity decommissioned + escalate → refer to platform admin for manual decision + │ + └── A policy may declare automatic resolution: + "on rehydration conflict → re_authorize if consuming Tenant + still meets sovereignty requirements" +``` + +**System policies for rehydration tenancy:** + +| Policy | Rule | +|--------|------| +| `RHY-001` | Tenancy, sovereignty, and cross-tenant authorizations always use current policies during rehydration — cannot be pinned | +| `RHY-002` | Rehydration that conflicts with current tenancy/sovereignty pauses and enters PENDING_REVIEW | +| `RHY-003` | A paused rehydration allocation is not automatically released — requires explicit resolution | +| `RHY-004` | A policy may declare automatic resolution behavior for rehydration tenancy conflicts | + +### 5.6 Partial Resolution of Q54 — Provider Selection + +The placement flag model clarifies the Q54 question (selected_provider as policy output vs placement component). The emerging answer: + +**Policies set placement constraints — the placement component selects the provider.** + +A GateKeeper policy may output: "must be in region EU-WEST, must support sovereignty capability PCI-DSS." The placement component reads these constraints and selects the specific provider within those constraints. The policy does not name the provider. The placement component names the provider. + +This is consistent with the portability model — a policy that names a specific provider would be portability-breaking. Policies set constraints. Placement honors constraints and selects. + +--- + +## 6. Drift Detection + +Drift is the difference between what DCM believes exists (Realized State) and what actually exists (Discovered State). + +### 6.1 Drift Detection Flow + +``` +Discovery cycle completes + │ Provider interrogated → Discovered State snapshot written + ▼ +Drift Detection component + │ Loads latest Discovered State for entity UUID + │ Loads latest Realized State events for entity UUID + │ Field-by-field comparison + ▼ +No drift detected + │ Discovery timestamp updated + │ No action + ▼ +Drift detected + │ Drift record created with: + │ - entity_uuid + │ - drifted_fields: [{field_path, realized_value, discovered_value}] + │ - discovery_timestamp + │ - drift_severity: + ▼ +Policy Engine evaluates drift + │ Drift response policy determines action: + │ REVERT: submit a rehydration request from Realized State to restore + │ UPDATE_DEFINITION: promote discovered state to new Realized State + │ ALERT: notify personas, no automatic action + │ ESCALATE: trigger human review workflow + │ + │ Response determined by drift severity, resource type, + │ resource ownership, and organizational policy + ▼ +Audit Store records drift event with full provenance +``` + + +### 6.3 Drift Severity Classification + +Drift severity is determined by combining three independent tiers. The final severity is the highest tier that applies. + +**Tier 1 — Field criticality (declared in Resource Type Specification):** + +```yaml +resource_type_spec: + fields: + display_name: + drift_criticality: minor # non-functional change + cpu_count: + drift_criticality: significant + memory_gb: + drift_criticality: significant + security_group_ids: + drift_criticality: critical # security-relevant change + firewall_rules: + drift_criticality: critical +``` + +**Tier 2 — Profile/layer magnitude thresholds:** + +```yaml +# system/drift/severity-thresholds layer (overridable at platform/tenant domain) +drift_severity_thresholds: + significant_field_magnitude_upgrade: + percentage_change_threshold: 50 # >50% change upgrades significant → critical + minor_field_magnitude_upgrade: + item_count_threshold: 10 # 10+ changed items upgrades minor → significant +``` + +**Tier 3 — Provider and consumer injection:** + +Providers may suggest severity in update notifications (raise only): +```yaml +provider_drift_hint: + field: memory_gb + suggested_severity: critical + reason: "Memory decrease on running workload risks OOM" +``` + +Consumers may override sensitivity on specific entities (raise or lower): +```yaml +entity: + drift_sensitivity_overrides: + - field: cpu_count + override_criticality: critical + reason: "Production payments workload — any CPU change is critical" +``` + +**Resolution rule:** The Drift Detection component takes the highest severity from all three tiers. Provider injection can raise but not lower the Tier 1/2 result. Consumer injection can raise or lower (entity owner controls their own resource's sensitivity). Profile governs whether consumer lowering is permitted. + + + +### 6.3 Drift Severity Classification + +Drift severity is determined by two independent dimensions declared in the Resource Type Specification — field criticality and change magnitude. The combination produces a deterministic severity classification for any drift event. + +#### Field Criticality (declared per field in Resource Type Spec) + +```yaml +resource_type_spec: + fqn: Compute.VirtualMachine + fields: + display_name: + drift_criticality: low # cosmetic; never affects function + cpu_count: + drift_criticality: medium # affects performance; not security + memory_gb: + drift_criticality: medium + security_group_ids: + drift_criticality: critical # security boundary field; always critical + os_image: + drift_criticality: critical # security posture; always critical + storage_gb: + drift_criticality: medium + network_interface_ids: + drift_criticality: high # connectivity; significant operational impact +``` + +**Criticality levels:** `low | medium | high | critical` + +#### Change Magnitude (profile-governed thresholds) + +```yaml +drift_magnitude_thresholds: + profile_defaults: + standard: + minor: change_pct < 10% + significant: change_pct 10-50% + critical: change_pct > 50% OR value_disappeared OR type_changed + prod: + minor: change_pct < 5% + significant: change_pct 5-25% + critical: change_pct > 25% OR value_disappeared OR type_changed +``` + +#### Severity Matrix + +| Field Criticality | Change Magnitude | Drift Severity | +|------------------|-----------------|----------------| +| low | any | minor | +| medium | minor | minor | +| medium | significant | significant | +| medium | critical | significant | +| high | minor | significant | +| high | significant | significant | +| high | critical | critical | +| critical | any | critical | + +**Unsanctioned changes** (no corresponding Requested State record) are always elevated one severity level above what the matrix produces. A `significant` unsanctioned change becomes `critical`. + +**Multi-field drift:** when multiple fields drift simultaneously, the overall severity is the highest severity among all drifted fields. + + +### 6.2 Unsanctioned Changes + +A specific category of drift — a change made directly to a resource without a corresponding DCM request. Detected by: +- Kubernetes: CR spec change without DCM request annotation +- VMware/OpenStack: resource modification not traceable to a DCM Requested State record +- General: any Discovered State field value that differs from Realized State without a Requested State record explaining the change + +Unsanctioned changes are always reported to the Policy Engine as `UNSANCTIONED_CHANGE` events. Policy determines the response. + +--- + +## 7. CI/CD Integration + +The GitOps stores are the natural integration point for CI/CD pipelines. DCM does not prescribe a specific CI/CD tool — the GitOps store contract requires hook support, and the CI/CD tool is a deployment choice. + +### 7.1 CI Pipeline (Intent State) + +Triggered on: branch creation or update (new or revised intent) + +``` +CI pipeline executes: + 1. Policy pre-validation (dry run — no state changes) + → Reports: which policies would apply, what they would do + 2. Cost estimation + → Reports: estimated cost for lifecycle of this resource + 3. Dependency graph validation + → Reports: all required dependent resources, any conflicts + 4. Sovereignty constraint check + → Reports: which sovereignty constraints apply, any violations + 5. Authorization check + → Reports: does this actor have permission to request this resource type? + 6. Auto-approve evaluation + → Reports: can this be merged automatically, or does it require human review? + +All results posted as PR comments on the Intent State branch +Consumer and approvers can review and debate before merge +``` + +### 7.2 CD Pipeline (Requested State) + +Triggered on: Intent State merge (PR merged to main) + +``` +CD pipeline executes: + 1. Request Payload Processor assembles full payload + 2. Full policy evaluation (binding — not dry run) + 3. Provider selection (or re-evaluation if placement flag set) + 4. Requested State committed to Git store + 5. Provider dispatch via API Gateway + 6. Status monitoring — poll or receive callbacks until terminal state + 7. Status written back to PR or status file + 8. Consumer notification +``` + +### 7.3 The Third Rail — Direct API Ingress + +Not all requests come through the GitOps PR workflow. Some requests come through direct API submission — automated systems, CI/CD pipelines, Terraform providers, programmatic consumers. These bypass the human review workflow but not governance. + +Direct API ingress: +- Creates an Intent State record (the submitted payload becomes the intent) +- Runs the same CI validation pipeline but non-interactively +- If auto-approve policy permits: proceeds directly to assembly and dispatch +- If human review required: creates a PR for review before proceeding +- Same governance pipeline regardless of ingress path + +The three ingress paths — PR workflow, direct API, and programmatic (Terraform/Ansible) — all converge on the same governance pipeline. The ingress path affects the review workflow; it never affects governance. + +--- + +## 7a. Four States Operational Gaps — Q75 through Q78 + +### 7a.1 Entity UUID Preservation on Rehydration (Q75) + +Entity UUIDs are **preserved on rehydration**. The UUID represents the stable logical identity of the resource across provider migrations, sovereignty changes, and lifecycle events. All external references — CMDB records, cost attribution, audit trails, cross-tenant relationships, dependency declarations — reference the entity by UUID. Generating a new UUID on rehydration would silently break all of those references. + +What changes on rehydration is the **provider-side identifier** — the actual VM ID, container name, or resource handle at the provider. These are recorded in the rehydration history: + +```yaml +entity: + uuid: # PRESERVED across all rehydrations + rehydration_history: + - rehydration_uuid: + rehydrated_at: + trigger: + from_provider_uuid: + to_provider_uuid: + from_realized_entity_id: "vm-12345" # provider's ID — no longer valid + to_realized_entity_id: "vm-67890" # new provider's ID after rehydration + rehydrated_by: + intent_state_ref: + previous_requested_state_ref: + new_requested_state_ref: +``` + +**Rehydration is transactional:** If the target provider cannot accept the entity (capacity unavailable, sovereignty mismatch discovered mid-rehydration), the original entity remains in its current state with no UUID change and no partial state. Failure preserves the pre-rehydration state completely. + +### 7a.2 Pinned Authentication Level for Rehydration (Q76) + +Entities may declare a minimum authentication level required to rehydrate them. This prevents escalation of privilege through the rehydration mechanism — a resource provisioned with hardware-token MFA authorization should not be re-instantiatable by a simple API key. + +```yaml +entity: + rehydration_constraints: + min_auth_level: hardware_token_mfa + # Ascending levels: api_key | ldap_password | oidc | oidc_mfa | + # hardware_token | hardware_token_mfa + auth_level_source: + allow_delegated_rehydration: false + # true = DCM service accounts may rehydrate if explicitly authorized +``` + +**Profile-governed enforcement:** + +| Profile | Enforcement | +|---------|------------| +| `minimal` | Not enforced — any auth level may rehydrate | +| `dev` | Not enforced | +| `standard` | Advisory — warn if rehydrating actor has lower auth | +| `prod` | Enforced — reject if rehydrating actor has lower auth | +| `fsi` | Enforced — dual approval required if auth level mismatch | +| `sovereign` | Enforced — dual approval always; logged in classified audit | + +**Automated rehydration:** When DCM triggers rehydration automatically (sovereignty violation, provider decommission), the rehydration uses DCM's internal service account. This requires `allow_delegated_rehydration: true` OR a platform admin must manually authorize the operation. Authorization produces an audit record preserving accountability even when the action is automated. + +### 7a.3 Concurrent Rehydration Handling (Q77) + +Rehydration requests acquire an **exclusive rehydration lease** per entity. Only one rehydration may be active per entity at any time. + +```yaml +rehydration_lease: + entity_uuid: + lease_uuid: + acquired_by: + acquired_at: + lease_ttl: PT2H # expires after 2 hours if not released + trigger: + status: +``` + +**Concurrent request handling:** + +``` +Second rehydration attempt arrives for entity + │ + ├── No active lease → acquire lease; proceed + │ + └── Active lease exists: + Priority higher than active → escalate to platform admin; queue + Same or lower priority → reject: + "Rehydration in progress — lease held since ; retry after PT2H" + REHYDRATION_BLOCKED audit event recorded +``` + +**Priority ordering:** +1. Security/compliance emergency (sovereignty violation at fsi/sovereign) +2. Manual platform admin rehydration +3. Automated sovereignty migration +4. Provider decommission migration +5. Manual consumer rehydration request + +**Lease TTL expiry:** If rehydration hangs or crashes, the lease expires after TTL. DCM marks the rehydration `failed` in rehydration_history, releases the lease, and triggers drift detection to assess partial completion at the provider. + +### 7a.4 Discovered State Retention (Q78) + +Discovered State is ephemeral operational data — not the authoritative source of truth (Realized State is). It is a snapshot used for drift detection. Three retention modes, all profile-governed: + +```yaml +discovered_state_retention: + mode: # hybrid recommended + + rolling_window: + retention: P7D # keep last 7 days; useful for trending + + event_driven: + retain_until: drift_resolved # keep until associated drift record resolved + # Ensures drift investigation has the discovery snapshot that triggered it + + hybrid: # recommended — combines both + minimum_retention: P24H + retain_until: drift_resolved # extend beyond minimum until drift resolved + maximum_retention: P30D # hard ceiling regardless of drift status +``` + +**Profile-governed defaults:** + +| Profile | Mode | Min Retention | Max Retention | +|---------|------|--------------|--------------| +| `minimal` | `rolling_window` | — | P3D | +| `dev` | `rolling_window` | — | P7D | +| `standard` | `hybrid` | P24H | P30D | +| `prod` | `hybrid` | P48H | P30D | +| `fsi` | `hybrid` | P7D | P90D | +| `sovereign` | `hybrid` | P7D | P90D | + +**Discovered State and the Audit Store:** + +Discovered State records are **NOT** stored in the Audit Store — they are too high-volume and too ephemeral for compliance-grade storage. However, drift events triggered by Discovered State ARE recorded in the Audit Store with a reference to the discovery snapshot UUID. After the Discovered State expires, the audit record still exists — it cannot link to the full snapshot, but the drift event itself is preserved. + +--- + +## 7b. Rehydration System Policies — Complete Set + +| Policy | Rule | +|--------|------| +| `RHY-001` | Tenancy and sovereignty are always current on rehydration — they cannot be pinned to historical state. | +| `RHY-002` | Sovereignty conflicts discovered during rehydration place the entity in PENDING_REVIEW state. | +| `RHY-003` | Resource allocations are not automatically released on rehydration. | +| `RHY-004` | Rehydration leases have TTL to prevent orphaned lease states. | +| `RHY-005` | Entity UUIDs are preserved on rehydration. The UUID represents stable logical identity across provider migrations. Provider-side identifiers change on rehydration and are recorded in rehydration_history. Rehydration is transactional — failure preserves pre-rehydration state without UUID change. | +| `RHY-006` | Entities may declare min_auth_level for rehydration. Profile governs enforcement. Automated rehydration by DCM service accounts requires allow_delegated_rehydration: true OR platform admin manual authorization with full audit trail. | +| `RHY-007` | Rehydration requests acquire an exclusive lease per entity before proceeding. Only one rehydration may be active per entity. Concurrent requests are queued (higher priority) or rejected (same/lower). Lease TTL prevents indefinite blocking. Expiry triggers drift detection for partial completion assessment. | +| `RHY-008` | Discovered State retention is profile-governed: rolling_window, event_driven, or hybrid. Discovered State is never stored in the Audit Store. Drift events triggered by Discovered State are recorded in the Audit Store with discovery snapshot UUID reference. Maximum retention: P30D for standard/prod; P90D for fsi/sovereign. | + +--- + +## 8. Open Questions + +| # | Question | Impact | Status | +|---|----------|--------|--------| +| 1 | Git repository structure for Intent and Requested stores | Store design | ✅ Resolved — handle-based directory structure; 4 repos; tenant isolation (STO-005) | +| 2 | Should the entity UUID be preserved or regenerated on rehydration? | Entity identity | ✅ Resolved — UUID preserved; rehydration_history records provider-side ID changes; transactional (RHY-005) | +| 3 | For pinned policy version rehydration — what is the minimum authorization level required? | Security | ✅ Resolved — min_auth_level on entity; profile-governed enforcement; delegated rehydration requires explicit authorization (RHY-006) | +| 4 | How are concurrent rehydration requests for the same entity handled? | Concurrency | ✅ Resolved — exclusive rehydration lease; priority ordering; TTL expiry triggers drift detection (RHY-007) | +| 5 | Should the Discovered Store retain full history or only a configurable window? | Retention | ✅ Resolved — hybrid mode recommended; profile-governed min/max; event-driven until drift resolved; max P30-90D (RHY-008) | +| 6 | How does the Search Index handle Git store unavailability? | Reliability | ✅ Resolved — serve degraded (warn + direct to authoritative); rebuild on recovery (STO-002) | + +--- + +## 9. Related Concepts + +- **data store** — the formal provider type for all DCM stores +- **Entity UUID** — the universal linking key across all four states +- **Rehydration** — using a prior state record as the starting point for a new request +- **Provider-Portable Rehydration** — rehydration with provider selection re-evaluated +- **Drift Detection** — comparing Realized State against Discovered State +- **Unsanctioned Change** — a resource modification not traceable to a DCM request +- **CI/CD Integration** — GitOps stores as the natural CI/CD integration point +- **Search Index** — queryable projection of GitOps stores, explicitly non-authoritative + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* diff --git a/content/docs/architecture/data-model/03-layering-and-versioning.md b/content/docs/architecture/data-model/03-layering-and-versioning.md new file mode 100644 index 0000000..08871f8 --- /dev/null +++ b/content/docs/architecture/data-model/03-layering-and-versioning.md @@ -0,0 +1,2164 @@ +# DCM Data Model — Data Layers and the Assembly Process + + + +**Document Status:** ✅ Complete +**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Entity Types](01-entity-types.md) | [Four States](02-four-states.md) | [Resource Type Hierarchy](05-resource-type-hierarchy.md) + +> **Foundation Document Reference** +> +> This document is a detailed reference for a specific domain of the DCM architecture. +> The three foundational abstractions — Data, Provider, and Policy — are defined in +> [00-foundations.md](00-foundations.md). All concepts in this document map to one or +> more of those three abstractions. +> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) +> +> **This document maps to: DATA** +> +> The Data abstraction — how Data is assembled from layers + + + +--- + +## 1. Purpose + +Data Layers are the mechanism by which DCM assembles a complete, contextually correct request payload from a set of composable, reusable data definitions. Rather than requiring consumers to specify every field of every resource they request, layers allow standards, organizational context, service-specific configuration, and consumer intent to be declared independently and merged into a unified payload at request time. + +Layers are the answer to the question: **how does a single consumer request become a complete, policy-validated, provider-ready payload?** + +The layering model enables: +- **Reuse** — a base configuration defined once is inherited by thousands of resources +- **Standardization** — organizational standards are encoded in layers, not in every individual request +- **Separation of concerns** — infrastructure teams own core and service layers; consumers own request layers; policy owners own policy layers +- **Scale** — 36 layer definitions can govern 40,000 VMs without duplication +- **Auditability** — every field in the merged payload knows which layer set it and why + +--- + +## 1a. Layers vs Policies — The Clear Distinction + +Layers and policies are the two foundational mechanisms of DCM's assembly process. They are complementary and distinct — understanding the difference is critical to using DCM correctly. + +### Layers Are Data + +A layer is a **declarative, immutable, versioned unit of data**. It carries static configuration values, organizational defaults, compliance metadata, and contextual information. A layer answers the question: **"what values should these fields have?"** + +Layers are **passive** — they declare values but do not execute logic. They do not evaluate the payload, make branching decisions, or enforce rules. The assembly process merges them in priority order. Layers come first. + +**What belongs in a layer:** +- Infrastructure defaults (DNS servers, NTP servers, MTU values) +- Organizational context (data center location, rack assignment, environment tier) +- Service-specific configuration defaults (VM sizing defaults, storage class preferences) +- Compliance metadata (data classification labels, retention tags, jurisdiction markers) +- Provider-specific configuration (provider default settings, tooling parameters) +- Business context (cost center defaults, environment labels, team tags) + +### Policies Are Logic + +A policy is an **executable rule** that evaluates the assembled payload and takes action. A policy answers the question: **"given this data, is it valid? what should change? should this proceed?"** + +Policies **execute** — they run logic (OPA Rego, DCM native rules, external evaluation calls). They can read every layer-provided value, validate correctness, transform fields, inject derived values, and gate requests. Policies come after layers — they operate on the assembled result. + +**What belongs in a policy:** +- Validation rules ("this field must be present and within these bounds") +- Compliance enforcement ("all resources must have a classification label") +- Derived value injection ("inject cost center from OIDC claims") +- Placement constraints ("must be in EU sovereignty zone") +- Approval gates ("resources above X size require manager approval") +- Security enforcement ("encryption must be enabled — if not, enable it or reject") + +### The Flow Is Strictly Unidirectional + +``` +Steps 1-4: LAYERS assembled → merged payload produced + │ Layers contribute field values + │ Higher priority layers override lower priority + │ Immutable fields locked at this stage + │ + ▼ +Steps 5-9: POLICIES execute → payload evaluated and acted upon + │ Policies read assembled payload + │ Transformation: modify/inject derived fields + │ Validation: verify correctness + │ GateKeeper: approve or reject + │ + ▼ +Provider-ready payload dispatched +``` + +Policies cannot set static configuration — that is a layer's job. A policy that finds itself repeatedly injecting the same static value into every request should be refactored: that value belongs in a layer. + +Layers cannot enforce rules — that is a policy's job. A layer that contains conditional logic or rule evaluation is being misused — that logic belongs in a policy. + +### The Decision Rule for Practitioners + +> "Is this a **value** that should appear in the payload? → **Layer** +> Is this a **rule** about whether the payload is correct? → **Policy** +> Is this a **value derived by evaluating** the payload? → **Policy** (Transformation type)" + +### The Analogy + +- Layers are the **ingredients** — pre-measured, pre-arranged, versioned +- Policies are the **chef** — decides what to do with the ingredients, can add derived elements, makes judgment calls, can reject the dish entirely + +Both are necessary. Neither replaces the other. + +--- + +## 2. What is a Layer? + +A Layer is a **declarative, immutable, versioned unit of data** that contributes some or all of its fields to a merged payload. Layers do not execute — they declare. The assembly process is what merges them. + +Every layer: +- Has a **UUID** that uniquely identifies it +- Has a **version** following the universal Major.Minor.Revision scheme +- Is **immutable once published** — changes produce a new version +- Carries a **reference to its parent entity** (UUID and version) +- Has an **origination timestamp** +- Can be **deprecated** following the universal deprecation model +- Contributes **provenance metadata** for every field it sets — any field set by a layer records that layer's UUID as its source + +Layers are stored in Git following GitOps practices. They are the configuration source of truth — not the assembled payload. + +--- + +## 3a. Provenance Model Configuration + +### 3a.1 The Three Provenance Models + +Field-level provenance tracks which layer set each field, which policy modified it, and the full change history. DCM supports three configurable models — organizations choose based on their scale, compliance requirements, and operational preferences. The active Profile provides a recommended default via its activated Policy Group. + +**Model A — Full Inline** +All provenance stored explicitly on every entity record. Every field carries its complete provenance inline: source layer, modifying policies, previous values, timestamps, actor chain. + +| Aspect | Detail | +|--------|--------| +| Storage cost | Very high — scales with entities × fields × changes | +| Query simplicity | Highest — all provenance in one record, no traversal | +| Write performance | Lowest — every field change requires provenance write | +| Audit clarity | Highest — regulators see everything in one record | +| Tooling required | Minimal | +| Best for | Small deployments; FSI/sovereign (regulatory clarity); home lab | + +**Model B — Deduplicated (Content-Addressed) ← RECOMMENDED** +Classical content-addressed deduplication applied to provenance. The layer chain is the deduplication key — every entity sharing the same configuration references the same chain rather than storing a copy. Only fields deviating from the chain store unique delta records. + +``` +Full provenance = layer chain content (deduplicated, shared) + entity deltas (unique per entity) +``` + +**Why lossless:** Layer chains are immutable. A reference to `layer-chain-abc123` always resolves to exactly the same data — no cache invalidation, no drift. This is what makes the deduplication lossless for audit. The reference always reconstructs the original. + +**Storage reduction:** 95-99% for standardized deployments (many entities, few unique chains). 36 layer definitions serving 40,000 VMs produces 36 chain references, not 8 million field provenance entries. + +| Aspect | Detail | +|--------|--------| +| Storage cost | Low — scales with unique configurations, not entity count | +| Query simplicity | Medium — chain traversal required for layer-set fields | +| Write performance | Highest — only deltas write; chain-matching fields are free | +| Audit clarity | Complete — full reconstruction always possible | +| Tooling required | Moderate — chain traversal tooling | +| Best for | Standard and prod deployments; large-scale environments | + +**Analogous to:** Git content-addressed objects, Docker image layers, ZFS block deduplication — all content-addressed, deduplicated, lossless. + +**Model C — Tiered Archive** +Hot/warm/cold storage tiers with decreasing detail. Recent provenance at full detail and fast access; older provenance compressed to change events; oldest compressed to hash anchors only (tamper-evidence without full reconstruction). + +| Aspect | Detail | +|--------|--------| +| Storage cost | Medium — time-dependent, degrades gracefully | +| Query simplicity | Medium — cross-tier joins for long time ranges | +| Write performance | Medium | +| Audit clarity | Full detail in hot tier; change events in warm; anchors in cold | +| Tooling required | Moderate — tier promotion jobs, consistency checks | +| Best for | Large deployments with long retention requirements | + +**Models B and C are orthogonal** — combine them for maximum efficiency: deduplicate at the entity level (Model B) AND tier the storage of chains and deltas (Model C). This is the highest-efficiency option for very large-scale deployments with long retention requirements. + +### 3a.2 Configurable Provenance Model + +The provenance model is declared in the DCM deployment configuration and activated via a Policy Group: + +```yaml +provenance_config: + model: + + # Model A — Full Inline + full_inline: + include_previous_values: true + include_actor_chain: true + include_policy_rationale: true + + # Model B — Deduplicated (Content-Addressed) + layer_chain_ref: + store_layer_derivable: false # do not store fields matching chain default + delta_detail_level: + history_document_retention: P7Y + chain_store_retention: P7Y # chains retained while any entity references them + + # Model C — Tiered Archive + tiered: + hot_tier_duration: P30D # full detail, fast access + warm_tier_duration: P365D # change events only + cold_tier_duration: P10Y # hash anchors only + warm_tier_detail: + + # Model B + C — Deduplicated + Tiered (maximum efficiency) + layer_chain_ref_tiered: + chain_store_hot: P365D # chains fast for 1 year + chain_store_warm: P7Y # chains slower for 7 years + delta_store_hot: P90D # deltas fast for 90 days + delta_store_warm: P7Y # deltas slower for 7 years +``` + +### 3a.3 Profile-Appropriate Provenance Policy Groups + +DCM ships four provenance Policy Groups. The active Profile activates the appropriate group by default. Organizations override by swapping the active group. + +| Group Handle | Model | Profile Default | Concern Type | +|-------------|-------|----------------|-------------| +| `system/group/provenance-full-inline` | A — Full Inline | minimal, dev, fsi, sovereign | implementation_posture | +| `system/group/provenance-deduplicated` | B — Deduplicated | standard, prod | implementation_posture | +| `system/group/provenance-tiered-archive` | C — Tiered | (available — not default) | implementation_posture | +| `system/group/provenance-deduplicated-tiered` | B+C — Combined | (available for large-scale) | implementation_posture | + +**To change provenance model:** +```yaml +# Override profile default — swap the active provenance group +tenant_config: + policy_group_overrides: + replace: + - from: system/group/provenance-full-inline + to: system/group/provenance-deduplicated + reason: "Deploying at scale — switching to deduplicated model" +``` + +### 3a.4 The Audit Completeness Guarantee + +Regardless of provenance model, full provenance must always be reconstructable: + +``` +OPS-002 Regardless of provenance model, full provenance must always be + reconstructable for any entity from the combination of: entity + record, layer chain store, and Audit Store. The provenance model + governs where data is stored and how it is accessed — not whether + it is available. +``` + +For Model B: chain reference + entity deltas → full provenance (lossless, immutable source) +For Model C: hot tier (full) OR warm tier (events) + cold tier (anchors prove integrity) +For Model A: entity record alone is sufficient + +### 3a.5 System Policies + +| Policy | Rule | +|--------|------| +| `OPS-001` | Field-level provenance model is configurable: full_inline, layer_chain_ref (deduplicated), tiered, or layer_chain_ref_tiered. Profile activates the appropriate Policy Group as default. Organizations override by replacing the active provenance group. Model B (layer_chain_ref) is the recommended default for standard+ profiles. | +| `OPS-002` | Regardless of provenance model, full provenance must always be reconstructable from the combination of entity record, layer chain store, and Audit Store. The provenance model governs storage location and access pattern — not data availability. | + +--- + + +### 2a. Layer Contributors + +Every layer type has a declared contributor type. The contributor determines what review is required before the layer becomes active in assembly. See [Federated Contribution Model](28-federated-contribution-model.md) Section 3 for the full contributor permission table. + +| Layer Type | Contributor | Domain | Review | +|-----------|-------------|--------|--------| +| Base Layer | Platform Admin | system | auto | +| Core Layer | Platform Admin | platform | auto | +| Intermediate / Customization Layer | Platform Admin, Consumer/Tenant | platform, tenant | per profile | +| Service Layer | Platform Admin, Service Provider | provider | reviewed (standard+) | +| Request Layer | Consumer/Tenant | tenant | auto (applied directly to request) | +| Policy Layer | All contributor types | per contributor role | per profile + contributor type | + +The Request Layer is the only layer type that does not require a PR review — it is a consumer's direct field declarations on a specific request. All other layers flow through the GitOps PR model. + + +## 3. Layer Types + +DCM defines six layer types. Each has a distinct purpose, scope, ownership model, and position in the assembly precedence chain. + +### 3.1 Base Layer + +**Purpose:** The foundation entity for a resource. Defines the minimum required fields and their default values for a given resource context. Everything starts with a Base Layer. + +**Scope:** Can be type-agnostic (a universal base) or type-scoped (a base specific to a Resource Type). A Base Layer that is type-scoped must declare its Resource Type. + +**Ownership:** DCM platform or platform implementor. + +**Characteristics:** +- Every layer chain must begin with a Base Layer +- Base Layers contain only universal fields — no provider-specific data +- A Base Layer for a typed resource must conform to the Resource Type Specification's universal field requirements +- Multiple Base Layers can exist for the same context — the applicable one is selected based on the request context + +**Examples:** +- CIS Benchmark base configuration +- Baseline OS configuration +- DMZ network base configuration + +--- + +### 3.2 Core Layers + +**Purpose:** Provide data that is applicable across any resource type. Core Layers carry organizational, infrastructure, and contextual data that is not specific to any one service. + +**Scope:** Type-agnostic by default. Core Layers apply to all resource types unless explicitly scoped. This is the primary distinction from Service Layers. + +**Ownership:** DCM platform, infrastructure teams, or platform implementors. + +**Characteristics:** +- Applied to every request regardless of resource type +- Cannot contain service-specific or provider-specific data +- Carry location, organizational, and infrastructure context +- Stored in the Core Layer Store +- Cached in the Service Layer Cache at deployment time + +**Examples:** +- Data Center layer (DC1, DC2) +- Zone layer (Zone 1, Zone 2) +- Rack layer +- Geographic region layer +- Environment layer (production, staging, development) + +> **Location Topology:** Location layers are one application of the Reference Data +> Layer pattern (Section 3.7). The standard schema for each location level — +> Country, Region, Zone, Site, Data Center, Hall, Cage, and Rack — is specified +> in [Location Topology Layer Model](48-location-topology-layers.md), including +> field definitions, priority bands, authority model, and hierarchy assembly. + +--- + +### 3.3 Intermediate / Customization Layers + +**Purpose:** Provide organizational or contextual overrides and customizations that sit between the base standards and the service-specific configuration. These layers encode the organizational hierarchy and deployment context. + +**Scope:** Can be type-agnostic or type-scoped. Scope is declared per layer. + +**Ownership:** Organizational teams, domain owners, platform implementors. + +**Characteristics:** +- Stack between Core Layers and Service Layers in the precedence chain +- Encode organizational structure (business unit, enclave, logical unit) +- Allow organizational customization without modifying base standards +- The Git repo hierarchy typically mirrors the intermediate layer hierarchy + +**Examples:** +- Ship layer (in Navy context: specific vessel configuration) +- Enclave layer (isolated network segment configuration) +- Business unit layer +- DMZ customization layer +- Production web tier layer + +--- + +### 3.4 Service Layers + +**Purpose:** Provide service-specific data required to build a complete request payload for a specific Resource Type. Service Layers are the bridge between general organizational context and provider-ready configuration. + +**Scope:** **Must be type-scoped.** A Service Layer without a declared Resource Type scope is invalid. The scope inheritance behavior is configurable per Service Layer declaration. + +**Type Scope Declaration:** +```yaml +type_scope: + resource_type_uuid: + resource_type_fully_qualified_name: + scope_inheritance: + # exact: applies only to the declared Resource Type + # descendants: applies to the declared Resource Type and all child types via inheritance +``` + +**Ownership:** Service Providers or service domain teams. Stored in Service Layer SCM (source control management). Registered with DCM as part of Service Provider registration. + +**Characteristics:** +- Only applied when the request resource type matches the layer's declared type scope +- Carry service-specific configuration, defaults, and constraints +- Must not contain provider-specific data unless marked as portability-breaking +- Cached in the Service Layer Cache at Service Provider registration time + +**Examples:** +- VM sizing layer (small, medium, large configurations for `Compute.VirtualMachine`) +- Web server configuration layer for `Compute.VirtualMachine` +- Network port configuration layer for `Network.Port` +- CL Web Service Data Layer for `Compute.VirtualMachine` (exact scope) +- General compute placement layer for `Compute.VirtualMachine` and descendants + +--- + +### 3.5 Request Layer + +**Purpose:** Carries the consumer's declared intent. The Request Layer is what the consumer provides — the fields they explicitly specify for their resource request. + +**Scope:** Scoped to the Resource Type the consumer is requesting. + +**Ownership:** Consumer (via Web UI or Consumer API). + +**Characteristics:** +- Created at the time the consumer submits a request +- Contains only what the consumer explicitly declares — it does not need to be complete +- The gap between what the consumer declares and what the provider needs is filled by the lower layers in the chain +- Has higher precedence than all data layers below it — consumer-declared values override layer defaults +- Is the direct source of the **Intent State** — the Request Layer as submitted by the consumer is stored in the Intent Store before any processing occurs +- After assembly and policy processing, the enriched payload becomes the **Requested State** + +**Examples:** +- Consumer requests a VM with `cpu_count: 8`, `ram_gb: 32`, `os: RHEL9`, `environment: production` +- Consumer requests a firewall rule with source/target network and port + +--- + +### 3.6 Policy Layers + +**Purpose:** Policy Layers are not data layers in the traditional sense — they do not add fields to the merge chain. Instead, they operate on the assembled payload after the data layers have been merged. They are the governance layer of the assembly process. + +**Scope:** Scoped by policy type and domain. Core Policies apply to all requests. Service Policies apply to specific Resource Types. Organizational and domain policies apply to specific organizational scopes. + +**Ownership:** Policy creators, security teams, compliance teams, organizational domain owners. + +**Policy Layer Types and Their Behavior:** + +| Policy Type | Behavior | Precedence Effect | +|-------------|----------|-------------------| +| **Validation** | Checks data against rules. Does not modify data. Returns pass/fail. If fail, request is rejected. | No precedence — pass/fail only | +| **Transformation** | Enriches or modifies data in the payload. Adds missing fields, applies standards, fills gaps. | Adds to or modifies the assembled payload — recorded in provenance | +| **GateKeeper** | Highest authority. Can override any field regardless of what was declared in lower layers or the Request Layer. Can halt execution entirely. Used for sovereignty constraints, security mandates, and hard compliance rules. | Overrides everything — including consumer input | + +**Characteristics:** +- Policies operate only on the policy definition, core data, and the data in the request payload +- Policy outcomes are deterministic — same input always produces same output for a given policy version +- All policy modifications are recorded in field-level provenance with policy UUID, operation type, and reason +- Policies are versioned using the universal versioning scheme +- Policies are maintained via GitOps practices + +--- + +### 3.7 Reference Data Layers + +**All of DCM's data — including Resource Type Specifications — is built on the same +layer model.** A Resource Type Specification is itself a data layer artifact: versioned, +owned by a declared Resource Type Authority, stored in GitOps, subject to the standard +lifecycle, and subject to the same domain-based access control as all other layers. The +only distinction is that Resource Type Specifications live in the Resource Type Registry +(which is itself a specialized layer store) rather than the Core Layer Store. + +This unified model means: +- The same governance tooling manages resource type definitions, reference data, and + service configuration +- The same lifecycle (developing → proposed → active → deprecated → retired) applies + to all three +- The same ownership declarations, the same GitOps workflow, the same authority tiers +- Provider extension layers (domain: `provider`) extend resource types for a specific + catalog item without modifying the Resource Type Specification itself — they are + injected during payload assembly only when that catalog item is selected + +**Purpose:** Provide governed, versioned sets of allowed values for use as field +constraints in Resource Type Specifications and Provider Catalog Items. Reference Data +Layers are the source of truth for any enumerated choice a consumer makes when +requesting a resource. + +**Scope:** Declared per layer type. A Reference Data Layer of type `os_image` is only +valid as a constraint source for fields that declare `layer_type: os_image`. Reference +Data Layers are not injected into the assembled request payload in the same way as other +layers — they are resolved at catalog render time to produce the `allowed_values` list +for a field constraint. + +**Ownership:** The team or authority responsible for governing that category of data. + +| Layer Type | Owning Authority | What it governs | +|------------|-----------------|-----------------| +| `location.*` | Data Center Operations | Where resources can be placed | +| `os_image` | Platform Security / OS Team | Approved OS images and versions | +| `vm_size` | Platform Team | Approved VM size profiles | +| `network_zone` | Network Operations | Available network zones and their properties | +| `environment` | Platform Governance | Deployment environments and their policy sets | +| `storage_class` | Storage Operations | Available storage tiers | +| `gpu_profile` | Platform Team | Approved GPU configurations | + +**Characteristics:** +- Same lifecycle as all other layers: `developing → proposed → active → deprecated → retired` +- Same governance: GitOps workflow, owned by declared authority, approved by designated tier +- Same versioning: `Major.Minor.Revision` — breaking changes require a major bump +- Same security: domain-based access control, same policy enforcement +- **Not merged into the request payload directly** — they are resolved at catalog render time + to produce the field constraint `allowed_values` list +- **Their structured data IS injected** when a consumer selects a value: selecting a + location layer injects all location context; selecting an OS image layer injects + image UUID, SHA, version, and EOL date into the payload + +**Adding a new allowed value** for any layer-referenced field means adding a new +Reference Data Layer instance of the appropriate type. No changes to the Resource Type +Specification, the catalog item, or any policy. The new value becomes available to +all catalog items that reference that layer type on the next sync cycle. + +**Retiring an allowed value** means retiring the Reference Data Layer instance. +Existing resources that used that value are unaffected. Future requests cannot +select it. + +**Example — OS Image layer:** + +```yaml +layer: + artifact_metadata: + uuid: + handle: "platform/os-images/rhel-9-4-approved" + version: "1.0.0" + status: active + owned_by: + display_name: "Platform Security Team" + group_handle: "groups/platform-security" + created_via: pr + + layer_type: reference_data + reference_data_type: os_image + scope: type_agnostic + + priority: + value: "150.01.0" + label: "platform.reference.os-image.rhel-9-4" + category: platform_reference + + data: + image_name: "RHEL 9.4" + image_uuid: "img-rhel-9-4-20260315" + image_sha256: "a1b2c3d4..." + os_family: rhel + major_version: 9 + minor_version: 4 + release_date: "2026-03-15" + eol_date: "2032-05-31" + approved_for_classifications: [public, internal, confidential, restricted] + cis_benchmark_version: "CIS RHEL 9 Benchmark v1.0" + fips_compliant: true + + concern_tags: [os-image, rhel, approved, fips-compliant] +``` + +**Example — VM Size layer:** + +```yaml +layer: + artifact_metadata: + uuid: + handle: "platform/vm-sizes/medium-general-purpose" + version: "2.1.0" + status: active + owned_by: + display_name: "Platform Team" + group_handle: "groups/platform-team" + created_via: pr + + layer_type: reference_data + reference_data_type: vm_size + scope: type_agnostic # applies across providers for Compute.VirtualMachine + + data: + size_name: "Medium — General Purpose" + size_code: "gp-medium" + display_name: "Medium (8 CPU / 32 GB)" + cpu_count: 8 + memory_gb: 32 + storage_gb: 80 + network_bandwidth_gbps: 10 + approved_for_workloads: [web, application, database, batch] + cost_tier: standard + + concern_tags: [vm-size, general-purpose, approved] +``` + +--- + + +## 4. Layer Identity — Domain, Handle, and Priority + +Every layer has a formal identity model with three components that together make it uniquely identifiable, locatable, and orderable within DCM. + +### 4.1 Layer Domain + +The **Layer Domain** mirrors the Policy domain model exactly. It declares ownership, storage location, and authorization scope. The same domain hierarchy, the same authority model, the same override precedence. + +| Domain | Meaning | Authorization | Can Override | +|--------|---------|--------------|-------------| +| `system` | DCM built-in layers — ship with DCM | DCM maintainers only | Nothing above system | +| `platform` | Platform team layers — apply across all Tenants | Platform team | tenant, service, provider | +| `tenant` | Tenant-specific layers — scoped to one Tenant | Tenant Admin | service, provider within Tenant | +| `service` | Service Provider contributed layers | Service Provider owner | provider | +| `provider` | Provider Catalog Item layers | Provider owner | Nothing above provider | +| `request` | Consumer-declared values in the request itself | Consumer | Nothing above request — lowest authority | + +A lower-domain layer cannot override a higher-domain layer. + +**Provider extension layers** (`domain: provider`) are contributed by Service Providers +as part of their catalog item registration. They add provider-specific fields to the +assembled payload for their offering only. They cannot override platform or tenant layers. +They carry the same portability implications as inline `provider_specific_extensions` in +the catalog item declaration — any request using them is non-portable and must be +explicitly marked. See [Resource Type Hierarchy](05-resource-type-hierarchy.md) Section 6.2 +for the catalog item `provider_extension_layer_handles` declaration. A `tenant` layer cannot override a `platform` layer. This is enforced at ingestion — the conflict detection pipeline checks domain authority before allowing a merge. + +**Domain mirrors policy authority:** Just as system-domain policies have highest authority in the Policy Engine, system-domain layers have highest authority in the assembly process. The same mental model applies to both. + +### 4.2 Layer Groups — DCMGroup with group_class: layer_grouping + +Just as Policy Groups organize policies into cohesive concern-based collections, **Layer Groups** organize layers. A Layer Group is a `DCMGroup` with `group_class: layer_grouping` — a versioned, audited, GitOps-managed collection of related layers. + +Layer Groups enable: +- **Discovery** — "show me all layers related to PCI compliance" +- **Composition** — include a group in a profile rather than listing individual layers +- **Governance** — activate or deactivate a concern's worth of layers in one operation + +```yaml +# A Layer Group — DCMGroup with group_class: layer_grouping +dcm_group: + artifact_metadata: + uuid: + handle: "platform/layer-groups/pci-network-standards" + version: "1.0.0" + status: active + group_class: layer_grouping + concern_tags: [pci-dss, networking, standards] + members: + - member_uuid: + member_type: layer + member_role: network_segmentation_defaults + - member_uuid: + member_type: layer + member_role: firewall_baseline + - member_uuid: + member_type: layer + member_role: tls_minimum_version +``` + +### 4.3 The Full Layer Structure + +Every layer carries: identity, domain and authority, compatibility metadata, per-field override metadata, and usage context. This mirrors the richness of a Policy registration. + +```yaml +layer: + artifact_metadata: + uuid: + handle: "platform/core/default-dns-config" + version: "1.2.0" + status: active + owned_by: + display_name: "Platform Infrastructure Team" + notification_endpoint: + created_via: pr # pr | api | migration | system + + # DOMAIN AND AUTHORITY + domain: platform + priority: + value: "500.20.0" + label: "platform.networking.dns" + category: platform + rationale: "Platform DNS infrastructure — primary and secondary resolvers" + + # CONCERN TAGS — for discoverability and grouping + concern_tags: [networking, dns, platform-defaults] + + # COMPATIBILITY METADATA — what this layer applies to + compatibility: + resource_types: [Compute.VirtualMachine, Compute.Container] + resource_type_versions: "^1.0.0" + provider_types: [] # empty = all providers + profile_constraints: [] # empty = all profiles; or: [standard, prod, fsi] + domains_applicable: [platform, tenant, service, provider] # which domains may use this + + # CONDITIONAL INCLUSION (Q23) — activation condition + activation_condition: + # Layer only included if this condition evaluates true during Step 2 (Layer Resolution) + field: tenant.tags + operator: not_contains # equals|not_equals|exists|not_exists|contains|in|not_in + value: custom-dns + # Compound conditions: + # conditions: + # operator: and # and | or + # rules: + # - field: request.gpu_requested + # operator: equals + # value: true + # - field: ingress.actor.roles + # operator: contains + # value: developer + + # FIELDS — with per-field override metadata + fields: + dns_servers: + value: [10.0.0.53, 10.0.0.54] + metadata: + override: allow # allow | constrained | immutable + basis_for_value: "Platform DNS infrastructure — primary and secondary" + provenance_note: "Set by platform infrastructure team per INFRA-2024-089" + dns_search_domain: + value: corp.example.com + metadata: + override: immutable # lower layers cannot override this field + locked_by_policy_uuid: + basis_for_value: "Corporate domain — cannot be customized per SECURITY-2024-034" + + # USAGE CONTEXT — human documentation embedded in the artifact + usage: + description: "Default DNS configuration for all platform VMs and containers" + applies_when: "All requests unless consumer declares layer exclusion or tenant has custom-dns tag" + excludes_when: "Tenant has custom_dns tag; consumer declares explicit layer exclusion" + supersedes: [] # handles of layers this replaces + conflicts_with: [] # handles of layers this conflicts with — detected at ingestion + + # SOURCE OF TRUTH + scm_location: + repository: https://git.corp.example.com/dcm-layers + path: platform/core/default-dns-config/v1.2.0.yaml + commit: +``` + +### 4.4 Layer Handle + +The **Layer Handle** is the human-readable, stable identifier for a layer within DCM. + +**Format:** `{domain}/{concern_or_type}/{name}` + +**Examples:** +``` +platform/core/cis-benchmark-linux +platform/core/security-cpu-limits +tenant/service/payments-vm-standards +system/base/universal-defaults +service/service/kubevirt-vm-defaults +provider/service/cloudnativepg-database-config +``` + +**Git path from handle:** +``` +{layer_store_root}/{domain}/{concern_or_type}/{name}/v{Major}.{Minor}.{Revision}.yaml + +# Example: +dcm-layers/platform/core/security-cpu-limits/v1.2.0.yaml +dcm-layers/tenant/{tenant-uuid}/service/payments-vm-standards/v1.0.0.yaml +``` + +### 4.5 Priority Schema + +The **Priority Schema** is the deterministic ordering mechanism for resolving conflicts between layers of the same type and scope. + +**Format:** `{integer}.{integer}.{integer}...` — unlimited depth + +**Comparison:** Left-to-right, segment by segment. **Higher numeric value = higher priority** (higher value = higher priority). No ceiling — you can always go higher. + +``` +900.10 beats 800.10 (900 > 800 at segment 1) +900.20 beats 900.10 (20 > 10 at segment 2) +900.10.5 beats 900.10 (longer path with matching prefix) +900.10.10 beats 900.10.5 (10 > 5 at segment 3) +``` + +**Reference Priority Taxonomy (advisory — not enforced by DCM):** + +| Suggested Range | Category | Rationale | +|-----------------|----------|-----------| +| `900.*` | Compliance | Regulatory mandates — highest authority | +| `800.*` | Security | Security standards | +| `700.*` | Sovereignty | Data residency constraints | +| `600.*` | Operations | SRE and operational standards | +| `500.*` | Platform | Platform-level defaults | +| `400.*` | Service | Service-specific configuration | +| `300.*` | Organization | Organizational defaults | +| `200.*` | Site | Location-specific overrides | +| `100.*` | Custom | Implementor-defined — lowest standard category | + +Higher number = higher priority. Organizations adopt, adapt, or ignore this taxonomy — DCM resolves conflicts purely by numeric comparison. + +--- + +## 4b. Artifact Metadata Standard + +Every DCM artifact — layers, policies, resource types, catalog items, provider registrations, entity definitions, and all other defined or stored objects — carries a standard **Artifact Metadata** block. This is a structural requirement, not optional. + +The artifact metadata block answers: **who created this, when, who owns it, what changed, and how do we contact them?** + +### 4b.1 Universal Artifact Metadata Structure + +```yaml +artifact_metadata: + + # Identity + uuid: + handle: + + # Versioning + version: + status: + + # Status detail — populated per status + status_detail: + # When status: proposed + proposed_at: + proposed_by: + uuid: + display_name: + email: + shadow_execution: + enabled: + started_at: + evaluation_count: + validation_dashboard_url: + + # When status: deprecated + deprecated_at: + deprecated_by: + uuid: + display_name: + replacement_uuid: + replacement_handle: + deprecation_reason: + migration_guidance: + sunset_date: + + # When status: retired + retired_at: + retired_by: + uuid: + display_name: + + # Origination + created_by: + uuid: + display_name: + email: + notification_endpoint: + created_at: + created_via: + # pr: submitted via GitOps PR workflow — full review history available + # api: submitted via direct API + # migration: imported from external system — provenance depth may be limited + # system: created by DCM itself (entity stubs, system artifacts) + + # Ownership — may differ from creator + owned_by: + uuid: + display_name: + email: + notification_endpoint: + # Note: created_by is the audit record (who physically submitted it) + # owned_by is the accountability record (who is responsible and gets notified) + + # Modification history — append-only + modifications: + - sequence: 1 + modified_by: + uuid: + display_name: + email: + modified_at: + modification_type: + version_before: + version_after: + change_summary: + pr_reference: + reason: +``` + +### 4b.2 The Five Artifact Statuses + +| Status | Meaning | Executes? | Output Applied? | Output Captured? | Merges to Active? | +|--------|---------|-----------|----------------|-----------------|------------------| +| `developing` | In active development. Development mode / dev pipeline only. | Dev mode only | No | Dev logs only | No — must transition to proposed first | +| `proposed` | Development complete. Submitted for validation. Shadow mode for policies. | Yes (shadow) | No | Yes — validation report | Yes — after review approval | +| `active` | Live and governing. Applied to all relevant requests. | Yes | Yes | Yes — audit/provenance | N/A | +| `deprecated` | Being phased out. Replacement available. Works but warns. | Yes | Yes | Yes — with deprecation warning | N/A | +| `retired` | End of life. Cannot be used. | No | No | No | No | + +**Status transition rules:** +``` +developing → proposed (author submits for review) +developing → retired (author abandons without proposing) +proposed → active (reviewers approve — via PR merge or API approval) +proposed → developing (returned for rework) +active → deprecated (replacement available — sunset date declared) +deprecated → retired (sunset date reached or manual retirement) +retired → (terminal — no transitions out) +``` + +### 4b.3 Proposed Status — Shadow Execution for Policies + +When a policy artifact is in `proposed` status, it runs in **shadow mode** against real request traffic: + +- Executes alongside active policies on every relevant request +- Output is captured in a `proposed_evaluation_record` — what it would have done +- Output is **never applied** to the actual request +- Shadow output feeds the Validation Dashboard for reviewer analysis +- Policy authors can see aggregate impact before activation + +```yaml +# Shadow output record — captured per real request evaluated +proposed_evaluation_record: + policy_uuid: + policy_version: + request_uuid: + tenant_uuid: + evaluated_at: + would_have_applied: + shadow_output: + would_have_rejected: + rejection_reason: + would_have_patched: + - field: + current_value: + would_have_set: + reason: + would_have_locked: + - field: + lock_type: + reason: + would_have_selected_provider: + impact_assessment: + category: + # none: policy would not have applied to this request + # low: minor enrichment only + # medium: significant field modifications + # high: would have rejected or locked critical fields + # critical: would have rejected or overridden consumer intent +``` + +### 4b.4 Contact Info — Two Modes + +Contact information supports both IdP-backed and standalone deployments: + +**Mode 1 — Identity Provider backed:** +The `uuid` field contains the DCM external entity reference UUID linking to an Identity.Person or Identity.Team in a registered Information Provider. The `display_name` is cached non-authoritatively for UI display. DCM can resolve the full identity record via the Information Provider on demand. + +**Mode 2 — Standalone (no Identity Provider):** +The `uuid` field is absent. `display_name`, `email`, and `notification_endpoint` are the primary identity fields. DCM accepts and records these directly without external verification. This mode supports bootstrapping, air-gapped deployments, and organizations that have not yet registered an Identity Information Provider. + +Both modes are fully supported. An organization can start in standalone mode and migrate to IdP-backed mode by adding `uuid` fields to existing artifact metadata — no other changes required. + +### 4b.5 Notifications from Artifact Metadata + +The `owned_by.notification_endpoint` is the target for all proactive DCM notifications about an artifact: + +| Event | Who Is Notified | +|-------|----------------| +| Layer conflict detected at ingestion | Owner of new layer AND owner of conflicting existing layer | +| Layer deprecated | Owners of all artifacts that reference the deprecated layer | +| Provider deregistered | Owners of all catalog items backed by that provider | +| Policy violation | Owner of the entity that violated the policy | +| Drift detected | Owner of the entity that drifted | +| Proposed policy shadow shows high/critical impact | Policy owner and designated reviewers | +| Artifact approaching sunset date | Artifact owner | + +--- + +## 4c. Conflict Detection at Ingestion + +Conflict detection runs at layer ingestion time — not at request assembly time. This ensures all layers in DCM are conflict-free before they are ever used. + +### 4c.1 Ingestion CI Pipeline + +When a layer is committed to the Layer Store (Git branch created or updated): + +``` +Layer committed to Git branch + │ + ▼ +CI Pipeline fires automatically + │ + ├── 1. Schema validation + │ Is the layer well-formed per the layer schema? + │ Does it carry required artifact metadata? + │ Is the version correctly incremented? + │ + ├── 2. Handle validation + │ Is the handle unique in DCM? + │ Does the handle match the Git path? + │ Does the domain match the submitting actor's authorization? + │ + ├── 3. Scope validation + │ If type-scoped: do declared resource types exist in the registry? + │ Is the layer type consistent with the domain? + │ + ├── 4. Priority validation + │ Is the priority value in valid dotted-notation format? + │ Does the priority category match the domain advisory range? + │ (Warning only if category/domain mismatch — not a block) + │ + ├── 5. Conflict detection + │ For each field in this layer: + │ Find all active layers of the same type and overlapping scope + │ Check if any declare the same field + │ If conflict found: + │ → Does the new layer declare a higher priority? → Allowed, documented + │ → Does the existing layer declare a higher priority? → Allowed, documented + │ → Neither declares priority? → CONFLICT ERROR — PR blocked + │ → Both declare equal priority? → CONFLICT ERROR — PR blocked + │ → Domain authority violation? → CONFLICT ERROR — PR blocked + │ + │ Conflict notification: + │ Posted as PR comment with: conflicting layer UUID, handle, owner + │ Both layer owners notified via notification_endpoint + │ + ├── 6. Deprecation reference validation + │ If status: deprecated — does replacement UUID exist? + │ + └── 7. Result + All checks pass → PR approved for merge + Any check fails → PR blocked, detailed error comment posted +``` + +### 4c.2 Conflict Resolution Rules + +| Situation | Resolution | Action | +|-----------|-----------|--------| +| New layer and existing layer conflict, no priority on either | CONFLICT ERROR | PR blocked. Both owners notified. One must declare priority or remove the conflicting field. | +| New layer has higher priority (higher value) than existing | Allowed — new layer wins | Documented in provenance. Warning posted if domain authority is unusual. | +| Existing layer has higher priority | Allowed — existing layer wins | New layer is a lower-priority alternative. Documented. | +| Both layers have equal priority | CONFLICT ERROR | PR blocked. Priority must be differentiated. | +| New layer from lower domain overrides higher domain | CONFLICT ERROR | Domain authority violation. Platform cannot be overridden by service layer. | +| Priority category suggests domain mismatch | WARNING | PR comment posted, not blocked. Merge allowed but reviewers are notified. | + +### 4c.3 Pre-Validation of All Layers + +Because conflict detection runs at ingestion, all layers resident in DCM are pre-validated: + +- No two active layers of the same type and scope conflict without explicit priority resolution +- The assembly process never encounters an ambiguous merge — all conflicts are resolved at definition time +- If a conflict is discovered after the fact (e.g., a new layer is activated that conflicts with an existing one that was already active when the new layer was ingested), the newer layer's ingestion pipeline should have caught this. A background validation job runs periodically to detect any edge cases. + +--- + +## 4d. Complete Layer Definition Structure + +Combining all elements — identity, artifact metadata, scope, priority, and fields: + +```yaml +# Complete layer definition +layer: + # === ARTIFACT METADATA (universal — required on all artifacts) === + artifact_metadata: + uuid: "layer-uuid-001" + handle: "platform/core/security-cpu-limits" + version: "1.2.0" + status: active + created_by: + uuid: "actor-uuid-001" # Optional — present if IdP registered + display_name: "Jane Smith" + email: "jane.smith@example.com" + notification_endpoint: "https://notify.example.com/webhooks/jane" + created_at: "2026-01-15T10:30:00Z" + created_via: pr + owned_by: + uuid: "team-uuid-security" # Optional — present if IdP registered + display_name: "Platform Security Team" + email: "platform-security@example.com" + notification_endpoint: "https://notify.example.com/webhooks/platform-security" + modifications: + - sequence: 1 + modified_by: + display_name: "Jane Smith" + email: "jane.smith@example.com" + modified_at: "2026-01-15T10:30:00Z" + modification_type: create + version_before: null + version_after: "1.0.0" + change_summary: "Initial creation — CPU limits per CISO mandate SEC-2024-047" + pr_reference: "https://github.com/org/dcm-layers/pull/42" + reason: "CISO mandate SEC-2024-047 requires CPU limits on all containers" + - sequence: 2 + modified_by: + display_name: "Bob Jones" + email: "bob.jones@example.com" + modified_at: "2026-02-20T14:00:00Z" + modification_type: update + version_before: "1.0.0" + version_after: "1.2.0" + change_summary: "Increased CPU limit from 4 to 8 per updated mandate" + pr_reference: "https://github.com/org/dcm-layers/pull/67" + reason: "Updated CISO mandate SEC-2024-047-rev2 allows 8 CPU" + + # === LAYER IDENTITY === + domain: platform + layer_type: core + + scope: + resource_types: + - Compute.Container + - Compute.Pod + # Empty list = type-agnostic (applies to all resource types) + + priority: + value: "200.30.10" + label: "security.container.cpu_limits" + category: security + rationale: > + CPU limit enforcement for container workloads per + CISO mandate SEC-2024-047. Overrides platform defaults. + + # === LAYER CHAIN === + parent_chain: + - uuid: "base-layer-uuid-001" + handle: "system/base/universal-defaults" + version: "1.0.0" + layer_type: base + + # === FIELDS === + fields: + cpu_limit: + value: 8 + metadata: + basis_for_value: "CISO mandate SEC-2024-047-rev2" + baseline_value: 4 + override: constrained + constraint_schema: + minimum: 1 + maximum: 8 +``` + +--- + +--- + +## 5. Precedence and Merge Rules + +When layers are merged to produce the assembled payload, fields from higher-precedence layers override fields from lower-precedence layers. The precedence order from lowest to highest is: + +``` +1. Base Layer (lowest precedence — foundation defaults) +2. Core Layers (organizational and infrastructure context) +3. Intermediate/Customization (organizational hierarchy overrides) +4. Service Layers (service-specific configuration) +5. Request Layer (consumer intent — overrides all data layers) +6. Transformation Policies (enrichment — adds or modifies fields) +7. Validation Policies (pass/fail — no field modification) +8. GateKeeper Policies (highest authority — overrides everything) +``` + +### 5.1 Override Behavior + +- A higher-precedence layer that declares a field **overrides** the value from all lower-precedence layers +- A higher-precedence layer that does **not** declare a field leaves the lower-precedence value intact +- Fields not declared at any layer level are absent from the payload — providers must declare all required fields as being covered by at least one layer in the chain +- GateKeeper policies can override **any** field including consumer-declared Request Layer values — this is the mechanism for enforcing sovereignty constraints, security mandates, and hard compliance rules + +### 5.2 Additive vs. Override Fields + +Some fields are **scalar** (a single value — one layer wins) and some are **additive** (a list or set — layers contribute to a collection). The field type in the Resource Type Specification declares which behavior applies: + +```yaml +field_name: + type: + merge_behavior: + # override: higher precedence layer's value replaces lower precedence value + # additive: all layers contribute their values to a merged collection +``` + +### 5.3 Conflict Resolution + +When two layers at the same precedence level declare conflicting values for the same field: +- The conflict is recorded and surfaced as a validation error +- The request is not processed until the conflict is resolved +- Conflict resolution is never silent — it is always recorded in provenance + +--- + +## 5a. Field Override Control + +Field override control is the mechanism by which DCM governs **who can change what, under what conditions**, across the layer precedence chain. It was present in the original data model rules as "override preference" metadata on fields — this section formalizes that concept as a graduated model that is **simple by default and powerful when needed**. + +**Design Principle:** A field with no override declaration is fully overridable by anyone. Restrictions are always opt-in. The model has three levels — you use only the level you need. Levels 1 and 2 cover the vast majority of real-world cases. Level 3 exists for fields that genuinely require nuanced, actor-specific governance. + +--- + +### 5a.1 Two Categories of Override Rule + +**Category 1 — Structural Rules (Request Payload Processor — non-overridable)** + +Enforced by the Request Payload Processor as DCM System behavior. Not configurable. Always applied: + +- A layer entity is immutable once versioned — no override can modify a published version +- A child layer cannot remove a field declared in a parent layer — it can only override the value +- The layer precedence order is fixed — Base → Core → Intermediate → Service → Request → Policy +- Circular layer references are rejected unconditionally +- A Service Layer without a declared type scope is rejected unconditionally + +**Category 2 — Business Rules (Policy Engine — configurable)** + +Enforced by the Policy Engine using the Validation/Transformation/GateKeeper mechanism. Override control metadata is set exclusively by the Policy Engine and carried in the payload as part of field-level provenance. Data layers and the Request Payload Processor never set override control. + +--- + +### 5a.2 Where Override Control is Declared + +Override control can be declared at two static levels and applied dynamically at runtime: + +**Level A — Resource Type Specification (portable, sets the ceiling)** +Declares the default override behavior for a field across all implementations of that Resource Type. These defaults travel with the type definition and apply to all providers and catalog items that implement the type. This sets the maximum permissiveness ceiling — lower levels can only restrict further. + +**Level B — Catalog Item (offering-specific, can only restrict)** +Declares additional restrictions for a specific curated offering beyond the Resource Type defaults. A "PCI Production VM" catalog item can lock `encryption_standard` to a single value even if the VM Resource Type allows a broader enum. Cannot expand beyond what the Resource Type permits. + +**Level C — Policy Engine (runtime, within static bounds)** +Applies override control at request processing time based on current organizational policies. Can only restrict within the bounds established by the Catalog Item (or Resource Type if no Catalog Item restriction exists). Higher-authority policy levels (Global) can grant expansion to trusted actors within their authority scope. + +**Inheritance Rule:** Override control can only be made more restrictive as it flows down the declaration hierarchy — Resource Type → Catalog Item → Runtime Policy. The sole exception is explicit trusted grants made by higher-authority actors (see Section 5a.6). + +--- + +### 5a.3 Level 1 — No Declaration (Default) + +No override control declaration on a field means it is fully overridable by any actor. This is the default for all fields. Zero configuration required. + +```yaml +# Level 1 — fully overridable, no declaration needed +cpu_count: + value: 4 +``` + +This covers the majority of fields in most implementations. + +--- + +### 5a.4 Level 2 — Simple Declaration + +A single `override` property covers the most common governance needs without requiring a full matrix. Sufficient for most governed fields. + +```yaml +# Level 2a — nobody can change this +sovereignty_zone: + value: us-east + override: immutable + +# Level 2b — anyone can change but only within these values +encryption_standard: + value: AES-256 + override: constrained + constraint_schema: + enum: [AES-256, AES-128] + +# Level 2c — explicit allow (same as default, but self-documenting) +display_name: + value: my-vm + override: allow +``` + +| Value | Meaning | Enforcement | +|-------|---------|-------------| +| `allow` | Default. Any actor may override. | Structural rules | +| `constrained` | Any actor may override within `constraint_schema` | Policy Engine — Validation | +| `immutable` | No actor may override at any level | Policy Engine — GateKeeper | + +--- + +### 5a.5 Level 3 — Matrix Declaration + +Full actor-level control for fields that require nuanced governance. Used only when Level 2 is insufficient. + +```yaml +billing_tag: + value: engineering + override_matrix: + default: allow + # Default permission for any actor not explicitly listed + # Options: allow | constrained | deny + + inheritance: restrict_only + # Catalog Items and lower-level declarations can only restrict + # Higher-authority actors can grant expansion via trusted_grants + + actors: + - actor: policy.global + permission: allow + can_expand: true + # Global policies can always override and can grant expansion + # to lower actors via trusted_grants + + - actor: policy.tenant + permission: allow + can_expand: true + # Tenant policies can override and grant within global ceiling + + - actor: policy.user + permission: deny + can_expand: false + # User policies cannot override and cannot grant to others + + - actor: consumer_request + permission: constrained + constraint_schema: + pattern: "^[a-z0-9-]+$" + can_expand: false + # Consumers can override within pattern, cannot grant expansion + + - actor: process_resource + permission: deny + can_expand: false + # Automation denied by default — grant via trusted_grants + + - actor: provider + permission: deny + can_expand: false + # Providers cannot modify this field + + - actor: sre_override + permission: allow + can_expand: false + # SREs have operational authority but cannot grant to others + + - actor: admin_override + permission: allow + can_expand: true + # Admins can override and grant within their scope level + + trusted_grants: + # Explicit expansion grants from higher-authority actors + # Used when an actor needs more permission than their default + - granted_to_uuid: + actor_type: process_resource + permission: allow + granted_by_policy_uuid: + reason: Patching automation trusted to update billing_tag + expires: + + constraint_schema: + pattern: "^[a-z0-9-]+$" + # Applied to all actors with permission: constrained +``` + +--- + +### 5a.6 Actor Registry + +The actor list is extensible. DCM ships with built-in actors. Organizations register custom actors following the same model. Custom actors default to `deny` until explicitly granted permissions. + +**Built-in actors:** + +| Actor | Default Scope | Can Expand | Notes | +|-------|--------------|------------|-------| +| `policy.global` | All tenants | ✅ | Highest authority — can grant to any actor | +| `policy.tenant` | Single tenant | ✅ | Within global ceiling | +| `policy.user` | Single user | ❌ | Can only restrict | +| `consumer_request` | Request submitter | ❌ | Can only restrict | +| `process_resource` | Automation execution | ❌ by default | Requires trusted grant | +| `provider` | Service Provider | ❌ | Can only restrict | +| `sre_override` | SRE team | ❌ | Operational authority, cannot grant | +| `admin_override` | DCM Admin | ✅ | Within their scope level | + +**Custom actor registration:** + +```yaml +custom_actor: + uuid: + name: + description: + registered_by_tenant_uuid: + default_permission: deny + # Custom actors always default to deny until explicitly granted + can_expand: false + # Custom actors cannot expand by default — requires explicit grant + version: + status: + provenance: + +``` + +Custom actors follow the universal versioning and deprecation model. A custom actor registered at Tenant scope cannot be granted Global-level authority. + +--- + +### 5a.7 Expansion Rules + +Actor expansion follows a strict hierarchy: + +- **`policy.global`** and **`admin_override`** at global scope — can grant expansion to any actor for any field, including fields declared `immutable` at lower levels +- **`policy.tenant`** and **`admin_override`** at tenant scope — can grant expansion within their tenant, cannot expand beyond what Global permits +- **`policy.user`**, **`consumer_request`**, **`provider`** — can never grant expansion regardless of what they receive +- **`sre_override`** — can never grant expansion but can be granted expansion by Tenant or Global +- **`process_resource`** — denied by default, can be granted expansion by Tenant or Global via `trusted_grants` +- **Custom actors** — denied by default, can be granted expansion by the level that registered them or higher + +**Trusted grants expire** — if an `expires` timestamp is set, the grant is automatically revoked at that time. Expired grants are retained in provenance for audit purposes but are no longer applied. + +--- + +### 5a.8 Override Control in the Assembly Process + +Override control is applied during Step 5 (Policy Processing) of the assembly process: + +``` +Layer Merge complete (Steps 1-4) + │ Fields have values — all fields default to Level 1 (allow) + │ Static override declarations from Resource Type and Catalog Item are loaded + ▼ +Transformation Policies + │ May set override: constrained or override_matrix on fields + │ May set baseline_value and basis_for_value metadata + │ Records policy UUID, level, and reason in field provenance + ▼ +Validation Policies + │ Verify existing override declarations are not violated + │ Verify actor permissions against current override_matrix + │ Pass/fail — no modification to override control + ▼ +GateKeeper Policies + │ May set override: immutable on fields + │ May override field values before locking + │ May issue trusted_grants to specific actors + │ Records policy UUID, level, lock type, and reason in provenance + ▼ +Requested State + │ All governed fields carry full override control metadata + │ Provenance chain complete — every lock and grant is traceable + ▼ +``` + +--- + +### 5a.9 Override Control and Rehydration + +During rehydration, the Intent State is replayed through the **current** Policy Engine. Override control declared in current policies is applied fresh. A field that was `allow` in the original request may be `immutable` if a new GateKeeper policy was added since. This is by design — rehydration applies current governance standards, not historical ones. + +The original consumer intent is preserved unchanged in the Intent Store. The new realized state reflects current governance. Both are auditable and traceable. + +The one exception is `pinned` policy version rehydration (Historical Exact or Historical Portable modes) — this deliberately replays historical policies and may bypass current immutable locks. Pinned rehydration requires elevated authorization precisely for this reason. + +--- + +### 5a.11 Global Policy Self-Override — The Immutable Ceiling Model + +**Q51 resolved:** When a Global GateKeeper policy sets `override: immutable` on a field, can a higher-priority Global policy still override it? + +**The answer emerges from execution order.** Policies execute highest-priority-first (highest numeric value first within a tier). The first policy to set `override: immutable` on a field locks it. All subsequent policies — including other Global policies with lower priority values — find the field locked and cannot modify it. In normal request processing, **default `immutable` is effectively absolute** — not through a special rule, but through execution order. + +**The `immutable_ceiling` declaration** is a forward-looking protection for fields that must remain locked even if a higher-priority policy is **added to the system later**: + +```yaml +# Default immutable — protected by execution order during this request +# The highest-priority Global GateKeeper to run first locks it +# No subsequent policy in this execution can change it +sovereignty_zone: + value: eu-west + override: immutable + # Safe in practice — execution order guarantees the first-runner wins + # Does NOT protect against a new higher-priority policy being added tomorrow + +# Absolute immutable — explicit forward-looking protection +# Protected even if a new higher-priority policy is added to the system +classification_level: + value: RESTRICTED + override: immutable + immutable_ceiling: absolute + # Cannot be overridden by ANY policy, ever + # If a policy attempts to override this, it receives a hard rejection + # The attempted override is logged in audit with full provenance + # Use for: sovereignty zones, data classification, hard compliance mandates +``` + +**The formal rule:** + +| Declaration | Protected During Execution? | Protected Against Future Policies? | Use Case | +|-------------|----------------------------|-------------------------------------|---------| +| `override: immutable` (default) | ✅ Yes — execution order | ❌ No | Most governed fields | +| `override: immutable` + `immutable_ceiling: absolute` | ✅ Yes | ✅ Yes — hard rejection | True non-negotiables | + +**`immutable_ceiling: absolute` is the nuclear option.** Use it sparingly — only for fields where the governance requirement is genuinely non-negotiable regardless of any future organizational policy change. Sovereignty zone on a sovereign deployment. Data classification on a restricted system. Encryption standard under a regulatory mandate with no variance permitted. + +**Audit behavior:** When a policy attempts to override a field with `immutable_ceiling: absolute`, the attempt is rejected silently from the requesting policy's perspective (the field simply doesn't change) but is fully logged in the Audit Store with the policy UUID, the attempted value, the rejection reason, and the UUID of the policy that set the ceiling. + +--- + +### 5a.10 Override Control Metadata — Full Structure + +The complete field metadata structure carrying override control in the payload: + +```yaml +field_name: + value: + metadata: + # Simple declaration (Level 2) — set by Policy Engine at runtime + override: + # OR matrix declaration (Level 3) — set by Policy Engine at runtime + override_matrix: + + + # Always present regardless of level + basis_for_value: + baseline_value: + locked_by_policy_uuid: + locked_at_level: + constraint_schema: + + provenance: + origin: + value: + source_type: + source_uuid: + timestamp: + modifications: + - sequence: 1 + previous_value: + modified_value: + source_uuid: + operation_type: + actor: + timestamp: + reason: +``` + +--- + +The Request Payload Processor assembles the final payload by executing the following **nine steps** in order. Each step is recorded in the payload's provenance chain. + +### Step 1 — Intent Capture +The consumer's Request Layer is received and stored as the **Intent State** in the Intent Store. No modification occurs at this step. The Intent State is the immutable record of what the consumer asked for. + +### Step 2 — Layer Resolution +The Request Payload Processor determines which layers apply to this request: +- Identifies the Resource Type from the Request Layer +- Retrieves the applicable Base Layer for the request context +- Retrieves all applicable Core Layers (type-agnostic — all apply) +- Retrieves applicable Intermediate/Customization Layers based on organizational context +- Retrieves applicable Service Layers whose declared type scope matches the request Resource Type +- Orders all retrieved layers according to the precedence chain + +### Step 3 — Layer Merge +Layers are merged in precedence order (lowest to highest). For each field: +- The value from the highest-precedence layer that declares it is used +- The source layer UUID and layer type are recorded in the field's provenance metadata +- Additive fields accumulate values from all layers that declare them + +### Step 4 — Request Layer Application +The consumer's Request Layer is applied last in the data layer merge. Consumer-declared values override all data layer values. Each override is recorded in provenance. + +### Step 5 — Pre-Placement Policy Processing +Policies matching the `request.layers_assembled` payload type are evaluated against the merged payload before any provider is known. GateKeeper, Transformation, Validation, and Governance Matrix policies may all fire at this stage — evaluated by the Policy Engine in domain precedence order: + +1. **Transformation Policies** — enrich and modify the payload. May set `override: constrained` on fields. Each transformation records the policy UUID, operation type, reason, and any override control declarations in provenance. +2. **Validation Policies** — check the payload against rules. Pass/fail only — no field modification. Failures reject the request. +3. **GateKeeper Policies** — apply hard overrides and blocks. May set `override: immutable`. All overrides recorded in provenance. + +Pre-placement policies produce **placement constraints** — declarative requirements a provider must satisfy (sovereignty zone, hardware class, conformance level, etc.). These constraints are carried forward as inputs to the Placement Engine. + +### Step 6 — Placement Engine — Placement Loop + +The Placement Engine takes the policy-processed payload and placement constraints, builds a candidate provider list (filtered by constraints, ordered by scoring criteria), and iterates through candidates until placement is confirmed or all candidates are exhausted. + +**Placement loop governance** (configurable by policy): +```yaml +placement_loop_config: + max_iterations: 5 # maximum candidates to attempt + max_duration_seconds: 30 # timeout for entire loop + on_exhaustion: + hold_ttl_seconds: 300 # how long provider holds resources +``` + +**Per-candidate iteration:** + +``` +── RESERVE QUERY (single atomic call to provider) ── + Request: constraints + resource spec + hold TTL + metadata_requested + Response status: + confirmed: resources held, constraints satisfied, metadata returned + partial: hold confirmed, some metadata unavailable + insufficient: provider lacks capacity — skip to next candidate + refused: provider cannot satisfy constraints — skip to next candidate + +── POLICY PHASE (placement_phase: loop) ── + Policies evaluate: payload + constraints + reserve query response + For each field declared in policy required_context: + Field present: evaluate normally + Field absent, required_context declared: + if_absent: gatekeep → release hold, abort loop, REJECT REQUEST + if_absent: warn → record warning, continue + if_absent: skip → record as skipped, continue + Field absent, no policy declares required_context: + → record policy_gap_record (implicit_approval), continue + Policy outcomes: + gatekeep → release hold, abort loop, REJECT REQUEST + reject_candidate → release hold, skip to next candidate + pass / warn → PLACEMENT CONFIRMED — exit loop +``` + +**Reserve query structure:** +```yaml +reserve_query_request: + request_uuid: + hold_uuid: + resource_type: + placement_constraints: + resource_spec: + cpu: 16 + ram_gb: 64 + storage_gb: 500 + hold_ttl_seconds: 300 + metadata_requested: + - capacity_available + - topology + - sovereignty_certifications + - patch_level + - maintenance_windows + +reserve_query_response: + hold_uuid: + provider_hold_reference: + hold_status: + hold_confirmed_spec: + cpu: 16 + ram_gb: 64 + storage_gb: 500 + zone: eu-west-1a + rack: rack-07 + metadata: + topology: + zone: eu-west-1a + rack: rack-07 + network_segment: vlan-142 + available_ips: ["10.20.4.0/24"] + sovereignty_certifications: + - cert: ISO-27001 + expires_at: "2027-06-30" + missing_metadata: + - field: patch_level + reason: "Provider does not track patch metadata at this conformance level" +``` + +**Non-hold queries** (available outside the placement loop for capacity checks, provider health, cost estimation, and pre-filtering): + +| Query Type | Hold? | Purpose | +|-----------|-------|---------| +| `reserve_query` | Yes — atomic | Primary placement loop query | +| `capacity_query` | No | Pre-loop filtering, dashboard, cost estimation | +| `metadata_query` | No | Provider health checks, audit, policy pre-evaluation | +| `constraint_verification` | No | Rapid pre-filter before entering the loop | + +**Policy gap records** — when a field is absent and no policy declares `required_context` for it: +```yaml +policy_gap_record: + request_uuid: + field: patch_level + field_value: null + evaluation_result: implicit_approval + reason: > + No active policy declared required_context for this field. + Field was absent in reserve query response. + Request proceeded without policy evaluation of this field. + provider_uuid: + recorded_at: + resolution_expected: realized_payload + # Provider expected to supply this field in the realized payload or discovery +``` + +**Provider metadata completeness — eventual consistency:** +Fields missing from the reserve query response are expected to be completed in: +1. **Realized payload** (primary) — provider returns full metadata when confirming realization +2. **Discovery loop** (fallback) — periodic discovery fills remaining gaps + +The realized entity carries `enrichment_status: pending | partial | complete` reflecting how complete its metadata is. This is the same pattern as the ingestion model. + +### Step 7 — Post-Placement Policy Processing +Policies with `placement_phase: post` (or `both`) execute after the Placement Engine has confirmed a provider selection. These policies have full access to the `placement` block of the payload including the provider selection, hold confirmation, and all returned metadata. + +1. **Transformation Policies** — provider-aware enrichment. Inject zone-specific configuration, provider-specific defaults, topology-derived values that are only knowable after provider selection. +2. **Validation Policies** — post-placement checks. Verify the selected provider meets requirements that couldn't be expressed as pre-placement constraints. +3. **GateKeeper Policies** — post-placement hard overrides. May inject mandatory fields triggered by the specific provider selected (e.g., additional data handling requirements for a provider in a specific jurisdiction). + +**Policy `placement_phase` values:** +```yaml +policy: + placement_phase:
+  # pre:  steps 5 — before provider known (default)
+  # loop: step 6 — inside placement loop, evaluates reserve query response
+  # post: step 7 — after placement confirmed, provider known
+  # both: pre and post (not loop)
+```
+
+**Policy `required_context` for missing metadata:**
+```yaml
+policy:
+  placement_phase: loop
+  required_context:
+    - field: placement.provider_metadata.sovereignty_certifications
+      if_absent: gatekeep
+      if_absent_reason: >
+        Cannot evaluate sovereignty compliance without provider
+        certification data. Blocking request. Provider must register
+        this metadata to participate in sovereignty-scoped requests.
+    - field: placement.provider_metadata.patch_level
+      if_absent: warn
+      if_absent_reason: >
+        Patch level not available. Proceeding with warning.
+        Provider notified to register patch metadata.
+```
+
+### Step 8 — Requested State Storage
+The fully assembled, policy-processed, placement-confirmed payload is stored as the **Requested State** in the Request Store. The Requested State includes:
+- All assembled resource fields with full provenance chain
+- Complete `placement` block: selected provider, hold UUID, all reserve query responses per iteration, all policy evaluations per iteration, placement constraints applied, alternatives considered
+- All `policy_gap_record` entries for implicit approvals
+- `enrichment_status` reflecting metadata completeness at dispatch time
+
+### Step 9 — Provider Dispatch
+The Requested State payload is dispatched to the selected Service Provider via the API Gateway. The resource hold placed during the Placement Loop is confirmed by dispatch. The provider uses the hold reference to fulfill the request against the reserved resources.
+
+---
+
+---
+
+## 7. Layer Assembly Diagram
+
+```
+Consumer Request
+      │
+      ▼
+┌─────────────────┐
+│  REQUEST LAYER  │  ← Consumer declared intent → stored as INTENT STATE (Step 1)
+└────────┬────────┘
+         │
+         ▼
+┌─────────────────────────────────────────────────────────┐
+│             LAYER RESOLUTION + MERGE (Steps 2-4)         │
+│                                                          │
+│  Base Layer          (lowest precedence)                 │
+│       ↓                                                  │
+│  Core Layers         (type-agnostic context)             │
+│       ↓                                                  │
+│  Intermediate Layers (organizational context)            │
+│       ↓                                                  │
+│  Service Layers      (type-scoped service config)        │
+│       ↓                                                  │
+│  Request Layer       (consumer intent — highest          │
+│                       data layer precedence)             │
+└────────┬────────────────────────────────────────────────┘
+         │  Merged payload with full provenance
+         ▼
+┌─────────────────────────────────────────────────────────┐
+│          PRE-PLACEMENT POLICY PROCESSING (Step 5)        │
+│                                                          │
+│  Transformation Policies  (enrich / modify)              │
+│       ↓                                                  │
+│  Validation Policies      (pass / fail check)            │
+│       ↓                                                  │
+│  GateKeeper Policies      (override / block)             │
+│       ↓ outputs: placement constraints                   │
+└────────┬────────────────────────────────────────────────┘
+         │  Policy-processed payload + placement constraints
+         ▼
+┌─────────────────────────────────────────────────────────┐
+│              PLACEMENT ENGINE — LOOP (Step 6)            │
+│                                                          │
+│  For each candidate provider (filtered + scored):        │
+│    │                                                     │
+│    ├── Reserve Query (atomic: verify + metadata + hold)  │
+│    │     confirmed / partial → policy phase              │
+│    │     insufficient / refused → next candidate         │
+│    │                                                     │
+│    └── Loop Policy Phase (placement_phase: loop)         │
+│          Field present → evaluate normally               │
+│          Field absent + required_context → if_absent     │
+│          Field absent + no policy → implicit_approval    │
+│          pass/warn → PLACEMENT CONFIRMED                 │
+│          reject_candidate → release hold, next           │
+│          gatekeep → release hold, REJECT REQUEST         │
+│                                                          │
+│  No candidates remain → on_exhaustion behavior           │
+└────────┬────────────────────────────────────────────────┘
+         │  selected_provider_uuid + placement block
+         ▼
+┌─────────────────────────────────────────────────────────┐
+│         POST-PLACEMENT POLICY PROCESSING (Step 7)        │
+│                                                          │
+│  Transformation Policies  (provider-aware enrichment)    │
+│       ↓                                                  │
+│  Validation Policies      (post-placement checks)        │
+│       ↓                                                  │
+│  GateKeeper Policies      (provider-triggered overrides) │
+└────────┬────────────────────────────────────────────────┘
+         │  Complete, validated, placement-confirmed payload
+         ▼
+┌─────────────────┐
+│ REQUESTED STATE │  ← Stored in Request Store (Step 8)
+└────────┬────────┘    includes: placement block, hold records,
+         │             policy gap records, enrichment_status
+         ▼
+   Service Provider  (Step 9 — dispatch, hold confirmed)
+```
+
+---
+
+## 8. Layer Scope and Type Enforcement
+
+### 8.1 Core Layer Scope Enforcement
+Core Layers are type-agnostic by default. They are applied to every request regardless of Resource Type. A Core Layer that contains service-specific or provider-specific data is invalid and must be rejected.
+
+### 8.2 Service Layer Scope Enforcement
+Service Layers must declare a Resource Type scope. The Request Payload Processor enforces this during Layer Resolution:
+- A Service Layer whose declared Resource Type does not match the request Resource Type is excluded from the merge
+- A Service Layer with `scope_inheritance: exact` is only included if the request Resource Type exactly matches the declared type
+- A Service Layer with `scope_inheritance: descendants` is included if the request Resource Type is the declared type or any descendant type in the inheritance hierarchy
+- A Service Layer with no declared type scope is invalid and must be rejected
+
+### 8.3 Unanticipated Data Interaction Prevention
+The type scoping rules for Service Layers are the primary mechanism for preventing unanticipated data interactions — one of the core data model objectives. Because Service Layers can only contribute to requests of their declared type, data from one service domain cannot inadvertently affect requests in another service domain.
+
+---
+
+## 9. Layer Versioning
+
+All layers follow the universal DCM versioning scheme: **Major.Minor.Revision**
+
+| Component | Trigger |
+|-----------|---------|
+| **Major** | Breaking changes — removing fields, changing field types, changing a field from optional to required |
+| **Minor** | Additive changes — adding new optional fields, adding new contextual data |
+| **Revision** | Data/configuration changes — updating field values, updating descriptions, updating metadata |
+
+**Immutability:** Once a layer version is published it cannot be modified. Any change produces a new version. Previous versions remain accessible and can be referenced by existing realized entities.
+
+**Parent Chain Versioning:** A layer's parent chain references specific versions of parent layers. Updating a parent layer does not automatically update child layers — child layers must be explicitly updated to reference the new parent version, producing a new version of the child layer.
+
+---
+
+## 10. Artifact Lifecycle — The Five Statuses
+
+All DCM artifacts — layers, policies, resource types, catalog items, and all other defined objects — follow a five-status lifecycle. The statuses are defined in Section 4b.2. For layers specifically:
+
+| Status | Layer Behavior |
+|--------|---------------|
+| `developing` | Layer is in active development. Only usable in development mode pipelines. Not loaded by the assembly process in production. |
+| `proposed` | Layer has been submitted for review (PR open). Not yet active. For policy layers: shadow execution runs. For data layers: layer is visible in the registry but not applied. Cannot merge to active until PR is approved. |
+| `active` | Layer is current and applied in assembly. Can be included in new layer chains. |
+| `deprecated` | Layer is being phased out. Existing chains using it continue to function. New chains should use the replacement. Deprecation warning recorded in assembly provenance. Must include replacement UUID, reason, migration guidance, and sunset date. |
+| `retired` | Layer cannot be included in new layer chains. Existing realized entities that reference it retain the reference for audit purposes but cannot be used for new requests. |
+
+**Status transition rules for layers:**
+```
+developing → proposed   (author submits PR)
+developing → retired    (author abandons)
+proposed   → active     (PR merged — approval complete)
+proposed   → developing (PR returned for rework)
+active     → deprecated (replacement available — sunset declared)
+deprecated → retired    (sunset date reached or manual retirement)
+```
+
+---
+
+## 11. Scale Example — 40,000 Linux VMs
+
+This example illustrates the power of the layering model at scale. 40,000 distinct VM configurations are governed by 36 layer definitions:
+
+```
+Base Entity (3 variants)
+├── CIS Benchmark
+├── Baseline
+└── DMZ / Payments
+
+  └── Layer Entity — OS Family (3 variants per base = 9 total)
+      ├── Common Linux Config / RHEL
+      ├── Common Linux Config / CoreOS
+      └── Common Linux Config / OEL
+
+        └── Layer Entity — OS Version (4 variants per OS layer = 36 total)
+            ├── RHEL 6
+            ├── RHEL 7
+            ├── RHEL 8
+            └── RHEL 9
+
+              └── Realized Entity — one per VM (40,000 total)
+                  Each realized entity carries FK references to its
+                  full layer chain (Base UUID + Layer UUIDs)
+                  and is stored in the CMDB
+```
+
+**Result:** 3 × 3 × 4 = **36 layer definitions** govern **40,000 VM configurations**. Each VM's realized entity is a lightweight reference to its layer chain — not a copy of all the configuration data.
+
+This also means:
+- Updating the CIS Benchmark base layer creates one new layer version that cascades to all 40,000 VMs at their next realization
+- Drift detection compares each VM's discovered state against its realized entity's layer chain
+- Any VM can be reproduced exactly by replaying its layer chain through the assembly process
+
+---
+
+## 12. Relationship to the Four States
+
+| Layer | State Relationship |
+|-------|-------------------|
+| Request Layer (as submitted) | Directly captured as **Intent State** — stored in Intent Store before any processing |
+| Assembled payload (post-merge, pre-policy) | Intermediate — not a named state, internal to assembly process |
+| Assembled payload (post-policy) | Becomes **Requested State** — stored in Request Store |
+| Provider execution result | Becomes **Realized State** — stored in Realized Store |
+| Discovery interrogation result | Becomes **Discovered State** — stored in Discovered Store |
+
+The layer chain of a Realized Entity is always traceable — given a Realized State record, the complete layer chain that produced it can be reconstructed, providing full audit capability back to the original Base Layer.
+
+---
+
+## 13. Layer Gaps — Q21 through Q24
+
+### 13.1 Consumer Layer Exclusion (Q21)
+
+Consumers may explicitly exclude specific layers from their request. Each exclusion carries a mandatory human-readable reason recorded in provenance and the audit trail.
+
+```yaml
+request:
+  resource_type: Compute.VirtualMachine
+  layer_exclusions:
+    - layer_handle: "platform/networking/default-dns-config"
+      reason: "This VM uses custom DNS — default config conflicts with application requirements"
+    - layer_uuid: 
+      reason: "Dev environment — monitoring layer not required"
+```
+
+**Exclusion mechanics:**
+- Excluded layers are removed from the candidate set during **Step 2 (Layer Resolution)** before priority ordering
+- Excluded layers produce no fields in the assembled payload
+- If a validation policy requires a field that would have been injected by an excluded layer, the validation fails with a clear message identifying the excluded layer
+- Exclusion is different from override — exclusion removes the entire layer; override changes specific field values
+
+**Policy enforcement:** GateKeeper policies may declare specific layers non-excludable:
+
+```yaml
+policy:
+  type: gatekeeper
+  rule: >
+    If request.layer_exclusions CONTAINS layer.concern_tags CONTAINS "security-baseline"
+    THEN gatekeep: "Security baseline layers cannot be excluded"
+  immutable_ceiling: absolute
+```
+
+### 13.2 Service Layer Versioning (Q22)
+
+Service Layers are **independently versioned artifacts** — not coupled to Service Provider versions. Service Providers declare semver-compatible version constraints for the layers they use.
+
+```yaml
+# Service Provider registration — layer compatibility declarations
+provider_registration:
+  layer_compatibility:
+    - layer_handle: "layers/vm-compute-defaults"
+      compatible_versions: "^1.0.0"    # any 1.x version
+    - layer_handle: "layers/vm-networking-config"
+      compatible_versions: "~1.2"      # any 1.2.x revision
+```
+
+**Version lifecycle:** Service Layers follow the standard five-status artifact lifecycle. A deprecated Service Layer continues to work for existing realizations until retired. If a provider bumps to a new major version and updates its compatibility declaration, the old layer version is no longer used for new requests via that provider but continues to work for existing realizations.
+
+**Cache invalidation:** Service Layer Cache entries carry the layer version. When the registered layer version increments, the cache entry is invalidated and refreshed before the next assembly.
+
+### 13.3 Conditional Layer Inclusion (Q23)
+
+Layers may declare an `activation_condition` — a field comparison evaluated during **Step 2 (Layer Resolution)**. Layers whose condition evaluates false are excluded from the candidate set.
+
+```yaml
+layer:
+  handle: "platform/compute/gpu-config"
+  activation_condition:
+    field: request.gpu_requested
+    operator: equals
+    value: true
+```
+
+**Compound conditions:**
+```yaml
+activation_condition:
+  conditions:
+    operator: and   # and | or
+    rules:
+      - field: request.gpu_requested
+        operator: equals
+        value: true
+      - field: request.resource_class
+        operator: in
+        value: [ml-training, gpu-compute]
+```
+
+**Condition field scope** — activation conditions may reference:
+- Request fields (`request.gpu_requested`)
+- Tenant attributes (`tenant.tags`, `tenant.profile`)
+- Resource type fields (`resource_type.version`)
+- Core Layer fields already resolved in Step 1 (`core_layers.location_region`)
+- Ingress fields (`ingress.actor.roles`) — enabling role-specific layers
+
+**Condition vs consumer exclusion:** Conditional inclusion is declared by the layer author and evaluated automatically. Consumer exclusion (Q21) is declared at request time by the consumer. Both result in the layer being absent from the candidate set — but for different reasons, recorded differently in provenance.
+
+### 13.4 Layer Chain and Service Dependencies (Q24)
+
+Each service dependency executes its **own independent layer chain** during assembly. Dependencies do not share the parent request's layer chain.
+
+**Dependencies inherit from parent (read-only context):**
+- Tenant UUID and sovereignty context
+- Parent's resolved placement fields (declared by Resource Type Specification as `propagated_to_dependencies`)
+- Parent's resolved identity fields (hostname, etc.)
+- Active Profile
+
+**Dependencies do NOT inherit:**
+- Parent consumer declarations
+- Resource-type-specific layers (each type has its own)
+- Provider-specific layers (each provider has its own)
+
+**Dependency assembly flow:**
+```
+Parent request: Compute.VirtualMachine
+  │
+  ▼  Steps 1-4: Parent layer chain → parent_assembled_payload
+  │
+  ▼  Step 5: Pre-placement policies on parent
+  │
+  ▼  Step 6: Placement loop — parent provider selected
+  │           Also identifies required dependency providers
+  │
+  ▼  For each dependency (parallel where ordering allows):
+  │  ├── Network.IPAddress → own layer chain (Steps 1-4)
+  │  │     Context: inherits parent resolved placement fields
+  │  ├── Network.Port → own layer chain (Steps 1-4)
+  │  │     Context: inherits parent + IP resolution result
+  │  └── DNS.Record → own layer chain (Steps 1-4)
+  │         Context: inherits parent + IP + Port results
+  │
+  ▼  Steps 7-9: Post-placement, storage, dispatch
+       Parent + all dependency payloads dispatched together
+```
+
+**Layer exclusions on dependencies** — consumers may declare per-dependency exclusions:
+```yaml
+request:
+  resource_type: Compute.VirtualMachine
+  dependencies:
+    - resource_type: Network.IPAddress
+      layer_exclusions:
+        - layer_handle: "layers/ip-default-ttl-config"
+          reason: "Custom TTL required — excluding default"
+```
+
+---
+
+## 13b. Override Control and Constraint Visibility Gaps
+
+### 13b.1 Override Preference Enforcement (Q50)
+
+Layer fields declare override intent using three values. The Request Payload Processor enforces this during assembly Step 3 (Layer Merge) — no separate GateKeeper policy required.
+
+```yaml
+fields:
+  dns_servers:
+    value: [10.0.0.53, 10.0.0.54]
+    metadata:
+      override: allow          # lower layers and consumers may change this
+
+  encryption_at_rest:
+    value: true
+    metadata:
+      override: immutable      # no lower-domain layer or consumer may change this
+      lock_reason: "CISO mandate SEC-2024-047 — encryption always required"
+
+  cpu_count:
+    value: 4
+    metadata:
+      override: constrained    # may change within declared bounds
+      constraint:
+        type: range
+        min: 1
+        max: 32
+        step: 1
+      constraint_reason: "Platform capacity planning bounds"
+```
+
+**Authority rule:** `immutable` prevents overrides only from *lower-authority domains*. A `platform` domain field marked `immutable` cannot be changed by `tenant`, `service`, `provider`, or `request` layers — but a `system` domain layer above it can still override it. Higher authority always wins.
+
+**GateKeeper escalation:** A GateKeeper policy at a higher authority level may additionally lock a field that a layer marked `allow` — this is the compliance escape hatch for mandates the layer author did not anticipate.
+
+**Enforcement point:** Step 3 (Layer Merge) — if a lower-priority layer or consumer request sets a field marked `immutable`, assembly halts with a clear error identifying the conflicting layer and the locking layer.
+
+### 13b.2 Constraint Schema Visibility (Q52)
+
+Constrained fields expose their constraint schema to consumers in the Service Catalog UI and Consumer API at a policy-governed disclosure level.
+
+```yaml
+constraint_visibility:
+  level: 
+  # full:    Show constraint type, bounds, constraint_reason, suggested values
+  # summary: Show bounds only — no reason, no suggestions
+  # hidden:  Field appears free-form; constraint silently enforced at submission
+```
+
+**Profile defaults:**
+
+| Profile | Default Level | Rationale |
+|---------|--------------|-----------|
+| `minimal` | `full` | All context helpful |
+| `dev` | `full` | Developers benefit from full schema |
+| `standard` | `full` | Good developer experience |
+| `prod` | `summary` | Bounds visible; reasons may be sensitive |
+| `fsi` | `summary` | Regulatory constraints may not need full exposure |
+| `sovereign` | `hidden` | Constraint details may be operationally sensitive |
+
+**UI rendering (full mode):**
+```
+VM Size — CPU Count
+  Enter a value between 1 and 32 (whole numbers)
+  Suggested: 2, 4, 8, 16
+  Reason: Platform capacity planning bounds
+```
+
+**API endpoint:** `GET /api/v1/catalog/items/{id}/schema` returns field schemas at the declared visibility level for the authenticated consumer's Tenant profile.
+
+Policy may override the profile default per field or resource type:
+```yaml
+policy:
+  type: transformation
+  rule: >
+    If resource_type == Compute.VirtualMachine
+    AND field.name == cpu_count
+    THEN set: constraint_visibility.level = full
+```
+
+### 13b.3 System Policies — Override Control
+
+| Policy | Rule |
+|--------|------|
+| `LAY-005` | Layer fields declare override intent as allow, constrained, or immutable. The Request Payload Processor enforces override declarations during assembly Step 3. immutable prevents overrides from lower-authority domains only — higher-domain layers may always override. GateKeeper policies may additionally lock allow or constrained fields at runtime. |
+| `LAY-006` | Constraint schemas on constrained fields are visible to consumers in the Service Catalog UI and Consumer API at a policy-governed disclosure level: full (constraint, bounds, reason, suggestions), summary (bounds only), or hidden (enforced but not displayed). Profile sets the default. Policy may override per field or resource type. |
+
+
+## 13a. Layer System Policies
+
+| Policy | Rule |
+|--------|------|
+| `LAY-001` | Consumers may declare `layer_exclusions` in their request. Each exclusion must carry a human-readable reason recorded in provenance. GateKeeper policies may declare specific layers non-excludable. Excluded layers produce no fields in the assembled payload. |
+| `LAY-002` | Service Layers are independently versioned artifacts. Service Providers declare layer compatibility using semver constraints. Service Layer Cache entries carry the layer version and are invalidated when the registered version changes. |
+| `LAY-003` | Service Layers may declare `activation_condition` evaluated during Step 2 (Layer Resolution). Layers whose conditions evaluate false are excluded from the candidate set. Condition evaluation results are recorded in the assembly provenance. Conditions may reference request fields, tenant attributes, resource type fields, resolved core layer fields, and ingress fields. |
+| `LAY-004` | Each service dependency executes its own independent layer chain during assembly. Dependencies inherit the parent's resolved placement and identity fields as declared by the Resource Type Specification. Dependencies do not inherit parent consumer declarations, resource-type-specific layers, or provider-specific layers. Layer exclusions may be declared per-dependency. |
+
+---
+
+## 14. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | How are conflicting Service Layers at the same precedence level resolved? | Assembly determinism | ✅ Resolved — priority schema + conflict detection at ingestion |
+| 2 | Should Core Layers be ordered within their precedence level? | Merge determinism | ✅ Resolved — priority schema provides deterministic ordering |
+| 3 | Can a consumer explicitly exclude a layer from their request? | Consumer control vs. standardization | ✅ Resolved — layer_exclusions with mandatory reason; GateKeeper can lock layers as non-excludable (LAY-001) |
+| 4 | How are Service Layers registered and versioned relative to Service Provider registration? | Provider contract | ✅ Resolved — independently versioned; provider declares semver compatibility; cache invalidation on version change (LAY-002) |
+| 5 | Should assembly support conditional layer inclusion? | Assembly flexibility | ✅ Resolved — activation_condition on layers; evaluated in Step 2; references request, tenant, resource type, core layer, and ingress fields (LAY-003) |
+| 6 | How does the layer chain interact with service dependencies? | Dependency model | ✅ Resolved — each dependency has its own layer chain; inherits parent resolved placement context; no consumer declaration inheritance (LAY-004) |
+| 7 | Should `override_preference` be declarable in layer definitions as a hint to the Policy Engine? | Override control | ✅ Resolved — override: allow/constrained/immutable enforced by Request Payload Processor at Step 3; GateKeeper may additionally lock (LAY-005) |
+| 8 | When `override_preference: immutable` is set — can a higher-priority policy still override it? | Override control precedence | ✅ Resolved — immutable prevents lower-authority overrides only; higher-domain layers always win; GateKeeper can additionally lock (LAY-005) |
+| 9 | Should the `constraint_schema` on a constrained field be visible to consumers in the Service Catalog UI? | Consumer experience | ✅ Resolved — full/summary/hidden disclosure levels; profile-governed defaults; API endpoint returns schema at declared visibility (LAY-006) |
+| 10 | Should the background validation job for detecting post-ingestion conflicts run on a schedule or be event-triggered? | Operational | ✅ Resolved — event-triggered primary (on layer ingestion/update) + weekly scheduled sweep safety net; async non-blocking; both produce same conflict record format (OPS-003) |
+| 11 | What is the minimum validation review period for a proposed policy before it can be activated? | Policy governance | ✅ Resolved — GateKeeper=14d, Validation=7d, Transformation=3d × profile multiplier (minimal=0×, dev=0.5×, standard=1×, prod=1.5×, fsi/sovereign=2×); DCM enforces; emergency bypass requires dual-approval audit (OPS-004) |
+
+---
+
+## 14. Related Concepts
+
+- **Request Payload Processor** — the control plane component that executes the assembly process; enforces structural layer rules
+- **Policy Engine** — executes Policy Layers (Validation, Transformation, GateKeeper) during the assembly process; the sole authority for setting field override control
+- **Field Override Control** — the mechanism governing who can change what field, under what conditions, at what policy level
+- **Override Preference** — per-field metadata declaring `allow`, `constrained`, or `immutable` — the formalization of the original data model "override preference" subtag
+- **Service Layer Cache** — caches Service Layer data at Service Provider registration time for efficient retrieval during assembly
+- **Core Layer Store** — stores all Core Layer definitions
+- **Intent State** — the Request Layer as submitted, before assembly
+- **Requested State** — the fully assembled, policy-processed payload
+- **Field-Level Provenance** — every field in the assembled payload records which layer set it and which policy modified it
+- **Resource Type Hierarchy** — defines the type scope that Service Layers must declare and that the assembly process enforces
+- **GitOps** — all layers are stored in Git, versioned and immutable
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/04-examples.md b/content/docs/architecture/data-model/04-examples.md
new file mode 100644
index 0000000..a1b8218
--- /dev/null
+++ b/content/docs/architecture/data-model/04-examples.md
@@ -0,0 +1,632 @@
+# DCM Data Model — Worked Examples
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Reference Examples
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + PROVIDER + POLICY**
+>
+> Worked examples showing all three abstractions in operation
+
+
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Four States](02-four-states.md) | [Entity Types](01-entity-types.md) | [Ownership, Sharing, and Allocation](04b-ownership-sharing-allocation.md) | [Layering and Versioning](03-layering-and-versioning.md)
+
+---
+
+## 1. Purpose
+
+This document provides end-to-end worked examples that make the DCM data model concrete. Each example traces the complete lifecycle of a resource through DCM — from consumer intent through the four states, showing exactly what data exists at each stage.
+
+These examples also resolve outstanding implementation details deferred from other documents, specifically the Git repository structure for the Intent and Requested stores.
+
+---
+
+## 2. Git Repository Structure (Optional Ingress Adapter)
+
+> **Note:** Git is an optional ingress adapter, not a required state store. All four data domains (Intent, Requested, Realized, Discovered) are stored in DCM's PostgreSQL database. These Git layouts apply only when teams choose to use Git/PR-based workflows for submitting intent. See [51-infrastructure-optimization.md](51-infrastructure-optimization.md) for the prescribed infrastructure model.
+
+This resolves the deferred Q54 item from the Four States document (Section 4.1).
+
+The Intent store uses a handle-based directory structure within Git when Git ingress is enabled. Tenant isolation is enforced at the directory level. Provider selection (the Q54 concern) is recorded in the assembled payload, not in the directory structure — so the directory structure is independent of which provider was selected.
+
+### 2.1 Intent Store Layout
+
+```
+intent-store/
+├── {tenant-uuid}/
+│   ├── {resource-type-category}/
+│   │   ├── {resource-type}/
+│   │   │   ├── {entity-uuid}/
+│   │   │   │   ├── intent.yaml          ← consumer's raw declaration
+│   │   │   │   └── .metadata.yaml       ← intent metadata (created_by, timestamp, ingress surface)
+│   │   │   └── {entity-uuid-2}/
+│   │   │       ├── intent.yaml
+│   │   │       └── .metadata.yaml
+│   │   └── ...
+│   └── ...
+└── ...
+
+# Example:
+intent-store/
+└── a1b2c3d4-tenant-uuid/
+    └── Compute/
+        └── VirtualMachine/
+            └── f5e6d7c8-entity-uuid/
+                ├── intent.yaml
+                └── .metadata.yaml
+```
+
+**Branch naming:** `intent/{tenant-uuid}/{entity-uuid}` for new requests. `intent/{tenant-uuid}/{entity-uuid}/v{n}` for revisions.
+
+**Merge to main:** Triggers the CD pipeline — Request Payload Processor begins assembly.
+
+### 2.2 Requested Store Layout
+
+```
+requested-store/
+└── {tenant-uuid}/
+    └── {resource-type-category}/
+        └── {resource-type}/
+            └── {entity-uuid}/
+                ├── requested.yaml          ← fully assembled payload
+                ├── assembly-provenance.yaml ← complete layer chain and policy evaluation record
+                ├── placement.yaml           ← provider selection and placement constraints
+                └── dependencies.yaml        ← resolved dependency graph (only present when entity has dependencies)
+
+# Example:
+requested-store/
+└── a1b2c3d4-tenant-uuid/
+    └── Compute/
+        └── VirtualMachine/
+            └── f5e6d7c8-entity-uuid/
+                ├── requested.yaml
+                ├── assembly-provenance.yaml
+                └── placement.yaml
+```
+
+### 2.3 Layer and Policy Store Layout
+
+**Core layers** are the organization's authoritative declarations — they define what the organization intends, not what providers report. For example, `datacenter-layer.yaml` declares the properties of datacenter `dc-us-east-1` (location, sovereignty zone, available VLANs). Providers are validated against these declarations. The Discovery Service detects drift when provider-reported state diverges from declared layers. Layers are not synced *from* providers — providers are measured *against* them.
+
+```
+layers/
+├── system/
+│   ├── core/
+│   │   ├── datacenter-layer.yaml
+│   │   └── environment-layer.yaml
+│   └── compliance/
+│       └── pci-dss-layer.yaml
+├── {tenant-uuid}/
+│   └── org/
+│       └── payments-team-layer.yaml
+└── providers/
+    └── {provider-uuid}/
+        └── vm-defaults-layer.yaml
+
+policies/
+├── system/
+│   ├── gatekeeper/
+│   │   └── vm-size-limits.yaml
+│   └── transformation/
+│       └── inject-monitoring.yaml
+└── {tenant-uuid}/
+    └── gatekeeper/
+        └── approved-os-images.yaml
+```
+
+---
+
+## 3. Example 1 — VM Provision End-to-End
+
+A developer on the AppTeam Tenant requests a standard Linux VM. This example traces the complete lifecycle through all four states.
+
+### 3.1 Consumer Submits Intent (Intent State)
+
+The developer submits the following intent via the Consumer API:
+
+```yaml
+# intent-store/a1b2c3d4-tenant/Compute/VirtualMachine/f5e6d7c8-entity/intent.yaml
+
+apiVersion: dcm.io/v1
+kind: ResourceIntent
+metadata:
+  entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0
+  resource_type: Compute.VirtualMachine
+  tenant_uuid: a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6   # AppTeam Tenant
+  submitted_by: b2c3d4e5-actor-uuid
+  submitted_at: 2026-03-15T09:00:00Z
+  ingress_surface: consumer_api
+
+spec:
+  # Consumer declares what they need — not how to provision it
+  cpu_count: 4
+  memory_gb: 8
+  storage_gb: 100
+  os_family: rhel
+  environment: production
+  name: "payments-api-server-01"
+  # No provider specified — consumer does not choose the provider
+```
+
+**CI pipeline runs immediately:**
+- Policy pre-validation: no GateKeeper violations detected (4 CPU is within AppTeam's quota)
+- Cost estimation: ~$0.32/hour based on current provider rates
+- Dependency check: no dependencies declared — clean
+- Sovereignty check: AppTeam's Tenant has `data_residency: EU-WEST` — placement must honor this
+- Authorization check: actor b2c3d4e5 has `request:compute:vm` permission in AppTeam Tenant
+- Auto-approve evaluation: meets all auto-approve criteria → PR auto-merged
+
+### 3.2 Assembly Produces Requested State
+
+After intent merge, the Request Payload Processor runs the nine-step assembly:
+
+**Step 3 — Layer Resolution and Merge:**
+
+```yaml
+# Layer chain assembled (in precedence order, highest to lowest):
+# 1. system/core/datacenter-layer.yaml        (system domain)
+# 2. system/core/environment-layer.yaml        (system domain)
+# 3. system/compliance/eu-west-layer.yaml      (system domain)
+# 4. org/appteam-defaults-layer.yaml           (tenant domain)
+# 5. providers/openstack/vm-defaults-layer.yaml (provider domain — pre-selected by policy)
+# 6. Consumer intent                            (request domain)
+
+# Resulting merged fields before policy evaluation:
+cpu_count:
+  value: 4                         # from consumer intent
+  provenance.origin.source_type: consumer
+  provenance.origin.source_uuid: f5e6d7c8-entity
+
+memory_gb:
+  value: 8                         # from consumer intent
+  provenance.origin.source_type: consumer
+
+storage_gb:
+  value: 100                       # from consumer intent
+
+data_center:
+  value: "EU-WEST-DC1"             # from datacenter layer
+  provenance.origin.source_type: base_layer
+  provenance.origin.source_uuid: dc-layer-uuid
+
+environment:
+  value: production                # from consumer intent (overrides layer default "dev")
+  provenance.modifications:
+    - sequence: 1
+      previous_value: dev          # layer default
+      modified_value: production   # consumer override
+      source_type: consumer
+
+monitoring_agent:
+  value: "datadog-agent:7.42"     # injected by org layer — consumer did not declare this
+  provenance.origin.source_type: intermediate_layer
+  provenance.origin.source_uuid: appteam-defaults-layer-uuid
+
+backup_policy:
+  value: "daily-30d-eu-west"      # injected by compliance layer
+  provenance.origin.source_type: intermediate_layer
+  provenance.origin.source_uuid: eu-west-compliance-layer-uuid
+```
+
+**Step 5-7 — Policy Evaluation:**
+
+```yaml
+# GateKeeper policy: vm-size-limits evaluates
+# Result: APPROVED (4 CPU within AppTeam's 16 CPU limit)
+
+# Transformation policy: inject-monitoring evaluates
+# Result: monitoring_endpoint field injected
+monitoring_endpoint:
+  value: "https://metrics.internal.eu-west.example.com"
+  provenance.modifications:
+    - sequence: 1
+      previous_value: null
+      modified_value: "https://metrics.internal.eu-west.example.com"
+      source_type: policy
+      source_uuid: inject-monitoring-policy-uuid
+      operation_type: enrichment
+      reason: "Standard monitoring endpoint for EU-WEST production resources"
+
+# GateKeeper policy: approved-os-images evaluates (AppTeam's tenant policy)
+# Result: APPROVED (rhel is in AppTeam's approved images list)
+```
+
+**Step 6 — Placement Engine selects provider:**
+- Sovereignty pre-filter: eligible providers must satisfy `data_residency: EU-WEST`
+- Reserve query to 3 eligible OpenStack instances
+- EU-WEST-Prod-1 responds: capacity available, confidence 94
+- EU-WEST-Prod-2 responds: capacity available, confidence 87
+- EU-WEST-Prod-3: insufficient capacity
+- Tie-breaking: EU-WEST-Prod-1 selected (highest confidence score)
+
+**Requested State committed:**
+
+```yaml
+# requested-store/a1b2c3d4-tenant/Compute/VirtualMachine/f5e6d7c8-entity/requested.yaml
+
+apiVersion: dcm.io/v1
+kind: RequestedState
+metadata:
+  entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0
+  resource_type: Compute.VirtualMachine
+  tenant_uuid: a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6
+  assembled_at: 2026-03-15T09:00:47Z
+  intent_state_ref: f5e6d7c8-intent-ref-uuid
+
+spec:
+  cpu_count: { value: 4, provenance: {...} }
+  memory_gb: { value: 8, provenance: {...} }
+  storage_gb: { value: 100, provenance: {...} }
+  os_family: { value: rhel, provenance: {...} }
+  environment: { value: production, provenance: {...} }
+  name: { value: "payments-api-server-01", provenance: {...} }
+  data_center: { value: "EU-WEST-DC1", provenance: {...} }
+  monitoring_agent: { value: "datadog-agent:7.42", provenance: {...} }
+  backup_policy: { value: "daily-30d-eu-west", provenance: {...} }
+  monitoring_endpoint: { value: "https://metrics...", provenance: {...} }
+
+placement:
+  selected_provider_uuid: eu-west-prod-1-provider-uuid
+  placement_reason: "highest confidence score among eligible providers"
+  sovereignty_satisfied: true
+  reserve_query_response_ref: 
+```
+
+### 3.3 Provider Realizes the Resource (Realized State)
+
+OpenStack EU-WEST-Prod-1 receives the payload, naturalizes it to OpenStack format, provisions the VM, and returns the denaturalized result:
+
+```yaml
+# Event written to Realized Store event stream (entity_uuid key)
+
+event_type: REALIZED
+entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0
+realized_at: 2026-03-15T09:03:12Z
+provider_uuid: eu-west-prod-1-provider-uuid
+
+# DCM unified fields
+cpu_count: { value: 4, provenance: { ...plus provider attribution } }
+memory_gb: { value: 8, provenance: {...} }
+storage_gb: { value: 100, provenance: {...} }
+
+# Provider-added fields (not in Requested State — added by provider after realization)
+provider_entity_id: "vm-0a1b2c3d"              # OpenStack's internal VM ID
+assigned_ip_address: "10.1.45.23"              # IP assigned by provider at realization
+hypervisor_host: "compute-node-07.eu-west"      # where the VM was physically placed
+actual_storage_gb: 102                          # actual allocated (rounded up)
+console_url: "https://console.eu-west.example.com/vm/0a1b2c3d"
+```
+
+### 3.4 Discovery Cycle (Discovered State)
+
+24 hours after realization, the discovery cycle runs:
+
+```yaml
+# Snapshot written to Discovered Store
+
+snapshot_type: DISCOVERED
+entity_uuid: f5e6d7c8-e9f0-a1b2-c3d4-e5f6a7b8c9d0
+discovered_at: 2026-03-16T09:00:00Z
+discovery_method: openstack_api_query
+provider_uuid: eu-west-prod-1-provider-uuid
+
+cpu_count: 4          # matches Realized State — no drift
+memory_gb: 8          # matches
+storage_gb: 102       # matches (actual_storage_gb from provider)
+provider_entity_id: "vm-0a1b2c3d"
+status: ACTIVE
+```
+
+Drift Detection runs field-by-field comparison: all fields match Realized State. No drift event generated.
+
+---
+
+## 4. Example 2 — IP Address Allocation
+
+An allocation request showing the `allocation` ownership model (pool → owned allocation).
+
+```yaml
+# Consumer submits intent for an IP address
+# intent-store/a1b2c3d4-tenant/Network/IPAddress/ip-entity-uuid/intent.yaml
+
+spec:
+  requested_from: network                   # request from the network pool
+  address_family: IPv4
+  purpose: vm_interface
+  attachment_ref: f5e6d7c8-entity-uuid     # the VM this IP will be assigned to
+
+# Assembly runs — placement engine finds eligible IPAddressPool
+# Pool: NetworkOps/Network/IPAddressPool/10.1.0.0-16 (owned by NetworkOps Tenant)
+# Available capacity: 65420 addresses
+
+# Provider carves allocation:
+# New entity created: IPAddress 10.1.45.23/32
+# Owned by: AppTeam Tenant (a1b2c3d4)
+# AllocationRecord relationship created:
+#   IPAddress 10.1.45.23/32 --[allocated_from]--> IPAddressPool 10.1.0.0/16
+
+# Realized State event for the new IPAddress entity:
+entity_uuid: ip-entity-uuid
+resource_type: Network.IPAddress
+ownership_model: allocation
+owned_by_tenant_uuid: a1b2c3d4-appteam-uuid    # AppTeam owns this
+allocated_from_pool_uuid: pool-entity-uuid      # NetworkOps owns the pool
+address: "10.1.45.23"
+prefix_length: 32
+address_family: IPv4
+```
+
+When AppTeam decommissions their VM, the IP address entity can also be decommissioned. The pool's available capacity increases by 1. NetworkOps Tenant is unaffected.
+
+---
+
+## 5. Example 3 — VLAN Attachment (Shareable)
+
+A VM attaches to an existing VLAN — the `shareable` ownership model (stake, not ownership).
+
+```yaml
+# VLAN-100 exists — owned by NetworkOps Tenant
+# entity_uuid: vlan-100-entity-uuid
+# ownership_model: shareable
+
+# Consumer (AppTeam) requests VM attachment to VLAN-100
+# No new VLAN entity is created — a stake relationship is established:
+
+relationship:
+  type: attached_to
+  source_entity_uuid: f5e6d7c8-vm-entity-uuid   # AppTeam's VM
+  target_entity_uuid: vlan-100-entity-uuid        # NetworkOps's VLAN
+  source_tenant_uuid: a1b2c3d4-appteam-uuid
+  target_tenant_uuid: netops-tenant-uuid
+  stake:
+    is_active: true
+    stake_strength: required                       # VM cannot function without VLAN
+    staked_at: 2026-03-15T09:03:12Z
+
+# If NetworkOps tries to decommission VLAN-100:
+# active required stakes: 3 (VM-A, VM-B, VM-C all have required stakes)
+# Result: DECOMMISSION_DEFERRED
+# NetworkOps notified: "VLAN-100 has 3 required stakeholders. Decommission deferred."
+# Each stakeholder (AppTeam, DevTeam, OpsTeam) notified:
+# "NetworkOps has requested decommission of VLAN-100. Please migrate your workloads."
+```
+
+---
+
+## 6. Example 4 — Brownfield Ingestion
+
+A VM discovered by the provider that DCM did not provision is brought under DCM lifecycle management.
+
+```yaml
+# Step 1: INGEST — discovery finds unknown VM
+discovered_entity:
+  provider_entity_id: "vm-legacy-0001"
+  resource_type: Compute.VirtualMachine
+  lifecycle_state: OPERATIONAL          # it's running
+  discovered_at: 2026-03-15T06:00:00Z
+  discovery_confidence: low             # no DCM provenance
+  transitional_tenant: __transitional__ # held in transitional Tenant during ingestion
+
+# Step 2: ENRICH — CMDB Information Provider enriches the entity
+# CMDB lookup by IP address finds the business owner record:
+enrichment:
+  owner_business_unit: "Payments Platform"
+  cost_center: "PAYM-4421"
+  product_owner: "Jane Smith"
+  compliance_scope: PCI-DSS
+  confidence_descriptor:
+    authority_level: primary            # CMDB is primary authority for ownership data
+    corroboration: single_source        # only CMDB has this data
+    source_trust: verified
+
+# Step 3: PROMOTE — operator assigns to AppTeam Tenant, creates entity record
+promotion:
+  target_tenant_uuid: a1b2c3d4-appteam-uuid
+  created_via: ingestion
+  intent_state_created: true            # Intent State created from discovered configuration
+  provenance_basis: discovered          # provenance chain starts from discovery
+  promoted_by: operator-actor-uuid
+  promoted_at: 2026-03-15T11:30:00Z
+```
+
+After promotion, the entity is a full DCM-managed entity. Drift detection is active. The operator can now request updates (targeted delta) or decommission through DCM.
+
+---
+
+## 7. Example 5 — Drift Detection and Remediation
+
+Six hours after the VM from Example 1 was realized, discovery finds a discrepancy:
+
+```yaml
+# Discovery finds:
+cpu_count: 4       # matches
+memory_gb: 16      # DRIFT — realized says 8, discovered says 16
+
+# Drift record created:
+drift_record:
+  entity_uuid: f5e6d7c8-entity-uuid
+  detected_at: 2026-03-15T15:00:00Z
+  drifted_fields:
+    - field_path: memory_gb
+      realized_value: 8
+      discovered_value: 16
+  drift_severity: significant      # memory doubling is significant
+  unsanctioned: true               # no DCM Requested State explains this change
+```
+
+**Policy Engine evaluates the drift record:**
+
+```yaml
+# Drift response policy for Compute.VirtualMachine at significant severity:
+# action: ESCALATE for unsanctioned changes
+
+escalation:
+  entity_uuid: f5e6d7c8-entity-uuid
+  notified:
+    - actor: b2c3d4e5-consumer-actor   # the entity owner
+    - actor: appteam-admin-actor        # AppTeam admin
+    - actor: sre-oncall-actor           # SRE on-call
+  escalation_reason: "Unsanctioned memory change: 8Gi → 16Gi"
+  resolution_options:
+    - REVERT: "Submit rehydration from Realized State to restore 8Gi memory"
+    - UPDATE_DEFINITION: "Promote discovered state — update entity definition to 16Gi"
+    - ACCEPT: "Accept the change; add to next review cycle"
+```
+
+The consumer reviews and chooses UPDATE_DEFINITION — the memory was legitimately increased by the infrastructure team for a critical workload. They submit an UPDATE_DEFINITION resolution, which creates a new Requested State reflecting 16Gi memory and updates the Realized State record. Future drift detection will compare against 16Gi.
+
+---
+
+## 8. Example 6 — Three-Tier Application (Compound Service with Binding Fields)
+
+This example shows how a compound service request flows through the dependency graph, with runtime values from one resource injected into dependent resources via `binding_fields`.
+
+### 8.1 Consumer Request
+
+A consumer requests a "Web Application — Standard" from the catalog. This is a single catalog item backed by a compound resource type that composes three resources:
+
+```yaml
+# Consumer submits via API
+POST /api/v1/requests
+{
+  "catalog_item_uuid": "webapp-standard-uuid",
+  "fields": {
+    "app_name": "pet-clinic",
+    "environment": "staging",
+    "db_engine": "postgresql",
+    "db_storage_gb": 50,
+    "web_replicas": 2
+  }
+}
+```
+
+### 8.2 Resource Type Spec — WebApp.ThreeTier
+
+The compound resource type spec declares three constituent resources and the binding fields that connect them:
+
+```yaml
+resource_type: WebApp.ThreeTier
+entity_type: composite_resource
+constituents:
+  - name: database
+    resource_type: Database.PostgreSQL
+    required: true
+    fields_from_parent:
+      - source: "db_engine"
+        target: "engine"
+      - source: "db_storage_gb"
+        target: "storage_gb"
+      - source: "environment"
+        target: "environment"
+
+  - name: backend
+    resource_type: Compute.VirtualMachine
+    required: true
+    depends_on: [database]
+    binding_fields:
+      - source: "database.ip_address"      # ← from realized Database
+        target: "backend.config.db_host"   # ← injected into Backend payload
+      - source: "database.port"
+        target: "backend.config.db_port"
+      - source: "database.credentials_ref"
+        target: "backend.config.db_credentials_ref"
+    fields_from_parent:
+      - source: "app_name"
+        target: "hostname_prefix"
+      - source: "environment"
+        target: "environment"
+
+  - name: frontend
+    resource_type: Compute.VirtualMachine
+    required: true
+    depends_on: [backend]
+    binding_fields:
+      - source: "backend.ip_address"       # ← from realized Backend
+        target: "frontend.config.api_host" # ← injected into Frontend payload
+      - source: "backend.port"
+        target: "frontend.config.api_port"
+    fields_from_parent:
+      - source: "app_name"
+        target: "hostname_prefix"
+      - source: "web_replicas"
+        target: "replicas"
+      - source: "environment"
+        target: "environment"
+
+lifecycle_policy:
+  on_constituent_failure: rollback_all    # If any constituent fails, decommission all
+```
+
+### 8.3 Pipeline Execution
+
+```
+1. Intent captured — consumer's request stored in intent_records
+
+2. Request Processor assembles the composite payload
+   → Resolves WebApp.ThreeTier resource type spec
+   → Identifies 3 constituent resources with dependency graph:
+     Database (no deps) → Backend (depends on Database) → Frontend (depends on Backend)
+
+3. Policy Engine evaluates the composite request
+   → GateKeeper: staging environment authorized for this tenant
+   → Validation: db_storage_gb within tier limits
+   → Transformation: injects monitoring agent config into all 3 constituents
+
+4. Placement Engine selects providers for each constituent
+   → Database → dcm-provider-database (scored highest for PostgreSQL in staging zone)
+   → Backend → dcm-provider-vm (KubeVirt provider)
+   → Frontend → dcm-provider-vm (same provider, same zone)
+
+5. Request Orchestrator dispatches in dependency order:
+
+   Step 5a: Dispatch Database to dcm-provider-database
+   → Provider realizes PostgreSQL instance
+   → Callbacks: ip_address: 10.0.1.50, port: 5432, credentials_ref: vault:secret/pet-clinic-db
+
+   Step 5b: Request Processor resolves binding_fields for Backend
+   → Injects: config.db_host = 10.0.1.50 (from Database.ip_address)
+   → Injects: config.db_port = 5432 (from Database.port)
+   → Injects: config.db_credentials_ref = vault:secret/pet-clinic-db
+   → Dispatch Backend to dcm-provider-vm
+   → Provider realizes VM with application config containing DB connection
+   → Callbacks: ip_address: 10.0.2.30, port: 8080
+
+   Step 5c: Request Processor resolves binding_fields for Frontend
+   → Injects: config.api_host = 10.0.2.30 (from Backend.ip_address)
+   → Injects: config.api_port = 8080 (from Backend.port)
+   → Dispatch Frontend to dcm-provider-vm
+   → Provider realizes 2 VM replicas with backend endpoint configured
+   → Callbacks: ip_addresses: [10.0.3.10, 10.0.3.11], port: 443
+
+6. All constituents realized — composite entity status: OPERATIONAL
+   → Audit records written for all 4 entities (composite + 3 constituents)
+   → Consumer sees single "pet-clinic" application in their resource list
+```
+
+### 8.4 What the Consumer Sees
+
+The consumer requested one catalog item and sees one composite resource. The three constituent resources are visible as children:
+
+```
+pet-clinic (WebApp.ThreeTier) — OPERATIONAL
+├── pet-clinic-db (Database.PostgreSQL) — OPERATIONAL
+│   ip_address: 10.0.1.50
+├── pet-clinic-backend (Compute.VirtualMachine) — OPERATIONAL
+│   ip_address: 10.0.2.30, config.db_host: 10.0.1.50
+└── pet-clinic-frontend (Compute.VirtualMachine × 2) — OPERATIONAL
+    ip_addresses: [10.0.3.10, 10.0.3.11], config.api_host: 10.0.2.30
+```
+
+If the database is decommissioned, the `lifecycle_policy: rollback_all` cascades to backend and frontend. If the consumer requests a tier upgrade, the Meta Provider coordinates the upgrade across all three constituents, maintaining the binding field connections throughout.
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/04b-ownership-sharing-allocation.md b/content/docs/architecture/data-model/04b-ownership-sharing-allocation.md
new file mode 100644
index 0000000..1e89ac1
--- /dev/null
+++ b/content/docs/architecture/data-model/04b-ownership-sharing-allocation.md
@@ -0,0 +1,307 @@
+# DCM Data Model — Ownership, Sharing, and Allocation
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA**
+>
+> The Data abstraction — ownership models for entity data
+
+
+**Related Documents:** [Entity Types](01-entity-types.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Entity Relationships](09-entity-relationships.md) | [Resource Grouping](08-resource-grouping.md)
+
+---
+
+## 1. Purpose
+
+This document defines the complete ownership model for DCM entities — specifically the three ownership patterns that govern how resources are owned, shared, and allocated across Tenants. It establishes precise vocabulary and clear boundaries between concepts that are frequently conflated:
+
+- **Ownership** — who is accountable for a resource's lifecycle and costs
+- **Shareable** — multiple entities have a stake in a single resource that they do not own
+- **Allocatable** — a pool resource yields independently owned sub-resources to consumers
+
+Getting this model right is foundational. It governs decommission safety (can this resource be removed?), cost attribution (who pays for this?), cross-tenant visibility (who can see this?), drift accountability (whose responsibility is remediation?), and placement decisions (which providers serve allocation requests?).
+
+---
+
+## 2. The Three Ownership Patterns
+
+### 2.1 Whole Allocation (Consumer Owns the Entity)
+
+**What it means:** The consumer receives the entire resource entity. It belongs exclusively to them. They own it outright — it is in their Tenant, they control its lifecycle, they bear its costs.
+
+**Structural model:**
+```
+Platform Tenant owns: the infrastructure, compute capacity, network fabric
+Consumer Tenant owns: VirtualMachine-A (a distinct entity)
+Consumer Tenant owns: VirtualMachine-B (another distinct entity)
+```
+There is no relationship between VirtualMachine-A and the infrastructure Tenant — the consumer simply used DCM's Service Provider to provision a resource. Once provisioned, the entity belongs to the consumer's Tenant entirely. The platform Tenant has no visibility into the consumer's entity unless an explicit Information Provider or cross-tenant relationship is established.
+
+**Examples:** VirtualMachine, Container, StorageVolume, NetworkInterface, DNSRecord.
+
+**Resource Type Spec declaration:**
+```yaml
+resource_type_spec:
+  fqn: Compute.VirtualMachine
+  ownership_model: whole_allocation
+  # Each consumer request produces an entity owned entirely by the requesting Tenant
+```
+
+**Decommission behavior:** Straightforward. The owning Tenant decommissions the entity. The provider releases the underlying physical resources. No other Tenant is affected.
+
+---
+
+### 2.2 Allocation (Consumer Owns a Carved Portion)
+
+**What it means:** A *pool* resource is owned by a platform or provider Tenant (the pool owner). When a consumer requests a resource of this type, they receive an *allocation* — a new, distinct entity carved from the pool. The consumer **owns** their allocation. The pool owner retains ownership of the pool.
+
+**The key distinction from Shareable:** The consumer's allocation is an independent entity with its own UUID, its own Tenant membership, its own lifecycle. It is not a reference to the pool — it is a new thing that came from the pool.
+
+**Structural model:**
+```
+NetworkOps Tenant owns: IPAddressPool 10.0.0.0/16 (pool entity)
+  │
+  ├── AppTeam Tenant owns: IPAddress 10.0.1.45/32  ← allocation entity (new UUID, AppTeam's Tenant)
+  ├── DevTeam Tenant owns: IPAddress 10.0.1.46/32  ← allocation entity (new UUID, DevTeam's Tenant)
+  └── OpsTeam Tenant owns: IPAddress 10.0.1.47/32  ← allocation entity (new UUID, OpsTeam's Tenant)
+```
+
+The IPAddress entities are not pointers to the pool — they are real entities owned by their Tenants. If AppTeam decommissions 10.0.1.45/32, it is released back to the pool. The pool capacity increases. No other Tenant's allocation is affected.
+
+**Examples:** IPAddress (from IPAddressPool), Subnet (from SubnetPool), VLAN ID (from VLANIDPool), StorageVolume (from StoragePool), PublicCertificate (from CertificateAuthorityPool).
+
+**Resource Type Spec declarations (both pool and allocation):**
+```yaml
+# The pool resource type
+resource_type_spec:
+  fqn: Network.IPAddressPool
+  ownership_model: whole_allocation    # the pool entity is owned outright by the platform Tenant
+  is_pool: true
+  allocation_produces_type: Network.IPAddress
+  capacity_tracking: true              # DCM tracks used/available capacity
+
+# The allocation resource type
+resource_type_spec:
+  fqn: Network.IPAddress
+  ownership_model: allocation          # each instance is an allocation from a pool
+  allocated_from_pool_type: Network.IPAddressPool
+  # When a consumer requests Network.IPAddress, DCM:
+  # 1. Runs placement to find an eligible IPAddressPool
+  # 2. The pool provider carves out a specific IP
+  # 3. DCM creates a new IPAddress entity owned by the requesting Tenant
+  # 4. Records an AllocationRecord relationship between the entity and the pool
+```
+
+**AllocationRecord relationship:**
+Every allocation entity carries an `allocated_from` relationship to its source pool:
+
+```yaml
+relationship:
+  relationship_uuid: 
+  type: allocated_from
+  source_entity_uuid:        # the allocation (e.g., IPAddress 10.0.1.45/32)
+  target_entity_uuid:        # the pool (e.g., IPAddressPool 10.0.0.0/16)
+  source_tenant_uuid:        # AppTeam Tenant
+  target_tenant_uuid:        # NetworkOps Tenant — cross-tenant relationship
+  allocation_ref:
+    allocation_size: "1/32"        # what was carved from the pool
+    allocated_at: 
+    allocated_by_actor_uuid: 
+    allocation_metadata:
+      pool_capacity_before: 65534
+      pool_capacity_after: 65533
+```
+
+**Decommission behavior:** When the consumer decommissions their allocation entity, DCM dispatches a decommission payload to the provider. The provider releases the specific allocated resource back to the pool. The pool's available capacity increases. The AllocationRecord relationship is terminated. The allocation entity enters DECOMMISSIONED state. The pool entity is unaffected.
+
+**Cross-tenant visibility:** The allocation entity is in the consumer's Tenant. The consumer cannot see the pool entity unless an explicit cross-tenant relationship or Information Provider is configured. The pool owner can see allocation counts and capacity via the Cost Analysis component and the provider's capacity reporting API — they cannot see the consumer's entity data.
+
+---
+
+### 2.3 Shareable (Consumer Has a Stake, Not Ownership)
+
+**What it means:** A single resource entity is owned by one Tenant (the resource owner) and multiple consumers attach to, depend on, or reference it. Consumers have a *stake* — a relationship that affects the resource's lifecycle — but they do not own any portion of it. The resource's lifecycle is governed entirely by its owner.
+
+**The key distinction from Allocation:** No new entity is created for the consumer. The consumer receives a relationship to the existing resource, not a new sub-entity. The consumer does not own anything — they hold a stake.
+
+**Structural model:**
+```
+NetworkOps Tenant owns: VLAN-100 (single entity — there is only one VLAN-100)
+  │
+  ├── AppTeam has stake: VM-A attached to VLAN-100   (relationship, not ownership)
+  ├── DevTeam has stake: VM-B attached to VLAN-100   (relationship, not ownership)
+  └── OpsTeam has stake: VM-C attached to VLAN-100   (relationship, not ownership)
+```
+
+VLAN-100 belongs to NetworkOps. AppTeam, DevTeam, and OpsTeam each have a VM attached to it. If DevTeam decommissions VM-B, VLAN-100 is unaffected — it still exists and serves VM-A and VM-C. If NetworkOps wants to decommission VLAN-100, they cannot do so while VMs are attached. Decommission is deferred until all stakes are released.
+
+**Examples:** VLAN (network fabric shared by many VMs), NetworkSegment, SharedFileSystem, DNS Zone, NTP Server, Certificate Authority (as a service), Transit Gateway.
+
+**Resource Type Spec declaration:**
+```yaml
+resource_type_spec:
+  fqn: Network.VLAN
+  ownership_model: shareable
+  # A single VLAN entity exists; consumers attach to it via relationships
+  # Consumers do not receive their own VLAN entity
+  decommission_policy:
+    defer_while_active_stakes: true
+    minimum_stake_count: 0       # can decommission when all stakes released
+    # Some resources may require minimum_stake_count: 1
+    # e.g., a DNS Zone that should never be empty
+```
+
+**Stake relationship:**
+```yaml
+relationship:
+  relationship_uuid: 
+  type: attached_to          # or: depends_on, references, uses
+  source_entity_uuid:    # VM-A (consumer's entity)
+  target_entity_uuid:    # VLAN-100 (shared resource)
+  source_tenant_uuid:    # AppTeam Tenant
+  target_tenant_uuid:    # NetworkOps Tenant
+  stake:
+    is_active: true
+    staked_at: 
+    staked_by_actor_uuid: 
+    stake_strength: 
+    # required: VM cannot function without VLAN attachment (blocks VLAN decommission)
+    # preferred: VM prefers attachment but can function without it
+    # optional: informational stake only
+```
+
+**Decommission behavior:** VLAN-100 cannot be decommissioned while any `stake_strength: required` stakes exist. The decommission attempt is deferred — not rejected — and a `DECOMMISSION_DEFERRED` event is generated. DCM notifies the resource owner of all active required stakes. The owner can request that stakeholders release their stakes (by decommissioning their VMs or migrating to a different VLAN) before decommission proceeds.
+
+**Cross-tenant visibility:** The consumer's VM can reference VLAN-100 (read access for attachment purposes). The consumer cannot modify VLAN-100 or see its owner's configuration details unless an explicit cross-tenant authorization grants that. The resource owner (NetworkOps) can see all active stakes on their resource — this is how they know which consumers are affected by a planned decommission.
+
+---
+
+## 3. Hybrid Case — Allocation from a Shareable Pool
+
+Some resources combine both patterns. A Subnet Pool is an allocatable pool. Each allocation (a specific /28) is owned by the consumer. But the /28 has a stake relationship to the parent /16 (which is shareable — owned by the network team, referenced by all subnets).
+
+```
+NetworkOps Tenant owns: SupernetPool 10.0.0.0/8 (allocatable pool)
+  │
+  ├── NetworkOps Tenant owns: 10.0.0.0/16 (allocation from /8 — NetworkOps-owned)
+  │     NetworkOps Tenant owns: 10.0.0.0/24 (allocation from /16 — NetworkOps-owned)
+  │
+  └── NetworkOps Tenant owns: 10.1.0.0/16 (allocation from /8 — NetworkOps-owned, shared)
+        ├── AppTeam Tenant owns: 10.1.0.0/24  ← consumer allocation (owned by AppTeam)
+        │     └── stake: attached to 10.1.0.0/16 (shareable — NetworkOps)
+        └── DevTeam Tenant owns: 10.1.1.0/24  ← consumer allocation (owned by DevTeam)
+              └── stake: attached to 10.1.0.0/16 (shareable — NetworkOps)
+```
+
+The consumer-facing 10.1.0.0/24 is an allocation — AppTeam owns it. The parent 10.1.0.0/16 is shareable — NetworkOps owns it, AppTeam and DevTeam both have stakes. The 10.1.0.0/24 has both an `allocated_from` relationship (to the /24 pool that produced it) and an `attached_to` relationship (stake in the parent /16).
+
+---
+
+## 4. Ownership Model Summary
+
+| Pattern | Consumer Gets | Consumer Owns | New Entity Created | Lifecycle Governed By | Cost Attribution |
+|---------|--------------|--------------|-------------------|----------------------|-----------------|
+| **Whole Allocation** | The entire resource | Yes, outright | Yes (same type) | Consumer Tenant | Consumer Tenant |
+| **Allocation** | A carved sub-resource | Yes, the allocation | Yes (sub-type) | Consumer Tenant | Consumer Tenant |
+| **Shareable** | A stake/relationship | No — stake only | No new entity | Resource Owner Tenant | Resource Owner Tenant (shared cost attribution possible via policy) |
+
+---
+
+## 5. Placement Engine Interaction
+
+The placement engine handles all three ownership models, but the selection criteria differ:
+
+**Whole Allocation:** Standard placement. The placement engine selects a provider with available capacity. The provider provisions the resource and returns it owned by the requesting Tenant.
+
+**Allocation:** The placement engine selects an eligible pool resource owned by a platform Tenant with sufficient available capacity. The pool provider carves an allocation and returns it. DCM creates the new allocation entity in the requesting Tenant.
+
+```yaml
+# Placement engine for allocation requests:
+# Step 1: Find providers that offer Network.IPAddressPool
+# Step 2: Filter by sovereignty constraints
+# Step 3: Filter by available capacity (pool.available_count > 0)
+# Step 4: Apply tie-breaking hierarchy
+# Step 5: Dispatch allocation request to selected pool provider
+# Step 6: Provider returns the specific carved allocation
+# Step 7: DCM creates IPAddress entity in requesting Tenant
+# Step 8: AllocationRecord relationship created
+```
+
+**Shareable:** The placement engine finds the shareable resource instance that satisfies the consumer's attachment constraints. No new entity is provisioned — the provider registers the stake relationship. If no eligible shareable instance exists, the request fails with a clear error (unlike Allocation where failure means insufficient pool capacity).
+
+---
+
+## 6. Decommission Safety Model
+
+The three patterns have different decommission safety behaviors:
+
+**Whole Allocation decommission:**
+- Owner Tenant initiates decommission
+- Policy checks for required relationships (do other entities depend on this?)
+- If required dependencies exist → `DECOMMISSION_DEFERRED` until dependencies release
+- If no required dependencies → dispatch decommission to provider → DECOMMISSIONED
+
+**Allocation decommission:**
+- Consumer Tenant initiates decommission of their allocation entity
+- DCM dispatches decommission to pool provider
+- Pool provider releases the resource back to pool
+- AllocationRecord relationship terminated
+- Pool `available_count` increases
+- Allocation entity → DECOMMISSIONED
+- Pool entity is unaffected
+
+**Shareable decommission:**
+- Resource owner Tenant initiates decommission of the shared resource
+- DCM checks `active_stake_count` for `stake_strength: required` stakes
+- If required stakes > 0 → `DECOMMISSION_DEFERRED`
+  - Notifications to all required stakeholders
+  - Owner retries decommission after stakeholders release
+- If required stakes == 0 → dispatch decommission to provider → DECOMMISSIONED
+  - Any remaining `optional` stakes are automatically terminated
+
+---
+
+## 7. Cost Attribution Model
+
+**Whole Allocation and Allocation:** Cost is attributed entirely to the owning Tenant. Standard Cost Analysis billing. The entity's `billing_state` governs the rate (billable/non_billable/reduced_rate).
+
+**Shareable:** The shared resource's cost is attributed to its owner Tenant by default. Organizations that want to distribute shared resource costs to stakeholders configure a cost attribution policy:
+
+```yaml
+cost_attribution_policy:
+  resource_type: Network.VLAN
+  model: 
+  # owner_bears_all: NetworkOps Tenant pays for VLAN regardless of how many VMs attach
+  # equal_split:     cost divided equally among active stakeholders
+  # proportional_by_usage: cost allocated by traffic volume or similar metric
+  # chargeback:      each stakeholder is invoiced for their declared portion
+```
+
+---
+
+## 8. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `OWN-001` | `whole_allocation` resources are owned entirely by the requesting Tenant from the moment of realization. The providing platform Tenant has no ownership claim. |
+| `OWN-002` | `allocation` resources are owned entirely by the requesting Tenant. The pool owner retains ownership of the pool entity only. AllocationRecord relationships are the only cross-Tenant link. |
+| `OWN-003` | `shareable` resources are owned by a single Tenant. Consumers hold stakes (relationships) only. No consumer owns any portion of a shareable resource. |
+| `OWN-004` | Decommission of a shareable resource is deferred while any `required` strength stakes are active. Optional stakes are terminated automatically on shareable resource decommission. |
+| `OWN-005` | Allocation entity decommission releases the allocation back to the source pool. The pool entity is never decommissioned by an allocation decommission. |
+| `OWN-006` | Cost attribution for shareable resources defaults to the resource owner Tenant. A cost attribution policy may redistribute costs to stakeholders. |
+| `OWN-007` | The ownership model for a resource type is declared in the Resource Type Specification and cannot be changed at the entity level. Ownership model is a type-level invariant. |
+| `OWN-008` | Cross-tenant AllocationRecord and stake relationships require that the requesting Tenant has either an active cross-tenant authorization or the resource type is declared as publicly allocatable/stakeable in its Resource Type Spec. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/05-resource-type-hierarchy.md b/content/docs/architecture/data-model/05-resource-type-hierarchy.md
new file mode 100644
index 0000000..7ee870c
--- /dev/null
+++ b/content/docs/architecture/data-model/05-resource-type-hierarchy.md
@@ -0,0 +1,817 @@
+# DCM Data Model — Resource Type Hierarchy and Service Catalog
+
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Entity Types](01-entity-types.md) | [Four States](02-four-states.md) | [Layering and Versioning](03-layering-and-versioning.md) | [Examples](04-examples.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA**
+>
+> The Data abstraction — Resource Type Specifications and Provider Catalog Items
+
+
+
+---
+
+## 1. Purpose
+
+The DCM Resource Type Hierarchy is the structural model that defines how services and resources are categorized, specified, and exposed through the DCM Service Catalog. It is the mechanism by which DCM achieves **resource portability** — the ability to express what a consumer needs independently of which specific provider delivers it.
+
+The hierarchy serves four goals:
+
+1. **Portability** — consumer intent can be fulfilled by any provider that satisfies the resource type contract, without the consumer needing to know which provider that is
+2. **Standardization** — a common vocabulary and data contract for all resource types encourages interoperability across providers, implementors, and the broader DCM community
+3. **Extensibility** — the model can be extended at every level without breaking existing definitions
+4. **Transparency** — any deviation from full portability is explicitly declared, versioned, and surfaced to consumers
+
+---
+
+
+### 1a. Precise Vocabulary — Resource Type vs Catalog Item
+
+These terms are frequently conflated. The distinction is architectural:
+
+**Resource Type** — the classification category. Groups catalog items for portability and discovery. Vendor-neutral by requirement. Defines the field schema that any provider offering this type must support. Examples: `Compute.VirtualMachine`, `Network.IPAddress`, `Process.AnsiblePlaybook`.
+
+**Resource Type Specification** — the versioned, formal definition of a Resource Type: field schema, constraints, lifecycle rules, portability classification, and allowed relationship types. Stored in the Resource Type Registry. Providers implement against a specific version. Example: `Compute.VirtualMachine v2.1.0`.
+
+**Provider Catalog Item** — what a specific Service Provider is offering to consumers. The provider's declaration: "I can fulfill `Compute.VirtualMachine v2.1.0` with these specific options, at this cost, with these availability characteristics, in this region." A catalog item is always linked to a specific Resource Type Specification version. Catalog items can represent resource allocations (a VM, a subnet) or processes (an automation job, a playbook execution, a pipeline run) — anything a provider offers for consumption.
+
+**The key relationship:** Consumers request by Resource Type (or Resource Type Specification version). DCM resolves to a Provider Catalog Item through the specificity narrowing algorithm. The catalog item is what actually gets provisioned. The resource type is the portable, vendor-neutral expression of intent.
+
+**Anti-vocabulary update:** Never say "catalog item" when you mean "resource type specification." Never say "resource type" when you mean a specific provider offering — use "catalog item" or "provider catalog item."
+
+
+## 2. The DCM Resource Type Registry
+
+DCM maintains an official **Resource Type Registry** — the authoritative source of standard resource type definitions. The registry is the foundation of portability across the DCM ecosystem.
+
+
+
+### 2.1c Resource Type Authority — Stewardship Model
+
+Every Resource Type Specification in the DCM registry is owned by a **Resource Type
+Authority** — the team or individual responsible for defining, maintaining, evolving,
+and deprecating that specification. This is not an implicit role; it is a declared
+field in the registry entry.
+
+The Resource Type Authority has full responsibility for:
+- Defining the universal fields (the vendor-neutral contract all providers must implement)
+- Deciding which fields are `conditional` vs `universal`
+- Declaring `layer_reference` constraints where the allowed values should be
+  governed by a named layer type rather than a static list
+- Reviewing provider extension proposals that add fields to their resource type
+- Publishing new versions when the contract changes
+- Deprecating the specification and declaring a replacement when it's superseded
+
+**Authority assignment by registry tier:**
+
+| Tier | Who is the Resource Type Authority |
+|------|------------------------------------|
+| Tier 1 — DCM Core | DCM Project maintainers (community PRs + named maintainer approval) |
+| Tier 2 — Verified Community | Named community maintainer(s) declared at registry entry time |
+| Tier 3 — Organization | Designated platform team, domain team, or SME group per the organization's governance model |
+
+**Within Tier 3 (Organization), typical authority assignments:**
+
+| Resource Type Category | Typical Owning Authority |
+|-----------------------|--------------------------|
+| `Compute.*` | Platform / Virtualization Team |
+| `Network.*` | Network Operations |
+| `Storage.*` | Storage Operations |
+| `Security.*` | Security / CISO Office |
+| `Platform.*` | Platform Engineering |
+| `Process.*` | Automation / DevOps Platform Team |
+| `Application.*` | Application Platform Team |
+
+**The authority model is the same as all other DCM artifacts.** Resource Type
+Specifications are versioned, GitOps-managed, authority-owned, and subject to
+the standard `developing → proposed → active → deprecated → retired` lifecycle.
+The Resource Type Authority is the approver in the GitOps workflow — the PR must
+be approved by the authority before the specification activates.
+
+
+### 2.1a Catalog Item vs Resource Type Specification — Critical Distinction
+
+These two terms are frequently conflated throughout the documentation. They are distinct concepts at different levels of the hierarchy:
+
+**Resource Type Specification (Registry entry):**
+- Vendor-neutral definition of a resource type's fields, constraints, lifecycle rules, and portability classification
+- Lives in the Resource Type Registry (Tier 1, 2, or 3)
+- Examples: `Compute.VirtualMachine v2.1.0`, `Network.VLAN v1.0.0`
+- Defines what the resource TYPE is, not what any specific provider offers
+
+**Provider Catalog Item (Service Catalog entry):**
+- A specific provider's offering implementing a Resource Type Specification
+- Includes provider-specific pricing, availability, SLAs, and performance characteristics
+- What consumers actually request via the Service Catalog
+- Examples: "EU-WEST-Prod-1's 4-CPU VM offering", "NetworkOps's VLAN service"
+- Tied to a specific provider; multiple providers can offer catalog items for the same Resource Type Spec
+
+**When to use each term:**
+- "The consumer requests a catalog item" ✓ — they request a provider's specific offering
+- "The resource type specification defines the field schema" ✓ — the spec defines structure
+- "The catalog item schema" ✗ — should be "the resource type specification schema"
+- "The consumer browses resource types" ✓ — they browse the type hierarchy
+- "The consumer selects a catalog item" ✓ — they select a specific provider offering
+
+**In the anti-vocabulary:** "Catalog Item" should not be used when "Resource Type Specification" is meant, and vice versa. The hierarchy is: Resource Type Category → Resource Type → Resource Type Specification → Provider Catalog Item.
+
+
+### 2.1 Registry Principles
+
+- The registry is **open** — third parties, implementors, and the community can propose new resource type definitions
+- Registry entries are **versioned and immutable** once published — changes produce new versions
+- Registry definitions are **vendor-neutral by hard requirement** — no vendor-specific data is permitted in a DCM-specified resource type unless that vendor is the exclusive provider of that technology stack
+- The registry itself is subject to the same **deprecation model** as all other DCM definitions
+- All registry entries follow the **universal versioning scheme** (Major.Minor.Revision)
+
+### 2.2 Default Resource Type Categories
+
+DCM ships with a default set of Resource Type Categories. Implementors may define additional categories following the specification. The registry contains both **Resource Types** (for provisioned resources) and **Information Types** (for external data references) — distinguished by category prefix.
+
+**Resource Type Categories:**
+
+| Category | Description |
+|----------|-------------|
+| `Compute` | Processing resources — virtual machines, containers, bare metal |
+| `Network` | Networking resources — IP addresses, VLANs, firewall rules, load balancers |
+| `Storage` | Storage resources — block, object, file, databases |
+| `Platform` | Platform services — Kubernetes clusters, application platforms |
+| `Security` | Security resources — certificates, secrets, HSMs, identity |
+| `Observability` | Monitoring and logging resources |
+| `Data` | Data services — streams, queues, pipelines |
+
+**Information Type Categories:**
+
+| Category | Description |
+|----------|-------------|
+| `Business` | Business organizational data — BusinessUnit, CostCenter, ProductOwner |
+| `Identity` | Identity and access data — Person, ServiceAccount, Group |
+| `Compliance` | Regulatory and compliance data — RegulatoryScope, AuditFramework |
+| `Operations` | Operational reference data — Runbook, SLA, SupportContract |
+
+All categories follow the same versioning, deprecation, and registry governance model. The `implements_type` field on provider registrations distinguishes whether a provider is a Service Provider (`service`) or an Information Provider (`information`).
+
+### 2.3 Registry Entry Structure
+
+Every entry in the Resource Type Registry carries the following metadata:
+
+```yaml
+registry_entry:
+  uuid: 
+  name: 
+  fully_qualified_name: 
+  version: 
+  parent_uuid: 
+  status:
+    state: 
+    deprecation_date: 
+    sunset_date: 
+    replacement_uuid: 
+    replacement_version: 
+    deprecation_reason: 
+    migration_guidance: 
+  portability:
+    classification: 
+    portability_breaking: 
+    portability_notes: 
+  ownership:
+    owner: 
+    owner_uuid: 
+    origination_date: 
+  description: 
+  specification_ref: 
+```
+
+---
+
+## 3. Resource Type Hierarchy Levels
+
+The hierarchy has four levels, from most abstract to most concrete. Each level builds on the one above it.
+
+### Level 1 — Resource Type Category
+
+The broadest classification. Defines the domain of a resource without any specificity about what the resource is.
+
+- DCM ships with default categories (see Section 2.2)
+- Implementors may define additional categories
+- Categories have no data fields — they are organizational containers
+- Categories are versioned and can be deprecated
+
+**Example:** `Compute`, `Network`, `Storage`
+
+---
+
+
+### 2.1b Layer-Referenced Field Constraints
+
+Fields in a Resource Type Specification can declare their allowed values as **a set of
+active layer instances** rather than as a static list. This is the `layer_reference`
+constraint type.
+
+**Why this matters:**
+
+A static `enum: [rhel, ubuntu, windows-server]` is valid at definition time but
+immediately becomes a governance problem — updating it requires a new version of the
+Resource Type Specification, goes through the full registry approval process, and
+affects all providers simultaneously.
+
+A `layer_reference` constraint delegates allowed-value governance to the layer system.
+The set of valid values for a field is determined at catalog item render time by
+querying the active layer instances of the declared layer type. Adding a new approved
+OS image is adding a new OS Image layer — owned by the appropriate authority, versioned,
+subject to GitOps workflow, immediately available to all catalog items that reference
+that layer type, without touching the Resource Type Specification.
+
+**Examples of layer-referenced fields:**
+
+| Field | Layer Type | Who creates layers | What a layer contains |
+|-------|-----------|-------------------|----------------------|
+| `location` | `location.data_center` | Data Center Operations | DC name, code, certifications, sovereignty, power, network |
+| `os_image` | `os_image` | Platform Security / OS Team | image name, version, SHA, approved status, EOL date |
+| `size` (optional) | `vm_size` | Platform Team | size name, CPU, RAM, storage defaults — **only if the org wants to constrain sizes via governed list**; organizations may instead let providers declare ad-hoc size constraints in their catalog items |
+| `network_zone` | `network_zone` | Network Operations | zone name, VLAN range, allowed protocols, firewall rules |
+| `environment` | `environment` | Platform Governance | environment name, policy set, TTL defaults, approval tier |
+| `storage_class` | `storage_class` | Storage Operations | class name, IOPS, throughput, redundancy, cost per GB |
+
+**The pattern applies to any field where:**
+- The valid values are governed by a specific team or authority
+- The set of values changes over time (new options added, old ones retired)
+- Each value carries structured metadata beyond just its name
+- Governance, versioning, and audit of the allowed set matters
+
+**The pattern does NOT apply to fields where:**
+- The valid values are intrinsic to the resource type itself (CPU range, memory range)
+- The provider is the appropriate authority for what values are valid for their offering
+- The field is informational (names, descriptions, tags)
+
+**This is an organizational decision.** The Resource Type Specification declares whether
+a field uses `layer_reference` or a static constraint. Organizations can choose either
+approach. A VM `size` field could use `layer_reference` if the organization wants to
+maintain a governed size catalog — or it could use a static `range` constraint and let
+each provider declare their available sizes in their Catalog Item. Both are valid.
+Neither requires the other. The Resource Type Authority decides per field based on
+whether organizational governance of the allowed values adds value.
+
+**What the consumer sees:**
+
+When `GET /api/v1/catalog/{uuid}` renders a field with a `layer_reference` constraint,
+DCM resolves the active layer instances of that type and returns them as the
+`allowed_values` list — each entry containing both the value to submit and the
+display data the GUI needs to render the selection:
+
+```json
+{
+  "field_name": "location",
+  "type": "string",
+  "required": true,
+  "constraint": {
+    "type": "layer_reference",
+    "layer_type": "location.data_center",
+    "allowed_values": [
+      {
+        "value": "layer-uuid-fra-dc1",
+        "display_name": "DC1 — Frankfurt Alpha",
+        "code": "FRA-DC1",
+        "zone": "eu-west-1a",
+        "sovereignty": "EU/GDPR",
+        "certifications": ["ISO 27001", "SOC 2 Type II"],
+        "capacity_status": "available"
+      },
+      {
+        "value": "layer-uuid-ams-dc2",
+        "display_name": "DC2 — Amsterdam Beta",
+        "code": "AMS-DC2",
+        "zone": "eu-west-1b",
+        "sovereignty": "EU/GDPR",
+        "certifications": ["ISO 27001"],
+        "capacity_status": "limited"
+      }
+    ]
+  }
+}
+```
+
+The consumer submits the layer UUID as the field value. DCM resolves it to the full
+layer, assembles the layer chain into the payload, and the provider receives the
+complete structured location context — not just a string.
+
+**Governance model:** Layer instances for any layer type follow the same lifecycle,
+controls, security, and governance as Resource Types themselves — they are versioned
+artifacts, owned by a declared authority, stored in GitOps, subject to the standard
+`developing → proposed → active → deprecated → retired` lifecycle. The authority
+that creates and governs the layer instances is the same authority that governs
+what values are valid for that field.
+
+
+### Level 2 — Resource Type
+
+Defines an abstract resource within a category. A Resource Type represents a class of resource that multiple providers can implement. Resource Types are the primary unit of portability in DCM.
+
+- DCM maintains default Resource Types in the registry
+- Community and implementors can define and register new Resource Types
+- Resource Types must be **vendor-neutral** — no provider-specific data
+- Resource Types declare their **base field specification** (universal fields only)
+- Resource Types are versioned and can be deprecated
+
+**Example:** `Compute.VirtualMachine`, `Network.IPAddress`, `Network.FirewallRule`
+
+---
+
+### Level 3 — Resource Type Specification
+
+The data contract for a Resource Type. A Resource Type Specification is itself
+a **data layer artifact** — it follows the same versioning, ownership, lifecycle,
+GitOps governance, and domain model as all other DCM layers. The Resource Type
+Authority is the `owned_by` declaration on the specification artifact. Changes
+produce new versions. The specification is immutable once active.
+
+The data contract for a Resource Type. Defines all fields — universal, conditional, and any declared extension points — along with their types, constraints, and portability classifications.
+
+- Every field in a specification carries a **portability classification** (see Section 4)
+- Specifications define which fields are required vs. optional
+- Specifications define validation constraints for each field
+- Specifications declare **extension points** where providers may add fields
+- Specifications are versioned independently of their Resource Type
+- Specifications can be deprecated
+
+**Example:** `Compute.VirtualMachine` specification defines: `cpu_count` (universal, required), `ram_gb` (universal, required), `storage_gb` (universal, required), `os_image` (universal, required), `high_availability` (conditional, optional)
+
+---
+
+### Level 4 — Provider Catalog Item
+
+A specific provider's concrete implementation of a Resource Type Specification. This is where provider-specific detail lives and where the abstract becomes actionable.
+
+- Provider Catalog Items are registered against a specific Resource Type Specification version
+- They must implement **all universal fields** of the parent specification
+- They may implement **conditional fields** (declared in their registration)
+- They may add **provider-specific extension fields** (must be marked portability-breaking)
+- They are versioned and can be deprecated
+- They declare their **sovereignty capabilities** (see Section 6)
+- They declare their **supported lifecycle operations** (see Section 7)
+
+**Example:** `Nutanix.VM.Small` implements `Compute.VirtualMachine` with `cpu_count: 4`, `ram_gb: 16`, `storage_gb: 60`
+
+---
+
+
+## 3a. Field Constraint Model — Three Choices
+
+When a Resource Type Authority defines a field, they make a deliberate decision
+about how that field's valid values are governed. There are three options:
+
+### Option 1 — Layer-Referenced Constraint
+
+```yaml
+field_name: location
+constraint:
+  type: layer_reference
+  layer_type: location.data_center
+```
+
+**Use when:** The valid values are a governed, versioned, authority-owned set that
+changes over time and carries structured metadata. Location, OS images, network zones,
+storage classes, and approved size profiles are all layer-referenced by default in DCM.
+
+**Portability:** Fields with `layer_reference` constraints are **fully portable** — any
+provider implementing this resource type resolves the same layer type to its own
+available instances. The consumer submits a layer UUID; each provider resolves it
+against their own registered location or OS image layers.
+
+**Governance:** Adding a new valid value = adding a new Reference Data Layer instance.
+No Resource Type Specification change needed. The authority that owns the layer type
+governs the set of valid values independently of the Resource Type Authority.
+
+---
+
+### Option 2 — Provider-Declared Constraint (ad-hoc)
+
+```yaml
+field_name: cpu_count
+constraint:
+  type: enum
+  allowed_values: [1, 2, 4, 8, 16, 32]
+  reason: "Powers of 2 required for NUMA alignment"
+```
+
+**Use when:** The valid values are inherent to the resource type itself and do not
+change based on what an organization has provisioned or approved. CPU counts, memory
+ranges, protocol versions, and other intrinsic technical constraints belong here.
+
+**When providers use it at catalog item level:** A provider can declare ad-hoc
+constraints in their Catalog Item declaration — restricting or narrowing the values
+declared in the Resource Type Specification for *their specific offering*. A provider
+may not offer all CPU counts the spec allows; their catalog item declares the subset
+they support. This is valid and does not affect portability as long as they stay within
+the bounds declared by the Resource Type Specification.
+
+**Portability:** Depends on how the constraint is used:
+- In the Resource Type Specification: portable if values are vendor-neutral
+- In the Catalog Item: provider narrows the spec's values — still portable if
+  another provider's catalog item supports the same field with overlapping values
+
+---
+
+### Option 3 — No Constraint (Provider Judgment)
+
+```yaml
+field_name: display_name
+constraint:
+  type: pattern
+  pattern: '^[a-z0-9-]{3,63}$'
+  # Or simply: no constraint block — free-form
+```
+
+**Use when:** The field is informational, free-form, or the provider is the
+appropriate authority for what constitutes a valid value for their offering.
+Names, descriptions, tags, and provider-internal identifiers belong here.
+
+**Portability:** No constraint means any value is valid — fully portable but
+with no guarantee of behavioral equivalence across providers.
+
+---
+
+### Decision Guide for Resource Type Authorities
+
+| Field characteristic | Recommended constraint type |
+|---------------------|----------------------------|
+| Value is from a governed, versioned, org-managed list | `layer_reference` |
+| Value determines which physical infrastructure is used | `layer_reference` |
+| Value is intrinsic to the resource type (CPU count, protocol) | `enum` or `range` |
+| Provider narrows a spec-defined range in their catalog item | `enum` or `range` at catalog item level |
+| Value is informational / naming / description | `pattern` or no constraint |
+| Value is entirely provider-internal | No constraint in spec; provider declares in catalog item |
+
+**An organization decides per field** — some fields in a resource type will be
+layer-referenced (location, OS image), some will have ad-hoc constraints (CPU range),
+and some will be free-form (display name). The Resource Type Authority makes these
+decisions when publishing the specification. Organizations can always add more
+governance later by adding a `layer_reference` constraint to a field that previously
+used an ad-hoc enum — this is a non-breaking minor version change.
+
+---
+
+
+## 4. Portability Classification
+
+Every field in every Resource Type Specification carries a portability classification. This classification is part of the field's metadata and is immutable once published for a given version.
+
+### 4.1 Classification Levels
+
+| Classification | Description | Portability Impact |
+|---|---|---|
+| `universal` | Part of the DCM standard spec. All providers implementing this type must support it. | Fully portable across all implementing providers |
+| `conditional` | Supported by multiple providers but not all. Providers declare support in their registration. | Portable across providers that declare support |
+| `provider-specific` | Specific to one provider or technology stack. Using this field locks the request to that provider. | Portability-breaking — must be explicitly marked |
+| `exclusive` | Only one provider supports this technology stack. Portability is not applicable by definition. | Not applicable — acknowledged and declared |
+
+### 4.2 Hard Portability Requirements
+
+The following are non-negotiable requirements for any DCM-specified Resource Type:
+
+1. All **universal** fields MUST be supported by ALL providers implementing that Resource Type
+2. **Provider-specific** fields MUST be explicitly marked as portability-breaking in the field metadata
+3. Consumers MUST be warned when their request contains portability-breaking fields
+4. The only exception to vendor-neutrality is the **exclusive** classification — where one provider is the sole implementor of a technology stack, explicitly acknowledged and declared in the registry
+5. Any Resource Type in the DCM registry that contains provider-specific fields as universal fields is invalid and must be rejected
+
+### 4.3 Portability Field Metadata
+
+Every field in a Resource Type Specification carries the following portability metadata:
+
+```yaml
+field_name:
+  type: 
+  required: 
+  description: 
+  portability:
+    classification: 
+    portability_breaking: 
+    portability_notes: 
+    supported_by: 
+  constraints:
+    - type: 
+      # range: min/max numeric bounds
+      # enum: static list of allowed string values
+      # pattern: regex pattern for string validation
+      # layer_reference: value must be the UUID of an active layer of the declared type
+      #   — used for single-select fields (e.g., pick one location)
+      # layer_reference_list: value is a list of layer UUIDs of the declared type
+      #   — used for multi-select fields (e.g., list of allowed zones)
+
+      # For layer_reference and layer_reference_list:
+      layer_type: 
+      # e.g., layer_type: location.data_center
+      #        layer_type: os_image
+      #        layer_type: approved_size
+      filter:
+        # Optional: restrict which layer instances are valid values
+        # Applied at catalog item render time to produce the available list
+        tags: [, ...]            # only layers with these tags
+        domain:  # only layers from this domain
+        concern_tags: [, ...]    # only layers with these concern tags
+      display_field:      # which layer field to show as display label
+      # e.g., display_field: data.dc_name → shows "DC1 — Frankfurt Alpha"
+      value_field:        # which layer field is the submitted value
+      # e.g., value_field: artifact_metadata.uuid → submits the layer UUID
+      #        value_field: data.dc_code           → submits "FRA-DC1"
+  default_value: 
+  provenance:
+    
+```
+
+---
+
+## 5. Inheritance Model
+
+Resource Types support inheritance, enabling specialization without duplication. A child type inherits all fields from its parent and may add new fields.
+
+### 5.1 Inheritance Rules
+
+1. A child type inherits **all fields** from its parent type — no field can be removed or redefined
+2. A child type may **add new fields** beyond its parent's specification
+3. A child type's portability classification can only be **equal to or more restrictive** than its parent — a child of a `universal` type may be `conditional`, but not vice versa
+4. Each level of the hierarchy is **independently versioned**
+5. Each level maintains a **reference to its parent UUID and version**
+6. Deprecating a parent type **does not automatically deprecate child types** — each must be independently deprecated with appropriate migration guidance
+
+### 5.2 Inheritance Example
+
+```
+Compute                                        # Category
+  └── VirtualMachine                           # Base Resource Type
+        ├── VirtualMachine.GPU                 # Inherits VirtualMachine
+        │     ├── gpu_count (conditional)
+        │     ├── gpu_memory_gb (conditional)
+        │     └── VirtualMachine.GPU.HighMemory  # Inherits VirtualMachine.GPU
+        │           └── extended_memory_gb (conditional)
+        └── VirtualMachine.HighAvailability    # Inherits VirtualMachine
+              ├── ha_mode (conditional)
+              └── failover_policy (conditional)
+```
+
+### 5.3 Inheritance Metadata
+
+Every Resource Type that inherits from a parent carries the following inheritance metadata:
+
+```yaml
+inheritance:
+  parent_uuid: 
+  parent_version: 
+  parent_fully_qualified_name: 
+  inherited_fields: 
+  added_fields: 
+```
+
+---
+
+## 6. Provider Registration and Catalog Item Declaration
+
+For a provider to participate in the DCM ecosystem and have its catalog items available for request resolution, it must register against the Resource Type Hierarchy.
+
+### 6.1 Provider Registration Declaration
+
+A provider's registration is a machine-readable declaration that DCM consumes to understand what the provider offers and how to route requests to it:
+
+```yaml
+provider_registration:
+  uuid: 
+  name: 
+  version: 
+  status:
+    state: 
+    deprecation_date: 
+    sunset_date: 
+    replacement_uuid: 
+    deprecation_reason: 
+    migration_guidance: 
+  catalog_items:
+    - 
+  sovereignty_capabilities:
+    
+  supported_lifecycle_operations:
+    
+  trust_declaration:
+    
+```
+
+### 6.2 Catalog Item Declaration
+
+Each catalog item a provider offers is declared against a specific Resource Type Specification version:
+
+```yaml
+catalog_item:
+  uuid: 
+  name: 
+  version: 
+  implements:
+    resource_type_uuid: 
+    resource_type_version: 
+    resource_type_fully_qualified_name: 
+  status:
+    state: 
+    deprecation_date: 
+    sunset_date: 
+    replacement_uuid: 
+    deprecation_reason: 
+    migration_guidance: 
+  universal_fields:
+    
+  conditional_fields_supported:
+    
+  provider_specific_extensions:
+    # Provider adds fields beyond the Resource Type Specification.
+    # Each field MUST be marked portability_breaking: true.
+    # The catalog item MUST set portability_warning: true.
+    # The Resource Type Authority MAY review and accept these as
+    # 'conditional' fields in a future version of the specification
+    # if multiple providers adopt the same extension.
+    #
+    # Example:
+    nutanix_acropolis_affinity_group:
+      type: string
+      portability_breaking: true
+      description: "Nutanix-specific affinity group assignment"
+
+  # Provider extension layers — alternative to inline extensions.
+  # Providers may contribute a Service Layer (domain: provider) that adds
+  # fields injected during payload assembly for their offering only.
+  # These layers are registered with DCM alongside the catalog item.
+  # They carry the same portability_breaking: true semantics.
+  provider_extension_layer_handles:
+    - "providers/nutanix-eu-west/layers/acropolis-extensions-v1"
+    # Layer domain: provider — cannot override platform or tenant layers
+    # Applied only when this catalog item is selected for dispatch
+
+  portability_warning: 
+  portability_class: 
+```
+
+---
+
+## 7. Request Resolution — Specificity Narrowing
+
+Provider selection in DCM is never explicit. The consumer declares intent using Resource Types and field values. The appropriate provider catalog item is selected by the DCM Policy Engine through progressive specificity narrowing.
+
+### 7.1 Resolution Steps
+
+```
+Step 1: Resource Type declared
+        → matches all providers implementing that Resource Type
+
+Step 2: Universal fields specified
+        → still matches all providers (all must support universal fields)
+
+Step 3: Conditional fields specified
+        → narrows to providers that declare support for those fields
+
+Step 4: Provider-specific fields used
+        → narrows to single provider
+        → portability warning issued and recorded in request provenance
+        → enforcement mode applied (block|warn|allow) per organizational policy
+
+Step 5: Placement and sovereignty constraints applied
+        → Policy Engine applies placement policies
+        → Provider sovereignty capabilities matched against request requirements
+        → Final provider catalog item selected
+
+Step 6: Provider catalog item UUID recorded in request payload provenance
+```
+
+### 7.2 Portability Warning Enforcement
+
+When a request contains portability-breaking fields, the Policy Engine applies the configured enforcement mode. This is organizational policy — configurable at the organization, domain, or service level:
+
+| Enforcement Mode | Behavior |
+|---|---|
+| `block` | Request is rejected. Consumer must remove portability-breaking fields or explicitly acknowledge the lock-in. |
+| `warn` | Request proceeds. Portability warning is recorded in request provenance and surfaced to the consumer. |
+| `allow` | Request proceeds silently. Portability-breaking fields are still recorded in provenance but no warning is surfaced. |
+
+The enforcement mode is itself a versioned, auditable policy — subject to the same provenance tracking as all other data in DCM.
+
+---
+
+## 8. Deprecation Model
+
+Every definition at every level of the Resource Type Hierarchy can be deprecated. Deprecation is a first-class concept in DCM — not an afterthought.
+
+### 8.1 Deprecation Lifecycle
+
+```
+active → deprecated → retired
+```
+
+| State | Meaning | System Behavior |
+|---|---|---|
+| `active` | Definition is current and fully supported | Normal operation |
+| `deprecated` | Definition is being phased out. Replacement is available. | Deprecation warning surfaced to consumers. Requests still processed. Warning recorded in provenance. |
+| `retired` | Definition is no longer honored. | Requests using retired definitions are rejected by the Policy Engine. |
+
+### 8.2 Deprecation Cascade Rules
+
+- Deprecating a **Resource Type** does not automatically deprecate its child types or provider catalog items — each must be independently deprecated
+- Deprecating a **Provider Catalog Item** does not affect other catalog items implementing the same Resource Type
+- Retiring a **Resource Type Specification version** causes all catalog items registered against that version to require re-registration against a current version
+- **Sunset dates** must provide sufficient migration runway — minimum notice periods may be defined by organizational policy
+
+### 8.3 Migration Guidance Requirement
+
+Any definition marked `deprecated` MUST include:
+- A reference to the replacement definition (UUID and version)
+- A human-readable deprecation reason
+- Human-readable migration guidance explaining how to transition
+- A sunset date giving consumers time to migrate
+
+---
+
+## 9. Versioning
+
+All definitions in the Resource Type Hierarchy follow the universal DCM versioning scheme.
+
+### 9.1 Version Scheme
+
+`Major.Minor.Revision`
+
+| Component | Trigger |
+|---|---|
+| **Major** | Breaking changes to the contract — removing fields, changing field types, changing required/optional status of universal fields |
+| **Minor** | Additive changes — adding new optional fields, adding new conditional fields, adding new extension points |
+| **Revision** | Data or configuration changes with no contract impact — updating descriptions, updating constraints that don't break existing data, updating metadata |
+
+### 9.2 Version Constraints in Requests
+
+Consumers and dependencies may declare version constraints in their requests:
+
+```yaml
+resource_type:
+  uuid: 
+  version_constraint: 
+  version: 
+```
+
+### 9.3 Version Immutability
+
+Once a version is published it is immutable. Any change — even a documentation correction — produces a new version. This applies to all definitions at all levels of the hierarchy.
+
+---
+
+## 10. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | What is the governance model for proposing and approving new Resource Types to the DCM registry? | Community adoption, quality control | ✅ Resolved — three-tier registry (DCM Core / Verified Community / Organization); PR-based proposals with automated validation gates; shadow validation period before active promotion; see doc 20 (REG-001, REG-002) |
+| 2 | Should the registry support a formal review/approval workflow before a Resource Type becomes `active`? | Registry integrity, community trust | ✅ Resolved — PR-based workflow with automated gates (schema, FQN conflict, dependency resolution) and mandatory shadow validation before active; review periods by change type; see doc 20 (REG-002) |
+| 3 | What is the minimum sunset period for deprecated definitions? | Migration planning, operational stability | ✅ Resolved — default sunset policies REG-DP-002: Tier 1=P12M, Tier 2=P6M; overridable via standard policy priority; locked as immutable in fsi/sovereign profiles; see doc 20 |
+| 4 | Should version constraints in requests be strictly enforced or advisory? | Operational flexibility vs. predictability | ✅ Resolved — strictly enforced; version_policy options: exact/compatible/latest_minor/latest; DCM never auto-upgrades across major versions; profile-governed defaults (fsi/sovereign=exact); see doc 20 (REG-004) |
+| 5 | How are conflicts resolved when multiple providers satisfy all narrowing criteria equally? | Request resolution determinism | ✅ Resolved — six-step tie-breaking: policy preference → provider priority → tenant affinity → cost analysis (if available) → least loaded → consistent hash on request_uuid; see doc 20 (REG-005) |
+| 6 | Should the registry be distributed or centralized? How does this interact with sovereignty requirements? | Registry availability, sovereignty | ✅ Resolved — federated model: DCM Project registry → Organization mirror → Sovereign DCM (offline/signed bundles); air-gap via signed bundle import; see doc 20 (REG-006) |
+
+---
+
+## 11. Related Concepts
+
+- **Portability** — the ability to fulfill a resource intent using any provider that satisfies the resource type contract
+- **Naturalization** — provider's responsibility to transform DCM unified data into provider-specific format
+- **Denaturalization** — provider's responsibility to transform provider-specific results back into DCM unified format
+- **Sovereign Execution Posture** — sovereignty capabilities declared in provider registration inform placement decisions
+- **Policy Engine** — applies portability enforcement, placement policies, and request resolution logic
+- **Field-Level Provenance** — every field modification during request resolution is recorded with source UUID and operation type
+- **Universal Versioning** — Major.Minor.Revision applies to all definitions at all levels of the hierarchy
+- **Deprecation** — universal model for phasing out definitions at any level with migration guidance
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+---
+
+## Resource Type Reference — Consumer vs Internal Format
+
+When consumers reference a resource type — in API calls, policy conditions, or query filters — DCM accepts two forms:
+
+| Form | Example | Notes |
+|------|---------|-------|
+| **FQN string** (recommended) | `Compute.VirtualMachine` | Stable across deployments; human-readable; returned by the service catalog |
+| **Registry UUID** | `a1b2c3d4-e5f6-...` | Deployment-specific; obtained from catalog API; suitable for programmatic use |
+
+DCM resolves either form to the canonical `(resource_type_uuid, resource_type_name)` pair during request assembly. The resolution happens in the **Request Payload Processor** before layer enrichment begins. Unresolvable references are rejected at validation time with a `422 Unprocessable Entity` response and code `RESOURCE_TYPE_NOT_FOUND`.
+
+**Internal representation:** All internal DCM data — entity records, dispatch payloads, audit records — always carry **both** `resource_type_uuid` and `resource_type_name` (FQN). The consumer-facing accept-both model is purely at the API boundary; internally DCM always uses the canonical pair.
+
+**Dispatch to operators:** The `CreateRequest` and `UpdateRequest` payloads sent to Service Providers always include both:
+- `resource_type_uuid` — the Registry UUID
+- `resource_type_name` — the FQN string
+
+Operators MUST NOT accept only one form; both will always be present.
+
diff --git a/content/docs/architecture/data-model/06-resource-service-entities.md b/content/docs/architecture/data-model/06-resource-service-entities.md
new file mode 100644
index 0000000..b087a20
--- /dev/null
+++ b/content/docs/architecture/data-model/06-resource-service-entities.md
@@ -0,0 +1,888 @@
+# DCM Data Model — Resource/Service Entities
+
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Operational Models](24-operational-models.md) | [Entity Types](01-entity-types.md) | [Ownership, Sharing, and Allocation](04b-ownership-sharing-allocation.md) | [Layering and Versioning](03-layering-and-versioning.md) | [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Service Dependencies](07-service-dependencies.md) | [Resource Grouping](08-resource-grouping.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + PROVIDER**
+>
+> Data: entity lifecycle. Provider: lifecycle events and update notifications
+
+
+
+---
+
+## 1. Purpose
+
+This document defines the two fundamental transactional concepts in DCM — the **Resource/Service Request** and the **Resource/Service Entity** — and establishes the ownership models, lifecycle principles, and provider relationship rules that govern them.
+
+Understanding the distinction between a Request and an Entity, and understanding DCM's role as the authoritative owner of all resource data regardless of operational ownership, is essential to understanding how DCM achieves its core goals of auditability, lifecycle management, and sovereignty.
+
+---
+
+## 2. Core Terminology
+
+### 2.1 Resource/Service Request
+
+A **Resource/Service Request** is what a consumer submits to DCM — the declared intent to consume a resource or service. It is the consumer side of the transaction.
+
+- Created when a consumer submits a request via the Web UI or Consumer API
+- Captured as the **Intent State** before any processing
+- Processed into the **Requested State** after assembly and policy validation
+- Is the initiating event that causes a Resource/Service Entity to be created
+
+A Request is not a thing — it is an **instruction**. It describes what the consumer wants. The provider acts on the Requested State to produce an Entity.
+
+### 2.2 Resource/Service Entity
+
+A **Resource/Service Entity** is the "thing" produced by a provider as a result of fulfilling a Resource/Service Request. It is the provider side of the transaction — the allocation made real.
+
+- Created when a provider fulfills a Requested State payload
+- Returned to DCM in unified data model format via Denaturalization
+- Captured as the **Realized State** in the Realized Store
+- Assigned to a **DCM Tenant** — the ownership boundary
+- Has a UUID, full provenance chain, and complete lifecycle from creation to decommission
+- Is the unit of consumption, cost attribution, drift detection, and audit in DCM
+
+A Resource/Service Entity IS a thing — it exists, it has state, it has an owner, and DCM manages its lifecycle.
+
+### 2.3 The Critical Distinction
+
+```
+Consumer submits        →  Resource/Service REQUEST  →  Intent/Requested State
+Provider fulfills       →  Resource/Service ENTITY   →  Realized State
+DCM manages lifecycle   →  ENTITY persists            →  Drift/Audit/Cost/Rehydration
+```
+
+---
+
+## 3. DCM as Authoritative Owner of All Resource Data
+
+This is the most fundamental principle governing Resource/Service Entities:
+
+**DCM is ALWAYS the system of record for Resource/Service Entity data. DCM is ALWAYS authoritative for the resource definition. DCM ALWAYS owns the lifecycle. This applies regardless of the operational ownership model.**
+
+The operational ownership model (described in Section 4) determines who has authority to operate on a Resource/Service Entity. It does not affect DCM's data ownership. Specifically:
+
+- DCM owns the **data definition** of every Resource/Service Entity — what it is, what it should be, what it was
+- DCM owns the **lifecycle** — from Requested through Realized to Decommissioned
+- DCM is **authoritative** — if a provider reports a change DCM was not aware of, DCM acts on it according to policy
+- DCM acts as the **Tenant advocate** — it protects the Tenant's interests in all provider interactions
+- Providers are **custodians** of the underlying infrastructure — they are not the system of record
+
+**When a provider reports an unsanctioned change:**
+
+If a provider reports a state change that was not initiated by a DCM request, the Policy Engine evaluates the change and determines the appropriate response:
+
+| Response | Description |
+|----------|-------------|
+| `ALERT` | Notify appropriate personas — Tenant owner, SRE, Auditor |
+| `REVERT` | Instruct provider to revert to DCM-declared realized state |
+| `UPDATE_DEFINITION` | Accept the change and update the realized state definition |
+| `INVESTIGATE` | Flag for human review before action |
+| `DECOMMISSION` | Initiate decommission if the change represents unrecoverable deviation |
+| `ESCALATE` | Escalate to higher policy tier for decision |
+
+The response is determined by Policy Engine evaluation against:
+- The Resource/Service definition
+- Service/Resource dependencies
+- Consumer preferences
+- Organizational and Tenant policies
+- Sovereignty requirements
+
+---
+
+## 4. Ownership Models
+
+DCM supports four ownership models for Resource/Service Entities. Every Provider Catalog Item must declare which ownership model(s) it supports. The ownership model is recorded in the Resource/Service Entity's provenance at creation time.
+
+### 4.1 Allocation Model
+
+The provider retains internal ownership of the underlying infrastructure. The consumer owns the Resource/Service Entity (the allocation) in their DCM Tenant. The provider can reclaim the underlying resource when the entity is decommissioned.
+
+**Characteristics:**
+- Provider retains asset ownership
+- Consumer owns the allocation — the Entity in their DCM Tenant
+- Provider has reclaim rights on decommission
+- Underlying infrastructure may be shared or subdivided
+- DCM manages the Entity lifecycle; provider manages the underlying resource
+
+**Examples:** Virtual Machine, Container, Network Port, IP Address, Firewall Rule, Database Instance
+
+---
+
+### 4.2 Whole Allocation Model
+
+The entire physical or logical resource is allocated as a single indivisible unit to one consumer's DCM Tenant. The provider retains internal ownership but the consumer has exclusive use of the whole resource. The resource cannot be subdivided or shared during the allocation period.
+
+**Characteristics:**
+- Provider retains asset ownership
+- Consumer has exclusive, indivisible use
+- The resource is not shared or subdivided
+- Provider has reclaim rights on decommission
+- DCM manages the Entity lifecycle
+
+**Examples:** Dedicated Bare Metal server (provider-owned), Dedicated Network appliance, Whole storage array allocation
+
+---
+
+### 4.3 Full Transfer Model
+
+The provider transfers complete ownership of the underlying resource to the consumer's DCM Tenant. The Resource/Service Entity IS the resource — there is no separation between the allocation and the underlying infrastructure from DCM's perspective. The consumer controls the full lifecycle including decommissioning. The provider has no reclaim rights after transfer.
+
+**Characteristics:**
+- Ownership of the underlying resource transfers to consumer's DCM Tenant
+- The Entity IS the resource — no allocation/infrastructure separation
+- Consumer controls full lifecycle including decommission
+- Provider has no reclaim rights post-transfer
+- Transfer is recorded in provenance — permanent audit record
+- DCM remains authoritative for data and lifecycle regardless of transfer
+
+**Examples:** Transferred Bare Metal server, Licensed software asset, Dedicated hardware appliance transferred to consumer
+
+---
+
+### 4.4 Hybrid Transfer Model
+
+Ownership can transfer multiple times across the lifecycle of the Resource/Service Entity. The current owner is always exactly one DCM Tenant, but ownership can be formally reassigned through a DCM-governed ownership transfer process. Every transfer is tracked, auditable, and policy-governed.
+
+**Characteristics:**
+- Ownership is held by exactly one DCM Tenant at any point in time
+- Ownership can be transferred to another DCM Tenant through a formal DCM process
+- Every transfer is recorded in the Entity's provenance chain — complete ownership history
+- Transfer requires Policy Engine validation and authorization
+- The receiving Tenant must accept the transfer — it cannot be forced
+- DCM remains authoritative for data and lifecycle through all transfers
+
+**Transfer Provenance Record:**
+```yaml
+ownership_transfer:
+  sequence: 
+  from_tenant_uuid: 
+  to_tenant_uuid: 
+  transfer_timestamp: 
+  authorized_by: 
+  transfer_reason: 
+  policy_uuid: 
+```
+
+**Examples:** Bare Metal server reallocated between tenants, Hardware asset transferred between business units, Licensed resource reassigned
+
+---
+
+### 4.5 Ownership Model Declaration
+
+Every Provider Catalog Item must declare the ownership model(s) it supports:
+
+```yaml
+catalog_item:
+  uuid: 
+  ownership_models_supported:
+    - allocation
+    - whole_allocation
+    - full_transfer
+    - hybrid_transfer
+  default_ownership_model: 
+  transfer_policy_required: 
+  # If true, a policy must be referenced in any transfer request
+```
+
+---
+
+## 5. Resource/Service Entity Lifecycle
+
+Every Resource/Service Entity progresses through a defined lifecycle. The lifecycle states are:
+
+```
+REQUESTED → PENDING → PROVISIONING → REALIZED → OPERATIONAL
+                                                      │
+                                          ┌───────────┼───────────┐
+                                          ▼           ▼           ▼
+                                      DEGRADED   MAINTENANCE  SUSPENDED
+                                          │           │           │
+                                          └───────────┼───────────┘
+                                                      ▼
+                                                DECOMMISSIONING
+                                                      │
+                                                      ▼
+                                                DECOMMISSIONED
+```
+
+| State | Description |
+|-------|-------------|
+| `REQUESTED` | Request submitted, Intent State captured |
+| `PENDING` | Requested State assembled, awaiting provider dispatch |
+| `PROVISIONING` | Provider is fulfilling the request |
+| `REALIZED` | Provider has fulfilled the request, Entity exists, Realized State captured |
+| `OPERATIONAL` | Entity is in active use |
+| `DEGRADED` | Entity is functioning but below expected operational characteristics |
+| `MAINTENANCE` | Entity is undergoing planned maintenance |
+| `SUSPENDED` | Entity is temporarily suspended — not operational but not decommissioned |
+| `DECOMMISSIONING` | Decommission process initiated |
+| `DECOMMISSIONED` | Entity no longer exists. Record retained permanently for audit. |
+
+**Terminal states:** `DECOMMISSIONED` is the only terminal state. Once decommissioned, the Entity record is immutable and retained permanently.
+
+---
+
+## 6. Process Resource Entities
+
+A **Process Resource Entity** is a distinct class of Resource/Service Entity representing ephemeral execution resources — automation jobs, playbooks, pipelines, workflows, and similar process-oriented resources.
+
+### 6.1 Characteristics
+
+- **Ephemeral lifecycle** — exists for the duration of execution, then terminates
+- **No ongoing realized state to manage** — lifecycle ends at COMPLETED or FAILED
+- **Execution record retained permanently** — the record of what the process did is immutable and permanent
+- **Must belong to a DCM Tenant** — even ephemeral resources must be owned
+- **Must be in the provenance chain** of any Resource/Service Entity they affect
+
+### 6.2 Process Resource Lifecycle
+
+```
+REQUESTED → INITIATED → EXECUTING → COMPLETED
+                                  → FAILED
+                                  → CANCELLED
+```
+
+| State | Description |
+|-------|-------------|
+| `REQUESTED` | Process request submitted |
+| `INITIATED` | Provider has begun execution |
+| `EXECUTING` | Process is actively running |
+| `COMPLETED` | Process completed successfully — terminal |
+| `FAILED` | Process failed — terminal |
+| `CANCELLED` | Process cancelled before completion — terminal |
+
+All terminal states are permanent. The execution record is immutable after reaching a terminal state.
+
+### 6.3 Process Resource Entity Data Model
+
+```yaml
+process_resource_entity:
+  uuid: 
+  entity_class: process
+  process_type: 
+  tenant_uuid: 
+  version: 
+  lifecycle_state: 
+  input_payload:
+    
+  output_payload:
+    
+  affected_entities:
+    - entity_uuid: 
+      effect_type: 
+      effect_description: 
+  execution_record:
+    initiated_timestamp: 
+    completed_timestamp: 
+    executing_provider_uuid: 
+    authorized_by_policy_uuid: 
+  provenance:
+    
+```
+
+### 6.4 Provenance Obligation for Process Resources
+
+If a Process Resource modifies the state of a Resource/Service Entity, that Entity's realized state provenance MUST reference the Process Resource Entity UUID as the source of the modification. This ensures that every change to an Infrastructure Entity can be traced back to the Process that caused it.
+
+---
+
+## 7. Provider Internal Lifecycle Model
+
+Providers have their own internal infrastructure that underpins the Resource/Service Entities they create. While that internal infrastructure is opaque to consumers, DCM needs visibility into it for placement, cost analysis, and operational governance.
+
+### 7.1 Provider Capacity Model
+
+DCM supports three capacity information modes. Mode 3 is mandatory for all providers. Modes 1 and 2 are configurable per provider registration.
+
+**Mode 1 — Dynamic Query (on-demand)**
+DCM queries the provider for current capacity as part of request processing. Used when real-time accuracy is critical or when the provider cannot maintain a registration schedule.
+
+```yaml
+capacity_query_response:
+  provider_uuid: 
+  resource_type_uuid: 
+  location_uuid: 
+  query_timestamp: 
+  available_capacity: 
+  reserved_capacity: 
+  committed_capacity: 
+  sovereignty_capabilities: 
+```
+
+**Mode 2 — Provider Registration (scheduled, preferred)**
+Provider registers capacity data with DCM on a configurable schedule. DCM maintains an internal capacity rating per provider, per Resource Type, per location. Default minimum update frequency: twice daily. Update frequency is configurable per provider registration.
+
+```yaml
+capacity_registration:
+  provider_uuid: 
+  registration_timestamp: 
+  next_scheduled_registration: 
+  capacity_by_resource_type:
+    - resource_type_uuid: 
+      location_uuid: 
+      available_capacity: 
+      reserved_capacity: 
+      committed_capacity: 
+      sovereignty_capabilities: 
+```
+
+**Mode 3 — Provider Denial (reactive, mandatory)**
+The provider validates it can fulfill a request before executing. If it cannot, it denies the request with reason `INSUFFICIENT_RESOURCES`. DCM receives the denial and can retry with an alternative provider. The denial triggers an immediate update to DCM's internal capacity rating for that provider.
+
+```yaml
+provider_denial:
+  provider_uuid: 
+  request_uuid: 
+  denial_reason: INSUFFICIENT_RESOURCES
+  denial_timestamp: 
+  resource_type_uuid: 
+  location_uuid: 
+  estimated_available_at: 
+```
+
+### 7.2 Provider Lifecycle Events
+
+Any provider event that affects Resource/Service Entity availability or operational characteristics MUST be reported to DCM immediately. Providers have a contractual obligation to report these events — this is non-negotiable.
+
+**Reportable Event Types:**
+
+| Event Type | Description | DCM Response |
+|------------|-------------|--------------|
+| `CAPACITY_CHANGE` | Available capacity increased or decreased | Update internal capacity rating |
+| `DEGRADATION` | Underlying resource is degraded | Policy Engine evaluation → ALERT/REVERT/ESCALATE |
+| `MAINTENANCE_SCHEDULED` | Planned maintenance window declared | Policy Engine evaluation → notify, migrate if needed |
+| `MAINTENANCE_STARTED` | Maintenance has begun | Update Entity state to MAINTENANCE |
+| `MAINTENANCE_COMPLETED` | Maintenance completed | Restore Entity state, trigger drift detection |
+| `UNSANCTIONED_CHANGE` | Change occurred that was not initiated by DCM | Policy Engine evaluation → REVERT/UPDATE/ALERT |
+| `ENTITY_HEALTH_CHANGE` | Entity health status changed | Policy Engine evaluation |
+| `PROVIDER_DEGRADATION` | Provider itself is degraded | Policy Engine evaluation → reroute new requests |
+| `DECOMMISSION_NOTICE` | Provider is decommissioning underlying resource | Policy Engine evaluation → migrate or decommission Entity |
+
+**Event Payload Format:**
+All provider lifecycle events must be reported in DCM unified data model format:
+
+```yaml
+provider_lifecycle_event:
+  event_uuid: 
+  event_type: 
+  provider_uuid: 
+  affected_entity_uuids:
+    - 
+  event_timestamp: 
+  event_details:
+    
+  severity: 
+  requires_immediate_action: 
+```
+
+**Maximum Reporting Latency:**
+Providers must report lifecycle events within the timeframe declared in their provider registration. For CRITICAL severity events, immediate reporting is required. The reporting latency SLA is part of the Provider SLA/Operational Contract.
+
+### 7.3 DCM Capacity Rating
+
+DCM maintains an internal capacity rating per provider, per Resource Type, per location. This rating is used by the Policy Engine for placement decisions.
+
+```yaml
+dcm_capacity_rating:
+  provider_uuid: 
+  resource_type_uuid: 
+  location_uuid: 
+  last_updated: 
+  update_source: 
+  available_capacity: 
+  capacity_confidence: 
+  # high: updated within last scheduled window
+  # medium: updated within 2x scheduled window
+  # low: stale — beyond 2x scheduled window
+  next_scheduled_update: 
+```
+
+---
+
+
+---
+
+## 7a. Provider Update Notification Model
+
+### 7a.1 The Fundamental Constraint — Realized State Only Changes via a Request
+
+DCM enforces a single foundational rule for the Realized Store:
+
+> **Realized State only changes when an authorized request produces a corresponding Requested State record. No exceptions.**
+
+This constraint unifies all state change pathways and eliminates ambiguity:
+
+- **Drift is always unsanctioned** — if Discovered State differs from Realized State and there is no corresponding Requested State record explaining the difference, it is drift. There is no such thing as "legitimate drift."
+- **Discovery does not update Realized State** — discovery writes only to the Discovered Store. It never updates the Realized Store, even if discovery shows an authorized change (the authorization produces its own Requested State and Realized State records).
+- **Providers cannot write directly to Realized State** — providers report changes via the Provider Update Notification API. DCM evaluates the notification and creates a Requested State record if approved. Only then does a new Realized State record get written.
+
+### 7a.2 Provider Update Notification
+
+A **Provider Update Notification** is a formal mechanism by which a Service Provider reports an authorized state change to DCM. This is distinct from a lifecycle event (which reports provider health) and distinct from an unsanctioned change (which triggers drift). A Provider Update Notification is the provider saying: "I made an authorized change to this entity — please record it as the new Realized State."
+
+**When is a Provider Update Notification appropriate:**
+
+| Scenario | Correct mechanism | Why |
+|----------|------------------|-----|
+| Provider auto-heals a failed disk | Provider Update Notification | Authorized maintenance action; new disk is the correct state |
+| Provider scales resources per pre-authorized auto-scale policy | Provider Update Notification | DCM pre-authorized the scaling policy; each scaling event is an authorized change |
+| Provider performs planned maintenance that changes an IP assignment | Provider Update Notification | Planned, coordinated change |
+| Unauthorized human modifies VM configuration at provider console | Drift event | No DCM authorization; treated as unsanctioned change |
+| Provider silently changes configuration without notifying DCM | Drift event (detected by discovery) | Unreported change is unsanctioned until evaluated |
+
+### 7a.3 Provider Update Notification API
+
+Service Providers submit update notifications via a dedicated endpoint on the DCM API Gateway:
+
+```
+POST /api/v1/provider/entities/{entity_uuid}/update-notification
+Authorization: 
+
+Request body:
+{
+  "provider_uuid": "",
+  "notification_uuid": "",      # idempotency key
+  "notification_type": "",
+  "changed_fields": {
+    "memory_gb": {
+      "previous_value": 8,
+      "new_value": 16,
+      "change_reason": "Auto-scale policy: payments-api-scale-up triggered at 85% memory utilization",
+      "authorizing_policy_ref": ""
+    }
+  },
+  "effective_at": "",
+  "provider_evidence_ref": ""
+}
+```
+
+### 7a.4 DCM Processing of Provider Update Notifications
+
+```
+Provider submits update notification
+  │
+  ▼ Authentication and authorization check
+  │   Verify: provider UUID is registered and active
+  │   Verify: provider has authority over this entity
+  │
+  ▼ Policy Engine evaluates notification
+  │   Evaluate: is this type of change pre-authorized for this entity/provider?
+  │   Evaluate: does the change violate any GateKeeper constraints?
+  │   Evaluate: does this change require consumer notification or approval?
+  │
+  ├── REJECTED
+  │   The change is not authorized.
+  │   DCM does NOT update Realized State.
+  │   The discrepancy between provider state and DCM Realized State becomes drift.
+  │   Provider receives rejection response with reason.
+  │   UNSANCTIONED_CHANGE event logged.
+  │
+  ├── REQUIRES_CONSUMER_APPROVAL
+  │   The change is plausible but requires consumer sign-off.
+  │   Notification queued. Consumer notified.
+  │   Entity enters PENDING_REVIEW state.
+  │   Provider receives "pending_approval" response.
+  │   On consumer approval → proceeds to APPROVED path.
+  │   On consumer rejection → treated as REJECTED.
+  │
+  └── APPROVED
+      DCM creates a Requested State record:
+        source_type: provider_update
+        actor: provider (service account)
+        authorizing_policy_uuid: 
+        changed_fields: [as reported by provider]
+      
+      DCM writes new Realized State snapshot:
+        source_type: provider_update
+        corresponding_requested_state_uuid: 
+        supersedes_realized_state_uuid: 
+      
+      Audit record written: PROVIDER_UPDATE_APPLIED
+      Provider receives "accepted" response.
+```
+
+### 7a.5 Pre-Authorization of Provider Updates
+
+Organizations can pre-authorize categories of provider updates through policy, eliminating the need for per-change human approval:
+
+```yaml
+policy:
+  type: gatekeeper
+  handle: "tenant/payments/allow-auto-scale"
+  rules:
+    - condition:
+        notification_type: auto_scale
+        provider_uuid: 
+        entity.resource_type: Compute.VirtualMachine
+        changed_fields: [memory_gb, cpu_count]
+        change_within_bounds:
+          memory_gb: { max_increase_factor: 2 }
+          cpu_count: { max_increase_factor: 2 }
+      action: approve
+      audit_note: "Auto-scale approved per payments team scaling policy"
+```
+
+This pre-authorization pattern allows providers to implement auto-scaling, auto-healing, and maintenance operations without requiring per-change manual approval, while keeping DCM's Realized Store accurate and traceable.
+
+### 7a.6 Provider Lifecycle Events
+
+
+| Event Type | Mechanism | DCM Response | Realized Store Updated? |
+|------------|-----------|-------------|------------------------|
+| `CAPACITY_CHANGE` | Lifecycle event | Update internal capacity rating | No |
+| `DEGRADATION` | Lifecycle event | Policy Engine → ALERT/ESCALATE | No |
+| `MAINTENANCE_SCHEDULED` | Lifecycle event | Notify, plan migration if needed | No |
+| `MAINTENANCE_CHANGE` | **Provider Update Notification** | Evaluate → Requested State if approved | Yes (if approved) |
+| `AUTO_SCALE` | **Provider Update Notification** | Evaluate per pre-auth policy → Requested State if approved | Yes (if approved) |
+| `AUTO_HEAL` | **Provider Update Notification** | Evaluate per pre-auth policy → Requested State if approved | Yes (if approved) |
+| `UNSANCTIONED_CHANGE` | Lifecycle event (no notification) | Drift event → Policy Engine → REVERT/ALERT/ESCALATE | No (drift, not update) |
+| `ENTITY_HEALTH_CHANGE` | Lifecycle event | Policy Engine evaluation | No |
+| `DECOMMISSION_NOTICE` | Lifecycle event | Policy Engine → migrate or decommission | No |
+
+### 7a.7 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `RSE-010` | Realized State only changes via an authorized request that produces a corresponding Requested State record. Drift detection, discovery cycles, and lifecycle events do not write to the Realized Store. |
+| `RSE-011` | Provider Update Notifications are evaluated by the Policy Engine before any Realized State change. Rejected notifications do not update Realized State — the discrepancy becomes drift. |
+| `RSE-012` | Categories of provider updates may be pre-authorized via GateKeeper policy. Pre-authorized updates are processed automatically without per-change human approval. |
+| `RSE-013` | Provider Update Notifications that require consumer approval place the entity in PENDING_REVIEW state. The provider receives a "pending_approval" response and the change is queued until resolution. |
+
+
+
+### 7c. Provider Accreditation Registration
+
+Every Service Provider must declare its accreditation status during registration. Accreditation declarations are references to accreditation records registered in DCM's accreditation registry (see [doc 26](26-accreditation-and-authorization-matrix.md)).
+
+```yaml
+provider_registration:
+  # ... existing fields ...
+  accreditations:
+    - accreditation_uuid:        # reference to registered accreditation record
+      framework: fedramp_high
+      status: active
+      expires_at: "2026-12-31"
+
+    - accreditation_uuid: 
+      framework: hipaa
+      accreditation_type: baa
+      status: active
+
+  # Self-declared compliance (lowest trust; used when no formal accreditation exists)
+  self_declared_compliance:
+    frameworks: [iso_27001]
+    last_self_review: "2026-01-15"
+    evidence_ref: 
+
+  # Maximum data classification this provider is permitted to handle
+  # DCM computes this from active accreditations; self_declared_max is the fallback
+  self_declared_max_data_classification: confidential
+```
+
+Providers without any accreditation records are treated as `self_declared` level and are subject to the most restrictive authorization matrix rules. They may only receive data classified as `public` or `internal`.
+
+
+## 8. Entity Relationships
+
+Every Resource/Service Entity carries a `relationships` section declaring its relationships to other entities — internal DCM entities, external data entities, and business context entities. The relationship model is universal — the same structure is used for all relationship types.
+
+See [Entity Relationships](09-entity-relationships.md) for the complete relationship model.
+
+```yaml
+resource_service_entity:
+  uuid: 
+  # ... other entity fields ...
+  relationships:
+    - relationship_uuid: 
+      this_entity_uuid: 
+      this_role: 
+      related_entity_uuid: 
+      related_entity_type: 
+      relationship_type: 
+      nature: 
+      lifecycle_policy:
+        on_related_destroy: 
+        on_related_suspend: 
+        on_related_modify: 
+      status: 
+      provenance:
+        
+```
+
+---
+
+## 9. DCM System Policies for Resource/Service Entities
+
+The following are **non-overridable DCM System Policies** that apply to all Resource/Service Entities:
+
+| Policy | Rule | Enforcement |
+|--------|------|-------------|
+| `RSE-001` | Every Resource/Service Entity must belong to exactly one DCM Tenant | Enforced at Entity creation — no Tenant = request rejected |
+| `RSE-002` | Every Resource/Service Entity must have a UUID | Enforced at Entity creation |
+| `RSE-003` | Every Resource/Service Entity must have a complete provenance chain | Enforced at every state transition |
+| `RSE-004` | Realized State payloads must be complete — not a status code | Enforced at provider response receipt |
+| `RSE-005` | Decommissioned Entity records are immutable and permanent | Enforced at decommission — records cannot be deleted |
+| `RSE-006` | Provider lifecycle events must be recorded in Entity provenance | Enforced at event receipt |
+| `RSE-007` | Ownership transfers must be authorized by policy | Enforced at transfer initiation |
+| `RSE-008` | Process Resource Entities must reference all affected Entity UUIDs | Enforced at process completion |
+
+---
+
+## 9a. Lifecycle Time Constraints
+
+### 9a.1 Concept
+
+**Lifecycle time constraints** declare when a resource should cease to exist or trigger a lifecycle action. They are a first-class field on any resource entity — governed, provenance-tracked, and subject to the standard override control model.
+
+Any source in the data model precedence chain can declare a time constraint: a consumer request, a Core Layer, a Service Layer, or a policy. The Policy Engine has full authority over constraints — a GateKeeper can lock a TTL immutable or set `immutable_ceiling: absolute` on an expiry date.
+
+### 9a.2 Constraint Structure
+
+```yaml
+lifecycle_constraints:
+  ttl:
+    duration: P14D                            # ISO 8601 duration
+    reference_point: realization_timestamp    # created_at | realization_timestamp | last_modified
+    on_expiry: 
+    metadata:
+      override: allow                         # standard override control
+      basis_for_value: "Consumer declared ephemeral — 14-day lab resource"
+
+  expires_at:
+    timestamp: "2026-06-30T23:59:59Z"         # absolute calendar date
+    on_expiry: notify
+    metadata:
+      override: immutable
+      locked_by_policy_uuid: 
+      basis_for_value: "Project deadline — resource must not persist beyond Q2"
+
+  enforcement:
+    warn_before_expiry: P1D                   # warn 1 day before expiry
+    grace_period: PT1H                        # 1 hour grace after expiry before action
+    on_grace_period_expiry: 
+```
+
+When both `ttl` and `expires_at` are declared, the earliest expiry wins (LTC-004).
+
+### 9a.3 Precedence
+
+Time constraints follow the same precedence as all other resource fields:
+
+```
+Base Layer (lowest — e.g., no TTL by default)
+  ↓  Core Layer (e.g., all dev resources: TTL 90 days)
+  ↓  Service Layer (e.g., ephemeral compute: TTL 7 days)
+  ↓  Request Layer (consumer declared)
+  ↓  Transformation Policy (enrich from business context)
+  ↓  GateKeeper Policy (highest — may lock immutable)
+```
+
+### 9a.4 Expiry Enforcement
+
+The **Lifecycle Constraint Enforcer** is a DCM control plane component — not a provider concern. It monitors realized entities, fires `on_expiry` actions when constraints are reached, and records all enforcement in provenance and the Audit Store.
+
+Entities whose `on_expiry` action fails to execute enter `PENDING_EXPIRY_ACTION` state and trigger an escalation (LTC-005).
+
+### 9a.5 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `LTC-001` | Lifecycle time constraints follow standard data model precedence |
+| `LTC-002` | GateKeeper policies may lock lifecycle constraints as immutable |
+| `LTC-003` | Expiry enforcement is a DCM control plane function |
+| `LTC-004` | When multiple time constraints exist, the earliest expiry wins |
+| `LTC-005` | Failed expiry action execution triggers `PENDING_EXPIRY_ACTION` state and escalation |
+
+---
+
+## 9a. Lifecycle Time Constraints — Process Resources (Q28)
+
+Process Resource entities must declare a maximum execution time. This is a mandatory field — not optional. A Process Resource with no execution time limit creates operational blindness (DCM cannot know if it is hung).
+
+```yaml
+process_resource_entity:
+  resource_type: Process.AnsiblePlaybook
+  execution_constraints:
+    max_execution_time: PT2H          # mandatory — ISO 8601 duration
+    expected_completion: PT30M        # advisory — when we expect completion
+    grace_period: PT15M               # grace period after max before action fires
+    on_max_exceeded: 
+    # escalate:  notify platform admin and provider; human decides
+    # terminate: DCM instructs provider to terminate the process
+    # notify:    notify consumer and wait; no automatic action
+    escalation_recipient: 
+```
+
+The Lifecycle Constraint Enforcer handles this — process execution time is a `lifecycle_constraint.ttl` with `reference_point: realization_timestamp`. The `on_max_exceeded` action maps to the standard `on_expiry` lifecycle action vocabulary.
+
+**Profile-governed default `on_max_exceeded`:**
+
+| Profile | Default Action |
+|---------|---------------|
+| `minimal` | `notify` |
+| `dev` | `notify` |
+| `standard` | `escalate` |
+| `prod` | `escalate` |
+| `fsi` | `terminate` |
+| `sovereign` | `terminate` |
+
+---
+
+## 9b. Billing State and SUSPENDED Entities (Q29)
+
+DCM carries billing state as a first-class field — the Cost Analysis component consumes it. Organizations declare billing behavior via policy — DCM does not decide what is billable.
+
+```yaml
+entity:
+  lifecycle_state: SUSPENDED
+  billing_state: 
+  billing_metadata:
+    billing_rate_multiplier: 0.3       # 30% of normal rate if reduced_rate
+    billing_reason: "Reserved capacity — suspended but resources held"
+    billing_policy_uuid:         # policy that determined this billing state
+    billable_components: [storage, ip_address]   # which sub-resources are billed
+    non_billable_components: [compute]
+```
+
+**Three billing models for SUSPENDED:**
+- **`billable`** — resources reserved and capacity held (stopped VM still consuming reserved IP and storage)
+- **`non_billable`** — resources fully released on suspension (spot/ephemeral resource)
+- **`reduced_rate`** — partial resources held (storage retained, compute released)
+
+Policy injects `billing_state` and `billing_metadata` during state transitions. A GateKeeper can declare: "all suspended VMs in the payments Tenant are billed at 30% — compute released but storage and IP retained."
+
+---
+
+## 9c. Bare Metal Indivisibility (Q26)
+
+Bare metal Whole Allocation uses the same `shareability.allowed: false` mechanism as any non-shareable resource (REL-017), plus an explicit `allocation_model` declaration:
+
+```yaml
+resource_type_spec:
+  fully_qualified_name: Compute.BareMetal
+  allocation_model: whole_unit         # whole_unit | fractional | pooled
+  shareability:
+    allowed: false                     # structural lock — cannot be changed by policy
+    indivisibility_reason: "Physical hardware — cannot be partitioned"
+  capacity:
+    unit: server
+    minimum_allocation: 1
+    maximum_allocation: 1              # whole unit only
+
+# Provider contract obligations for bare metal:
+provider_contract_obligations:
+  - Report full physical identity in realized payload (serial_number, hardware_profile)
+  - Exclusive placement hold during reserve_query — no concurrent holds on same server
+  - Notify DCM immediately if any sharing attempt is detected (drift trigger)
+```
+
+---
+
+## 9d. Capacity Confidence — Automatic Actions (Q27)
+
+Capacity confidence ratings trigger policy-governed automatic actions. Policy determines the action per confidence level; the active Profile sets defaults.
+
+```yaml
+capacity_confidence_policy:
+  HIGH:
+    action: proceed
+    max_data_age: PT5M
+  MEDIUM:
+    action: proceed_with_warning      # default — overridable by policy
+    max_data_age: PT30M
+  LOW:
+    action: refresh_before_placement  # default — trigger Mode 1 query
+    max_data_age: PT1H
+    trigger_mode1_query: true
+```
+
+**Profile-governed defaults:**
+
+| Profile | HIGH | MEDIUM | LOW |
+|---------|------|--------|-----|
+| `minimal` | proceed | proceed | proceed_with_warning |
+| `dev` | proceed | proceed | refresh_before_placement |
+| `standard` | proceed | proceed_with_warning | refresh_before_placement |
+| `prod` | proceed | refresh_before_placement | reject |
+| `fsi` | proceed | refresh_before_placement | reject |
+| `sovereign` | proceed | refresh_before_placement | reject |
+
+---
+
+## 9e. Ownership Transfer Count (Q25)
+
+Ownership transfers are unlimited by default. Each transfer is immutably recorded with a monotonically incrementing `transfer_number`. Policy may declare a maximum per resource type.
+
+```yaml
+ownership_transfer_record:
+  transfer_uuid: 
+  transfer_number: 3              # monotonically incrementing — never resets
+  from_tenant_uuid: 
+  to_tenant_uuid: 
+  authorized_by: 
+  transfer_timestamp: 
+  reason: 
+  policy_uuid: 
+```
+
+Policy-governed maximum when needed:
+```yaml
+policy:
+  type: gatekeeper
+  rule: >
+    If resource.ownership_transfer_count > 5
+    AND resource_type == Compute.VirtualMachine
+    THEN gatekeep: "VM has exceeded 5 ownership transfers — manual review required"
+```
+
+---
+
+## 10. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | For Hybrid Transfer — what is the maximum number of ownership transfers allowed, or is it unlimited? | Operational complexity | ✅ Resolved — unlimited by default; policy may declare maximum; monotonically incrementing transfer_number (ENT-001) |
+| 2 | For Whole Allocation of bare metal — how is the indivisibility enforced at the provider level? | Provider contract | ✅ Resolved — allocation_model: whole_unit; shareability.allowed: false; exclusive hold; provider reports physical identity (ENT-002) |
+| 3 | Should capacity confidence ratings trigger automatic actions? | Capacity model | ✅ Resolved — policy-governed actions per confidence level; LOW triggers Mode 1 query by default in standard+; profile-governed (ENT-003) |
+| 4 | For Process Resources — should there be a maximum execution time? | Operational governance | ✅ Resolved — mandatory max_execution_time; enforced by Lifecycle Constraint Enforcer; profile-governed on_max_exceeded (ENT-004) |
+| 5 | How does the SUSPENDED state interact with cost analysis? | Cost model | ✅ Resolved — billing_state field (billable/non_billable/reduced_rate); policy injects on state transition; Cost Analysis consumes (ENT-005) |
+
+---
+
+## 11. DCM System Policies — Entity and Dependency Gaps
+
+| Policy | Rule |
+|--------|------|
+| `ENT-001` | Ownership transfer count is unlimited by default. Policy may declare a maximum transfer count per resource type. Each transfer is immutably recorded with a monotonically incrementing transfer_number and mandatory reason field. |
+| `ENT-002` | Bare metal resources declare `allocation_model: whole_unit` and `shareability.allowed: false`. Placement holds are exclusive. Providers must report the server's physical identity in the realized payload and notify DCM of any sharing attempt. |
+| `ENT-003` | Capacity confidence ratings trigger policy-governed automatic actions. LOW confidence triggers a Mode 1 Information Provider query by default in standard+ profiles. Profile determines the default action per confidence level. |
+| `ENT-004` | Process Resource entities must declare `max_execution_time`. This field is mandatory. Execution time is enforced by the Lifecycle Constraint Enforcer. Profile governs the default `on_max_exceeded` action. |
+| `ENT-005` | Entity `billing_state` (billable, non_billable, or reduced_rate) is a first-class field injected by policy during state transitions. The Cost Analysis component consumes `billing_state` for cost attribution. DCM does not decide billing policy — it carries the billing signal. |
+
+---
+
+
+
+- **DCM Tenant** — the mandatory ownership boundary for all Resource/Service Entities
+- **Four States** — Intent, Requested, Realized, Discovered — the state lifecycle of a Resource/Service Request and Entity
+- **Field-Level Provenance** — every state transition and ownership transfer is recorded in Entity provenance
+- **Policy Engine** — evaluates provider events and unsanctioned changes, determines response actions
+- **Service Dependencies** — Resource/Service Entities declare dependencies on other Entities
+- **Resource Grouping** — Entities belong to a Tenant and optionally to additional Resource Groups
+- **Provider Contract** — governs provider obligations including capacity reporting and event notification
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/07-service-dependencies.md b/content/docs/architecture/data-model/07-service-dependencies.md
new file mode 100644
index 0000000..7985327
--- /dev/null
+++ b/content/docs/architecture/data-model/07-service-dependencies.md
@@ -0,0 +1,548 @@
+# DCM Data Model — Service Dependencies
+
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Entity Relationships](09-entity-relationships.md) | [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Resource/Service Entities](06-resource-service-entities.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA**
+>
+> The Data abstraction — dependency graph as embedded data structure
+
+
+
+> **Scope:** This document covers dependency declaration, rehydration ordering, and failure handling. The underlying data structure is the Entity Relationship Graph defined in [Entity Relationships](09-entity-relationships.md).
+
+---
+
+## 1. Purpose
+
+This document defines how service dependencies are declared, resolved, and managed within DCM. Dependencies are a core data model concern — not an orchestration concern. The data structures defined here enable DCM to know the complete resource footprint of any request before execution begins, which is essential for cost analysis, placement decisions, rehydration, and audit.
+
+---
+
+## 2. Why Dependencies Must Be Declared in Advance
+
+Dependencies must be declared in the data model — not discovered at runtime by providers. This is a hard requirement driven by four core DCM goals:
+
+**Auditability** — the complete dependency graph must be known before execution. Every resource that will be created as part of fulfilling a request must be visible in the request's provenance chain from the start.
+
+**Cost Analysis** — accurate cost estimation and cost-based placement require knowing the full resource footprint before provisioning. Hidden dependencies produce hidden costs that only become visible after the fact.
+
+**Placement** — the Policy Engine cannot make optimal placement decisions without knowing all resources that will be created. A Web Server request that implicitly spawns a VM, IP address, and firewall rule has placement requirements that span multiple resource types.
+
+**Idempotency and Consistency** — if dependencies are declared in the service definition, the same request always produces the same dependency graph. Provider-driven dependency discovery at runtime breaks idempotency — different provider implementations could produce different dependency graphs for the same logical request.
+
+---
+
+## 3. Hybrid Dependency Declaration Model
+
+DCM uses a hybrid model for dependency declaration that operates at two levels:
+
+### 3.1 Type-Level Dependencies (Resource Type Specification)
+
+Dependencies declared at the Resource Type Specification level are **portable and provider-agnostic**. They define what kinds of resources are needed — not which specific provider supplies them.
+
+- Declared in the Resource Type Specification
+- Apply to all Provider Catalog Items implementing that Resource Type
+- Use Resource Type UUIDs — not provider-specific references
+- Required for all implementations of the Resource Type
+- Portable — the dependency can be fulfilled by any provider implementing the required Resource Type
+
+**Example:**
+```yaml
+resource_type: Compute.VirtualMachine
+type_level_dependencies:
+  - dependency_uuid: 
+    required_resource_type_uuid: 
+    required_resource_type_name: Network.IPAddress
+    dependency_type: hard
+    cardinality: one_to_one
+    description: Every VM requires exactly one IP address
+  - dependency_uuid: 
+    required_resource_type_uuid: 
+    required_resource_type_name: Network.FirewallRule
+    dependency_type: hard
+    cardinality: one_to_many
+    description: Every VM requires at least one firewall rule
+```
+
+### 3.2 Provider-Specific Dependencies (Provider Catalog Item)
+
+Dependencies declared at the Provider Catalog Item level are **provider-specific additions** beyond the type-level dependencies. They must be marked as portability-breaking.
+
+- Declared in the Provider Catalog Item registration
+- Apply only to requests fulfilled by that specific provider
+- Must be marked `portability_breaking: true`
+- Visible to the Policy Engine for governance decisions
+- Surfaced to consumers as portability warnings
+
+**Example:**
+```yaml
+catalog_item: Nutanix.VM.Small
+provider_specific_dependencies:
+  - dependency_uuid: 
+    required_resource_type_uuid: 
+    required_resource_type_name: Nutanix.StorageContainer
+    dependency_type: hard
+    portability_breaking: true
+    description: Nutanix VMs require a Nutanix Storage Container
+    portability_warning: This dependency locks this request to Nutanix providers
+```
+
+---
+
+## 4. Dependency Types
+
+Every declared dependency must specify its type:
+
+| Type | Description | Behavior |
+|------|-------------|----------|
+| `hard` | Must be realized before or alongside the dependent resource | Failure of dependency fails the dependent resource |
+| `soft` | Preferred but not blocking | Failure of dependency is recorded but does not block the dependent resource |
+| `conditional` | Required only if specific conditions in the request payload are met | Evaluated by Policy Engine against request data |
+
+---
+
+## 5. Dependency Cardinality
+
+Every declared dependency must specify its cardinality:
+
+| Cardinality | Description | Example |
+|-------------|-------------|---------|
+| `one_to_one` | Exactly one dependency resource required | One VM needs exactly one primary IP |
+| `one_to_many` | One or more dependency resources required | One VM needs one or more firewall rules |
+| `one_to_optional` | Zero or one dependency resource | One VM may optionally have a secondary IP |
+| `one_to_range` | A specific numeric range required | One load balancer needs 2-6 backend VMs |
+
+---
+
+## 6. Dependency Graph
+
+When a request is processed, the Request Payload Processor constructs a **Dependency Graph** — a complete map of all resources that must be created to fulfill the request, including all transitive dependencies.
+
+### 6.1 Dependency Graph Structure
+
+```yaml
+dependency_graph:
+  graph_uuid: 
+  root_request_uuid: 
+  tenant_uuid: 
+  created_timestamp: 
+  nodes:
+    - node_uuid: 
+      resource_type_uuid: 
+      resource_type_name: 
+      request_uuid: 
+      entity_uuid: 
+      lifecycle_state: 
+      dependencies:
+        - dependency_uuid: 
+          dependent_node_uuid: 
+          dependency_type: 
+          status: 
+  edges:
+    - from_node_uuid: 
+      to_node_uuid: 
+      dependency_uuid: 
+      dependency_type: 
+```
+
+### 6.2 Transitive Dependencies
+
+DCM resolves transitive dependencies — the full chain of dependencies, not just direct ones.
+
+**Example — Web Server request:**
+```
+Web Server (requested)
+  ├── VM (hard dependency of Web Server)
+  │     ├── IP Address (hard dependency of VM)
+  │     │     └── Network (hard dependency of IP Address)
+  │     └── Firewall Rule (hard dependency of VM)
+  │           └── IP Address (reference — already in graph)
+  └── DNS Record (soft dependency of Web Server)
+        └── IP Address (reference — already in graph)
+```
+
+The dependency graph contains each resource exactly once — circular references and duplicate nodes are detected and resolved. A resource that appears as a dependency of multiple nodes is represented as a single node with multiple incoming edges.
+
+### 6.3 Dependency Graph and the Four States
+
+The dependency graph is part of the request's data from the moment it is constructed:
+
+- **Intent State** — consumer's request, no dependency graph yet
+- **Requested State** — dependency graph constructed and attached, all nodes in PENDING state
+- **Realized State** — nodes updated to REALIZED as providers fulfill each dependency
+- **Discovered State** — dependency graph used to scope discovery — discover all nodes in the graph
+
+---
+
+## 7. Dependency Payload Passing
+
+When a dependency resource is realized, its realized payload must be passed to the dependent resource's provider. This is how a provider knows the details of the resources it depends on — IP addresses, network configurations, security group IDs, etc.
+
+### 7.1 The Payload Passing Mechanism
+
+```
+Dependency Resource realized
+  │
+  ▼
+Realized State payload captured in Realized Store
+  │
+  ▼
+Dependency node in graph updated: entity_uuid recorded, status → SATISFIED
+  │
+  ▼
+Dependent resource's Requested State payload enriched with dependency data
+  │  Recorded in field-level provenance — source_type: dependency_payload
+  │  source_uuid: 
+  ▼
+Enriched payload dispatched to dependent resource's provider
+```
+
+### 7.2 Dependency Data in Request Payloads
+
+When a dependency is satisfied, the dependent resource's Requested State payload is enriched with the dependency entity's UUID and relevant realized data:
+
+```yaml
+# Original request payload for VM
+vm_request:
+  cpu_count: 8
+  ram_gb: 32
+  os: RHEL9
+
+# After IP Address dependency is realized
+vm_request:
+  cpu_count: 8
+  ram_gb: 32
+  os: RHEL9
+  dependencies:
+    ip_address:
+      entity_uuid: 
+      ip_address: 192.168.1.45
+      network_uuid: 
+      subnet: 192.168.1.0/24
+      provenance:
+        source_type: dependency_payload
+        source_uuid: 
+        timestamp: 
+```
+
+---
+
+## 8. Dependency Resolution Order
+
+The dependency graph determines resolution order. Resources with no unsatisfied hard dependencies can be dispatched immediately. Resources with unsatisfied hard dependencies wait until their dependencies are satisfied.
+
+### 8.1 Resolution Rules
+
+- A resource node can only be dispatched when all its `hard` dependencies are in SATISFIED state
+- `soft` dependencies do not block dispatch — they are attempted but failure does not block
+- `conditional` dependencies are evaluated by the Policy Engine before the graph is constructed — if conditions are not met, the conditional dependency node is not added to the graph
+- Independent branches of the dependency graph can be resolved in parallel — the Orchestration component determines parallelism
+- Circular dependencies are invalid — the Policy Engine rejects any dependency graph with circular references
+
+### 8.2 Failure Handling
+
+Dependency failure handling is **configurable per request or per policy**:
+
+| Failure Mode | Behavior |
+|-------------|---------|
+| `fail_all` | Any hard dependency failure fails the entire request. All partially realized nodes are decommissioned. |
+| `fail_dependent` | A hard dependency failure fails only the dependent resource and its dependents. Independent branches continue. |
+| `retry` | Failed dependencies are retried with the same or alternative provider before failing. Retry count and provider selection policy are configurable. |
+| `partial_complete` | Request is marked partially complete. Failed nodes are flagged for retry or manual intervention. |
+
+The failure mode is declared in the request payload or in an applicable organizational policy.
+
+---
+
+## 9. Rehydration and the Dependency Graph
+
+The dependency graph is the primary mechanism enabling **DC Rehydration** — the ability to reconstruct any resource and its dependencies from scratch.
+
+### 9.1 Rehydration Process
+
+Rehydration uses the **Intent State** of the original request — not the Realized State — to reconstruct the dependency graph. This ensures that rehydration applies current policies and standards rather than replaying an old realized state.
+
+```
+Rehydration initiated for a Tenant / Group / Entity
+  │
+  ▼
+Intent State(s) retrieved from Intent Store
+  │
+  ▼
+Dependency graphs reconstructed from Intent States
+  │
+  ▼
+Graphs processed through current Policy Engine
+  │  Current policies applied — may differ from original request
+  │  Current placement policies applied
+  │  Current sovereignty constraints applied
+  ▼
+New Requested State payloads generated
+  │
+  ▼
+Resources realized in dependency order
+  │
+  ▼
+New Realized States recorded
+```
+
+### 9.2 Intent Portability in Rehydration
+
+Because rehydration uses Intent State rather than Realized State:
+- Resources can be rehydrated to a different provider — as long as the provider supports the required Resource Types
+- Current organizational standards and policies are applied — ensuring rehydrated resources meet current compliance requirements
+- Provider-specific dependencies (portability-breaking) may prevent rehydration to a different provider — this is surfaced as a portability warning during rehydration planning
+
+### 9.3 Rehydration Scope
+
+Rehydration can be scoped to:
+- A single Resource/Service Entity and its full dependency graph
+- A Resource Group — all entities in the group and their dependency graphs
+- A Tenant — all entities owned by the Tenant
+- A full Data Center — all entities across all Tenants in a location
+
+The dependency graph ensures that rehydration is always complete — no orphaned resources, no missing dependencies.
+
+---
+
+## 10. Dependency Declaration in Service Catalog Items
+
+Service Catalog Items must declare their dependencies as part of their definition. A catalog item with undeclared dependencies is invalid and will be rejected by the Policy Engine.
+
+```yaml
+catalog_item:
+  uuid: 
+  name: Web Server Service
+  resource_type_uuid: 
+  type_level_dependencies:
+    - dependency_uuid: 
+      required_resource_type_uuid: 
+      dependency_type: hard
+      cardinality: one_to_one
+  provider_specific_dependencies: []
+  conditional_dependencies:
+    - dependency_uuid: 
+      required_resource_type_uuid: 
+      dependency_type: conditional
+      condition:
+        field: high_availability
+        operator: equals
+        value: true
+      description: Load balancer required when high_availability is true
+```
+
+---
+
+## 11. DCM System Policies for Dependencies
+
+| Policy | Rule | Enforcement |
+|--------|------|-------------|
+| `DEP-001` | All dependencies must be declared before a catalog item is active | Enforced at catalog item registration |
+| `DEP-002` | Circular dependencies are invalid | Enforced at dependency graph construction |
+| `DEP-003` | Provider-specific dependencies must be marked portability-breaking | Enforced at provider catalog item registration |
+| `DEP-004` | Dependency payloads must be passed to dependent providers in DCM unified format | Enforced at dependency satisfaction |
+| `DEP-005` | Every node in a dependency graph must have a UUID | Enforced at graph construction |
+
+---
+
+## 11a. Dependency Graph Versioning (Q30)
+
+Dependency graphs are versioned as properties of their parent catalog item — not as independent artifacts. When the dependency graph changes, the catalog item version increments following standard semver semantics:
+
+| Change | Semver Impact | Reason |
+|--------|--------------|--------|
+| Dependency version constraint tightened | Revision bump | Compatible — narrower constraint |
+| New optional dependency added | Minor bump | Compatible — additive |
+| New required dependency added | **Major bump** | Breaking — consumers must update |
+| Required dependency removed | **Major bump** | Breaking — consumers may depend on it |
+| Dependency type changed | **Major bump** | Breaking — structural change |
+
+**At request time:** The catalog item version determines the dependency graph. A consumer pinning to `catalog_item_version: "1.5.3"` gets exactly the dependency graph declared in that version.
+
+**For existing realizations:** The dependency graph version is captured in the Requested State assembly provenance. Rehydration with `re_evaluate: false` replays from the Requested State. Rehydration with `re_evaluate: true` uses the current dependency graph for the selected version.
+
+---
+
+## 11b. Dependency Graph Storage (Q31)
+
+The dependency graph is embedded in assembly provenance — not a separate entity.
+
+| Level | What is stored | Where |
+|-------|---------------|-------|
+| Declared dependency graph | Part of Resource Type Specification | GitOps Layer/Policy Store |
+| Resolved dependency graph | `placement.yaml` in Requested State | GitOps Requested Store |
+| Realized dependency graph | Realized State events per dependency | Event Stream / Realized Store |
+
+```yaml
+# In placement.yaml — resolved dependency graph
+dependency_resolution:
+  - dependency_role: storage
+    resource_type: Storage.Block
+    resolved_provider_uuid: 
+    resolved_catalog_item_version: "1.2.0"
+    reserved_entity_uuid: 
+    reservation_hold_uuid: 
+  - dependency_role: networking
+    resource_type: Network.IPAddress
+    resolved_provider_uuid: 
+    reserved_entity_uuid: 
+```
+
+The full dependency chain is always traceable from the Requested State record — no separate entity needed.
+
+---
+
+## 11c. Dependency Graph Depth (Q33)
+
+Dependency graph depth is limited to a profile-governed maximum. Circular dependency detection is always enforced regardless of depth configuration.
+
+```yaml
+dependency_depth_policy:
+  max_depth: 10                  # configurable via Policy Group
+  on_max_exceeded: reject        # reject with clear error identifying depth + chain
+  cycle_detection: always        # non-configurable — always enforced
+```
+
+**Profile-governed defaults:**
+
+| Profile | Default Max Depth | Rationale |
+|---------|-----------------|-----------|
+| `minimal` | 20 | Home lab — free composition |
+| `dev` | 15 | Development — generous |
+| `standard` | 10 | Production baseline |
+| `prod` | 10 | Production |
+| `fsi` | 7 | Tight — complex dependencies harder to audit |
+| `sovereign` | 7 | Maximum control |
+
+In practice, well-designed service compositions rarely exceed 5-6 levels. Depth 10 provides headroom without allowing pathological compositions.
+
+---
+
+## 11d. compound service definition Composition Visibility (Q34)
+
+compound service definitions declare how their internal composition is exposed to DCM. This determines whether sub-resources are DCM entities subject to standard lifecycle management, or opaque to DCM.
+
+```yaml
+compound_service_registration:
+  composition_visibility:
+    mode: 
+    # opaque:      Consumer sees only top-level service entity
+    #              Sub-resources not visible in DCM
+    # transparent: All sub-resources registered as DCM entities
+    #              Full dependency graph visible; drift detection on all
+    # selective:   Provider declares which sub-resources are DCM-visible
+    dcm_visible_sub_resources:    # if selective
+      - resource_type: Compute.VirtualMachine
+        role: control_plane_node
+      - resource_type: Network.LoadBalancer
+        role: api_endpoint
+```
+
+**Drift detection interaction:**
+- `opaque` — drift detection only on what the compound service definition reports via realized payload; sub-resources are provider's responsibility
+- `transparent` — drift detection on all sub-resources as full DCM entities
+- `selective` — drift detection on declared DCM-visible sub-resources only
+
+---
+
+## 12. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | How are dependency graphs versioned — does a new version of a catalog item invalidate existing dependency graphs? | Versioning model | ✅ Resolved — versioned as part of catalog item; semver semantics; captured in assembly provenance (ENT-006) |
+| 2 | Should the dependency graph be stored as a separate entity or embedded in the request payload? | Data model structure | ✅ Resolved — embedded in assembly provenance; declared in Resource Type Spec; resolved in placement.yaml (ENT-007) |
+| 3 | How are cross-tenant dependencies handled? | Multi-tenancy | ✅ Resolved — governed by REL-010/011/012 and DEP-001/002/003; see Entity Relationships doc |
+| 4 | Should there be a maximum dependency graph depth? | Operational complexity | ✅ Resolved — profile-governed max (10 standard/prod, 7 fsi/sovereign); circular detection always enforced (ENT-008) |
+| 5 | How does the dependency graph interact with the compound service definition model? | Provider model | ✅ Resolved — composition_visibility (opaque/transparent/selective); transparent/selective registers sub-resources as DCM entities (ENT-009) |
+
+---
+
+## 13. DCM System Policies — Dependency Gaps
+
+| Policy | Rule |
+|--------|------|
+| `ENT-006` | Dependency graphs are versioned as properties of their parent catalog item. New required dependency or removed dependency is a major (breaking) version bump. The dependency graph version used in a realization is captured in assembly provenance. |
+| `ENT-007` | The declared dependency graph is embedded in the Resource Type Specification. The resolved dependency graph is embedded in the Requested State assembly provenance (placement.yaml). No separate dependency graph entity is required. |
+| `ENT-008` | Dependency graph depth is limited to a profile-governed maximum (default: 10 for standard/prod; 7 for fsi/sovereign). Requests exceeding the maximum depth are rejected with a clear error. Circular dependency detection is always enforced regardless of depth configuration. |
+| `ENT-009` | compound service definitions declare composition_visibility as opaque, transparent, or selective. Transparent and selective modes register sub-resources as DCM entities subject to standard lifecycle management and drift detection. Opaque mode delegates sub-resource management entirely to the provider. |
+
+---
+
+
+
+- **Resource Type Specification** — declares type-level dependencies for a Resource Type
+- **Provider Catalog Item** — declares provider-specific additional dependencies
+- **Request Payload Processor** — constructs the dependency graph during assembly
+- **Policy Engine** — evaluates conditional dependencies, enforces dependency policies, governs failure handling
+- **Intent Portability** — rehydration uses Intent State to allow replay with different providers
+- **Field-Level Provenance** — dependency payload data is recorded with source Entity UUID
+- **Resource Grouping** — rehydration can be scoped to groups and tenants
+
+
+---
+
+## 8. Compound Service Compensation Declaration
+
+### 8.1 Overview
+
+Compound services (delivered by compound service definitions) must declare compensation behavior for each component. This declaration is part of the service definition — not discovered at runtime. See [Operational Models](24-operational-models.md) Section 6 for the full compensation execution model.
+
+### 8.2 Compensation Fields on Service Components
+
+```yaml
+service_component:
+  id: vm
+  resource_type: Compute.VirtualMachine
+  required_for_delivery: 
+  # atomic: must succeed; failure triggers full compensation rollback
+  # partial: failure → DEGRADED state; compound service delivered partially
+
+  compensation_on_failure: 
+  # decommission_immediately: decommission this component as part of rollback
+  # release_allocation:       release allocation back to pool (for allocatable resources)
+  # skip:                     do not compensate; used for partial delivery components
+  # notify:                   notify owner; human decides compensation
+
+  compensation_order: 
+  # Lower numbers compensate first; higher numbers compensate last
+  # Reverse dependency order is the default if not declared
+
+  depends_on: []
+```
+
+### 8.3 Partial Delivery Policy
+
+```yaml
+partial_delivery_policy:
+  min_required_components: [vm, ip]  # compound DEGRADED if only these succeed
+  degraded_is_acceptable: true
+  auto_retry_optional_components:
+    enabled: true
+    max_attempts: 3
+    interval: PT15M
+    on_exhaustion: notify_owner
+```
+
+### 8.4 System Policies — Compensation
+
+| Policy | Rule |
+|--------|------|
+| `DEP-010` | Compensation executes in reverse dependency order (highest compensation_order first). |
+| `DEP-011` | Compensation failure triggers COMPENSATION_FAILED state and immediate orphan detection. |
+| `DEP-012` | Components with required_for_delivery: partial are not compensation-triggering. Their failure produces a DEGRADED compound entity. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/08-resource-grouping.md b/content/docs/architecture/data-model/08-resource-grouping.md
new file mode 100644
index 0000000..0ee2bee
--- /dev/null
+++ b/content/docs/architecture/data-model/08-resource-grouping.md
@@ -0,0 +1,388 @@
+# DCM Data Model — Resource Grouping
+
+
+
+> **Related:** See [Universal Group Model](15-universal-groups.md) for the canonical group model. The constructs here (Tenants, Resource Groups) map 1:1 to `group_class` values in that model.
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Service Dependencies](07-service-dependencies.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA**
+>
+> The Data abstraction — DCMGroup typed extensions (Tenant, Resource Group, Cross-Tenant Auth)
+
+
+
+---
+
+## 1. Purpose
+
+This document defines how Resource/Service Entities are organized into groups within DCM. Grouping provides the ownership, organizational context, cost attribution, policy scope, and rehydration targeting that makes DCM operationally meaningful at scale.
+
+Two concepts are defined here:
+1. **DCM Tenant** — the mandatory, first-class ownership boundary for all Resource/Service Entities
+2. **Resource Groups** — flexible, composable grouping entities that provide additional organizational context
+
+---
+
+## 2. DCM Tenant
+
+### 2.1 Definition
+
+A **DCM Tenant** is the primary ownership and isolation boundary for Resource/Service Entities in DCM. Every Resource/Service Entity — including Process Resources — must belong to exactly one DCM Tenant at any point in time.
+
+Tenant membership is the answer to the question: **who owns this resource?**
+
+### 2.2 Tenant as a DCM System Policy
+
+Mandatory Tenant membership is a **non-overridable DCM System Policy**:
+
+| Policy | Rule | Enforcement |
+|--------|------|-------------|
+| `TEN-001` | Every Resource/Service Entity must belong to exactly one DCM Tenant | Enforced at Entity creation — no Tenant = request rejected |
+| `TEN-002` | Tenant membership cannot be empty — a Tenant must exist before resources can be created in it | Enforced at request processing |
+| `TEN-003` | A Resource/Service Entity cannot exist without a Tenant | Enforced at all lifecycle states |
+
+### 2.3 What Tenant Provides
+
+The Tenant boundary enables the following DCM capabilities for all resources it owns:
+
+| Capability | Description |
+|------------|-------------|
+| **Ownership** | Unambiguous answer to "who owns this resource" — always answerable, always auditable |
+| **Isolation** | Resources in one Tenant are isolated from resources in another — hard tenancy enforcement |
+| **Cost Attribution** | All resource costs roll up to the owning Tenant |
+| **Policy Scope** | Tenant-level policies apply to all resources in the Tenant |
+| **Drift Detection Scope** | Drift detection can be scoped to a Tenant |
+| **Rehydration Scope** | A full Tenant can be targeted for rehydration |
+| **Audit Scope** | All activity within a Tenant is auditable as a unit |
+| **Sovereignty Boundary** | Sovereignty constraints can be applied at the Tenant level |
+
+### 2.4 Tenant Entity Definition
+
+```yaml
+dcm_tenant:
+  uuid: 
+  name: 
+  description: 
+  version: 
+  status:
+    state: 
+    deprecation_date: 
+    sunset_date: 
+    replacement_uuid: 
+    deprecation_reason: 
+    migration_guidance: 
+  ownership:
+    owner_uuid: 
+    owner_type: 
+    created_timestamp: 
+  membership_policy:
+    exclusive: true
+    # A resource belongs to exactly one Tenant
+    # This is non-overridable
+  sovereignty_constraints:
+    
+  policies:
+    
+  provenance:
+    
+```
+
+### 2.5 Tenant and Resource Consumption
+
+A resource belongs to exactly one Tenant — its **owner**. However, a resource can be **consumed** by multiple Tenants via the DCM Service Catalog. Ownership and consumption are distinct:
+
+- **Ownership** (Tenant membership) — who is responsible for the lifecycle, cost, and compliance of this resource
+- **Consumption** — who uses or depends on this resource as a service
+
+Cross-tenant consumption is tracked through service requests and cost attribution — not through Tenant membership. A shared DNS service owned by a Platform Tenant can be consumed by any number of application Tenants. The DNS Entity belongs to the Platform Tenant. Consumption is tracked via service requests from each consuming Tenant.
+
+---
+
+## 3. Resource Groups
+
+### 3.1 Definition
+
+A **Resource Group** is a flexible, composable grouping entity that provides organizational context, operational scope, and policy targeting beyond what Tenant membership provides.
+
+Resource Groups function like **structured tags** — a resource accumulates group memberships that describe its context from multiple dimensions simultaneously. A VM could simultaneously belong to:
+- `Deployment: WebApp-v2` (what deployment it is part of)
+- `BusinessUnit: Payments` (which business unit owns the workload)
+- `RegulatoryScope: PCI-DSS` (which compliance regime applies)
+- `CostCenter: CC-4421` (where costs are attributed)
+
+Each group membership is a different dimension of context — not a hierarchy within a single dimension.
+
+### 3.2 Resource Group Classes
+
+DCM defines two classes of Resource Group, both implementing the same **Resource Group Interface**:
+
+**Class 1 — DCM Default Resource Group**
+Built into DCM. The standard mechanism for grouping resources. No implementor customization required to use it.
+
+**Class 2 — Custom Resource Group**
+Implementor-defined grouping entities. Tied to internal business structures — business units, product lines, regulatory scopes, cost centers, etc. Full parity with DCM Default Resource Groups in terms of DCM capabilities.
+
+Both classes implement the same interface. The DCM Default Resource Group is simply DCM's own implementation of the Resource Group Interface. Custom groups are implementor-defined implementations of the same interface.
+
+### 3.3 The Resource Group Interface
+
+Every Resource Group — both DCM default and custom — must implement this interface:
+
+```yaml
+resource_group:
+  uuid: 
+  name: 
+  description: 
+  group_class: 
+  group_type: 
+  version: 
+  status:
+    state: 
+    deprecation_date: 
+    sunset_date: 
+    replacement_uuid: 
+    deprecation_reason: 
+    migration_guidance: 
+  nesting:
+    supported: 
+    # If true, this group can contain other groups as members
+    max_depth: 
+  membership:
+    members:
+      - member_uuid: 
+        member_type: 
+        joined_timestamp: 
+        joined_by_uuid: 
+    membership_policy:
+      exclusive: 
+      # If true, a resource can only belong to one group of this type at a time
+      # If false, a resource can belong to multiple groups of this type
+      max_memberships: 
+      # Maximum number of groups of this type a resource can belong to
+      allowed_entity_types:
+        
+  policies:
+    
+  provenance:
+    
+```
+
+### 3.4 Multi-Group Membership
+
+A Resource/Service Entity can belong to multiple Resource Groups across all classes. This multi-dimensional membership is what gives groups their tag-like flexibility.
+
+**Membership constraints are configurable per group definition:**
+- A group can declare `exclusive: true` — meaning a resource can only belong to one group of that type at a time
+- Example: A `RegulatoryScope` group might declare `exclusive: true` — a resource cannot be in both EU-GDPR and US-FISMA regulatory scopes simultaneously
+- Example: A `Deployment` group might declare `exclusive: false` — a resource could participate in multiple deployments
+
+**Policy-governed membership:**
+Organizational policies can further restrict multi-group membership. For example, a sovereignty policy could declare that resources in a PCI-DSS scope cannot be in the same group as resources in a non-PCI scope.
+
+### 3.5 Nesting
+
+Resource Groups that declare `nesting: true` can contain other Resource Groups as members in addition to individual Resource/Service Entities.
+
+**Example nesting structure:**
+```
+Tenant: Payments Platform
+  │
+  └── Resource Group: Deployment — WebApp-v2         (nesting: true)
+        ├── Resource Group: Service — Frontend        (nesting: true)
+        │     ├── Entity: Web Server VM 1
+        │     ├── Entity: Web Server VM 2
+        │     └── Entity: Load Balancer
+        └── Resource Group: Service — Backend         (nesting: true)
+              ├── Entity: App Server VM 1
+              ├── Entity: App Server VM 2
+              └── Entity: Database
+```
+
+**Nesting rules:**
+- Circular nesting is invalid — a group cannot contain itself directly or transitively
+- Nesting depth is declared per group — `max_depth: unlimited` allows arbitrary depth
+- A child group inherits policy scope from parent groups — policies applied to a parent group propagate to all child groups and their members
+- Cost rollup propagates up the nesting hierarchy
+
+---
+
+## 4. DCM System Policies for Resource Grouping
+
+| Policy | Rule | Enforcement |
+|--------|------|-------------|
+| `GRP-001` | Every Resource/Service Entity must belong to exactly one DCM Tenant | Enforced at Entity creation |
+| `GRP-002` | A Resource/Service Entity cannot be removed from its Tenant without being transferred to another Tenant | Enforced at all lifecycle states |
+| `GRP-003` | Circular nesting in Resource Groups is invalid | Enforced at group membership modification |
+| `GRP-004` | Custom Resource Groups must implement the full Resource Group Interface | Enforced at group registration |
+| `GRP-005` | Exclusive membership groups must reject membership requests that violate exclusivity | Enforced at group membership addition |
+
+---
+
+## 5. Grouping and DCM Capabilities
+
+Resource Groups enable the following DCM capabilities at the group scope:
+
+| Capability | Tenant | Resource Group |
+|------------|--------|---------------|
+| Cost Attribution | ✅ Primary | ✅ Rollup within group |
+| Policy Scope | ✅ | ✅ |
+| Drift Detection Scope | ✅ | ✅ |
+| Rehydration Scope | ✅ Full Tenant | ✅ Group and dependencies |
+| Audit Scope | ✅ | ✅ |
+| Placement Constraints | ✅ | ✅ |
+| Sovereignty Boundary | ✅ | ✅ |
+
+---
+
+## 6. Process Resources and Grouping
+
+Process Resource Entities follow the same grouping rules as Infrastructure Resource Entities:
+
+- Must belong to exactly one DCM Tenant — non-overridable
+- Can optionally belong to Resource Groups
+- Typically grouped under the same Deployment or Service group as the resources they operate on
+- Tenant membership ensures cost attribution for execution resources
+- Group membership enables operational scoping — "show me all automation jobs that ran against this Deployment"
+
+---
+
+## 7. Custom Resource Group Registration
+
+Implementors register custom Resource Group types as part of their DCM implementation. Custom group types must declare their full interface implementation:
+
+```yaml
+custom_group_type_registration:
+  uuid: 
+  type_name: 
+  version: 
+  description: 
+  implementing_organization_uuid: 
+  interface_version: 
+  default_membership_policy:
+    exclusive: 
+    max_memberships: 
+  nesting_supported: 
+  allowed_entity_types: 
+  status:
+    state: 
+```
+
+---
+
+## 8. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should there be a DCM-maintained registry of well-known custom group types to encourage standardization? | Interoperability | ✅ Resolved — group_subclass open and advisory; community subclass catalog as non-authoritative reference; no validation or enforcement; see doc 15 (GRP-011) |
+| 2 | How does group membership interact with sovereignty — can a group span sovereignty boundaries? | Sovereignty model | ✅ Resolved — class-specific: tenant_boundary never cross-sovereignty (structural); resource_grouping permitted+policy restriction; policy_collection always permitted; composite governed by most restrictive member; see doc 15 (GRP-012) |
+| 3 | When a Tenant is decommissioned, what happens to its resources and group memberships? | Lifecycle management | ✅ Resolved — four-phase staged decommission: pre-validation → resource decommission → membership cleanup → audit archival; child groups must be resolved first; audit records never destroyed; see doc 15 (GRP-013) |
+| 4 | Should Resource Groups support time-bounded membership — a resource belongs to a group for a defined period? | Operational flexibility | ✅ Resolved — valid_from/expires_at already in Universal Group Model; on_expiry (remove/notify/suspend_member); Lifecycle Constraint Enforcer handles; MEMBER_REMOVE audit record; see doc 15 (GRP-014) |
+| 5 | How are group-level policies inherited by nested child groups — is inheritance opt-in or opt-out? | Policy model | ✅ Resolved — class-specific defaults profile-governed; tenant_boundary: opt_out (standard/prod), opt_in (fsi/sovereign); federation always opt_in; composite opt_out; see doc 15 (GRP-015) |
+
+---
+
+## 9. Related Concepts
+
+- **DCM Tenant** — primary ownership boundary, mandatory for all entities
+- **Resource/Service Entity** — the thing being grouped
+- **Policy Engine** — enforces grouping system policies and evaluates group-level organizational policies
+- **Cost Analysis** — rolls up costs through group hierarchies
+- **Drift Detection** — can be scoped to a group
+- **Rehydration** — can target a group as the unit of reconstruction
+- **Field-Level Provenance** — group membership changes are recorded in entity provenance
+
+
+---
+
+## 10. Cross-Tenant Authorization Lifecycle
+
+Cross-tenant authorizations are the formal mechanism by which one Tenant grants another Tenant access to a shared resource or allocation. They are DCMGroup instances with `group_class: cross_tenant_authorization`.
+
+### 10.1 Creation
+
+Cross-tenant authorizations are created by:
+
+| Actor | Mechanism | When |
+|-------|-----------|------|
+| Granting Tenant Admin | Explicit manual grant via Admin API | Normal cross-tenant sharing setup |
+| Platform Admin | Emergency authorization | Operational incident; requires dual approval in fsi/sovereign |
+| Policy (pre-authorization) | GateKeeper policy auto-creates authorization | Pre-approved sharing patterns |
+
+```yaml
+cross_tenant_authorization:
+  artifact_metadata:
+    uuid: 
+    handle: "xta/networkops/appteam/vlan-100"
+    version: "1.0.0"
+    status: active
+  group_class: cross_tenant_authorization
+  granting_tenant_uuid: 
+  consuming_tenant_uuid: 
+  authorized_resource_types: [Network.VLAN, Network.IPAddressPool]
+  authorized_entity_uuids: []    # null = all resources of declared types
+  duration: P1Y                                   # null = perpetual until revoked
+  expires_at: 
+  created_by: 
+  created_at: 
+  purpose: "AppTeam VMs require VLAN-100 attachment for production network access"
+```
+
+### 10.2 Duration and Renewal
+
+- **Fixed duration** (`duration: P1Y`): expires automatically. Notification sent P30D before expiry. Consuming Tenant must request renewal. If not renewed, enters EXPIRING state, then EXPIRED.
+- **Perpetual** (`duration: null`): active until explicitly revoked. No automatic expiry.
+- **Renewal**: consuming Tenant submits a new authorization request. Granting Tenant approves. New authorization created; old one superseded.
+
+### 10.3 Revocation
+
+```
+Granting Tenant admin revokes authorization
+  │
+  ▼ Authorization status → REVOKED
+  │
+  ▼ Identify active allocations and stakes under this authorization
+  │   All cross-tenant allocations/stakes enter PENDING_REVIEW
+  │
+  ▼ Notifications sent to:
+  │   Consuming Tenant Admin (action required: migrate or release)
+  │   Affected resource owners in consuming Tenant
+  │   Platform Admin (informational)
+  │
+  ▼ Resolution deadline: P30D (configurable; P7D for fsi/sovereign)
+  │
+  ├── Consuming Tenant releases stakes/allocations → authorization closes cleanly
+  └── Deadline exceeded → Platform Admin escalation
+      Policy may declare automatic release on deadline exceeded
+```
+
+### 10.4 What Happens to Active Allocations on Revocation
+
+Resources already allocated under a now-revoked authorization are NOT immediately decommissioned — this would break production workloads. Instead:
+- The allocation/stake relationship enters PENDING_REVIEW
+- The authorization revocation is recorded as the `pending_review_trigger`
+- The consuming Tenant has a grace period to migrate or release
+- Automatic decommission on deadline exceeded is a policy declaration, not a default
+
+### 10.5 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `XTA-001` | Cross-tenant authorizations require explicit creation by the granting Tenant admin, a platform admin, or a pre-authorization policy. They are never implicitly created. |
+| `XTA-002` | Fixed-duration authorizations generate a P30D expiry warning notification. Non-renewal results in EXPIRING then EXPIRED states. |
+| `XTA-003` | Authorization revocation places active allocations and stakes in PENDING_REVIEW with a policy-governed grace period. Resources are not automatically decommissioned on revocation. |
+| `XTA-004` | Automatic decommission of resources on authorization expiry or revocation requires explicit policy declaration. It is not the default behavior. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/09-entity-relationships.md b/content/docs/architecture/data-model/09-entity-relationships.md
new file mode 100644
index 0000000..abe418a
--- /dev/null
+++ b/content/docs/architecture/data-model/09-entity-relationships.md
@@ -0,0 +1,1071 @@
+# DCM Data Model — Entity Relationships
+
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Service Dependencies](07-service-dependencies.md) | [Resource Grouping](08-resource-grouping.md) | [Information Providers](10-information-providers.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + POLICY**
+>
+> Data: relationship records. Policy: Lifecycle Policy output schema
+
+
+
+---
+
+## 1. Purpose
+
+The DCM Entity Relationship model is the **universal mechanism for expressing relationships between any two entities in DCM** — whether between two Resource/Service Entities, between an entity and external business data, or between entities at the service definition level.
+
+A single relationship model is used everywhere. There is no separate binding mechanism for storage, no separate dependency graph structure, no separate business data association mechanism. One model serves all relationship types across the full lifecycle — from pre-realization planning through to post-realization management, drift detection, cost rollup, and rehydration.
+
+This document defines the Entity Relationship Graph, which is the data structure underlying service dependency declaration (doc 07) and rehydration ordering. The Service Dependencies document retains content on rehydration ordering and failure handling, which operate on the relationship graph defined here.
+
+---
+
+## 2. Design Principle
+
+**Single model. Minimum variance. Simple by default.**
+
+The worst outcome is a data model with different mechanisms for expressing similar concepts. Every relationship in DCM — whether a VM requires storage, an application contains a web server, or a resource references a Business Unit — is expressed using the same structure. The only things that vary are the relationship type, role, and nature — all of which are declared fields, not structural differences.
+
+---
+
+## 3. The Universal Relationship Structure
+
+Every relationship is a first-class data object with its own UUID. It is recorded **bidirectionally** — on both participating entities. The same `relationship_uuid` appears on both sides, identifying the relationship itself.
+
+### 3.1 Relationship Record Structure
+
+```yaml
+relationship:
+  relationship_uuid: 
+  
+  # This entity's perspective
+  this_entity_uuid: 
+  this_role: 
+  
+  # The related entity
+  related_entity_uuid: 
+  related_entity_type: 
+  related_entity_role: 
+  
+  # For external entities only
+  information_provider_uuid: 
+  information_type: 
+  lookup_method: 
+  
+  # Relationship semantics
+  relationship_type: 
+  nature: 
+  
+  # Lifecycle policy — for constituent and operational relationships only
+  lifecycle_policy:
+    on_related_destroy: 
+    on_related_suspend: 
+    on_related_modify: 
+  
+  # Metadata
+  version: 
+  status: 
+  created_timestamp: 
+  created_by_uuid: 
+  
+  provenance:
+    
+```
+
+### 3.2 Bidirectional Recording
+
+Every relationship is recorded on both participating entities. The `relationship_uuid` is identical on both sides — it identifies the relationship itself, not one side of it.
+
+**Example — VM requires Storage:**
+
+```yaml
+# On the VM Entity
+relationships:
+  - relationship_uuid: "rel-uuid-001"
+    this_entity_uuid: "vm-uuid-001"
+    this_role: compute
+    related_entity_uuid: "storage-uuid-001"
+    related_entity_type: internal
+    related_entity_role: storage
+    relationship_type: requires
+    nature: constituent
+    lifecycle_policy:
+      on_related_destroy: destroy
+      on_related_suspend: suspend
+      on_related_modify: notify
+
+# On the Storage Entity
+relationships:
+  - relationship_uuid: "rel-uuid-001"
+    this_entity_uuid: "storage-uuid-001"
+    this_role: storage
+    related_entity_uuid: "vm-uuid-001"
+    related_entity_type: internal
+    related_entity_role: compute
+    relationship_type: required_by
+    nature: constituent
+    lifecycle_policy:
+      on_related_destroy: destroy
+      on_related_suspend: suspend
+      on_related_modify: notify
+```
+
+---
+
+## 4. Relationship Types
+
+Relationship types form a fixed standard vocabulary. Every type has an inverse — when you record the relationship on both entities, the type is expressed from each entity's perspective.
+
+| Type | Inverse | Meaning |
+|------|---------|---------|
+| `requires` | `required_by` | This entity cannot function without the related entity |
+| `depends_on` | `dependency_of` | This entity uses the related entity but can degrade without it |
+| `contains` | `contained_by` | This entity is a logical container for the related entity |
+| `references` | `referenced_by` | This entity references the related entity without owning or requiring it |
+| `peer` | `peer` | Equal relationship — neither owns, requires, or contains the other |
+| `manages` | `managed_by` | This entity has lifecycle management authority over the related entity |
+
+---
+
+## 5. Relationship Roles
+
+Roles describe the **function** a related entity serves in a relationship. They are semantic labels that carry meaning for humans and for policy evaluation — they do not affect system behavior directly.
+
+### 5.1 Standard Roles (DCM-defined)
+
+| Role | Description |
+|------|-------------|
+| `compute` | Processing resource — VM, container, bare metal |
+| `storage` | Storage resource — block, object, file |
+| `networking` | Network resource — IP, VLAN, subnet, port |
+| `security` | Security resource — firewall rule, certificate, HSM |
+| `database` | Database resource — relational, NoSQL, time-series |
+| `web` | Web tier resource — web server, reverse proxy, CDN |
+| `app` | Application tier resource — app server, runtime |
+| `cache` | Caching resource — in-memory cache, CDN layer |
+| `queue` | Messaging resource — message queue, event stream |
+| `pipeline` | Pipeline resource — CI/CD, data pipeline |
+| `identity` | Identity resource — service account, credential |
+| `monitoring` | Monitoring resource — metrics, logging, alerting |
+| `business_unit` | Business Unit association |
+| `cost_center` | Cost Center association |
+| `product_owner` | Product Owner association |
+| `regulatory_scope` | Regulatory or compliance scope association |
+
+### 5.2 Custom Roles (extensible)
+
+Organizations register custom roles for domain-specific relationship semantics. Custom roles are semantic labels only — they do not change system behavior. DCM core ignores unknown custom roles in operational decisions but carries them in payloads for downstream consumers.
+
+```yaml
+custom_role_registration:
+  uuid: 
+  name: 
+  description: 
+  registered_by_tenant_uuid: 
+  category: 
+  version: 
+  status: 
+```
+
+---
+
+## 6. Relationship Nature
+
+Nature describes the **structural character** of a relationship — what it means for the entities involved.
+
+| Nature | Meaning | Lifecycle Policy | Example |
+|--------|---------|-----------------|---------|
+| `constituent` | The related entity is a required component of this entity's definition | Required — declared on relationship | VM requires its boot disk |
+| `operational` | The related entity is needed for operation but is not part of the definition | Required — declared on relationship | Web server depends on load balancer |
+| `informational` | The related entity provides context or reference only — no operational dependency | Not applicable | Resource references its Business Unit |
+
+---
+
+## 6a. Relationship Type × Nature Matrix
+
+The two dimensions of every relationship — type and nature — form a matrix of valid combinations. This matrix makes explicit what each combination means semantically and what behavioral rules apply. Not all 18 combinations are valid.
+
+| | `constituent` | `operational` | `informational` |
+|---|---|---|---|
+| **`requires`** | ✅ **Core constituent** — entity cannot function without this component; component is part of its definition. Lifecycle policy required. | ✅ **Hard operational dependency** — entity cannot function without this but it is not a component. Lifecycle policy required. | ⚠️ **Invalid** — if an entity truly requires something, it has an operational or constituent dependency, not merely informational context. |
+| **`depends_on`** | ✅ **Soft constituent** — entity degrades without this component but is not fully broken. Lifecycle policy required. | ✅ **Primary cell for allocated resources** — soft operational dependency. Cross-tenant allocations live here. Lifecycle policy required. | ✅ **Awareness dependency** — entity is aware of and tracks this entity but has no hard operational dependency. No lifecycle policy. |
+| **`contains`** | ✅ **Ownership container** — this entity logically owns and contains the related entity as a component. Lifecycle policy required. | ⚠️ **Rare** — containing something operationally is unusual; most containment is constituent. Use with explicit justification. | ❌ **Invalid** — containing something purely informational has no semantic meaning. |
+| **`references`** | ❌ **Invalid** — a reference implies no ownership or dependency; constituent implies the opposite. | ❌ **Invalid** — if there is an operational dependency, use `depends_on`. A reference that creates operational coupling is mismodeled. | ✅ **Pure informational reference** — primary cell for Business Unit, Cost Center, Product Owner relationships. No lifecycle policy. |
+| **`peer`** | ❌ **Invalid** — peers cannot be constituent components of each other. | ✅ **Operational peers** — equal entities with mutual operational interdependency. Lifecycle policy on each side. | ✅ **Informational peers** — equal entities that are aware of each other. No lifecycle policy. |
+| **`manages`** | ✅ **Component management** — this entity has lifecycle authority over a component it manages. Lifecycle policy required. | ✅ **Operational management** — this entity manages the operations of another entity without owning it. Lifecycle policy required. | ✅ **Audit/reporting management** — management relationship for visibility only. No lifecycle policy. |
+
+**Key behavioral rules derived from the matrix:**
+
+- Any `constituent` or `operational` relationship **must** declare a lifecycle policy (REL-004, REL-008)
+- `constituent` + `requires` is the strongest possible relationship — both the entity and its component are mutually dependent; cross-tenant is prohibited (REL-010)
+- `operational` + `depends_on` is the **allocated resource cell** — this is where cross-tenant allocations are modeled
+- `informational` + `references` is the **business context cell** — Business Unit, Cost Center, Person relationships live here
+- `❌ Invalid` combinations must be rejected by the Policy Engine at request time
+
+---
+
+## 6b. Cross-Tenant Relationships
+
+### 6b.1 The Governing Principle
+
+The relationship **nature** determines whether a cross-tenant relationship is permitted:
+
+| Nature | Cross-Tenant Permitted? | Governing Rule |
+|--------|------------------------|---------------|
+| `constituent` | ❌ Never | REL-010 — DCM System Policy |
+| `operational` | ✅ With explicit dual authorization | REL-011 — both Tenants must authorize |
+| `informational` | ✅ Unless denied by hard tenancy | REL-012 — blocked only by `deny_all` |
+
+### 6b.2 Hard Tenancy Declaration
+
+Tenants declare their cross-tenant relationship policy. This is enforced by the GateKeeper Policy Engine at request time:
+
+```yaml
+tenant:
+  uuid: 
+  hard_tenancy:
+    cross_tenant_relationships: explicit_only
+    # deny_all:            no relationships of any nature may cross this boundary
+    # explicit_only:       ALL cross-tenant must be explicitly authorized (DEFAULT)
+    # operational_permitted: operational cross-tenant permitted; informational requires explicit auth
+    # allow_all:           all cross-tenant permitted — requires justification
+```
+
+**Default is `explicit_only` — informational sharing is not open by default.** Every cross-tenant relationship of any nature requires an explicit `cross_tenant_authorization` record. This closes the model — cross-tenant access must be deliberately granted, not passively permitted.
+
+### 6b.3 DCM System Policies for Cross-Tenant Relationships
+
+| Policy | Rule |
+|--------|------|
+| `REL-010` | Constituent relationships may not cross Tenant boundaries |
+| `REL-011` | Cross-tenant operational relationships require explicit authorization from both the owning Tenant and the consuming Tenant |
+| `REL-012` | A Tenant with `hard_tenancy.cross_tenant_relationships: deny_all` may not participate in any cross-tenant relationship in any direction |
+| `XTA-001` | Cross-tenant information sharing is closed by default — explicit authorization required for all cross-tenant relationships of any nature (see Policy Organization document Section 6) |
+| `XTA-002` | Cross-tenant authorizations must specify who, what, when, and where |
+| `XTA-003` | More specific authorizations take precedence: field_specific > resource_specific > tenant_global |
+| `XTA-004` | All cross-tenant authorization decisions are policy-driven and DCM-enforced |
+| `XTA-005` | Sovereignty constraints declared by either Tenant must be honored by all cross-tenant relationships |
+
+---
+
+## 6c. Allocated Resources — Cross-Tenant Operational Model
+
+### 6c.1 Concept
+
+An **Allocated Resource** is a pre-defined, discrete slice of a parent resource — provisioned by the owning Tenant and made available for consuming Tenants to claim. The allocated resource becomes a **first-class entity** in the consuming Tenant's scope with its own UUID, its own lifecycle, and its own governance — while maintaining a formal `depends_on` + `operational` relationship to the parent resource across the Tenant boundary.
+
+This models real infrastructure practice: the network team pre-carves VLANs, the storage team pre-partitions pools, the platform team pre-defines availability zones. Consumers claim from what is available.
+
+The relationship type is `depends_on` + `operational` — the allocated entity depends on the parent operationally but is not a constituent component of it. The allocation is the relationship; the entity itself is independently governed.
+
+### 6c.2 Parent Resource — Available Allocations
+
+The owning Tenant pre-defines allocations on the parent resource:
+
+```yaml
+parent_resource_entity:
+  uuid: 
+  tenant_uuid: 
+
+  available_allocations:
+    - allocation_uuid: 
+      allocation_type: Network.VLANRange
+      allocation_spec:
+        vlan_range: "100-199"
+        bandwidth: "10Gbps"
+      status: 
+      claimable_by:
+        - tenant_uuid: 
+        - tenant_uuid: 
+        # Empty list = any authorized Tenant may claim
+
+  active_allocations:
+    - allocation_uuid: 
+      claimed_by_tenant_uuid: 
+      claimed_entity_uuid: 
+      claimed_at: 
+      notification_endpoint: 
+      # Parent uses this to notify Tenant A of lifecycle changes
+```
+
+### 6c.3 Allocated Entity — In the Consuming Tenant
+
+When a consuming Tenant claims an available allocation, DCM creates a first-class entity in the consuming Tenant's scope:
+
+```yaml
+allocated_entity:
+  uuid: 
+  entity_type: infrastructure_resource  # ownership_model: allocation
+  resource_type_uuid: 
+  tenant_uuid:   # Belongs to the consuming Tenant
+
+  allocation_spec:
+    vlan_range: "100-199"
+    bandwidth: "10Gbps"
+    # The specific slice allocated to this Tenant
+
+  parent_allocation:
+    parent_entity_uuid: 
+    parent_tenant_uuid: 
+    allocation_uuid: 
+
+  lifecycle_state: OPERATIONAL
+
+  parent_lifecycle_policy:
+    on_parent_destroy: notify_then_detach
+    on_parent_suspend: suspend
+    on_parent_maintenance: notify
+    on_parent_degrade: notify
+    on_parent_capacity_change: notify
+
+  relationships:
+    - relationship_uuid: 
+      related_entity_uuid: 
+      related_entity_type: internal
+      related_entity_tenant_uuid: 
+      relationship_type: depends_on
+      nature: operational
+      cross_tenant: true
+      allocation_uuid: 
+      authorized_by:
+        owning_tenant_policy_uuid: 
+        consuming_tenant_policy_uuid: 
+
+  artifact_metadata:
+    
+```
+
+### 6c.4 Lifecycle Event Propagation
+
+When the parent resource changes state, DCM iterates all active allocations and propagates according to each allocation's `parent_lifecycle_policy`:
+
+```
+Parent resource enters MAINTENANCE
+  │
+  ▼
+DCM iterates active_allocations
+  │
+  For each active allocation:
+  │  Read parent_lifecycle_policy.on_parent_maintenance
+  │  → notify: dispatch lifecycle event to consuming Tenant
+  │  → suspend: transition allocated entity to SUSPENDED state
+  │  → detach: terminate relationship, allocated entity becomes independent
+  │
+  Policy Engine evaluates each propagation:
+  │  SLA commitments that gate maintenance?
+  │  Override policies in consuming Tenant?
+  ▼
+Events dispatched via notification_endpoint on each active_allocation record
+```
+
+### 6c.5 Claiming Flow
+
+```
+Parent Tenant pre-defines available_allocations on parent resource
+  │
+  ▼
+Consuming Tenant A submits claim request
+  │  Specifies: parent_entity_uuid, allocation_uuid
+  ▼
+Policy Engine evaluates:
+  │  Is allocation_uuid still available?
+  │  Is Tenant A in claimable_by list (or list is open)?
+  │  Does Tenant A's cross_tenant policy permit this?
+  │  Does Infrastructure Tenant's cross_tenant policy permit this?
+  ▼
+DCM creates:
+  │  Allocated Entity (owned by Tenant A) with UUID
+  │  depends_on / dependency_of relationship (bidirectional, cross_tenant: true)
+  │  Updates parent's available_allocation status: available → claimed
+  │  Adds record to parent's active_allocations
+  │  Provenance recorded on both entities
+  ▼
+Infrastructure Tenant owner notified of new claim
+  │  Via owned_by.notification_endpoint on the parent entity
+```
+
+---
+
+## 7. Lifecycle Policies
+
+Lifecycle policies declare what happens to an entity when its related entity changes state. They apply to `constituent` and `operational` relationships only — `informational` relationships have no lifecycle implications.
+
+### 7.1 Policy Actions
+
+| Action | Meaning |
+|--------|---------|
+| `destroy` | Destroy this entity when the related entity is destroyed |
+| `retain` | Keep this entity when the related entity is destroyed — it becomes independent |
+| `detach` | Detach this entity from the relationship — relationship terminated, entity retained |
+| `notify` | Notify appropriate personas and trigger Policy Engine evaluation — no automatic action |
+| `suspend` | Suspend this entity when the related entity is suspended |
+| `cascade` | Cascade the change from the related entity to this entity |
+| `ignore` | Take no action — the change to the related entity does not affect this entity |
+
+### 7.2 Lifecycle Action Hierarchy — Save Overrides Destroy
+
+When a shared resource has multiple active relationships and a lifecycle event triggers, each relationship may produce a different action recommendation. DCM resolves conflicts using a deterministic hierarchy — **the most conservative action always wins**:
+
+```
+retain        ← most conservative — entity preserved unconditionally
+  │
+notify        ← inform and wait — human decision required
+  │
+suspend       ← temporarily inactive — reversible
+  │
+detach        ← relationship released — entity becomes independent
+  │
+cascade       ← propagate state change from related entity
+  │
+destroy       ← least conservative — entity terminated
+```
+
+**The save_overrides_destroy rule (REL-018):** If any active relationship recommends `retain`, the entity is retained regardless of what any other relationship recommends — including relationships with `override: immutable` lifecycle policies. `retain` is the save. It always beats `destroy`.
+
+This rule applies automatically and silently when the hierarchy resolves cleanly (e.g., `retain` beats `destroy`). It is recorded in the `lifecycle_conflict_record` with severity `info` for audit purposes but requires no notification.
+
+### 7.3 Lifecycle Conflict Detection
+
+**Not all multi-recommendation scenarios are conflicts.** The hierarchy resolves most cases deterministically. A conflict worth surfacing occurs when:
+
+1. **Adjacent hierarchy levels** — two relationships recommend actions that are one step apart (e.g., `notify` vs `suspend`) — the hierarchy resolves it but the ambiguity is worth surfacing
+2. **An immutable lifecycle lock couldn't be honored** — a GateKeeper set `on_related_destroy: destroy` with `immutable_ceiling: absolute` but `retain` from another relationship won per REL-018
+3. **`notify` is the winning action** — inherently means human decision required; the notification should include the full conflict picture
+
+**Conflict severity:**
+
+| Scenario | Severity | Action |
+|----------|---------|--------|
+| `retain` beats `destroy` — non-adjacent levels | `info` | Logged only — working as designed |
+| All relationships agree | None | No record needed |
+| Adjacent levels (e.g., `notify` vs `suspend`) | `warning` | Notify entity owner and affected policy owners |
+| `notify` is the winning action | `warning` | Notify owner — human decision required |
+| Immutable lifecycle lock overridden by REL-018 | `critical` | Notify entity owner, policy owner, and platform admin |
+
+**Lifecycle conflict record:**
+
+```yaml
+lifecycle_conflict_record:
+  entity_uuid: 
+  event_trigger: 
+  triggering_entity_uuid: 
+  action_recommendations:
+    - relationship_uuid: 
+      related_entity_uuid: 
+      recommended_action: destroy
+      source: lifecycle_policy
+    - relationship_uuid: 
+      related_entity_uuid: 
+      recommended_action: retain
+      source: gatekeeper_policy
+      policy_uuid: 
+    - relationship_uuid: 
+      related_entity_uuid: 
+      recommended_action: notify
+      source: lifecycle_policy
+  resolved_action: retain
+  resolution_rule: save_overrides_destroy
+  conflict_detected: true
+  conflict_severity: info
+  notifications_sent:
+    - recipient_uuid: 
+      message: "Lifecycle conflict resolved: retain overrode destroy and notify."
+  recorded_at: 
+```
+
+### 7.4 Lifecycle Policy Authority Hierarchy
+
+Lifecycle policies follow the same three-tier authority model as override control:
+
+```
+Resource Type Specification default (lowest — portable default)
+  │
+  ▼
+Provider Catalog Item default (provider preference)
+  │
+  ▼
+Consumer declaration (at request time — within Resource Type bounds)
+  │
+  ▼
+DCM System Policy (non-overridable — sovereignty and compliance mandates)
+```
+
+**Example:** A DCM System Policy might declare that all storage entities in a PCI-DSS scope must `retain` when their parent VM is destroyed — regardless of what the provider default or consumer declared.
+
+---
+
+## 7a. Shared Resource Model — Same-Tenant
+
+### 7a.1 Concept
+
+A **Shared Resource** is an entity within a single Tenant that has active relationships from multiple parent entities. Rather than being exclusively owned by one parent, it is referenced by N parents — each with its own lifecycle relationship.
+
+This is the same-tenant counterpart to the cross-tenant Allocated Resource model. Both use reference counting to defer destructive actions. The sharing model applies within a Tenant; the allocation model applies across Tenant boundaries.
+
+**Examples:** Shared NFS volume mounted by multiple VMs. Shared database cluster used by multiple application services. Shared VLAN used by multiple VMs. Shared TLS certificate used by multiple services.
+
+### 7a.2 The `sharing_model` Declaration
+
+The Resource Type Specification declares whether instances of a type can be shared. Individual entities carry the runtime sharing state:
+
+```yaml
+# On the Resource Type Specification
+resource_type_spec:
+  fully_qualified_name: Storage.SharedVolume
+  shareability:
+    allowed: true
+    default_sharing_scope: tenant    # tenant | cross_tenant
+    max_active_relationships: null   # null = unlimited; integer = cap (e.g., license seats)
+
+# On the entity instance
+entity:
+  uuid: 
+  sharing_model:
+    shareable: true
+    sharing_scope: tenant
+    active_relationship_count: 3     # DCM maintains this — do not set manually
+    minimum_relationship_count: 0    # below this, on_last_relationship_released fires
+    on_last_relationship_released: 
+    # destroy: entity destroyed when last relationship is released
+    # retain:  entity persists independently — becomes unowned
+    # notify:  notify owner, entity enters PENDING_DECISION
+```
+
+**`shareability.allowed: false`** on a Resource Type (e.g., `Compute.BootDisk`) means the Policy Engine rejects any attempt to create a second active constituent or operational relationship to an instance. Boot disks, primary network interfaces, and similar exclusively-owned resources are non-shareable by type definition (REL-017).
+
+### 7a.3 Reference Count Lifecycle
+
+DCM maintains `active_relationship_count` automatically:
+
+- **Relationship created** → `active_relationship_count` incremented
+- **Relationship released** (parent decommissioned, relationship detached) → `active_relationship_count` decremented
+- **Informational relationships** → never counted (REL-016)
+- **Count reaches `minimum_relationship_count`** → `on_last_relationship_released` fires
+
+When a parent entity is destroyed and has a relationship to a shared resource:
+
+```
+Parent entity destroyed
+  │
+  ▼
+DCM collects action recommendations from all active relationships on shared resource
+  │  Each relationship's lifecycle policy produces one recommendation
+  │  Informational relationships excluded
+  │
+  ▼
+Action resolution — save_overrides_destroy hierarchy (REL-018)
+  │  Most conservative recommendation wins
+  │  Lifecycle conflict record created if multiple recommendations differ
+  │
+  ▼
+Execute winning action
+  │  retain → shared resource unaffected
+  │  notify → PENDING_DECISION state, notifications dispatched
+  │  suspend → shared resource suspended
+  │  detach → parent's relationship released, count decremented
+  │  destroy → only if count reaches minimum_relationship_count (REL-015)
+  │
+  ▼
+Deferred destruction record created (if action was deferred)
+```
+
+### 7a.4 Deferred Destruction Records
+
+Every time a destructive action is deferred by the reference count mechanism:
+
+```yaml
+deferred_destruction_record:
+  entity_uuid: 
+  triggering_request_uuid: 
+  triggering_relationship_uuid: 
+  relationship_count_before: 3
+  relationship_count_after: 2
+  action_taken: deferred
+  reason: "active_relationship_count above minimum. Destruction deferred."
+  remaining_relationships:
+    - relationship_uuid: 
+      related_entity_uuid: 
+      relationship_type: required_by
+    - relationship_uuid: 
+      related_entity_uuid: 
+      relationship_type: dependency_of
+  recorded_at: 
+```
+
+When the last relationship is released:
+
+```yaml
+deferred_destruction_record:
+  relationship_count_before: 1
+  relationship_count_after: 0
+  action_taken: "on_last_relationship_released → destroy"
+  reason: "Last active relationship released. Executing on_last_relationship_released."
+  recorded_at: 
+```
+
+### 7a.5 Unified with the Allocated Resource Model
+
+The same-tenant sharing model and the cross-tenant allocated resource model are the same concept at different scopes:
+
+| Dimension | Same-Tenant Sharing | Cross-Tenant Allocation |
+|-----------|--------------------|-----------------------|
+| Scope | Within one Tenant | Across Tenant boundaries |
+| Pre-definition | Not required — relationships declared at request time | Parent pre-defines `available_allocations` |
+| Reference tracking | `active_relationship_count` on entity | `active_allocations` list on parent |
+| Destruction deferral | Deferred until count reaches minimum | Deferred until last allocation released |
+| Lifecycle events | `on_last_relationship_released` | `parent_lifecycle_policy` per allocation |
+| Governed by | REL-015 through REL-019 | REL-011, REL-014 |
+
+---
+
+## 8. Relationship Declarations — Where They Live
+
+Relationship declarations exist at multiple levels, each building on the previous:
+
+### 8.1 Resource Type Specification (structural ceiling)
+
+Declares what relationships are **possible** for a resource type. Sets the ceiling — lower levels can only declare relationships within these bounds.
+
+```yaml
+resource_type: Compute.VirtualMachine
+possible_relationships:
+  - role: storage
+    relationship_type: requires
+    nature: constituent
+    permitted_related_types:
+      - Storage.Block
+      - Storage.File
+    default_lifecycle_policy:
+      on_related_destroy: destroy
+      on_related_suspend: suspend
+    binding_types_permitted: [owned, referenced]
+    consumer_declarable: true
+    # Consumer can declare binding_type and lifecycle_policy override
+
+  - role: networking
+    relationship_type: requires
+    nature: constituent
+    permitted_related_types:
+      - Network.IPAddress
+    default_lifecycle_policy:
+      on_related_destroy: destroy
+    consumer_declarable: false
+    # DCM manages this automatically — consumer cannot override
+```
+
+### 8.2 Catalog Item (offering-specific)
+
+Declares the **actual relationships** for a specific curated offering. Can only be more restrictive than the Resource Type Specification.
+
+```yaml
+catalog_item: Production VM
+relationships:
+  - role: storage
+    relationship_type: requires
+    nature: constituent
+    related_catalog_item_uuid: 
+    lifecycle_policy:
+      on_related_destroy: retain
+      # Overrides Resource Type default of destroy
+      # Storage persists even if VM is destroyed — production data protection
+    binding_type: owned
+```
+
+### 8.3 Request Time (consumer-declared)
+
+The consumer declares relationships in their request. Bundled declarations (storage fields within a VM request) are automatically expanded into relationship records by the Request Payload Processor.
+
+```yaml
+# Explicit relationship declaration in a request
+request:
+  resource_type: Compute.VirtualMachine
+  # ... other fields ...
+  relationships:
+    - role: storage
+      relationship_type: requires
+      binding_type: referenced
+      related_entity_uuid: 
+      # Consumer referencing existing storage — not creating new
+
+# Bundled declaration — expanded automatically
+request:
+  resource_type: Compute.VirtualMachine
+  storage:
+    disks:
+      - name: boot
+        capacity: 100GB
+        # Processor expands this into a Storage Entity stub
+        # and a relationship record with binding_type: owned
+```
+
+### 8.4 External Data Relationships
+
+Relationships to external data entities follow the same structure with `related_entity_type: external`:
+
+```yaml
+# On a VM Entity — relationship to external Business Unit
+relationships:
+  - relationship_uuid: 
+    this_entity_uuid: 
+    this_role: 
+    related_entity_uuid: 
+    related_entity_type: external
+    information_provider_uuid: 
+    information_type: Business.BusinessUnit
+    relationship_type: references
+    role: business_unit
+    nature: informational
+    lookup_method: primary_key
+```
+
+---
+
+## 9. Bundled Declaration Expansion
+
+When a consumer includes resource configuration as bundled fields (e.g., storage within a VM request), the Request Payload Processor expands these into first-class entities and relationship records.
+
+### 9.1 Expansion Process
+
+```
+Consumer submits bundled VM request with storage fields
+  │
+  ▼
+Request Payload Processor
+  │  Reads expansion rules from Resource Type Specification
+  │  For each expandable field:
+  │    1. Creates a Resource/Service Entity stub (PENDING state)
+  │       with its own UUID, Tenant membership, Resource Type
+  │    2. Creates a Relationship record on both the parent stub
+  │       and the child stub
+  │    3. Applies lifecycle policy from:
+  │       consumer declaration → provider default → Resource Type default
+  │       → DCM System Policy override
+  │    4. Adds the child entity stub to the relationship graph
+  ▼
+Policy Engine validates:
+  │  Binding type is permitted by Resource Type Specification
+  │  Consumer has override_matrix permission to declare binding type
+  │  Lifecycle policy is not overridden by a DCM System Policy
+  ▼
+Service Provider receives:
+  │  Parent entity request payload
+  │  Child entity stub UUIDs embedded in parent payload
+  │  Provisions resources natively
+  │  Returns realized payloads for all entities in DCM unified format
+  ▼
+DCM updates:
+  │  Parent entity: PENDING → REALIZED
+  │  Child entities: PENDING → REALIZED
+  │  All relationship records: status → active
+  │  Full provenance recorded on all entities and relationships
+```
+
+### 9.2 Expansion Rules in Resource Type Specification
+
+The expansion rule declares which fields expand into entities and how:
+
+```yaml
+field_definition:
+  field_name: storage
+  type: object
+  expansion:
+    expand_to_entity: true
+    entity_resource_type_uuid: 
+    entity_resource_type_name: Storage.Block
+    default_binding_type: owned
+    binding_types_permitted: [owned, referenced]
+    default_lifecycle_policy:
+      on_related_destroy: destroy
+      on_related_suspend: suspend
+    consumer_can_override_lifecycle: true
+    consumer_can_override_binding_type: true
+```
+
+---
+
+## 10. The Entity Relationship Graph
+
+All relationships across all entities form a traversable **Entity Relationship Graph** — the complete map of how all entities in DCM relate to each other.
+
+### 10.1 Graph Properties
+
+- Every node is a Resource/Service Entity (internal or external reference)
+- Every edge is a Relationship with a UUID
+- The graph is bidirectional — traversable from any node in any direction
+- Every node exists exactly once — shared entities appear once with multiple relationship edges
+- Circular relationships are invalid and must be rejected
+
+### 10.2 Graph and the Four States
+
+The relationship graph exists across all four states:
+
+| State | Graph Role |
+|-------|-----------|
+| Intent State | Graph declared at request time — nodes are intent stubs |
+| Requested State | Graph fully assembled — nodes are PENDING entity stubs with UUIDs |
+| Realized State | Graph populated — nodes are REALIZED entities with full provenance |
+| Discovered State | Graph used for comparison — discovered entities matched against realized graph |
+
+### 10.3 Graph Applications
+
+| Application | How the Graph is Used |
+|-------------|----------------------|
+| **Rehydration** | Full graph traversal from a root entity — all related entities identified and realized in dependency order |
+| **Cost Rollup** | Graph traversal accumulates costs across all related constituent entities |
+| **Drift Detection** | Discovered State graph compared against Realized State graph — structural and data differences identified |
+| **Decommission** | Graph traversal determines decommission order — lifecycle policies applied at each edge |
+| **Placement** | Pre-realization graph used to understand full resource footprint for placement decisions |
+| **Impact Analysis** | Graph traversal from any node identifies all entities affected by a change |
+
+---
+
+## 11. Relationship Integrity
+
+### 11.1 DCM System Policies for Relationships
+
+| Policy | Rule |
+|--------|------|
+| `REL-001` | Every relationship must have a UUID |
+| `REL-002` | Every relationship must be recorded on both participating entities |
+| `REL-003` | Circular relationships are invalid and must be rejected |
+| `REL-004` | A constituent or operational relationship must have a lifecycle policy declared somewhere in the authority chain before provider dispatch |
+| `REL-005` | External relationships must reference a registered Information Provider |
+| `REL-006` | Relationship types must be from the standard vocabulary |
+| `REL-007` | Consumer-declared binding types must be permitted by the Resource Type Specification |
+| `REL-008` | A constituent relationship lifecycle policy may not be set to `ignore` for `on_related_destroy` |
+| `REL-009` | Lifecycle policy conflicts between policies are resolved by the standard Policy Engine authority hierarchy — no special case |
+| `REL-010` | Constituent relationships may not cross Tenant boundaries |
+| `REL-011` | Cross-tenant operational relationships require explicit authorization from both the owning Tenant and the consuming Tenant |
+| `REL-012` | A Tenant with `hard_tenancy.cross_tenant_relationships: deny_all` may not participate in any cross-tenant relationship in any direction |
+| `REL-013` | `❌ Invalid` relationship type × nature combinations (per the matrix in Section 6a) must be rejected by the Policy Engine at request time |
+| `REL-014` | An allocated resource claim requires a matching `available` allocation record on the parent entity |
+| `REL-015` | A destructive lifecycle action on a shared resource entity (`ownership_model: shareable` (see [Ownership, Sharing, and Allocation](04b-ownership-sharing-allocation.md))) is deferred until `active_relationship_count` reaches `minimum_relationship_count` |
+| `REL-016` | Informational relationships do not contribute to `active_relationship_count` on shared resource entities |
+| `REL-017` | A Resource Type Specification with `shareability.allowed: false` must reject any attempt to create more than one active constituent or operational relationship to an instance of that type |
+| `REL-018` | When a lifecycle event produces multiple action recommendations on a shared resource, the most conservative action wins per the hierarchy: `retain > notify > suspend > detach > cascade > destroy` (save_overrides_destroy) |
+| `REL-019` | When lifecycle action recommendations conflict, a `lifecycle_conflict_record` is created. Conflicts at `warning` or `critical` severity trigger notifications to the entity owner and affected policy owners |
+
+### 11.2 Lifecycle Policy Conflict Resolution
+
+Lifecycle policy fields on relationships are fields. They carry the same `override` metadata, the same provenance obligations, and resolve under the same Policy Engine authority hierarchy as any other field in DCM. There is no special case — minimum variance applies.
+
+**Authority chain for a relationship lifecycle policy field (lowest to highest):**
+
+```
+Resource Type Specification default
+  → Provider Catalog Item default
+    → Consumer declaration at request time
+      → Transformation Policy (may set override: constrained)
+        → Validation Policy (checks — no modification)
+          → GateKeeper Policy (may set override: immutable)
+            → DCM System Policies REL-008, REL-009 (non-overridable)
+```
+
+**Within the Policy Engine**, the priority schema governs conflicts between policies at the same tier. Highest numeric priority value within a tier runs first. The first policy to set `override: immutable` on a lifecycle policy field locks it — all subsequent policies in that execution find it locked and cannot modify it.
+
+**Conflict detection at ingestion** applies to lifecycle policy declarations in policies exactly as it does to layer fields:
+- Two policies both declare `on_related_destroy` for the same relationship type without priority differentiation → CONFLICT ERROR at ingestion — both owners notified
+- One has higher priority value → Higher wins, documented in provenance
+- Equal priority → CONFLICT ERROR
+
+**`immutable_ceiling: absolute` applies here.** A sovereign compliance mandate that storage must always be retained when a VM is destroyed — `on_related_destroy: retain` with `immutable_ceiling: absolute` — cannot be overridden by any future policy regardless of priority.
+
+**Example — compliant lifecycle policy field with override control:**
+
+```yaml
+lifecycle_policy:
+  on_related_destroy:
+    value: retain
+    metadata:
+      override: immutable
+      locked_by_policy_uuid: 
+      locked_at_level: global
+      basis_for_value: "Compliance mandate — storage must outlive VM for audit retention"
+      immutable_ceiling: absolute
+    provenance:
+      origin:
+        source_type: policy
+        source_uuid: 
+        timestamp: 
+      modifications: []
+```
+
+### 11.2a Cross-Tenant Dependency System Policies
+
+| Policy | Rule |
+|--------|------|
+| `DEP-001` | Cross-tenant constituent dependencies are prohibited — a dependency that would produce a constituent cross-tenant relationship is rejected at dependency graph construction time |
+| `DEP-002` | Cross-tenant operational dependencies require a valid available allocation record on the target resource — failure returns `CROSS_TENANT_DEPENDENCY_UNAVAILABLE` |
+| `DEP-003` | A Resource Type Specification may only declare cross-tenant dependencies if explicitly marked `cross_tenant: permitted` — default is `cross_tenant: not_permitted` |
+
+### 11.3 Relationship Versioning and Deprecation
+
+Relationships follow the universal versioning and deprecation model. A relationship version changes when its lifecycle policy, nature, or role changes. Terminated relationships are retained in provenance permanently.
+
+---
+
+## 12. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | How are relationship conflicts resolved — two policies declare different lifecycle policies for the same relationship? | Policy model | ✅ Resolved — standard Policy Engine authority hierarchy; REL-008 and REL-009 |
+| 2 | Should relationship roles be validated against the role registry at request time, or is validation advisory? | Operational complexity | ✅ Resolved — advisory default; Resource Type Spec may declare permitted_relationship_roles with role_validation: advisory/enforced; community role catalog; see doc 09 Section 12 (REL-020) |
+| 3 | How does the relationship graph interact with multi-tenant scenarios — can a relationship cross Tenant boundaries? | Multi-tenancy | ✅ Resolved — nature governs; constituent never; operational with dual auth; informational unless deny_all; REL-010/011/012 |
+| 4 | Should there be a maximum relationship graph depth to prevent runaway complexity? | Operational governance | ✅ Resolved — profile-governed max depth: 15 standard/prod, 10 fsi/sovereign; circular detection always enforced; depth = traversal distance; see doc 09 Section 12 (REL-021) |
+| 5 | How are shared entities represented in the relationship graph — an entity required by multiple parents? | Graph model | ✅ Resolved — sharing_model declaration; active_relationship_count; save_overrides_destroy hierarchy (REL-018); lifecycle_conflict_record; REL-015 through REL-019 |
+
+
+---
+
+## 14. Notification Traversal Rules
+
+The entity relationship graph is the source of truth for notification audiences. This section defines how relationships govern notification traversal for the Notification Model (doc 23).
+
+### 14.1 Relationship Properties Relevant to Notifications
+
+Every relationship carries two properties that the Notification Router uses for audience resolution:
+
+```yaml
+relationship:
+  type: attached_to
+  stake_strength: 
+  notification_relevance:
+    # Declared in the Resource Type Spec for this relationship type
+    # Can be overridden per relationship instance
+    notifiable_events: [entity.decommissioning, entity.state_changed, entity.ttl_expired]
+    traversal_depth: 1               # how many hops from this relationship
+    audience_role: stakeholder       # role assigned to notified party
+```
+
+### 14.2 Stake Strength and Notification Threshold
+
+Different event types use different minimum stake strengths for notification:
+
+| Event Category | Minimum Stake Strength | Rationale |
+|---------------|----------------------|-----------|
+| `entity.decommissioning` | optional | All stakeholders should know |
+| `entity.decommissioned` | optional | All stakeholders should know |
+| `entity.state_changed` (to FAILED/DEGRADED) | required | Only required stakeholders are affected |
+| `entity.state_changed` (to OPERATIONAL) | preferred | Recovery notification broader |
+| `entity.ttl_expired` | required | Only required stakeholders need to act |
+| `drift.detected` | — (owner only) | Drift is the owner's concern |
+| `dependency.state_changed` | required | Only affects required dependents |
+
+The minimum stake strength threshold per event type is declared in the resource type specification and can be overridden by a platform-domain policy.
+
+### 14.3 Notification Traversal and Graph Depth
+
+Notification traversal respects the same depth limits as other graph operations (REL-021: max depth 15 standard/prod, 10 fsi/sovereign). However, notification traversal depth is typically much shallower — most event types only traverse depth 1 (direct relationships).
+
+```
+VLAN-100 decommissioning (depth 1 traversal):
+  Direct relationships:
+    ├── VM-A (attached_to, required) → AppTeam notified as stakeholder
+    ├── VM-B (attached_to, required) → DevTeam notified as stakeholder
+    └── VM-C (attached_to, optional) → OpsTeam notified as observer
+  No depth-2 traversal — VM-A's dependencies are not notified about VLAN changes
+```
+
+Security events (sovereignty violation, audit chain break) use depth 0 (system audiences only — no relationship traversal needed).
+
+### 14.4 Notification Traversal Policies
+
+| Policy | Rule |
+|--------|------|
+| `REL-022` | Notification traversal follows relationship edges from the changed entity. Traversal depth per event type is declared in the Resource Type Specification. Default traversal depth is 1. |
+| `REL-023` | Notification traversal respects sovereignty boundaries. Cross-tenant notifications carry only content authorized for the receiving Tenant. |
+| `REL-024` | The same actor reached via multiple relationship paths receives a single notification with all applicable audience_roles listed. |
+
+
+---
+
+## 13. Related Concepts
+
+- **Entity Relationship Graph** — the complete traversable graph of all entity relationships in DCM
+- **Information Provider** — provider type for external data entities referenced in relationships
+- **Bundled Declaration Expansion** — processor mechanism for expanding bundled fields into entities and relationships
+- **Lifecycle Policy** — declares what happens to an entity when its related entity changes state
+- **Service Dependencies** — document covering rehydration ordering and failure handling on the relationship graph
+- **Resource Type Specification** — declares possible relationships for a resource type
+- **External Entity Reference** — stable pointer to data owned by an external system
+
+
+## 12. Relationship Gap Resolutions — Q58 and Q60
+
+### 12.1 Relationship Role Validation (Q58)
+
+Relationship roles are semantic labels — human-readable identifiers for the function a member plays in a relationship. By default, role validation is advisory. Resource Type Specifications may declare a closed set of permitted roles with enforced validation.
+
+```yaml
+resource_type_spec:
+  fully_qualified_name: Compute.VirtualMachine
+  permitted_relationship_roles:
+    - role: storage
+      relationship_types: [requires]
+      permitted_related_types: [Storage.Block, Storage.File]
+    - role: networking
+      relationship_types: [requires]
+      permitted_related_types: [Network.IPAddress, Network.Port]
+    - role: dns
+      relationship_types: [depends_on]
+      permitted_related_types: [DNS.Record]
+    - role: load_balancer
+      relationship_types: [depends_on]
+      permitted_related_types: [Network.LoadBalancer]
+  role_validation: advisory   # advisory | enforced
+  # advisory: unknown roles produce a warning in assembly provenance
+  # enforced: unknown roles are rejected at request time
+```
+
+**Community role catalog:** DCM ships a non-authoritative reference list of commonly-used roles. Organizations freely declare roles not in the catalog when role_validation is advisory.
+
+### 12.2 Maximum Relationship Graph Depth (Q60)
+
+Relationship graph depth is limited to a profile-governed maximum. Circular relationship detection is always enforced regardless of depth configuration.
+
+```yaml
+relationship_depth_policy:
+  max_depth: 15                  # configurable via Policy Group
+  on_max_exceeded: reject        # reject with clear error
+  cycle_detection: always        # non-configurable — always enforced
+  # Depth = maximum traversal distance between any two entities
+  # NOT the count of relationships on one entity
+```
+
+**Profile-governed defaults:**
+
+| Profile | Max Depth | Rationale |
+|---------|----------|-----------|
+| `minimal` | 25 | Home lab — free composition |
+| `dev` | 20 | Development — generous |
+| `standard` | 15 | Production baseline |
+| `prod` | 15 | Production |
+| `fsi` | 10 | Tighter — complex graphs harder to audit |
+| `sovereign` | 10 | Maximum control |
+
+**Note:** Relationship depth differs from dependency depth (ENT-008). Dependency depth counts the provisioning chain. Relationship depth counts the graph traversal distance between any two entities. A VM with 50 IP address relationships has depth 1, not 50.
+
+---
+
+## 13. System Policies — Relationship Gaps
+
+| Policy | Rule |
+|--------|------|
+| `REL-020` | Relationship roles are semantic labels. Resource Type Specifications may declare permitted_relationship_roles with advisory or enforced validation. Advisory produces assembly warnings for unknown roles. Enforced rejects unknown roles at request time. DCM maintains a community role catalog as a non-authoritative reference. |
+| `REL-021` | Relationship graph depth is limited to a profile-governed maximum (default: 15 for standard/prod; 10 for fsi/sovereign). Circular relationship detection is always enforced regardless of depth configuration. Depth is measured as the maximum traversal distance between any two entities in the relationship graph. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
\ No newline at end of file
diff --git a/content/docs/architecture/data-model/10-information-providers.md b/content/docs/architecture/data-model/10-information-providers.md
new file mode 100644
index 0000000..323dc05
--- /dev/null
+++ b/content/docs/architecture/data-model/10-information-providers.md
@@ -0,0 +1,446 @@
+# DCM Data Model — Information Providers
+
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Entity Relationships](09-entity-relationships.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: PROVIDER**
+>
+> The Provider abstraction — Information Provider capability extension
+
+
+
+---
+
+## 1. Purpose
+
+An **Information Provider** is a registered DCM provider that serves as the authoritative source for a specific category of data that DCM needs to reference but does not own. It exposes external data to DCM through a standard interface, enabling DCM to look up, verify, and relate external records without caching or owning them.
+
+Information Providers are a first-class provider type in DCM alongside Service Providers and compound service definitions. They follow the same registration, health check, trust, and contract model as Service Providers — adapted where applicable to the lookup-only nature of information retrieval.
+
+---
+
+## 2. Why Information Providers Exist
+
+DCM manages the lifecycle of resources it provisions. But resources exist in a broader organizational context — they are owned by business units, attributed to cost centers, associated with product owners, governed by regulatory scopes. This contextual data lives in authoritative external systems (HR systems, finance systems, CMDBs, ITSM tools) that DCM does not and should not own.
+
+Without a formal model for referencing external data, organizations face two bad choices:
+- **Copy the data into DCM** — creating duplication, staleness, and an ownership conflict with the authoritative system
+- **Ignore the data** — losing business context, cost attribution, and compliance traceability
+
+Information Providers solve this by giving DCM a standard, stable, governed interface to external data without requiring ownership transfer.
+
+---
+
+## 3. Information Provider in the Provider Ecosystem
+
+DCM defines eleven provider types, all implementing the unified Provider base contract. This document focuses on the Information Provider type. For the complete provider type list see [A-provider-contract.md](A-provider-contract.md).
+
+| Provider Type | Purpose | Data Direction | DCM Owns Result? |
+|--------------|---------|---------------|-----------------|
+| **Service Provider** | Executes work, realizes resources | DCM → Provider → DCM | Yes — DCM owns the realized entity |
+| **Information Provider** | Serves authoritative external data | DCM → Provider (lookup only) | No — external system is authoritative |
+| **compound service definition** | Composes multiple providers | DCM → Meta → Child Providers → DCM | Yes — DCM owns the composite result |
+
+---
+
+## 4. Information Provider Contract
+
+Information Providers follow the same provider contract model as Service Providers where applicable. The contract dimensions are:
+
+### 4.1 Registration Contract
+Same model as Service Providers. Information Providers register with DCM declaring their endpoint, the information types they implement, their lookup capabilities, and their extended schema.
+
+### 4.2 Health Check Contract
+Same model as Service Providers. Information Providers expose a `/health` endpoint. DCM polls it on the same configurable interval. `Ready`/`NotReady` state machine applies. An `NotReady` Information Provider is excluded from lookups — relationships referencing it are flagged for on-demand verification fallback.
+
+### 4.3 Trust Contract
+Same model as Service Providers. Information Providers must be registered, validated, and certified before DCM will accept their data. The chain of trust applies to data returned by Information Providers — provenance records the provider UUID for every field sourced from an Information Provider.
+
+### 4.4 Capacity Contract
+Adapted for lookup capacity rather than resource provisioning capacity. Information Providers declare and report their query capacity — requests per second, rate limits, availability windows.
+
+```yaml
+capacity_registration:
+  provider_uuid: 
+  registration_timestamp: 
+  capacity_by_information_type:
+    - information_type_uuid: 
+      queries_per_second: 1000
+      rate_limit_window: 60s
+      availability: 99.9%
+```
+
+### 4.5 Lifecycle Event Contract
+Same model as Service Providers. Information Providers have a contractual obligation to notify DCM when records they have provided references for change status. DCM receives the notification and updates the external entity reference record accordingly.
+
+**Reportable event types for Information Providers:**
+
+| Event Type | Description | DCM Response |
+|------------|-------------|--------------|
+| `RECORD_DEACTIVATED` | A referenced record has been deactivated | Update reference status, Policy Engine evaluation |
+| `RECORD_MERGED` | Two records merged — UUID may change | Update external_uuid in reference record |
+| `RECORD_SPLIT` | One record split into multiple | Policy Engine evaluation — which new record applies? |
+| `UUID_CHANGED` | Record UUID changed in external system | Update external_uuid, re-verify all references |
+| `DATA_UPDATED` | Standard field values changed | Update last_verified, notify relationships |
+| `PROVIDER_DEGRADED` | Provider is degraded but operational | DCM flags affected references for on-demand verification |
+
+### 4.6 Naturalization/Denaturalization Contract
+Information Providers translate their native data format (HR system JSON, finance system XML, LDAP records, REST APIs) into the DCM unified data model format. The translation is the provider's responsibility — DCM always receives data in DCM format.
+
+---
+
+## 5. Standard vs Extended Data
+
+### 5.1 Standard Data (DCM-defined)
+
+Fields that are part of the DCM-specified schema for an information type. DCM core uses these fields for lookups, relationship matching, policy evaluation, and display. They are portable across all implementations of that information type.
+
+DCM only relies on standard data for operational decisions. Extended data is carried in the payload but is not used for DCM core operations.
+
+### 5.2 Extended Data (organization-defined)
+
+Additional fields organizations add to enrich the standard schema for their specific needs. Declared in the provider's extended schema registration. DCM carries extended data in the payload for downstream consumers — policy engines, cost analysis tools, reporting — that know how to use them.
+
+```yaml
+# Standard + Extended data example — Business.BusinessUnit
+business_unit_record:
+  # Standard fields — DCM defined, used for lookups
+  uuid: "bu-uuid-001"
+  name: "Payments Platform"
+  code: "BU-PAY"
+  parent_uuid: "bu-uuid-root"
+  organization_uuid: "org-uuid-001"
+  status: active
+
+  # Extended fields — organization defined
+  extensions:
+    profit_center_code: "PC-4421"
+    regulatory_jurisdiction: "EU"
+    trading_desk_id: "TD-007"
+    risk_tier: 1
+    internal_charge_code: "IC-PAY-001"
+```
+
+---
+
+## 6. Lookup Key Model
+
+DCM looks up external records using a stable primary key — always the external UUID where available — with a fallback chain for systems that don't support UUID-based lookup.
+
+### 6.1 External Entity Reference Structure
+
+```yaml
+external_entity_reference:
+  uuid: 
+  # DCM UUID is what gets stored in relationship declarations
+  # If the external system changes its UUID, only this record changes
+  # All relationships pointing to dcm-uuid remain valid
+
+  external_uuid: 
+  information_provider_uuid: 
+  information_type_uuid: 
+  information_type_name: Business.BusinessUnit
+
+  lookup_method:
+    primary_key: external_uuid
+    # Always attempted first
+    fallback_keys:
+      - field: code
+        value: "BU-PAY"
+      - field: name
+        value: "Payments Platform"
+    # Fallback keys tried in order if primary_key lookup fails
+
+  # Non-authoritative display cache — for UI convenience only
+  display_name: "Payments Platform"
+  display_name_authoritative: false
+
+  verification:
+    last_verified: 
+    last_verified_method: 
+    verification_status: 
+    next_scheduled_verification: 
+
+  status:
+    state: 
+
+  provenance:
+    
+```
+
+### 6.2 Why DCM UUID Wraps External UUID
+
+The DCM-generated UUID is the stable internal anchor. This means:
+- All relationship declarations inside DCM reference the DCM UUID
+- If the external system changes its UUID (migration, system upgrade), only the `external_entity_reference` record needs updating
+- All relationships pointing to the DCM UUID remain valid without modification
+- The provenance chain tracks the change via the `UUID_CHANGED` lifecycle event
+
+---
+
+## 7. Three-Mode Verification Model
+
+DCM uses a trust-but-verify approach to external entity references. The external system is trusted as authoritative for the data — DCM does not validate content. But DCM verifies that references remain valid — the UUID still exists and the record is still active.
+
+### 7.1 Mode 1 — Scheduled Verification (DCM-initiated)
+
+DCM calls the Information Provider's `/verify/{uuid}` endpoint on a configurable schedule for all registered external entity references. Default frequency: configurable — suggested minimum twice daily. Updates `last_verified` and `verification_status`.
+
+### 7.2 Mode 2 — Provider Push (Information Provider obligation)
+
+The Information Provider notifies DCM when a referenced record changes status. This is a contractual obligation — same model as Service Provider lifecycle events. DCM receives the notification, updates the external entity reference, and the Policy Engine evaluates the appropriate response.
+
+### 7.3 Mode 3 — On-Demand Verification (fallback)
+
+When a relationship involving an external entity reference is accessed during request processing, policy evaluation, or drift detection, DCM can verify the reference in real time before relying on it. Used when:
+- `last_verified` is beyond the acceptable staleness window
+- The operation requires high confidence
+- Scheduled verification returned `stale` or `unverifiable`
+
+### 7.4 Verification Fallback Chain
+
+```
+External entity reference accessed
+  │
+  ▼
+Is verification_status: verified AND last_verified within window?
+  │ Yes → proceed with reference
+  │ No ↓
+  ▼
+Mode 3 — on-demand verify via Information Provider /verify/{uuid}
+  │ Success → update last_verified, verification_status: verified, proceed
+  │ Failure ↓
+  ▼
+Policy Engine evaluates:
+  Options (configurable per information type and organizational policy):
+    block_request   — reject request until reference is verified
+    warn_and_proceed — proceed with warning recorded in provenance
+    use_display_only — use display_name only, no operational reliance
+    escalate        — notify appropriate personas for human resolution
+```
+
+---
+
+## 8. Information Type Registry
+
+Information types live in the same DCM Resource Type Registry as Resource Types, distinguished by category prefix. Same versioning, same deprecation model, same governance.
+
+### 8.1 Standard Information Type Categories
+
+| Category | Description | Examples |
+|----------|-------------|---------|
+| `Business.*` | Business organizational data | BusinessUnit, CostCenter, ProductOwner |
+| `Identity.*` | Identity and access data | Person, ServiceAccount, Group |
+| `Compliance.*` | Regulatory and compliance data | RegulatoryScope, AuditFramework |
+| `Operations.*` | Operational reference data | Runbook, SLA, SupportContract |
+
+### 8.2 DCM Default Information Types
+
+```yaml
+# Business.BusinessUnit
+information_type:
+  uuid: 
+  name: Business.BusinessUnit
+  category: Business
+  version: 1.0.0
+  standard_fields:
+    - name: uuid
+      type: string
+      required: true
+      lookup_supported: true
+    - name: name
+      type: string
+      required: true
+      lookup_supported: true
+    - name: code
+      type: string
+      required: false
+      lookup_supported: true
+    - name: parent_uuid
+      type: string
+      required: false
+      lookup_supported: false
+    - name: organization_uuid
+      type: string
+      required: true
+      lookup_supported: false
+    - name: status
+      type: enum
+      values: [active, inactive]
+      required: true
+      lookup_supported: false
+  extended_fields_permitted: true
+  status: active
+
+# Business.CostCenter
+information_type:
+  uuid: 
+  name: Business.CostCenter
+  standard_fields:
+    - name: uuid
+      lookup_supported: true
+    - name: name
+      lookup_supported: true
+    - name: code
+      lookup_supported: true
+    - name: owner_uuid
+      lookup_supported: false
+    - name: budget_period
+      lookup_supported: false
+    - name: status
+      lookup_supported: false
+
+# Identity.Person
+information_type:
+  uuid: 
+  name: Identity.Person
+  standard_fields:
+    - name: uuid
+      lookup_supported: true
+    - name: name
+      lookup_supported: true
+    - name: email
+      lookup_supported: true
+    - name: employee_id
+      lookup_supported: true
+    - name: department_uuid
+      lookup_supported: false
+    - name: status
+      lookup_supported: false
+```
+
+### 8.3 Custom Information Types
+
+Organizations register custom information types following the same model:
+
+```yaml
+custom_information_type:
+  uuid: 
+  name: 
+  # Must use a non-reserved category prefix or register a new one
+  category: 
+  version: 
+  registered_by_tenant_uuid: 
+  standard_fields:
+    
+  extended_fields_permitted: 
+  status: 
+```
+
+---
+
+## 9. Information Provider Registration
+
+```yaml
+information_provider_registration:
+  uuid: 
+  name: 
+  display_name: 
+  version: 
+
+  implements:
+    - information_type_uuid: 
+      information_type_name: Business.BusinessUnit
+      information_type_version: 
+      lookup_methods_supported: [primary_key, code]
+      extended_fields_supported: true
+      extended_schema:
+        
+
+  endpoint: 
+
+  capacity:
+    queries_per_second: 
+    rate_limit_window: 
+    update_frequency: 
+
+  sovereignty_constraints:
+    
+
+  trust_declaration:
+    
+
+  health_check:
+    endpoint: /health
+    poll_interval_seconds: 
+
+  status:
+    state: 
+    deprecation_date: 
+    sunset_date: 
+    replacement_uuid: 
+    deprecation_reason: 
+    migration_guidance: 
+
+  provenance:
+    
+```
+
+---
+
+## 10. Mandatory Information Provider API Endpoints
+
+All Information Providers must implement these endpoints as part of their provider contract:
+
+| Method | Endpoint | Description |
+|--------|----------|-------------|
+| `GET` | `/health` | Provider health check — same as Service Provider |
+| `GET` | `/lookup/{uuid}` | Returns standard + extended data for a record by external UUID |
+| `GET` | `/verify/{uuid}` | Lightweight — confirms UUID exists and is active |
+| `POST` | `/search` | Finds records matching standard field criteria (fallback lookup) |
+| `POST` | `/notify` | DCM calls this to acknowledge receipt of provider push events |
+
+---
+
+## 11. Internally Owned Business Data
+
+When an organization decides to manage business context data in DCM rather than reference an external system, they define it as a DCM Resource Type in the `Business.*` or custom category. Internally owned business data follows the **standard resource entity model** exactly:
+
+- Has a UUID
+- Has a Resource Type (`Business.BusinessUnit`, `Business.CostCenter`, etc.)
+- Has provenance
+- Has versioning
+- Has relationships to other entities
+- Follows the universal lifecycle (active → deprecated → retired)
+- Can be grouped under Tenants and Resource Groups
+
+The relationship model is identical whether the related entity is internal or external — the `related_entity_type` field (`internal` vs `external`) is the only difference from the consuming entity's perspective.
+
+This means an organization can start with an external Information Provider reference and migrate to internally owned business data later — relationships remain structurally the same, only the `related_entity_type` changes.
+
+---
+
+## 12. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | How are conflicting provider push events handled — two Information Providers claim authority for the same record? | Data integrity | ✅ Resolved — authority_level (primary/secondary/advisory) + authority_scope; conflict_resolution strategies; ingestion-time conflict detection; conflict records; see doc 21 (INF-001) |
+| 2 | Should Information Providers support write-back — DCM updating external records via the provider? | Scope expansion | ✅ Resolved — optional declared capability; policy-triggered write-back; audit records produced; credentials via credential management service; see doc 21 (INF-002) |
+| 3 | How is the extended schema versioned — if a provider adds or removes extended fields, how are existing references affected? | Versioning | ✅ Resolved — semver semantics on extended schema; field removal/type change = major; new optional field = minor; migration plan required for major bumps; see doc 21 (INF-003) |
+| 4 | Should DCM maintain a registry of well-known Information Providers (HR systems, finance systems) to simplify onboarding? | Adoption | ✅ Resolved — three-tier Information Provider Registry (Core/Community/Organization); same governance model as Resource Type Registry; separate registries; see doc 21 (INF-004) |
+| 5 | How does the verification model interact with air-gapped environments where Information Providers may be unreachable? | Sovereignty | ✅ Resolved — three air-gap modes: pre-verified signed bundle, internal mTLS, periodic online re-verification with cached tokens; profile-governed cache expiry (prod/fsi/sovereign=suspend on expiry); see doc 21 (INF-005) |
+
+---
+
+## 13. Related Concepts
+
+- **External Entity Reference** — the stable pointer record DCM uses to reference external data
+- **Entity Relationships** — the universal relationship model that uses Information Provider references
+- **Service Provider** — counterpart provider type for resource provisioning
+- **Resource Type Registry** — the unified registry containing both Resource Types and Information Types
+- **Trust Contract** — the provider trust model shared across all provider types
+- **Naturalization/Denaturalization** — translation between external native format and DCM unified format
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/11-data-store-contracts.md b/content/docs/architecture/data-model/11-data-store-contracts.md
new file mode 100644
index 0000000..2541690
--- /dev/null
+++ b/content/docs/architecture/data-model/11-data-store-contracts.md
@@ -0,0 +1,154 @@
+# DCM Data Model — Data Store Contracts
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Specification — Enforcement rules for the four data domains
+**Related Documents:** [Four States](02-four-states.md) | [Universal Audit](16-universal-audit.md) | [Infrastructure Requirements](51-infrastructure-optimization.md)
+
+> **Foundation Document Reference**
+>
+> This document specifies the enforcement contracts for DCM's four data domains.
+> All four domains are stored in a single PostgreSQL-compatible database.
+> See [51-infrastructure-optimization.md](51-infrastructure-optimization.md) for the
+> infrastructure requirements and deployment profiles.
+>
+> **This document maps to: DATA**
+
+---
+
+## 1. Purpose
+
+DCM's data integrity guarantees are enforced at the database level, not the application level. Application code may have bugs; the database enforces the invariants regardless. This document specifies the contracts that the database must satisfy for each data domain.
+
+These contracts are implemented via PostgreSQL-native mechanisms: `REVOKE` grants, Row-Level Security (RLS), triggers, CHECK constraints, and append-only table design. The canonical SQL schema implementing these contracts is in `schemas/sql/001-initial.sql`.
+
+---
+
+## 2. Data Domain Contracts
+
+### 2.1 Intent Domain (`intent_records`)
+
+The Intent domain stores consumer declarations exactly as submitted — before any processing, enrichment, or policy application.
+
+**Contract:**
+- **Append-only** — rows are never updated or deleted. `REVOKE UPDATE, DELETE ON intent_records FROM dcm_app`.
+- **Versioned** — resubmissions create new rows with incrementing `intent_version`. Previous versions are preserved.
+- **Immutable fields** — `intent_uuid`, `entity_uuid`, `tenant_uuid`, `submitted_by`, `submitted_at`, `submitted_via` are set on insert and never change.
+- **Ingress tracking** — `submitted_via` records the ingress path (`api`, `gitops`, `cli`, `message_bus`).
+- **Tenant-isolated** — RLS ensures `dcm_app` can only read/write intents belonging to the current tenant (`SET dcm.current_tenant_uuid`).
+
+### 2.2 Requested Domain (`requested_records`)
+
+The Requested domain stores fully assembled, policy-evaluated, placed payloads — the authorized dispatch record.
+
+**Contract:**
+- **Append-only** — rows are never updated or deleted. `REVOKE UPDATE, DELETE ON requested_records FROM dcm_app`.
+- **Traceable** — every record has a non-nullable `intent_uuid` (which intent produced this) and `operation_uuid` (which operation authorized this).
+- **Complete provenance** — `layer_sources` records which layers contributed and how many fields each contributed. `policy_results` records which policies evaluated and their outcomes. `provenance` records field-level origin for every field.
+- **Placement recorded** — `placement_result` records the provider selection decision, score, and constraints.
+- **Tenant-isolated** — RLS per tenant.
+
+### 2.3 Realized Domain (`realized_entities`)
+
+The Realized domain stores provider-confirmed state as versioned snapshots.
+
+**Contract:**
+- **Append-on-change** — state changes create new rows. The `is_current` flag marks the latest version. Previous versions are retained for point-in-time queries and rehydration.
+- **Complete snapshots** — each row captures the full entity state, not a delta from previous state. This makes rehydration a direct lookup rather than an event replay.
+- **Traceable** — every record has `request_uuid` linking it to the authorized request that produced this state.
+- **Versioned** — `version_major.minor.revision` follows semantic versioning. Breaking field changes increment major; additive changes increment minor; data-only changes increment revision.
+- **Tenant-isolated** — RLS per tenant.
+
+### 2.4 Discovered Domain (`discovered_records`)
+
+The Discovered domain stores independently observed resource state from provider discovery runs.
+
+**Contract:**
+- **Ephemeral** — discovery runs produce fresh snapshots. Previous runs are retained for trend analysis and drift history, subject to retention policy.
+- **Grouped by run** — `discovery_run_uuid` groups all records from a single discovery cycle.
+- **Match tracking** — `entity_uuid` links discovered resources to known DCM entities. Null `entity_uuid` indicates an orphan candidate (resource exists at provider but has no DCM entity).
+- **Confidence scored** — `match_confidence` indicates how certain the match is (`exact`, `high`, `low`, `unmatched`).
+- **Tenant-isolated** — RLS per tenant (discovered resources inherit tenant from their matched entity or provider).
+
+---
+
+## 3. Audit Record Contract
+
+Audit records have the strictest contract in DCM. They are the compliance evidence trail.
+
+**Contract:**
+- **Append-only** — `REVOKE UPDATE, DELETE ON audit_records FROM dcm_app`. Belt-and-suspenders trigger prevents modification even by privileged roles.
+- **Hash chain** — each record's `record_hash` is `SHA-256(record_content + previous_record_hash)`. `chain_sequence` is monotonically increasing per entity. First record uses `GENESIS-HASH` as the previous hash.
+- **Tamper-evident** — breaking the hash chain is detectable by verifying `chain_sequence` order and recomputing hashes. The Audit Service exposes `POST /api/v1/audit/chain/verify` for chain integrity verification.
+- **Non-repudiable** — every record captures `immediate_actor_uuid`, `authorized_by_uuid`, `session_uuid`, and the complete before/after state.
+- **Retention** — minimum P365D across all deployment profiles. FedRAMP/sovereign profiles may require P2555D (7 years).
+- **Separate privilege** — `dcm_audit` role has INSERT+SELECT only. `dcm_app` has INSERT+SELECT only. No role has UPDATE or DELETE.
+
+---
+
+## 4. Pipeline Event Contract
+
+Pipeline events route work between control plane services.
+
+**Contract:**
+- **Append-only** — events are never modified after publication.
+- **Real-time notification** — PostgreSQL `LISTEN/NOTIFY` trigger fires on every insert, notifying subscribed services.
+- **Consumption tracking** — `consumed_by` and `consumed_at` track which services have processed each event.
+- **Ordered** — events within a single entity are ordered by `published_at`. Cross-entity ordering is not guaranteed.
+- **Tenant-isolated** — RLS on SELECT ensures services only see events for the current tenant context.
+
+For high-throughput deployments (>1000 events/sec), Kafka can be deployed alongside PostgreSQL. Events are written to both the `pipeline_events` table (for persistence and query) and the Kafka topic (for high-throughput consumer groups). The table is the source of truth; Kafka is the performance layer.
+
+---
+
+## 5. Tenant Isolation Enforcement
+
+Row-Level Security (RLS) is the tenant isolation mechanism across all data domains.
+
+**How it works:**
+1. The API Gateway extracts tenant from the JWT claims and sets `X-DCM-Tenant` header
+2. Each service sets `SET dcm.current_tenant_uuid = '{uuid}'` on its database connection before any query
+3. RLS policies on every tenant-scoped table filter rows to the current tenant
+4. `dcm_app` role is subject to RLS — it cannot query across tenants
+5. `dcm_admin` role bypasses RLS for platform administration (separately audited)
+
+**Tables with RLS:**
+`intent_records`, `requested_records`, `realized_entities`, `discovered_records`, `operations`, `audit_records`, `subscriptions`, `subscription_entities`, `subscription_updates`, `pipeline_events`
+
+---
+
+## 6. Sovereignty Partitioning
+
+For deployments spanning multiple sovereignty zones, DCM deploys separate PostgreSQL instances per zone. Data does not cross sovereignty boundaries at the database level.
+
+**Model:**
+- Each sovereignty zone has its own PostgreSQL instance with its own connection string
+- The API Gateway routes requests to the correct zone's database based on tenant and resource sovereignty declarations
+- Cross-zone queries are explicitly prohibited — a query in Zone A cannot read data from Zone B
+- Federation between zones uses the Peer DCM protocol, which transfers only the minimum data required and is subject to sovereignty policy evaluation
+
+**Deployment profiles:**
+- **Minimal/Standard:** Single PostgreSQL instance (all data in one zone)
+- **Enterprise:** PostgreSQL with read replicas (one write primary, read replicas per region)
+- **Sovereign:** Separate PostgreSQL instance per sovereignty zone, no cross-zone replication
+
+---
+
+## 7. Resilience and Recovery
+
+**Write-Ahead Log (WAL):**
+PostgreSQL's WAL ensures that committed transactions survive process crashes. DCM does not implement its own WAL — it relies on PostgreSQL's native durability guarantees.
+
+**Backup and point-in-time recovery:**
+PostgreSQL's continuous archiving and point-in-time recovery (PITR) provide the disaster recovery mechanism. The Crunchy Postgres Operator (or equivalent) manages automated backups.
+
+**Audit chain recovery:**
+If the audit hash chain is broken (database restored from backup to a point before the latest audit record), the chain verification endpoint detects the break. The recovery procedure is:
+1. Verify chain integrity — identify the break point
+2. Records after the break point are marked with `chain_recovery: true`
+3. A new chain segment begins from the break point with a `RECOVERY-HASH` seed
+4. Both chain segments are retained — the original (broken) and the recovery segment
+5. The break event itself is recorded as an audit record
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/12-audit-provenance-observability.md b/content/docs/architecture/data-model/12-audit-provenance-observability.md
new file mode 100644
index 0000000..4a36404
--- /dev/null
+++ b/content/docs/architecture/data-model/12-audit-provenance-observability.md
@@ -0,0 +1,461 @@
+# DCM Data Model — Audit, Provenance, and Observability
+
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Four States](02-four-states.md) | [data stores](11-storage-providers.md) | [Context and Purpose](00-context-and-purpose.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA**
+>
+> The Data abstraction — audit records, provenance as structural data
+
+
+
+---
+
+## 1. Purpose
+
+Audit, Provenance, and Observability are three distinct but related capabilities in DCM. They are often conflated — this document separates them precisely, defines their relationship, and establishes the architectural model for each.
+
+| Capability | Question Answered | Audience | Time Orientation |
+|------------|------------------|----------|-----------------|
+| **Provenance** | Where did this data come from and how did it change? | System — embedded in data | Embedded in every payload |
+| **Audit** | What happened, who authorized it, can you prove it? | Auditors, Compliance, Security | Backward-looking |
+| **Observability** | Is the system healthy and performing within expectations? | SRE, Platform Engineers | Forward-looking, real-time |
+
+---
+
+## 2. Provenance
+
+### 2.1 Definition
+
+Provenance is the structural data lineage mechanism embedded in every field of every DCM payload. It is not a separate system — it is part of the data itself. Every field that can be created or modified by any DCM process carries provenance metadata alongside its value.
+
+Provenance answers: "where did this value come from, what modified it, and why?"
+
+Audit queries provenance to answer its questions. Observability does not use provenance directly — it operates on event streams and metrics.
+
+### 2.2 Provenance Structure
+
+See [Context and Purpose — Section 4.4](00-context-and-purpose.md) for the complete field-level provenance structure. The key elements:
+
+```yaml
+field_name:
+  value: 
+  metadata:
+    override: 
+    basis_for_value: 
+    baseline_value: 
+    locked_by_policy_uuid: 
+    locked_at_level: 
+  provenance:
+    origin:
+      value: 
+      source_type: 
+      source_uuid: 
+      timestamp: 
+    modifications:
+      - sequence: 1
+        previous_value: 
+        modified_value: 
+        source_uuid: 
+        operation_type: 
+        actor_uuid: 
+        timestamp: 
+        reason: 
+```
+
+### 2.3 Provenance Obligations
+
+Every DCM component that modifies data carries a provenance obligation — it must record its UUID, operation type, actor, timestamp, and reason for every field it touches. A component that modifies data without recording provenance violates the data model contract.
+
+| Component | Provenance Obligation |
+|-----------|----------------------|
+| Request Payload Processor | Record source UUID and type for every field assembled from layers |
+| Policy Engine | Record policy UUID, level, operation type, and reason for every field modified or locked |
+| Service Provider (Denaturalization) | Record provider UUID and timestamp for every field in the realized payload |
+| data store | Emit provenance event to Audit component on every write |
+| Resource Discovery | Record provider UUID, timestamp, and method for every discovered field |
+| Rehydration Pipeline | Record source store, source record UUID, rehydration reason, and actor UUID |
+
+### 2.4 Provenance Across the Full Lifecycle
+
+The provenance chain for a single field may span multiple lifecycle stages:
+
+```
+Base Layer sets encryption_standard: AES-128
+  origin: {source_type: base_layer, source_uuid: layer-uuid-001}
+
+Transformation Policy enriches to AES-256
+  modification: {source_uuid: policy-uuid-001, operation: transformation,
+                 reason: "Security standard requires AES-256 minimum"}
+
+GateKeeper Policy locks as immutable
+  modification: {source_uuid: policy-uuid-002, operation: lock,
+                 reason: "CISO mandate — encryption standard non-negotiable"}
+
+Provider reports realized value: AES-256
+  modification: {source_uuid: provider-uuid-001, operation: denaturalization,
+                 reason: "Provider confirmed encryption standard applied"}
+
+Drift detected: discovered value AES-128
+  modification: {source_uuid: discovery-uuid-001, operation: discovery,
+                 reason: "Direct modification detected outside DCM lifecycle"}
+```
+
+The complete chain tells the full story of that field across its entire existence.
+
+---
+
+## 3. Audit
+
+### 3.1 Definition
+
+Audit is the compliance-grade, queryable record of all significant actions across the DCM lifecycle. It is backward-looking, human-readable, and access-controlled by persona. It answers: "what happened, who authorized it, can you prove it?"
+
+Audit is a **separate component** — not a query against the GitOps stores, not a view into provenance directly. It aggregates and indexes provenance events from all stores and presents them through a structured query API surfaced by the DCM API Gateway.
+
+### 3.2 Architecture
+
+```
+All data stores emit provenance events (contractual obligation)
+  │
+  │  Events include: entity_uuid, operation, actor_uuid,
+  │  timestamp, payload_hash, store_reference
+  ▼
+Audit Component
+  │  Receives provenance events from all stores
+  │  Correlates events by entity_uuid across all stores
+  │  Indexes for structured query: by entity, tenant, actor,
+  │  time range, operation type, policy UUID
+  │  Maintains immutable records — audit records are never modified
+  │  Enforces long retention (regulatory periods — configurable,
+  │  minimum 7 years for FSI deployments)
+  │  Verifies payload hashes — detects store tampering
+  ▼
+DCM API Gateway
+  │  Surfaces Audit query API with persona-based access control
+  │  Auditor: full access — all entities, all tenants, all time
+  │  SRE: full access within operational scope
+  │  Admin: full access within administrative scope
+  │  Consumer: own entities and requests only
+  │  Provider: own provider's operations only
+```
+
+### 3.3 Audit API (via DCM API Gateway)
+
+```
+GET  /api/v1/audit/entities/{uuid}/history
+     Returns: complete lifecycle history for an entity
+     Fields: all state transitions, all provenance events, all actor actions
+
+GET  /api/v1/audit/requests/{uuid}/provenance
+     Returns: complete provenance chain for a specific request
+     Fields: intent, assembly, policy evaluation, provider dispatch, realization
+
+GET  /api/v1/audit/policies/{uuid}/evaluations
+     Returns: all evaluations of a specific policy across all requests
+     Fields: when it ran, what it did, which entities it affected
+
+GET  /api/v1/audit/actors/{uuid}/activity
+     Returns: all actions taken by a specific actor
+     Fields: requests submitted, approvals given, policy evaluations triggered
+
+GET  /api/v1/audit/tenants/{uuid}/activity
+     Returns: all activity within a specific tenant
+     Fields: requests, realizations, drift events, policy violations
+
+POST /api/v1/audit/query
+     Body: structured audit query with field filters, time range, pagination
+     Returns: matching audit records
+```
+
+### 3.4 Audit Record Structure
+
+```yaml
+audit_record:
+  audit_uuid: 
+  entity_uuid: 
+  tenant_uuid: 
+  event_type: 
+  timestamp: 
+  actor_uuid: 
+  actor_type: 
+
+  source_store:
+    store_type: 
+    store_uuid: 
+    store_reference: 
+    payload_hash: 
+
+  provenance_summary:
+    
+
+  policy_context:
+    policies_evaluated: []
+    policies_applied: []
+    policies_rejected: []
+    override_control_changes: []
+
+  related_records:
+    intent_record_uuid: 
+    requested_record_uuid: 
+    realized_event_uuid: 
+    rehydration_source_uuid: 
+```
+
+### 3.5 Audit Integrity
+
+Audit records are immutable. The Audit component verifies payload hashes against the data store's stored values on every read — if a hash mismatch is detected, the Audit component flags the record as potentially tampered and escalates to the Policy Engine.
+
+The Audit Store itself is a data store with the highest consistency and durability requirements — linearizable consistency, synchronous replication, cryptographic payload hashing, and compliance-grade retention.
+
+---
+
+## 4. Observability
+
+### 4.1 Definition
+
+Observability is real-time insight into the health, performance, and behavior of the DCM system. It is forward-looking, machine-readable, and aggregated. It answers: "is the system healthy, where are the bottlenecks, what is the error rate?"
+
+Observability is operationally oriented — SREs and platform engineers use it to understand system behavior and respond to incidents. It does not carry the compliance obligations of Audit.
+
+### 4.2 The Three Pillars
+
+**Metrics** — quantitative measurements of system state over time
+- Request throughput: requests/second by resource type, tenant, provider
+- Latency: assembly time, policy evaluation time, provider dispatch time, end-to-end time
+- Error rates: policy rejection rate, provider failure rate, drift detection rate
+- Capacity: provider utilization, store capacity, queue depth
+- Cost: accumulated cost by tenant, resource type, provider
+
+**Traces** — distributed traces of request execution across components
+- Full request trace from Intent State creation through provider dispatch
+- Policy evaluation trace — which policies ran, in what order, how long each took
+- Assembly trace — which layers were applied, in what order, what each contributed
+
+**Logs** — structured event logs from all DCM components
+- Component startup and shutdown
+- Registration events (provider registration, deregistration)
+- Error conditions
+- Drift detection events
+- Unsanctioned change events
+
+### 4.3 Architecture
+
+```
+DCM components emit metrics, traces, and logs
+  │
+  │  All telemetry in OpenTelemetry format
+  │  Standardized metric names, trace context propagation,
+  │  structured log format
+  ▼
+Observability Store
+  │  Time-series metrics store (Prometheus-compatible)
+  │  Distributed trace store (Jaeger/Zipkin compatible)
+  │  Log aggregation (structured, indexed)
+  │  Short-to-medium retention (configurable — typically 90 days)
+  ▼
+DCM API Gateway
+  │  GET /api/v1/observability/metrics
+  │  GET /api/v1/observability/traces/{request_uuid}
+  │  GET /api/v1/observability/health
+  │  GET /api/v1/observability/providers/{uuid}/performance
+  ▼
+Dashboards and alerting (external tooling)
+  │  Grafana, DataDog, Splunk — implementor choice
+  │  DCM provides OpenTelemetry-compatible telemetry
+  │  Dashboards are deployment artifacts, not DCM artifacts
+```
+
+### 4.4 Standard DCM Metrics
+
+```
+# Request lifecycle
+dcm_requests_total{resource_type, tenant, status}
+dcm_request_duration_seconds{resource_type, stage}
+dcm_requests_in_flight{resource_type, tenant}
+
+# Policy Engine
+dcm_policy_evaluations_total{policy_type, result}
+dcm_policy_evaluation_duration_seconds{policy_type}
+dcm_policy_rejections_total{policy_uuid, resource_type}
+
+# Provider
+dcm_provider_requests_total{provider_uuid, resource_type, status}
+dcm_provider_response_duration_seconds{provider_uuid}
+dcm_provider_capacity_available{provider_uuid, resource_type}
+dcm_provider_health_status{provider_uuid}
+
+# Drift
+dcm_drift_detections_total{resource_type, severity}
+dcm_unsanctioned_changes_total{resource_type, provider_uuid}
+dcm_drift_resolution_duration_seconds{resolution_type}
+
+# Storage
+dcm_store_write_duration_seconds{store_type, store_uuid}
+dcm_store_read_duration_seconds{store_type, store_uuid}
+dcm_store_health_status{store_type, store_uuid}
+
+# Rehydration
+dcm_rehydrations_total{source_store, placement_mode, policy_version}
+dcm_rehydration_duration_seconds{source_store}
+```
+
+### 4.5 Observability vs Audit — The Key Distinctions
+
+| Dimension | Audit | Observability |
+|-----------|-------|---------------|
+| **Retention** | Regulatory period (years) | Operational window (days-months) |
+| **Access control** | Strict persona-based | Operational teams |
+| **Data volume** | Moderate — per-entity events | High — continuous time series |
+| **Query model** | Structured, entity-centric | Aggregated, time-series |
+| **Immutability** | Absolute — records never modified | Aggregated data may be downsampled |
+| **Compliance** | Compliance-grade — hash-verified | Operational — best effort |
+| **Use case** | Prove what happened | Understand what is happening |
+
+---
+
+## 5. The API Gateway — Unified Access
+
+All three capabilities — Provenance (embedded in data), Audit (structured history), and Observability (operational telemetry) — are surfaced through the DCM API Gateway. There is no separate endpoint for audit or observability. All DCM capabilities live in a unified API hierarchy.
+
+```
+DCM API Gateway
+  │
+  ├── /api/v1/catalog/          # Service Catalog
+  ├── /api/v1/requests/         # Request submission and management
+  ├── /api/v1/entities/         # Entity lifecycle management
+  ├── /api/v1/providers/        # Provider registration and management
+  ├── /api/v1/policies/         # Policy management
+  ├── /api/v1/audit/            # Audit queries
+  ├── /api/v1/observability/    # Operational metrics and traces
+  └── /api/v1/admin/            # Administrative functions
+```
+
+Persona-based access control is enforced at the API Gateway level for all endpoints. The same authentication and authorization model applies across the entire API surface.
+
+---
+
+## 6. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should the Audit Store be a specialized data store or can a general pipeline_events table satisfy the audit contract? | Architecture | ✅ Resolved — specialized PostgreSQL store contract; append-only; hash chain integrity; reference-based retention; compliance queries; see doc 11 (STO-004) |
+| 2 | How are audit records replicated across sites in air-gapped or geographically distributed deployments? | Sovereignty | ✅ Resolved — live sync for Regional DCMs; signed bundle for Sovereign DCMs; sovereignty check required; hash chain preserved across transport (AUD-018) |
+| 3 | Should DCM provide a default observability dashboard or only the telemetry? | Deployment | ✅ Resolved — default Grafana dashboard for minimal/dev/standard; enterprise integration recommended for prod; required for fsi; local-only for sovereign (OBS-002) |
+| 4 | How does the Audit component handle provenance events from a data store that has been deregistered? | Operational | ✅ Resolved — two-stage model handles this; Commit Log independent of data stores; gap record inserted on Audit Store recovery; chain makes gap explicit (AUD-019) |
+
+---
+
+## 7. Related Concepts
+
+- **Provenance** — field-level data lineage embedded in every DCM payload
+- **Audit Store** — compliance-grade, immutable store of all audit records
+- **Observability Store** — time-series metrics, traces, and logs
+- **data store** — formal provider type for all DCM stores
+- **API Gateway** — unified access point for all DCM capabilities including audit and observability
+- **Drift Detection** — uses discovered vs realized state comparison; drift events feed the Audit component
+- **Unsanctioned Change** — a specific audit event type triggered by unauthorized resource modification
+
+
+## 7. Audit Provenance Observability Gap Resolutions
+
+### 7.1 Audit Store Architecture (Q1)
+
+Resolved as STO-004 in doc 11 (data stores). The Audit Store is a specialized PostgreSQL store contract — append-only with immutability enforcement, hash chain integrity, reference-based retention tracking, and compliance-grade multi-dimensional queries. The Event Stream is the delivery channel only, not the compliance destination.
+
+### 7.2 Audit Record Replication Across Sites (Q2)
+
+Each DCM instance maintains its own Audit Store. Replication uses live sync (Regional DCMs with connectivity) or signed bundle export (Sovereign DCMs without connectivity).
+
+```yaml
+audit_replication:
+  model: 
+
+  live_sync:                        # Regional DCMs with Hub connectivity
+    direction: regional_to_hub      # Regional pushes aggregated view to Hub
+    filters:
+      include: [SECURITY, GATEKEEPER_TRIGGERED, SOVEREIGNTY_VIOLATION]
+    sovereignty_check: required     # before any replication
+
+  signed_bundle:                    # Sovereign DCMs
+    export_on: [scheduled, connectivity_window, on_demand]
+    schedule: "0 0 * * 0"           # weekly during connectivity window
+    encryption: required
+    hash_chain_preserved: true      # chain integrity maintained across transport
+    import_at: hub_dcm_audit_store
+
+  per_instance_only:                # fully isolated Sovereign DCMs
+    export_on_request: via_signed_bundle_manual_transfer
+```
+
+### 7.3 Default Observability Dashboard (Q3)
+
+DCM ships a default Grafana-based observability dashboard for minimal/dev/standard profiles.
+
+```yaml
+default_observability_dashboard:
+  implementation: grafana
+  pre_built_dashboards:
+    - dcm_overview              # request throughput, error rates, component health
+    - resource_lifecycle        # entity state transitions, rehydration activity
+    - policy_evaluation         # GateKeeper triggers, shadow results, validation failures
+    - provider_health           # provider availability, capacity confidence, trust scores
+    - audit_integrity           # hash chain status, pending forwards, chain breaks
+    - federation_status         # federation tunnel health, cross-DCM traffic
+  profile_behavior:
+    minimal: included
+    dev: included
+    standard: included_optional         # shipped; organizations may substitute
+    prod: integration_recommended       # integrate with enterprise observability
+    fsi: integration_required
+    sovereign: local_only               # local Grafana; no external connections
+  export_formats: [prometheus, opentelemetry, json]
+```
+
+### 7.4 Audit Component Handling Failing data store (Q4)
+
+The two-stage audit model handles this by design — the Stage 1 Commit Log (etcd) has no dependency on any data store.
+
+```
+store failure detected
+  │
+  ▼ Stage 1 Commit Log (etcd) — independent of data store
+  │   Records: STORAGE_PROVIDER_FAILURE event immediately
+  │
+  ▼ Stage 2 Audit Forward Service — async, after recovery
+  │   Forwards accumulated events including the failure event itself
+  │
+  ▼ For Audit Store self-failure specifically:
+      Commit Log accumulates events as pending_forward
+      On recovery: queue drains in order
+      AUDIT_STORE_UNAVAILABLE gap record inserted with exact outage timestamps
+      Hash chain makes the gap explicitly visible — not hidden
+```
+
+The gap record is not a failure — it is evidence of correct behavior. Auditors can see exactly when the Audit Store was unavailable and that no records were lost (all arrived after recovery).
+
+### 7.5 System Policies — Audit Provenance Gaps
+
+| Policy | Rule |
+|--------|------|
+| `STO-004` | The Audit Store is a specialized PostgreSQL store contract — append-only, hash chain integrity, reference-based retention, compliance-grade queries. Event Stream is the delivery channel only. (See doc 11) |
+| `AUD-018` | Audit records are replicated using live sync (Regional DCMs) or signed bundle export (Sovereign DCMs). Sovereignty checks required before any replication. Hash chain integrity preserved across transport. Fully isolated Sovereign DCMs maintain local-only audit stores with manual export. |
+| `OBS-002` | DCM ships a default Grafana-based observability dashboard for minimal/dev/standard profiles. Standard+ profiles may substitute enterprise platforms. FSI requires enterprise observability. Sovereign DCMs use local dashboard only with no external connections. |
+| `AUD-019` | store failures are recorded via the Stage 1 Commit Log (etcd), which is independent of all data stores. Audit Store self-failures produce pending_forward records. On recovery, a gap record (AUDIT_STORE_UNAVAILABLE) is inserted with the outage window timestamps. The hash chain gap is explicit and auditable. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/13-ingestion-model.md b/content/docs/architecture/data-model/13-ingestion-model.md
new file mode 100644
index 0000000..7ca22af
--- /dev/null
+++ b/content/docs/architecture/data-model/13-ingestion-model.md
@@ -0,0 +1,412 @@
+# DCM Data Model — Ingestion Model
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Four States](02-four-states.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Entity Relationships](09-entity-relationships.md) | [Resource Grouping](08-resource-grouping.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + PROVIDER**
+>
+> Data: ingestion state artifacts. Provider: discovery provider invocation
+
+
+
+---
+
+## 1. Purpose
+
+The DCM Ingestion Model is the **unified mechanism for bringing entities that exist outside DCM's lifecycle control into DCM's governance model**. It applies to three distinct sources:
+
+- **Brownfield Discovery** — entities discovered by a Service Provider that already exist in the infrastructure but are unknown to DCM
+- **Manual Import** — entities imported from external systems (CMDBs, spreadsheets, legacy records) during onboarding
+
+All three sources follow the same pattern: ingest, enrich, and promote. The same data structures, the same governance policies, the same audit trail, and the same transitional holding mechanism apply regardless of source.
+
+**The three-step pattern:**
+
+```
+1. INGEST   — bring the entity into DCM with whatever identity and metadata is available
+2. ENRICH   — associate business data, ownership, Tenant assignment, and relationships
+3. PROMOTE  — transition from holding state to full DCM lifecycle ownership
+```
+
+---
+
+## 2. Design Principles
+
+**Unified model — minimum variance.** brownfield ingestion and brownfield ingestion are the same fundamental operation. One model, one audit record structure, one set of governance policies.
+
+**Non-blocking.** Entities that cannot be immediately assigned a Tenant do not block migration or discovery. They land in the `__transitional__` Tenant and are resolved progressively. Migration does not require every entity to be assigned before any entity can proceed.
+
+**Provenance transparency.** Ingested entities are honest about their provenance depth. `created_via: migration` or `created_via: discovery` on the artifact metadata signals that the chain has limited depth. The ingestion record carries the confidence level.
+
+**Promotion gates governance.** An entity in a holding state cannot be the parent of a new allocated resource claim, cannot be used as a hard dependency by new requests, and cannot receive new operational relationships until promoted. Informational relationships are permitted — entities can be referenced during enrichment.
+
+**Audit completeness.** Every ingested entity carries an `ingestion_record` in its provenance. Every Tenant assignment, enrichment action, and promotion event is recorded with actor, timestamp, and reason.
+
+---
+
+## 3. Ingestion Lifecycle States
+
+Entities going through ingestion follow a distinct mini-lifecycle before entering the standard entity lifecycle:
+
+```
+INGESTED
+  │  Entity exists in DCM. Minimal metadata. Tenant may be __transitional__.
+  │  Action: enrich — add business data, assign relationships, assign real Tenant
+  ▼
+ENRICHING
+  │  Tenant assigned. Metadata being completed. Relationships being established.
+  │  Action: complete enrichment, satisfy governance requirements
+  ▼
+PROMOTED
+  │  All required fields present. Governance satisfied. Full DCM lifecycle assumed.
+  ▼
+OPERATIONAL  (standard entity lifecycle from here)
+```
+
+### 3.1 State Behavior
+
+| State | Tenant | New Requests Can Use? | Parent for Allocations? | New Relationships? |
+|-------|--------|----------------------|------------------------|-------------------|
+| `INGESTED` | `__transitional__` or assigned | No | No | Informational only |
+| `ENRICHING` | Assigned | No | No | Operational (read-only) |
+| `PROMOTED` | Assigned | Yes | Yes | All types |
+| `OPERATIONAL` | Assigned | Yes | Yes | All types |
+
+### 3.2 Promotion Requirements
+
+Before an entity can be promoted, the following must be satisfied:
+
+- Assigned to a real Tenant (not `__transitional__`)
+- All `universal` fields on the Resource Type Specification are populated
+- All `constituent` relationships declared on the Resource Type Specification are resolved
+- At least one actor has reviewed and authorized the promotion
+- `ingestion_record.enrichment_status` is `complete`
+
+---
+
+## 4. The `__transitional__` Tenant
+
+The `__transitional__` Tenant is a DCM System artifact — a system-managed holding area for entities that have been ingested but not yet assigned to a real Tenant.
+
+```yaml
+tenant:
+  uuid: 
+  handle: "__transitional__"
+  type: system_managed
+  purpose: ingestion_holding
+  governance:
+    max_residency_days: 90          # configurable per deployment
+    on_max_residency: escalate      # escalate | block | alert
+    escalation_endpoint: 
+  hard_tenancy:
+    cross_tenant_relationships: operational_only
+  artifact_metadata:
+    created_by:
+      display_name: "DCM Ingestion System"
+    created_via: system
+    status: active
+```
+
+**Properties:**
+- Cannot be deleted
+- Cannot be renamed
+- Cannot be used for new resource provisioning — only ingestion assignment
+- Entities in `__transitional__` are fully auditable and visible in DCM
+- Governance policy enforces maximum residency and escalation
+
+---
+
+## 5. The Ingestion Record
+
+Every ingested entity carries an `ingestion_record` in its provenance chain. This is the audit record of how the entity entered DCM.
+
+```yaml
+ingestion_record:
+  ingestion_uuid: 
+  resource_entity_uuid: 
+  ingestion_timestamp: 
+
+  ingestion_source: 
+
+  # brownfield ingestion fields (when ingestion_source: legacy_import)
+  legacy_identifier: 
+  legacy_metadata_snapshot: 
+
+  # Brownfield discovery fields (when ingestion_source: brownfield_discovery)
+  discovered_state_uuid: 
+  discovery_provider_uuid: 
+  discovery_timestamp: 
+
+  # Manual import fields (when ingestion_source: manual_import)
+  import_source_system: 
+  import_reference: 
+
+  # Common fields
+  assigned_tenant_uuid: 
+  assignment_method: 
+  assignment_signal: >
+    Human-readable description of what drove auto-assignment.
+    e.g., "Resource group membership: payments-group → Payments Tenant"
+    e.g., "Business unit metadata: BU-PAY → Payments Tenant"
+    e.g., "No signal found — assigned to __transitional__"
+  assigned_by:
+    uuid: 
+    display_name: 
+    timestamp: 
+
+  ingestion_confidence: 
+  # high:   strong unambiguous signal — auto-assignment reliable
+  # medium: inferred from metadata — reasonable confidence, human review recommended
+  # low:    orphaned or conflicting signals — assigned to __transitional__
+
+  enrichment_status: 
+  enrichment_history:
+    - sequence: 1
+      action: 
+      performed_by:
+        display_name: 
+      timestamp: 
+      detail: 
+
+  promoted_at: 
+  promoted_by:
+    display_name: 
+```
+
+---
+
+## 6. Auto-Assignment Signals
+
+When DCM ingests an entity, it attempts auto-assignment to a real Tenant using the following signals in priority order:
+
+| Signal | Confidence | Description |
+|--------|-----------|-------------|
+| Explicit ownership metadata | High | Business unit, cost center, or team tag on the resource maps unambiguously to a Tenant |
+| Resource group membership | High | Resource belongs to a group that maps to a known Tenant |
+| Request history | High | Legacy record identifies the requesting team, which maps to a Tenant |
+| Network / location context | Medium | Resource's location, VLAN, or network segment maps to a Tenant by convention |
+| Naming convention | Medium | Resource name matches a known Tenant naming pattern |
+| Provider context | Medium | Resource was provisioned by a known provider associated with a Tenant |
+| No signal found | Low | No auto-assignment possible — entity goes to `__transitional__` |
+
+Multiple signals can be combined. If signals conflict, the higher-confidence signal wins and the conflict is recorded in the ingestion record with `ingestion_confidence: medium` regardless of individual signal strengths.
+
+---
+
+## 8. Brownfield Ingestion
+
+### 8.1 Overview
+
+Brownfield ingestion brings infrastructure that already exists in the real world — but is unknown to DCM — under DCM lifecycle management. The source is the **Discovered State**: a Service Provider interrogates existing infrastructure and creates Discovered State records for everything it finds.
+
+This is the "greening the brownfield" use case — taking an unmanaged estate and progressively bringing it under DCM governance without requiring a big-bang cutover.
+
+### 8.2 Brownfield Flow
+
+```
+Service Provider performs discovery scan
+  │  Interrogates existing infrastructure
+  │  Creates Discovered State records for all found entities
+  │
+  ▼  DCM identifies "unmanaged" discovered entities
+  │  Discovered State records with no matching Realized State = unmanaged
+  │  These are brownfield candidates
+  │
+  ▼  Ingestion initiation
+  │  Platform admin or automated policy initiates ingestion
+  │  DCM creates entity stubs with:
+  │    - New UUID (DCM-assigned)
+  │    - ingestion_source: brownfield_discovery
+  │    - State: INGESTED
+  │    - Tenant: __transitional__ (pending enrichment)
+  │    - ingestion_record linking to Discovered State UUID
+  │
+  ▼  Enrichment
+  │  Business data associated (owner, cost center, purpose)
+  │  Tenant assigned based on auto-assignment signals
+  │  Relationships established to other entities
+  │  Missing fields populated from discovery data
+  │
+  ▼  Promotion
+  │  Review and authorization by responsible actor
+  │  State: ENRICHING → PROMOTED
+  │  DCM assumes lifecycle ownership:
+  │    - Discovered State record becomes the initial Realized State
+  │    - Entity enters standard DCM lifecycle (OPERATIONAL)
+  │    - Drift detection active from this point forward
+  │
+  ▼  OPERATIONAL
+     DCM now manages the full lifecycle of this previously unmanaged entity
+```
+
+### 8.3 Discovered → Realized Promotion
+
+When a brownfield entity is promoted, its Discovered State record is promoted to become the initial Realized State. This is the moment DCM assumes lifecycle authority:
+
+```yaml
+realized_state_record:
+  entity_uuid: 
+  source: brownfield_promotion
+  ingestion_uuid: 
+  discovered_state_uuid: 
+  promoted_at: 
+  promoted_by:
+    display_name: 
+  initial_realized_payload: 
+  provenance:
+    origin:
+      source_type: brownfield_discovery
+      source_uuid: 
+      timestamp: 
+```
+
+From this point, the standard drift detection cycle runs: future discoveries are compared against the Realized State and any deviations are flagged as drift.
+
+---
+
+## 9. Relationship to the Four States
+
+Ingestion interacts with the Four States model as follows:
+
+| Ingestion Source | States Involved | Flow |
+|-----------------|----------------|------|
+| brownfield ingestion | Intent → Requested → (no Realized yet) | Legacy records treated as incomplete Requested State; migration creates minimal Realized State |
+| Brownfield Discovery | Discovered → Realized | Discovered State is promoted to Realized State at promotion |
+| Manual Import | None initially | Entity stub created; no prior state records; Realized State created at promotion from import data |
+
+In all cases: once an entity reaches `PROMOTED`, it has a Realized State record and full Four States tracking begins.
+
+---
+
+## 10. DCM System Policies — Full List
+
+| Policy | Rule |
+|--------|------|
+| `ING-001` | Every entity ingested into DCM must be assigned to exactly one Tenant — either a real Tenant or `__transitional__` — before it is eligible for new requests |
+| `ING-002` | Entities in `INGESTED` or `ENRICHING` state may not be the parent resource for a new allocated resource claim |
+| `ING-003` | The `__transitional__` Tenant is system-managed — cannot be deleted, renamed, or used for new resource provisioning |
+| `ING-004` | Every ingested entity must carry an `ingestion_record` in its provenance chain |
+| `ING-005` | Entities in `__transitional__` beyond `max_residency_days` must trigger the configured escalation action |
+| `ING-006` | A brownfield entity may not be promoted to `PROMOTED` state without explicit actor authorization |
+| `ING-007` | At promotion, the Discovered State record must be promoted to Realized State — this is the moment DCM assumes lifecycle ownership |
+
+---
+
+## 11. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should the auto-assignment signal priority order be configurable per deployment? | Migration flexibility | ✅ Resolved — platform domain layer declares priority; explicit_tenant_tag fixed first; default_tenant fixed last; middle signals configurable (ING-012) |
+| 2 | Can multiple entities be promoted in bulk? | Operational efficiency | ✅ Resolved — bulk promotion supported; profile-governed max batch sizes; preview required; PT24H rollback; BULK_PROMOTE audit (ING-013) |
+| 3 | Should there be a maximum number of ingestion sources per entity? | Data integrity | ✅ Resolved — profile-governed max (5 standard/prod, 3 fsi/sovereign); warn or reject on exceed (ING-014) |
+| 4 | How does ingestion interact with the Service Catalog? | Catalog model | ✅ Resolved — ingested entities promotable to catalog items; bidirectional drift detection (ING-015) |
+
+---
+
+## 12. Related Concepts
+
+- **`__transitional__` Tenant** — system-managed holding Tenant for unassigned ingested entities
+- **Ingestion Record** — provenance record carried by every ingested entity
+- **Four States** — Discovered State is the entry point for brownfield ingestion; Realized State is the output of promotion
+- **Brownfield** — existing infrastructure not yet under DCM lifecycle management
+- **Drift Detection** — begins for brownfield entities at the moment of promotion
+- **Greening the Brownfield** — the progressive process of bringing unmanaged infrastructure under DCM lifecycle control
+
+
+## 8. Ingestion Gap Resolutions
+
+### 8.1 Configurable Signal Priority Order (Q1)
+
+The ingestion signal priority order is declared in a platform-domain layer and configurable per deployment. `explicit_tenant_tag` always has highest priority; `default_tenant` always has lowest. The middle signals may be reordered.
+
+```yaml
+layer:
+  handle: "platform/ingestion/signal-priority"
+  domain: platform
+  fields:
+    ingestion_signal_priority:
+      - explicit_tenant_tag         # fixed: always first
+      - provider_declared_tenant    # configurable order
+      - network_segment_mapping     # configurable order
+      - hardware_class_mapping      # configurable order
+      - geographic_location         # configurable order
+      - default_tenant              # fixed: always last
+```
+
+### 8.2 Bulk Entity Promotion (Q2)
+
+Bulk promotion is supported with profile-governed limits and approval requirements.
+
+```yaml
+bulk_promotion_config:
+  max_entities_per_bulk: 500        # configurable per profile
+  requires_approval: true
+  preview_required: true            # must review bulk preview before confirming
+  rollback_window: PT24H
+  audit_record: BULK_PROMOTE        # single audit event with full member list
+```
+
+| Profile | Max per Bulk | Approval Required |
+|---------|-------------|-----------------|
+| minimal | Unlimited | No |
+| dev | 1000 | No |
+| standard | 500 | Recommended |
+| prod | 100 | Yes |
+| fsi | 50 | Yes + dual approval |
+| sovereign | 25 | Yes + dual approval |
+
+### 8.3 Maximum Ingestion Sources per Entity (Q3)
+
+Profile-governed maximum to encourage clear data ownership and manageable conflict resolution.
+
+```yaml
+ingestion_source_limits:
+  max_sources_per_entity: 5         # default for standard/prod
+  on_max_exceeded: 
+  profile_defaults:
+    minimal: unlimited
+    dev: 10
+    standard: 5
+    prod: 5
+    fsi: 3
+    sovereign: 3
+```
+
+### 8.4 Ingestion to Service Catalog Promotion (Q4)
+
+Ingested entities may be promoted to Service Catalog items — the pathway from brownfield discovery to catalog-driven management.
+
+```
+Ingested entity
+  → Operator associates entity with Resource Type Specification
+  → Fields validated against spec
+  → Service Catalog item created from entity's configuration
+  → Entity becomes template ("golden example") for this catalog item
+  → Future requests use catalog item
+  → Drift detection bidirectional:
+      entity drifts from catalog item → drift event
+      catalog item updated → entity flagged for review
+```
+
+### 8.5 System Policies — Ingestion Gaps
+
+| Policy | Rule |
+|--------|------|
+| `ING-012` | Ingestion signal priority order is declared in a platform domain layer and configurable per deployment. explicit_tenant_tag always has highest priority. default_tenant always has lowest priority. Middle signals are reorderable. |
+| `ING-013` | Bulk entity promotion is supported with profile-governed maximum batch sizes and approval requirements. Preview required before confirmation. Rollback window PT24H. Single BULK_PROMOTE audit record with full member list. |
+| `ING-014` | Maximum ingestion sources per entity is profile-governed (default: 5 for standard/prod; 3 for fsi/sovereign). Exceeding the maximum triggers warn or reject per policy. |
+| `ING-015` | Ingested entities may be associated with Resource Type Specifications and promoted to Service Catalog items. Drift detection operates bidirectionally between the ingested entity and its associated catalog item. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/14-policy-profiles.md b/content/docs/architecture/data-model/14-policy-profiles.md
new file mode 100644
index 0000000..7de4888
--- /dev/null
+++ b/content/docs/architecture/data-model/14-policy-profiles.md
@@ -0,0 +1,1311 @@
+# DCM Data Model — Policy Organization: Groups, Profiles, and External Policy Evaluators
+
+
+> **Universal Group Model:** Policy Groups (`group_class: policy_collection`) and Policy Profiles (`group_class: policy_profile`) are expressions of the [Universal Group Model](15-universal-groups.md). The structures defined in this document remain authoritative for policy-specific behavior; the universal model adds composability, cross-type membership, and the ability to include policy groups within composite groups.
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Scoring Model](29-scoring-model.md) | [Context and Purpose](00-context-and-purpose.md) | [Data Layers and Assembly](03-layering-and-versioning.md) | [Entity Relationships](09-entity-relationships.md) | [data stores](11-storage-providers.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + POLICY**
+>
+> Data: Policy Group and Profile artifacts. Policy: concern types and composition
+
+
+
+---
+
+> **Standards and Compliance Reference:** See [Standards and Compliance Catalog](40-standards-catalog.md) for the complete mapping of compliance frameworks (HIPAA, PCI DSS, FedRAMP, NIST SP 800-53, GDPR, DoD IL4) to DCM profiles and system requirements.
+
+## 1. Purpose
+
+DCM's Policy Engine is powerful — but power without usability is a barrier to adoption. This document defines the **policy organization model**: the structures that make DCM easy to configure correctly for any use case, from a home lab evaluation to a sovereign financial services deployment.
+
+Three concepts work together:
+
+- **Policy Groups** — cohesive collections of policies addressing a single identifiable concern (a technology, a compliance standard, a sovereignty requirement, a business process)
+- **Policy Profiles** — complete DCM configurations for a specific use case, composed of Policy Groups
+- **External Policy Evaluators** — external authoritative sources that supply policies directly into DCM, extending the provider model to its fifth type
+
+The relationship is compositional:
+
+```
+Policy Profile     — complete use-case configuration
+  │  composed of
+  ▼
+Policy Groups      — single-concern policy collections
+  │  composed of
+  ▼
+Policies           — individual Transformation / Validation / GateKeeper rules
+  │  optionally sourced from
+  ▼
+External Policy Evaluators   — external authoritative policy sources
+```
+
+---
+
+## 1a. Design Priority Order in Policy Profiles
+
+Profiles implement the DCM design priority order (see [Foundational Abstractions](00-foundations.md)):
+
+1. **Security:** Profile defaults implement security correctly. Lower profiles have less strict enforcement — not absent security.
+2. **Ease of use:** Profile defaults minimize configuration burden. `standard` profile should work for most deployments without customization.
+3. **Extensibility:** Profiles compose with compliance domain overlays. Organizations add compliance requirements additively without rewriting base configuration.
+4. **Fit for purpose:** Every profile must support the complete DCM lifecycle.
+
+**The `minimal` profile is not "security optional"** — it is the security model with minimal operational overhead. All security properties are present; thresholds and automation levels are relaxed.
+
+---
+
+## 1b. Policy Authorship — Federated Contribution Model
+
+Policies in DCM are not exclusively authored by platform admins. The DCM federated contribution model enables all actor types to author policies within their permitted domain scope:
+
+- **Platform admins** — all domains, all policy types
+- **Consumers / Tenant admins** — tenant domain policies (GateKeeper, Transformation, Recovery, Lifecycle, Orchestration Flow, Governance Matrix rules scoped to their Tenant)
+- **Service Providers** — provider-domain GateKeeper and Validation policies for their resource types
+- **Peer DCM instances** — policy templates contributed through verified federation relationships
+
+This is not a special case — it is the standard GitOps PR model applied to all contributor types. Consumer-authored policies go through the same lifecycle (developing → proposed → active) with appropriate review requirements per the active profile. See [Federated Contribution Model](28-federated-contribution-model.md) for the complete specification.
+
+---
+
+## 2. Policy Groups
+## 1a. Two-Dimensional Profile Model
+
+
+### 1a.0 Profile — One Posture, Multiple Compliance Domains
+
+**A DCM deployment runs exactly one Deployment Posture and zero or more Compliance Domain Groups simultaneously.** This is the complete profile model. No new concept is needed.
+
+```
+Active DCM Governance = one Deployment Posture + [zero or more Compliance Domains]
+
+Examples:
+  prod + hipaa                         ← healthcare production
+  prod + hipaa + gdpr                  ← EU healthcare production  
+  sovereign + fedramp-high + dod-il5   ← classified federal
+  standard                             ← general enterprise, no compliance overlay
+  dev + hipaa                          ← healthcare development (hipaa policies active,
+                                          but operational cost reduced by dev posture)
+```
+
+**Deployment Postures are mutually exclusive** — you cannot be both `prod` and `dev`. One posture governs the operational characteristics of the entire DCM deployment.
+
+**Compliance domains are additive** — HIPAA + FSI is valid and common. Each compliance domain group adds its own set of policies and constraints on top of the posture. They do not conflict with each other at the domain level (they govern different aspects of data handling); they may produce policy conflicts at the field level, which are resolved through the standard policy conflict resolution process.
+
+**The dev posture and compliance domains** — applying `dev` posture to a HIPAA-scoped deployment does not remove HIPAA obligations. It relaxes the *operational cost* of meeting them: less redundancy, shorter retention windows where permitted, advisory enforcement where HIPAA allows flexibility. The HIPAA compliance domain group remains active and its mandatory controls remain enforced.
+
+**Modules vs Profiles** — DCM uses both concepts with distinct meanings:
+- A **Profile** is a governance configuration: it declares how DCM behaves and what operational and compliance requirements apply.
+- A **Module** is a capability extension: it adds new functions to DCM (e.g., a HIPAA record validator, a custom resource type). Modules are not profiles and do not configure DCM behavior — they extend what DCM can do.
+
+
+### 1a.1 The Gap in the Original Model
+
+The original six profiles (minimal → sovereign) are organized around **deployment posture** — how strict, how redundant, how governed. But organizations need compliance governance that is orthogonal to posture. A healthcare organization needs HIPAA controls regardless of whether they deploy at `standard` or `sovereign` posture. A payment processor needs PCI-DSS regardless of their redundancy profile.
+
+The new model makes compliance a first-class dimension that composes with posture:
+
+```
+Complete Profile = Deployment Posture Group + Compliance Domain Group(s)
+```
+
+### 1a.2 Dimension 1 — Deployment Posture Groups
+
+Posture groups govern how the DCM infrastructure itself behaves — redundancy, enforcement strictness, audit retention, tenancy model, cross-tenant defaults. These are the vertical axis from least to most governed.
+
+| Group Handle | Posture | Key Behaviors |
+|-------------|---------|--------------|
+| `system/group/posture-minimal` | Minimal | Advisory only; single instance; no redundancy |
+| `system/group/posture-dev` | Development | Warn-not-block; basic logging; ephemeral defaults |
+| `system/group/posture-standard` | Standard | Full enforcement; 3-replica; explicit cross-tenant |
+| `system/group/posture-prod` | Production | Full enforcement + SLA; geo-replicated; cost governance |
+| `system/group/posture-hardened` | Enterprise Hardened | 5-replica; 7-year audit; hard tenancy; dual approval |
+| `system/group/posture-sovereign` | Sovereign | Air-gap; deny_all cross-tenant; 10-year audit; signed bundles |
+
+### 1a.3 Dimension 2 — Compliance Domain Groups
+
+Compliance domain groups govern what data handling, audit, and control requirements apply to resources managed by DCM. Multiple compliance domains can apply simultaneously.
+
+| Group Handle | Domain | Key Controls |
+|-------------|--------|-------------|
+| `system/group/compliance-fsi` | Financial Services | Basel III, SOX, Dodd-Frank financial controls |
+| `system/group/compliance-pci-dss` | Payment Card | PCI-DSS v4 — payment card industry |
+| `system/group/compliance-hipaa` | Healthcare | HIPAA/HITECH PHI handling and audit |
+| `system/group/compliance-fedramp-moderate` | US Federal Moderate | FedRAMP Moderate — NIST 800-53 Moderate baseline |
+| `system/group/compliance-fedramp-high` | US Federal High | FedRAMP High — NIST 800-53 High baseline |
+| `system/group/compliance-dod-il2` | DoD IL2 | Public/unclassified defense data |
+| `system/group/compliance-dod-il4` | DoD IL4 | Controlled Unclassified Information (CUI) |
+| `system/group/compliance-dod-il5` | DoD IL5 | National Security Systems non-classified |
+| `system/group/compliance-dod-il6` | DoD IL6 | Classified — maximum sovereign posture |
+| `system/group/compliance-government` | Government General | General government/public sector controls |
+| `system/group/compliance-gdpr` | EU Data Protection | GDPR data residency and rights |
+| `system/group/compliance-iso27001` | ISO 27001 | Information security management |
+| `system/group/compliance-nist-800-53` | NIST 800-53 | NIST security control framework |
+| `system/group/compliance-soc2` | SOC 2 | Service organization controls |
+| `system/group/compliance-nerc-cip` | Critical Infrastructure | Energy/utilities NERC CIP |
+| `system/group/compliance-sovereign` | Sovereign/Classified | Sovereign deployment, air-gap, classified |
+
+### 1a.4 Compliance Domain Group Contents
+
+#### `system/group/compliance-hipaa`
+
+HIPAA/HITECH controls for Protected Health Information (PHI):
+- PHI field classification enforcement (fields containing PHI must be tagged `phi: true`)
+- PHI access control (only roles with `phi_authorized: true` may access PHI-tagged fields)
+- Audit retention: P6Y minimum (HIPAA requires 6 years from creation or last use)
+- Encryption at rest: AES-256 required for all PHI storage
+- Transmission security: TLS 1.3 minimum for PHI in transit
+- Breach notification workflow: sovereignty_violation_record triggers HIPAA breach assessment
+- Business Associate Agreement (BAA) tracking: providers handling PHI must declare `baa_in_place: true` in sovereignty_declaration
+- Right to Access: consumer data export capability required for PHI entities
+- Minimum Necessary standard: data_request_spec on Mode 4 providers limited to minimum PHI fields
+
+#### `system/group/compliance-government`
+
+General government and public sector controls:
+- Data classification mandatory on all resources (classification_level field required)
+- Cross-boundary controls: data cannot cross classification levels without explicit policy
+- Audit retention: P10Y minimum
+- Air-gap capability required for Sensitive compartments
+- Actor authentication must declare clearance level in external_identity claims
+- All provider sovereignty_declarations must declare government_access_risk
+
+#### `system/group/compliance-fedramp-moderate`
+
+FedRAMP Moderate authorization controls:
+- NIST 800-53 Rev 5 Moderate baseline implemented as policy group
+- FedRAMP-authorized provider preference injected as placement constraint
+- Continuous monitoring: drift detection mandatory; P24H maximum drift resolution window
+- Incident response: webhook required for security events (audit.chain_break, drift.escalated)
+- POA&M tracking: `poam_status` field in status_metadata on all policy artifacts
+- Boundary protection: explicit ingress/egress control documentation
+
+#### `system/group/compliance-dod-il4`
+
+DoD Impact Level 4 — Controlled Unclassified Information:
+- Inherits compliance-fedramp-moderate
+- CUI handling markers on all data fields containing controlled information
+- Sovereign posture within US jurisdiction boundary
+- Provider sovereignty_declaration must exclude foreign sub-processors
+- CMMC Level 2 cyber hygiene controls
+
+#### `system/group/compliance-sovereign`
+
+Sovereign and classified deployment controls:
+- All data must remain within declared sovereignty boundary
+- Air-gap capability: provider air_gap_capable: true required
+- Signed bundle import only — no live registry connectivity
+- deny_all cross-tenant cross-boundary data flows
+- Hardware security module (HSM) required for key management
+- Audit records: 10-year retention; cryptographic signing required
+
+### 1a.5 Profile Composition Model
+
+The six built-in profiles become explicit posture+compliance compositions:
+
+```yaml
+system/profile/minimal:
+  policy_groups: [system/group/posture-minimal]
+
+system/profile/dev:
+  extends: system/profile/minimal
+  policy_groups: [system/group/posture-dev]
+
+system/profile/standard:
+  extends: system/profile/dev
+  policy_groups: [system/group/posture-standard]
+
+system/profile/prod:
+  extends: system/profile/standard
+  policy_groups: [system/group/posture-prod]
+
+system/profile/fsi:
+  extends: system/profile/prod
+  policy_groups:
+    - system/group/posture-hardened
+    - system/group/compliance-fsi
+    - system/group/compliance-pci-dss
+    - system/group/compliance-iso27001
+
+system/profile/sovereign:
+  extends: system/profile/fsi
+  policy_groups:
+    - system/group/posture-sovereign
+    - system/group/compliance-sovereign
+```
+
+### 1a.6 DCM Built-In Extended Profiles
+
+In addition to the six core profiles, DCM ships extended profiles for common compliance domains:
+
+```yaml
+system/profile/hipaa-prod:
+  extends: system/profile/prod
+  policy_groups:
+    - system/group/compliance-hipaa
+    - system/group/compliance-iso27001
+  description: "Production infrastructure with HIPAA/HITECH compliance"
+
+system/profile/hipaa-sovereign:
+  extends: system/profile/sovereign
+  policy_groups:
+    - system/group/compliance-hipaa
+  description: "Sovereign deployment with HIPAA/HITECH compliance — highest healthcare posture"
+
+system/profile/fedramp-moderate:
+  extends: system/profile/prod
+  policy_groups:
+    - system/group/compliance-fedramp-moderate
+    - system/group/compliance-nist-800-53
+  description: "FedRAMP Moderate authorized deployment"
+
+system/profile/fedramp-high:
+  extends: system/profile/sovereign
+  policy_groups:
+    - system/group/compliance-fedramp-high
+    - system/group/compliance-nist-800-53
+  description: "FedRAMP High authorized deployment"
+
+system/profile/government:
+  extends: system/profile/prod
+  policy_groups:
+    - system/group/compliance-government
+    - system/group/compliance-nist-800-53
+  description: "General government and public sector deployment"
+
+system/profile/dod-il4:
+  extends: system/profile/sovereign
+  policy_groups:
+    - system/group/compliance-dod-il4
+    - system/group/compliance-fedramp-high
+    - system/group/compliance-nist-800-53
+  description: "DoD Impact Level 4 — Controlled Unclassified Information"
+
+system/profile/dod-il5:
+  extends: system/profile/dod-il4
+  policy_groups:
+    - system/group/compliance-dod-il5
+  description: "DoD Impact Level 5 — National Security Systems non-classified"
+
+system/profile/dod-il6:
+  extends: system/profile/dod-il5
+  policy_groups:
+    - system/group/compliance-dod-il6
+    - system/group/compliance-sovereign
+  description: "DoD Impact Level 6 — Classified"
+```
+
+### 1a.7 Organization Custom Profiles — Composition Examples
+
+```yaml
+# Healthcare with federal cloud authorization
+org/profile/hipaa-fedramp:
+  extends: system/profile/fedramp-moderate
+  policy_groups:
+    - system/group/compliance-hipaa
+  description: "Healthcare workloads on FedRAMP Moderate platform"
+
+# Multi-compliance financial + healthcare
+org/profile/fsi-hipaa:
+  extends: system/profile/fsi
+  policy_groups:
+    - system/group/compliance-hipaa
+  description: "FSI-grade deployment for organizations managing both financial and health data"
+
+# Tenant-level compliance override — platform is standard; this Tenant is PCI-scoped
+# (declared in tenant_config — not profile)
+tenant_compliance_overlay:
+  active_profile: system/profile/standard    # platform posture
+  compliance_groups:
+    - system/group/compliance-pci-dss        # this Tenant handles payment cards
+    - system/group/compliance-hipaa          # this Tenant also handles PHI
+  # Other Tenants on same platform have standard posture, no compliance overlay
+```
+
+### 1a.8 Compliance at Tenant Level
+
+Compliance domain groups may apply at Tenant level — different Tenants on the same DCM deployment can have different compliance domains:
+
+```yaml
+tenant_config:
+  active_profile: system/profile/prod        # posture from platform
+  compliance_groups:
+    - system/group/compliance-hipaa          # this Tenant handles PHI
+    - system/group/compliance-pci-dss        # this Tenant processes payments
+```
+
+This is the critical capability: **one DCM deployment, multiple compliance postures per Tenant**. A hospital system can run a single DCM with: clinical Tenants (HIPAA), billing Tenants (HIPAA + PCI-DSS), and administrative Tenants (standard) — all on the same platform profile.
+
+### 1a.9 System Policies — Profile Composition
+
+| Policy | Rule |
+|--------|------|
+| `PROF-001` | Profiles compose a Deployment Posture Group with zero or more Compliance Domain Groups. Posture groups govern DCM infrastructure behavior. Compliance domain groups govern data handling, audit, and control requirements. |
+| `PROF-002` | Compliance Domain Groups may be applied at platform level (all Tenants) or Tenant level (specific Tenants). Tenant-level compliance groups are additive — they do not replace platform-level groups. |
+| `PROF-003` | DCM ships built-in Compliance Domain Groups for: FSI, PCI-DSS, HIPAA/HITECH, FedRAMP Moderate, FedRAMP High, DoD IL2-IL6, Government, GDPR, ISO 27001, NIST 800-53, SOC2, NERC-CIP, and Sovereign/Classified. Organizations extend these groups or compose them into custom profiles. |
+| `PROF-004` | The `implementation_posture` concern_type Policy Groups (provenance model, auth simplicity, deployment complexity) are independent of compliance domain — organizations select their implementation posture separately from their compliance requirements. |
+
+---
+
+
+### 2.1 Definition
+
+A **Policy Group** is a versioned, cohesive collection of policies that together address a **single identifiable concern**. The group is the unit of reuse — activate a group to enable a concern, not individual policies.
+
+```yaml
+policy_group:
+  artifact_metadata:
+    uuid: 
+    handle: "system/group/pci-dss"
+    version: "1.2.0"
+    status: active
+    owned_by:
+      display_name: "DCM Project Team"
+      notification_endpoint: 
+
+  name: "PCI-DSS v4"
+  description: >
+    Policy group implementing PCI-DSS v4 controls relevant to
+    DCM-managed infrastructure. Enforces encryption standards,
+    network segmentation, access control, and audit requirements
+    for resources in PCI scope.
+
+  concern_type: compliance
+  concern_tags: [pci-dss, financial, encryption, network-segmentation]
+  extends: null   # or another group handle — inherits all parent policies
+
+  # Source — locally authored or from a External Policy Evaluator
+  source:
+    type: 
+    provider_uuid: 
+    provider_group_reference: 
+    on_provider_update: 
+    # proposed: provider updates require local review before activation
+    # active:   provider updates activate immediately (trusted providers only)
+
+  # Constituent policies
+  policies:
+    - policy_uuid: 
+      handle: "system/gatekeeper/pci-encryption-aes256"
+      description: "Enforce AES-256 on all PCI-scoped storage"
+      placement_phase: pre
+    - policy_uuid: 
+      handle: "system/validation/pci-network-segmentation"
+      description: "Validate network segment isolation for PCI resources"
+      placement_phase: pre
+    - policy_uuid: 
+      handle: "system/transformation/pci-classification-inject"
+      description: "Auto-inject PCI classification on scoped resources"
+      placement_phase: pre
+    - policy_uuid: 
+      handle: "system/gatekeeper/pci-audit-retention"
+      description: "Enforce 10-year audit retention for PCI evidence"
+      placement_phase: pre
+
+  # Activation scope — surgical application within a profile
+  activation_scope:
+    resource_types: []          # empty = all resource types
+    tenant_tags: [pci-scope]    # only Tenants tagged pci-scope
+    regions: []                 # empty = all regions
+
+  # Conflict declarations
+  conflicts_with:
+    - group_handle: "system/group/dev-defaults"
+      reason: "PCI requires blocking enforcement; dev-defaults uses warn-only"
+      resolution: this_group_wins
+```
+
+### 2.2 Concern Types
+
+| Type | Description | Examples |
+|------|-------------|---------|
+| `technology` | Policies specific to a technology or provider | kubevirt, openstack, kubernetes, vmware |
+| `compliance` | Regulatory or standards compliance | pci-dss, iso-27001, nist-800-53, fedramp |
+| `sovereignty` | Data residency, jurisdictional, air-gap | gdpr-eu, air-gap, data-residency-uk |
+| `business` | Business process, cost, lifecycle governance | cost-governance, ephemeral-resources, chargeback |
+| `operational` | Operational posture, defaults, SLAs | dev-defaults, hard-tenancy, sla-enforcement |
+| `security` | Security controls and posture | data-classification, zero-trust, encryption-baseline |
+| `implementation_posture` | Implementation complexity vs capability trade-offs | provenance-full-inline, provenance-deduplicated, single-instance-deployment, advisory-policies-only |
+
+### 2.3 Group Inheritance
+
+A Policy Group may extend another group — inheriting all its policies and overriding or adding to them:
+
+```yaml
+policy_group:
+  handle: "org/group/pci-dss-extended"
+  extends: "system/group/pci-dss"
+  # Inherits all system/group/pci-dss policies
+  # Additional policies added below are on top of the parent
+  policies:
+    - policy_uuid: 
+      handle: "org/gatekeeper/our-pci-additional-control"
+```
+
+### 2.4 DCM Built-In Policy Groups
+
+DCM ships the following policy groups as part of its standard distribution:
+
+| Handle | Concern | Description |
+|--------|---------|-------------|
+| `system/group/core-minimal` | operational | Absolute minimum — UUID requirements, basic well-formedness |
+| `system/group/dev-defaults` | operational | Warn-not-block, 90-day TTL defaults, single-auth cross-tenant |
+| `system/group/ephemeral-resources` | business | TTL enforcement, auto-expiry, short-lived resource defaults |
+| `system/group/audit-basic` | operational | Basic audit logging, 90-day retention |
+| `system/group/audit-compliance` | compliance | Compliance-grade audit, configurable retention |
+| `system/group/data-classification` | security | Data classification tagging and handling rules |
+| `system/group/cost-governance` | business | Budget enforcement, cost attribution, TTL governance |
+| `system/group/sla-enforcement` | operational | SLA tracking, availability commitments |
+| `system/group/hard-tenancy` | operational | Full tenant isolation, deny_all cross-tenant default |
+| `system/group/explicit-cross-tenant` | operational | Explicit cross-tenant authorization requirement (XTA-001 through XTA-005) |
+| `system/group/zero-trust` | security | Zero-trust network and identity enforcement |
+| `system/group/encryption-baseline` | security | AES-256 minimum, TLS 1.3, key rotation |
+| `system/group/pci-dss` | compliance | PCI-DSS v4 controls |
+| `system/group/gdpr-eu` | sovereignty | GDPR data residency and handling |
+| `system/group/nist-800-53` | compliance | NIST 800-53 control implementation |
+| `system/group/iso-27001` | compliance | ISO 27001 controls |
+| `system/group/fedramp-moderate` | compliance | FedRAMP Moderate baseline |
+| `system/group/air-gap` | sovereignty | Air-gapped deployment constraints |
+| `system/group/fsi-audit` | compliance | Financial services audit retention (7-year minimum) |
+| `system/group/lifecycle-ttl-enforcement` | operational | Lifecycle time constraint enforcement (LTC-001 through LTC-004) |
+| `system/group/kubevirt` | technology | KubeVirt provider-specific policies |
+| `system/group/openstack` | technology | OpenStack provider-specific policies |
+| `system/group/vmware` | technology | VMware provider-specific policies |
+| `system/group/provenance-full-inline` | implementation_posture | Model A — all provenance inline on entity records; simplest; highest storage cost |
+| `system/group/provenance-deduplicated` | implementation_posture | Model B — content-addressed deduplication; recommended; 95-99% storage reduction; lossless |
+| `system/group/provenance-tiered-archive` | implementation_posture | Model C — hot/warm/cold tiers; balances cost and access speed |
+| `system/group/provenance-deduplicated-tiered` | implementation_posture | Model B+C — maximum efficiency for very large-scale deployments |
+
+---
+
+## 3. Policy Profiles
+
+### 3.1 Definition
+
+A **Policy Profile** is a named, versioned, curated composition of Policy Groups that together configure DCM for a specific use case. Activating a profile is the primary configuration mechanism — most deployments should activate a profile and then add organization-specific groups on top rather than configuring policies individually.
+
+```yaml
+policy_profile:
+  artifact_metadata:
+    uuid: 
+    handle: "system/profile/fsi"
+    version: "1.0.0"
+    status: active
+    owned_by:
+      display_name: "DCM Project Team"
+
+  name: "FSI Production"
+  description: >
+    Policy profile for Financial Services production deployments.
+    Enforces hard tenancy, regulatory-grade audit retention,
+    full sovereignty controls, and explicit cross-tenant authorization.
+
+  target_use_case: fsi_production
+  extends: "system/profile/prod"   # inherits all prod groups + overrides
+
+  enforcement_summary:
+    tenancy: hard_tenancy_required
+    audit_retention_years: 7
+    sovereignty: full
+    cross_tenant_default: explicit_only
+    policy_enforcement: blocking
+    immutable_fields: [sovereignty_zone, classification_level, audit_retention]
+    policy_version_pinning: permitted_with_elevation
+
+  policy_groups:
+    - group_handle: "system/group/fsi-audit"
+    - group_handle: "system/group/pci-dss"
+    - group_handle: "system/group/gdpr-eu"
+    - group_handle: "system/group/hard-tenancy"
+    - group_handle: "system/group/explicit-cross-tenant"
+    - group_handle: "system/group/encryption-baseline"
+```
+
+### 3.2 DCM Built-In Profiles
+
+DCM ships six profiles covering the spectrum from minimal to sovereign:
+
+#### `system/profile/minimal` — Home Lab / Evaluation
+
+```yaml
+handle: "system/profile/minimal"
+name: "Minimal"
+extends: null
+description: >
+  Minimal configuration for home lab, local testing, and evaluation.
+  Most controls advisory only. Single Tenant auto-created on first use.
+  No audit requirements. No sovereignty enforcement.
+
+enforcement_summary:
+  tenancy: optional
+  audit_retention: none
+  sovereignty: none
+  cross_tenant_default: allow_all
+  policy_enforcement: advisory     # policies warn but do not block
+  time_constraints: optional
+
+auto_tenant:
+  enabled: true
+  default_tenant_handle: "default"
+  # TEN-001 satisfied silently — no explicit Tenant declaration required
+
+policy_groups:
+  - group_handle: "system/group/core-minimal"
+```
+
+#### `system/profile/dev` — Development Environments
+
+```yaml
+handle: "system/profile/dev"
+name: "Development"
+extends: "system/profile/minimal"
+description: >
+  Development environment profile. Tenancy recommended but not blocking.
+  Basic logging. Ephemeral resource defaults. Warn-not-block enforcement.
+
+enforcement_summary:
+  tenancy: recommended
+  audit_retention: basic
+  sovereignty: none
+  cross_tenant_default: operational_only
+  policy_enforcement: warn_only
+  default_ttl: P90D              # dev resources default to 90-day TTL
+
+policy_groups:
+  - group_handle: "system/group/dev-defaults"
+  - group_handle: "system/group/ephemeral-resources"
+  - group_handle: "system/group/audit-basic"
+```
+
+#### `system/profile/standard` — General Enterprise Production
+
+```yaml
+handle: "system/profile/standard"
+name: "Standard"
+extends: "system/profile/dev"
+description: >
+  General enterprise production profile. Full policy enforcement.
+  Tenancy required. Explicit cross-tenant authorization. Basic
+  data classification. Compliance-grade audit.
+
+enforcement_summary:
+  tenancy: required
+  audit_retention: compliance_grade
+  sovereignty: configurable
+  cross_tenant_default: explicit_only
+  policy_enforcement: blocking
+
+policy_groups:
+  - group_handle: "system/group/data-classification"
+  - group_handle: "system/group/audit-compliance"
+  - group_handle: "system/group/explicit-cross-tenant"
+  - group_handle: "system/group/encryption-baseline"
+```
+
+#### `system/profile/prod` — Production with SLA Requirements
+
+```yaml
+handle: "system/profile/prod"
+name: "Production"
+extends: "system/profile/standard"
+description: >
+  Production profile with SLA enforcement, cost governance, and
+  full lifecycle constraint enforcement.
+
+enforcement_summary:
+  tenancy: required
+  audit_retention: compliance_grade
+  sovereignty: configurable
+  cross_tenant_default: explicit_only
+  policy_enforcement: blocking
+
+policy_groups:
+  - group_handle: "system/group/cost-governance"
+  - group_handle: "system/group/sla-enforcement"
+  - group_handle: "system/group/lifecycle-ttl-enforcement"
+```
+
+#### `system/profile/fsi` — Financial Services Production
+
+```yaml
+handle: "system/profile/fsi"
+name: "FSI Production"
+extends: "system/profile/prod"
+description: >
+  Financial services production. Hard tenancy. 7-year audit retention.
+  Full sovereignty enforcement. PCI-DSS and GDPR compliance.
+
+enforcement_summary:
+  tenancy: hard_tenancy_required
+  audit_retention_years: 7
+  sovereignty: full
+  cross_tenant_default: explicit_only
+  policy_enforcement: blocking
+
+policy_groups:
+  - group_handle: "system/group/fsi-audit"
+  - group_handle: "system/group/pci-dss"
+  - group_handle: "system/group/gdpr-eu"
+  - group_handle: "system/group/hard-tenancy"
+```
+
+#### `system/profile/sovereign` — Air-Gapped / Sovereign Deployments
+
+```yaml
+handle: "system/profile/sovereign"
+name: "Sovereign"
+extends: "system/profile/fsi"
+description: >
+  Maximum control profile for air-gapped, sovereign, and highest-security
+  deployments. Complete tenant isolation. Zero external dependencies.
+  Maximum audit and sovereignty enforcement.
+
+enforcement_summary:
+  tenancy: hard_tenancy_required
+  audit_retention_years: 10
+  sovereignty: maximum
+  cross_tenant_default: deny_all
+  policy_enforcement: blocking
+  immutable_ceiling_on_all_sovereignty_fields: true
+
+policy_groups:
+  - group_handle: "system/group/air-gap"
+  - group_handle: "system/group/zero-trust"
+```
+
+### 3.3 Profile Inheritance Chain
+
+```
+system/profile/sovereign
+  extends: system/profile/fsi
+    extends: system/profile/prod
+      extends: system/profile/standard
+        extends: system/profile/dev
+          extends: system/profile/minimal
+            extends: null (base)
+```
+
+Each level adds groups without replacing parent groups. An organization extending a DCM profile only needs to declare what differs:
+
+```yaml
+# Organization custom profile
+org/profile/my-prod:
+  extends: system/profile/prod
+  policy_groups:
+    - group_handle: "org/group/our-naming-conventions"
+    - group_handle: "org/group/our-cost-centers"
+    - group_handle: "system/group/iso-27001"    # add a DCM compliance group
+```
+
+### 3.4 Profile Activation
+
+Profiles activate at three levels — more specific takes precedence:
+
+```yaml
+# DCM installation default
+installation_config:
+  default_profile: "system/profile/minimal"
+
+# Platform-level (applies to all Tenants)
+platform_config:
+  active_profile: "system/profile/prod"
+  minimum_tenant_profile: "system/profile/dev"   # Tenants cannot go below this
+  maximum_tenant_profile: null                    # null = no ceiling
+
+# Tenant-level override
+tenant_config:
+  active_profile: "system/profile/fsi"           # must be >= minimum_tenant_profile
+```
+
+**A Tenant cannot activate a profile less restrictive than the platform minimum.** A sovereign deployment can set `minimum_tenant_profile: system/profile/sovereign` — no Tenant can drop below that level.
+
+### 3.5 Profile Conflict Resolution
+
+When a profile is activated, DCM runs conflict detection across all constituent group policies — same ingestion conflict detection that layers use. Conflicts must be resolved before a profile is marked `active`.
+
+Conflict resolution order:
+1. **Explicit `conflicts_with` declarations** on groups — use declared resolution rule
+2. **Priority schema** — higher numeric priority wins
+3. **Domain authority** — `system` beats `platform` beats `tenant`
+4. **Unresolved** — profile activation fails with detailed conflict report
+
+### 3.6 Profile Shadow Validation
+
+When a profile is in `proposed` status, all its constituent policies run in shadow mode — the same proposed policy shadow execution model. The Validation Dashboard shows the aggregate impact across all policies in the profile before activation. This enables safe preview of what a profile upgrade would do to an existing deployment.
+
+---
+
+## 4. Policy Evaluation Modes
+
+DCM supports two policy evaluation modes. The distinction is whether DCM or an external system performs the evaluation — not how policies are delivered to the evaluator.
+
+### 4.1 Internal Mode — DCM Evaluates
+
+In Internal mode, the Policy Manager evaluates all policies using its embedded OPA engine. Policies can arrive through any delivery mechanism:
+
+| Delivery | Description |
+|----------|-------------|
+| **API / GitOps** | Policies stored in DCM's database, managed via API or Git ingress adapter |
+| **OPA Bundle** | Standard OPA bundle protocol — point OPA at a bundle server URL |
+| **External Schema** | Policies in non-Rego format (e.g., XACML, custom JSON) naturalized to Rego by DCM before evaluation |
+
+All three delivery mechanisms result in the same thing: Rego policies evaluated by OPA against the request payload. Where OPA runs (embedded Go library, sidecar container, or remote OPA instance) is a deployment topology decision — not a mode distinction.
+
+**Policy registration:**
+```yaml
+policy:
+  handle: "vm-size-limits"
+  policy_type: gatekeeper
+  delivery:
+    mode: push                              # or: pull, opa_bundle, external_schema
+    source_url: "https://git.corp/policies" # for pull/bundle modes
+    format: rego                            # or: xacml, custom_json (naturalized to rego)
+  activation: active                        # or: proposed (shadow mode)
+  trust_level: trusted                      # trusted, verified, untrusted
+```
+
+**Trust levels (Internal mode):**
+- `trusted` — GateKeeper authority (can deny requests)
+- `verified` — Transformation and Validation authority only
+- `untrusted` — advisory only (shadow mode enforcement)
+
+### 4.2 External Mode — External Provider Evaluates
+
+In External mode, DCM sends evaluation context to an external endpoint. The external system evaluates and/or enriches the data, and returns a structured result. DCM does not see the policy logic — it trusts the results within scoped bounds.
+
+**External evaluation can:**
+- **Evaluate** — return pass/fail, score, or recommendation
+- **Enrich** — inject additional fields into the payload (risk scores, compliance citations, cost predictions, organizational context)
+- **Both** — combined decision + enrichment in a single response
+
+**Registration:**
+```yaml
+policy:
+  handle: "compliance-scanner"
+  policy_type: validation
+  delivery:
+    mode: external                          # External mode
+    endpoint: "https://compliance.corp/api/evaluate"
+    auth: mtls
+  data_request_spec:                        # data minimization — only declared fields sent
+    fields: [resource_type, sovereignty_zone, data_classification, tenant_uuid]
+  on_unavailable: gatekeep                  # fail-closed — unknown is not safe
+  trust_level: verified                     # minimum verified for enrichment
+```
+
+### 4.3 External Mode Governance (BBQ-001 through BBQ-009)
+
+External evaluation introduces governance concerns that Internal mode does not:
+
+| ID | Requirement |
+|----|-------------|
+| BBQ-001 | Data sovereignty check before any query is sent to an external endpoint |
+| BBQ-002 | Data minimization — only fields declared in `data_request_spec` are sent |
+| BBQ-003 | If the external endpoint is outside the entity's sovereignty zone, the query is blocked unless explicitly authorized |
+| BBQ-004 | Full audit record per query-response cycle, including `audit_token` for cross-system correlation |
+| BBQ-005 | Default failure behavior is `gatekeep` — if the external system is unavailable, the request is denied (fail-closed) |
+| BBQ-006 | Cached results must include the original query timestamp and validity period in provenance |
+| BBQ-007 | Fields injected by external enrichment carry standard field-level provenance: `source_type: external_external_policy_evaluator`, `source_uuid`, and `audit_token` |
+| BBQ-008 | The override control model applies to enrichment-injected fields — a GateKeeper policy may restrict or refuse external enrichment on specific fields |
+| BBQ-009 | External enrichment requires minimum `verified` trust level; GateKeeper authority requires `trusted` with dual-approval elevation |
+
+### 4.4 Policy Sources and Policy Groups
+
+Policies from any source (Internal or External) participate in the same Policy Group mechanism. A Policy Group composes multiple policies into a named, versioned, reviewable unit with explicit conflict declarations:
+
+```yaml
+policy_group:
+  handle: "pci-dss-v4-controls"
+  policies:
+    - ref: "card-data-encryption"           # Internal — Rego policy in DCM
+    - ref: "network-segmentation-check"     # Internal — OPA bundle
+    - ref: "compliance-scanner"             # External — calls external endpoint
+  activation_scope:
+    resource_types: ["*"]
+    tenant_tags: ["pci"]
+```
+
+### 4.5 Policy Health and Lifecycle
+
+- **Internal policies:** Health is determined by OPA engine health. If OPA is unavailable, all Internal policies are degraded.
+- **External policies:** Health is determined by endpoint availability. Each external endpoint has a health check (HTTP GET to a declared health URL). Unhealthy external policies trigger their `on_unavailable` behavior (default: `gatekeep`).
+- **Deprecation:** Policies follow the `active → deprecated → retired` lifecycle. Deprecated policies fire with a warning in the audit trail. Retired policies are no longer evaluated.
+
+---
+
+## 5. Lifecycle Time Constraints
+
+### 5.1 Concept
+
+Lifecycle time constraints declare **when a resource should cease to exist or trigger a lifecycle action**. They are a first-class field on any resource entity — not metadata, not a tag, but a governed field that follows the standard data model precedence and override control.
+
+### 5.2 Constraint Types
+
+| Type | Format | Description |
+|------|--------|-------------|
+| `ttl` | ISO 8601 duration (e.g., `P14D`) | Relative — expires N time after the reference point |
+| `expires_at` | ISO 8601 timestamp | Absolute — expires at a specific calendar date/time |
+
+When both are declared, the **earliest expiry wins** (LTC-004).
+
+### 5.3 Data Model
+
+```yaml
+lifecycle_constraints:
+  ttl:
+    duration: P14D                       # ISO 8601 duration — 14 days
+    reference_point: realization_timestamp  # created_at | realization_timestamp | last_modified
+    on_expiry: 
+    metadata:
+      override: allow                    # standard override control applies
+      basis_for_value: "Consumer declared ephemeral — 14-day lab resource"
+
+  expires_at:
+    timestamp: "2026-06-30T23:59:59Z"
+    on_expiry: notify
+    metadata:
+      override: immutable
+      locked_by_policy_uuid: 
+      basis_for_value: "Project deadline — resource must not persist beyond Q2"
+
+  # Enforcement behavior
+  enforcement:
+    warn_before_expiry: P1D              # warn 1 day before expiry
+    warn_notification_endpoint: 
+    grace_period: PT1H                  # 1 hour grace after expiry before action
+    on_grace_period_expiry: 
+```
+
+### 5.4 Precedence
+
+Lifecycle time constraints follow the standard data model precedence chain:
+
+```
+Base Layer default (lowest — e.g., "no TTL by default")
+  ↓
+Core Layer (e.g., "all dev environment resources: TTL 90 days")
+  ↓
+Service Layer (e.g., "ephemeral compute: TTL 7 days")
+  ↓
+Request Layer (consumer declared TTL)
+  ↓
+Transformation Policy (enrich TTL from business context)
+  ↓
+GateKeeper Policy (highest — may lock TTL as immutable)
+```
+
+A consumer can declare `ttl: P14D` in their request. A GateKeeper policy can override this to `P7D` and lock it immutable if organizational policy mandates shorter maximum lifetimes. A Core Layer can set default TTLs for resource classes. The provenance chain records every modification.
+
+### 5.5 Expiry Enforcement
+
+The **Lifecycle Constraint Enforcer** is a DCM control plane component that:
+- Monitors all realized entities against their declared lifecycle constraints
+- Fires the configured `on_expiry` action when a constraint is reached
+- Records the enforcement action in provenance and the Audit Store
+- Emits expiry warnings `warn_before_expiry` duration before the deadline
+
+Expiry enforcement is a DCM concern — not a provider concern. The provider does not need to know about or implement TTL logic.
+
+### 5.6 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `LTC-001` | Lifecycle time constraints follow standard data model precedence — layers, request, policies |
+| `LTC-002` | GateKeeper policies may lock lifecycle constraints as `override: immutable` or `immutable_ceiling: absolute` |
+| `LTC-003` | Expiry enforcement is a DCM control plane function — not a provider concern |
+| `LTC-004` | When multiple time constraints exist on an entity, the earliest expiry wins |
+| `LTC-005` | Expired entities that fail to execute their `on_expiry` action enter `PENDING_EXPIRY_ACTION` state and trigger an escalation |
+
+---
+
+## 6. Cross-Tenancy Authorization Model
+
+### 6.1 Default Stance — Closed
+
+Cross-tenant information sharing is **closed by default**. No cross-tenant relationship of any nature is permitted unless explicitly authorized. This applies to both operational dependencies and informational relationships.
+
+The hard tenancy spectrum:
+
+| Setting | Meaning |
+|---------|---------|
+| `deny_all` | No cross-tenant relationships of any nature |
+| `explicit_only` | All cross-tenant must be explicitly authorized (DEFAULT) |
+| `operational_permitted` | Operational cross-tenant permitted; informational requires explicit auth |
+| `allow_all` | All cross-tenant permitted — requires justification; not available in sovereign profile |
+
+The default shifts from the Q59 model's `operational_only` to `explicit_only`. Informational sharing is no longer implicitly open — every informational cross-tenant relationship requires an explicit authorization record.
+
+### 6.2 Cross-Tenant Authorization Record
+
+```yaml
+cross_tenant_authorization:
+  artifact_metadata:
+    uuid: 
+    handle: "tenant-a/auth/shared-network-read"
+    version: "1.0.0"
+    status: active
+    owned_by:
+      display_name: "Infrastructure Tenant Admin"
+
+  # WHO
+  authorized_consumer_tenant_uuid: 
+  authorized_actor_constraint:
+    roles: [service_account, automation]    # null = any actor in the tenant
+    specific_uuids: []                      # specific actor UUIDs if needed
+
+  # WHAT
+  resource_entity_uuid: 
+  resource_type_uuid: 
+  permitted_fields:
+    - field: network_segment
+    - field: vlan_id
+    # empty list = all fields permitted
+  permitted_relationship_natures: [informational, operational]
+
+  # WHEN
+  valid_from: 
+  expires_at: 
+
+  # WHERE
+  permitted_in_regions: [eu-west, eu-central]   # null = any region
+  sovereignty_constraints:
+    must_honor_consuming_tenant_sovereignty: true
+    must_honor_owning_tenant_sovereignty: true
+
+  # GOVERNANCE
+  authorized_by_policy_uuid: 
+  authorization_level: 
+  # Hierarchy: field_specific > resource_specific > tenant_global
+  # More specific = higher precedence
+```
+
+### 6.3 Authorization Hierarchy
+
+More specific authorizations take precedence over broader ones:
+
+```
+field_specific     ← highest precedence — only these exact fields on this entity
+  │
+resource_specific  ← this entity — all permitted fields
+  │
+tenant_global      ← all entities in this Tenant — broadest
+```
+
+If a tenant-global policy says "allow informational sharing with Tenant B" but a resource-specific policy says "this resource is not shareable with anyone," the resource-specific policy wins.
+
+### 6.4 System Policies — Cross-Tenancy
+
+| Policy | Rule |
+|--------|------|
+| `XTA-001` | Cross-tenant information sharing is closed by default — explicit authorization required |
+| `XTA-002` | Cross-tenant authorizations must specify who, what, when, and where |
+| `XTA-003` | More specific authorizations take precedence: field_specific > resource_specific > tenant_global |
+| `XTA-004` | All cross-tenant authorization decisions are policy-driven and DCM-enforced |
+| `XTA-005` | Sovereignty constraints declared by either Tenant must be honored by all cross-tenant relationships |
+
+---
+
+## 7. Rehydration Tenancy Controls
+
+### 7.1 Tenancy and Sovereignty Are Always Current
+
+Tenancy controls, sovereignty directives, and cross-tenant authorizations are **always evaluated against current policies during rehydration**. They cannot be pinned to historical versions.
+
+```yaml
+rehydration:
+  re_evaluate: true/false          # governs placement
+  policy_version: current/pinned   # governs resource configuration policies
+  # The following are ALWAYS current — cannot be pinned:
+  tenancy_controls: always_current
+  sovereignty_controls: always_current
+  cross_tenant_authorizations: always_current
+```
+
+### 7.2 Rehydration Tenancy Conflict
+
+When rehydration produces a tenancy or sovereignty constraint that conflicts with an existing cross-tenant allocation:
+
+```yaml
+rehydration_tenancy_conflict_record:
+  rehydration_request_uuid: 
+  entity_uuid: 
+  conflict_type: cross_tenant_authorization_conflict
+  original_authorization_uuid: 
+  current_policy_violation:
+    policy_uuid: 
+    violation: "Consuming Tenant no longer has authorization for this allocation"
+  action_taken: paused
+  entity_state: PENDING_REVIEW
+  notifications_sent:
+    - entity_owner
+    - owning_tenant_admin
+    - consuming_tenant_admin
+    - platform_admin
+  resolution_options:
+    - re_authorize
+    - release
+    - escalate
+  policy_override_available: true
+```
+
+### 7.3 System Policies — Rehydration
+
+| Policy | Rule |
+|--------|------|
+| `RHY-001` | Tenancy, sovereignty, and cross-tenant authorizations always use current policies during rehydration |
+| `RHY-002` | Rehydration that conflicts with current tenancy/sovereignty pauses and enters PENDING_REVIEW |
+| `RHY-003` | A paused rehydration allocation is not automatically released — requires explicit resolution |
+| `RHY-004` | A policy may declare automatic resolution behavior for rehydration tenancy conflicts |
+
+---
+
+## 8. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should organizations be able to submit custom profiles and groups back to the DCM project registry? | Community | ✅ Resolved — community submissions via PR-based workflow; Tier 2; documented use case + deployment reference + test results + named maintainer (PROF-005) |
+| 2 | Should there be a certified profile program — profiles that have been validated against specific regulatory frameworks? | Compliance | ✅ Resolved — certified profile program with third-party certification metadata; certified profiles promoted to Tier 1; applies to artifact not deployment (PROF-006) |
+| 3 | Should External Policy Evaluator trust elevation require a formal approval workflow in DCM UI or is out-of-band approval sufficient? | Security | ✅ Resolved — formal approval workflow; profile-governed approvers (1 standard → 3 sovereign); P7D shadow period; POLICY_PROVIDER_ELEVATED audit (PROF-007) |
+| 4 | Should the default TTL for dev profile resources be configurable at the platform level or only at the group level? | Configuration | ✅ Resolved — overridable at platform domain layer; per-resource-type TTL overrides; on_expiry action configurable (PROF-008) |
+| 5 | How does External Policy Evaluator delivery interact with air-gapped deployments — pull from internal mirror? | Sovereignty | ✅ Resolved — signed bundle model identical to registry; Mode 4 in sovereign restricted to within-boundary endpoints (PROF-009) |
+
+---
+
+## 9. Related Concepts
+
+- **Policy Engine** — executes the policies organized by groups and profiles
+- **Policy Layers** — the assembly process step where policies execute
+- **Artifact Metadata** — universal metadata on all policy artifacts
+- **Five Artifact Statuses** — developing → proposed → active → deprecated → retired
+- **Shadow Execution** — proposed policies and profiles run in shadow mode for validation
+- **Override Control** — policies set override control on fields; immutable_ceiling: absolute for non-negotiables
+- **Ingestion Model** — policy profile requirements may gate brownfield promotion
+- **Four States** — rehydration tenancy controls govern how historical states are replayed
+
+
+## 8. Policy Profile Gap Resolutions
+
+### 8.1 Community Profile and Group Submissions (Q1)
+
+Organizations may submit custom profiles and policy groups to the DCM community registry following the same PR-based proposal workflow as Resource Types. Community contributions live in Tier 2 (Verified Community).
+
+```yaml
+community_profile_submission:
+  profile:
+    handle: "community/profile/hipaa-openstack"
+    extends: system/profile/hipaa-prod
+    description: "HIPAA production profile for OpenStack deployments"
+    policy_groups:
+      - system/group/compliance-hipaa
+      - community/group/openstack-security-baseline
+    contributed_by: "Healthcare IT Community Group"
+    tested_with: [OpenStack 2024.1, DCM 1.0]
+  required_for_submission:
+    - documented_use_case
+    - at_least_one_real_deployment_reference
+    - test_results_against_reference_implementation
+    - named_maintainer
+```
+
+Community profiles carry the same lifecycle as Resource Types — shadow validation before active, deprecation policies, sunset periods. Organizations adopt them directly or extend them further.
+
+### 8.2 Certified Profile Program (Q2)
+
+DCM supports a certified profile program where profiles carry formal third-party certification metadata against compliance frameworks. Certified profiles are promoted to Tier 1 (DCM Core).
+
+```yaml
+profile_certification:
+  certifications:
+    - framework: HIPAA
+      certifying_body: "Coalfire Systems"
+      certification_date: "2025-11-01"
+      expires_at: "2027-11-01"
+      certification_scope: "PHI data lifecycle management via DCM"
+      certificate_ref:
+        vault_credential_ref: 
+        path: "dcm/registry/certifications/hipaa-prod-2025"
+```
+
+**Important:** Profile certification applies to the profile artifact only — it does not certify the deploying organization's compliance posture. A certified profile is evidence that the profile implements the required controls; it is not a compliance certification of any specific deployment.
+
+### 8.3 External Policy Evaluator Trust Elevation Approval (Q3)
+
+External Policy Evaluator trust elevation (increasing the mode level) requires a formal approval workflow. Approval requirements are profile-governed.
+
+```yaml
+external_evaluation_trust_elevation:
+  elevation_request:
+    from_mode: 1
+    to_mode: 3
+    justification: "Need OPA Rego for complex placement constraints"
+
+  approval_requirements:
+    standard:
+      approvers: [platform_admin]
+      min_approvers: 1
+    prod:
+      approvers: [platform_admin, security_owner]
+      min_approvers: 2
+    fsi:
+      approvers: [platform_admin, security_owner, compliance_officer]
+      min_approvers: 2
+      verified_required: true
+    sovereign:
+      approvers: [platform_admin, security_owner, compliance_officer]
+      min_approvers: 3
+      requires_change_control_ticket: true
+
+  shadow_period_after_elevation: P7D    # elevated mode runs in shadow before active
+  audit_record: POLICY_PROVIDER_ELEVATED
+```
+
+The P7D shadow period catches unintended consequences before elevated outputs become binding on production requests.
+
+### 8.4 Dev Profile Resource TTL Configurability (Q4)
+
+The default TTL for dev profile resources is declared in the system domain layer and overridable at the platform domain level.
+
+```yaml
+layer:
+  handle: "platform/dev-profile/resource-ttl-override"
+  domain: platform
+  fields:
+    dev_profile_resource_ttl:
+      default_ttl: P30D               # platform override: 30d instead of system default P7D
+      max_ttl: P90D                   # consumers cannot declare TTL > 90 days in dev
+      on_expiry: notify               # notify (consumers can extend) vs destroy
+      per_resource_type_overrides:
+        Compute.VirtualMachine: P7D
+        Storage.Block: P14D
+        DNS.Record: P3D
+```
+
+### 8.5 Air-Gapped External Policy Evaluator Delivery (Q5)
+
+External Policy Evaluator delivery in air-gapped deployments uses signed bundles — same model as the registry bundle system.
+
+```yaml
+external_evaluation_airgap:
+  delivery_mode: signed_bundle
+  bundle_contents:
+    - provider_registration_yaml
+    - policy_artifacts_zip
+    - mode_specific_package:
+        mode_3: opa_rego_bundle       # OPA Rego files + data
+        mode_4: endpoint_config       # endpoint declaration (must be within boundary)
+  signing_key_ref: 
+  expires_at: 
+```
+
+**Mode 4 sovereign constraint:** In sovereign profiles, Mode 4 External Policy Evaluators may only call endpoints within the sovereignty boundary. External AI service calls are blocked by the BBQ-001 sovereignty check before any Mode 4 query.
+
+### 8.6 System Policies — Policy Profile Gaps
+
+| Policy | Rule |
+|--------|------|
+| `PROF-005` | Organizations may submit custom profiles and policy groups to the DCM community registry via the same PR-based proposal workflow as Resource Types. Community contributions live in Tier 2. Submissions require documented use case, at least one production deployment reference, test results, and a named maintainer. |
+| `PROF-006` | DCM supports a certified profile program where profiles carry formal third-party certification metadata. Certified profiles are promoted to Tier 1. Profile certification applies to the artifact only — it does not certify the deploying organization's compliance posture. |
+| `PROF-007` | External Policy Evaluator trust elevation requires a formal approval workflow (standard: 1 platform admin; prod: platform admin + security owner; fsi/sovereign: dual approval + compliance officer). Elevated providers run in shadow mode for P7D before activation. All elevations produce a POLICY_PROVIDER_ELEVATED audit record. |
+| `PROF-008` | The default TTL for dev profile resources is declared in the system domain layer and overridable at the platform domain level. Per-resource-type TTL overrides are supported. The on_expiry action is configurable. |
+| `PROF-009` | External Policy Evaluator delivery in air-gapped deployments uses signed bundles identical to the registry bundle model. Mode 4 providers in sovereign profiles may only call endpoints within the sovereignty boundary. |
+
+
+
+---
+
+## 9. Recovery Posture Policy Groups
+
+### 9.1 recovery_posture as a Concern Type
+
+`recovery_posture` is a Policy Group concern_type that governs how DCM responds to provisioning failures, timeouts, and ambiguous states. It is the fifth concern type alongside security, compliance, operational, and implementation posture.
+
+Recovery posture groups contain Recovery Policies — a formal DCM policy type that maps trigger conditions (DISPATCH_TIMEOUT, PARTIAL_REALIZATION, etc.) to response actions (DRIFT_RECONCILE, DISCARD_AND_REQUEUE, NOTIFY_AND_WAIT, etc.).
+
+See [Operational Models](24-operational-models.md) Section 5 for the complete Recovery Policy model, trigger vocabulary, and action vocabulary.
+
+### 9.2 Four Built-in Recovery Posture Groups
+
+| Group Handle | Posture | Appropriate For |
+|-------------|---------|----------------|
+| `system/group/recovery-automated-reconciliation` | Let drift detection converge on correct state | Dev, standard environments |
+| `system/group/recovery-discard-and-requeue` | Clean up and restart on any ambiguity | Consistency-critical environments |
+| `system/group/recovery-notify-and-wait` | Always notify human; never act automatically | FSI, sovereign, regulated environments |
+| `system/group/recovery-aggressive-retry` | Retry everything before giving up | High-transient-failure environments |
+
+### 9.3 Profile Binding Defaults
+
+| Profile | Default Recovery Posture |
+|---------|------------------------|
+| `minimal` | recovery-automated-reconciliation |
+| `dev` | recovery-automated-reconciliation |
+| `standard` | recovery-automated-reconciliation |
+| `prod` | recovery-notify-and-wait |
+| `fsi` | recovery-notify-and-wait |
+| `sovereign` | recovery-notify-and-wait |
+
+### 9.4 Override Hierarchy
+
+Organizations override recovery posture at Tenant or resource-type level without changing the deployment profile:
+
+```yaml
+# Tenant override — all resources in this Tenant use discard-and-requeue
+tenant_config:
+  recovery_profile_override: recovery-discard-and-requeue
+
+# Resource-type override — VMs get aggressive retry regardless of Tenant/profile
+resource_type_recovery_override:
+  resource_type: Compute.VirtualMachine
+  recovery_profile: recovery-aggressive-retry
+```
+
+Resource-type override wins over Tenant override wins over profile default.
+
+
+
+---
+
+## 10. Zero Trust Posture Policy Groups
+
+### 10.1 zero_trust_posture as a Concern Type
+
+`zero_trust_posture` is the sixth Policy Group concern type. It governs authentication requirements, credential lifetime, revocation check frequency, and hardware attestation requirements for all DCM interactions.
+
+See [Accreditation and Authorization Matrix](26-accreditation-and-authorization-matrix.md) Section 5 for the complete zero trust model.
+
+### 10.2 Four Zero Trust Posture Levels
+
+| Posture | Boundary | Internal | Hardware | Profile Default |
+|---------|---------|----------|----------|----------------|
+| `none` | Perimeter model | Trusted | Not required | minimal |
+| `boundary` | Zero trust at external boundaries | Service mesh | Not required | dev, standard |
+| `full` | Zero trust everywhere | Per-call auth | Not required | prod, fsi |
+| `hardware_attested` | Zero trust everywhere | Per-call auth | Required (TPM/HSM) | sovereign |
+
+### 10.3 Credential Lifetime Defaults
+
+| Profile | Max credential lifetime |
+|---------|------------------------|
+| minimal | PT8H |
+| dev | PT4H |
+| standard | PT1H |
+| prod | PT30M |
+| fsi | PT15M |
+| sovereign | PT15M + hardware attestation |
+
+### 10.4 Hard Data Boundary Constraints
+
+The sovereign profile enforces a hard constraint via the Data/Capability Authorization Matrix: **data classified as `sovereign` or `classified` never crosses any interaction boundary**. This constraint is declared with `hard_constraint: true` in the federation boundary matrix and cannot be overridden by any policy, profile, or operator action. It is enforced at the matrix level, not the policy level.
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/15-universal-groups.md b/content/docs/architecture/data-model/15-universal-groups.md
new file mode 100644
index 0000000..3f1a351
--- /dev/null
+++ b/content/docs/architecture/data-model/15-universal-groups.md
@@ -0,0 +1,661 @@
+# DCM Data Model — Universal Group Model
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Resource Grouping](08-resource-grouping.md) | [Entity Relationships](09-entity-relationships.md) | [Policy Organization](14-policy-profiles.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA**
+>
+> The Data abstraction — DCMGroup as universal grouping artifact
+
+
+
+---
+
+## 1. Purpose
+
+DCM previously used eight distinct grouping constructs — Tenant, Resource Groups, Custom Resource Groups, Policy Groups, Policy Profiles, Layer Domain, Activation Scope, and Cross-Tenant Authorization. Each had its own structure, its own API, its own documentation, and its own membership model. This fragmentation created cognitive overhead for operators and prevented natural organizational structures (such as "everything related to Payments") from being expressed as a single construct.
+
+The **Universal Group Model** collapses all grouping into a single `DCMGroup` entity distinguished by `group_class` metadata. One mental model. One API. One registry. The same UUID, versioning, lifecycle, policy targeting, and audit trail apply to every group regardless of its class.
+
+**What changes:**
+- All grouping constructs become `group_class` values on `DCMGroup`
+- Existing UUIDs, handles, and API references are preserved
+- Existing APIs become class-filtered views of the universal group store
+- Migration is additive — no breaking changes
+
+**What does NOT change:**
+- The structural invariants of each construct (one Tenant per resource, no constituent cross-tenant, etc.)
+- Policy enforcement behavior — governed by the active Profile, not per-group configuration
+- The hard-tenancy model — tenant_boundary groups retain all isolation guarantees
+
+---
+
+## 2. The DCMGroup Entity
+
+### 2.1 Universal Structure
+
+```yaml
+dcm_group:
+  artifact_metadata:
+    uuid: 
+    handle: 
+    version: 
+    status: 
+    created_by: 
+    owned_by: 
+    created_via: 
+    modifications: 
+
+  # IDENTITY
+  name: 
+  description: 
+  concern_tags: [payments, pci-scope, eu-west]  # free tagging — discoverability
+
+  # WHAT KIND OF GROUP
+  group_class: 
+  group_subclass: 
+  # group_subclass examples: cost_center, business_unit, compliance_scope, project
+
+  # MEMBERSHIP
+  member_types_permitted: [resource_entity, policy, layer, group, tenant]
+  # Determines what can be a member of this group
+  # Single-type groups declare one type (e.g., [policy] for policy_collection)
+  # Composite groups declare multiple types
+
+  exclusivity:
+    per_member: 
+    # one:  a member can belong to only one group of this class at a time
+    # many: a member can belong to multiple groups of this class simultaneously
+    per_group: 
+    cap: 
+
+  members:
+    - member_uuid: 
+      member_type: 
+      member_role: 
+      added_at: 
+      added_by: 
+      valid_from: 
+      expires_at: 
+      membership_status: 
+
+  # BEHAVIOR
+  enforcement_model: 
+  # advisory:   group is a tag — no system behavior enforced by DCM
+  # enforced:   group drives policy scoping and system behavior
+  # mandatory:  group membership is non-optional (structural requirement)
+  # NOTE: For tenant_boundary groups, enforcement_model is profile-governed
+  # — the active Profile sets the enforcement floor, not per-group configuration
+
+  cross_boundary:
+    tenant_spanning: 
+    sovereignty_spanning: 
+
+  lifecycle_coupling:
+    on_group_destroy: 
+    # detach (DEFAULT): destroying the group releases memberships but
+    #                   does NOT destroy members
+    # cascade: destroying the group destroys all members
+    # notify:  destroying the group notifies owners and waits for confirmation
+    # retain:  group cannot be destroyed while it has members
+    on_member_destroy: 
+
+  # INHERITANCE AND COMPOSITION
+  extends: 
+  includes_groups:
+    - group_uuid: 
+      member_type_filter: [resource_entity]  # optional — only include this type
+      # If omitted: all member types from the included group are pulled in
+
+  # NESTING (for tenant_boundary groups)
+  parent_group_uuid: 
+  child_groups: [, ...]  # populated by DCM — do not set manually
+
+  # POLICY TARGETING
+  # Any policy can target this group by UUID or handle — no special declaration
+  # Policy targeting a composite group applies to all member types by default
+  # Policy can narrow with: member_type_filter: [resource_entity]
+```
+
+### 2.2 Group Classes
+
+| group_class | Replaces | member_types_permitted | exclusivity.per_member | enforcement_model |
+|-------------|---------|----------------------|----------------------|------------------|
+| `tenant_boundary` | Tenant | resource_entity, group | one (structural lock) | profile-governed |
+| `resource_grouping` | Resource Group, Custom Resource Group | resource_entity | many | advisory |
+| `policy_collection` | Policy Group | policy | many | enforced |
+| `policy_profile` | Policy Profile | group (policy_collection only) | many | enforced |
+| `layer_grouping` | Layer Domain grouping | layer | many | enforced |
+| `provider_grouping` | Provider collections | provider | many | advisory |
+| `composite` | (new) | all types | many | configurable |
+| `federation` | (new) | group (tenant_boundary) | many | advisory |
+
+### 2.3 Structural Invariants — Non-Overridable
+
+Regardless of `enforcement_model`, `group_class`, or active Profile, the following structural invariants always hold:
+
+| Invariant | Applies To | Rule |
+|-----------|-----------|------|
+| `GRP-INV-001` | `tenant_boundary` | A resource_entity may belong to exactly one active tenant_boundary group |
+| `GRP-INV-002` | `tenant_boundary` | Constituent relationships may not cross tenant_boundary group boundaries |
+| `GRP-INV-003` | `tenant_boundary` | Destroying a parent tenant_boundary group requires explicit resolution of all child groups first — no silent cascade |
+| `GRP-INV-004` | `tenant_boundary` | A resource in a child tenant_boundary group belongs to the child — never the parent |
+| `GRP-INV-005` | All | Circular group membership is invalid |
+| `GRP-INV-006` | All | A group cannot be a member of itself |
+
+---
+
+## 3. Group Class Reference
+
+### 3.1 tenant_boundary
+
+**Replaces:** Tenant entity  
+**Purpose:** Ownership boundary, isolation enforcement, cost attribution, audit scope, sovereignty boundary
+
+```yaml
+dcm_group:
+  group_class: tenant_boundary
+  member_types_permitted: [resource_entity, group]
+  exclusivity:
+    per_member: one   # STRUCTURAL LOCK — cannot be changed by policy
+  enforcement_model: mandatory   # set by active Profile — not configurable per-group
+  cross_boundary:
+    tenant_spanning: false   # STRUCTURAL LOCK
+  lifecycle_coupling:
+    on_group_destroy: notify   # requires explicit resolution
+    on_member_destroy: remove_from_group
+
+  # Tenant-specific fields preserved from original model
+  tenant_config:
+    hard_tenancy:
+      cross_tenant_relationships: explicit_only
+    active_profile: system/profile/standard
+    minimum_child_profile: null
+```
+
+**Profile-governed enforcement:**
+- `minimal` profile → `enforcement_model: advisory` (tenancy optional)
+- `dev` profile → `enforcement_model: enforced` (tenancy recommended)
+- `standard` and above → `enforcement_model: mandatory` (tenancy required)
+
+### 3.2 resource_grouping
+
+**Replaces:** DCM Default Resource Group, Custom Resource Group  
+**Purpose:** Flexible composable grouping of resource entities — structured tagging
+
+```yaml
+dcm_group:
+  group_class: resource_grouping
+  group_subclass: cost_center   # advisory — CostCenter, BusinessUnit, Project, Team...
+  member_types_permitted: [resource_entity]
+  exclusivity:
+    per_member: many   # a resource can be in multiple resource groups
+  enforcement_model: advisory
+```
+
+### 3.3 policy_collection
+
+**Replaces:** Policy Group  
+**Purpose:** Cohesive collection of policies addressing a single concern
+
+```yaml
+dcm_group:
+  group_class: policy_collection
+  concern_tags: [pci-dss, encryption, network-segmentation]
+  member_types_permitted: [policy]
+  enforcement_model: enforced
+  # Source — local or External Policy Evaluator
+  source:
+    type: 
+    provider_uuid: 
+    on_provider_update: 
+```
+
+### 3.4 policy_profile
+
+**Replaces:** Policy Profile  
+**Purpose:** Complete DCM configuration for a use case, composed of policy_collection groups
+
+```yaml
+dcm_group:
+  group_class: policy_profile
+  member_types_permitted: [group]   # only policy_collection groups
+  extends:     # inherits all parent's groups
+  enforcement_model: enforced
+```
+
+### 3.5 composite
+
+**New concept:** A group whose members span multiple member types — the organizational unit for a complete concern.
+
+```yaml
+dcm_group:
+  group_class: composite
+  name: "Payments Platform"
+  concern_tags: [payments, pci-scope]
+  member_types_permitted: [resource_entity, policy, layer, group, provider]
+  enforcement_model: advisory   # composite groups are organizational — advisory default
+
+  members:
+    - member_uuid: 
+      member_type: resource_entity
+      member_role: compute
+    - member_uuid: 
+      member_type: group
+      member_role: compliance_governance
+    - member_uuid: 
+      member_type: group
+      member_role: resource_inventory
+    - member_uuid: 
+      member_type: layer
+      member_role: configuration
+```
+
+**Policy targeting composite groups:**
+```yaml
+policy:
+  target_groups:
+    - group_uuid: 
+      member_type_filter: [resource_entity]   # narrow to resources only
+      # Omit member_type_filter to apply to ALL member types (default)
+```
+
+### 3.6 federation
+
+**New concept:** A group of tenant_boundary groups that share governance, visibility, and resources while maintaining complete independence.
+
+```yaml
+dcm_group:
+  group_class: federation
+  name: "Global FSI Federation"
+  member_types_permitted: [group]   # tenant_boundary groups only
+  enforcement_model: advisory       # federation cannot override member Tenant isolation
+
+  members:
+    - member_uuid: 
+      member_type: group
+      member_role: member_tenant
+    - member_uuid: 
+      member_type: group
+      member_role: member_tenant
+    - member_uuid: 
+      member_type: group
+      member_role: shared_governance
+
+  federation_config:
+    shared_policy_inheritance: 
+    # opt_in:  member Tenants must explicitly adopt shared policies
+    # opt_out: shared policies apply to all members unless explicitly excluded
+    cross_member_visibility: 
+    consolidated_reporting: true
+```
+
+---
+
+## 4. Nested Tenants
+
+### 4.1 Concept
+
+A **Nested Tenant** is a `tenant_boundary` group that is a member of a parent `tenant_boundary` group. The child Tenant maintains complete isolation — its resources belong to it, not the parent. The parent Tenant has governance overlay, cost rollup authority, and audit aggregation across all children.
+
+```
+corporate_tenant (tenant_boundary)
+  │  child_groups:
+  ├── business_unit_a_tenant (tenant_boundary)
+  │     └── resources, policies, layers owned by BU-A
+  └── business_unit_b_tenant (tenant_boundary)
+        └── resources, policies, layers owned by BU-B
+```
+
+### 4.2 Structural Invariants for Nested Tenants
+
+- A resource belongs to the **leaf** tenant_boundary group — never the parent (GRP-INV-004)
+- Parent Tenant has **governance overlay** — not ownership
+- Parent Tenant destruction requires all child Tenants to be resolved first (GRP-INV-003)
+- Constituent relationships cannot cross any tenant_boundary boundary — including parent-child (GRP-INV-002)
+
+### 4.3 Policy Inheritance Direction
+
+Policy inheritance from parent to child Tenant is profile-governed:
+
+| Profile | Default | Meaning |
+|---------|---------|---------|
+| `minimal`, `dev` | `opt_in` | Child Tenants must explicitly adopt parent policies |
+| `standard`, `prod` | `opt_out` | Parent policies cascade to children unless child excludes |
+| `fsi`, `sovereign` | `opt_in` | Nothing crosses without consent |
+
+```yaml
+nested_tenant_config:
+  parent_group_uuid: 
+  policy_inheritance: opt_out   # governed by active Profile
+  parent_policy_exclusions:
+    - policy_uuid:    # explicitly excluded from cascading to this child
+  cost_rollup_to_parent: true
+  audit_visible_to_parent: true
+  sovereign_boundary: independent   # child sovereignty independent of parent
+```
+
+### 4.4 Nested Tenant Use Cases
+
+- **Enterprise structure:** Corporate → Business Unit → Team Tenants
+- **Multi-region deployment:** Global Tenant → Regional Tenants → Zone Tenants
+- **Multi-tier compliance:** Organization Tenant → PCI-scope Tenant → Payment-processing Tenant
+- **Partner/customer isolation:** Platform Tenant → Customer A Tenant → Customer B Tenant
+
+---
+
+## 5. Federated Tenants
+
+### 5.1 Concept
+
+A **Federated Tenant** structure is a `federation` group containing multiple independent `tenant_boundary` groups. Member Tenants maintain complete independence — the federation provides shared governance, consolidated visibility, and mutual cross-tenant authorization within the federation scope.
+
+### 5.2 Federation Capabilities
+
+- **Shared policy application:** `policy_collection` groups included in the federation apply to all member Tenants (per `shared_policy_inheritance` setting)
+- **Cross-member visibility:** federation members can declare mutual `cross_tenant_authorization` scoped to federation membership — without requiring separate bilateral authorizations
+- **Consolidated reporting:** cost, audit, and observability queries scoped to the federation group return aggregated results across all member Tenants
+- **Federation-level governance:** policies targeting the federation group apply to all member Tenants
+
+### 5.3 Federation vs Nesting
+
+| Dimension | Nested Tenants | Federated Tenants |
+|-----------|---------------|-----------------|
+| Relationship | Parent-child hierarchy | Peer membership |
+| Governance direction | Top-down from parent | Shared among peers |
+| Independence | Child subordinate to parent | Members fully independent |
+| Cost rollup | Mandatory to parent | Configurable |
+| Use case | Enterprise hierarchy | Multi-organization collaboration |
+
+---
+
+## 6. Group Registry and API
+
+### 6.1 Universal Registry
+
+All groups are stored in a single **Group Registry** — a GitOps store following the standard data store contract. The registry is queryable by any combination of fields.
+
+### 6.2 Class-Filtered API Views
+
+The universal registry exposes class-filtered views for convenience:
+
+| Endpoint | Equivalent Query |
+|----------|----------------|
+| `GET /tenants` | `GET /groups?group_class=tenant_boundary` |
+| `GET /resource-groups` | `GET /groups?group_class=resource_grouping` |
+| `GET /policy-groups` | `GET /groups?group_class=policy_collection` |
+| `GET /policy-profiles` | `GET /groups?group_class=policy_profile` |
+| `GET /federations` | `GET /groups?group_class=federation` |
+
+Existing API references continue to work unchanged. New API consumers can use the universal endpoint.
+
+---
+
+## 8. DCM System Policies
+
+| Policy | Rule |
+|--------|------|
+| `GRP-INV-001` | A resource_entity may belong to exactly one active tenant_boundary group |
+| `GRP-INV-002` | Constituent relationships may not cross tenant_boundary group boundaries |
+| `GRP-INV-003` | Destroying a parent tenant_boundary group requires explicit resolution of all child groups first |
+| `GRP-INV-004` | A resource in a child tenant_boundary group belongs to the child — never the parent |
+| `GRP-INV-005` | Circular group membership is invalid and must be rejected |
+| `GRP-INV-006` | A group cannot be a member of itself |
+| `GRP-007` | Composite group `on_group_destroy` default is `detach` — destroying a group releases memberships but does not destroy members |
+| `GRP-008` | Policies targeting a composite group apply to all member types by default; `member_type_filter` narrows scope |
+| `GRP-009` | Federation groups cannot override member Tenant isolation boundaries |
+| `GRP-010` | Nested Tenant policy inheritance direction is governed by the active Profile — not per-group configuration |
+
+---
+
+## 9. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should composite group policy targeting emit a linting warning when no member_type_filter is declared? | Operational safety | ✅ Resolved — linting warning (not error) when composite policy targeting has no member_type filter; suppress with explicit_no_filter: true (GRP-016) |
+| 2 | Should there be a maximum nesting depth for tenant_boundary groups? | Operational governance | ✅ Resolved — Maximum nesting depth is profile-governed: standard/prod = 5 levels; fsi/sovereign = 3 levels. Deeper nesting creates policy inheritance complexity and audit graph depth issues. Enforced at group creation time. |
+| 3 | How does group membership interact with the Search Index? | Performance | ✅ Resolved — Group membership is indexed in the Search Index as a field on each entity record (member_of_groups: [uuid, ...]). The Search Index supports querying by group_uuid. Group membership changes trigger an incremental index update (not full rebuild). Staleness follows the standard Search Index model (PT5M standard profile). |
+| 4 | Should time-bounded memberships (expires_at) trigger notifications before expiry? | Consumer experience | ✅ Resolved — warn_before_expiry field on membership (GRP-014) |
+
+---
+
+## 9. Grouping and Relationship Gap Resolutions
+
+### 9.1 Community Subclass Catalog (Q35)
+
+The `group_class` set is closed — system behavior is tied to declared classes only. `group_subclass` is open and advisory. DCM maintains a community subclass catalog as a non-authoritative reference shipped with the well-known Information Provider Registry:
+
+```yaml
+# Community subclass catalog (advisory — not enforced, not validated)
+common_group_subclasses:
+  resource_grouping:
+    - subclass: cost_center
+      description: "Financial cost attribution grouping"
+    - subclass: business_unit
+      description: "Organizational business unit"
+    - subclass: project
+      description: "Project-scoped resource collection"
+    - subclass: environment
+      description: "Environment grouping (prod/staging/dev)"
+    - subclass: application
+      description: "Application component grouping"
+  policy_collection:
+    - subclass: compliance_framework
+      description: "Policies implementing a compliance framework"
+    - subclass: technology_baseline
+      description: "Technology-specific policy baseline"
+```
+
+Organizations freely declare subclasses not in the catalog — there is no validation or enforcement on subclass values.
+
+### 9.2 Group Sovereignty Interaction (Q36)
+
+Sovereignty interaction is group_class-specific:
+
+| group_class | Cross-Sovereignty | Notes |
+|-------------|-----------------|-------|
+| `tenant_boundary` | **Never** | Structural — not configurable |
+| `resource_grouping` | Permitted by default | Policy may restrict for classified resources |
+| `policy_collection` | Always permitted | Policies have no sovereignty — governance artifacts |
+| `layer_grouping` | Always permitted | Layers have no sovereignty |
+| `composite` | Governed by most restrictive member | If contains cross-sovereignty resources, resource rules apply |
+| `federation` | Permitted with DCM federation rules | DCM-003 governs data flows |
+
+```yaml
+# Policy restricting cross-sovereignty resource group membership
+policy:
+  type: gatekeeper
+  rule: >
+    If group.group_class == resource_grouping
+    AND member.classification_level IN [confidential, restricted]
+    AND member.sovereignty_zone != group.primary_sovereignty_zone
+    THEN gatekeep: "Classified resources cannot join cross-sovereignty resource groups"
+```
+
+### 9.3 Tenant Decommission Lifecycle (Q37)
+
+Tenant decommission is the highest-stakes lifecycle operation in DCM. It requires mandatory pre-decommission validation and follows a staged sequence.
+
+**Phase 1 — Pre-decommission validation (blocking):**
+- All resources in decommissionable state (not PROVISIONING or active incidents)
+- Cross-tenant operational relationships accounted for (consuming Tenants notified)
+- Allocated resources claimed by other Tenants addressed (returned or migrated)
+- Active rehydration leases released
+- Compliance holds reviewed (HIPAA/PCI records may need archival)
+- Child tenant_boundary groups resolved first (GRP-INV-003)
+
+**Phase 2 — Resource decommission (per lifecycle policy):**
+```
+For each resource in the Tenant:
+  cascade → decommission resource (default for tenant_boundary)
+  retain  → resource enters ORPHANED state (operator must rehome or destroy)
+  notify  → alert owner; resource enters PENDING_DECOMMISSION
+```
+
+**Phase 3 — Group membership cleanup:**
+- Remove Tenant from all group memberships
+- Empty federation groups enter EMPTY state
+- Orphaned child groups must have been resolved in Phase 1
+
+**Phase 4 — Audit record archival:**
+All audit records enter `all_retired` retention_status. They are **never destroyed** as part of Tenant decommission. Post-lifecycle retention clock starts per governing policy.
+
+### 9.4 Time-Bounded Group Membership (Q38)
+
+Group memberships already support time-bounded validity via `valid_from` and `expires_at` in the Universal Group Model. The Lifecycle Constraint Enforcer handles expiry.
+
+```yaml
+member:
+  member_uuid: 
+  member_type: resource_entity
+  valid_from: "2026-01-01T00:00:00Z"
+  expires_at: "2026-12-31T23:59:59Z"
+  membership_status: 
+  on_expiry: 
+  # remove:         member silently removed from group on expiry
+  # notify:         notify group owner; member remains with expired status (default)
+  # suspend_member: transition the member entity to SUSPENDED state
+  warn_before_expiry: P7D          # notify 7 days before expiry
+```
+
+Membership expiry produces a `MEMBER_REMOVE` audit record with `reason: membership_ttl_expired`.
+
+### 9.5 Group Policy Inheritance — Nested Groups (Q39)
+
+Policy inheritance for nested groups is group_class-specific and profile-governed:
+
+| group_class | Default | Profile Override |
+|-------------|---------|----------------|
+| `tenant_boundary` | `opt_out` (parent cascades unless child excludes) | `opt_in` for minimal/dev/fsi/sovereign |
+| `resource_grouping` | Not applicable | Resource groups are tags — policies target them, not inherit through them |
+| `policy_collection` | Not applicable | Policy collections use `extends` for inheritance |
+| `composite` | `opt_out` | Configurable per group |
+| `federation` | `opt_in` | Peer consent always required — not configurable |
+
+```yaml
+# Nested group policy inheritance declaration
+dcm_group:
+  group_class: tenant_boundary
+  parent_group_uuid: 
+  policy_inheritance: opt_out     # governed by active Profile
+  parent_policy_exclusions:
+    - policy_uuid:          # explicitly excluded from cascading to this child
+```
+
+---
+
+## 10. System Policies — Grouping Gaps
+
+| Policy | Rule |
+|--------|------|
+| `GRP-011` | The group_class set is closed — system behavior is tied to declared classes only. group_subclass is open and advisory. DCM maintains a community subclass catalog as a non-authoritative reference. No validation or enforcement on subclass values. |
+| `GRP-012` | Sovereignty interaction is group_class-specific. tenant_boundary groups never span sovereignty boundaries (structural). resource_grouping groups may span sovereignty boundaries by default — policy may restrict for classified resources. policy_collection and layer_grouping groups always permitted cross-sovereignty. composite groups are governed by the sovereignty rules of their most restrictive member type. |
+| `GRP-013` | Tenant decommission requires pre-decommission validation (resource state, cross-tenant relationships, compliance holds, child group resolution). Resources follow declared lifecycle policy. Child tenant_boundary groups must be resolved before parent decommission. Audit records enter post-lifecycle retention — never destroyed as part of Tenant decommission. |
+| `GRP-014` | Group memberships support time-bounded validity via valid_from and expires_at. Membership expiry is enforced by the Lifecycle Constraint Enforcer. Expiry produces a MEMBER_REMOVE audit record. on_expiry action (remove, notify, suspend_member) declared per membership. Default: notify. |
+| `GRP-015` | Group policy inheritance is group_class-specific and profile-governed. tenant_boundary: opt_out (standard/prod) or opt_in (minimal/dev/fsi/sovereign). federation: always opt_in — peer consent required. composite: opt_out by default. resource_grouping and policy_collection: not applicable. |
+
+
+## 11. Related Concepts
+
+- **Resource Grouping** (doc 08) — original resource grouping model, now implemented via `group_class: resource_grouping`
+- **Policy Organization** (doc 14) — Policy Groups and Profiles, now implemented via `group_class: policy_collection` and `group_class: policy_profile`
+- **Entity Relationships** (doc 09) — cross-tenant authorized relationships between groups
+- **Universal Audit Model** (doc 16) — all group changes produce audit records
+- **Ingestion Model** (doc 13) — migration of existing constructs to universal groups
+
+
+---
+
+## 13. Cross-Tenant Authorization Lifecycle
+
+### 13.1 What Cross-Tenant Authorizations Are
+
+A `cross_tenant_authorization` is a DCMGroup with `group_class: cross_tenant_authorization`. It is the formal mechanism by which one Tenant grants another Tenant permission to reference, allocate from, or stake a resource that belongs to the granting Tenant.
+
+Without a cross-tenant authorization, entities in different Tenants cannot form relationships. The authorization is the bridge that enables cross-Tenant resource sharing while maintaining isolation.
+
+### 13.2 Authorization Lifecycle
+
+```yaml
+cross_tenant_authorization:
+  artifact_metadata:
+    uuid: 
+    handle: "org/cross-tenant-auth/networkops-to-appteam-vlan100"
+    version: "1.0.0"
+    status: active
+
+  granting_tenant_uuid: 
+  receiving_tenant_uuid: 
+  authorized_resources:
+    - resource_uuid: 
+      permitted_operations: [stake, read]
+    - resource_type: Network.IPAddress
+      source_pool_uuid: 
+      permitted_operations: [allocate]
+
+  # Duration
+  valid_from: 
+  expires_at:        # null = perpetual until revoked
+  auto_renew: false
+
+  # Who created this
+  granted_by_actor_uuid: 
+  granted_at: 
+```
+
+### 13.3 Who Creates Cross-Tenant Authorizations
+
+| Creator | Scenario | Authorization type |
+|---------|---------|-------------------|
+| Granting Tenant Admin | Standard: NetworkOps authorizes AppTeam to use VLAN-100 | explicit |
+| Platform Admin | Emergency or platform-managed shared infrastructure | platform_managed |
+| Pre-authorization policy | Policy automatically authorizes based on conditions | policy_auto |
+
+### 13.4 Revocation and Its Consequences
+
+When a cross-tenant authorization is revoked:
+
+```
+Authorization revoked (by granting Tenant admin, platform admin, or expiry)
+  │
+  ▼ All active allocations and stakes under this authorization are identified
+  │
+  ▼ For each active allocation / stake:
+  │   Entity enters PENDING_REVIEW state
+  │   pending_review_record created:
+  │     trigger: cross_tenant_auth.revoked
+  │     resolution_options: [re_authorize, release, migrate, escalate]
+  │
+  ▼ Notifications sent:
+  │   Granting Tenant Admin
+  │   Receiving Tenant Admin
+  │   Each affected resource owner
+  │   Platform Admin (if platform_managed authorization)
+  │
+  ▼ Resolution deadline: PT72H (configurable per profile)
+  │
+  └── On deadline: on_deadline_exceeded recovery policy fires
+```
+
+### 13.5 System Policies — Cross-Tenant Authorization
+
+| Policy | Rule |
+|--------|------|
+| `CTX-001` | Cross-tenant relationships require an active cross-tenant authorization or a resource type declared publicly_stakeable / publicly_allocatable in its Resource Type Spec. |
+| `CTX-002` | Cross-tenant authorization revocation places all active dependent entities in PENDING_REVIEW. Revocation does not immediately release allocations. |
+| `CTX-003` | Cross-tenant authorization expiry is treated identically to explicit revocation. |
+| `CTX-004` | Platform Admin may create cross-tenant authorizations on behalf of any Tenant. All platform-managed authorizations carry a platform_managed flag and are visible in the platform admin audit log. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/16-universal-audit.md b/content/docs/architecture/data-model/16-universal-audit.md
new file mode 100644
index 0000000..5172509
--- /dev/null
+++ b/content/docs/architecture/data-model/16-universal-audit.md
@@ -0,0 +1,743 @@
+# DCM Data Model — Universal Audit Model
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [Audit, Provenance, and Observability](12-audit-provenance-observability.md) | [data stores](11-storage-providers.md) | [Universal Groups](15-universal-groups.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA**
+>
+> The Data abstraction — Audit Record structure and tamper-evident chain
+
+
+
+---
+
+## 1. Purpose
+
+The Universal Audit Model defines the **unconditional obligation** for every DCM component to record every change to every artifact in a uniform, tamper-evident, retention-governed audit trail. No change is silent. No change is exempt.
+
+**The four required fields for every audit record:**
+- **Date and time** — when the change occurred (ISO 8601 with milliseconds)
+- **Who** — the complete actor chain (immediate actor + human authorization chain)
+- **What** — the subject of the change (entity UUID, type, handle)
+- **Action** — what happened (closed vocabulary — not free text)
+
+**The retention requirement:** Audit records must survive at least as long as any referenced resource or group is live. Policy governs what happens after all affected parties reach terminal state.
+
+---
+
+## 2. Design Principles
+
+**Universal — no exceptions.** Every mutation to every DCM artifact produces an audit record. Resources, policies, layers, groups, relationships, providers, configurations, authorizations, external evaluation queries, ingestion events, rehydration events, drift events, login events — all covered.
+
+**Append-only — tamper-evident.** Audit records are never modified or deleted while retention obligations apply. Each record carries a hash of its own content and a reference to the previous record's hash — forming a tamper-evident chain per entity.
+
+**Guaranteed delivery — not guaranteed synchrony.** Audit writes use a write-ahead log (WAL) pattern — the change and its audit record are written to a local WAL first, then delivered to the Audit Store asynchronously with retry. A change is never silent — it may be briefly buffered, but delivery is guaranteed before the WAL is cleared.
+
+**Reference-based retention — not time-based.** Audit records are retained while any referenced entity is live. Fixed time schedules (7 years, 10 years) are applied only after all referenced entities reach terminal state. A record created 20 years ago is retained unconditionally if any entity it references is still operational.
+
+**Policy-governed post-lifecycle retention.** After all referenced entities reach terminal state, policy determines how long to keep the audit record. Default is 7 years post-retirement (FSI-aligned). Organizations configure per Profile.
+
+---
+
+## 3. The Universal Audit Record
+
+```yaml
+audit_record:
+  # IDENTITY — immutable once written
+  record_uuid: 
+  record_timestamp: 
+  dcm_version: 
+
+  # WHO — composite actor chain
+  actor:
+    # The immediate actor that performed the change
+    immediate:
+      type: 
+      uuid: 
+      display_name: 
+      session_uuid: 
+
+    # The human who ultimately authorized this action (traceable chain)
+    authorized_by:
+      uuid: 
+      display_name: 
+      authorization_method: 
+      # direct_action:      human directly performed this
+      # request_submission: human submitted the request that triggered this
+      # policy_activation:  human activated the policy that triggered this
+      # system_policy:      DCM System Policy — no individual human
+      # scheduled:          scheduled job — authorized by job owner
+
+    # Links to originating context
+    request_uuid: 
+    policy_uuid: 
+    policy_version: 
+    correlation_id: 
+
+  # WHAT — the subject of the change
+  subject:
+    entity_uuid: 
+    entity_type: 
+    entity_handle: 
+    entity_version_before: 
+    entity_version_after: 
+
+  # ACTION — closed vocabulary
+  action: 
+
+  # ACTION DETAIL — structured per action type
+  action_detail:
+    # For MODIFY, ENRICH, LOCK
+    field_changes:
+      - field: 
+        previous_value: 
+        new_value: 
+        change_reason: 
+        locked_after: 
+
+    # For STATE_TRANSITION
+    state_transition:
+      from_state: 
+      to_state: 
+      transition_reason: 
+      triggered_by: 
+
+    # For RELATIONSHIP_CREATE, RELATIONSHIP_RELEASE
+    relationship_detail:
+      related_entity_uuid: 
+      related_entity_type: 
+      relationship_type: 
+      relationship_nature: 
+      cross_tenant: 
+
+    # For MEMBER_ADD, MEMBER_REMOVE
+    membership_detail:
+      group_uuid: 
+      group_class: 
+      member_role: 
+      time_bounded: 
+      expires_at: 
+
+    # For EVALUATE (policy evaluation)
+    evaluation_detail:
+      policy_uuid: 
+      policy_version: 
+      outcome: 
+      placement_phase: 
+      missing_fields: []
+
+    # For QUERY (external evaluation)
+    query_detail:
+      provider_uuid: 
+      fields_queried: []
+      result_type: 
+      outcome: 
+      audit_token: 
+      cached_result: 
+
+    # For DRIFT_DETECT
+    drift_detail:
+      drifted_fields:
+        - field: 
+          realized_value: 
+          discovered_value: 
+      drift_severity: 
+      policy_response: 
+
+  # CONTEXT
+  context:
+    tenant_uuid: 
+    request_uuid: 
+    session_uuid: 
+    profile_active: 
+    tags: [...]   # arbitrary searchable tags
+
+  # RETENTION
+  retention:
+    referenced_entities:
+      - entity_uuid: 
+        entity_type: 
+        last_known_state: 
+    retention_status: 
+    # live:           at least one referenced entity is non-retired — retain unconditionally
+    # all_retired:    all referenced entities have reached terminal state
+    # policy_governed: apply governing_policy after all_retired
+    governing_policy_uuid: 
+    retain_until: 
+
+  # INTEGRITY — tamper-evident hash chain
+  integrity:
+    record_hash: 
+    previous_record_hash: 
+    # Forms a per-entity hash chain — inserting, modifying, or deleting a
+    # historical record breaks the chain, detectable by verification
+    chain_sequence: 
+    signed_by: 
+    signature: 
+```
+
+---
+
+## 4. Action Vocabulary
+
+The `action` field uses a closed vocabulary. Free-text actions are invalid and rejected at write time (AUD-007).
+
+| Action | Applies To | Description |
+|--------|-----------|-------------|
+| `CREATE` | All | New artifact created |
+| `MODIFY` | All | Artifact field changed |
+| `STATE_TRANSITION` | Entities, groups | Lifecycle state changed |
+| `DELETE` | All | Artifact destroyed / decommissioned |
+| `ACTIVATE` | Policies, profiles, groups | Artifact made active |
+| `DEACTIVATE` | Policies, profiles, groups | Artifact made inactive |
+| `DEPRECATE` | All | Artifact deprecated |
+| `RETIRE` | All | Artifact retired |
+| `MEMBER_ADD` | Groups | Member added to group |
+| `MEMBER_REMOVE` | Groups | Member removed from group |
+| `RELATIONSHIP_CREATE` | Entities | Relationship established |
+| `RELATIONSHIP_RELEASE` | Entities | Relationship released |
+| `AUTHORIZE` | Cross-tenant, actors | Authorization granted |
+| `REVOKE` | Cross-tenant, actors | Authorization revoked |
+| `EVALUATE` | Policies | Policy evaluated (with outcome) |
+| `ENRICH` | Fields | Field enriched by policy, layer, or external policy evaluation |
+| `LOCK` | Fields | Field locked (override: immutable set) |
+| `HOLD_PLACE` | Resources | Resource hold placed with provider |
+| `HOLD_CONFIRM` | Resources | Resource hold confirmed |
+| `HOLD_RELEASE` | Resources | Resource hold released |
+| `DRIFT_DETECT` | Entities | Drift detected between Realized and Discovered |
+| `DRIFT_RESOLVE` | Entities | Drift resolved |
+| `INGEST` | Entities | Entity ingested (brownfield or import) |
+| `PROMOTE` | Entities | Ingested entity promoted to full lifecycle |
+| `EXPIRE` | Entities | Lifecycle time constraint expiry action fired |
+| `REHYDRATE` | Entities | Rehydration requested |
+| `QUERY` | external evaluation | Black box query sent and result received |
+| `DISCOVER` | Entities | Discovery cycle completed |
+| `LOGIN` | Actors | Actor authentication event |
+| `LOGOUT` | Actors | Actor session ended |
+| `CONFIG_CHANGE` | Platform | DCM configuration changed (profile activated, etc.) |
+
+---
+
+## 5. The "Who" — Composite Actor Record
+
+The `who` in an audit record is not a single identity — it is a **composite actor chain** tracing from the immediate action back to the human who ultimately authorized it.
+
+### 5.1 Actor Types
+
+| Type | Example | authorized_by |
+|------|---------|--------------|
+| `human` | Platform admin changes a policy | Self |
+| `system_component` | Lifecycle Constraint Enforcer fires expiry | Policy that set the constraint → human who activated policy |
+| `policy` | Transformation Policy enriches a field | Human who activated the policy |
+| `provider` | Service Provider updates Realized State | Dispatch that triggered it → human who submitted request |
+| `scheduled_job` | Discovery cycle runs | Owner of the scheduled job |
+| `mode4_provider` | Black box enriches a field | Policy that triggered the query → human who activated policy |
+
+### 5.2 System-Initiated Actions
+
+For system-initiated changes where there is no immediate human actor, the authorization chain traces back as far as possible:
+
+```yaml
+actor:
+  immediate:
+    type: system_component
+    uuid: 
+    display_name: "Lifecycle Constraint Enforcer"
+  authorized_by:
+    uuid: null   # no specific human — system policy
+    display_name: "DCM System Policy LTC-003"
+    authorization_method: system_policy
+  policy_uuid: 
+  policy_version: "1.0.0"
+```
+
+---
+
+## 6. Retention Model
+
+### 6.1 Reference-Based Retention
+
+Audit records are retained based on the lifecycle state of all referenced entities — not on a fixed time schedule.
+
+```
+Audit record created
+  │  retention_status: live (all referenced entities tracked)
+  │
+  ▼  [continuous monitoring]
+  │
+  As referenced entities change state:
+  │  DCM updates last_known_state on each referenced_entity
+  │  When all reach terminal state → retention_status: all_retired
+  │
+  ▼  retention_status: all_retired
+  │  Governing policy determines retain_until date
+  │  retention_status: policy_governed
+  │
+  ▼  retain_until reached
+     Audit record eligible for destruction / archival
+```
+
+### 6.2 Post-Lifecycle Retention Options
+
+| Policy Setting | Meaning | Default Profile |
+|---------------|---------|----------------|
+| `destroy_immediately` | Destroy when last entity retires | (not available in standard+) |
+| `retain_for: P90D` | 90 days post-retirement | dev profile |
+| `retain_for: P3Y` | 3 years post-retirement | standard profile |
+| `retain_for: P7Y` | 7 years post-retirement | prod, fsi profiles (DEFAULT) |
+| `retain_for: P10Y` | 10 years post-retirement | sovereign profile |
+| `retain_indefinitely` | Never destroy | optional — maximum compliance |
+| `archive_after: P1Y` | Move to cold storage 1 year post-retirement | configurable |
+
+### 6.3 Retention Shorter Than Referenced Entity Lifetime
+
+This cannot happen. While any referenced entity is live, `retention_status: live` and the record is retained unconditionally. The retention policy only applies **after** all referenced entities reach terminal state. A 90-day retention policy means "90 days after the last referenced entity is retired" — not "90 days after creation."
+
+---
+
+## 7. Two-Stage Audit — Synchronous Commit + Async Enrichment
+
+### 7.1 The Design
+
+DCM uses a **two-stage audit model** that provides synchronous durability guarantees without impacting request processing performance.
+
+```
+Stage 1 — Commit Log (synchronous, in critical path, < 1ms)
+Stage 2 — Audit Store (asynchronous, out of critical path, full record)
+```
+
+**Stage 1** writes a minimal Commit Log entry synchronously using consensus protocol (Raft). The write is confirmed when a quorum of Commit Log replicas acknowledges it. The operation returns success after Stage 1 confirms — not after the Audit Store write.
+
+**Stage 2** runs asynchronously via the Audit Forward Service: enriches the minimal Commit Log entry into a full audit_record, computes the hash chain, and writes to the Audit Store with retry.
+
+### 7.2 Stage 1 — Commit Log Entry (minimal, ultra-fast)
+
+```yaml
+commit_log_entry:
+  entry_uuid:             # links to full audit_record in Stage 2
+  sequence:            # monotonically increasing — global ordering
+  timestamp:    # authoritative audit timestamp
+  entity_uuid: 
+  entity_type: 
+  action: 
+  actor_uuid:             # immediate actor only
+  request_uuid:           # if applicable
+  tenant_uuid: 
+  change_fingerprint: 
+  # change_fingerprint enables Stage 2 to verify full record matches Stage 1
+
+  status: 
+  forwarded_at:       # populated by Audit Forward Service
+  audit_record_uuid:      # UUID of full audit_record in Audit Store
+```
+
+**Stage 1 guarantees:** the change happened, at this exact time, this actor performed it, this entity was affected, this action was taken. Full detail follows in Stage 2.
+
+**Commit Log quorum write** (distributed deployment):
+```
+Write confirmed when quorum acknowledges:
+  ├── Replica 1 (local node)     → ACK ─┐
+  ├── Replica 2 (different node) → ACK ─┤ quorum (2/3) — write confirmed
+  └── Replica 3 (different zone) → (async best-effort)
+```
+
+### 7.3 Stage 2 — Audit Forward Service
+
+```
+Audit Forward Service reads pending_forward Commit Log entries
+  │
+  ├── Retrieve full change context from DCM internal state
+  │   (field values before/after, complete actor chain, relationship detail)
+  │
+  ├── Construct complete audit_record (full structure per Section 3)
+  │   - Compute record_hash + previous_record_hash (hash chain)
+  │   - Set retention.referenced_entities
+  │
+  ├── Write to Audit Store
+  │   → Success: mark commit_log_entry status: forwarded
+  │   → Failure: retry with exponential backoff
+  │              N retries exhausted → status: forward_failed, alert admin
+  │
+  └── Commit Log entry eligible for cleanup after:
+      status: forwarded AND entry age > Commit Log retention window
+```
+
+### 7.4 Recoverability
+
+| Failure Scenario | Recovery |
+|-----------------|---------|
+| DCM crashes after Stage 1, before Stage 2 | On restart, Audit Forward Service replays all `pending_forward` entries |
+| Audit Store unavailable | Commit Log accumulates; Audit Forward Service retries when Audit Store recovers |
+| Stage 2 fails mid-enrichment | Commit Log entry remains `pending_forward`; retried from committed Stage 1 data |
+| Commit Log quorum unavailable | Stage 1 fails → operation aborted → no silent change |
+| All Commit Log replicas lost | Recovery from replica backup; forward_failed entries investigated |
+
+### 7.5 Performance Characteristics
+
+| Component | Latency | In Critical Path? |
+|-----------|---------|-----------------|
+| Stage 1 — Commit Log quorum write | < 1ms (local NVMe + Raft) | Yes |
+| Stage 2 — Audit Store write | 5–50ms (network + indexing) | No |
+| Full audit record visible | Seconds to minutes after Stage 1 | No |
+
+**The Stage 1 timestamp is the authoritative audit timestamp.** Stage 2 write time is when the full record became queryable — not when the change occurred.
+
+---
+
+---
+
+## 8. Tamper-Evidence and Payload Integrity — Merkle Tree
+
+DCM unifies audit trail integrity and payload chain-of-custody verification into a single mechanism: a **Merkle tree** following the RFC 9162 (Certificate Transparency v2.0) pattern. Each audit record is a leaf in the tree. The tree provides O(log n) inclusion proofs (prove a record exists) and O(log n) consistency proofs (prove the log is append-only). Signed Tree Heads provide the root of trust.
+
+This model satisfies NIST SP 800-53 AU-9(3) (cryptographic protection of audit information), AU-10 (non-repudiation with AU-10(2) producer identity binding, AU-10(3) chain of custody, AU-10(5) digital signatures), SI-7 (information integrity), and NIST SP 800-171 3.3 (audit and accountability for CUI/CMMC).
+
+### 8.1 Audit Granularity Levels
+
+Administrators configure how granular the audit trail is. Higher granularity produces more leaves per request, enabling more precise forensics at the cost of more storage and processing.
+
+| Level | Leaves per request | What is tracked | Use case |
+|-------|-------------------|----------------|----------|
+| **stage** | ~5–6 | One leaf per pipeline stage (intent, assembly, policy, placement, dispatch, callback). Each leaf includes before/after payload hash for the entire stage. | Homelab, dev, demo — proves the pipeline executed correctly. |
+| **mutation** | ~15–30 | One leaf per individual change — each layer merge, each policy evaluation, each constraint resolution, each placement decision. Before/after payload hash per mutation. | Production — full chain of custody. Shows exactly which policy or layer caused each change. |
+| **field** | ~15–30 (same leaf count, richer content) | Mutation-level plus per-field old/new value hashes on every mutation. For overrides, records both old and new value hashes. | FedRAMP, sovereign, FSI — maximum auditability. An auditor can verify not just that a field changed, but what it changed from and to. |
+
+**Profile defaults:**
+
+| Profile | Default granularity | Override permitted? |
+|---------|-------------------|-------------------|
+| `minimal` | stage | Yes — can increase |
+| `dev` | stage | Yes |
+| `standard` | mutation | Yes — can increase to field |
+| `prod` | mutation | Yes |
+| `fsi` | field | No — field is minimum for FSI |
+| `sovereign` | field | No — field is minimum for sovereign |
+
+**Configuration:**
+```yaml
+audit:
+  granularity: stage | mutation | field      # default per profile
+  signed_tree_head_interval: 100             # compute STH every N leaves (default)
+  signed_tree_head_max_delay: PT60S          # or every T seconds, whichever comes first
+  verification_mode: synchronous | asynchronous | disabled
+  tree_node_storage: materialized | computed # store intermediate nodes or recompute
+```
+
+`verification_mode` controls whether the next pipeline stage verifies the previous stage's signature before processing:
+- `synchronous` — next stage blocks until verification passes (required for fsi/sovereign)
+- `asynchronous` — verification runs in background; pipeline proceeds optimistically
+- `disabled` — no inter-stage verification (homelab only)
+
+### 8.2 Leaf Structure
+
+Every audit record (regardless of granularity level) is a Merkle tree leaf with this structure:
+
+```yaml
+audit_leaf:
+  # Identity
+  leaf_uuid: 
+  leaf_index:                          # position in global Merkle tree
+  request_uuid:                       # which request this belongs to
+  entity_uuid:                 # primary entity (null for system events)
+  tenant_uuid: 
+
+  # What happened
+  stage: intent_submitted | layer_applied | policy_evaluated | constraint_resolved |
+         placement_scored | dispatched | provider_callback | ...
+  source: ""                        # which layer, policy, or service
+  source_type: actor | layer_merge | policy_gatekeeper | policy_transformation |
+               policy_validation | constraint_resolution | placement | dispatch |
+               provider_callback | system
+  action:                # from Section 4
+  decision: 
+
+  # Chain of custody — payload integrity
+  input_payload_hash:              # hash of payload BEFORE this mutation
+  output_payload_hash:             # hash of payload AFTER this mutation
+  context_hash:             # evaluation context hash (policy stages only)
+
+  # Field-level detail (mutation and field granularity only)
+  fields_changed: ["field.path.one", "field.path.two"]    # mutation+ only
+  field_mutations:                                         # field granularity only
+    - field: "monitoring.agent_config"
+      action: injected | overridden | removed
+      old_value_hash:       # null for injected (no previous value)
+      new_value_hash: 
+
+  # Who
+  signer_uuid:                        # service or actor identity
+  signer_type: service | actor | provider
+  timestamp: 
+
+  # Cryptographic binding
+  signature:         # signature over all above fields
+  previous_leaf_hash:              # hash of the previous leaf for this request
+```
+
+At **stage** granularity, `fields_changed` and `field_mutations` are omitted. The leaf records only the before/after payload hash for the entire stage.
+
+At **mutation** granularity, `fields_changed` is populated (which fields changed) but `field_mutations` (old/new value hashes per field) is omitted.
+
+At **field** granularity, both are populated. Full forensic detail.
+
+### 8.3 Signed Tree Heads
+
+The Audit Service periodically computes a new Merkle root and signs it:
+
+```yaml
+signed_tree_head:
+  tree_size:                           # number of leaves in the tree
+  timestamp: 
+  sha256_root_hash:                # Merkle root
+  signature:                       # signed by DCM's audit signing key
+```
+
+Signed Tree Heads are computed every N leaves or every T seconds (configurable). They are the root of trust for all verification. External auditors verify records against the signed tree head — they don't need access to the database, only the tree head and the inclusion proof.
+
+### 8.4 Verification
+
+**Inclusion proof** — prove a specific record exists in the tree:
+```
+POST /api/v1/audit/tree/inclusion-proof
+Input: leaf_hash, tree_size
+Output: proof path (O(log n) hashes from leaf to root)
+```
+
+**Consistency proof** — prove the current tree is a strict superset of a previous tree:
+```
+POST /api/v1/audit/tree/consistency-proof
+Input: old_tree_size, new_tree_size
+Output: proof path (O(log n) hashes)
+```
+
+**Request chain verification** — prove a specific request's full pipeline integrity:
+```
+POST /api/v1/audit/tree/verify-request/{request_uuid}
+Output: all leaves for the request + inclusion proofs + payload hash chain verification
+  - Verifies each leaf is in the tree (inclusion proofs)
+  - Verifies output_payload_hash[N] == input_payload_hash[N+1] (chain of custody)
+  - Verifies all signatures against known signing keys
+  - Verifies timestamps are monotonically increasing
+  - Returns: VERIFIED | CHAIN_BREAK (with break location) | SIGNATURE_INVALID
+```
+
+### 8.5 Applicable Standards
+
+| Standard | Control | How DCM satisfies it |
+|----------|---------|---------------------|
+| RFC 9162 | Certificate Transparency v2.0 — Merkle tree, inclusion/consistency proofs, signed tree heads | Structural model for DCM's audit tree |
+| NIST 800-53 AU-9(3) | Cryptographic protection of audit information | Merkle tree + signed tree heads |
+| NIST 800-53 AU-10(2) | Validate binding of information producer identity | Each leaf signed by producing service |
+| NIST 800-53 AU-10(3) | Chain of custody | input/output payload hash linking across leaves |
+| NIST 800-53 AU-10(5) | Digital signatures on audit records | Ed25519/ECDSA-P256 per leaf + tree head |
+| NIST 800-53 SI-7(1) | Integrity checks | Payload hash verification at each stage transition |
+| NIST 800-171 3.3 | Audit and accountability for CUI | Full audit trail with integrity protection |
+| FIPS 186-5 | Digital Signature Standard | Ed25519 or ECDSA-P256 for signatures; ML-DSA ready for post-quantum |
+| FIPS 180-4 | SHA-2 | SHA-256 for all hashing |
+| FedRAMP High | AU-10 non-repudiation required | Merkle tree + signing satisfies all AU-10 enhancements |
+| SOC 2 Type II CC7.2 | Detection of unauthorized changes | Consistency proofs detect any log modification |
+
+---
+
+## 9. DCM System Policies
+
+| Policy | Rule |
+|--------|------|
+| `AUD-001` | Every modification to any DCM artifact must produce a Commit Log entry synchronously before the operation returns success. Commit Log write failure aborts the operation — no silent unaudited changes. |
+| `AUD-002` | Audit records are append-only and immutable. No audit record may be modified or deleted while retention_status is `live` or `policy_governed`. |
+| `AUD-003` | Audit records must survive at least as long as any referenced entity is in a non-retired/non-decommissioned state (retention_status: live). |
+| `AUD-004` | Post-lifecycle retention is governed by policy. Default is `retain_for: P7Y` after all referenced entities reach terminal state. |
+| `AUD-005` | The actor field must identify both the immediate actor and the authorized_by human actor chain to the extent traceable. |
+| `AUD-006` | Audit records form a Merkle tree (RFC 9162 pattern). Each leaf carries a signature from the producing service and a payload hash linking it to adjacent leaves in the request chain. |
+| `AUD-007` | The action field must use the closed vocabulary — free-text action fields are invalid and must be rejected at write time. |
+| `AUD-008` | Audit Store implementations must support queries by: entity_uuid, actor_uuid, action, timestamp range, tenant_uuid, request_uuid, leaf_index, and retention_status. |
+| `AUD-009` | The Audit Forward Service must deliver all Commit Log entries to the Audit Store with exponential backoff retry. Commit Log entries may only be cleared after both: (a) Audit Store confirms receipt AND (b) entry has aged beyond the Commit Log retention window. |
+| `AUD-010` | Merkle tree verification (inclusion proofs, consistency proofs, request chain verification) must be available as first-class DCM operations. Verification failures trigger immediate security alerts. |
+| `AUD-011` | On DCM restart, the Audit Forward Service must replay all `status: pending_forward` Commit Log entries before accepting new operations. |
+| `AUD-012` | Signed Tree Heads must be computed at the interval configured by the deployment profile. STH signing key must be stored in the secrets table (internal) or Vault (external). |
+| `AUD-013` | The Stage 1 timestamp in the Commit Log is the authoritative audit timestamp. Stage 2 enrichment timestamps record when the full audit record became queryable — not when the change occurred. |
+| `AUD-014` | Audit granularity level is configured per deployment profile. `fsi` and `sovereign` profiles require `field` granularity — this cannot be downgraded. |
+| `AUD-015` | Inter-stage verification mode is configured per deployment profile. `fsi` and `sovereign` profiles require `synchronous` verification — the pipeline halts if any signature verification fails. |
+
+---
+
+## 10. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should hash chain verification run continuously or on-demand? | Security | ✅ Resolved — three levels: continuous write (chain construction), scheduled sweep (weekly to 6-hourly per profile), on-demand (operator-triggered); failure → security alert + integrity incident (AUD-014) |
+| 2 | Should the WAL have a configurable maximum capacity, and what happens when it is reached? | Availability | ✅ Resolved — configurable max capacity; alert_and_continue (standard/prod); reject_new_ops (fsi/sovereign); backpressure at 75%/90%; P7D max age escalation (AUD-015) |
+| 3 | Should audit records for system-initiated changes (no human actor) be flagged differently in the dashboard? | Operational | ✅ Resolved — actor.type: human/service_account/system; system_actor block with component/trigger/policy; full audit records; enables filtering in queries and dashboards (AUD-016) |
+| 4 | How does hash chain verification interact with distributed DCM deployments where audit records may be written to multiple regional stores? | Architecture | ✅ Resolved — per-instance hash chains; daily Merkle root federation integrity proof at Hub DCM; cross-instance queries via parallel chains + correlation_id (AUD-017) |
+
+---
+
+## 11. Related Concepts
+
+- **Audit, Provenance, and Observability** (doc 12) — three distinct concerns; this document covers the audit concern in full
+- **Field-Level Provenance** — data lineage embedded in every payload; separate from audit records
+- **data stores** (doc 11) — Audit Store contract: append-only, WAL delivery, hash chain, retention tracking
+- **Universal Groups** (doc 15) — all group changes produce audit records per this model
+- **Policy Organization** (doc 14) — policy activation, shadow evaluation, and external evaluation queries all produce audit records
+
+
+## 10. Universal Audit Gap Resolutions
+
+### 10.1 Hash Chain Verification Modes (Q1)
+
+Hash chain verification operates at three independent levels:
+
+```yaml
+hash_chain_verification:
+  continuous_write: true              # always — hash computed on every write (chain construction)
+
+  scheduled_sweep:
+    enabled: true
+    schedule:
+      standard: "0 2 * * 0"          # weekly
+      prod: "0 2 * * *"              # daily
+      fsi: "0 */6 * * *"            # every 6 hours
+      sovereign: "0 */6 * * *"
+
+  on_demand:
+    enabled: true                     # always available to platform admin
+    max_range: P365D                  # maximum time range per verification run
+
+  on_verification_failure:
+    action: alert_security_team
+    halt_new_writes: false            # do not halt — alert and investigate
+    # Halting writes is itself a security risk; alerting is the correct response
+    create_integrity_incident: true
+```
+
+Continuous verification is part of chain construction (not a separate process). Scheduled sweep catches tampering between writes. On-demand is available for incident investigation, compliance audit, and pre-report verification.
+
+### 10.2 Commit Log Maximum Capacity (Q2)
+
+The Commit Log has a configurable maximum capacity with a declared overflow policy — different profiles have different trade-offs between availability and audit completeness.
+
+```yaml
+commit_log_capacity:
+  max_size: 10Gi                      # configurable; profile-governed
+  max_age: P7D                        # records older than 7d escalate regardless
+  on_capacity_exceeded:
+    profile_defaults:
+      minimal: alert_and_continue     # availability priority
+      dev: alert_and_continue
+      standard: alert_and_continue
+      prod: alert_and_continue
+      fsi: reject_new_ops             # audit completeness priority
+      sovereign: reject_new_ops
+  warn_at_percent: 75                 # alert at 75% capacity
+  urgent_at_percent: 90              # urgent alert at 90%
+```
+
+**`reject_new_ops` for fsi/sovereign:** Operating without a functional audit trail is a compliance violation in regulated environments. Stopping operations is preferable to operating unaudited — same principle as Commit Log quorum unavailability → abort operation (STO-002).
+
+### 10.3 System-Initiated Audit Records (Q3)
+
+Audit records for system-initiated changes declare `actor.type: system` with a `system_actor` block identifying the DCM component, trigger, and authorizing policy.
+
+```yaml
+audit_record:
+  action: REHYDRATE
+  actor:
+    uuid: 
+    type: system                      # human | service_account | system
+    system_actor:
+      component: lifecycle_constraint_enforcer
+      trigger: entity_ttl_expired
+      entity_uuid: 
+      policy_uuid: 
+    authorization: implicit           # implicit = authorized by DCM architecture
+                                      # explicit = authorized by named policy
+```
+
+System actor records are full audit records — they appear in all queries and compliance reports. `actor.type` enables filtering:
+- `filter: actor.type = human` → all human-initiated changes
+- `filter: actor.type = system` → all automated lifecycle operations
+- `filter: actor.type = service_account` → all API/programmatic changes
+
+### 10.4 Distributed Hash Chain Integrity (Q4)
+
+In distributed DCM deployments (Hub + Regional + Sovereign DCMs), each instance maintains its own independent hash chain. Federation-level integrity is provided by daily Merkle root proofs.
+
+```yaml
+distributed_hash_chain:
+  model: per_instance               # each DCM instance has its own chain
+  instance_chain:
+    chain_id:    # chain scoped to this instance
+
+  federation_integrity_proof:
+    enabled: true
+    schedule: "0 0 * * *"           # daily
+    mechanism: merkle_root
+    # Hub DCM collects chain tip hashes from all Regional DCMs
+    # Computes Merkle root → stores as federation_integrity_record
+    # Any chain break in any instance is detectable against this root
+    stored_at: hub_dcm_audit_store
+    signed_by: hub_dcm_service_account
+```
+
+**Cross-instance queries:** Records from different chains are presented as parallel chains with cross-references via `correlation_id`. Not merged into a single chain — each instance's chain remains independently verifiable. Federation-level verification requires Hub DCM connectivity; per-instance verification is always available locally.
+
+### 10.5 System Policies — Universal Audit Gaps
+
+| Policy | Rule |
+|--------|------|
+| `AUD-014` | Hash chain verification operates at three levels: continuous (hash computed on every write), scheduled sweep (weekly to every 6 hours per profile), and on-demand (operator-triggered for any time range). Verification failure triggers a security alert and integrity incident. New audit writes continue — halting writes is itself a security risk. |
+| `AUD-015` | The Commit Log has configurable maximum capacity with a declared overflow policy: alert_and_continue (standard/prod) or reject_new_ops (fsi/sovereign). Backpressure alerts fire at 75% and 90% capacity. Records older than P7D trigger escalation regardless of capacity. |
+| `AUD-016` | Audit records for system-initiated changes declare actor.type: system with a system_actor block identifying the DCM component, trigger, and authorizing policy. System actor records are full audit records appearing in all queries and compliance reports. actor.type enables filtering between human, service_account, and system-initiated changes. |
+| `AUD-017` | In distributed DCM deployments, each instance maintains its own independent hash chain scoped to that instance. Federation-level integrity is maintained via daily Merkle root proofs computed from all instance chain tips, stored at the Hub DCM. Cross-instance audit queries present parallel chains with cross-references via correlation_id. |
+
+
+## 9a. Audit vs Observability — The Definitive Distinction (Q16)
+
+Audit and Observability are separate components with separate storage contracts, separate consumers, and opposite fundamental trade-offs. They cannot be combined without violating one contract or the other.
+
+### 9a.1 Comparison
+
+| Dimension | Audit | Observability |
+|-----------|-------|--------------|
+| **Purpose** | Immutable record of WHAT HAPPENED and WHO authorized it | Real-time visibility into SYSTEM HEALTH and PERFORMANCE |
+| **Primary consumers** | Auditors, compliance, security, legal, regulators | SREs, platform engineers, operators, dashboards |
+| **Write rate** | Low — one record per action | Very high — multiple per second per component |
+| **Retention** | Very long — P7Y+ (compliance-driven) | Short — days to months (operational) |
+| **Mutability** | Never — append-only, hash-chained | Downsampling and aggregation acceptable |
+| **Accuracy** | 100% required — no sampling | Statistical sampling acceptable |
+| **Compliance grade** | Required | Not required |
+| **Cost per event** | High — hash chain computation | Low — time series append |
+| **Failure behavior** | Missing audit = compliance violation | Missing observability = operational inconvenience |
+| **Query model** | Point-in-time, actor-based, compliance reports | Time-series, rate queries, anomaly detection |
+| **Data model** | Closed 30-action vocabulary, structured | Open schema — any component emits any metric |
+| **Storage type** | Audit Store (specialized sub-type) | Time-series database (Prometheus, InfluxDB) |
+
+### 9a.2 The Relationship
+
+Observability data MAY reference audit record UUIDs for correlation — a spike in error rate can link to audit records from that time window. But they live in separate stores with separate contracts.
+
+- **Audit** answers: "What happened and who authorized it?"
+- **Observability** answers: "Is the system healthy and how is it performing?"
+
+These are different questions requiring different storage architectures.
+
+### 9a.3 System Policy
+
+| Policy | Rule |
+|--------|------|
+| `AUD-013` | Audit and Observability are separate components with separate storage contracts, consumers, and failure behaviors. Audit is compliance-grade, append-only, hash-chained, long-retention. Observability is operational, time-series, high-throughput, short-retention. They serve different consumers and cannot be combined without violating one contract or the other. Observability data may reference audit record UUIDs for correlation but is stored separately. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/17-deployment-redundancy.md b/content/docs/architecture/data-model/17-deployment-redundancy.md
new file mode 100644
index 0000000..048eed7
--- /dev/null
+++ b/content/docs/architecture/data-model/17-deployment-redundancy.md
@@ -0,0 +1,786 @@
+# DCM Data Model — Deployment and Redundancy Model
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Context and Purpose](00-context-and-purpose.md) | [data stores](11-storage-providers.md) | [Universal Audit Model](16-universal-audit.md) | [Policy Organization](14-policy-profiles.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + PROVIDER**
+>
+> Data: deployment specification. Provider: data store redundancy
+
+
+
+---
+
+> **Operational guidance:** GitOps the disaster recovery runbook and RTO/RPO tables are in [Operational Reference](41-operational-reference.md) Section 3.
+
+## 1. Purpose
+
+Every DCM component, every data store, and every capability is designed for redundancy by default. Redundancy is not an add-on or an advanced configuration — it is the baseline operational posture for all profiles above `minimal`.
+
+**The minimal profile** provides single-instance deployment for home lab and evaluation simplicity. All other profiles assume redundancy as the floor. The transition from `minimal` to `dev` is the transition from "works on my laptop" to "survives a node failure."
+
+**Everything in DCM runs as a container in a pod.** No bare-metal DCM components. No special-case deployment paths. Every DCM component follows the same container lifecycle, the same health check model, the same rolling update pattern. DCM runs on Kubernetes — and manages Kubernetes.
+
+**DCM is self-hosting.** DCM's own deployment is expressible as DCM resources. DCM can manage its own lifecycle, detect drift in its own components, and rehydrate its own deployment from Git. This is the ultimate expression of the data center repave use case — DCM restoring itself.
+
+---
+
+## 2. Design Principles
+
+**Redundant by default.** Every component, store, and capability has a redundancy model. The `minimal` profile sets `replicas: 1`. Every other profile sets `replicas: >= 3` with quorum writes and anti-affinity scheduling.
+
+**Everything containerized.** All DCM components run as containers in Kubernetes pods. No exceptions. This gives a consistent deployment model, rolling updates, health checks, and self-healing as first-class properties.
+
+**Profile-governed redundancy.** Replica counts, quorum thresholds, geo-replication, and anti-affinity requirements are declared by the active Profile — not by per-component configuration. Activating a Profile configures redundancy for the entire deployment.
+
+**Self-hosting.** DCM's own deployment is a DCM resource. DCM manages itself through the same model it uses to manage customer infrastructure.
+
+**Stateless control plane.** All DCM control plane components are stateless — all state lives in external stores. Any component instance can fail and be replaced without data loss. State recovery means restarting a pod — not restoring a database.
+
+**Quorum writes for durability.** All durable stores use quorum writes — a write is confirmed only when a majority of replicas acknowledge it. This ensures durability even if a minority of replicas fail simultaneously.
+
+---
+
+## 3. Component Redundancy Model
+
+### 3.1 Control Plane Components
+
+All control plane components are stateless, horizontally scalable, and deployed as Kubernetes Deployments with configurable replica counts.
+
+```yaml
+component_redundancy:
+  component: request_payload_processor   # same model for all components
+  deployment_type: kubernetes_deployment
+  replicas: 3                    # set by active Profile
+  affinity:
+    anti_affinity: required      # pods spread across nodes
+    zone_spread: preferred       # prefer spreading across availability zones
+  disruption_budget:
+    min_available: 2             # always keep 2 running during rolling updates
+  health_check:
+    liveness:
+      path: /healthz
+      interval_seconds: 10
+      failure_threshold: 3
+    readiness:
+      path: /readyz
+      interval_seconds: 5
+      failure_threshold: 2
+  rolling_update:
+    strategy: RollingUpdate
+    max_unavailable: 0           # never take a pod down before replacement is ready
+    max_surge: 1
+```
+
+**Control plane components:**
+
+| Component | Stateless? | Replica Model |
+|-----------|-----------|--------------|
+| API Gateway | Yes | Deployment + HorizontalPodAutoscaler |
+| Request Payload Processor | Yes | Deployment |
+| Policy Engine (OPA) | Yes | Deployment + PolicyBundle sidecar |
+| Placement Engine | Yes | Deployment |
+| Service Catalog | Yes | Deployment |
+| IDM / IAM | Yes | Deployment (external IdP recommended) |
+| Audit Forward Service | Yes | Deployment (1 active + 1 standby) |
+| Lifecycle Constraint Enforcer | Yes | Deployment (leader election) |
+| Drift Detection | Yes | Deployment (leader election for scheduling) |
+| Resource Discovery | Yes | Deployment (leader election) |
+| Message Bus Router | Yes | Deployment |
+
+**Leader election** for scheduler-type components (Lifecycle Constraint Enforcer, Drift Detection, Resource Discovery): multiple replicas run but only one holds the leader lease at a time. On leader failure, a replica acquires the lease within seconds.
+
+### 3.2 Data Store Redundancy
+
+All DCM data stores run as containers. Each store type has a declared replication and quorum model.
+
+#### Commit Log
+
+```yaml
+commit_log:
+  replicas: 3
+  write_quorum: 2               # confirmed durable when 2/3 replicas acknowledge
+  read_quorum: 1                # any replica can serve reads
+  affinity:
+    zone_spread: required       # replicas MUST span availability zones
+  implementation: etcd          # or equivalent consensus store
+  # etcd is purpose-built for this pattern: Raft consensus, quorum writes,
+  # sub-millisecond local writes, proven in Kubernetes itself
+```
+
+The Commit Log uses consensus protocol (Raft/equivalent). A write is confirmed when the quorum acknowledges — ensuring durability even if a minority of replicas fail simultaneously.
+
+#### DCM database (Intent, Requested, Layers, Policies)
+
+```yaml
+gitops_store:
+  implementation: gitea          # or equivalent self-hosted Git
+  replicas: 3
+  replication_mode: active_active  # any node can accept writes
+  write_quorum: 2
+  backup:
+    enabled: true
+    schedule: "0 */6 * * *"    # every 6 hours
+    retention: 30d
+```
+
+#### pipeline_events table (Realized, Discovered, Audit Events)
+
+```yaml
+event_stream:
+  implementation: kafka          # or equivalent
+  brokers: 3
+  replication_factor: 3
+  min_insync_replicas: 2        # minimum replicas that must acknowledge a write
+  partitions: 12                # enables parallel consumption
+  retention:
+    bytes: -1                   # unlimited — retention governed by policy
+    ms: -1                      # unlimited
+```
+
+#### Audit Store
+
+```yaml
+audit_store:
+  implementation: elasticsearch  # or equivalent — optimized for queryable retention
+  replicas: 3
+  primary_shards: 5
+  replica_shards: 1             # each shard has 1 replica = 2 copies total
+  geo_replicated: true          # in prod/fsi/sovereign profiles
+  append_only_enforced: true    # storage layer enforces immutability
+```
+
+#### Search Index (Non-Authoritative)
+
+```yaml
+search_index:
+  implementation: elasticsearch
+  replicas: 2                   # lower redundancy — can rebuild from Git
+  rebuild_from_git: true        # on data loss, rebuild from authoritative stores
+```
+
+### 3.3 Container Specification
+
+Every DCM component pod follows a common security and resource model:
+
+```yaml
+pod_spec:
+  security_context:
+    run_as_non_root: true
+    run_as_user: 65534           # nobody
+    run_as_group: 65534
+    fs_group: 65534
+    seccomp_profile:
+      type: RuntimeDefault
+    capabilities:
+      drop: [ALL]
+
+  containers:
+    - name: 
+      image: ghcr.io/dcm-project/:
+      image_pull_policy: IfNotPresent
+      security_context:
+        read_only_root_filesystem: true
+        allow_privilege_escalation: false
+      resources:
+        requests:
+          cpu: 500m
+          memory: 512Mi
+        limits:
+          cpu: 2000m
+          memory: 2Gi
+      liveness_probe: 
+      readiness_probe: 
+      volume_mounts:
+        - name: tmp
+          mount_path: /tmp       # writable temp — no other writable paths
+
+  volumes:
+    - name: tmp
+      empty_dir: {}
+```
+
+---
+
+## 4. Redundancy by Profile
+
+Profile activation configures redundancy for the entire deployment. Organizations do not configure replica counts individually — they activate a profile.
+
+```yaml
+# Redundancy matrix per profile
+redundancy_matrix:
+  minimal:
+    control_plane_replicas: 1
+    store_replicas: 1
+    write_quorum: false
+    zone_spread: false
+    geo_replication: false
+    anti_affinity: false
+    note: "Single-instance. No redundancy. Home lab and evaluation only."
+
+  dev:
+    control_plane_replicas: 1
+    store_replicas: 1
+    write_quorum: false
+    zone_spread: false
+    geo_replication: false
+    anti_affinity: false
+    note: "Single-instance with backup. Basic resilience for dev environments."
+
+  standard:
+    control_plane_replicas: 3
+    store_replicas: 3
+    write_quorum: 2             # 2 of 3
+    zone_spread: preferred
+    geo_replication: false
+    anti_affinity: required
+    note: "Production baseline. Survives single node or zone failure."
+
+  prod:
+    control_plane_replicas: 3
+    store_replicas: 3
+    write_quorum: 2
+    zone_spread: required
+    geo_replication: true
+    anti_affinity: required
+    sla_disruption_budget: "always 2 replicas available"
+    note: "Production with SLA. Geo-replicated stores."
+
+  fsi:
+    control_plane_replicas: 5
+    store_replicas: 5
+    write_quorum: 3             # 3 of 5
+    zone_spread: required
+    geo_replication: true
+    anti_affinity: required
+    audit_store_replicas: 5
+    note: "FSI-grade. Higher quorum threshold. Compliance-grade audit."
+
+  sovereign:
+    control_plane_replicas: 5
+    store_replicas: 5
+    write_quorum: 3
+    zone_spread: required
+    geo_replication: true        # within sovereignty boundary only
+    anti_affinity: required
+    air_gap_backup: true
+    sovereignty_boundary_enforced: true
+    note: "Maximum. All geo-replication within sovereignty boundary."
+```
+
+---
+
+## 5. The DCM Deployment Specification
+
+The DCM deployment itself is a DCM resource — declared in YAML, stored in Git, governed by Policy, subject to the same four-state lifecycle as any other resource.
+
+```yaml
+dcm_deployment:
+  artifact_metadata:
+    uuid: 
+    handle: "deployments/primary/dcm-control-plane"
+    version: "1.0.0"
+    status: active
+
+  profile: system/profile/standard
+  kubernetes_namespace: dcm-system
+
+  # Redundancy — set by active Profile, overridable per component
+  redundancy:
+    control_plane:
+      replicas: 3
+      affinity:
+        anti_affinity: required
+        zone_spread: preferred
+      disruption_budget:
+        min_available: 2
+
+    stores:
+      commit_log:
+        replicas: 3
+        write_quorum: 2
+        implementation: etcd
+      gitops_store:
+        replicas: 3
+        write_quorum: 2
+        implementation: gitea
+      event_stream:
+        brokers: 3
+        replication_factor: 3
+        min_insync_replicas: 2
+        implementation: kafka
+      audit_store:
+        replicas: 3
+        geo_replicated: false     # standard profile default
+        implementation: elasticsearch
+      search_index:
+        replicas: 2
+        implementation: elasticsearch
+
+  # Provider health configuration
+  providers:
+    health_check_interval_seconds: 30
+    unhealthy_threshold: 3
+    automatic_failover: true
+
+  # Self-hosting: DCM manages its own deployment
+  self_managed: true
+  drift_detection_enabled: true
+  rehydration_enabled: true
+  # DCM detects if its own components drift from declared spec
+  # and can rehydrate (redeploy) from this declaration
+```
+
+---
+
+## 6. Self-Hosting — DCM Managing Itself
+
+DCM's own deployment is managed through the same model it uses to manage customer infrastructure. This is the **self-hosting principle** — DCM eats its own cooking.
+
+### 6.1 What Self-Hosting Means
+
+- DCM control plane components are defined as Resource Entities in DCM
+- DCM data stores are defined as data store resources in DCM
+- DCM's own Policy Groups govern DCM's own deployment constraints
+- DCM runs drift detection on its own components — a component running the wrong image version is drift
+- DCM can rehydrate its own deployment from the `dcm_deployment` declaration in Git
+
+### 6.2 The Bootstrap Problem
+
+DCM cannot manage itself before it exists. The bootstrap sequence:
+
+```
+1. Bootstrap installer deploys minimal DCM (single instance, no redundancy)
+   from a declarative bootstrap manifest
+   │
+2. Bootstrap DCM reads the target dcm_deployment declaration from Git
+   │
+3. Bootstrap DCM provisions itself to the target state:
+   - Scales from 1 to N replicas
+   - Provisions redundant stores
+   - Configures quorum
+   │
+4. Bootstrap instance hands off to the now-redundant DCM
+   │
+5. DCM manages its own lifecycle from this point forward
+```
+
+The bootstrap manifest is the only thing that exists outside DCM's management scope. It is minimal by design — just enough to get DCM running.
+
+### 6.3 Self-Hosted Drift Detection
+
+DCM continuously compares its own running state against the `dcm_deployment` declaration:
+
+| Drift Type | Example | Response |
+|-----------|---------|---------|
+| Wrong image version | Component running v1.1.0, declared v1.2.0 | Rolling update triggered |
+| Wrong replica count | 2 replicas running, declared 3 | Scale-up triggered |
+| Wrong resource limits | Component using more than declared limits | Alert + potential eviction |
+| Store replication mismatch | Store has 2 replicas, declared 3 | Replication repair triggered |
+
+### 6.4 The Repave Scenario
+
+The ultimate test of self-hosting: DCM is lost entirely (ransomware, catastrophic failure). Recovery:
+
+```
+1. Deploy bootstrap installer to new Kubernetes cluster
+   │
+2. Bootstrap DCM reads dcm_deployment declaration from Git backup
+   │
+3. DCM provisions itself — full redundant deployment
+   │
+4. DCM reads all resource declarations from Git
+   │
+5. DCM rehydrates customer workloads in dependency order
+   │
+6. Drift detection validates recovered state matches declared state
+```
+
+The recovery time is bounded by infrastructure provisioning speed — not by backup restoration or manual configuration. Everything is code. Everything is declarative. Everything is in Git.
+
+---
+
+## 7. Commit Log Redundancy — Two-Stage Audit Integration
+
+The Commit Log is the synchronous component of the two-stage audit model. In a distributed deployment, "synchronous durable write" means quorum acknowledgment:
+
+```
+DCM component initiates change
+  │
+  ▼
+Write to Commit Log (Raft consensus)
+  │  Propose to leader
+  │  Leader replicates to followers
+  │  Write confirmed when quorum (2/3 or 3/5) acknowledge
+  │
+  ├── Replica 1 (local node)     → ACK ─┐
+  ├── Replica 2 (different node) → ACK ─┤ quorum reached
+  └── Replica 3 (different zone) → ACK ─┘
+  │
+  ▼  Operation returns success (< 1ms typical with NVMe)
+  │
+  ▼  [async — Audit Forward Service]
+Read from any surviving Commit Log replica
+  │  Enrich → write to Audit Store
+  └── Clear Commit Log entry after Audit Store confirms
+```
+
+**Failure scenarios and recovery:**
+
+| Failure | During Stage 1 | Effect |
+|---------|---------------|--------|
+| Single replica fails before quorum | Quorum still achievable | No impact |
+| Majority fail before quorum | Commit Log unavailable | Operation aborted — no silent change |
+| Leader fails after quorum | New leader elected (seconds) | In-flight writes complete on new leader |
+| All replicas fail after quorum | Audit Forward Service reads from backup | Full recovery on restart |
+| Audit Store unavailable | Commit Log accumulates | Forward resumes when Audit Store recovers |
+
+---
+
+## 8. Network Architecture
+
+> **Full internal auth specification:** See [Internal Component Authentication](36-internal-component-auth.md) for component identity model, Internal CA, bootstrap protocol, and ICOM-001–ICOM-009 system policies.
+
+### 8.1 Service Mesh
+
+All DCM component-to-component communication uses a service mesh (Istio or equivalent):
+- mTLS everywhere (RFC 8446 TLS 1.3 + RFC 5280 X.509) — no plaintext internal communication
+- Traffic policies enforced at mesh level
+- Observability: traces, metrics, logs for all inter-component calls
+- Circuit breaking: prevent cascade failures
+
+### 8.2 Ingress
+
+External traffic enters through a redundant Ingress layer:
+
+```
+External clients
+  │
+  ▼
+Load Balancer (external — cloud or on-premises)
+  │
+  ▼
+Ingress Controller (replicated — 2+ instances)
+  │
+  ▼
+API Gateway pods (3+ instances, anti-affinity)
+  │
+  ▼
+Internal service mesh
+```
+
+### 8.3 DNS and Service Discovery
+
+All DCM components address each other via Kubernetes Service DNS. No hardcoded IPs. Service discovery is automatic — a new pod replica is immediately addressable.
+
+---
+
+## 9. DCM System Policies — Redundancy
+
+| Policy | Rule |
+|--------|------|
+| `RED-001` | All DCM control plane components must run as containers in Kubernetes pods |
+| `RED-002` | All control plane components must be stateless — all persistent state in external stores |
+| `RED-003` | In profiles above `minimal`, all control plane components must have `replicas >= 3` with anti-affinity |
+| `RED-004` | All durable stores in profiles above `minimal` must use quorum writes with `write_quorum >= 2` |
+| `RED-005` | The Commit Log must use consensus protocol (Raft or equivalent) with quorum writes |
+| `RED-006` | The DCM deployment must be declared as a DCM resource in Git — self-hosting required |
+| `RED-007` | DCM must run drift detection on its own components — version drift is treated as resource drift |
+| `RED-008` | A rolling update of any DCM component must not reduce available replicas below `min_available` |
+| `RED-009` | All DCM component communication must use mTLS — no plaintext internal communication |
+| `RED-010` | The bootstrap manifest is the only DCM configuration outside DCM's management scope |
+
+---
+
+## 10. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should the bootstrap manifest be version-controlled and verifiable? | Bootstrap integrity | ✅ Resolved — GitOps store + hash verification at every startup; tampering prevents start; operator-signed (RED-011) |
+| 2 | How does DCM handle Kubernetes cluster upgrades in sovereign deployments? | Operational | ✅ Resolved — pre-staged images via signed bundles; maintenance mode during upgrade; startup verification before resume (RED-012) |
+| 3 | Should DCM support non-Kubernetes container runtimes? | Portability | ✅ Resolved — Kubernetes primary/required for production; Podman/Docker Compose for dev/community only (RED-013) |
+| 4 | What is the minimum hardware specification per profile? | Implementation | ✅ Resolved — declared as DCM Resource definitions; enforced by placement engine; table documented (RED-014) |
+| 5 | How does DCM's self-hosted drift detection handle DCM drifting from its own state? | Self-hosting | ✅ Resolved — DCM is DCM-managed resource; Operator reconciles; bootstrap hash provides independent check; audit hash chain externally verifiable (RED-015) |
+
+---
+
+## 11. Related Concepts
+
+- **Universal Audit Model** (doc 16) — two-stage audit; Commit Log quorum model
+- **Policy Organization** (doc 14) — Profile-governed redundancy configuration
+- **data stores** (doc 11) — Store contracts include replication requirements
+- **Four States** (doc 02) — all state stores are redundant per this model
+- **Ingestion Model** (doc 13) — DCM's own deployment recovery uses the repave/rehydration pattern
+
+
+## 8. Deployment Redundancy Gap Resolutions
+
+### 8.1 Bootstrap Manifest Verification (Q1)
+
+The bootstrap manifest is stored in the GitOps store, hash-verified at DCM startup and every restart. Tampering prevents DCM from starting.
+
+```yaml
+bootstrap_manifest:
+  version: "1.0.0"
+  manifest_uuid: 
+  manifest_hash:           # computed at creation; verified at every startup
+  signed_by:    # signed by deploying operator
+```
+
+DCM startup sequence: verify manifest hash → verify manifest signature → proceed with initialization. On failure: refuse to start; emit security alert; notify platform admin.
+
+### 8.2 Kubernetes Cluster Upgrades in Sovereign Deployments (Q2)
+
+Sovereign DCM (air-gapped) uses pre-staged container images and the maintenance mode pattern:
+
+```
+Pre-upgrade:
+  1. Pre-stage all DCM container images in local registry (signed bundles)
+  2. DCM enters maintenance mode — new requests queued; in-flight complete
+
+During upgrade:
+  3. Kubernetes upgrade proceeds using pre-staged images
+  4. DCM components restart against new cluster version
+
+Post-upgrade:
+  5. Startup verification (bootstrap manifest hash check)
+  6. DCM exits maintenance mode — queued requests resume
+```
+
+Same signed bundle model as registry updates — no new pattern needed.
+
+### 8.3 Non-Kubernetes Container Runtime Support (Q3)
+
+Kubernetes is DCM's primary and recommended container runtime. Non-Kubernetes runtimes (Podman, Docker Compose) are supported for development and community extension only. The DCM Operator is Kubernetes-native and not supported on other runtimes. Production deployments must use Kubernetes.
+
+### 8.4 Minimum Hardware Specifications (Q4)
+
+Expressed as DCM Resource definitions per profile — machine-readable and enforced by the placement engine during self-deployment.
+
+| Profile | CPU | Memory | Storage | Replicas |
+|---------|-----|--------|---------|---------|
+| minimal | 2 cores | 4 Gi | 20 Gi | 1 |
+| dev | 4 cores | 8 Gi | 50 Gi | 1 |
+| standard | 8 cores | 16 Gi | 100 Gi | 3 |
+| prod | 16 cores | 32 Gi | 200 Gi | 3 |
+| fsi | 32 cores | 64 Gi | 500 Gi | 5 |
+| sovereign | 32 cores | 64 Gi | 500 Gi | 5 |
+
+These are minimums. Production workloads may require significantly more based on managed resource count.
+
+### 8.5 DCM Self-Hosted Drift Detection (Q5)
+
+DCM's own deployment is a DCM-managed resource subject to the same drift detection as any other resource. The DCM Operator continuously reconciles running components against the declared deployment manifest.
+
+**The bootstrap paradox — who watches the watchman:**
+- DCM Operator drifts → bootstrap manifest hash verification (RED-011) detects independently
+- Audit component compromised → Audit Store hash chain break is detectable by external verification
+- Full DCM compromise → signed bundle verification at import time provides external trust anchor
+
+### 8.6 System Policies — Deployment Redundancy Gaps
+
+| Policy | Rule |
+|--------|------|
+| `RED-011` | The bootstrap manifest is version-controlled in the GitOps store, hash-verified at DCM startup and every restart. Bootstrap manifest tampering prevents DCM from starting and triggers a security alert. |
+| `RED-012` | Kubernetes cluster upgrades in Sovereign DCM deployments use pre-staged container images from the local signed bundle registry. DCM enters maintenance mode during upgrade. In-flight operations complete before upgrade. DCM exits maintenance mode on successful startup verification. |
+| `RED-013` | Kubernetes is DCM's primary and recommended container runtime. Non-Kubernetes runtimes are supported for development and community extension purposes only. Production deployments must use Kubernetes. |
+| `RED-014` | Minimum hardware specifications are expressed as DCM Resource definitions per profile and enforced by the placement engine during DCM self-deployment. |
+| `RED-015` | DCM's own deployment is a DCM-managed resource subject to the same drift detection as any other resource. Bootstrap manifest hash verification (RED-011) provides independent verification. Audit Store hash chain breaks are detectable externally. |
+
+
+
+---
+
+## 9. Bootstrap Tenant Creation Sequence
+
+### 9.1 The Bootstrap Problem
+
+DCM requires every entity to belong to exactly one Tenant. But during initial deployment, no Tenants exist. The bootstrap sequence resolves this by creating the foundational Tenants as part of DCM startup, declared in the bootstrap manifest.
+
+### 9.2 The Three Foundation Tenants
+
+The bootstrap manifest declares three system Tenants that are created before any consumer can submit requests:
+
+```yaml
+bootstrap_tenants:
+  - handle: "__platform__"
+    display_name: "DCM Platform"
+    purpose: "Owns DCM's own control plane resources (components, stores, providers)"
+    automatically_created: true
+    cannot_be_decommissioned: true
+
+  - handle: "__transitional__"
+    display_name: "Transitional"
+    purpose: "Holds brownfield entities during ingestion before promotion to a real Tenant"
+    automatically_created: true
+    cannot_be_decommissioned: true
+
+  - handle: "__system__"
+    display_name: "System"
+    purpose: "Owns system-level artifacts (system layers, system policies, system workflows)"
+    automatically_created: true
+    cannot_be_decommissioned: true
+```
+
+### 9.3 Bootstrap Startup Sequence
+
+```
+DCM starts
+  │
+  ▼ Step 1: Verify bootstrap manifest hash and signature
+  │
+  ▼ Step 2: Initialize storage providers
+  │   GitOps stores initialized
+  │   Audit Store initialized
+  │   Commit Log initialized
+  │
+  ▼ Step 3: Create foundation Tenants (if not already existing)
+  │   __platform__, __transitional__, __system__
+  │
+  ▼ Step 4: Create initial Platform Admin actor
+  │   Declared in bootstrap manifest
+  │   Assigned to __platform__ Tenant
+  │   Given platform_admin role
+  │
+  ▼ Step 5: Activate system domain layers and policies
+  │   System layers loaded from GitOps store
+  │   System policies activated
+  │   Built-in recovery profiles activated
+  │
+  ▼ Step 6: Register built-in providers
+  │   Built-in Auth Provider
+  │   Search Index data store
+  │   Audit Store data store
+  │   (All owned by __platform__ Tenant)
+  │
+  ▼ Step 7: DCM ready
+      Consumer API, Provider API, Admin API accepting requests
+      Platform Admin can now create organization Tenants
+      Organization Tenants can request resources
+```
+
+### 9.4 System Policy
+
+| Policy | Rule |
+|--------|------|
+| `RED-016` | The three foundation Tenants (__platform__, __transitional__, __system__) are created during bootstrap and cannot be decommissioned. All DCM control plane resources are owned by __platform__. All brownfield ingested entities enter __transitional__ before promotion. |
+
+
+
+---
+
+## 9. Bootstrap Sequence and Initial Tenant Creation
+
+### 9.1 The Bootstrap Problem
+
+The DCM data model requires every entity to be owned by a Tenant. But Tenants are themselves DCM entities. The bootstrap sequence defines how the initial Tenants and platform admin actor are created before DCM can accept external requests.
+
+### 9.2 Bootstrap Manifest
+
+The bootstrap manifest (RED-011) declares the initial state required for DCM to start. It includes:
+
+```yaml
+bootstrap_manifest:
+  version: "1.0.0"
+  signed_by: 
+
+  # Initial system Tenants (created before any external requests)
+  system_tenants:
+    - uuid: 
+      handle: "__platform__"
+      display_name: "DCM Platform"
+      description: "System Tenant owning DCM's own control plane resources"
+      immutable: true             # cannot be decommissioned or modified by regular operators
+
+    - uuid: 
+      handle: "__transitional__"
+      display_name: "Transitional"
+      description: "Holding Tenant for brownfield ingestion (INGEST phase)"
+      immutable: true
+
+  # Initial platform admin actor
+  bootstrap_admin:
+    uuid: 
+    username: "dcm-bootstrap-admin"
+    auth_provider: builtin
+    roles: [platform_admin]
+    credential_ref: 
+    # This credential is rotated on first login
+
+  # Active profile for initial deployment
+  initial_profile:
+    deployment_posture: minimal    # or as declared; can be changed post-bootstrap
+    compliance_domains: []
+
+  # Bootstrap admin's initial Tenant
+  initial_tenant:
+    uuid: 
+    handle: "org-default"
+    display_name: "Default Organization Tenant"
+    owned_by: bootstrap_admin
+```
+
+### 9.3 Bootstrap Sequence
+
+```
+DCM starts → bootstrap manifest hash verified (RED-011)
+  │
+  ▼ System Tenants created (before Policy Engine active):
+  │   __platform__ Tenant — owns DCM control plane resources
+  │   __transitional__ Tenant — brownfield ingestion holding
+  │   These are created by the bootstrap process itself, not through the request pipeline
+  │
+  ▼ Bootstrap admin actor created
+  │   Auth Provider initialized with bootstrap credential
+  │   Platform Admin role assigned
+  │
+  ▼ Initial profile activated
+  │   Deployment posture policies loaded
+  │   Compliance domain policies loaded (if declared)
+  │
+  ▼ Policy Engine comes online
+  │   All subsequent operations go through the standard request pipeline
+  │
+  ▼ Bootstrap admin creates the initial organization Tenant (optional)
+  │   First real request through the pipeline
+  │   Creates the initial production Tenant for organizational resources
+  │
+  ▼ Bootstrap admin credential rotation notification sent
+  │   Bootstrap credential must be rotated on first login
+  │   After rotation, bootstrap_admin becomes a standard platform admin actor
+  │
+  ▼ DCM accepts external requests
+```
+
+### 9.4 System Tenants
+
+The `__platform__` and `__transitional__` Tenants are created by the bootstrap process and are immutable:
+
+| System Tenant | Purpose | Who can modify |
+|--------------|---------|---------------|
+| `__platform__` | Owns DCM's own control plane resources | Platform Admin (restricted operations only) |
+| `__transitional__` | Brownfield ingestion holding area | Ingestion pipeline only |
+
+These Tenants are exempt from the normal Tenant decommission workflow — they cannot be decommissioned while DCM is operational.
+
+### 9.5 System Policy
+
+| Policy | Rule |
+|--------|------|
+| `BOOT-001` | The __platform__ and __transitional__ system Tenants are created by the bootstrap process before the Policy Engine comes online. They are immutable and cannot be decommissioned while DCM is running. |
+| `BOOT-002` | The bootstrap admin credential must be rotated on first login. The bootstrap manifest declares the initial credential reference only; the credential itself is managed by the credential management service. |
+| `BOOT-003` | After bootstrap, all Tenant creation and modification goes through the standard request pipeline. The bootstrap process is a one-time operation. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/18-webhooks-messaging.md b/content/docs/architecture/data-model/18-webhooks-messaging.md
new file mode 100644
index 0000000..26732e5
--- /dev/null
+++ b/content/docs/architecture/data-model/18-webhooks-messaging.md
@@ -0,0 +1,825 @@
+# DCM Data Model — Webhooks, Messaging, and External Integration
+
+
+**Document Status:** ✅ Complete
+**Related Documents (updated):** [Notification Model](23-notification-model.md) | [Entity Relationships](09-entity-relationships.md)  
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: PROVIDER**
+>
+> The Provider abstraction — Message Bus and webhook delivery channels
+
+
+**Related Documents:** [Universal Audit Model](16-universal-audit.md) | [Deployment and Redundancy](17-deployment-redundancy.md) | [Authentication and Authorization](19-auth-providers.md) | [Policy Organization](14-policy-profiles.md)
+
+---
+
+
+> **See [Event Catalog](33-event-catalog.md)** — authoritative source for all DCM event types and payload schemas.
+
+## 1. Purpose
+
+DCM communicates with the outside world through three complementary mechanisms:
+
+- **Outbound Webhooks** — DCM pushes event notifications to external HTTP endpoints
+- **Inbound Webhooks** — External systems push requests, queries, and events to DCM HTTP endpoints
+- **event routing services** — DCM integrates with external message buses for persistent, high-throughput bidirectional event streaming
+
+All three mechanisms are authenticated, authorized, and audited identically to any other DCM API call. There is no privileged back-channel. Every integration is a registered DCM actor subject to full Policy Engine evaluation.
+
+**Webhooks are optional and policy-governed.** The active Profile sets defaults — `fsi` and `sovereign` profiles may require webhook or message bus coverage for audit events. `minimal` and `dev` profiles make them fully optional.
+
+---
+
+## 2. Ingress and Egress — The Universal Actor Model
+
+### 2.1 The `ingress` Block
+
+Every request entering DCM carries an immutable `ingress` block set by the DCM ingress layer before Policy Engine evaluation. It is never consumer-declarable and never modifiable by policies — policies may only read it.
+
+```yaml
+ingress:
+  # HOW it arrived
+  surface: 
+  protocol: 
+  authenticated_via: 
+  authorized_via: policy_engine
+
+  # WHO sent it — fully resolved actor context
+  actor:
+    uuid: 
+    type: 
+    display_name: "Jane Smith"
+    identity_source: 
+    auth_provider_uuid:           # which Auth Provider authenticated
+    auth_provider_type: 
+
+    # Resolved DCM roles and scope (at authentication time)
+    roles: [sre]
+    tenant_scope: []
+    groups: []
+    permissions: [request.submit, query.entity_state]
+
+    # Authorization chain
+    authorized_by:
+      method: 
+      authorizing_entity_uuid: 
+      authorization_timestamp: 
+      expiry: 
+
+    # Session context (human actors)
+    session_uuid: 
+    session_started_at: 
+    mfa_verified: 
+
+    # External identity (federated actors)
+    external_identity:
+      provider: 
+      subject: "uid=jsmith,cn=users,cn=accounts,dc=corp,dc=example,dc=com"
+      claims:
+        email: jsmith@corp.example.com
+        display_name: "Jane Smith"
+        department: Engineering
+        cost_center: CC-1234
+        ldap_groups: [cn=dcm-sre,...]
+        sid: "S-1-5-21-..."        # AD Security Identifier
+
+    # Rate limit tracking
+    rate_limit_bucket: 
+
+  # Surface-specific detail
+  webhook_registration_uuid:    # if surface: webhook_inbound
+  (optional infrastructure)_uuid:    # if surface: message_bus_inbound
+  message_offset:             # if surface: message_bus_inbound
+  scheduler_job_uuid:           # if surface: scheduler
+  parent_request_uuid:          # if surface: policy_engine|rehydration
+  source_ip: 
+```
+
+### 2.2 The `egress` Block
+
+All outbound calls from DCM carry DCM's authenticated identity:
+
+```yaml
+egress:
+  surface: 
+  protocol: 
+  actor:
+    uuid: 
+    type: dcm_internal
+    component: 
+    authenticated_via: 
+    credential_ref:
+      service_provider_uuid: 
+      secret_path: 
+  originating_request_uuid: 
+  originating_actor_uuid: 
+```
+
+### 2.3 Policy Engine Use Cases — Ingress/Egress Fields
+
+The ingress block enables a rich class of governance rules:
+
+```yaml
+# Require specific auth for sensitive operations
+policy: "If action == decommission AND ingress.actor.mfa_verified == false THEN gatekeep"
+
+# Block legacy API keys from production Tenants
+policy: "If tenant.profile == prod AND ingress.actor.identity_source == static_api_key THEN gatekeep"
+
+# Require enterprise auth for security resources
+policy: "If resource_type IN [Network.FirewallRule] AND ingress.actor.auth_provider_type NOT IN [oidc, freeipa, active_directory] THEN gatekeep"
+
+# Enrich from OIDC claims
+policy: "If ingress.actor.external_identity.claims.department EXISTS THEN inject: business_context.department"
+
+# Block message bus inbound from non-service-accounts
+policy: "If ingress.surface == message_bus_inbound AND ingress.actor.type != webhook_service_account THEN gatekeep"
+
+# Sovereignty check on inbound message bus
+policy: "If ingress.surface == message_bus_inbound AND (optional infrastructure).jurisdiction != tenant.sovereignty_zone THEN gatekeep"
+```
+
+
+### 2.5 Ingress API vs Consumer API — Relationship Clarification
+
+These two terms refer to different architectural layers:
+
+**Ingress API (infrastructure layer):**
+The network-level entry point for all inbound requests to the DCM control plane. It handles:
+- TLS termination
+- Authentication token validation
+- Setting the immutable `ingress` block on every request (surface, actor, timestamp, mfa_verified)
+- Rate limiting at the network level
+- Routing to the appropriate internal component (Consumer API handlers, Provider API handlers, Admin API handlers)
+
+The Ingress API is infrastructure — it is not directly defined in any consumer-facing specification.
+
+**Consumer API (application layer):**
+The logical REST API surface that consumers interact with, as defined in the [Consumer API Specification](../specifications/consumer-api-spec.md). The Consumer API is *served through* the Ingress API. When a consumer calls `POST /api/v1/requests`, that request enters through the Ingress API (which sets the ingress block) and is then handled by the Consumer API component.
+
+**Other APIs served through the Ingress API:**
+- **Provider API** — the callback and notification endpoints that Service Providers call (`/api/v1/provider/...`)
+- **Admin API** — platform administration operations (`/api/v1/admin/...`)
+- **Webhook Inbound** — external systems calling DCM (`/api/v1/webhooks/...`)
+
+**The Ingress API is one, the Consumer API is one of several logical surfaces routed through it.**
+
+---
+
+### 2.6 Consumer Rate Limiting and Quota Model
+
+Consumer-side rate limiting and resource quotas are enforced by GateKeeper policies — not hardcoded limits. This keeps quota enforcement consistent with DCM's policy-driven model.
+
+**Request rate limiting (per actor):**
+
+```yaml
+rate_limit_policy:
+  type: gatekeeper
+  handle: "system/quotas/api-rate-limit"
+  trigger: request.initiated
+  conditions:
+    - field: ingress.actor_uuid
+      rate_window: PT1M
+      max_requests: 60           # configurable per Tenant policy
+  action: reject
+  rejection_code: 429
+  rejection_message: "Rate limit exceeded. Retry after PT1M."
+```
+
+**Resource quotas (per Tenant per resource type):**
+
+```yaml
+quota_policy:
+  type: gatekeeper
+  handle: "tenant/payments/vm-quota"
+  trigger: request.initiated
+  conditions:
+    - field: request.resource_type
+      equals: Compute.VirtualMachine
+    - field: tenant.active_entity_count
+      resource_type: Compute.VirtualMachine
+      operator: gte
+      value: 100                 # max 100 concurrent VMs for this Tenant
+  action: reject
+  rejection_message: "VM quota exceeded (100). Request a quota increase via the Admin API."
+```
+
+**Quota increase process:** Tenants request quota increases through the standard request process. A quota change request produces a Requested State record, goes through policy evaluation, and requires Platform Admin approval for significant increases.
+
+**Profile-governed defaults:**
+
+| Profile | Default API rate limit | Default resource quota |
+|---------|----------------------|----------------------|
+| minimal | 10 req/min | Unlimited |
+| dev | 60 req/min | Unlimited |
+| standard | 60 req/min | Policy-declared |
+| prod | 120 req/min | Policy-declared |
+| fsi | 60 req/min | Strict policy-declared |
+| sovereign | 30 req/min | Strict policy-declared |
+
+
+### 2.4 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `ING-008` | All DCM requests must carry an ingress block. The ingress block is set by the DCM ingress layer and is immutable — policies may read but not modify it. |
+| `ING-009` | The ingress block must include a fully resolved actor context: uuid, type, identity_source, roles, tenant_scope, auth_provider_uuid, and authorized_by chain. |
+| `ING-010` | All egress calls from DCM must carry DCM's authenticated identity. Unauthenticated egress is rejected. |
+| `ING-011` | Authentication enforcement is profile-governed. Standard and above reject unauthenticated requests. Minimal and dev support lightweight authenticated modes. There is no anonymous access in any profile. |
+| `ING-012` | Webhook and message bus inbound surfaces always require authentication regardless of active Profile. |
+| `ING-013` | Rate limiting is enforced per registered actor. Exceeding rate limits returns 429 Too Many Requests. |
+
+---
+
+## 3. Outbound Webhooks
+
+> **⚠️ Architecture Update — Notification Model Supersedes Outbound Webhooks**
+>
+> The outbound webhook model described in Section 3 has been one delivery channel within the Notification Model. Outbound webhooks are now one delivery channel type within the notification service model rather than a parallel mechanism.
+>
+> **For new implementations:** Use the notification service subscription model (doc 23, Section 6) with a webhook-type notification service.
+>
+>
+> The key improvement in the new model: audience is derived from the **entity relationship graph**, not from a manually maintained subscriber list. A webhook subscription for VLAN drift events will now automatically include all VMs attached to that VLAN as audience context.
+
+### 3.1 Concept
+
+DCM pushes event notifications to registered external HTTP endpoints. Outbound webhooks are **optional and policy-governed** — the active Profile and Policy Groups determine which events require external notification.
+
+### 3.2 Webhook Registration
+
+```yaml
+webhook_registration:
+  artifact_metadata:
+    uuid: 
+    handle: "org/webhooks/payments-drift-alerts"
+    version: "1.0.0"
+    status: active
+    owned_by:
+      display_name: "Payments Platform Team"
+      notification_endpoint: 
+
+  name: "Payments Platform Drift Alerts"
+  description: "Notifies payments team of drift detection events"
+
+  # SCOPE
+  scope:
+    type: 
+    tenant_uuid: 
+    cross_tenant_authorization_uuid:    # if cross_tenant
+
+  # EVENT SUBSCRIPTIONS
+  event_subscriptions:
+    - event_type: drift.detected
+      schema_version: "1.0"
+      adapter: true              # DCM transforms newer schemas to 1.0
+      filter:
+        tenant_uuid: 
+        resource_types: [Compute.VirtualMachine, Storage.Block]
+    - event_type: request.realized
+      schema_version: "1.0"
+    - event_type: entity.state_transition
+      schema_version: "1.0"
+      filter:
+        to_states: [DEGRADED, FAILED]
+
+  # ENDPOINT
+  endpoint:
+    url: https://alerts.payments.corp.example.com/dcm/events
+    sovereignty_check: true     # verify endpoint jurisdiction before delivery
+
+  # AUTHENTICATION
+  authentication:
+    mode: 
+    secret_ref:
+      service_provider_uuid: 
+      secret_path: "dcm/webhooks/payments-drift/hmac-secret"
+    rotation_policy:
+      automatic: true
+      interval: P90D
+      transition_window: P7D
+      notify_before: P14D
+
+  # RELIABILITY
+  retry_policy:
+    max_attempts: 7
+    backoff: exponential
+    initial_interval: PT5S
+    max_interval: PT1H
+    timeout_per_attempt: PT10S
+    on_exhaustion: dead_letter   # dead_letter | discard | escalate
+
+  # HEALTH
+  health:
+    failure_threshold: 10
+    suspension_notification: true
+    auto_deactivate_after: P30D
+    status: active
+
+  # SCHEMA COMPATIBILITY
+  schema_adapter:
+    enabled: true
+    # DCM maintains forward-compatibility adapters per schema version
+    # Consumer stays on declared schema_version indefinitely
+    deprecation_notice_days: 90
+```
+
+### 3.3 Event Taxonomy
+
+> The table below is the event taxonomy for webhook subscriptions.
+
+The event taxonomy maps onto the Universal Audit action vocabulary. All are versioned registry entries:
+
+| Category | Events |
+|----------|--------|
+| Entity lifecycle | `entity.created`, `entity.modified`, `entity.state_transition`, `entity.deleted`, `entity.expired`, `entity.rehydrated` |
+| Group | `group.member_added`, `group.member_removed`, `group.created`, `group.deleted` |
+| Relationship | `relationship.created`, `relationship.released` |
+| Policy | `policy.activated`, `policy.deactivated`, `policy.evaluated` (fail/gatekeep only), `policy.shadow_result` |
+| Provider | `provider.healthy`, `provider.degraded`, `provider.unhealthy`, `provider.registered`, `provider.deregistered` |
+| Audit/security | `audit.chain_break`, `audit.forward_failed` |
+| Drift | `drift.detected`, `drift.resolved`, `drift.escalated` |
+| Request | `request.submitted`, `request.approved`, `request.rejected`, `request.realized`, `request.failed` |
+| Rehydration | `rehydration.started`, `rehydration.completed`, `rehydration.paused`, `rehydration.interrupted` |
+| Authorization | `authorization.granted`, `authorization.revoked` |
+| Webhook | `webhook.secret_rotated`, `webhook.suspended`, `webhook.schema_deprecated` |
+
+### 3.4 Payload Format
+
+```yaml
+webhook_payload:
+  # Envelope
+  event_uuid:               # idempotency key
+  event_type: drift.detected
+  event_schema_version: "1.0"
+  timestamp:            # from Stage 1 Commit Log — authoritative
+  dcm_version: 
+
+  # Subject
+  subject:
+    entity_uuid: 
+    entity_type: infrastructure_resource
+    entity_handle: 
+    tenant_uuid: 
+
+  # Delta
+  delta:
+    drifted_fields:
+      - field: cpu_count
+        realized_value: 4
+        discovered_value: 8
+    drift_severity: significant
+
+  # Links
+  links:
+    self: 
+    audit_record: 
+```
+
+### 3.5 Delivery Guarantees
+
+- **At-least-once** — not exactly-once; consumers must be idempotent using `event_uuid`
+- **Per-entity ordering** — events for a given `entity_uuid` delivered in Commit Log sequence order
+- **Cross-entity ordering** — not guaranteed; use `timestamp` for actual occurrence time
+- **Sovereignty-aware** — delivery blocked if endpoint jurisdiction incompatible with Tenant sovereignty (WHK-004)
+
+---
+
+## 4. Inbound Webhooks
+
+### 4.1 Concept
+
+DCM exposes authenticated HTTP endpoints that external systems call to submit requests, queries, and events. Inbound webhooks are subject to full Policy Engine evaluation — identical to any other API call.
+
+### 4.2 Inbound Endpoints
+
+| Endpoint | Purpose |
+|----------|---------|
+| `POST /webhooks/inbound/request` | Submit a service request |
+| `POST /webhooks/inbound/query` | Query entity state or catalog |
+| `POST /webhooks/inbound/event` | Push an event (provider state change, CI/CD signal) |
+| `POST /webhooks/inbound/ingestion` | Push brownfield ingestion data |
+| `POST /webhooks/inbound/data` | Push enrichment or information data |
+
+### 4.3 Webhook Actor Registration
+
+Every inbound webhook caller must be registered as a **webhook actor** — a service account in the DCM identity model:
+
+```yaml
+webhook_actor:
+  artifact_metadata:
+    uuid: 
+    handle: "actors/webhook/cicd-pipeline-prod"
+    status: active
+
+  name: "CI/CD Pipeline Production"
+  actor_type: webhook_service_account
+
+  # Authentication
+  authentication:
+    mode: hmac_sha256
+    secret_ref:
+      service_provider_uuid: 
+      secret_path: "dcm/webhooks/inbound/cicd-pipeline/hmac"
+
+  # Authorization
+  role: consumer
+  tenant_scope: []
+  permitted_operations:
+    - request.submit
+    - query.entity_state
+    - query.catalog
+
+  # Rate limiting
+  rate_limit:
+    requests_per_minute: 60
+    burst: 10
+
+  # Audit identity
+  audit_identity:
+    display_name: "CI/CD Pipeline (Production)"
+    system: "jenkins-prod-01"
+```
+
+### 4.4 Response Model
+
+- **Queries** — synchronous response with result
+- **Requests and events** — `202 Accepted` + `request_uuid`; caller polls status or registers outbound webhook for completion notification
+
+---
+
+## 5. event routing service
+
+### 5.1 Concept
+
+A **event routing service** is the sixth DCM provider type — a persistent, high-throughput integration with an external message bus for bidirectional event streaming. Where webhooks are point-to-point HTTP calls, a event routing service is a durable pub/sub connection.
+
+### 5.2 Registration
+
+```yaml
+(optional infrastructure)_registration:
+  artifact_metadata:
+    uuid: 
+    handle: "providers/messagebus/corporate-kafka"
+    version: "1.0.0"
+    status: active
+
+  name: "Corporate Kafka Cluster"
+  provider_type: message_bus
+
+  # Direction
+  direction: 
+
+  # Protocol
+  protocol: 
+
+  # Connection
+  connection:
+    brokers: [kafka-1.corp:9093, kafka-2.corp:9093, kafka-3.corp:9093]
+    credentials_ref:
+      service_provider_uuid: 
+      secret_path: "dcm/providers/messagebus/corporate-kafka/credentials"
+    tls:
+      mode: mtls
+      ca_cert_ref:
+        service_provider_uuid: 
+        secret_path: "dcm/providers/messagebus/corporate-kafka/ca-cert"
+
+  # Outbound — DCM publishes to external bus
+  outbound:
+    topic_mapping:
+      entity.state_transition: "dcm.entities.state"
+      drift.detected: "dcm.drift.alerts"
+      request.realized: "dcm.requests.completed"
+      audit.chain_break: "dcm.security.alerts"
+    schema_version: "1.0"
+    delivery_guarantee: at_least_once
+
+  # Inbound — DCM consumes from external bus
+  inbound:
+    consumer_group: "dcm-inbound-prod"
+    topic_mapping:
+      "cicd.deployment.completed": request.submit
+      "cmdb.discovery.update": ingestion.push
+      "itsm.change.approved": request.approve
+    # Inbound messages processed as authenticated API calls
+    actor_identity_uuid: 
+    # Same Policy Engine evaluation as inbound webhooks
+
+  # Sovereignty
+  operational_sovereignty:
+    jurisdiction: eu-west
+    certifications: [ISO-27001, GDPR-compliant]
+
+  # Health
+  health_check:
+    interval_seconds: 30
+    on_unhealthy: alert
+```
+
+### 5.3 Architecture
+
+```
+DCM internal Message Bus (internal pub/sub backbone)
+  │
+  ├── Webhook Delivery Service ──────→ External HTTP endpoints (outbound webhooks)
+  │
+  └── Message Bus Bridge Service ────→ External message bus (event routing service)
+                                 ←─── External message bus (inbound)
+```
+
+The internal Message Bus is never exposed directly. All external event integration goes through either the Webhook Delivery Service or the Message Bus Bridge Service — both of which handle authentication, authorization, sovereignty checks, and schema transformation.
+
+---
+
+## 6. Git PR Ingress — Distributed Git Request Mechanism
+
+### 6.1 Concept
+
+DCM supports **git_pr_merge** as a twelfth ingress surface — enabling teams to submit DCM resource definitions as Pull Requests to a DCM-watched Git repository. This is the native workflow for infrastructure-as-code teams: open a PR, get it reviewed by humans and DCM's policy engine simultaneously, merge to execute.
+
+**Why Git PR ingress matters:**
+- GitOps teams work in Git — their deployment workflow is already PR-based
+- Security and compliance teams review infrastructure changes the same way they review code
+- The PR itself is the human review record; DCM's audit trail captures the automated processing
+- Rollback is a Git revert — natural and familiar
+- Multi-team approval workflows use existing Git branch protection rules
+- The PR diff shows exactly what changes — field-level visibility
+
+### 6.2 The Git Request Watcher
+
+A dedicated control plane component — the **Git Request Watcher** — monitors designated repositories via webhooks (preferred) or polling. It is policy-governed: which repositories it watches, which branches trigger processing, and which resource types may be submitted via Git PR.
+
+### 6.3 Request Repository Structure
+
+```
+dcm-requests/                              ← DCM-watched request repository
+  {tenant-uuid}/
+    pending/
+      {resource-handle}/
+        request.yaml                       ← Standard DCM resource definition
+    realized/
+      {resource-handle}/
+        realized.yaml                      ← DCM writes realized state here on success
+    failed/
+      {resource-handle}/
+        request.yaml                       ← Moved here on failure with error detail
+```
+
+### 6.4 Git Actor Identity Resolution
+
+**Authentication is always required.** Git PR ingress actors must be resolved to DCM actors through the registered Auth Provider — DCM trusts the Git server's authentication assertion, not user-declared Git configuration. Anyone can set their local `git config user.email` to anything; DCM ignores self-declared identity.
+
+**The trust chain:**
+```
+Git server authenticates user (SSH key, OAuth token, password)
+  │  Git server's authentication is trusted — not user's claimed identity
+  ▼
+DCM Git Request Watcher receives PR merge webhook
+  │  Webhook payload contains: actor.login, actor.auth_method, actor.external_id
+  │  All verified by the Git server
+  ▼
+Auth Provider resolution (same path as web UI login for the same user):
+  ├── OIDC/OAuth: Git server OAuth subject → OIDC Auth Provider userinfo lookup
+  ├── LDAP/AD: Git server username → LDAP lookup → DCM actor
+  ├── SSH key: Git server key fingerprint → DCM SSH key registry → DCM actor
+  └── Service account: Git service account → registered webhook actor
+  ▼
+Fully resolved DCM actor — same roles, groups, tenant scope as any other user
+```
+
+**Resolution methods:**
+
+```yaml
+git_actor_resolution:
+  # Method 1: OIDC/OAuth (recommended — Git server uses same IdP as DCM)
+  method: oidc_subject_lookup
+  auth_provider_uuid: 
+
+  # Method 2: LDAP/AD (enterprise — Git server authenticates via corporate directory)
+  method: ldap_username_lookup
+  auth_provider_uuid: 
+
+  # Method 3: SSH key fingerprint
+  method: ssh_key_fingerprint
+  # Keys registered in DCM SSH key registry, linked to actor UUIDs
+
+  # Method 4: Service account (automated workflows)
+  method: webhook_service_account
+  # Git service account mapped to registered webhook actor
+```
+
+**Identity resolution failure — explicit rejection:**
+
+When DCM cannot map the merge actor to a DCM actor, the PR is rejected with an actionable comment. Never silently ignored.
+
+```
+❌ DCM Identity Resolution Failed
+
+DCM could not map the merge actor "jsmith" to a DCM actor identity.
+
+Possible causes:
+  • Your Git account is not linked to a DCM actor via the corporate Auth Provider
+  • Your DCM actor account has been suspended or deactivated
+
+To resolve:
+  • Contact your platform administrator: https://dcm.corp.example.com/actors/git-identity-setup
+
+This PR will not be processed until identity resolution succeeds.
+```
+
+### 6.5 The ingress Block for Git PR
+
+```yaml
+ingress:
+  surface: git_pr_merge               # or: git_pr_open (for shadow validation)
+  protocol: https
+  authenticated_via: oidc             # or: ldap_direct_bind, active_directory, ssh_key
+  actor:
+    uuid: 
+    type: human
+    display_name: "Jane Smith"
+    identity_source: oidc
+    auth_provider_uuid: 
+    roles: [consumer]
+    tenant_scope: []
+    groups: []
+    # Groups and tenant scope: SAME mappings as web UI login for this user
+    external_identity:
+      provider: github                # or: gitlab, gitea, freeipa, active_directory
+      subject:         # verified by Git server
+      git_username: jsmith
+      git_verified_email: jsmith@corp.com  # from Git server record — not git config
+    mfa_verified: true                # from Auth Provider session record
+  git_context:
+    repository: https://git.corp.example.com/dcm-requests/payments
+    pr_number: 142
+    pr_url: https://git.corp.example.com/payments/pulls/142
+    merge_commit: 
+    base_branch: main
+    pr_author: jsmith
+    pr_reviewers: [platform-team, security-team]
+    pr_approved_by: [, ]
+    # Approved by: DCM resolves Git reviewer identities via same Auth Provider
+```
+
+### 6.6 PR Lifecycle — DCM Processing Flow
+
+```
+1. Developer creates resource definition YAML (standard DCM request format)
+   │
+2. Opens PR against dcm-requests/{tenant-uuid}/pending/
+   │
+3. DCM Git Watcher detects PR (git_pr_open event)
+   │
+4. DCM resolves PR author → DCM actor via Auth Provider
+   │  Failure → post rejection comment; stop processing
+   │
+5. DCM validates actor tenant scope against target Tenant
+   │  Failure → post rejection comment; stop processing
+   │
+6. Shadow policy evaluation (same nine-step assembly — dry run)
+   │  Results posted as PR review comments:
+   │  "✅ Schema valid"
+   │  "✅ All policies pass"
+   │  "⚠️  Will be placed in eu-west-1 per sovereignty policy"
+   │  "❌ GateKeeper: VM size exceeds quota — reduce cpu_count"
+   │
+7. Human review and approval (standard Git PR workflow)
+   │  Branch protection enforces required reviewers
+   │  Policy may declare: require DCM-defined approvers in git_context.pr_approved_by
+   │
+8. PR merged to main (git_pr_merge event)
+   │  ingress_surface: git_pr_merge
+   │  Actor re-verified at merge time (not assumed from PR open time)
+   │
+9. DCM processes as standard nine-step assembly (real — not shadow)
+   │
+10. DCM posts realization result as PR comment + status check
+    │  "✅ VM payments-prod-01 realized — UUID: "
+    │  "❌ Realization failed — audit record: "
+    │
+11. DCM commits realized state to realized/ directory (optional)
+    Git history = full lifecycle record
+```
+
+### 6.7 Policy Engine Use Cases — Git PR Ingress
+
+```yaml
+# Require PR approval before processing production resources
+policy:
+  type: gatekeeper
+  rule: >
+    If ingress.surface == git_pr_merge
+    AND tenant.profile IN [prod, fsi, sovereign]
+    AND ingress.git_context.pr_approved_by NOT CONTAINS required_approvers
+    THEN gatekeep: "PR requires approval from platform admin and security owner"
+
+# Require MFA for Git PR merges in production Tenants
+policy:
+  type: gatekeeper
+  rule: >
+    If ingress.surface == git_pr_merge
+    AND tenant.profile IN [prod, fsi, sovereign]
+    AND ingress.actor.mfa_verified == false
+    THEN gatekeep: "MFA required for Git PR merges in production Tenants"
+
+# Restrict resource types submittable via Git PR
+policy:
+  type: gatekeeper
+  rule: >
+    If ingress.surface == git_pr_merge
+    AND resource_type NOT IN [Compute.VirtualMachine, Storage.Block]
+    THEN gatekeep: "Only compute and storage resources may be submitted via Git PR"
+
+# Require actor to be in authorized Git team for target Tenant
+policy:
+  type: gatekeeper
+  rule: >
+    If ingress.surface == git_pr_merge
+    AND ingress.actor.groups NOT CONTAINS tenant.authorized_git_groups
+    THEN gatekeep: "Git PR author is not in an authorized group for this Tenant"
+
+# Post shadow results as PR comments (transformation)
+policy:
+  type: transformation
+  placement_phase: pre
+  rule: >
+    If ingress.surface IN [git_pr_merge, git_pr_open]
+    THEN inject: git_feedback.post_as_pr_comment = true
+```
+
+### 6.8 Updated Ingress Surface Taxonomy
+
+```yaml
+ingress_surface_taxonomy:
+  - web_ui                 # DCM's own web interface
+  - consumer_api           # Direct REST API call
+  - webhook_inbound        # Inbound webhook call
+  - message_bus_inbound    # Inbound message bus
+  - provider_callback      # Provider reporting back
+  - policy_engine          # Policy-generated sub-request
+  - scheduler              # Scheduled/timed trigger
+  - rehydration            # Rehydration-generated
+  - ingestion              # Ingestion pipeline
+  - dcm_internal           # DCM system-generated
+  - operator_cli           # Command line interface
+  - git_pr_merge           # Git PR merge → execute  ← new
+  - git_pr_open            # Git PR open → shadow validation only  ← new
+```
+
+### 6.9 System Policies — Git PR Ingress
+
+| Policy | Rule |
+|--------|------|
+| `GIT-001` | DCM supports git_pr_merge and git_pr_open as ingress surfaces. Git PR ingress is subject to full Policy Engine evaluation identical to API ingress. |
+| `GIT-002` | DCM trusts the Git server's authentication assertion — not user-declared Git configuration. DCM resolves the Git server's verified identity through the registered Auth Provider to produce a fully-resolved DCM actor with the same role, group, and tenant scope mappings as any other user authenticated via the same Auth Provider. |
+| `GIT-003` | Unresolvable Git actor identities are rejected with an actionable PR comment. PRs are never silently ignored. |
+| `GIT-004` | The resolved Git PR actor must have the target Tenant UUID in their tenant_scope. PRs targeting Tenants outside the actor's scope are rejected — same enforcement as API tenant scope checks. |
+| `GIT-005` | DCM posts shadow policy evaluation results as PR review comments on git_pr_open. GateKeeper failures should be surfaced via repository branch protection integration. |
+| `GIT-006` | PR approval status may be declared as an authorization requirement by policy. DCM checks declared reviewer approvals against the PR's actual approval record before processing a merged PR. |
+| `GIT-007` | The Git Request Watcher component is policy-governed: which repositories it monitors, which branches trigger processing, and which resource types may be submitted via Git PR are declared via Policy Group. |
+| `GIT-008` | Actor identity is re-verified at merge time — not assumed from PR open time. A user whose DCM actor is suspended between PR open and merge will be rejected at merge. |
+
+---
+
+
+
+| Policy | Rule |
+|--------|------|
+| `WHK-001` | Events for a given entity_uuid are delivered in Commit Log sequence order. Cross-entity ordering is not guaranteed. |
+| `WHK-002` | Webhook delivery uses at-least-once semantics. Consumers must be idempotent using event_uuid as the deduplication key. |
+| `WHK-003` | Outbound webhook authentication must be declared at registration. Supported: hmac_sha256 (default), mtls, bearer_token. Unauthenticated webhooks are rejected. |
+| `WHK-004` | Webhook endpoints that would deliver data outside a Tenant's sovereignty boundary are subject to sovereignty checks. |
+| `WHK-005` | Webhook governance is policy-driven. Profiles set defaults — fsi/sovereign profiles may require webhook coverage for audit events via Policy Group. |
+| `WHK-006` | Webhook registrations must declare the event schema version expected. DCM supports current and N-1 schema versions simultaneously. |
+| `WHK-007` | Platform-scoped webhooks require Platform Admin role and are audit-logged as CONFIG_CHANGE. |
+| `WHK-008` | Cross-tenant webhooks require a valid cross_tenant_authorization record (XTA-001). |
+| `WHK-009` | Inbound webhook callers must be registered as webhook actors with explicit role, tenant scope, and permitted operations. Unregistered callers are rejected with 401. |
+| `WHK-010` | Inbound webhook calls are subject to full Policy Engine evaluation — identical to any other API call. No bypass. |
+| `WHK-011` | All inbound webhook calls are recorded in the audit trail with the webhook actor as the immediate actor. |
+| `WHK-012` | Inbound webhook endpoints return 202 Accepted + request_uuid for async operations. |
+| `WHK-013` | Rate limiting is enforced per registered webhook actor. Exceeding rate limits returns 429 Too Many Requests. |
+| `WHK-014` | All credential references in webhook and message bus configurations must resolve through a registered credential management service. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/19-auth-providers.md b/content/docs/architecture/data-model/19-auth-providers.md
new file mode 100644
index 0000000..02238f3
--- /dev/null
+++ b/content/docs/architecture/data-model/19-auth-providers.md
@@ -0,0 +1,688 @@
+# DCM Data Model — Authentication, Authorization, and Auth Providers
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Webhooks and Messaging](18-webhooks-messaging.md) | [Policy Organization](14-policy-profiles.md) | [Deployment and Redundancy](17-deployment-redundancy.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: PROVIDER**
+>
+> The Provider abstraction — Auth Provider and credential management service extensions
+
+
+
+---
+
+## 1. Purpose
+
+DCM authenticates and authorizes every request — inbound and outbound — through a unified **Auth Provider** model. An Auth Provider is the eighth DCM provider type: an external (or built-in) system that answers two questions:
+
+1. **Authentication** — is this identity who they claim to be?
+2. **Authorization** — what is this identity permitted to do?
+
+Every authentication mode DCM supports — static API key, local users, GitHub OAuth, LDAP, FreeIPA, Active Directory, OIDC, mTLS — is an Auth Provider implementation. The built-in Auth Provider ships with DCM and requires zero external configuration, enabling immediate home lab and evaluation use. External Auth Providers are registered artifacts, versioned, GitOps-managed, and audited.
+
+**Authentication is always required — there is no anonymous access in any DCM profile.** The difference between profiles is how much effort authentication setup requires, not whether it exists.
+
+---
+
+## 2. Auth and credential management service Types
+
+Auth Providers and credential management services are two of the eleven DCM provider types (see [Unified Provider Contract](A-provider-contract.md)). This section covers the authentication modes and configurations supported:
+
+Auth Provider completes the DCM provider ecosystem:
+
+| # | Type | Purpose |
+|---|------|---------|
+| 1 | **Service Provider** | Realizes resources |
+| 2 | **Information Provider** | Serves authoritative external data |
+| 3 | **compound service definition** | Composes multiple providers |
+| 4 | **Data Store (PostgreSQL)** | Persists DCM state |
+| 5 | **External Policy Evaluator** | Evaluates policies externally |
+| 6 | **credential management service** | Manages secrets and credentials |
+| 7 | **Auth Provider** | Authenticates actor identities |
+| 8 | **notification service** | Delivers notifications |
+| 9 | **event routing service** | Async event streaming |
+| 10 | **Resource Type Registry** | Serves the Resource Type Registry |
+| 11 | **Peer DCM** | Another DCM instance (federation) |
+
+---
+
+## 3. Auth Provider Registration
+
+```yaml
+auth_provider_registration:
+  artifact_metadata:
+    uuid: 
+    handle: "providers/auth/corporate-freeipa"
+    version: "1.0.0"
+    status: active
+    owned_by:
+      display_name: "Platform Team"
+
+  name: "Corporate FreeIPA"
+  description: "Primary enterprise directory — FreeIPA with Kerberos"
+
+  # Capabilities
+  capabilities:
+    authentication: true
+    authorization: true
+    mfa: false                    # does this provider enforce MFA?
+    session_management: true
+    group_sync: true
+
+  # Provider type
+  provider_type: 
+
+  # What actor types this provider can authenticate
+  authenticates: [human, service_account, webhook_service_account]
+
+  # Trust level
+  trust_level: 
+  # authoritative: DCM accepts all decisions without re-evaluation
+  # verified:      DCM accepts with additional Policy Engine checks
+  # advisory:      DCM treats decisions as input — full re-evaluation always
+
+  # Connection credentials
+  connection_credentials_ref:
+    service_provider_uuid: 
+    secret_path: "dcm/auth/freeipa/bind-password"
+
+  # Health check
+  health_check:
+    interval_seconds: 30
+    on_unhealthy: 
+    fallback_provider_uuid: 
+
+  # Session configuration
+  session:
+    token_ttl: PT8H
+    refresh_enabled: true
+    refresh_ttl: P7D
+    concurrent_sessions: 3
+
+  # Role mapping — external groups → DCM roles
+  role_mapping:
+    default_role: consumer
+    group_role_map:
+      - external_group: "cn=dcm-admins,cn=groups,cn=accounts,dc=corp,dc=example,dc=com"
+        dcm_role: platform_admin
+      - external_group: "cn=dcm-sre,cn=groups,cn=accounts,dc=corp,dc=example,dc=com"
+        dcm_role: sre
+      - external_group: "cn=dcm-consumers,cn=groups,cn=accounts,dc=corp,dc=example,dc=com"
+        dcm_role: consumer
+
+  # Tenant mapping — external groups → DCM Tenants
+  tenant_mapping:
+    strategy: 
+    group_tenant_map:
+      - external_group: "cn=payments-team,cn=groups,..."
+        tenant_uuid: 
+      - external_group: "cn=platform-team,cn=groups,..."
+        tenant_scope: [all]
+
+  # Config changes go through shadow validation
+  on_config_change: proposed
+```
+
+---
+
+## 4. Authentication Modes
+
+### 4.1 Built-In Auth Provider (zero configuration)
+
+Ships with DCM. Always registered. Cannot be deregistered — only deprioritized.
+
+```yaml
+built_in_auth_provider:
+  handle: "providers/auth/dcm-builtin"
+  provider_type: built_in
+  modes:
+    static_api_key:
+      enabled: true             # generated at bootstrap — shown once
+    local_users:
+      enabled: true             # managed via: dcm user create
+    github_oauth:
+      enabled: false            # opt-in: requires client_id + secret
+    gitlab_oauth:
+      enabled: false            # opt-in: requires client_id + secret
+```
+
+**Static API Key** — generated at bootstrap, shown once:
+```
+✓ DCM is ready.
+Your bootstrap API key (shown once — store it securely):
+  dcm_sk_a1b2c3...f7g8
+
+curl -H "Authorization: Bearer dcm_sk_a1b2c3...f7g8" https://localhost:8443/api/v1/catalog
+```
+
+**Local Users** — managed via CLI:
+```bash
+dcm user create --username admin --role platform_admin
+dcm user create --username developer --role consumer --tenant payments
+```
+
+### 4.2 GitHub / GitLab OAuth
+
+```yaml
+auth_provider:
+  provider_type: github_oauth
+  config:
+    client_id: 
+    client_secret_ref:
+      service_provider: dcm_internal
+      path: "dcm/auth/github/client-secret"
+    role_mapping:
+      default_role: consumer
+      org_role_map:
+        - github_org: "my-lab-org"
+          dcm_role: platform_admin
+```
+
+### 4.3 LDAP / FreeIPA (RFC 4511)
+
+```yaml
+auth_provider:
+  provider_type: freeipa          # or: ldap
+  config:
+    server: ldaps://freeipa.corp.example.com:636
+    tls:
+      mode: ldaps                 # ldaps | starttls
+      ca_cert_ref:
+        service_provider: dcm_internal
+        path: "dcm/auth/freeipa/ca-cert"
+    bind_dn: "uid=dcm-service,cn=users,cn=accounts,dc=corp,dc=example,dc=com"
+    bind_password_ref:
+      service_provider: dcm_internal
+      path: "dcm/auth/freeipa/bind-password"
+
+    user_search:
+      base_dn: "cn=users,cn=accounts,dc=corp,dc=example,dc=com"
+      filter: "(uid={username})"
+      attributes:
+        username: uid
+        email: mail
+        display_name: cn
+
+    group_search:
+      base_dn: "cn=groups,cn=accounts,dc=corp,dc=example,dc=com"
+      filter: "(member={user_dn})"
+      attributes:
+        group_name: cn
+
+    # FreeIPA-specific integrations
+    kerberos:
+      enabled: true               # SSO for Linux CLI users
+      keytab_ref:
+        service_provider: dcm_internal
+        path: "dcm/auth/freeipa/dcm.keytab"
+      service_principal: "HTTP/dcm.corp.example.com@CORP.EXAMPLE.COM"
+    hbac:
+      enforce: true               # Honor FreeIPA Host-Based Access Control
+    ca:
+      trust_freeipa_ca: true      # Trust FreeIPA CA for mTLS
+
+    group_sync:
+      enabled: true
+      interval_seconds: 300
+      on_group_change: reauthorize
+```
+
+### 4.4 Active Directory
+
+```yaml
+auth_provider:
+  provider_type: active_directory
+  config:
+    domain_controllers:
+      - ldaps://dc01.corp.example.com:636
+      - ldaps://dc02.corp.example.com:636   # automatic failover
+    tls:
+      mode: ldaps
+      ca_cert_ref:
+        service_provider: dcm_internal
+        path: "dcm/auth/ad/ca-cert"
+    bind_dn: "CN=DCM Service,OU=Service Accounts,DC=corp,DC=example,DC=com"
+    bind_password_ref:
+      service_provider: dcm_internal
+      path: "dcm/auth/ad/bind-password"
+
+    user_search:
+      base_dn: "DC=corp,DC=example,DC=com"
+      filter: "(sAMAccountName={username})"
+      # UPN alternative: "(userPrincipalName={username}@corp.example.com)"
+      attributes:
+        username: sAMAccountName
+        email: userPrincipalName
+        display_name: displayName
+        sid: objectSid            # AD Security Identifier — for audit
+
+    group_search:
+      base_dn: "DC=corp,DC=example,DC=com"
+      # LDAP_MATCHING_RULE_IN_CHAIN — resolves nested AD group membership
+      filter: "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={user_dn}))"
+      attributes:
+        group_name: cn
+        group_dn: distinguishedName
+```
+
+### 4.5 OIDC
+
+```yaml
+auth_provider:
+  provider_type: oidc
+  config:
+    issuer: https://accounts.google.com      # or: Okta, Azure AD, Keycloak, Dex
+    client_id: dcm-production
+    client_secret_ref:
+      service_provider: dcm_internal
+      path: "dcm/auth/oidc/client-secret"
+    scopes: [openid, profile, email, groups]
+    claims_mapping:
+      username: preferred_username
+      email: email
+      display_name: name
+      groups: groups
+      department: department          # custom claims
+      cost_center: cost_center
+```
+
+### 4.6 mTLS
+
+```yaml
+auth_provider:
+  provider_type: mtls
+  config:
+    ca_cert_ref:
+      service_provider: dcm_internal
+      path: "dcm/auth/mtls/ca-cert"
+    # Client certificate CN → DCM actor mapping
+    cn_actor_mapping:
+      - cn_pattern: "service-account-*"
+        actor_type: service_account
+        default_role: consumer
+      - cn_pattern: "provider-*"
+        actor_type: provider
+```
+
+---
+
+## 5. Multiple Auth Providers — Priority and Routing
+
+DCM routes to the appropriate Auth Provider based on the authentication signal present in the request:
+
+```yaml
+auth_provider_resolution:
+  resolution_order:
+    - signal: mtls_client_cert
+      provider_uuid: 
+    - signal: bearer_token_oidc
+      provider_uuid: 
+    - signal: bearer_token_apikey
+      provider_uuid: 
+    - signal: basic_auth
+      provider_uuid: 
+    - signal: hmac_signature
+      provider_uuid: 
+    - signal: none
+      action: reject               # always — no anonymous access
+```
+
+### 5.1 Auth Provider Chain
+
+Authentication and authorization enrichment can be chained:
+
+```yaml
+auth_provider_chain:
+  authentication:
+    provider_uuid:    # fast LDAP bind
+  enrichment:
+    provider_uuid:    # LDAP group membership
+  augmentation:
+    provider_uuid:  # OIDC userinfo for rich claims
+    # (department, cost_center, project codes from HR system)
+```
+
+---
+
+## 6. credential management service
+
+A **credential management service** is the seventh DCM provider type — a cross-cutting dependency that any DCM component or provider registration references for secret resolution. DCM never stores credentials directly.
+
+```yaml
+service_provider_registration:
+  artifact_metadata:
+    uuid: 
+    handle: "providers/credentials/hashicorp-vault-prod"
+    status: active
+
+  name: "HashiCorp Vault Production"
+  backend_type: 
+
+  connection:
+    endpoint: https://vault.corp.example.com:8200
+    auth_method: 
+    namespace: 
+
+  credential_types: [hmac_secret, api_key, certificate, connection_string,
+                     bearer_token, private_key, username_password, ldap_bind]
+
+  health_check:
+    interval_seconds: 60
+    on_unhealthy: 
+    # suspend_dependents: suspend all components using this provider
+    # fail_open: continue using cached credentials (risk — use cautiously)
+
+  caching:
+    enabled: true
+    ttl_seconds: 300             # refresh from vault every 5 minutes
+```
+
+**Credential references** — used everywhere a secret is needed:
+
+```yaml
+# In webhook authentication
+secret_ref:
+  service_provider_uuid: 
+  secret_path: "dcm/webhooks/payments/hmac-secret"
+  version: latest
+
+# In Auth Provider connection
+bind_password_ref:
+  service_provider_uuid: 
+  secret_path: "dcm/auth/freeipa/bind-password"
+
+# In Service Provider registration
+credentials_ref:
+  service_provider_uuid: 
+  secret_path: "dcm/providers/kubevirt/service-account"
+```
+
+Credentials are cached in memory per the configured TTL. On cache miss, DCM fetches from the credential management service. Credentials never appear in audit records (only the `secret_path` is recorded), never in Git, never in logs.
+
+---
+
+## 7. The Authentication Ladder
+
+Every rung is authenticated. The ladder is about setup effort — not whether authentication exists.
+
+| Profile | Auth Modes Available | Setup Effort | Notes |
+|---------|---------------------|-------------|-------|
+| `minimal` | Static API key, Local user/password | 30 seconds – 2 minutes | Generated at bootstrap; zero external config |
+| `dev` | + GitHub/GitLab OAuth, FreeIPA/AD (direct bind) | 5–15 minutes | OAuth requires app registration; LDAP requires server config |
+| `standard` | + OIDC via broker (Dex/Keycloak), AD/FreeIPA direct | 30–60 minutes | Enterprise directory or IdP integration |
+| `prod` | + OIDC direct, MFA | 1–2 hours | Full enterprise IdP; MFA configurable |
+| `fsi` | + mTLS required, MFA required | 4–8 hours | Certificate infrastructure required |
+| `sovereign` | + Air-gapped OIDC/mTLS | 1–2 days | No external auth dependencies |
+
+### 7.1 First-Run Setup
+
+```
+DCM First Run Setup
+═══════════════════════════════════════════════════════
+
+Welcome to DCM. Choose an authentication mode:
+
+  [1] Static API Key    — Instant start. One key, full access.
+                          Best for: solo home lab, quick evaluation.
+
+  [2] Local Users       — Create usernames and passwords.
+                          Best for: small team, dev environment.
+
+  [3] GitHub OAuth      — Login with GitHub accounts.
+                          Best for: dev teams using GitHub.
+
+  [4] Configure later   — Start with API key, switch to OIDC/LDAP later.
+
+Choice [1]:
+```
+
+### 7.2 The Upgrade Path
+
+Authentication configuration is a DCM artifact — versioned, stored in Git, upgradeable via standard lifecycle:
+
+```bash
+# Upgrade from static API key to FreeIPA LDAP
+dcm auth configure \
+  --provider-type freeipa \
+  --server ldaps://freeipa.corp.example.com:636 \
+  --bind-dn "uid=dcm-service,..." \
+  --bind-password-ref "dcm/auth/freeipa/bind-password"
+# DCM validates, runs in shadow mode, cuts over — API key deprecated on schedule
+```
+
+---
+
+## 8. Profile-Governed Enforcement
+
+| Feature | minimal | dev | standard | prod | fsi | sovereign |
+|---------|---------|-----|---------|------|-----|----------|
+| Static API key | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
+| Local user/password | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
+| GitHub/GitLab OAuth | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
+| LDAP direct bind | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| OIDC (any provider) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| mTLS | ❌ | ❌ | Optional | Recommended | Required | Required |
+| MFA | ❌ | ❌ | Optional | Configurable | Required | Required |
+| Air-gapped OIDC | ❌ | ❌ | ❌ | ❌ | Optional | Required |
+| Anonymous access | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+
+---
+
+> **Session revocation lifecycle:** See [Session Token Revocation](35-session-revocation.md) for the complete session revocation model including AUTH-016–AUTH-022 (actor deprovisioning, revocation registry, token introspection, concurrent session enforcement).
+
+## 9. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `AUTH-001` | All DCM authentication must be handled through a registered Auth Provider. The built-in Auth Provider is always available and cannot be deregistered. |
+| `AUTH-002` | Multiple Auth Providers may be registered simultaneously. The ingress layer routes to the appropriate provider based on the authentication signal in the request. |
+| `AUTH-003` | Auth Provider trust level governs how DCM treats decisions: authoritative (accepted as-is), verified (with Policy Engine augmentation), advisory (full re-evaluation). |
+| `AUTH-004` | Auth Provider role and tenant mappings are versioned artifacts subject to standard DCM artifact lifecycle. Changes go through proposed → active validation. |
+| `AUTH-005` | If an Auth Provider becomes unhealthy, existing sessions remain valid until TTL expiry. New authentication attempts route to the configured fallback provider or are rejected. |
+| `AUTH-006` | The Auth Provider used to authenticate a request is recorded in the ingress block and carried into the audit record. Policies may act on auth_provider_uuid and provider_type. |
+| `AUTH-007` | Auth Provider configuration credentials must reference a registered credential management service. Plaintext credentials are rejected. |
+| `AUTH-008` | There is no anonymous access in any DCM profile. Minimal and dev profiles support lightweight authenticated modes requiring minimal setup. |
+| `AUTH-009` | Webhook and message bus inbound surfaces always require authentication regardless of active Profile. Anonymous actors are never permitted on these surfaces. |
+| `AUTH-010` | Rate limiting is enforced per authenticated actor. Limits are declared on the Auth Provider or webhook actor registration. |
+
+---
+
+## 10. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should DCM support SCIM for automated user provisioning from enterprise IdPs? | Enterprise integration | ✅ Resolved — SCIM 2.0 (RFC 7643 / RFC 7644) optional capability; provisions actors and group memberships; roles not SCIM-provisioned; suspend on deprovision default (AUTH-012) |
+| 2 | How does Auth Provider failover interact with in-flight requests during the transition? | Reliability | ✅ Resolved — in-flight requests complete on cached tokens; failover chain for new auth; session TTL respected during outage; all providers unavailable → reject (AUTH-013) |
+| 3 | Should MFA enforcement be per-operation (step-up MFA) or per-session? | Security UX | ✅ Resolved — two-tier MFA: per-session + step-up; policy declares which operations require step-up; PT10M step-up token TTL; profile-governed defaults (AUTH-014) |
+| 4 | Should the built-in Auth Provider's local user store be backed by a pluggable database? | Architecture | ✅ Resolved — pluggable storage backend; SQLite for minimal/dev; PostgreSQL for standard+; encryption required for fsi/sovereign; local store for bootstrap/service accounts only (AUTH-015) |
+
+---
+
+## 11. Related Concepts
+
+- **Webhooks and Messaging** (doc 18) — ingress/egress actor model; webhook actor registration
+- **Policy Organization** (doc 14) — policies act on auth_provider_type and ingress fields
+- **Universal Audit Model** (doc 16) — auth provider and ingress context in every audit record
+- **credential management service** — resolves all Auth Provider connection secrets
+- **Universal Group Model** (doc 15) — group memberships resolved via Auth Provider group sync
+
+## 10. Git Identity Resolution
+
+When DCM processes Git PR ingress, it must resolve the Git server's verified actor identity to a DCM actor with full role, group, and tenant scope context — identical to web UI or API login for the same user.
+
+### 10.1 The Trust Model
+
+DCM trusts the **Git server's authentication assertion** — not user-declared Git configuration. The Git server has already authenticated the user (via SSH key, OAuth token, or LDAP password). DCM receives the Git server's verified identity from the PR merge webhook and resolves it through the registered Auth Provider.
+
+```
+Git server authenticates user → PR merge webhook → DCM Auth Provider resolution → DCM actor
+```
+
+### 10.2 Resolution Methods
+
+| Method | When Used | Auth Provider |
+|--------|----------|--------------|
+| `oidc_subject_lookup` | Git server uses same OIDC/OAuth IdP as DCM | OIDC Auth Provider |
+| `ldap_username_lookup` | Git server authenticates via LDAP/AD | LDAP/AD Auth Provider |
+| `ssh_key_fingerprint` | SSH key-authenticated Git workflows | DCM SSH key registry |
+| `webhook_service_account` | Automated CI/CD Git workflows | Registered webhook actor |
+
+### 10.3 The Resolved Actor
+
+The resolved Git actor carries **identical** role, group, and tenant scope to the same user authenticating via web UI:
+
+```yaml
+# A user who logs into the web UI via FreeIPA LDAP gets:
+# roles: [sre], tenant_scope: [payments-uuid], groups: [payments-team-uuid]
+
+# The same user merging a Git PR via their Git account gets:
+# roles: [sre], tenant_scope: [payments-uuid], groups: [payments-team-uuid]
+# — identical — because both resolve through the same FreeIPA Auth Provider
+```
+
+This is the key invariant: **Git PR ingress does not grant different permissions than any other ingress surface.** The same Auth Provider, the same group mappings, the same tenant scope enforcement.
+
+### 10.4 System Policy
+
+| Policy | Rule |
+|--------|------|
+| `AUTH-011` | Git PR actor identity resolution must use the registered Auth Provider. DCM trusts the Git server's verified identity assertion — not user-declared Git configuration. The resolved actor carries the same role, group, and tenant scope as any other user authenticated via the same Auth Provider. |
+
+
+## 11. Auth Provider Gap Resolutions
+
+### 11.1 SCIM 2.0 User Provisioning (Q1)
+
+DCM supports SCIM 2.0 as an optional Auth Provider capability for enterprise deployments. SCIM automates actor lifecycle management — provisioning, attribute updates, and deprovisioning — from enterprise IdPs (Okta, Azure AD, Ping Identity, JumpCloud).
+
+```yaml
+scim_provider_config:
+  enabled: true
+  scim_version: "2.0"
+  endpoint: https://dcm.corp.example.com/scim/v2
+  auth:
+    mode: bearer_token
+    token_ref:
+      service_provider_uuid: 
+      path: "dcm/auth/scim/bearer-token"
+
+  provisioned_resources:
+    dcm_actors: true             # create/update/deactivate DCM actor records
+    group_memberships: true      # manage DCM group memberships from IdP groups
+    role_assignments: false      # roles managed by DCM policy — not SCIM
+
+  attribute_mapping:
+    idp_userName: actor.username
+    idp_email: actor.email
+    idp_displayName: actor.display_name
+    idp_department: actor.status_metadata.department
+    idp_groups: actor.groups     # IdP groups → DCM group memberships (where mapped)
+
+  on_user_deprovisioned:
+    action: suspend              # suspend | deactivate | archive
+    # suspend: reversible; sessions terminated; leases released
+    in_flight_request_handling: complete_then_suspend
+```
+
+**What SCIM does NOT manage:** Roles are not SCIM-provisioned — they require explicit DCM policy authorization. This prevents privilege escalation through the SCIM channel.
+
+### 11.2 Auth Provider Failover and In-Flight Requests (Q2)
+
+In-flight requests authenticated before Auth Provider failure continue to completion using cached session tokens. New requests follow the declared failover chain.
+
+```yaml
+auth_failover_config:
+  primary_provider_uuid: 
+  failover_chain:
+    - provider_uuid: 
+      promotion_delay: PT30S          # wait 30s before promoting failover
+    - provider_uuid: 
+  session_cache:
+    enabled: true
+    ttl: PT8H                         # valid sessions remain valid during outage
+```
+
+**Three scenarios:**
+- **Mid-assembly request (already authenticated):** Continues to completion — session token carries resolved roles/groups/tenant scope; Auth Provider not needed for assembly
+- **New request, Auth Provider down:** Follows failover chain; served from session cache if session still valid
+- **Session expiry during outage:** Requires re-authentication via available failover provider; if all unavailable → reject with clear error
+
+### 11.3 Step-Up MFA (Q3)
+
+MFA enforcement is two-tier: per-session (validated at login, captured in `mfa_verified` field) and step-up (additional challenge at sensitive operations within an already-authenticated session).
+
+```yaml
+step_up_mfa_config:
+  step_up_required_for:
+    - platform_policy_activate
+    - provider_decommission
+    - tenant_decommission
+    - sovereignty_zone_change
+    - auth_provider_update
+    - manual_rehydration          # if entity min_auth_level = hardware_token_mfa
+  step_up_method: 
+  step_up_token_ttl: PT10M
+  step_up_challenge_max_age: PT5M
+```
+
+**Profile defaults:**
+
+| Profile | Per-Session MFA | Step-Up Required |
+|---------|----------------|-----------------|
+| minimal | No | No |
+| dev | No | No |
+| standard | Recommended | Optional |
+| prod | Required | Destructive operations |
+| fsi | Required | All policy changes |
+| sovereign | Required (hardware token) | All administrative operations |
+
+### 11.4 Built-In Auth Provider Storage Backend (Q4)
+
+The built-in Auth Provider's local user store uses a pluggable storage backend following the data store model.
+
+```yaml
+builtin_auth_provider_config:
+  user_store:
+    profile_defaults:
+      minimal: sqlite            # zero infrastructure; single-file
+      dev: sqlite
+      standard: postgresql       # durable; concurrent; backupable
+      prod: postgresql
+      fsi: postgresql            # encrypted storage (TDE required)
+      sovereign: postgresql      # HSM-backed encryption required
+    encryption_at_rest:
+      required_profiles: [fsi, sovereign]
+      key_ref:
+        service_provider_uuid: 
+        path: "dcm/auth/builtin/encryption-key"
+```
+
+**The local user store should only contain:** bootstrap users, service accounts, and API key holders. Enterprise users belong in external Auth Providers (LDAP, OIDC, SCIM).
+
+### 11.5 System Policies — Auth Provider Gaps
+
+| Policy | Rule |
+|--------|------|
+| `AUTH-012` | DCM supports SCIM 2.0 as an optional Auth Provider capability. SCIM provisions and deprovisions DCM actors and group memberships. Roles are not SCIM-provisioned — they require explicit DCM policy authorization. SCIM deprovisioning suspends actors by default; in-flight requests complete before suspension. |
+| `AUTH-013` | In-flight requests authenticated before Auth Provider failure continue using cached session tokens. New requests follow the declared failover chain. Sessions remain valid for their declared TTL during outages. Session expiry during outage requires re-authentication via available failover provider. All providers unavailable → new authentication rejected. |
+| `AUTH-014` | MFA enforcement is two-tier: per-session MFA (captured in mfa_verified field) and step-up MFA (additional challenge at sensitive operations). Policy declares which operations require step-up regardless of session MFA status. Step-up tokens are short-lived (PT10M default). Profile governs default requirements. |
+| `AUTH-015` | The built-in Auth Provider uses a pluggable storage backend. SQLite is the default for minimal/dev profiles. PostgreSQL is the default for standard+ profiles. FSI and sovereign profiles require encryption at rest. The local user store should only contain bootstrap users, service accounts, and API key holders. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/20-registry-governance.md b/content/docs/architecture/data-model/20-registry-governance.md
new file mode 100644
index 0000000..44354df
--- /dev/null
+++ b/content/docs/architecture/data-model/20-registry-governance.md
@@ -0,0 +1,652 @@
+# DCM Data Model — Registry Governance
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Policy Organization](14-policy-profiles.md) | [Deployment and Redundancy](17-deployment-redundancy.md) | [Auth Providers](19-auth-providers.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + PROVIDER**
+>
+> Data: registry artifacts. Provider: Resource Type Registry extension
+
+
+
+---
+
+## 1. Purpose
+
+The Resource Type Registry is the authoritative catalog of Resource Type Specifications available to DCM deployments. It governs what resources can be requested, how they are defined, and how those definitions evolve over time. Registry governance defines how new types are proposed, reviewed, approved, versioned, deprecated, and distributed — including in air-gapped and sovereign deployments.
+
+Registry governance follows the same principles as all other DCM governance: GitOps-managed, policy-driven, profile-governed for ease of use, and audited.
+
+---
+
+## 2. The Three-Tier Registry
+
+### 2.1 Registry Tiers
+
+| Tier | Name | Maintained By | Contains | Governed By |
+|------|------|--------------|---------|------------|
+| 1 | **DCM Core** | DCM Project team | Universal resource types | DCM maintainers + community |
+| 2 | **Verified Community** | Named community maintainers | Technology/platform-specific types | Named maintainer(s) + DCM oversight |
+| 3 | **Organization** | Deploying organization | Organization-specific/proprietary types | Organization's own process |
+
+**Tier 1 examples:** `Compute.VirtualMachine`, `Network.VLAN`, `Network.IPAddress`, `Storage.Block`, `Storage.File`, `Container.Pod`
+
+**Tier 2 examples:** `OpenStack.HeatStack`, `VMware.NSXSegment`, `KubeVirt.VirtualMachine`, `Ansible.Playbook`
+
+**Tier 3 examples:** `Acme.LegacyMainframeJob`, `Corp.ServiceNowTicket`, `Internal.ComplianceReport`
+
+
+### 2a. Three-Tier Model Applied to All Artifact Types
+
+The three-tier registry model applies to all DCM artifact types, not just resource type specs. Every artifact in DCM has a tier that determines its trust level and the review requirements for changes:
+
+| Tier | Maintained by | Examples | Review for changes |
+|------|--------------|---------|-------------------|
+| **Core** | DCM Project | Built-in policies, base layers, system resource types | DCM project PR process |
+| **Verified Community** | Named community maintainers | Community resource types, shared policy templates, vetted provider specs | Community review + platform admin acceptance |
+| **Organization** | Deploying organization | Tenant policies, provider catalog items, org-specific specs | Per profile (auto → authorized) |
+
+**Contributor sub-tiers within Organization tier:**
+- `organization/platform` — authored by platform admins; highest trust in org tier
+- `organization/provider` — authored by registered Service Providers; scoped to their resource types
+- `organization/tenant` — authored by Consumer/Tenant actors; scoped to their Tenant
+
+This means a tenant-authored GateKeeper policy is Organization/Tenant tier — it has lower inherent trust than a platform-authored policy at the same domain level, and may require additional review per the active profile. See [Federated Contribution Model](28-federated-contribution-model.md).
+
+
+### 2.2 The Federated Registry Model
+
+The registry uses a federated model — not centralized, not fully distributed. This supports air-gapped and sovereign deployments without external dependencies.
+
+```
+DCM Project Registry (authoritative origin)
+  Published at: registry.dcm-project.github.io
+  Contains: Tier 1 Core + Tier 2 Verified Community
+  │
+  ▼  Sync (scheduled pull)
+Organization Registry (local mirror)
+  Hosted internally by the deploying organization
+  Adds: Tier 3 Organization-specific types
+  Authoritative for: this organization's DCM deployments
+  Can operate offline: yes — pulls during sync windows
+  │
+  ▼  Signed bundle transfer (for air-gapped)
+Air-gapped Registry (offline copy)
+  No external connectivity required
+  Updated via signed bundles verified against org public key
+  Authoritative for: this sovereign/air-gapped deployment
+```
+
+---
+
+## 3. Proposal and Review Workflow
+
+### 3.1 The PR-Based Proposal Flow
+
+The PR submitter becomes the **Resource Type Authority** for the submitted specification
+unless an alternative authority is declared in the `owned_by` field. The authority is
+the required approver for all future version PRs against that specification — no version
+of the specification can be activated without the authority's approval (or the authority
+designating a successor via a formal authority transfer PR).
+
+Resource Type proposals follow a GitOps PR-based workflow — not form submissions or tickets. A proposal is a Pull Request against the registry repository.
+
+> **Resource Type Authority:** The PR submitter becomes the **Resource Type Authority**
+> for the specification unless an alternative is declared in the `owned_by` field.
+> The authority is the required approver for all future version PRs — no new version
+> activates without their approval. Authority can be transferred via a formal transfer PR.
+> This is the same `owned_by` governance model applied to all DCM artifacts.
+
+
+
+```
+1. Author creates Resource Type Specification draft
+   ├── Standard artifact format (uuid, handle, version, status: developing)
+   ├── Schema definition
+   ├── Lifecycle declarations
+   ├── Declared dependencies (must exist in registry)
+   └── At least one example request payload
+
+2. Author opens Pull Request
+   ├── PR template: use case justification, example provider implementation,
+   │   test cases, schema validation passing
+   └── Status automatically set to: proposed (on PR open)
+
+3. Automated validation gates (must all pass before review begins)
+   ├── Schema validator passes
+   ├── No FQN conflict with existing active entries
+   ├── All declared dependencies resolve
+   ├── Breaking change detector (if version > 1.0.0)
+   └── Test case coverage (at least one valid example payload)
+
+4. Community review period (see Section 3.2)
+
+5. Maintainer approval + merge
+   └── Status: proposed → enters shadow validation
+
+6. Shadow validation period (same duration as review period)
+   ├── Specification available to DCM deployments opted into proposed feed
+   ├── Issues reported back as PR comments
+   └── Must pass without critical issues before promotion
+
+7. Promotion to active
+   └── Status: active → available in standard registry feed
+```
+
+### 3.2 Review Periods by Change Type
+
+| Change Type | Min Review Period | Shadow Validation | Approvers Required |
+|-------------|-----------------|-------------------|-------------------|
+| New Tier 1 resource type | 14 days | 14 days | 2 DCM maintainers |
+| New Tier 2 resource type | 7 days | 7 days | 1 DCM maintainer + named tier maintainer |
+| Minor version (non-breaking) | 7 days | 7 days | 1 DCM maintainer |
+| Revision (config data only) | 3 days | 3 days | 1 DCM maintainer (or auto-approve if CI passes) |
+| Breaking change (major version) | 21 days | 21 days | 2 DCM maintainers + community comment period |
+| Deprecation | 30 days | N/A | 2 DCM maintainers + affected provider notification |
+| Emergency (security) | Waived | 7 days minimum | 2 DCM maintainers + immediate notification |
+
+---
+
+## 4. Versioning
+
+### 4.1 Version Schema
+
+Resource Type Specifications use semantic versioning: `Major.Minor.Revision`
+
+| Component | Meaning | Compatibility |
+|-----------|---------|--------------|
+| **Major** | Breaking change — field removed, type changed, behavior incompatible | Not compatible with previous major |
+| **Minor** | Non-breaking addition — new optional fields, new lifecycle states | Compatible within major |
+| **Revision** | Configuration data change — no structural change | Compatible within minor |
+
+### 4.2 Version Resolution Policy
+
+Version constraints in requests are **strictly enforced** — DCM never silently resolves to a different version than declared. The resolution policy governs how much flexibility a consumer has:
+
+```yaml
+resource_type_version_constraint:
+  resource_type: Compute.VirtualMachine
+  version_policy: 
+  # exact:        Must match — "1.2.3" means only 1.2.3
+  # compatible:   Same major — "^1.2.3" means >= 1.2.3 < 2.0.0
+  # latest_minor: Latest revision of specified minor — "~1.2" means 1.2.x
+  # latest:       Always use the latest active version
+  pinned_version: "1.2.3"   # required if version_policy: exact
+```
+
+**DCM never automatically upgrades across major versions regardless of version_policy.** Moving from v1.x to v2.x always requires explicit consumer action.
+
+### 4.3 Profile-Governed Version Policy Defaults
+
+| Profile | Default Version Policy | Rationale |
+|---------|----------------------|-----------|
+| `minimal` | `latest` | Home lab — always current, no pinning overhead |
+| `dev` | `compatible` | Dev — tracks major version, picks up fixes automatically |
+| `standard` | `compatible` | Production — stable within major version |
+| `prod` | `compatible` | Production — explicit major version control |
+| `fsi` | `exact` | Regulatory — version-controlled for auditability |
+| `sovereign` | `exact` | Maximum control — exact versions for reproducibility |
+
+---
+
+## 5. Deprecation Lifecycle
+
+### 5.1 The Default Deprecation Policy
+
+Deprecation lifecycle is governed by **default DCM system policies** — not hard-coded values. These defaults can be overridden using the standard policy priority mechanism. Higher-priority organizational policies can shorten, extend, or lock any of these values.
+
+```yaml
+# Default deprecation lifecycle policies (platform domain — overridable)
+deprecation_lifecycle_policies:
+
+  REG-DP-001:
+    name: "Default deprecation notification period"
+    value: P30D           # 30 days notice before deprecation status applied
+    override: allow       # organizations may change this
+
+  REG-DP-002:
+    name: "Default sunset period by tier"
+    values:
+      tier_1: P12M        # 12 months for Core registry types
+      tier_2: P6M         # 6 months for Verified Community types
+      tier_3: organization_governed
+    override: allow
+    profile_locks:
+      fsi: immutable      # FSI profile locks sunset periods
+      sovereign: immutable
+
+  REG-DP-003:
+    name: "Default migration window after retirement"
+    value: P90D           # 90 days after retirement — realizations enter DEPRECATED_RUNTIME
+    override: allow
+
+  REG-DP-004:
+    name: "Migration target declaration"
+    requirement: required_in_deprecation_notice
+    # Deprecation notice must declare: successor type or explicit migration guidance
+    override: allow
+
+  REG-DP-005:
+    name: "Behavior on retirement — new requests"
+    value: reject         # retired types reject new requests (not warn — reject)
+    override: not_permitted   # this is structural — cannot be changed
+
+  REG-DP-006:
+    name: "Behavior on retirement — existing realizations"
+    value: deprecated_runtime_state
+    # Existing realizations enter DEPRECATED_RUNTIME state:
+    # - Eligible for: modify, decommission, drift detection
+    # - Not eligible for: rehydration using deprecated type
+    # - Not automatically destroyed
+    override: allow
+
+  REG-DP-007:
+    name: "Emergency deprecation migration window"
+    value: P30D           # minimum 30 days even for security emergency
+    override: not_permitted   # floor cannot be removed
+```
+
+### 5.2 Deprecation Lifecycle Flow
+
+```
+Resource Type in active status
+  │
+  ▼  Deprecation proposal (PR + 30 day review)
+Status: deprecated
+  │  Notification dispatched to:
+  │  - All registered providers implementing this type
+  │  - All organizations with active realizations
+  │  - All webhook registrations subscribed to registry events
+  │
+  ▼  Sunset period (P12M Tier 1 / P6M Tier 2 — per REG-DP-002)
+  │  During sunset:
+  │  - New requests: succeed with deprecation warning
+  │  - Existing realizations: unaffected
+  │  - Drift detection: continues
+  │  - Provider implementations: remain valid
+  │
+  ▼  Retirement (status: retired)
+  │  Existing realizations → DEPRECATED_RUNTIME state
+  │  New requests → rejected (REG-DP-005)
+  │
+  ▼  Migration window (P90D — per REG-DP-003)
+  │  Organizations migrate realizations to successor type
+  │  DEPRECATED_RUNTIME entities can be decommissioned or migrated
+  │
+  ▼  Post-migration window
+     DEPRECATED_RUNTIME entities remain operational but unsupported
+     Drift detection: continues but remediation is manual
+```
+
+### 5.3 Overriding Deprecation Defaults
+
+Organizations use standard policy priority to customize deprecation behavior:
+
+```yaml
+# Organizational policy: extend Tier 2 sunset to 12 months
+policy:
+  domain: platform
+  priority: 600.0.0
+  type: gatekeeper
+  rule: >
+    If registry.deprecation.tier == tier_2
+    THEN override: sunset_period = P12M
+    basis: "Our tooling requires longer migration windows"
+```
+
+```yaml
+# FSI profile lock: sunset periods immutable
+policy:
+  domain: system
+  priority: 900.0.0
+  immutable_ceiling: absolute
+  rule: >
+    If active_profile IN [fsi, sovereign]
+    THEN lock: REG-DP-002 as immutable
+    rationale: "Regulatory change control requirements"
+```
+
+---
+
+## 6. Provider Selection Tie-Breaking
+
+When the placement engine has multiple viable provider candidates that satisfy all constraints equally, the following hierarchy resolves the tie deterministically:
+
+### 6.1 Tie-Breaking Hierarchy
+
+```
+Priority  Factor                    Condition
+────────  ──────────────────────    ─────────────────────────────────────────
+1         Policy preference         A Transformation policy injected a
+                                    preference_score or preferred_provider_uuid
+
+2         Provider priority         Providers declare a numeric priority
+                                    Higher value = preferred (default: 50)
+
+3         Tenant affinity           Tenant's Policy Group declares preferred
+                                    providers for specific resource types
+
+4         Cost analysis             Cost Analysis component has current data
+                                    AND cost is determinable for candidates
+                                    Prefer lower total cost (CapEx + OpEx)
+                                    SKIP if cost data absent or incomparable
+
+5         Least loaded              Current capacity utilization from reserve_query
+                                    If utilization differs > 10%: prefer less loaded
+                                    SKIP if utilization data unavailable
+
+6         Consistent hash           SHA-256(request_uuid + resource_type + sorted_candidate_uuids)
+                                    Deterministic — same request always resolves
+                                    to same provider in a stable cluster
+                                    Never round-robin
+```
+
+### 6.2 Cost Analysis Integration
+
+Cost analysis ranks above operational load because cost is a business decision. When cost data is available and comparable:
+
+- **CapEx consideration:** provider infrastructure cost allocation per resource type
+- **OpEx consideration:** operational overhead, licensing, support costs per resource unit
+- **Comparability requirement:** cost must be expressed in the same currency and time period; if not comparable (different currencies, missing data), skip to step 5
+
+Cost data is sourced from the **Cost Analysis** control plane component. If Cost Analysis is not deployed or does not have current data for the candidate providers, this step is skipped without blocking placement.
+
+```yaml
+# Cost analysis in placement loop
+placement_cost_evaluation:
+  enabled: true                    # false if Cost Analysis unavailable
+  data_freshness_max: PT1H         # reject cost data older than 1 hour
+  comparison_threshold: 0.05       # 5% cost difference to trigger preference
+  # If candidates are within 5% cost: skip cost as a tiebreaker
+  cost_components:
+    - capex_allocation_per_unit
+    - opex_per_unit_per_hour
+    - licensing_per_unit
+```
+
+### 6.3 Provider Priority Declaration
+
+```yaml
+provider_registration:
+  provider_priority: 100   # default: 50; higher = preferred when equal
+  cost_metadata:
+    capex_allocation_per_unit: 12.50    # USD per VM-month
+    opex_per_unit_per_hour: 0.08       # USD per VM-hour
+    currency: USD
+    last_updated: 
+```
+
+---
+
+## 7. The Resource Type Registry
+
+### 7.1 Concept
+
+The Resource Type Registry is a specialized sub-type of Information Provider — the mechanism through which a DCM deployment accesses its authoritative Resource Type Registry. Every DCM deployment has exactly one active Resource Type Registry.
+
+### 7.2 Registration
+
+```yaml
+internal_registry_registration:
+  artifact_metadata:
+    uuid: 
+    handle: "providers/registry/org-primary"
+    version: "1.0.0"
+    status: active
+
+  name: "Organization Primary Registry"
+  provider_type: registry              # sub-type of information_provider
+
+  # Registry source
+  registry_url: https://registry.corp.example.com
+  tier_1_source: https://registry.dcm-project.github.io   # upstream pull
+  tier_2_sources:
+    - https://registry.dcm-project.github.io
+    - https://registry.partner-org.example.com            # verified partner
+
+  # Sync configuration
+  sync:
+    schedule: "0 2 * * *"            # nightly pull from upstream
+    on_sync_failure: 
+    cache_ttl: P7D                   # use cached if upstream unavailable
+
+  # Air-gapped / sovereign configuration
+  offline_mode: false                # true: no external connectivity
+  signed_bundle_import: false        # true: updates via signed bundles only
+  bundle_signing_key_ref:
+    service_provider_uuid: 
+    secret_path: "dcm/registry/bundle-verification-key"
+
+  # Sovereignty filtering
+  sovereignty_filter:
+    enabled: true
+    permitted_jurisdictions: [eu-west, eu-central]
+    # Only activate resource types flagged as compatible with these jurisdictions
+
+  # Vendor approval list
+  vendor_allowlist:
+    enabled: false                   # true in prod/fsi/sovereign
+    permitted_vendors: [dcm-project, vmware, redhat, hashicorp]
+    # Resource types from non-listed vendors are not activated
+```
+
+### 7.3 Signed Bundle Model (Air-Gapped Updates)
+
+```
+Online workstation (with registry access)
+  │
+  Pull registry delta since last sync
+  Sign with organization private key (via credential management service)
+  Package: registry-update-YYYY-MM-DD.bundle
+  │
+  Transfer via approved secure channel
+  │
+Air-gapped DCM deployment
+  │
+  Verify signature against organization public key
+  Import bundle → update local registry
+  Emit: registry.sync_completed audit event
+```
+
+### 7.4 Resource Type Registry Policies
+
+The Resource Type Registry is fully policy-governed — policies act on registry operations at every stage:
+
+**Sovereignty enforcement:**
+```yaml
+policy:
+  type: gatekeeper
+  target: registry_sync
+  rule: "If resource_type.jurisdiction_compatibility NOT CONTAINS tenant.sovereignty_zone THEN reject_activation"
+```
+
+**Vendor allowlist:**
+```yaml
+policy:
+  type: gatekeeper
+  target: registry_activation
+  rule: "If resource_type.publisher NOT IN approved_vendor_list THEN gatekeep: require_manual_approval"
+```
+
+**Bundle verification:**
+```yaml
+policy:
+  type: gatekeeper
+  target: registry_bundle_import
+  rule: "If bundle.signature_valid == false THEN reject: unsigned bundles not permitted"
+```
+
+**Version pinning in production:**
+```yaml
+policy:
+  type: gatekeeper
+  target: registry_sync
+  rule: "If active_profile == prod AND resource_type.version_delta.type == major THEN gatekeep: major version upgrades require manual approval"
+```
+
+**Audit all syncs:**
+```yaml
+policy:
+  type: transformation
+  target: registry_sync
+  rule: "Always inject: sync_audit.required = true, sync_audit.reviewer = platform_admin"
+```
+
+### 7.5 Profile-Appropriate Registry Policy Groups
+
+DCM ships built-in registry policy groups — one per profile, activated automatically:
+
+| Group Handle | Profile | Key Behaviors |
+|-------------|---------|--------------|
+| `system/group/registry-minimal` | minimal | Advisory only; pull everything; no restrictions; warn on unverified sources |
+| `system/group/registry-dev` | dev | Warn on unverified sources; pull Tier 1+2; no vendor restrictions |
+| `system/group/registry-standard` | standard | Block unverified sources; Tier 1+2 only; sovereignty filter enabled |
+| `system/group/registry-prod` | prod | Strict version pinning; approved vendor list; audit all syncs; major version manual approval |
+| `system/group/registry-fsi` | fsi | Exact version pinning; approved vendor list; immutable sunset periods; all syncs audited with dual approval |
+| `system/group/registry-sovereign` | sovereign | Signed bundles only; offline registry; no external connectivity; bundle verification required |
+
+Organizations can replace or extend these groups using standard Policy Group composition.
+
+---
+
+## 8. DCM System Policies
+
+| Policy | Rule |
+|--------|------|
+| `REG-001` | Resource Type proposals follow a PR-based GitOps workflow with automated validation gates (schema, FQN conflict, dependency resolution, breaking change detection) that must all pass before review begins. |
+| `REG-002` | All registry changes require a minimum review period by change type and a mandatory shadow validation period in `proposed` status before promotion to `active`. |
+| `REG-003` | Deprecation lifecycle is governed by default policies REG-DP-001 through REG-DP-007. These defaults are overridable via standard policy priority except where locked by active Profile. |
+| `REG-004` | Version constraints in requests are strictly enforced. DCM never automatically upgrades across major versions regardless of version_policy. Version resolution policy is profile-governed. |
+| `REG-005` | When multiple providers satisfy all placement criteria equally, the tie-breaking hierarchy applies: policy preference → provider priority → tenant affinity → cost analysis (if available) → least loaded → consistent hash on request_uuid. |
+| `REG-006` | The registry uses a federated model. Air-gapped and sovereign deployments use offline registries populated via signed bundles verified against the organization's public key. |
+| `REG-007` | The Resource Type Registry is policy-governed. Profile-appropriate registry policy groups are activated by default. Organizations may extend or replace these groups using standard Policy Group composition. |
+| `REG-DP-001` | Default deprecation notification period: P30D before deprecation status is applied. Overridable. |
+| `REG-DP-002` | Default sunset period: Tier 1 = P12M, Tier 2 = P6M. Overridable; locked as immutable in fsi and sovereign profiles. |
+| `REG-DP-003` | Default migration window after retirement: P90D. Overridable. |
+| `REG-DP-004` | Deprecation notices must declare a successor type or explicit migration guidance. Overridable. |
+| `REG-DP-005` | Retired resource types reject new requests. Not overridable — structural. |
+| `REG-DP-006` | Existing realizations of retired types enter DEPRECATED_RUNTIME state — eligible for modify and decommission, not rehydration. Overridable. |
+| `REG-DP-007` | Emergency deprecation minimum migration window: P30D. Not overridable — floor cannot be removed. |
+
+---
+
+## 9. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should there be a certified registry tier between Tier 2 and DCM Core for formally audited types? | Ecosystem | ✅ Resolved — no fourth tier; certification metadata within existing tier structure serves same purpose (REG-008) |
+| 2 | Should organizations be able to publish their Tier 3 types to the Verified Community registry? | Community | ✅ Resolved — Tier 3 to Tier 2 promotion via PR pathway with additional requirements: production deployment + OSS license + named maintainer + migration path (REG-009) |
+| 3 | How does the Resource Type Registry handle a scenario where the upstream DCM Project Registry is permanently unavailable? | Resilience | ✅ Resolved — Organization Registry mirror is self-sufficient; upstream loss is governance decision not operational crisis; three long-term options (REG-010) |
+| 4 | Should cost metadata on provider registrations be sourced from the Cost Analysis component or declared statically? | Architecture | ✅ Resolved — static or Cost Analysis sourcing; hybrid with Cost Analysis preferred; placement engine uses freshest available (REG-011) |
+
+---
+
+## 10. Related Concepts
+
+- **Resource Type Hierarchy** (doc 05) — the structure of Resource Type Specifications
+- **Policy Organization** (doc 14) — Policy Groups governing registry behavior
+- **Deployment and Redundancy** (doc 17) — registry sync and offline operation
+- **Auth Providers** (doc 19) — authentication for registry access
+- **Universal Audit Model** (doc 16) — all registry operations produce audit records
+
+
+## 11. Registry Governance Gap Resolutions
+
+### 11.1 No Fourth Registry Tier — Certification Metadata Instead (Q1)
+
+A formal fourth registry tier is not introduced. Resource Type Specifications in any tier may carry certification metadata from recognized certifying bodies. Certification provides equivalent assurance to a separate tier without the governance complexity.
+
+```yaml
+resource_type_spec:
+  registry_tier: 2
+  tier_certifications:
+    - certifying_body: "OpenStack Foundation"
+      certification: "OpenStack Powered"
+      certified_versions: [">=2023.1"]
+      certificate_ref: 
+```
+
+Users seeking "formally audited types" filter on certification metadata — same result as a separate tier, without the structural fragmentation.
+
+### 11.2 Tier 3 to Tier 2 Promotion Pathway (Q2)
+
+Organizations may promote Tier 3 Resource Type Specifications to Tier 2 (Verified Community) via the standard PR-based promotion pathway with additional requirements.
+
+```yaml
+tier_3_to_tier_2_promotion:
+  requirements:
+    - at_least_one_production_deployment: true
+    - documented_use_case: true
+    - open_source_license_compatible: true   # DCM is Apache 2.0
+    - named_community_maintainer: true
+    - test_suite_included: true
+    - migration_path_from_tier3: documented  # handles name changes, schema diffs
+  review_period: 14 days
+  existing_tier3_users_notified: true        # current users notified of promotion
+```
+
+The promotion pathway gives organizations a route from internal tooling to community contribution without requiring a ground-up rewrite. The migration path documentation ensures existing Tier 3 deployments can upgrade smoothly.
+
+### 11.3 Upstream Registry Permanently Unavailable (Q3)
+
+The Organization Registry mirror operates independently from the upstream DCM Project Registry. Permanent upstream loss is a governance decision, not an operational crisis.
+
+**Short-term:** Organization Registry mirror is self-sufficient for all operations. Existing types continue working normally.
+
+**Medium-term:** Resource Type Registry enters "independent operation" mode — new Tier 1/2 types cannot be added (no upstream to sync from); existing types continue operating; Tier 3 unaffected (organization-governed).
+
+**Long-term governance options:**
+- **Option A:** Designate a community mirror as the new upstream (community self-governance)
+- **Option B:** Fork the registry — organization takes ownership of their copy
+- **Option C:** Continue as independent installation (no new community types)
+
+The mirror's self-sufficiency means existing deployments never experience an operational outage due to upstream unavailability.
+
+### 11.4 Provider Cost Metadata Source (Q4)
+
+Provider cost metadata may be declared statically or sourced dynamically from the Cost Analysis component.
+
+```yaml
+provider_cost_metadata:
+  source: 
+
+  static:
+    capex_allocation_per_unit: 12.50   # USD per VM-month
+    opex_per_unit_per_hour: 0.08
+    currency: USD
+    last_updated: 
+
+  cost_analysis:
+    query_interval: PT1H               # refresh cost data hourly
+    fallback: static
+    fallback_max_age: PT24H            # use static if Cost Analysis data older than 24h
+
+  hybrid:
+    prefer: cost_analysis
+    static_for_unavailable: true
+```
+
+The placement engine's cost analysis step (tie-breaking step 4) uses whichever source is freshest and available — Cost Analysis preferred, static as fallback. No changes required to the placement tie-breaking model.
+
+### 11.5 System Policies — Registry Governance Gaps
+
+| Policy | Rule |
+|--------|------|
+| `REG-008` | A formal fourth registry tier is not introduced. Resource Type Specifications in any tier may carry certification metadata from recognized certifying bodies. Certification metadata is a filter criterion — not a structural tier boundary. |
+| `REG-009` | Organizations may promote Tier 3 Resource Type Specifications to Tier 2 via the standard PR-based promotion pathway with additional requirements: at least one production deployment, OSS-compatible license, named community maintainer, and documented migration path from the Tier 3 handle. |
+| `REG-010` | The Organization Registry mirror operates independently from the upstream DCM Project Registry. Permanent upstream unavailability does not affect existing operations. New community type adoption requires a designated community mirror, organizational fork, or independent operation decision. |
+| `REG-011` | Provider cost metadata may be declared statically or sourced dynamically from the Cost Analysis component. Hybrid mode uses Cost Analysis when available and falls back to static. The placement engine uses whichever source is freshest and available. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/21-information-providers-advanced.md b/content/docs/architecture/data-model/21-information-providers-advanced.md
new file mode 100644
index 0000000..d5cafdb
--- /dev/null
+++ b/content/docs/architecture/data-model/21-information-providers-advanced.md
@@ -0,0 +1,699 @@
+# DCM Data Model — Information Providers: Confidence Scoring, Authority, and Conflict Resolution
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Information Providers](10-information-providers.md) | [Policy Organization](14-policy-profiles.md) | [Universal Audit Model](16-universal-audit.md) | [DCM Federation](22-dcm-federation.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: PROVIDER**
+>
+> The Provider abstraction — Information Provider advanced capabilities
+
+
+
+---
+
+## 1. Purpose
+
+This document extends the base Information Provider model with the advanced concepts required for enterprise-grade information governance: confidence scoring for all provider-supplied data, authority and priority declarations as layer-defined organizational knowledge, ingestion-time conflict detection with policy-driven resolution, write-back capabilities, schema versioning, the well-known Information Provider Registry, and air-gapped verification.
+
+---
+
+## 2. Confidence Scoring — The Hybrid Descriptor Model
+
+### 2.1 Purpose and Design Goals
+
+Every field value supplied by an Information Provider carries a confidence descriptor. DCM aggregates data from multiple external sources — CMDB, HR systems, IPAM, asset management, monitoring tools — each with different freshness, authority, and reliability. The confidence model answers: **how much should you trust this field value?**
+
+Three goals drive the design:
+- **Accuracy** — each dimension of confidence is independently meaningful and auditable
+- **Reliability** — a derived numeric score enables mathematical composition for placement decisions and conflict resolution
+- **Ease of use** — a derived band (very_high through very_low) is what humans and policies work with day to day
+
+### 2.2 The Primary Data Model — Confidence Descriptor
+
+The **confidence_descriptor** is the primary data model. The score and band are derived from it — not the other way around. This separation removes false precision: the score is explicitly a convenience derivation, not an independent measurement.
+
+```yaml
+field_confidence:
+  # PRIMARY — stored; set at specific lifecycle points
+  authority_level: primary          # set at provider registration from authority layer
+  corroboration: single_source      # set at ingestion; updated on subsequent pushes
+  source_trust: verified            # maintained by trust scoring system (INF-009)
+  last_updated_at:        # set at each push event
+  source_provider_uuid:       # set at ingestion
+
+  # DERIVED — computed on demand; never stored as primary
+  freshness: high                   # computed: (now - last_updated_at) vs thresholds
+  data_age_minutes: 87              # computed: now - last_updated_at
+  score: 86                         # computed: from descriptor components
+  band: high                        # computed: from score vs band thresholds
+```
+
+### 2.3 Who Sets Each Descriptor Field
+
+| Field | Set By | When | How |
+|-------|--------|------|-----|
+| `authority_level` | Authority declaration layer | Provider registration | Organizational knowledge — static per field per provider |
+| `corroboration` | DCM ingestion pipeline | Each push event | Compared against existing values from other providers |
+| `source_trust` | DCM trust scoring system | Event-triggered + weekly | Push failures, schema errors, health check, re-verification |
+| `last_updated_at` | DCM ingestion pipeline | Each push event | Timestamp of the push event |
+| `freshness` | DCM — derived | Query time | Computed from `now - last_updated_at` vs thresholds |
+| `score` | DCM — derived | Query time | Computed from descriptor components |
+| `band` | DCM — derived | Query time | Computed from score vs band thresholds |
+
+**DCM computes all derived values — providers never self-declare confidence.**
+
+### 2.4 Descriptor Component Values
+
+**`authority_level`** — from the authority declaration layer:
+
+| Value | Meaning |
+|-------|---------|
+| `primary` | Declared primary authoritative source for this field |
+| `secondary` | Corroborating source; used if primary unavailable |
+| `advisory` | Context only; never used for decisions |
+| `discovered` | Value found via active interrogation |
+| `self_reported` | Entity reported its own value |
+| `inferred` | Value inferred from other data |
+
+**`corroboration`** — computed at ingestion time:
+
+| Value | Condition | Confidence Effect |
+|-------|-----------|-----------------|
+| `confirmed` | 2+ providers agree on this value | Increases confidence |
+| `single_source` | Only one provider has asserted this value | Neutral |
+| `contested` | 2+ providers disagree on this value | Reduces confidence |
+
+**`source_trust`** — maintained by trust scoring system:
+
+| Value | Condition | Confidence Effect |
+|-------|-----------|-----------------|
+| `verified` | Provider identity, sovereignty, certs all current | Full confidence |
+| `degraded` | Provider has elevated error/conflict rate | Reduced confidence |
+| `suspended` | Provider below trust threshold; pushes stopped | No new data |
+
+**`freshness`** — computed from `data_age_minutes`:
+
+| Value | Age Threshold |
+|-------|-------------|
+| `high` | < 1 hour |
+| `medium` | 1 hour – 1 day |
+| `low` | 1 day – 7 days |
+| `stale` | > 7 days |
+
+### 2.5 The Score Derivation Formula
+
+The score is a convenience number derived deterministically from the descriptor. It enables mathematical composition (cross-DCM scoring, conflict resolution ordering) where a single number is needed.
+
+```
+score = min(100, base(authority_level)
+              × freshness_multiplier(freshness)
+              × corroboration_multiplier(corroboration)
+              × trust_multiplier(source_trust))
+```
+
+**Base values by authority_level:**
+
+| authority_level | Base Score |
+|----------------|-----------|
+| `primary` | 90 |
+| `secondary` | 70 |
+| `discovered` | 60 |
+| `advisory` | 50 |
+| `self_reported` | 40 |
+| `inferred` | 30 |
+
+**Freshness multipliers:**
+
+| freshness | Multiplier |
+|-----------|-----------|
+| `high` | 1.00 |
+| `medium` | 0.95 |
+| `low` | 0.85 |
+| `stale` | 0.50 |
+
+**Corroboration multipliers:**
+
+| corroboration | Multiplier |
+|--------------|-----------|
+| `confirmed` | 1.15 |
+| `single_source` | 1.00 |
+| `contested` | 0.60 |
+
+**Trust multipliers:**
+
+| source_trust | Multiplier |
+|-------------|-----------|
+| `verified` | 1.00 |
+| `degraded` | 0.75 |
+| `suspended` | 0.00 |
+
+**Example:** Primary authority, fresh data (30 min old), single source, verified:
+`min(100, 90 × 1.00 × 1.00 × 1.00)` = **90**
+
+**Example:** Primary authority, medium freshness (4 hours), two sources agree, verified:
+`min(100, 90 × 0.95 × 1.15 × 1.00)` = 98.3 → **98**
+
+**Example:** Secondary authority, stale data (10 days), contested, degraded:
+`min(100, 70 × 0.50 × 0.60 × 0.75)` = 15.75 → **16**
+
+### 2.6 Score Bands — For Policy Use
+
+Policies use bands, not raw scores. This avoids the brittleness of threshold values like "reject if score < 73":
+
+| Band | Score Range | Policy Label |
+|------|------------|-------------|
+| Very High | 81-100 | `very_high` |
+| High | 61-80 | `high` |
+| Medium | 41-60 | `medium` |
+| Low | 21-40 | `low` |
+| Very Low | 0-20 | `very_low` |
+
+```yaml
+# Policy using band — clear and maintainable
+policy:
+  type: gatekeeper
+  rule: >
+    If field.owner_business_unit.band IN [very_low, low]
+    THEN gatekeep: "Business unit confidence insufficient — manual verification required"
+
+# Policy using individual descriptor dimensions — most precise
+policy:
+  type: gatekeeper
+  rule: >
+    If field.cost_center.corroboration == contested
+    THEN gatekeep: "Cost center is contested between providers — resolve before provisioning"
+
+# Policy using score — for mathematical thresholds
+policy:
+  type: gatekeeper
+  rule: >
+    If field.cost_center.score < 60
+    THEN gatekeep: "Cost center confidence below required threshold"
+```
+
+### 2.7 Derivation Chain Summary
+
+```
+STORED (authoritative):
+  authority_level + corroboration + source_trust + last_updated_at
+
+DERIVED AT QUERY TIME (deterministic from stored):
+  freshness ← (now - last_updated_at) vs thresholds
+  score     ← base(authority_level) × freshness_mult × corroboration_mult × trust_mult
+  band      ← score vs band thresholds
+
+AUDIT RECORD (what auditors can reconstruct from):
+  authority_level (from registration)
+  corroboration (from ingestion event)
+  source_trust (from trust audit at that time)
+  last_updated_at (from push event timestamp)
+  → score and band fully reconstructable from these four stored fields
+```
+
+### 2.8 Configurable Derivation
+
+Organizations may configure the base scores, multipliers, and band thresholds via Policy Group. This allows domain-specific calibration without changing the underlying descriptor model:
+
+```yaml
+confidence_derivation_config:
+  # Override defaults for this deployment
+  base_scores:
+    primary: 90           # default — can increase to 95 for high-trust environments
+    secondary: 70
+  freshness_thresholds:
+    high_max_minutes: 60  # default 60; can tighten to 15 for real-time requirements
+    medium_max_minutes: 1440
+    low_max_minutes: 10080
+  band_thresholds:
+    very_high_min: 81     # default; adjust as needed
+    high_min: 61
+```
+
+Adjusted derivation configs are stored as Policy Group artifacts — versioned, auditable, and profile-governed.
+
+## 3. Authority and Priority — Layer-Defined
+
+### 3.1 Authority as Layer Data
+
+Information Provider authority scope and priority are **layer-defined** — not just policy-driven. They represent static organizational knowledge about information architecture ("our CMDB is the authoritative source for business unit data"). This knowledge belongs in a `platform` domain layer — versioned, GitOps-managed, and inherited by all requests.
+
+```yaml
+layer:
+  handle: "platform/information-authority/cmdb-authority"
+  domain: platform
+  priority: 600.0.0
+  concern_tags: [information-authority, cmdb, organizational-data]
+  fields:
+    information_authority:
+      primary_sources:
+        - provider_uuid: 
+          fields: [owner_business_unit, cost_center, cmdb_id, cmdb_location]
+          authority_level: primary
+          priority: 900.0.0
+        - provider_uuid: 
+          fields: [asset_tag, purchase_date, warranty_expiry, serial_number]
+          authority_level: primary
+          priority: 900.0.0
+      secondary_sources:
+        - provider_uuid: 
+          fields: [owner_business_unit, employee_id]
+          authority_level: secondary
+          priority: 500.0.0
+          # Secondary: corroborates primary; used if primary unavailable
+      advisory_sources:
+        - provider_uuid: 
+          fields: [reported_hostname, reported_ip]
+          authority_level: advisory
+          priority: 200.0.0
+          # Advisory: context only; never used for decisions
+```
+
+### 3.2 Priority Within Authority Level
+
+When multiple providers have the same `authority_level`, the `priority` field (using the same numeric priority schema as layers and policies) determines which value wins:
+
+```
+Higher priority value → wins when authority levels are equal
+Authority level hierarchy: primary > secondary > advisory
+Within same authority level: higher priority number wins
+```
+
+### 3.3 Policy Acting on Authority
+
+Policies can act on authority metadata at runtime:
+
+```yaml
+# Transformation: enrich payload with confidence-weighted values
+policy:
+  type: transformation
+  rule: >
+    If field.owner_business_unit.confidence_score < 60
+    AND field.owner_business_unit.authority_level != primary
+    THEN inject: request_flags.requires_manual_business_unit_verification = true
+
+# GateKeeper: require high confidence for financial operations
+policy:
+  type: gatekeeper
+  rule: >
+    If resource_type == Compute.VirtualMachine
+    AND field.cost_center.confidence_band IN [very_low, low]
+    THEN gatekeep: "Cost center assignment confidence insufficient for VM provisioning"
+```
+
+---
+
+## 4. Ingestion-Time Conflict Detection and Resolution
+
+### 4.1 Conflict Detection at Ingestion
+
+Conflict detection occurs at ingestion time — when DCM receives a push event from an Information Provider. This is the correct architectural moment: before the data enters the entity record, not after.
+
+```
+Information Provider push event received
+  │
+  ▼ 1. Schema validation
+  │   Validate against provider's declared schema version
+  │   Reject on violation (strict) or warn (lenient per policy)
+  │
+  ▼ 2. Authority scope check
+  │   Is this provider authorized to assert values for these fields on this entity?
+  │   Reject unauthorized field assertions (INF-001)
+  │
+  ▼ 3. Confidence score computation
+  │   Compute per-field score using standard formula (Section 2.2)
+  │   Factor: source authority level, data freshness, corroboration
+  │
+  ▼ 4. Conflict detection
+  │   For each field: does an existing value exist from another provider?
+  │   Same value → corroboration (confidence increases, multiplier applied)
+  │   Different value → conflict record created
+  │   No existing value → new assertion (accept)
+  │
+  ▼ 5. Conflict resolution policy
+  │   Apply declared resolution strategy:
+  │   higher_authority_wins → use higher authority_level value
+  │   higher_confidence_wins → use higher confidence score
+  │   higher_priority_wins → use value from higher-priority provider
+  │   escalate → create conflict record; human resolves; existing value retained
+  │   merge → combine values (array/set fields only)
+  │
+  ▼ 6. Entity record update
+  │   Write accepted values with full field provenance
+  │
+  ▼ 7. Audit record — INGEST action
+     All field changes, conflicts detected/resolved, confidence scores,
+     authority assertions recorded in Audit Store
+```
+
+### 4.2 The Conflict Record
+
+```yaml
+information_provider_conflict_record:
+  record_uuid: 
+  detected_at: 
+  field_path: owner_business_unit
+  subject_entity_uuid: 
+  conflicting_values:
+    - provider_uuid: 
+      authority_level: primary
+      priority: 900.0.0
+      value: "Payments Team"
+      confidence_score: 86
+      received_at: 
+    - provider_uuid: 
+      authority_level: secondary
+      priority: 500.0.0
+      value: "Infrastructure Team"
+      confidence_score: 72
+      received_at: 
+  resolution_strategy: higher_authority_wins
+  resolution_status: 
+  auto_resolved_value: "Payments Team"
+  auto_resolved_reason: "primary authority_level wins over secondary"
+  # If escalated:
+  resolved_by: 
+  resolved_value: "Payments Team"
+  resolution_reason: "CMDB is authoritative for business unit — HR system has stale data"
+  resolution_audit_record_uuid: 
+```
+
+### 4.3 Authority Scope Conflicts at Registration
+
+When a new provider registers and declares authority over a field already claimed by an existing provider at the same or higher authority level, DCM detects the conflict at registration time and requires explicit resolution before the provider becomes active.
+
+```
+New provider registers authority_scope: [owner_business_unit, primary]
+  │
+  ▼ DCM checks: existing primary authority for owner_business_unit?
+  │
+  ├── No existing primary → register; no conflict
+  │
+  └── Existing primary provider found:
+        Create: authority_scope_conflict_record
+        Action required:
+          - Demote new provider to secondary, or
+          - Demote existing provider to secondary, or
+          - Declare explicit resolution strategy for this field
+        Provider registration blocked until resolved
+```
+
+---
+
+## 5. Write-Back Capability (Q63)
+
+Information Providers may optionally support write-back — DCM updating external records when entity state changes. Write-back is triggered by policy, never automatic.
+
+```yaml
+information_provider_registration:
+  capabilities:
+    read: true                       # always required
+    write_back: true                 # optional
+    write_back_operations:
+      - operation: create
+        resource_types: [Compute.VirtualMachine]
+        fields: [hostname, ip_address, owner_business_unit, lifecycle_state]
+      - operation: update
+        resource_types: [Compute.VirtualMachine]
+      - operation: delete
+        resource_types: [Compute.VirtualMachine]
+    write_back_authentication:
+      mode: api_key
+      key_ref:
+        service_provider_uuid: 
+        secret_path: "dcm/providers/info/cmdb/write-key"
+```
+
+**Policy triggers write-back:**
+
+```yaml
+policy:
+  type: transformation
+  placement_phase: post
+  rule: >
+    If action IN [CREATE, STATE_TRANSITION, DELETE]
+    AND resource_type == Compute.VirtualMachine
+    THEN trigger_write_back:
+      provider_uuid: 
+      operation: update
+      fields: [hostname, ip_address, lifecycle_state, owner_business_unit]
+```
+
+Write-back produces an audit record with `ENRICH` action and `source_type: information_provider_write_back`.
+
+---
+
+## 6. Extended Schema Versioning (Q64)
+
+Information Provider extended schemas follow semver semantics — the same model as Resource Type Specifications.
+
+```yaml
+information_provider_registration:
+  extended_schema:
+    version: "2.1.0"
+    fields:
+      - name: cmdb_id
+        type: string
+        required: false
+      - name: cmdb_ci_class
+        type: string
+        required: false
+      - name: cmdb_location
+        type: object
+        required: false
+    changelog:
+      "2.0.0": "Removed deprecated cmdb_legacy_id field (major — breaking)"
+      "2.1.0": "Added cmdb_location optional field (minor — compatible)"
+    migration_plan:              # required for major version bumps
+      from_version: "1.x"
+      migration_script_ref: "git://cmdb-provider/migrations/v1-to-v2.yaml"
+      migration_window: P30D
+```
+
+**Semver semantics for extended schemas:**
+
+| Change | Version Bump | Reason |
+|--------|-------------|--------|
+| Field removed | **Major** | Breaking — consumers may depend on it |
+| Field type changed | **Major** | Breaking — consumers must update |
+| New optional field added | **Minor** | Compatible — additive |
+| Description or constraint changed | **Revision** | Compatible — no structural change |
+
+DCM validates incoming push data against the declared schema version. Major version bumps require a declared migration plan before the new schema version activates.
+
+---
+
+## 7. Well-Known Information Provider Registry (Q65)
+
+DCM maintains a three-tier Information Provider Registry following the same governance model as the Resource Type Registry.
+
+| Tier | Name | Contains | Examples |
+|------|------|---------|---------|
+| 1 | DCM Core | Universal integration patterns | Generic CMDB, Generic IPAM, Generic DNS |
+| 2 | Verified Community | Specific platform integrations | ServiceNow, Infoblox, NetBox, FreeIPA, AD, HashiCorp Vault |
+| 3 | Organization | Internal/proprietary | Acme ERP, Corp Asset Database |
+
+Well-known provider registrations include:
+- Pre-configured authority scope declarations
+- Pre-built extended schema definitions
+- Pre-configured write-back operation mappings
+- Connection templates with documented credential requirements
+- Example Policy Group activations for common use cases
+- Health check endpoint patterns
+
+The Information Provider Registry is **distinct from the Resource Type Registry** — separate governance, separate GitOps repositories — but shares the same infrastructure pattern: federated, PR-based proposals, automated validation, shadow validation period, and signed bundles for air-gapped import.
+
+---
+
+## 8. Air-Gapped Verification (Q66)
+
+Three modes for Information Provider verification in air-gapped environments:
+
+### 8.1 Mode 1 — Pre-Verified Signed Bundle (Recommended)
+
+```yaml
+air_gapped_provider_bundle:
+  bundle_uuid: 
+  bundle_type: information_provider
+  signed_at: 
+  signing_key_ref: 
+  provider_registration: 
+  tls_certificate_chain: 
+  schema_definitions: 
+  verification_token: 
+  expires_at:         # bundles have expiry
+```
+
+### 8.2 Mode 2 — Internal mTLS (Internal Providers)
+
+Providers that are themselves internal (internal CMDB, internal IPAM) register with `air_gap_mode: internal_only` and verify using internal mTLS certificates issued by the organization's internal CA (FreeIPA CA or equivalent).
+
+### 8.3 Mode 3 — Periodic Online Re-Verification
+
+For environments air-gapped most of the time but with occasional connectivity windows:
+
+```yaml
+provider_verification:
+  mode: periodic_online
+  cache_ttl: P30D                # how long cached verification is valid
+  on_cache_expiry:
+    minimal: continue            # continue without re-verification
+    dev: alert                   # warn but continue
+    standard: alert              # warn but continue
+    prod: suspend                # suspend until re-verified
+    fsi: suspend
+    sovereign: suspend
+```
+
+---
+
+## 8a. Information Provider Trust Score Validation (Q15)
+
+### 8a.1 Dual-Trigger Model
+
+Information Provider trust scores are maintained per provider using the same dual-trigger pattern as conflict validation: event-triggered updates (primary) with scheduled re-verification (safety net).
+
+**Event-triggered updates:**
+- Provider push fails schema validation → `source_trust` degraded
+- Provider push conflicts with primary authority source → `source_trust` degraded
+- Health check fails → `source_trust` degraded
+- Sovereignty declaration change → trust re-evaluated against current Tenant requirements
+- Provider registration update (new credentials, endpoint change) → re-verification triggered
+
+**Scheduled re-verification:**
+- Daily: health check against all active Information Providers
+- Weekly: full re-verification (identity, sovereignty, certifications, schema compatibility)
+- `fsi` / `sovereign` profiles: daily full re-verification
+
+### 8a.2 Trust Score Structure
+
+```yaml
+information_provider_trust_score:
+  provider_uuid: 
+  score: 87                        # 0-100; contributes to source_trust field
+  scored_at: 
+  components:
+    identity_verified: true        # mTLS cert chain valid; re-checked weekly
+    endpoint_reachable: true       # health check; re-checked daily
+    schema_current: true           # schema version matches registered
+    sovereignty_compatible: true   # sovereignty matches Tenant requirements
+    certifications_current: true   # certifications not expired
+    push_error_rate:
+      rate: 0.02                   # 2% of pushes had schema/auth errors (7-day rolling)
+      weight: 0.15                 # contributes 15% to score degradation
+    conflict_rate:
+      rate: 0.05                   # 5% of pushes had value conflicts (7-day rolling)
+      weight: 0.10
+  decay_rate: per_7_days           # score decays if not re-verified
+  current_source_trust: verified   # verified | degraded | suspended
+  action_on_score_below:
+    threshold: 60
+    action: 
+    # suspend:       stop accepting pushes from this provider
+    # alert:         notify platform admin; continue with degraded trust
+    # reduce_weight: automatically degrade source_trust to degraded
+```
+
+### 8a.3 Trust Score to source_trust Mapping
+
+| Trust Score | source_trust | Effect on Confidence |
+|------------|-------------|---------------------|
+| ≥ 80 | `verified` | Full confidence multiplier (1.00) |
+| 60-79 | `degraded` | Reduced confidence multiplier (0.75) |
+| < 60 | `suspended` | No new data accepted; score = 0 |
+
+
+## 9. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `INF-001` | Information Providers declare authority_level (primary, secondary, advisory) and authority_scope (resource types and fields). Conflicting authority scope declarations are detected at registration time. Conflicting field values from different providers at ingestion time are resolved per declared strategy. All conflicts produce audit records. |
+| `INF-002` | Information Providers may declare write_back capability with specific operations (create, update, delete) and resource types. Write-back is triggered by policy only — never automatic. Write-back produces audit records with ENRICH action. Credentials reference a registered credential management service. |
+| `INF-003` | Information Provider extended schemas are versioned using semver. Removing a field or changing a field type is a major (breaking) version bump requiring a declared migration plan. Adding an optional field is a minor bump. DCM validates incoming push data against the declared schema version. |
+| `INF-004` | DCM maintains a three-tier Information Provider Registry (Core, Verified Community, Organization) following the same governance model as the Resource Type Registry. |
+| `INF-009` | Information Provider trust scores (0-100) are maintained per provider with event-triggered updates (push failure, schema mismatch, conflict, health check) and scheduled weekly re-verification. Trust score degradation transitions source_trust to degraded (reduced confidence multiplier). Suspension stops accepting pushes. Policy governs thresholds and actions per provider. |
+| `INF-005` | In air-gapped environments, Information Providers verify via pre-verified signed bundles, internal mTLS, or periodic online re-verification with cached tokens. Profile governs cache expiry behavior. |
+| `INF-006` | Information Provider field values carry a confidence score (0-100) computed from: source authority level, data freshness, and corroboration. DCM computes scores — providers do not self-declare confidence. Scores decay with data age. |
+| `INF-007` | Authority scope and priority for Information Providers are declared in platform or system domain layers. Policy acts on confidence scores and bands — gating, filtering, and escalating based on threshold declarations. |
+| `INF-008` | Conflict detection occurs at ingestion time. Policy governs automated resolution strategy. All conflicts — detected, auto-resolved, and escalated — produce INGEST audit records with the full conflict detail. |
+
+---
+
+## 10. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should confidence score decay be linear or exponential? | Scoring model | ✅ Resolved — freshness is discrete threshold bands not decay curve; stale multiplier 0.50 provides the cliff effect |
+| 2 | Should DCM expose confidence score aggregation APIs — e.g., "average confidence across all fields for this entity"? | Consumer experience | ✅ Resolved — per-entity confidence aggregation endpoint; overall band = lowest field band; computed on demand; identifies contested and stale fields (INF-010) |
+| 3 | Should conflicting providers receive notification when their value is overridden? | Provider relationship | ✅ Resolved — provider opt-in override notifications; webhook or Message Bus; payload policy-governed; overriding value may be redacted (INF-011) |
+
+
+## 11. Information Provider Advanced Gap Resolutions
+
+### 11.1 Confidence Score Aggregation API (Q2)
+
+DCM exposes a per-entity confidence aggregation endpoint. The overall band reflects the lowest (most conservative) field band — preventing high-scoring fields from masking problematic ones. Aggregations are computed on demand — never stored (freshness changes continuously).
+
+```yaml
+confidence_aggregation_api:
+  endpoint: GET /api/v1/entities/{uuid}/confidence
+  response:
+    entity_uuid: 
+    overall_band: high              # lowest band across all fields (conservative)
+    field_summaries:
+      - field: owner_business_unit
+        band: high
+        score: 86
+        authority_level: primary
+        last_updated_at: 
+      - field: cost_center
+        band: medium
+        score: 54
+        corroboration: contested    # two providers disagree
+    lowest_confidence_fields:
+      - field: cost_center
+        reason: contested
+      - field: asset_tag
+        reason: stale               # data age > 7 days
+    computed_at: 
+```
+
+### 11.2 Conflicting Provider Override Notifications (Q3)
+
+Information Providers may opt in to override notifications. Not a universal default — read-only public registries have no use for notifications; internal CMDBs may want to investigate discrepancies.
+
+```yaml
+information_provider_registration:
+  conflict_notification:
+    enabled: true                   # provider opts in
+    notification_channel: webhook   # or: message_bus
+    notification_endpoint: https://cmdb.corp.example.com/dcm-notifications
+    notify_on: [value_overridden, value_contested, authority_superseded]
+    notification_payload:
+      field_path: true
+      overriding_provider_uuid: true
+      overriding_value: false       # may be redacted for confidentiality
+      conflict_record_uuid: true
+```
+
+**Privacy:** The overriding value may be confidential (from a classified source). Policy governs what is included in the notification payload. GateKeeper can redact the overriding value if classified.
+
+### 11.3 System Policies — Information Provider Advanced Gaps
+
+| Policy | Rule |
+|--------|------|
+| `INF-010` | DCM exposes a confidence aggregation endpoint per entity (GET /api/v1/entities/{uuid}/confidence). The overall confidence band reflects the lowest (most conservative) field band. Aggregations are computed on demand — never stored. The response identifies contested and stale fields requiring attention. |
+| `INF-011` | Information Providers may opt in to override notifications by declaring conflict_notification in their registration. Notifications sent via webhook or Message Bus. The notification payload is policy-governed — the overriding value may be redacted for confidentiality reasons. |
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/22-dcm-federation.md b/content/docs/architecture/data-model/22-dcm-federation.md
new file mode 100644
index 0000000..887ea99
--- /dev/null
+++ b/content/docs/architecture/data-model/22-dcm-federation.md
@@ -0,0 +1,592 @@
+# DCM Data Model — DCM Federation, Peering, and Cross-Instance Coordination
+
+
+**Document Status:** ✅ Complete  
+**Related Documents:** [Federated Contribution Model](28-federated-contribution-model.md) | [Universal Group Model](15-universal-groups.md) | [data stores](11-storage-providers.md) | [Auth Providers](19-auth-providers.md) | [Information Providers Advanced](21-information-providers-advanced.md)
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: PROVIDER + POLICY**
+>
+> Provider: Peer DCM as typed Provider. Policy: federation governance rules
+
+
+
+---
+
+> **Federated Contribution:** Federation contribution follows the [Federated Contribution Model](28-federated-contribution-model.md) — peer DCMs are contributors to each other's artifact stores, scoped by their federation trust posture.
+
+## 1. Purpose
+
+DCM instances do not operate in isolation. Organizations with multiple data centers, regions, or organizational boundaries may run multiple DCM instances that need to coordinate, share resources, and maintain consistent governance. This document defines how DCM instances relate to each other — as peers, in parent-child hierarchies, or as hub-and-spoke configurations — and the mechanisms for cross-instance resource sharing, data export/import, and provider federation eligibility.
+
+---
+
+## 2. DCM-to-DCM Relationship Types
+
+### 2.1 Three Relationship Types
+
+**Peer DCM** — two DCM instances at the same organizational level that share resources or information. Regional DCM instances sharing VLAN allocations. Campus DCM instances sharing compute capacity across departments.
+
+**Parent-Child DCM** — hierarchical relationship where the parent has governance overlay authority over child instances. Corporate DCM with regional children. Service provider DCM with customer children. Parent does not own child resources — it has governance visibility and policy overlay authority (same model as nested Tenants in the Universal Group Model).
+
+**Hub DCM** — specialized Parent DCM acting as a clearinghouse for resource allocation across multiple children. The Hub holds the master resource inventory; children request allocations from the Hub.
+
+### 2.2 Relationship Mapping to Universal Group Model
+
+DCM-to-DCM relationships use the existing Universal Group Model:
+
+```yaml
+# Peer relationship — federation group
+dcm_group:
+  group_class: federation
+  name: "EU-US Regional Federation"
+  members:
+    - member_uuid: 
+      member_type: dcm_peer
+      member_role: eu_region_dcm
+    - member_uuid: 
+      member_type: dcm_peer
+      member_role: us_region_dcm
+  federation_config:
+    shared_policy_inheritance: opt_in
+    cross_member_visibility: audit_only
+
+# Parent-Child relationship — nesting
+dcm_group:
+  group_class: tenant_boundary
+  name: "Corporate DCM"
+  child_groups:
+    - 
+    - 
+  policy_inheritance: opt_out      # parent policies cascade unless child excludes
+```
+
+---
+
+## 3. Provider Federation Eligibility
+
+### 3.1 Concept
+
+Every provider registration carries a `federation_eligibility` declaration — whether the provider can participate in cross-DCM federation, with whom, and under what conditions. This is **layer-defined** (static organizational knowledge) and **policy-enforced** (runtime governance).
+
+### 3.2 Federation Eligibility on Provider Registration
+
+```yaml
+provider_registration:
+  handle: "providers/service/eu-compute-primary"
+
+  federation_eligibility:
+    mode: 
+    # none:      Provider cannot participate in any DCM federation
+    #            (sovereign, classified, or compliance-restricted providers)
+    # selective: Federation permitted only with explicitly declared partners
+    # open:      Federation permitted with any trusted DCM peer
+    #            (sovereignty checks always apply regardless)
+
+    permitted_partners:
+      - partner_type: 
+        dcm_instance_uuids: []          # specific instances
+        dcm_instance_tags: [region-eu, internal]  # tag-based matching
+        dcm_certification_required: [ISO-27001, GDPR-compliant]
+        relationship_requires_approval: true   # bilateral approval required
+
+    federation_scope:
+      permitted_resource_types:
+        - resource_type: Compute.VirtualMachine
+          operations: [allocate, query_capacity]
+          # NOT: decommission — remote DCMs cannot decommission local resources
+        - resource_type: Network.VLAN
+          operations: [allocate, query_capacity, release]
+      data_sharing:
+        capacity_data: true
+        realized_state: false           # do not share realized state details
+        pricing_data: true
+        sovereignty_declaration: true   # always share — required for federation
+      max_allocations_per_partner: 100
+      max_concurrent_allocations: 500
+
+    override_reason: null               # populated when overriding layer default
+```
+
+### 3.3 Layer-Defined Federation Defaults
+
+Federation eligibility defaults live in a `platform` domain layer — static organizational knowledge inherited by all providers unless overridden. Individual provider registrations may be **more restrictive** than the layer default (always permitted); **less restrictive** requires GateKeeper policy approval.
+
+```yaml
+layer:
+  handle: "platform/federation/provider-federation-defaults"
+  domain: platform
+  priority: 600.0.0
+  fields:
+    provider_federation_defaults:
+      compute_providers:
+        federation_eligibility:
+          mode: selective
+          permitted_partners:
+            - partner_type: dcm_peer
+              dcm_instance_tags: [internal, eu-region]
+              dcm_certification_required: [ISO-27001]
+          federation_scope:
+            permitted_resource_types:
+              - resource_type: Compute.VirtualMachine
+                operations: [allocate, query_capacity]
+      (prescribed infrastructure)s:
+        federation_eligibility:
+          mode: none                    # storage never federated — data sovereignty
+      network_providers:
+        federation_eligibility:
+          mode: selective
+          permitted_partners:
+            - partner_type: dcm_peer
+              dcm_instance_tags: [internal]
+      information_providers:
+        federation_eligibility:
+          mode: selective
+          data_sharing_restrictions:
+            max_classification: internal   # never share confidential/restricted
+```
+
+### 3.4 Policy Enforcement on Federation
+
+Policies act on federation eligibility at three enforcement points:
+
+**At tunnel establishment:**
+```yaml
+policy:
+  type: gatekeeper
+  target: dcm_tunnel_establishment
+  rule: >
+    If provider.federation_eligibility.mode == none
+    THEN gatekeep: "Provider is not eligible for federation"
+
+policy:
+  type: gatekeeper
+  target: dcm_tunnel_establishment
+  rule: >
+    If remote_dcm.sovereignty_zone NOT IN permitted_sovereignty_zones
+    THEN gatekeep: "Remote DCM sovereignty zone incompatible with local requirements"
+
+policy:
+  type: gatekeeper
+  target: dcm_tunnel_establishment
+  rule: >
+    If remote_dcm.certifications NOT CONTAINS
+       provider.federation_eligibility.permitted_partners.dcm_certification_required
+    THEN gatekeep: "Remote DCM does not hold required certifications"
+```
+
+**At allocation time:**
+```yaml
+policy:
+  type: gatekeeper
+  target: cross_dcm_allocation
+  rule: >
+    If resource_type NOT IN provider.federation_eligibility.federation_scope.permitted_resource_types
+    THEN gatekeep: "Resource type not permitted through this federation tunnel"
+
+policy:
+  type: gatekeeper
+  target: cross_dcm_allocation
+  rule: >
+    If cross_dcm_allocations_active > provider.federation_eligibility.max_concurrent_allocations
+    THEN gatekeep: "Maximum concurrent federation allocations exceeded"
+```
+
+**At data egress:**
+```yaml
+policy:
+  type: gatekeeper
+  target: dcm_tunnel_data_egress
+  rule: >
+    If data.classification > remote_dcm.max_data_classification_receivable
+    THEN gatekeep: "Data classification exceeds remote DCM authorization"
+```
+
+---
+
+## 4. The DCM Provider — Cross-Instance Tunneling
+
+### 4.1 Concept
+
+A **DCM Provider** is a ninth provider type that wraps another DCM instance's API, enabling one DCM to consume resources managed by another DCM as if they were local providers.
+
+| # | Type | Purpose |
+|---|------|---------|
+| 1 | Service Provider | Realizes resources |
+| 2 | Information Provider | Serves authoritative external data |
+| 3 | compound service definition | Composes multiple providers |
+| 4 | data store | Persists DCM state |
+| 5 | External Policy Evaluator | Supplies and evaluates policies |
+| 6 | event routing service | Bridges internal/external event streams |
+| 7 | credential management service | Resolves secrets |
+| 8 | Auth Provider | Authenticates identities |
+| 9 | **DCM Provider** | Wraps another DCM instance's API |
+
+### 4.2 DCM Provider Registration
+
+```yaml
+dcm_provider_registration:
+  artifact_metadata:
+    uuid: 
+    handle: "providers/dcm/region-eu-dcm"
+    status: active
+
+  provider_type: dcm_provider
+  relationship_type: 
+
+  remote_dcm:
+    instance_uuid: 
+    endpoint: https://dcm.region-eu.corp.example.com
+    dcm_version: "2.1.0"          # minimum compatible version
+    sovereignty_declaration_ref:   # verified at registration time
+
+  # Authentication — always mTLS for DCM-to-DCM
+  auth:
+    mode: mtls
+    client_cert_ref:
+      service_provider_uuid: 
+      path: "dcm/dcm-providers/region-eu/client-cert"
+    server_ca_ref:
+      service_provider_uuid: 
+      path: "dcm/dcm-providers/region-eu/server-ca"
+
+  # Tunnel configuration
+  tunnel_config:
+    encrypted: true                # always — not configurable
+    sovereignty_boundary_check: true  # always — not configurable
+    permitted_resource_types: [Compute.VirtualMachine, Network.VLAN]
+    max_allocation_per_request: 10
+    audit_forwarding: true         # forward audit records to local Audit Store
+    observability_forwarding: true
+
+  # Sovereignty — must be compatible with local requirements
+  sovereignty_declaration:
+    remote_jurisdiction: eu-west
+    data_residency_guarantee: true
+    certifications: [ISO-27001, GDPR-compliant]
+
+  # Health check
+  health_check:
+    endpoint: /api/v1/health
+    interval_seconds: 60
+    on_unhealthy: suspend_allocations
+```
+
+### 4.3 Primary Concerns on All DCM Tunnels
+
+These are non-negotiable on every DCM-to-DCM connection:
+
+| Concern | Enforcement |
+|---------|-----------|
+| **Sovereignty** | Sovereignty_declaration verified before tunnel establishment; data classification checked before every egress |
+| **Authentication** | Always mTLS — no API key, no bearer token — mutual certificate authentication between DCM instances |
+| **Authorization** | Local DCM policies govern ALL resources obtained through a tunnel; remote DCM's policies do not override local |
+| **Audit** | All cross-DCM operations produce audit records in BOTH DCM instances; shared `correlation_id` links the two trails |
+| **Observability** | Cross-DCM resource allocation visible in both instances' observability stores |
+| **Governance** | Local GateKeeper policies apply to all resources from any tunnel source |
+
+---
+
+## 5. Cross-DCM Confidence Scoring
+
+Resources obtained through a DCM tunnel carry compound confidence scores — the resource's confidence in its source DCM, degraded by the tunnel trust score:
+
+```
+cross_dcm_confidence = source_resource_confidence × (tunnel_trust_score / 100)
+```
+
+A resource with confidence 90 in source DCM, through a tunnel with trust score 85: `90 × 85/100 = 76.5` → **77**
+
+### 5.1 Federation Trust Score
+
+```yaml
+dcm_federation_trust_score:
+  remote_dcm_uuid: 
+  score: 84                        # 0-100
+  scored_at: 
+  decay_rate: per_30_days
+  factors:
+    identity_verified: true        # mTLS certificate chain verified
+    sovereignty_compatible: true
+    certifications_current: true
+    audit_trail_integrity: true    # audit hash chains verified on sample
+    uptime_score: 0.98
+    compliance_score: 0.90         # policy compliance in recent operations
+    data_completeness: 0.92
+  action_on_score_below:
+    threshold: 60
+    action: 
+```
+
+---
+
+## 6. DCM Export and Import
+
+### 6.1 Export Package
+
+DCM state is fully exportable as a signed package — for disaster recovery, migration, cross-DCM sharing, and Hub DCM onboarding.
+
+```yaml
+dcm_export_package:
+  package_uuid: 
+  exported_at: 
+  exported_by: 
+  dcm_version: 
+  signed_by: 
+
+  scope:
+    tenants: [, ...]
+    resource_types: all
+    layers: [platform, tenant]
+    policies: [platform, tenant]
+    providers: registrations_only   # not credentials — never export credentials
+    entities: [intent_state, requested_state]   # not realized (that's provider state)
+    groups: all
+    audit_records:
+      date_range: [, ]
+      include_hash_chain: true      # for audit trail verification on import
+
+  sovereignty:
+    classification: internal
+    permitted_import_jurisdictions: [eu-west]
+    signed: true
+    encryption: aes256_gcm
+```
+
+### 6.2 Import Trust Score
+
+When importing from another DCM instance, each imported resource carries a trust score:
+
+```yaml
+import_trust_score:
+  score: 78                        # 0-100
+  factors:
+    source_dcm_verified: true      # source DCM identity verified
+    sovereignty_compatible: true
+    data_completeness: 0.92
+    schema_compatibility: 1.00     # source schema matches current version
+    audit_trail_complete: true     # audit records included and hash chain valid
+    certifications_current: true
+  action_on_low_score: 
+  threshold: 70                    # reject if below
+```
+
+### 6.3 Scoring in Resource Definition and Allocation
+
+Resources imported from or allocated through peer DCMs carry their compound confidence score throughout their lifecycle in the importing DCM. The score is visible to the placement engine, Cost Analysis, and the Policy Engine — enabling policies that prefer locally-managed resources over federated resources when scores are comparable.
+
+---
+
+## 7. Profile-Appropriate Federation Policy Groups
+
+DCM ships built-in federation policy groups activated by default per profile:
+
+| Group | Profile | Behavior |
+|-------|---------|---------|
+| `system/group/federation-minimal` | minimal | No federation — single instance only |
+| `system/group/federation-dev` | dev | Peer federation permitted; advisory only |
+| `system/group/federation-standard` | standard | Peer federation with certification requirements |
+| `system/group/federation-prod` | prod | Selective federation; bilateral approval; audit forwarding |
+| `system/group/federation-fsi` | fsi | Strict federation; within-jurisdiction only; full audit; no storage federation |
+| `system/group/federation-sovereign` | sovereign | No external federation; internal peer federation within sovereignty boundary only |
+
+---
+
+## 8. DCM System Policies — Federation
+
+| Policy | Rule |
+|--------|------|
+| `DCM-001` | DCM instances may establish peer, parent-child, or hub relationships using the Universal Group Model federation and nesting constructs. |
+| `DCM-002` | All DCM-to-DCM communication uses mTLS. No API key or bearer token. Sovereignty checks are mandatory before tunnel establishment. These requirements are non-configurable. |
+| `DCM-003` | Local DCM policies govern all resources obtained through DCM tunnels. Cross-DCM operations produce audit records in both DCM instances with a shared correlation_id. |
+| `DCM-004` | DCM state is exportable as a signed package. Imported packages carry a trust score (0-100) computed from source verification, sovereignty compatibility, data completeness, schema compatibility, and audit trail integrity. |
+| `DCM-005` | Resources obtained through DCM tunnels carry compound confidence scores: source_resource_confidence × (tunnel_trust_score / 100). |
+| `DCM-006` | Every provider registration must declare federation_eligibility (mode: none, selective, or open). Federation eligibility defaults are declared in platform domain layers. Individual provider registrations may be more restrictive — never more permissive without GateKeeper policy approval. |
+| `DCM-007` | Provider federation scope declares: permitted resource types, permitted operations per type, data sharing permissions, and allocation limits. Remote DCMs cannot decommission local resources through a federation tunnel. |
+| `DCM-008` | Storage providers default to federation_eligibility.mode: none. Data sovereignty constraints prohibit storage federation unless explicitly authorized by sovereign policy with full justification. |
+
+---
+
+## 9. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | How are DCM-to-DCM certificate rotation and renewal handled — coordinated or independent? | Operations | ✅ Resolved — independent rotation with P30D overlap; peer notification 60 days before expiry via Message Bus; auto-renewal at 90 days (DCM-009) |
+| 2 | Should Hub DCM relationships support automatic load balancing across child DCMs? | Architecture | ✅ Resolved — full placement engine logic at DCM instance level; sovereignty as hard pre-filter; tie-breaking hierarchy same as provider selection; sub-regional routing recursive (DCM-010) |
+| 3 | How does drift detection work for resources allocated from a peer DCM — who is responsible for discovery? | Operational | ✅ Resolved — provider-side DCM discovers; consumer-side DCM compares; events via federation Message Bus; peer unavailable = alert-and-hold (DCM-011) |
+| 4 | Should cross-DCM audit records be synchronized — so each DCM has the other's audit records? | Compliance | ✅ Resolved — correlation_id reference model; no full sync; on-demand pull with platform admin auth + sovereignty check (DCM-012) |
+| 5 | What is the maximum supported federation depth (peer of peer of peer)? | Architecture | ✅ Resolved — profile-governed max depth: minimal/dev=5, standard/prod=3, fsi/sovereign=2; measured as hops from deepest to Hub (DCM-013) |
+
+
+## 11. Federation Gap Resolutions
+
+### 11.1 Certificate Rotation and Renewal (Q1)
+
+DCM-to-DCM mTLS certificates rotate independently per instance with a coordinated notification model. Coordinated simultaneous rotation would create a single point of failure.
+
+```yaml
+dcm_federation_cert_rotation:
+  rotation_model: independent_with_overlap
+  overlap_period: P30D              # old cert valid for 30 days after new cert issued
+  notification:
+    notify_peers_at: P60D_before_expiry
+    notification_channel: message_bus
+    notification_payload:
+      new_cert_public_key: 
+      new_cert_valid_from: 
+      old_cert_expires_at: 
+  automatic_renewal:
+    trigger_at: P90D_before_expiry
+    requires_approval: false          # renewal is automatic; elevation requires approval
+```
+
+The P30D overlap allows peers to update their trust stores at their own pace without service interruption.
+
+### 11.2 Federation Routing — Placement Engine at the DCM Level (Q2)
+
+**Hub DCM federation routing follows the same placement engine logic as provider selection.** Regional DCMs are treated as DCM Provider instances in the placement engine.
+
+**Sovereignty is a hard pre-filter — not a preference:**
+
+```
+Before placement loop:
+  Filter eligible Regional DCMs where:
+    sovereignty_declaration satisfies request constraints
+    operating_jurisdictions includes required jurisdictions
+    sovereignty_zone matches tenant.sovereignty_zone
+  
+  If no eligible Regional DCMs → Reject with clear error
+  Only eligible DCMs enter the placement loop
+```
+
+**The full federation routing flow:**
+
+```
+Request arrives at Hub DCM
+  │
+  ▼ Steps 1-5: Standard nine-step assembly (layers, policies, placement constraints)
+  │  Pre-placement policies may declare federation routing constraints:
+  │    "This resource must be in a Regional DCM with EU sovereignty"
+  │    "This Tenant's resources must stay in Regional DCM-EU-West"
+  │
+  ▼ Step 6: Placement loop — at the DCM instance level
+  │  Reserve query to eligible Regional DCMs:
+  │    capacity available? sovereignty compatible? trust score adequate?
+  │
+  ▼ Tie-breaking (same hierarchy as provider selection):
+  │  1. Policy preference (policy declares preferred Regional DCM)
+  │  2. Federation priority (numeric priority on DCM Provider registration)
+  │  3. Tenant affinity (Tenant's resources prefer a specific Regional DCM)
+  │  4. Sovereignty match quality (exact match over partial match)
+  │  5. Geographic affinity (closest regional to consumer)
+  │  6. Least loaded (capacity utilization across instances)
+  │  7. Consistent hash (deterministic tiebreaker)
+  │
+  ▼ Selected Regional DCM receives assembled request payload
+  │  Runs its own local assembly and placement (regional layers, regional providers)
+  │  Returns realization result to Hub DCM → forwarded to consumer
+  │
+  ▼ Sub-regional routing: Regional DCM acts as Hub for its children
+    Same logic applies recursively within federation depth limit (DCM-013)
+```
+
+**Load balancing is the least-loaded step in the hierarchy** — not a primary strategy. Sovereignty, policy, and tenant affinity all take precedence. Optional `hub_dcm_load_balancing` configuration:
+
+```yaml
+hub_dcm_load_balancing:
+  enabled: true                     # default: true
+  sovereignty_override: true        # always — sovereignty is a hard pre-filter
+  fallback_on_regional_unavailable: route_to_next_eligible
+```
+
+### 11.3 Federated Drift Detection Ownership (Q3)
+
+Provider-side DCM is responsible for discovery; consumer-side DCM is responsible for drift comparison.
+
+```yaml
+federated_drift_detection:
+  discovery_responsibility: provider_side_dcm
+  comparison_responsibility: consumer_side_dcm
+  mechanism:
+    provider_dcm:
+      - Run standard discovery against its providers
+      - Publish Discovered State events to federation Message Bus
+      - Tagged with: entity_uuid + consumer_dcm_uuid + correlation_id
+    consumer_dcm:
+      - Subscribe to Discovered State events for its federated entities
+      - Compare against its Requested State
+      - Trigger drift response policy if drift detected
+  on_peer_dcm_unavailable:
+    action: alert_and_hold            # not assumed drift
+    max_hold_period: PT24H
+    on_hold_exceeded: escalate_to_platform_admin
+```
+
+### 11.4 Cross-DCM Audit Record Correlation (Q4)
+
+No full synchronization. Each DCM keeps its own authoritative audit trail. Cross-DCM correlation uses correlation_id references and on-demand pull.
+
+```yaml
+cross_dcm_audit_correlation:
+  model: correlation_id_reference
+  local_audit_record:
+    action: ALLOCATE_FROM_PEER
+    correlation_id: 
+    peer_dcm_uuid: 
+    peer_audit_record_uuid:     # reference — not a copy
+  on_demand_pull:
+    endpoint: GET /api/v1/audit/cross-dcm/{correlation_id}
+    requires: platform_admin + peer_dcm_authorization + sovereignty_check
+```
+
+Full synchronization is not required — auditors follow correlation_id to the peer DCM on demand.
+
+### 11.5 Maximum Federation Depth (Q5)
+
+```yaml
+federation_depth_policy:
+  max_depth: 3                        # profile-governed
+  on_max_exceeded: reject_federation_establishment
+  profile_defaults:
+    minimal: 5
+    dev: 5
+    standard: 3
+    prod: 3
+    fsi: 2
+    sovereign: 2
+```
+
+Depth is measured as hops from the deepest instance to the Hub DCM. Depth 3 covers Hub → Regional → Sub-Regional → Edge — sufficient for most real-world architectures.
+
+### 11.6 System Policies — Federation Gaps
+
+| Policy | Rule |
+|--------|------|
+| `DCM-009` | DCM-to-DCM mTLS certificates rotate independently per instance with a P30D overlap period. Peers are notified 60 days before expiry via Message Bus. Automatic renewal triggers 90 days before expiry. The overlap period allows peers to update trust stores without coordinated downtime. |
+| `DCM-010` | Hub DCM federation routing follows the same placement engine logic as provider selection. Sovereignty is a hard pre-filter — only Regional DCMs satisfying all sovereignty constraints enter the placement loop. The tie-breaking hierarchy applies at the DCM instance level: policy preference → federation priority → tenant affinity → sovereignty match quality → geographic affinity → least loaded → consistent hash. Regional DCMs are treated as DCM Provider instances. Sub-regional routing applies the same logic recursively within the federation depth limit. |
+| `DCM-011` | For resources allocated from peer DCMs, the provider-side DCM is responsible for discovery. The consumer-side DCM is responsible for drift comparison. Discovered State events are published via federation Message Bus with correlation_id. Peer DCM unavailability triggers alert-and-hold — not assumed drift. |
+| `DCM-012` | Cross-DCM audit records are referenced via correlation_id — not fully synchronized. Each DCM keeps its own authoritative audit trail. Cross-DCM correlation uses on-demand pull with platform admin authorization and sovereignty check. |
+| `DCM-013` | Federation depth is limited to a profile-governed maximum (default: 3 for standard/prod; 2 for fsi/sovereign; 5 for minimal/dev). Requests to establish federation beyond the maximum depth are rejected. Depth is measured as hops from the deepest instance to the Hub DCM. |
+
+
+---
+
+## 10. Related Concepts
+
+- **Universal Group Model** (doc 15) — federation and nesting group classes
+- **data stores** (doc 11) — storage never federated by default
+- **Auth Providers** (doc 19) — mTLS for DCM-to-DCM authentication
+- **Universal Audit Model** (doc 16) — audit records in both DCM instances; correlation_id
+- **Registry Governance** (doc 20) — signed bundles for air-gapped registry updates
+- **Information Providers Advanced** (doc 21) — confidence scoring used in cross-DCM context
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/23-notification-model.md b/content/docs/architecture/data-model/23-notification-model.md
new file mode 100644
index 0000000..e44e58a
--- /dev/null
+++ b/content/docs/architecture/data-model/23-notification-model.md
@@ -0,0 +1,601 @@
+# DCM Data Model — Notification Model
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: PROVIDER + POLICY**
+>
+> Provider: notification service. Policy: audience resolution and subscription rules
+
+
+**Related Documents:** [Webhooks, Messaging, and External Integration](18-webhooks-messaging.md) | [Entity Relationships](09-entity-relationships.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Auth Providers](19-auth-providers.md) | [Universal Audit](16-universal-audit.md)
+
+---
+
+
+> **See [Event Catalog](33-event-catalog.md)** — authoritative source for all DCM event types and payload schemas.
+
+## 1. Purpose
+
+The DCM Notification Model defines a **unified, configurable notification pipeline** that routes event notifications to all parties with a stake in a changed resource — not just the original requestor. The audience for any notification is derived from the **entity relationship graph**, not from who submitted the original request.
+
+This document defines:
+- The notification service — the ninth DCM provider type
+- The event taxonomy — a closed vocabulary of notification-worthy events
+- The audience resolution model — how the relationship graph determines who gets notified
+- The subscription model — how actors declare their notification preferences
+- The notification payload structure — the unified envelope all notification services receive
+- The delivery pipeline — from event trigger through audience resolution through provider delivery
+
+Outbound webhooks are one delivery channel within this model, implemented via the notification service.
+
+---
+
+## 2. Design Principles
+
+**Relationship graph determines audience.** When a resource changes, DCM traverses the entity relationship graph to find all stakeholders. A VLAN decommission notifies every VM attached to that VLAN, regardless of which Tenant owns each VM. The graph is the source of truth for notification scope.
+
+**Delivery mechanism is configurable, not prescribed.** DCM generates and routes notifications. How they are delivered — email, Slack, PagerDuty, ServiceNow, webhook, SMS — is the concern of a notification service. Organizations register the notification service(s) that fit their operations.
+
+**Three notification tiers.** Some notifications are mandatory and non-suppressable (security, sovereignty violations, audit chain breaks). Some are Tenant-default (all resource lifecycle events in a Tenant). Some are actor-subscription (specific events on specific resources). All three compose without conflict.
+
+**Audience role shapes the notification.** The same event produces different notifications for an owner ("your resource changed") versus a stakeholder ("a resource you depend on changed") versus an approver ("your approval is required"). The audience role is part of the notification envelope.
+
+**Delivery is audited.** Every notification dispatch is an audit record. Delivery failures are tracked and escalated per policy.
+
+---
+
+## 3. The notification service
+
+The notification service is the ninth formal DCM provider type. It handles the translation from DCM's unified notification envelope to the delivery channel's native format, and it handles delivery, retry, and delivery confirmation.
+
+### 3.1 Provider Types Table Update
+
+| # | Type | Purpose |
+|---|------|---------|
+| 1 | Service Provider | Realizes resources |
+| 2 | Information Provider | Serves authoritative external data |
+| 3 | compound service definition | Composes multiple providers |
+| 4 | data store | Persists DCM state |
+| 5 | event routing service | Event streaming and messaging |
+| 6 | External Policy Evaluator | External policy logic |
+| 7 | credential management service | Resolves secrets |
+| 8 | Auth Provider | Authenticates identities |
+| **9** | **notification service** | **Delivers notifications via configured channels** |
+
+### 3.2 notification service Registration
+
+```yaml
+service_provider_registration:
+  artifact_metadata:
+    uuid: 
+    handle: "org/notifications/slack-provider"
+    version: "1.0.0"
+    status: active
+    owned_by: { display_name: "Platform Engineering" }
+
+  provider_type: notification
+  display_name: "Slack notification service"
+  description: "Delivers DCM notifications to configured Slack channels"
+
+  # Delivery channels this provider supports
+  delivery_channels:
+    - channel_type: slack
+      config_schema_ref:           # JSON Schema for channel config
+      supports_threading: true
+      supports_urgency_routing: true     # different channels per urgency level
+    - channel_type: webhook
+      config_schema_ref: 
+
+  # Sovereignty declaration — same model as all providers
+  sovereignty_declaration:
+    data_residency_guarantee: EU
+    operating_jurisdictions: [DE, FR, NL]
+
+  # Delivery guarantees this provider offers
+  delivery_guarantees:
+    at_least_once: true
+    idempotency_key: notification_uuid
+    max_delivery_latency: PT30S        # for critical urgency
+    retry_policy:
+      max_attempts: 7
+      backoff: exponential
+      initial_interval: PT5S
+      max_interval: PT1H
+      on_exhaustion: dead_letter
+
+  # Health check endpoint (on the provider)
+  health_endpoint: https://notif-provider.corp.example.com/health
+
+  # Callback endpoint (DCM calls this to submit notifications)
+  delivery_endpoint: https://notif-provider.corp.example.com/deliver
+```
+
+### 3.3 Multiple notification services
+
+Organizations may register multiple notification services — one for Slack, one for PagerDuty, one for ServiceNow tickets. Notification subscriptions declare which provider to use for delivery. The Notification Router in DCM routes each notification to the correct provider based on the subscription's `service_provider_uuid`.
+
+---
+
+## 4. The Event Taxonomy
+
+The notification event taxonomy is a **closed vocabulary** — a finite, versioned set of event types that DCM can generate notifications for. Events are grouped by category. Subscriptions reference these event types by name.
+
+### 4.1 Request Lifecycle Events
+
+| Event Type | Trigger | Default Audience |
+|-----------|---------|-----------------|
+| `request.acknowledged` | Request received, Intent State created | Owner |
+| `request.requires_approval` | Policy requires human review before dispatch | Owner, Approvers |
+| `request.approved` | Intent State PR merged; proceeding to assembly | Owner |
+| `request.dispatched` | Requested State committed; dispatched to provider | Owner |
+| `request.completed` | Provider confirmed realization; Realized State written | Owner |
+| `request.failed` | Request failed at any stage | Owner |
+| `request.cancelled` | Consumer cancelled; request terminated | Owner |
+| `request.gatekeeper_rejected` | GateKeeper policy rejected the request | Owner, Policy Owner |
+
+### 4.2 Resource Lifecycle Events
+
+| Event Type | Trigger | Default Audience |
+|-----------|---------|-----------------|
+| `entity.realized` | Entity first realized by provider | Owner, Stakeholders (depth 1) |
+| `entity.state_changed` | Entity lifecycle state transition | Owner, Stakeholders (required) |
+| `entity.ttl_warning` | TTL expires within declared warning window | Owner |
+| `entity.ttl_expired` | TTL reached; expiry action triggered | Owner, Stakeholders (required) |
+| `entity.suspended` | Entity entered SUSPENDED state | Owner, Stakeholders (required) |
+| `entity.resumed` | Entity exited SUSPENDED state | Owner, Stakeholders (required) |
+| `entity.decommissioning` | Decommission initiated | Owner, Stakeholders (all) |
+| `entity.decommissioned` | Entity fully decommissioned | Owner, Stakeholders (all) |
+| `entity.decommission_deferred` | Decommission blocked by active stakes | Owner, Stakeholders (required) |
+| `entity.ownership_transferred` | Ownership moved to a different Tenant | Previous Owner, New Owner |
+| `entity.pending_review` | Entity entered PENDING_REVIEW state | Owner, Platform Admin |
+
+### 4.3 Drift and Discovery Events
+
+| Event Type | Trigger | Default Audience |
+|-----------|---------|-----------------|
+| `drift.detected` | Discovered State differs from Realized State | Owner |
+| `drift.severity_escalated` | Drift severity increased | Owner, Platform Admin |
+| `drift.resolved` | Drift resolved (REVERT or UPDATE_DEFINITION) | Owner |
+| `drift.escalated` | Drift escalated to human review | Owner, Platform Admin, SRE |
+| `unsanctioned_change.detected` | Change detected with no corresponding Requested State record | Owner, Security Team, Platform Admin |
+
+### 4.4 Provider Update Events
+
+| Event Type | Trigger | Default Audience |
+|-----------|---------|-----------------|
+| `provider_update.submitted` | Provider submitted an update notification | Owner |
+| `provider_update.requires_approval` | Provider update requires consumer approval | Owner (approval required) |
+| `provider_update.approved` | Provider update approved; Realized State updated | Owner |
+| `provider_update.rejected` | Provider update rejected; becomes drift | Owner, Provider Team |
+| `provider_update.auto_approved` | Provider update auto-approved by pre-authorization policy | Owner (informational) |
+
+### 4.5 Dependency and Relationship Events
+
+| Event Type | Trigger | Default Audience |
+|-----------|---------|-----------------|
+| `dependency.state_changed` | A required dependency's state changed | Owner of dependent entity |
+| `stakeholder.resource_decommissioning` | A shared resource the actor stakes is decommissioning | All stakeholders |
+| `allocation.pool_capacity_low` | Allocation pool capacity below threshold | Pool Owner, Platform Admin |
+| `allocation.released` | Allocation decommissioned; capacity returned to pool | Pool Owner |
+| `cross_tenant_auth.expiring` | Cross-tenant authorization expiring | Both Tenant Admins |
+| `cross_tenant_auth.revoked` | Cross-tenant authorization revoked while allocation active | Both Tenant Admins, Affected Resource Owners |
+
+### 4.6 Governance Events
+
+| Event Type | Trigger | Default Audience |
+|-----------|---------|-----------------|
+| `policy.activated` | Policy moved to active status | Platform Admin, Policy Owner |
+| `policy.deactivated` | Policy deactivated | Platform Admin, Policy Owner |
+| `external_policy_evaluation.trust_elevated` | External Policy Evaluator mode level elevated | Platform Admin, Security Team |
+| `profile.changed` | Active deployment profile changed | Platform Admin, All Tenant Admins |
+| `catalog_item.deprecated` | Catalog item deprecated | All consumers with active resources of that type |
+
+### 4.7 Security and System Events (Mandatory — Non-Suppressable)
+
+| Event Type | Trigger | Audience |
+|-----------|---------|---------|
+| `audit.chain_integrity_alert` | Hash chain verification failure detected | Security Team, Platform Admin |
+| `sovereignty.violation` | Resource in violation of sovereignty constraints | Platform Admin, Security Team, Resource Owner |
+| `sovereignty.migration_required` | Provider sovereignty change requires entity migration | Platform Admin, Resource Owner |
+| `federation.tunnel_degraded` | DCM-to-DCM federation tunnel health degraded | Platform Admin, SRE |
+| `auth.provider_failover` | Auth Provider failed over to secondary | Platform Admin |
+| `rehydration.blocked` | Concurrent rehydration attempt rejected | Requesting Actor, Platform Admin |
+| `security.unsanctioned_provider_write` | Attempted write to Realized Store without Requested State ref | Security Team, Platform Admin |
+
+---
+
+## 5. Audience Resolution — The Relationship Graph Model
+
+### 5.1 The Fundamental Rule
+
+**The audience for a notification is every entity with a stake in the changed resource, resolved by traversing the relationship graph from the changed entity.**
+
+The notification system does not maintain a separate subscriber list per entity. It derives the audience at event time by traversing the relationship graph. This means the audience is always current — adding a new VM attachment to a VLAN automatically includes that VM's owner in future VLAN notifications, without any subscription update required.
+
+### 5.2 Audience Resolution Algorithm
+
+```
+Event fires on entity E (e.g., VLAN-100 decommissioning)
+  │
+  ▼ Step 1: Resolve direct owner
+  │   entity.owned_by_tenant_uuid → Tenant Admin and resource owner actors
+  │   Audience role: owner
+  │
+  ▼ Step 2: Traverse relationship graph
+  │   For each relationship on entity E:
+  │     Check: is this relationship type notification-relevant for this event type?
+  │     Check: does the relationship's stake_strength meet the minimum for this event?
+  │     If yes: resolve the related entity's owner → add to audience
+  │     Audience role: stakeholder
+  │
+  ▼ Step 3: Check for approval requirements
+  │   Does this event require approval from a specific actor?
+  │   If yes: add approver to audience
+  │   Audience role: approver
+  │
+  ▼ Step 4: Apply mandatory system audiences
+  │   Security events: always include Security Team and Platform Admin
+  │   Governance events: always include Policy Owner and Platform Admin
+  │   (These cannot be filtered out by subscription preferences)
+  │
+  ▼ Step 5: Apply actor subscription overrides
+  │   Actors with explicit subscriptions to this event type → include/exclude per subscription
+  │   (Subscriptions can add additional audience; they cannot remove mandatory audiences)
+  │
+  ▼ Step 6: Deduplicate and resolve contact details
+  │   Same actor via multiple paths → one notification with all audience_roles listed
+  │   Resolve each actor to their configured notification channels
+  │
+  ▼ Step 7: Route to notification service(s)
+      One notification per actor per configured channel
+      Notification envelope includes audience_role
+```
+
+### 5.3 Relationship Notification Relevance
+
+The Resource Type Specification declares which relationship types are notification-relevant and for which events:
+
+```yaml
+resource_type_spec:
+  fqn: Network.VLAN
+  notification_rules:
+    - event_type: entity.decommissioning
+      notify_relationships:
+        - relationship_type: attached_to     # source direction (VMs attached to this VLAN)
+          min_stake_strength: required        # only required stakes get notified
+          traversal_depth: 1                  # direct relationships only
+          audience_role: stakeholder
+        - relationship_type: attached_to
+          min_stake_strength: optional        # optional stakes get informational notice
+          traversal_depth: 1
+          audience_role: observer
+
+    - event_type: entity.state_changed
+      notify_relationships:
+        - relationship_type: attached_to
+          min_stake_strength: required
+          traversal_depth: 1
+          audience_role: stakeholder
+```
+
+**Traversal depth:** `1` means direct relationships only. `2` means relationships of related entities. In most cases `1` is correct — deeper traversal is reserved for critical security events that affect the entire graph.
+
+### 5.4 Cross-Tenant Notification
+
+Notification traversal follows relationship graphs across Tenant boundaries. If a VM in AppTeam Tenant has a `required` stake in a VLAN owned by NetworkOps Tenant, and the VLAN is decommissioned, AppTeam receives a stakeholder notification — even though the VLAN belongs to a different Tenant.
+
+Cross-tenant notifications are governed by the same sovereignty rules as cross-tenant data access:
+- Notification content is limited to what the receiving Tenant is authorized to know
+- The notification identifies the changed resource but does not expose the owning Tenant's configuration details
+- Sovereignty checks apply to notification delivery (a notification about an EU-sovereign resource cannot be delivered to a US-based endpoint)
+
+```yaml
+notification_sovereignty_check:
+  # Before delivering cross-tenant notification:
+  check:
+    - receiver_tenant_sovereignty_compatible: true
+    - notification_content_authorized_for_receiver: true
+    - delivery_endpoint_jurisdiction_compatible: true
+  on_failure: redact_and_deliver   # or: suppress_with_audit | block_with_alert
+```
+
+---
+
+## 6. Notification Subscriptions
+
+### 6.1 Three Subscription Tiers
+
+**Tier 1 — Mandatory System Notifications (non-suppressable):**
+Security events, sovereignty violations, audit chain breaks. Always delivered to the declared system audiences (Security Team, Platform Admin) regardless of any subscription configuration. No actor or policy can suppress these.
+
+**Tier 2 — Tenant Default Notifications:**
+Configured by Tenant admins for all resources in their Tenant. Establishes the baseline notification behavior — which events trigger notifications, which channels to use, and which urgency mapping to apply.
+
+```yaml
+tenant_notification_defaults:
+  tenant_uuid: 
+  service_provider_uuid: 
+
+  default_channel_config:
+    channel_type: slack
+    workspace: "corp"
+    urgency_routing:
+      critical: "#platform-incidents"
+      high: "#platform-alerts"
+      medium: "#platform-notifications"
+      low: "#platform-digest"        # batched hourly
+
+  # Which event categories are enabled by default for all resources in this Tenant
+  enabled_event_categories:
+    request_lifecycle: [request.completed, request.failed, request.gatekeeper_rejected]
+    resource_lifecycle: [entity.state_changed, entity.ttl_warning, entity.decommissioning]
+    drift: [drift.detected, unsanctioned_change.detected]
+    provider_update: [provider_update.requires_approval, provider_update.rejected]
+    dependency: [stakeholder.resource_decommissioning, cross_tenant_auth.revoked]
+
+  # Urgency defaults per event type
+  urgency_overrides:
+    unsanctioned_change.detected: critical
+    drift.detected: high
+    entity.ttl_warning: medium
+    request.completed: low
+```
+
+**Tier 3 — Actor-Level Subscriptions:**
+Individual actors subscribe to specific events on specific resources or resource types. Most useful for service accounts (CI/CD pipelines, monitoring tools) that need targeted event feeds.
+
+```yaml
+actor_notification_subscription:
+  subscription_uuid: 
+  actor_uuid: 
+  service_provider_uuid: 
+
+  channel_config:
+    channel_type: pagerduty
+    service_id: "payments-api-on-call"
+    escalation_policy_id: "payments-prod"
+
+  subscriptions:
+    # Subscribe to all drift events on VMs in AppTeam Tenant
+    - scope:
+        tenant_uuid: 
+        resource_type: Compute.VirtualMachine
+      events: [drift.detected, unsanctioned_change.detected]
+      urgency_override: high
+
+    # Subscribe to decommission of a specific VLAN I depend on
+    - scope:
+        entity_uuid: 
+      events: [entity.decommissioning, entity.decommissioned]
+      urgency_override: critical
+```
+
+### 6.2 Subscription Composition Rules
+
+When multiple subscription tiers match for the same actor and event:
+- Mandatory system notifications always fire (cannot be suppressed)
+- Tenant defaults fire unless the actor's subscription explicitly opts out for that event type
+- Actor subscriptions can add additional channels or override urgency — they do not suppress Tenant defaults unless the subscription explicitly declares `suppress_tenant_default: true`
+- Deduplication: if the same notification would be delivered to the same actor via two channels from two subscription matches, deliver once per channel (not once per subscription match)
+
+---
+
+## 7. Notification Payload — The Unified Envelope
+
+Every notification delivered to a notification service uses this unified envelope. The notification service translates it to the delivery channel's native format.
+
+```yaml
+notification:
+  # Identity
+  notification_uuid:               # idempotency key
+  correlation_id:                  # links to the audit record for the triggering event
+  generated_at: 
+
+  # The event
+  event_type: entity.decommissioning     # from the closed taxonomy
+  event_uuid:                      # the triggering event's UUID
+  urgency: 
+
+  # The subject entity
+  entity:
+    uuid: 
+    handle: 
+    resource_type: Network.VLAN
+    display_name: "VLAN-100 (EU-WEST Production)"
+    tenant_uuid: 
+    tenant_display_name: "NetworkOps"
+
+  # Audience context
+  audience:
+    actor_uuid: 
+    actor_display_name: "Jane Smith"
+    audience_role: 
+    # stakeholder: explains WHY this actor is in the audience
+    stakeholder_reason:
+      via_entity_uuid:        # "because your VM-A is attached to this VLAN"
+      via_relationship_type: attached_to
+      via_entity_display_name: "VM-A (payments-api-server-01)"
+
+  # What changed
+  context:
+    previous_state: OPERATIONAL
+    new_state: DECOMMISSIONING
+    change_summary: "VLAN-100 decommission initiated by NetworkOps team"
+    changed_fields: []
+    changed_by:
+      actor_uuid: 
+      actor_display_name: "Bob Jones (NetworkOps)"
+    effective_at: 
+
+  # Action required (if any)
+  requires_action: false
+  action:
+    type: null                           # approve | acknowledge | migrate | release_stake
+    description: null
+    action_url: null
+    deadline: null
+
+  # Deep links
+  links:
+    entity_url: "https://dcm.corp.example.com/resources/"
+    event_url: "https://dcm.corp.example.com/audit/"
+    related_entities:
+      - uuid: 
+        display_name: "VM-A (payments-api-server-01)"
+        url: "https://dcm.corp.example.com/resources/"
+```
+
+### 7.1 Urgency Mapping
+
+| Urgency | Meaning | Typical delivery target |
+|---------|---------|------------------------|
+| `critical` | Immediate action required; outage or security risk imminent | On-call pager, incident channel |
+| `high` | Action required; significant impact if not addressed | Alert channel, SRE queue |
+| `medium` | Action recommended; non-urgent but should not be ignored | Notification channel, daily digest |
+| `low` | Informational; no action required | Digest, async channel |
+
+Default urgency per event type is declared in the event taxonomy. Tenant defaults and actor subscriptions may override upward or downward.
+
+---
+
+## 8. The Delivery Pipeline
+
+```
+Event fires (e.g., VLAN-100 enters DECOMMISSIONING state)
+  │
+  ▼ Stage 1: Audit record written (Stage 1 Commit Log — synchronous)
+  │   ENTITY_STATE_CHANGED audit record committed
+  │   event_uuid assigned
+  │
+  ▼ Stage 2: Notification Router evaluates
+  │   Load entity relationship graph for VLAN-100
+  │   Run audience resolution algorithm (Section 5.2)
+  │   Result: [AppTeam, DevTeam, OpsTeam] as stakeholders; [NetworkOps] as owner
+  │
+  ▼ Stage 3: Subscription resolution
+  │   For each audience member:
+  │     Resolve Tier 1 mandatory notifications
+  │     Apply Tier 2 Tenant defaults
+  │     Apply Tier 3 actor subscriptions
+  │     Determine: which notification service(s); which channel config; urgency
+  │
+  ▼ Stage 4: Notification envelope generation
+  │   One envelope per audience member per delivery
+  │   Audience role set correctly (owner / stakeholder / approver / observer)
+  │   Stakeholder reason populated for non-owners
+  │
+  ▼ Stage 5: Route to notification service(s)
+  │   POST to provider delivery endpoint with notification envelope
+  │   Provider translates to delivery channel (Slack, PagerDuty, email, etc.)
+  │   Provider returns delivery_uuid and status
+  │
+  ▼ Stage 6: Delivery confirmation
+  │   Provider reports: delivered | failed | queued
+  │   Delivery record written to Notification Delivery Store
+  │   NOTIFICATION_DISPATCHED audit record written (async)
+  │
+  ▼ Stage 7: Failure handling
+      On provider delivery failure:
+        Retry per provider's declared retry policy
+        On exhaustion: dead_letter to platform admin
+        On critical urgency exhaustion: escalate immediately
+        NOTIFICATION_DELIVERY_FAILED audit record written
+```
+
+### 8.1 Notification Delivery Store
+
+A lightweight store (not the Audit Store) tracking delivery status per notification:
+
+```yaml
+notification_delivery_record:
+  delivery_uuid: 
+  notification_uuid: 
+  actor_uuid: 
+  service_provider_uuid: 
+  channel_type: slack
+  status: 
+  dispatched_at: 
+  delivered_at: 
+  failure_reason: 
+  retry_count: 2
+```
+
+---
+
+## 9. Provider Update Notification Integration
+
+Provider Update Notifications (doc 06, Section 7a) integrate with the notification model at two points:
+
+**When provider submits update notification:**
+- `provider_update.submitted` fires → Owner notified (informational)
+
+**When provider update requires consumer approval:**
+- `provider_update.requires_approval` fires → Owner notified (action required)
+- `action.type: approve`
+- `action.action_url` points to `/api/v1/resources/{uuid}/provider-notifications/{uuid}:approve`
+- `action.deadline` set per policy (default PT24H — if no response, escalate)
+
+**On resolution:**
+- Approved → `provider_update.approved` fires → Owner notified; Stakeholders notified of state change via `entity.state_changed`
+- Rejected → `provider_update.rejected` fires → Owner notified; becomes drift event → `drift.detected` fires
+
+---
+
+## 10. Relationship to Webhooks and Message Bus
+
+### 10.1 Webhooks as a Notification Channel
+
+Outbound webhooks (doc 18) are now **one delivery channel type within the notification service model** rather than a parallel mechanism. A notification service with `channel_type: webhook` delivers notifications to configured HTTP endpoints using the unified notification envelope.
+
+The webhook registration model (doc 18, Section 3.2) is implemented as actor-level subscriptions (Section 6.1, Tier 3) with a webhook-type notification service. 
+
+### 10.2 Message Bus as Notification Infrastructure
+
+The event routing service (doc 18, Section 5) is the **internal transport** for the notification pipeline. The Notification Router publishes notification events to the Message Bus. notification services subscribe to their assigned topics. This decouples event generation from delivery and enables high-throughput notification processing.
+
+```
+DCM Event → Notification Router → Message Bus → notification service subscription
+```
+
+The Message Bus is infrastructure — not a notification channel. Consumers do not subscribe to the Message Bus directly for notifications; they use the subscription model (Section 6.1).
+
+---
+
+## 11. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `NOT-001` | The audience for every notification is derived from the entity relationship graph at event time. DCM does not maintain static subscriber lists per entity. |
+| `NOT-002` | Mandatory system notifications (Tier 1: security, sovereignty, audit chain) are never suppressable by any subscription configuration or policy. |
+| `NOT-003` | Cross-tenant notifications carry only information the receiving Tenant is authorized to see. Sovereignty checks apply to notification delivery endpoints. |
+| `NOT-004` | Every notification dispatch is an audit record. Delivery failures are tracked. Critical urgency delivery exhaustion triggers immediate escalation to Platform Admin. |
+| `NOT-005` | Provider Update Notifications that require consumer approval carry `action.type: approve` and `action.deadline`. If the deadline passes without resolution, the notification escalates per policy. |
+| `NOT-006` | Notification traversal depth is bounded. Resource Type Specifications declare the maximum traversal depth for each event type. Default: depth 1 (direct relationships only). |
+| `NOT-007` | A notification service must be registered and active before notifications can be delivered. DCM does not have a built-in delivery channel — at minimum a webhook-type notification service must be configured for external delivery. |
+| `NOT-008` | The notification event taxonomy is a closed vocabulary. Custom event types are not supported. New event types require a DCM registry proposal following standard governance. |
+
+---
+
+## 12. Related Concepts
+
+- **notification service** — the ninth DCM provider type; handles translation and delivery
+- **Notification Router** — DCM control plane component that resolves audiences and routes to providers
+- **Audience Resolution** — deriving notification recipients from the entity relationship graph
+- **Notification Subscription** — actor or Tenant declaration of notification preferences
+- **Notification Delivery Store** — lightweight store tracking delivery status
+- **Provider Update Notification** — formal provider mechanism for reporting authorized state changes (see doc 06, Section 7a)
+- **Outbound Webhook** — one delivery channel type within the notification service model
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/24-operational-models.md b/content/docs/architecture/data-model/24-operational-models.md
new file mode 100644
index 0000000..b51b07d
--- /dev/null
+++ b/content/docs/architecture/data-model/24-operational-models.md
@@ -0,0 +1,859 @@
+# DCM Data Model — Operational Models
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: POLICY**
+>
+> The Policy abstraction — Recovery Policy types, trigger vocabulary, action vocabulary
+
+
+**Related Documents:** [Four States](02-four-states.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Service Dependencies](07-service-dependencies.md) | [Policy Profiles](14-policy-profiles.md) | [Notification Model](23-notification-model.md)
+
+---
+
+## 1. Purpose
+
+This document defines the operational models that govern DCM behavior at the edges of the normal provisioning lifecycle — when things go wrong, take too long, or produce ambiguous outcomes. Four operational models are defined:
+
+1. **Timeout Model** — assembly, dispatch, and reserve-query timeouts
+2. **Cancellation Propagation Model** — consumer-initiated cancellation at any lifecycle stage
+3. **Discovery Scheduling Model** — what triggers discovery cycles and how they are managed
+4. **Recovery Policy Model** — the unified, policy-governed response to all failure and ambiguity scenarios
+
+The Recovery Policy Model is the foundational concept. Timeouts, cancellation outcomes, partial realization, and compensation failures all produce trigger conditions that Recovery Policies handle. Organizations declare their recovery posture via profile-bound Policy Groups — not via ad-hoc per-incident decisions.
+
+---
+
+## 2. Timeout Model
+
+### 2.1 Three Timeout Scopes
+
+There are three distinct timeout concerns in the DCM pipeline. Each is independently configurable and independently audited.
+
+```yaml
+timeout_declarations:
+  assembly_timeout:
+    description: "Maximum time for the Request Payload Processor to complete nine-step assembly"
+    profile_defaults:
+      minimal: PT5M
+      dev: PT5M
+      standard: PT3M
+      prod: PT2M
+      fsi: PT2M
+      sovereign: PT2M
+    on_timeout: trigger ASSEMBLY_TIMEOUT recovery policy
+    includes: layer_resolution, policy_evaluation, placement_engine_loop
+    # Assembly timeout fires if the total assembly pipeline exceeds this duration
+    # Individual sub-steps also have per-step timeouts (see below)
+
+  dispatch_timeout:
+    description: "Maximum time to wait for provider realization after dispatch"
+    profile_defaults:
+      minimal: PT2H
+      dev: PT1H
+      standard: PT1H
+      prod: PT30M
+      fsi: PT30M
+      sovereign: PT30M
+    resource_type_overrides:
+      # Some resource types legitimately take longer to provision
+      Compute.BareMetalServer: PT4H
+      Storage.LargeVolume: PT2H
+    on_timeout: trigger DISPATCH_TIMEOUT recovery policy
+
+  reserve_query_timeout:
+    description: "Maximum time for a single provider to respond to a reserve query"
+    profile_defaults:
+      minimal: PT30S
+      dev: PT30S
+      standard: PT10S
+      prod: PT5S
+      fsi: PT5S
+      sovereign: PT10S
+    on_timeout: skip this provider; continue placement loop with remaining candidates
+    # Reserve query timeout does not trigger RESERVE_QUERY_TIMEOUT recovery policy
+    # unless ALL candidates have timed out or been exhausted
+```
+
+### 2.2 Per-Step Assembly Sub-Timeouts
+
+The nine-step assembly has per-step sub-timeouts. These are not independently configurable — they are proportional fractions of the assembly_timeout:
+
+| Step | Fraction of assembly_timeout |
+|------|----------------------------|
+| Layer Resolution | 20% |
+| Layer Merge | 10% |
+| Policy Evaluation (each policy) | 15% total, 5% per Mode 1/2, 30s per Internal evaluation, PT2M per external evaluation |
+| Placement Engine Loop | 40% |
+| Requested State Persistence | 10% |
+
+A external policy evaluation that takes longer than PT2M per query causes an ASSEMBLY_TIMEOUT regardless of the overall assembly_timeout remaining. This prevents a single slow External Policy Evaluator from consuming the entire assembly budget.
+
+### 2.3 Timeout Audit Records
+
+Every timeout produces an audit record:
+
+```yaml
+audit_record:
+  action: ASSEMBLY_TIMEOUT | DISPATCH_TIMEOUT | RESERVE_QUERY_TIMEOUT
+  actor:
+    type: system
+    system_actor:
+      component: request_payload_processor | provider_dispatch | placement_engine
+      trigger: timeout
+  entity_uuid: 
+  details:
+    timeout_duration: 
+    actual_elapsed: 
+    step_at_timeout: 
+    recovery_policy_triggered: 
+```
+
+---
+
+## 3. Cancellation Propagation Model
+
+### 3.1 Three Cancellation Scenarios
+
+Cancellation behavior depends on the entity's lifecycle state at the time the consumer submits a cancellation request.
+
+**Scenario 1 — Cancel before dispatch (ACKNOWLEDGED → ASSEMBLING → AWAITING_APPROVAL):**
+
+```
+Consumer submits DELETE /api/v1/requests/{uuid}
+  │
+  ▼ Entity state: pre-DISPATCHED
+  │   Assembly halted immediately
+  │   No provider interaction required
+  │   Intent State record marked CANCELLED
+  │   Entity enters CANCELLED state (terminal)
+  │   Audit: REQUEST_CANCELLED
+  │   Recovery Policy: not triggered (clean cancel)
+  │
+  └── Response: 200 OK { "status": "CANCELLED" }
+```
+
+**Scenario 2 — Cancel after dispatch, provider not yet started (DISPATCHED):**
+
+```
+Consumer submits DELETE /api/v1/requests/{uuid}
+  │
+  ▼ Entity state: DISPATCHED (provider received payload but has not started)
+  │   DCM sends cancellation payload to provider cancel endpoint
+  │   Provider acknowledges: "not started, cancellation clean"
+  │   Entity enters CANCELLED state (terminal)
+  │   Recovery Policy: not triggered (clean cancel)
+  │
+  └── Response: 202 Accepted { "status": "CANCELLING" }
+      → status polling shows CANCELLED when provider confirms
+```
+
+**Scenario 3 — Cancel while provider is executing (PROVISIONING):**
+
+```
+Consumer submits DELETE /api/v1/requests/{uuid}
+  │
+  ▼ Entity state: PROVISIONING
+  │   DCM checks provider.supports_cancellation
+  │
+  ├── Provider supports cancellation:
+  │   DCM sends cancellation payload
+  │   Provider attempts rollback
+  │   ├── Rollback clean: entity → CANCELLED (terminal)
+  │   ├── Rollback partial: trigger CANCELLATION_FAILED recovery policy
+  │   └── No response: trigger CANCELLATION_FAILED recovery policy
+  │
+  └── Provider does not support cancellation:
+      Entity enters CANCEL_PENDING state
+      DCM waits for provider to complete
+      On provider REALIZED response:
+        Recovery Policy LATE_RESPONSE_RECEIVED fires
+        (configured action: typically DISCARD_AND_REQUEUE for cancellation context)
+      On provider FAILED response:
+        Entity → FAILED (terminal) — no compensation needed
+```
+
+### 3.2 Provider Cancellation Capability Declaration
+
+Providers declare cancellation support in their registration:
+
+```yaml
+provider_cancellation_capabilities:
+  supports_cancellation: true
+  cancellation_supported_during: [DISPATCHED, PROVISIONING]
+  # DISPATCHED: can cancel before work begins
+  # PROVISIONING: can cancel and roll back mid-execution
+  cancellation_endpoint: POST /api/v1/provider/entities/{entity_uuid}/cancel
+  cancellation_response_time: PT30S    # SLA for cancellation response
+  partial_rollback_possible: true
+  # true: cancellation may leave partial resources → CANCELLATION_FAILED path
+  # false: cancellation is all-or-nothing (rare)
+```
+
+### 3.3 Cancellation Payload
+
+```json
+{
+  "cancellation_uuid": "",
+  "entity_uuid": "",
+  "requested_state_uuid": "",
+  "reason": "consumer_requested | timeout | policy_triggered",
+  "requested_at": "",
+  "best_effort": true
+}
+```
+
+`best_effort: true` is always set — DCM never guarantees cancellation success. The provider makes a best-effort attempt; outcomes flow through the Recovery Policy model.
+
+---
+
+## 4. Discovery Scheduling Model
+
+### 4.1 The Discovery Scheduler Component
+
+The **Discovery Scheduler** is a DCM control plane component responsible for triggering discovery cycles. It maintains a priority queue of pending discovery requests and dispatches them to the appropriate Service Provider's discovery endpoint.
+
+The Discovery Scheduler is distinct from drift detection. The Discovery Scheduler triggers discovery and writes Discovered State. Drift Detection reads Discovered State and compares it to Realized State. These are separate, independent components.
+
+### 4.2 Three Discovery Trigger Types
+
+**Trigger Type 1 — Scheduled (cron-based):**
+
+Discovery schedules are declared in the Resource Type Specification and in provider registrations. The Discovery Scheduler runs these on the declared cadence.
+
+```yaml
+resource_type_spec:
+  fqn: Compute.VirtualMachine
+  discovery_schedule:
+    default_interval: PT15M      # discover VMs every 15 minutes
+    # Override by profile:
+    profile_overrides:
+      minimal: PT4H              # less frequent in home lab
+      fsi: PT5M                  # more frequent in regulated environments
+      sovereign: PT5M
+
+  # Per-provider discovery endpoint
+  discovery_endpoint_path: /api/v1/provider/discover
+  discovery_method: api_query    # api_query | passive_event | hybrid
+```
+
+```yaml
+provider_registration:
+  discovery_capabilities:
+    supports_discovery: true
+    discovery_endpoint: POST /api/v1/provider/entities/discover
+    max_entities_per_discovery_batch: 1000
+    discovery_latency_p95: PT10S    # how long discovery typically takes
+    supports_incremental_discovery: true
+    # incremental: only entities changed since last_discovery_timestamp
+    # full: all entities every time
+```
+
+**Trigger Type 2 — Event-triggered:**
+
+Specific DCM events automatically schedule an out-of-cycle discovery pass:
+
+```yaml
+event_triggered_discovery:
+  triggers:
+    - event: entity.realized
+      discovery_delay: PT30S           # allow provider to stabilize
+      scope: this_entity
+      reason: "Confirm realization matches Requested State"
+
+    - event: drift.resolved
+      discovery_delay: PT60S
+      scope: this_entity
+      reason: "Confirm remediation took effect"
+
+    - event: provider_update.approved
+      discovery_delay: PT30S
+      scope: this_entity
+      reason: "Confirm provider update is reflected in infrastructure"
+
+    - event: provider.degraded
+      discovery_delay: PT0S           # immediate
+      scope: all_entities_on_provider
+      reason: "Assess impact of provider degradation"
+
+    - event: TIMEOUT_PENDING          # recovery trigger
+      discovery_delay: PT5M
+      scope: this_entity
+      reason: "Orphan detection after timeout"
+
+    - event: COMPENSATION_FAILED
+      discovery_delay: PT0S           # immediate
+      scope: this_entity_and_dependents
+      reason: "Find orphaned resources after compensation failure"
+```
+
+**Trigger Type 3 — On-demand:**
+
+Platform admins and SREs can trigger discovery manually:
+
+```
+POST /api/v1/admin/discovery:trigger
+
+{
+  "scope": "entity | resource_type | provider | tenant",
+  "entity_uuid": "",          # if scope: entity
+  "resource_type": "",         # if scope: resource_type
+  "provider_uuid": "",         # if scope: provider
+  "tenant_uuid": "",           # if scope: tenant
+  "reason": "incident investigation",
+  "priority": "high"
+}
+```
+
+On-demand discovery is also used by:
+- The CI/CD pipeline pre-validation step (confirm current state before assembly)
+- The brownfield ingestion pipeline (initial discovery of existing infrastructure)
+- The orphan detection pipeline (targeted search for potentially-orphaned resources)
+
+### 4.3 Discovery Queue Management
+
+The Discovery Scheduler manages a priority queue. Priority order:
+
+1. **Critical** — COMPENSATION_FAILED orphan detection, sovereignty violation assessment
+2. **High** — on-demand from platform admin, event-triggered (provider.degraded)
+3. **Standard** — event-triggered (entity.realized, drift.resolved)
+4. **Background** — scheduled discovery passes
+
+Queue depth is bounded per profile. When the queue is full, new Background-priority items are dropped (with a log entry). Standard and above are never dropped — they wait.
+
+### 4.4 Discovery Audit
+
+Every discovery cycle produces an audit record:
+
+```yaml
+audit_record:
+  action: DISCOVERY_CYCLE_COMPLETED | DISCOVERY_CYCLE_FAILED
+  actor:
+    type: system
+    system_actor:
+      component: discovery_scheduler
+      trigger: scheduled | event_triggered | on_demand
+      trigger_event_uuid: 
+  entity_uuid:           # null for batch discovery
+  details:
+    entities_discovered: 47
+    new_entities_found: 2           # brownfield candidates
+    duration: PT8S
+```
+
+---
+
+## 5. Recovery Policy Model
+
+### 5.1 Recovery Policy as a Policy Type
+
+Recovery Policies are a formal DCM policy type alongside GateKeeper, Validation, and Transformation. They use the same authoring model, the same GitOps store, the same shadow mode validation, the same activation workflow, and the same audit trail.
+
+```yaml
+recovery_policy:
+  artifact_metadata:
+    uuid: 
+    handle: "system/recovery/discard-on-timeout"
+    version: "1.0.0"
+    status: active
+    owned_by: { display_name: "DCM Core Team" }
+
+  policy_type: recovery
+  trigger: DISPATCH_TIMEOUT         # the trigger condition this policy handles
+  action: DISCARD_AND_REQUEUE       # the action to take
+
+  # Optional additional conditions
+  conditions:
+    - field: entity.resource_type
+      operator: in
+      value: [Compute.VirtualMachine, Container.Pod]
+    - field: entity.owned_by_tenant.profile
+      operator: equals
+      value: prod
+
+  # Action parameters (depend on action type)
+  action_parameters:
+    requeue_delay: PT0S             # immediate requeue
+    notify_before_action: true
+    notification_urgency: high
+
+  # Deadline for NOTIFY_AND_WAIT actions
+  deadline: null                    # not applicable for DISCARD_AND_REQUEUE
+  on_deadline_exceeded: null
+```
+
+### 5.2 Trigger Vocabulary (Closed)
+
+| Trigger | Description |
+|---------|-------------|
+| `ASSEMBLY_TIMEOUT` | Assembly pipeline exceeded configured timeout |
+| `DISPATCH_TIMEOUT` | Provider did not respond within dispatch_timeout |
+| `RESERVE_QUERY_ALL_EXHAUSTED` | All placement candidates timed out or rejected |
+| `LATE_RESPONSE_RECEIVED` | Provider responded after DCM declared timeout |
+| `CANCELLATION_SENT` | DCM sent cancellation to provider |
+| `CANCELLATION_CONFIRMED` | Provider confirmed clean cancellation |
+| `CANCELLATION_FAILED` | Provider could not cancel; partial state possible |
+| `PARTIAL_REALIZATION` | Compound service partially realized |
+| `COMPENSATION_IN_PROGRESS` | Rollback of partial components underway |
+| `COMPENSATION_FAILED` | Rollback itself failed; orphaned resources possible |
+
+### 5.3 Action Vocabulary (Closed)
+
+| Action | Description |
+|--------|-------------|
+| `DRIFT_RECONCILE` | Schedule discovery pass; let drift detection resolve actual state via configured drift response policy |
+| `DISCARD_AND_REQUEUE` | Best-effort cleanup sent to provider; new request cycle created immediately from Intent State |
+| `DISCARD_NO_REQUEUE` | Best-effort cleanup sent to provider; entity FAILED; no automatic requeue |
+| `ACCEPT_LATE_REALIZATION` | Accept late provider response; write Realized State; entity proceeds to OPERATIONAL |
+| `COMPENSATE_AND_FAIL` | Execute compensation rollback for compound service; entity FAILED when complete |
+| `NOTIFY_AND_WAIT` | Fire notification to configured audience; wait for human decision up to deadline |
+| `ESCALATE` | Notify platform admin immediately; no automatic action |
+| `RETRY` | Retry the failed operation with configured backoff |
+
+### 5.4 The Four Built-in Recovery Profile Groups
+
+#### recovery-automated-reconciliation
+
+"Let the system converge on correct state — trust drift detection and policy."
+
+Appropriate for: standard and dev environments where operational continuity takes priority over strict consistency.
+
+```yaml
+recovery_policy_group:
+  handle: "system/group/recovery-automated-reconciliation"
+  concern_type: recovery_posture
+  policies:
+    - trigger: ASSEMBLY_TIMEOUT
+      action: RETRY
+      max_attempts: 3
+      backoff: exponential
+      initial_interval: PT30S
+      on_exhaustion: ESCALATE
+
+    - trigger: DISPATCH_TIMEOUT
+      action: DRIFT_RECONCILE
+      # Discovery finds what actually exists; drift response policy handles it
+
+    - trigger: LATE_RESPONSE_RECEIVED
+      action: ACCEPT_LATE_REALIZATION
+      # Provider did the work; accept it
+
+    - trigger: CANCELLATION_FAILED
+      action: DRIFT_RECONCILE
+      # Cannot confirm cleanup; discovery finds orphans
+
+    - trigger: PARTIAL_REALIZATION
+      action: DRIFT_RECONCILE
+      # Discover what's there; drift policy handles component gaps
+
+    - trigger: COMPENSATION_FAILED
+      action: ESCALATE
+      # Human needed when cleanup itself fails
+```
+
+#### recovery-discard-and-requeue
+
+"On any ambiguity, clean up and start fresh — prioritize consistency over continuity."
+
+Appropriate for: environments where reproducibility is paramount, resources are cheap to reprovision, untracked resources are a compliance concern.
+
+```yaml
+recovery_policy_group:
+  handle: "system/group/recovery-discard-and-requeue"
+  concern_type: recovery_posture
+  policies:
+    - trigger: ASSEMBLY_TIMEOUT
+      action: RETRY
+      max_attempts: 2
+      on_exhaustion: DISCARD_NO_REQUEUE
+
+    - trigger: DISPATCH_TIMEOUT
+      action: DISCARD_AND_REQUEUE
+      # Best-effort cleanup; new request cycle immediately
+
+    - trigger: LATE_RESPONSE_RECEIVED
+      action: DISCARD_AND_REQUEUE
+      # Provider completed after DCM moved on; discard that work
+      # (requeue already happened on timeout; this prevents duplicate resources)
+
+    - trigger: CANCELLATION_FAILED
+      action: DISCARD_NO_REQUEUE
+      # Cannot clean up; FAILED; human reviews orphans before requeue
+
+    - trigger: PARTIAL_REALIZATION
+      action: COMPENSATE_AND_FAIL
+      # Roll back everything; start fresh
+
+    - trigger: COMPENSATION_FAILED
+      action: ESCALATE
+      # Cannot even roll back; human needed
+```
+
+#### recovery-notify-and-wait
+
+"Never act automatically — always notify a human and wait for explicit authorization."
+
+Appropriate for: FSI and sovereign environments where automated resource creation or deletion has regulatory implications, where change control processes must be honored.
+
+```yaml
+recovery_policy_group:
+  handle: "system/group/recovery-notify-and-wait"
+  concern_type: recovery_posture
+  policies:
+    - trigger: ASSEMBLY_TIMEOUT
+      action: NOTIFY_AND_WAIT
+      deadline: PT2H
+      notification_urgency: high
+      on_deadline_exceeded: ESCALATE
+
+    - trigger: DISPATCH_TIMEOUT
+      action: NOTIFY_AND_WAIT
+      deadline: PT4H
+      notification_urgency: high
+      on_deadline_exceeded: ESCALATE
+
+    - trigger: LATE_RESPONSE_RECEIVED
+      action: NOTIFY_AND_WAIT
+      deadline: PT4H
+      notification_urgency: medium
+      on_deadline_exceeded: DISCARD_NO_REQUEUE
+
+    - trigger: CANCELLATION_FAILED
+      action: NOTIFY_AND_WAIT
+      deadline: PT8H
+      notification_urgency: high
+      on_deadline_exceeded: ESCALATE
+
+    - trigger: PARTIAL_REALIZATION
+      action: NOTIFY_AND_WAIT
+      deadline: PT8H
+      notification_urgency: high
+      on_deadline_exceeded: COMPENSATE_AND_FAIL
+
+    - trigger: COMPENSATION_FAILED
+      action: ESCALATE
+      # Always escalate compensation failures — no deadline
+```
+
+#### recovery-aggressive-retry
+
+"Retry everything before giving up — maximize first-time success rate."
+
+Appropriate for: environments with transient provider issues, where retries are cheap and manual intervention capacity is limited.
+
+```yaml
+recovery_policy_group:
+  handle: "system/group/recovery-aggressive-retry"
+  concern_type: recovery_posture
+  policies:
+    - trigger: ASSEMBLY_TIMEOUT
+      action: RETRY
+      max_attempts: 5
+      backoff: exponential
+      initial_interval: PT15S
+      max_interval: PT5M
+      on_exhaustion: NOTIFY_AND_WAIT
+      deadline: PT2H
+
+    - trigger: DISPATCH_TIMEOUT
+      action: RETRY
+      max_attempts: 3
+      backoff: linear
+      interval: PT5M
+      on_exhaustion: DRIFT_RECONCILE
+
+    - trigger: RESERVE_QUERY_ALL_EXHAUSTED
+      action: RETRY
+      max_attempts: 3
+      backoff: exponential
+      initial_interval: PT1M
+      on_exhaustion: ESCALATE
+
+    - trigger: PARTIAL_REALIZATION
+      action: RETRY
+      retry_scope: failed_components_only   # preserve succeeded components
+      max_attempts: 3
+      interval: PT15M
+      on_exhaustion: COMPENSATE_AND_FAIL
+
+    - trigger: CANCELLATION_FAILED
+      action: DRIFT_RECONCILE
+
+    - trigger: COMPENSATION_FAILED
+      action: ESCALATE
+```
+
+### 5.5 Profile Binding
+
+Recovery profile groups bind to deployment profiles as defaults, with override at Tenant and resource-type levels:
+
+```yaml
+profile_recovery_defaults:
+  minimal:    recovery-automated-reconciliation
+  dev:        recovery-automated-reconciliation
+  standard:   recovery-automated-reconciliation
+  prod:       recovery-notify-and-wait
+  fsi:        recovery-notify-and-wait
+  sovereign:  recovery-notify-and-wait
+
+# Tenant-level override
+tenant_config:
+  tenant_uuid: 
+  recovery_profile_override: recovery-discard-and-requeue
+
+# Resource-type-level override (most specific; wins over Tenant and profile)
+resource_type_recovery_override:
+  resource_type: Compute.VirtualMachine
+  recovery_profile: recovery-aggressive-retry
+  # VMs use aggressive retry; other types use Tenant/profile default
+```
+
+### 5.6 NOTIFY_AND_WAIT Consumer Interface
+
+When a recovery policy fires `NOTIFY_AND_WAIT`, a notification is sent to the entity owner with a time-bounded decision interface:
+
+```
+GET /api/v1/resources/{entity_uuid}/recovery-decisions
+
+Response:
+{
+  "recovery_decision_uuid": "",
+  "trigger": "DISPATCH_TIMEOUT",
+  "entity_uuid": "",
+  "deadline": "",
+  "available_actions": [
+    {
+      "action": "DRIFT_RECONCILE",
+      "description": "Let discovery determine actual state and reconcile automatically"
+    },
+    {
+      "action": "DISCARD_AND_REQUEUE",
+      "description": "Best-effort cleanup, then requeue as a new request"
+    },
+    {
+      "action": "DISCARD_NO_REQUEUE",
+      "description": "Best-effort cleanup only; no automatic requeue"
+    }
+  ]
+}
+
+POST /api/v1/resources/{entity_uuid}/recovery-decisions/{recovery_decision_uuid}
+{
+  "action": "DISCARD_AND_REQUEUE",
+  "reason": "Provider was known to be degraded at time of timeout"
+}
+```
+
+Platform admins may also use the Admin API to resolve pending recovery decisions for any entity regardless of Tenant.
+
+### 5.7 Recovery Policy Evaluation Precedence
+
+The Policy Engine evaluates recovery policies in domain precedence order, same as all other policies:
+
+```
+1. Resource-type-level override (most specific)
+2. Tenant-level override
+3. Active profile's recovery posture group
+4. System default (automated-reconciliation)
+
+First matching policy for the trigger condition wins.
+Multiple recovery policies for the same trigger at the same domain level
+→ policy conflict; CONFLICT_ERROR at ingestion; platform admin notified.
+```
+
+---
+
+## 6. Compound Service Compensation Model
+
+### 6.1 Compensation Declaration in Service Dependencies
+
+Each component in a compound service declares its compensation behavior:
+
+```yaml
+compound_service_spec:
+  service_type: ApplicationStack.WebApp
+  components:
+    - id: vm
+      resource_type: Compute.VirtualMachine
+      required_for_delivery: atomic        # must succeed; failure triggers compensation
+      compensation_on_failure: decommission_immediately
+      compensation_order: 3                # decommissioned last (highest number = last)
+
+    - id: ip
+      resource_type: Network.IPAddress
+      required_for_delivery: atomic
+      compensation_on_failure: release_allocation
+      compensation_order: 1                # decommissioned first
+      depends_on: []
+
+    - id: dns
+      resource_type: DNS.Record
+      required_for_delivery: partial       # failure → DEGRADED, not FAILED
+      compensation_on_failure: skip        # DNS failure doesn't trigger VM decommission
+      depends_on: [vm, ip]
+
+    - id: loadbalancer
+      resource_type: Network.LoadBalancer
+      required_for_delivery: partial
+      compensation_on_failure: skip
+      depends_on: [vm, ip]
+
+  partial_delivery_policy:
+    min_required_components: [vm, ip]     # compound DEGRADED if only these succeed
+    degraded_is_acceptable: true          # DEGRADED entity is delivered; not FAILED
+    auto_retry_optional_components:
+      enabled: true
+      max_attempts: 3
+      interval: PT15M
+      on_exhaustion: notify_owner
+```
+
+### 6.2 Compensation Execution Order
+
+Compensation always runs in reverse dependency order — last-provisioned is first-decommissioned:
+
+```
+Successful so far: vm ✓, ip ✓
+Failed: dns ✗ (atomic)
+Compensation triggered:
+  Step 1: decommission vm (compensation_order: 3 → runs first in reverse)
+  Step 2: release ip allocation (compensation_order: 1 → runs second in reverse)
+  Compound entity → FAILED (terminal for this request cycle)
+```
+
+### 6.3 Compensation Failure
+
+If a compensation step fails (the VM decommission itself fails):
+
+```
+Compensation of vm FAILED
+  Entity enters COMPENSATION_FAILED state
+  COMPENSATION_FAILED recovery policy fires:
+    default: ESCALATE to platform admin
+  Orphan detection triggered immediately:
+    Scoped to provider + entity characteristics
+    Finds the VM that couldn't be decommissioned
+    Creates ORPHAN_CANDIDATE record
+  Platform admin reviews:
+    Manually decommission at provider
+    OR adopt into DCM lifecycle as a new entity
+```
+
+---
+
+## 7. Orphan Detection Pipeline
+
+When cleanup cannot be guaranteed, DCM runs an orphan detection pass to find resources that may have been provisioned but have no corresponding Realized State record.
+
+### 7.1 Orphan Detection Triggers
+
+- Dispatch timeout with cancellation sent
+- Cancellation failed
+- Compensation failed
+- DISCARD_NO_REQUEUE action taken
+- Manual platform admin trigger
+
+### 7.2 Orphan Detection Query
+
+```yaml
+orphan_detection_query:
+  provider_uuid: 
+  time_window:
+    from: 
+    to: 
+  match_criteria:
+    resource_type: 
+    characteristics:             # key fields from the Requested State
+      name_pattern: 
+      size_class: 
+      tags: 
+  exclude:
+    known_realized_state_uuids: [, ...]   # entities DCM knows about
+```
+
+### 7.3 Orphan Candidate Lifecycle
+
+```yaml
+orphan_candidate:
+  orphan_candidate_uuid: 
+  suspected_request_uuid:      # the request that may have created this
+  provider_entity_id:        # what the provider calls it
+  provider_uuid: 
+  discovered_at: 
+  characteristics: { ... }
+  status: 
+  resolution:
+    action: 
+    resolved_by: 
+    resolved_at: 
+```
+
+Orphan candidates are surfaced in the Platform Admin dashboard and generate a NOTIFICATION (audience: Platform Admin) with urgency: high.
+
+---
+
+## 8. New Lifecycle States
+
+Five new states are added to the Infrastructure Resource Entity lifecycle:
+
+| State | Meaning | Recovery Policy Trigger |
+|-------|---------|------------------------|
+| `TIMEOUT_PENDING` | Dispatch timeout fired; cancellation sent; awaiting outcome | `DISPATCH_TIMEOUT` |
+| `LATE_REALIZATION_PENDING` | Provider responded after timeout; NOTIFY_AND_WAIT active | `LATE_RESPONSE_RECEIVED` |
+| `INDETERMINATE_REALIZATION` | State is ambiguous; drift detection resolving | — (drift detection runs) |
+| `COMPENSATION_IN_PROGRESS` | Compound service rollback underway | — |
+| `COMPENSATION_FAILED` | Rollback itself failed; orphaned resources possible | `COMPENSATION_FAILED` |
+
+Updated state machine (additions to doc 02 and doc 06):
+
+```
+Normal flow:
+REQUESTED → PENDING → PROVISIONING → REALIZED → OPERATIONAL → DECOMMISSIONED
+
+Recovery states:
+PROVISIONING → [timeout] → TIMEOUT_PENDING
+  TIMEOUT_PENDING → [late response + NOTIFY_AND_WAIT] → LATE_REALIZATION_PENDING
+  TIMEOUT_PENDING → [DRIFT_RECONCILE] → INDETERMINATE_REALIZATION
+  TIMEOUT_PENDING → [DISCARD_AND_REQUEUE] → FAILED + new REQUESTED (new cycle)
+
+PROVISIONING → [partial failure] → COMPENSATION_IN_PROGRESS
+  COMPENSATION_IN_PROGRESS → [all compensated] → FAILED
+  COMPENSATION_IN_PROGRESS → [compensation fails] → COMPENSATION_FAILED
+
+LATE_REALIZATION_PENDING → [human accepts / ACCEPT_LATE] → REALIZED → OPERATIONAL
+LATE_REALIZATION_PENDING → [human discards / DISCARD] → FAILED
+
+INDETERMINATE_REALIZATION → [drift reconciles] → REALIZED or FAILED
+COMPENSATION_FAILED → [human resolves] → FAILED (after manual cleanup)
+```
+
+---
+
+## 9. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `OPS-010` | Assembly timeout, dispatch timeout, and reserve-query timeout are independently configurable. All are profile-governed with resource-type overrides permitted for types with legitimately long provisioning times. |
+| `OPS-011` | Cancellation is always best-effort. DCM never guarantees cancellation success. All cancellation outcomes flow through the Recovery Policy model. |
+| `OPS-012` | Provider cancellation capability is declared at registration. Providers that do not support cancellation use the CANCEL_PENDING → LATE_RESPONSE_RECEIVED path when a cancel is requested during PROVISIONING. |
+| `OPS-013` | Discovery is triggered by three independent mechanisms: scheduled (cron), event-triggered, and on-demand. All three write to the Discovered Store independently. |
+| `OPS-014` | Recovery Policies are a formal DCM policy type. They use the same authoring, activation, shadow mode, and audit model as GateKeeper, Validation, and Transformation policies. |
+| `OPS-015` | Four built-in recovery profile groups are provided: recovery-automated-reconciliation, recovery-discard-and-requeue, recovery-notify-and-wait, recovery-aggressive-retry. |
+| `OPS-016` | Recovery profile defaults are bound to deployment profiles. Organizations may override at Tenant or resource-type level. Resource-type override wins over Tenant override wins over profile default. |
+| `OPS-017` | Compound service compensation runs in reverse dependency order. Compensation failure triggers COMPENSATION_FAILED state and immediate orphan detection. |
+| `OPS-018` | Orphan detection triggers on any path where cleanup cannot be guaranteed. Orphan candidates are surfaced to platform admin with urgency: high. |
+| `OPS-019` | NOTIFY_AND_WAIT recovery actions carry a deadline. If the deadline passes without human resolution, the configured on_deadline_exceeded action fires automatically. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/25-control-plane-components.md b/content/docs/architecture/data-model/25-control-plane-components.md
new file mode 100644
index 0000000..b45248e
--- /dev/null
+++ b/content/docs/architecture/data-model/25-control-plane-components.md
@@ -0,0 +1,821 @@
+# DCM Data Model — Control Plane Components
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: RUNTIME**
+>
+> Runtime implementations of the three abstractions — not a fourth abstraction
+
+
+**Related Documents:** [Internal Component Authentication](36-internal-component-auth.md) | [Context and Purpose](00-context-and-purpose.md) | [Four States](02-four-states.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Operational Models](24-operational-models.md) | [Policy Profiles](14-policy-profiles.md)
+
+---
+
+
+The DCM Control Plane consists of **nine components** that implement the three foundational abstractions at runtime.
+
+## 1. Purpose
+
+> **Internal component authentication:** See [Internal Component Authentication](36-internal-component-auth.md) for the mTLS and interaction credential model governing all component-to-component calls within the DCM control plane.
+
+
+This document formally defines the DCM control plane components that are referenced throughout the data model documents. Two components are defined here:
+
+1. **The Request Orchestrator** — the event bus and coordinator of the request lifecycle pipeline
+2. **The Cost Analysis Component** — the internal DCM component that provides cost signals for placement, catalog, and attribution
+
+---
+
+## 2. The Request Orchestrator
+
+### 2.1 Role
+
+The Request Orchestrator is the **event bus and pipeline coordinator** for all DCM request lifecycle operations. It does not perform any pipeline work itself — it listens for events, evaluates which components need to act on them, and routes work to the appropriate components.
+
+The Request Orchestrator embodies DCM's **data-driven, policy-triggered orchestration model**: the pipeline is not a fixed procedural sequence. It is a cascade of event-condition-action responses, where policies define what happens when specific payload states are observed.
+
+### 2.2 Data-Driven Orchestration Principle
+
+**Policies ARE the orchestration.** The Request Orchestrator does not contain hardcoded pipeline logic. It publishes events to the Policy Engine; policies match on payload type and state; policy actions produce new payload states; those new states trigger further policy evaluations.
+
+This means:
+- Adding a new pipeline step = writing a new policy (no code change)
+- Removing a step = deactivating a policy
+- Changing when a step fires = changing a policy condition
+- A static workflow (e.g., always require human approval for prod VMs) = a policy that always matches for those conditions
+- A dynamic workflow (e.g., route to different approval processes based on cost) = a policy with conditional logic
+
+Static and dynamic flows compose naturally — a static policy defines a guaranteed step; a dynamic policy defines a conditional step. Both are expressed as policies, evaluated by the same engine, producing deterministic outcomes.
+
+**Determinism guarantee:** Dynamic execution remains deterministic because:
+- The payload type vocabulary is a closed set
+- Policy evaluation order within a domain level is deterministic (domain precedence)
+- The payload mutation model is immutable (each policy produces a new payload version)
+- The same input state always produces the same output state
+
+### 2.3 The Payload Type Vocabulary
+
+Every event in DCM carries a payload with a declared type. Policies pattern-match on these types. The payload type vocabulary is the foundational contract of the orchestration model.
+
+```yaml
+payload_types:
+  # Request lifecycle
+  request.initiated:          # consumer submitted a request
+  request.intent_captured:    # Intent State written
+  request.layers_assembled:   # layer assembly complete
+  request.policies_evaluated: # all active policies evaluated
+  request.placement_complete: # provider selected
+  request.dispatched:         # sent to provider
+  request.realized:           # provider confirmed realization
+  request.failed:             # terminal failure
+  request.cancelled:          # cancelled
+
+  # Provider update
+  provider_update.received:   # provider submitted update notification
+  provider_update.evaluated:  # policy evaluation complete
+  provider_update.accepted:   # accepted; Realized State updating
+  provider_update.rejected:   # rejected; becomes drift
+
+  # Drift and discovery
+  discovery.cycle_complete:
+  drift.detected:
+  drift.resolved:
+
+  # Recovery
+  recovery.timeout_fired:
+  recovery.late_response:
+  recovery.compensation_triggered:
+
+  # Governance
+  policy.activated:
+  layer.updated:
+  profile.changed:
+```
+
+### 2.4 Event Routing Model
+
+```
+Event published: { type: "request.initiated", payload: {...}, entity_uuid: X }
+  │
+  ▼ Request Orchestrator receives event
+  │   Routes to Policy Engine: "evaluate all policies matching request.initiated"
+  │
+  ▼ Policy Engine evaluates in domain precedence order
+  │   Matching policies fire; payload mutations accumulated
+  │   New payload state produced: { type: "request.layers_assembled", ... }
+  │
+  ▼ Request Orchestrator receives new event
+  │   Routes to Policy Engine for next evaluation cycle
+  │   (parallel if no data dependencies between active policies)
+  │
+  ▼ Continues until terminal state (request.realized or request.failed)
+```
+
+**Parallel execution:** Policies that have no data dependencies on each other evaluate concurrently. The Request Orchestrator tracks dependency declarations between policies and executes in parallel where safe.
+
+### 2.5 Static Flow Support
+
+Organizations that require guaranteed sequential flows express them as ordered policy sets:
+
+```yaml
+static_flow_policy_group:
+  handle: "org/flows/prod-vm-approval-flow"
+  concern_type: orchestration_flow
+  ordered: true                # policies execute in declared sequence, not parallel
+  policies:
+    - step: 1
+      handle: "org/policies/cost-check"
+      condition: "request.initiated AND resource_type=Compute.VirtualMachine AND tenant.profile=prod"
+      on_fail: halt
+    - step: 2
+      handle: "org/policies/manager-approval"
+      condition: "request.cost_estimated > 500"
+      on_fail: halt
+    - step: 3
+      handle: "org/policies/security-review"
+      condition: "always"
+      on_fail: halt
+```
+
+A static flow is a Policy Group with `concern_type: orchestration_flow` and `ordered: true`. The Request Orchestrator respects the declared order. Static flows integrate with dynamic policies — a dynamic policy can fire alongside the static flow steps.
+
+### 2.5a Named Workflows vs Dynamic Policies — How They Compose
+
+The Request Orchestrator does not distinguish between named workflows and dynamic policies — both arrive as events and are routed to the Policy Engine. The distinction is in *how they are declared*:
+
+**Named Workflow Artifacts** (Orchestration Flow Policies with `ordered: true`) declare an explicit step sequence. An operator reading the workflow can see every step in order. Steps reference payload types from the closed vocabulary. Named workflows are the *explicit, visible skeleton* of a process.
+
+**Dynamic Policies** (GateKeeper, Transformation, Recovery) fire when their match conditions are satisfied, regardless of workflow position. They are not declared in the workflow artifact. They are the *conditional behavior* that fills in the skeleton.
+
+**Example — request lifecycle:**
+```
+Named workflow "system/workflows/request-lifecycle" declares:
+  Step 1: request.initiated    → capture intent
+  Step 2: request.intent_captured → run layer assembly
+  Step 3: request.layers_assembled → run placement
+  Step 4: request.placement_complete → dispatch
+
+Dynamic policies also fire:
+  GateKeeper "vm-size-limits" fires on request.layers_assembled
+    if cpu_count > 32 → deny
+  Transformation "inject-monitoring" fires on request.layers_assembled
+    → adds monitoring_endpoint field
+  Recovery "notify-on-timeout" fires on recovery.timeout_fired
+    → NOTIFY_AND_WAIT action
+```
+
+The named workflow and the dynamic policies are independent artifacts. Adding a new GateKeeper does not modify the workflow. Modifying the workflow does not affect dynamic policies. They compose through the same Policy Engine evaluation on the same events.
+
+### 2.6 Request Orchestrator Responsibilities
+
+| Responsibility | Description |
+|----------------|-------------|
+| Event routing | Receive all request lifecycle events; route to appropriate components |
+| Pipeline coordination | Sequence component interactions per data dependencies |
+| Timeout monitoring | Track dispatch_timeout and assembly_timeout; fire recovery triggers |
+| Dependency resolution | For compound services, sequence component provisioning per dependency graph |
+| Status tracking | Maintain current status of all in-flight requests; respond to status queries |
+| Recovery coordination | On timeout/failure, invoke Recovery Policy evaluation |
+
+---
+
+## 3. The Cost Analysis Component
+
+### 3.1 Role
+
+The Cost Analysis Component is an **internal DCM control plane component** that provides cost signals to other components. It is not a billing system and not a provider type. It does not manage financial transactions, produce invoices, or serve as the authoritative financial record. It provides cost *signals* that DCM uses for placement decisions, pre-request estimation, and ongoing attribution.
+
+The authoritative billing record lives in the organization's financial system. A billing system can register as an Information Provider to push authoritative cost data back into DCM for attribution records.
+
+### 3.2 Three Cost Functions
+
+**Function 1 — Pre-request cost estimation:**
+Given a catalog item and assembled field values, compute the estimated lifecycle cost. Used by:
+- Service Catalog describe endpoint (consumer sees cost before requesting)
+- CI pipeline pre-validation (cost estimate in PR comment)
+- Placement engine tie-breaker step 4 (cheapest eligible provider)
+
+**Function 2 — Placement cost input:**
+During Step 6 placement, provide current cost data per eligible provider for the requested resource type. If Cost Analysis data is unavailable, the placement engine falls back to static declared costs per REG-011.
+
+**Function 3 — Ongoing cost attribution:**
+For realized entities, track ongoing consumption and attribute costs to the owning Tenant. Consumed by OBS-005 (consumer cost view) and the resource describe endpoint (`estimated_cost_per_hour` field).
+
+### 3.3 Cost Data Sources
+
+The Cost Analysis Component ingests cost data from two sources, following the REG-011 hybrid model:
+
+```yaml
+cost_data_sources:
+  static:
+    source: provider_registration     # declared at provider registration time
+    update_frequency: manual          # updated when rates change
+    fields: [capex_per_unit, opex_per_unit_per_hour, currency]
+
+  dynamic:
+    source: external_cost_api         # external billing API or cloud pricing API
+    registered_as: information_provider
+    query_interval: PT1H
+    fallback: static                  # use static if dynamic unavailable
+    fallback_max_age: PT24H
+```
+
+### 3.4 Cost Estimation Model
+
+```yaml
+cost_estimation_request:
+  catalog_item_uuid: 
+  assembled_fields:
+    cpu_count: 4
+    memory_gb: 8
+    storage_gb: 100
+  tenant_uuid: 
+  requested_duration: P30D           # optional; lifecycle estimate
+
+cost_estimation_response:
+  estimated_cost:
+    per_hour: 0.32
+    per_month: 230.40
+    lifecycle_estimate: 691.20       # if requested_duration provided
+    currency: USD
+    confidence: high                 # high: current Cost Analysis data
+                                     # medium: data > PT1H old
+                                     # low: static fallback
+    breakdown:
+      - component: compute
+        per_hour: 0.28
+      - component: ip_allocation
+        per_hour: 0.04
+  cost_data_timestamp: 
+```
+
+### 3.5 Cost Attribution for Realized Entities
+
+```yaml
+entity_cost_attribution:
+  entity_uuid: 
+  tenant_uuid: 
+  billing_state: billable            # billable | non_billable | reduced_rate
+  current_rate:
+    per_hour: 0.32
+    currency: USD
+    rate_effective_since: 
+  monthly_accrual: 230.40
+  cost_data_source: cost_analysis    # cost_analysis | static | unknown
+```
+
+### 3.6 Integration with Placement Engine
+
+The placement engine queries Cost Analysis at step 4 of the tie-breaking hierarchy:
+
+```
+Step 4 — Cost Analysis (if available and determinable):
+  Query Cost Analysis for each eligible provider
+  Cost Analysis returns: estimated cost per unit per provider
+  Placement engine prefers lowest cost among equally-ranked candidates
+  If Cost Analysis unavailable: skip step 4; proceed to step 5
+  # Cost Analysis unavailability never blocks placement
+```
+
+---
+
+## 4. Related Policies
+
+| Policy | Rule |
+|--------|------|
+| `CTL-001` | The Request Orchestrator is the single event bus for all request lifecycle events. No component communicates directly with another component outside of events published to the Request Orchestrator. |
+| `CTL-002` | Policies ARE the orchestration. The Request Orchestrator does not contain hardcoded pipeline logic. Pipeline behavior is modified by adding, removing, or changing policies — not by changing the orchestrator. |
+| `CTL-003` | Dynamic and static flows compose naturally. Static flows are Policy Groups with concern_type: orchestration_flow and ordered: true. Both types are evaluated by the same Policy Engine. |
+| `CTL-004` | Cost Analysis is not a billing system. It provides cost signals for placement and attribution. The authoritative billing record lives in the organization's financial system, which may register as an Information Provider. |
+| `CTL-005` | Cost Analysis unavailability never blocks placement. The placement engine falls back to static declared costs per REG-011 and skips the Cost Analysis tie-breaking step. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+---
+
+## 4. The Placement Engine
+
+### 4.1 Role
+
+The Placement Engine selects the specific Service Provider that will fulfill a resource request. It runs as step 6 of the Request Payload Processor assembly pipeline and is invoked by the Request Orchestrator after all policies have been evaluated and the assembled payload is ready for dispatch.
+
+The Placement Engine does not make business decisions — those are made by policies (which inject constraints and preferences). The Placement Engine applies those constraints to find eligible providers, then deterministically resolves ties using a declared hierarchy.
+
+### 4.2 Input and Output
+
+**Input:**
+- Assembled payload with full field-level provenance
+- Sovereignty constraints (from compliance domain profile and any policy-injected constraints)
+- Accreditation requirements (from Data/Capability Authorization Matrix)
+- Preference scores or preferred provider UUIDs (if injected by Transformation policy)
+- Tenant affinity declarations
+
+**Output:**
+- Selected provider UUID
+- Placement reason (why this provider was selected)
+- Sovereignty satisfaction record (which constraints were checked and passed)
+- Reserve confirmation (provider confirmed it has capacity for this specific request)
+
+This output is written to `placement.yaml` in the Requested Store directory.
+
+### 4.3 Placement Algorithm — Six Steps
+
+```
+Step 1: Sovereignty Pre-Filter
+  Eliminate any provider whose sovereignty_declaration does not satisfy
+  the request's sovereignty constraints.
+  → Providers that fail this step are never contacted.
+  → If zero providers remain: RESERVE_QUERY_ALL_EXHAUSTED recovery trigger fires.
+
+Step 2: Accreditation Filter
+  Eliminate any provider that does not hold the required accreditations
+  for the data classifications present in the assembled payload.
+  → Checked against the active Data/Capability Authorization Matrix.
+  → Providers with accreditation gaps are excluded.
+
+Step 3: Capability Filter
+  Eliminate any provider that does not declare support for the
+  requested resource type and all required capabilities.
+  → Based on provider registration catalog item declarations.
+
+Step 4: Reserve Query
+  Send a reserve query to each remaining candidate provider in parallel.
+  → Providers have reserve_query_timeout to respond (profile-governed: PT5–30S).
+  → Providers that do not respond within timeout: excluded from this placement cycle.
+  → Providers that respond with INSUFFICIENT_CAPACITY: excluded; DCM updates
+    internal capacity rating for that provider.
+  → Providers that confirm capacity: advance to tie-breaking.
+
+Step 5: Tie-Breaking (deterministic hierarchy)
+  Applied when multiple providers confirmed capacity in Step 4:
+
+  Priority 1: Policy preference
+    A Transformation policy injected a preference_score or preferred_provider_uuid.
+    Highest preference_score wins. preferred_provider_uuid is absolute — skips Steps 2-6.
+
+  Priority 2: Provider declared priority
+    Providers declare a numeric priority at registration (default: 50).
+    Higher value = preferred when all else equal.
+
+  Priority 3: Tenant affinity
+    Tenant's Policy Group declares preferred providers for specific resource types.
+    Affinity preference is a soft preference — does not override accreditation or sovereignty.
+
+  Priority 4: Cost Analysis
+    Cost Analysis component provides current cost per unit per candidate provider.
+    Prefer lower total cost (CapEx + OpEx + licensing).
+    Skip if: Cost Analysis unavailable, data stale > PT1H, or cost difference < 5%.
+
+  Priority 5: Least loaded
+    Prefer provider with lower current capacity utilization from reserve_query response.
+    Skip if: utilization difference < 10%, or utilization data not returned.
+
+  Priority 6: Consistent hash (final tiebreaker — always resolves)
+    SHA-256(request_uuid + resource_type + sorted_candidate_uuids)
+    Deterministic — same request always resolves to the same provider in a stable cluster.
+    Never round-robin.
+
+Step 6: Reserve Confirmation
+  Notify the selected provider that its reservation is confirmed.
+  Other providers that responded to the reserve query receive a reservation release.
+  → Prevents capacity holds from accumulating across providers for the same request.
+```
+
+### 4.4 Reserve Query Protocol
+
+```yaml
+reserve_query:
+  query_uuid:                # idempotency key
+  entity_uuid: 
+  resource_type: Compute.VirtualMachine
+  resource_type_spec_version: "2.1.0"
+  requested_fields:
+    cpu_count: 4
+    memory_gb: 8
+    storage_gb: 100
+  sovereignty_requirements:
+    data_residency: EU
+  reservation_hold_ttl: PT5M      # provider holds capacity for this duration
+                                   # released when: confirmed, rejected, or TTL expires
+```
+
+```yaml
+reserve_query_response:
+  query_uuid: 
+  provider_uuid: 
+  status: confirmed | insufficient_capacity | capability_not_supported
+  capacity_held_until:    # if confirmed
+  utilization_pct: 42               # current load; used for Step 5 tiebreaking
+  cost_per_hour: 0.32               # if Cost Analysis integration enabled
+  currency: USD
+```
+
+### 4.5 Placement Configuration
+
+```yaml
+placement_engine_config:
+  reserve_query_timeout: PT10S       # profile-governed default
+  parallel_reserve_queries: true     # always true; all candidates queried simultaneously
+  max_candidates_per_placement: 10   # cap on parallel reserve queries
+  cost_freshness_max: PT1H
+  cost_difference_threshold: 0.05   # 5% — skip cost step if within this band
+  utilization_difference_threshold: 0.10  # 10% — skip utilization step if within this band
+  reservation_hold_ttl: PT5M
+```
+
+### 4.6 Placement Failure and Recovery
+
+When placement cannot find an eligible provider:
+
+| Failure Reason | Recovery Trigger |
+|---------------|-----------------|
+| All providers fail sovereignty filter | `RESERVE_QUERY_ALL_EXHAUSTED` |
+| All providers fail accreditation filter | `RESERVE_QUERY_ALL_EXHAUSTED` |
+| All providers respond INSUFFICIENT_CAPACITY | `RESERVE_QUERY_ALL_EXHAUSTED` |
+| All reserve queries time out | `RESERVE_QUERY_ALL_EXHAUSTED` |
+
+The `RESERVE_QUERY_ALL_EXHAUSTED` trigger fires the active Recovery Policy. Default action per profile: standard/prod → `NOTIFY_AND_WAIT`; dev → `RETRY` with exponential backoff.
+
+### 4.7 Placement System Policies
+
+| Policy | Rule |
+|--------|------|
+| `PLC-001` | Sovereignty pre-filter runs before any provider is contacted. Providers that fail sovereignty constraints never receive reserve queries. |
+| `PLC-002` | Accreditation filter runs before reserve queries. Providers without required accreditations for the payload's data classifications are excluded. |
+| `PLC-003` | Reserve queries are sent in parallel to all eligible candidates. Sequential querying is not permitted — it introduces latency and prevents fair capacity comparison. |
+| `PLC-004` | The consistent hash tiebreaker is always the final tiebreaker. It ensures deterministic provider selection for identical inputs without round-robin non-determinism. |
+| `PLC-005` | A confirmed reservation hold must be released when not used — either by confirmation dispatch or by explicit release on timeout. Capacity holds must not accumulate silently. |
+| `PLC-006` | The Placement Engine never selects a provider based solely on network position or co-location. Every selection is based on declared constraints, policies, and the tie-breaking hierarchy. |
+
+---
+
+## 5. The Lifecycle Constraint Enforcer
+
+### 5.1 Role
+
+The Lifecycle Constraint Enforcer is a DCM control plane component that monitors all realized entities against their declared lifecycle constraints and fires expiry actions when constraints are reached. It is the authoritative enforcer of TTL, expiry date, and maximum execution time declarations.
+
+Lifecycle constraint enforcement is a DCM concern — not a provider concern. The provider does not need to know about or implement any TTL logic.
+
+### 5.2 What It Monitors
+
+The Lifecycle Constraint Enforcer monitors three categories of constraint:
+
+**Category 1 — Entity TTL:**
+Duration-based: entity expires T duration after a reference point (realization, creation, last modification).
+
+**Category 2 — Entity Expiry Date:**
+Calendar-based: entity expires at an absolute timestamp.
+
+**Category 3 — Process Resource Maximum Execution Time:**
+Process Resources must declare `max_execution_time`. The Enforcer monitors all executing Process Resources and fires `on_max_exceeded` when the limit is reached.
+
+### 5.3 Monitoring Loop
+
+```
+Lifecycle Constraint Enforcer runs continuously:
+
+  Every cycle (interval: PT1M for standard/prod; PT5M for minimal/dev):
+
+    Query Realized Store for entities with:
+      lifecycle_state IN [OPERATIONAL, SUSPENDED, EXECUTING]
+      AND lifecycle_constraints declared
+      AND NOT already in terminal state
+
+    For each entity:
+      Compute time_remaining = constraint_expiry - now()
+
+      If time_remaining <= warn_before_expiry:
+        If warn_not_yet_sent:
+          Emit: entity.ttl_warning notification
+          Record: WARNING_EMITTED in entity provenance
+
+      If time_remaining <= 0:
+        Execute on_expiry action (see Section 5.4)
+```
+
+### 5.4 Expiry Action Execution
+
+When a lifecycle constraint fires, the Enforcer executes the declared `on_expiry` action:
+
+| Action | Behavior |
+|--------|---------|
+| `decommission` | Submit a decommission request through the standard pipeline — produces Requested State, dispatches to provider, full audit trail |
+| `suspend` | Submit a suspend request through the standard pipeline |
+| `notify` | Fire `entity.ttl_expired` notification to entity owner; no automated action |
+| `review` | Entity enters PENDING_EXPIRY_ACTION state; Platform Admin and owner notified |
+| `escalate` | Immediately escalate to Platform Admin; entity enters PENDING_EXPIRY_ACTION state |
+
+**Grace period:** Expiry actions are not immediate. The Enforcer respects the declared `grace_period` (default PT1H) — the action fires `grace_period` after the constraint expires, giving human operators a window to intervene.
+
+**Action failure:** If the expiry action fails to execute (provider unreachable, dependency conflict), the entity enters `PENDING_EXPIRY_ACTION` state (LTC-005). The Enforcer retries per the active Recovery Policy. Platform Admin is notified with urgency: high.
+
+### 5.5 Expiry Audit Records
+
+Every expiry-related event produces an audit record:
+
+```yaml
+audit_record:
+  action: EXPIRY_WARNING | EXPIRY_ACTION_FIRED | EXPIRY_ACTION_FAILED |
+          PENDING_EXPIRY_ACTION_ENTERED
+  actor:
+    type: system
+    system_actor:
+      component: lifecycle_constraint_enforcer
+      trigger: ttl_reached | expires_at_reached | max_execution_time_reached
+  entity_uuid: 
+  details:
+    constraint_type: ttl | expires_at | max_execution_time
+    constraint_value: 
+    action_taken: decommission | suspend | notify | review | escalate
+    grace_period_remaining: 
+```
+
+### 5.6 Process Resource Enforcement
+
+Process Resources require `max_execution_time` (mandatory). The Enforcer monitors all EXECUTING Process Resources:
+
+```
+Process Resource enters EXECUTING state
+  │
+  ▼ Enforcer records: execution_started_at; computes execution_timeout_at
+  │
+  ▼ On every monitoring cycle:
+  │   If now() >= execution_timeout_at:
+  │     Emit: PROCESS_TIMEOUT event
+  │     Entity state → FAILED
+  │     Recovery Policy: COMPENSATION_FAILED trigger if resources were modified
+  │     Notification: entity owner + Platform Admin (urgency: high)
+```
+
+### 5.7 Lifecycle Constraint Enforcer Policies
+
+| Policy | Rule |
+|--------|------|
+| `LCE-001` | The Lifecycle Constraint Enforcer runs as a continuous monitor. It does not rely on provider callbacks or event triggers for expiry detection — it polls based on declared constraints. |
+| `LCE-002` | Expiry actions are submitted through the standard DCM request pipeline. Decommission-on-expiry produces a Requested State record with `actor: system/lifecycle-constraint-enforcer`. |
+| `LCE-003` | The Enforcer respects the declared grace_period before firing expiry actions. Grace period gives human operators a window to intervene before automated action. |
+| `LCE-004` | Process Resource max_execution_time enforcement fires immediately on breach — no grace period. Hung processes are failed immediately to prevent resource leaks. |
+| `LCE-005` | Expiry action failures enter PENDING_EXPIRY_ACTION state. The Enforcer retries per the active Recovery Policy. Indefinite retry without escalation is not permitted. |
+
+---
+
+## 6. The Search Index
+
+### 6.1 Role
+
+The Search Index is a **non-authoritative, queryable projection** of the GitOps stores (Intent Store and Requested Store). It enables millisecond-latency queries against stored entities without traversing Git history, while the GitOps stores remain the authoritative source of truth.
+
+The Search Index is a PostgreSQL store contract. It has its own registration, health check, and sovereignty declaration. It is never the source of truth — if the Search Index and the GitOps store disagree, the GitOps store wins unconditionally.
+
+### 6.2 What It Indexes
+
+The Search Index maintains a projection of key fields from Intent State and Requested State records, enabling queries without retrieving full payloads from Git:
+
+```yaml
+search_index_record:
+  entity_uuid: 
+  entity_handle: 
+  resource_type: Compute.VirtualMachine
+  resource_type_category: Compute
+  tenant_uuid: 
+  lifecycle_state: OPERATIONAL
+  drift_status: clean
+  provider_uuid: 
+  deployment_posture: prod
+  compliance_domains: [hipaa]
+  data_classifications: [restricted]      # highest classification in entity
+  created_at: 
+  updated_at: 
+  cost_per_hour: 0.32
+  currency: USD
+  git_path: intent-store/tenant-uuid/Compute/VirtualMachine/entity-uuid/intent.yaml
+  # git_path is the pointer back to the authoritative record
+  tags: { environment: production, team: payments }
+```
+
+### 6.3 Required Query Operations
+
+| Operation | Description |
+|-----------|-------------|
+| `find_by_uuid(entity_uuid)` | Return index record for a single entity |
+| `find_by_tenant(tenant_uuid, filters)` | Return all entities for a Tenant with optional field filters |
+| `find_by_resource_type(fqn, filters)` | Return all entities of a resource type |
+| `find_by_provider(provider_uuid, filters)` | Return all entities hosted at a provider |
+| `find_by_lifecycle_state(state, tenant_uuid)` | Return entities in a given lifecycle state |
+| `find_by_drift_status(status, tenant_uuid)` | Return drifted or clean entities |
+| `find_by_data_classification(classification)` | Return entities containing data of a given classification |
+| `full_text_search(query, tenant_uuid)` | Full-text search across handle, display_name, tags |
+
+All queries return the `git_path` — consumers fetch the full payload from Git if needed.
+
+### 6.4 Consistency Model
+
+The Search Index is **eventually consistent** with the GitOps stores. There is a defined maximum staleness:
+
+```yaml
+search_index_consistency:
+  max_staleness: PT5M         # index must be within 5 minutes of GitOps store
+  profile_overrides:
+    prod: PT2M
+    fsi: PT1M
+    sovereign: PT1M
+  on_staleness_exceeded:
+    action: degrade_with_warning    # serve results with staleness warning
+    alert: platform_admin           # alert on staleness exceeding 2× max
+  rebuild_on_recovery: true         # full index rebuild from Git history on failure
+  rebuild_max_duration: PT4H        # must complete within 4 hours for standard+
+```
+
+### 6.5 Unavailability Behavior
+
+If the Search Index is unavailable:
+- DCM degrades search operations gracefully: returns a `503 Service Degraded` response with a reference to the authoritative Git store
+- Writes are not affected — GitOps stores are written directly; the index is updated asynchronously
+- On recovery: the Search Index rebuilds from Git history
+- No data is lost if the index is lost — it is always reconstructable from Git
+
+### 6.6 Search Index Policies
+
+| Policy | Rule |
+|--------|------|
+| `SIX-001` | The Search Index is non-authoritative. GitOps stores win on any disagreement. Consumers must be prepared to receive a git_path and fetch from the authoritative store. |
+| `SIX-002` | The Search Index must be rebuildable from Git history at any time. Implementations that cannot perform a full index rebuild are non-conformant. |
+| `SIX-003` | Search Index staleness beyond the profile-governed maximum triggers a platform admin alert. Staleness is surfaced in query responses — consumers are never served stale data silently. |
+| `SIX-004` | Search Index unavailability degrades queries without impacting writes. Write operations proceed directly to the authoritative GitOps stores regardless of Search Index availability. |
+
+---
+
+
+---
+
+## 7. The Drift Reconciliation Component
+
+### 7.1 Role
+
+The Drift Reconciliation Component compares the Discovered State of entities against their Realized State to detect, classify, and respond to drift. It is the consumer of Discovered Store data and the producer of drift records that feed into the Policy Engine for response evaluation.
+
+Drift Reconciliation is purely a read-and-compare component — it never writes to the Realized Store. It reads Discovered State, reads Realized State, computes differences, classifies severity, and fires events into the Request Orchestrator. The Policy Engine and Recovery Policies determine what happens next.
+
+### 7.2 Inputs and Outputs
+
+**Inputs:**
+- Discovered State snapshots (from Discovered Store, written by Discovery Scheduler)
+- Realized State snapshots (from Realized Store)
+- Resource Type Specifications (for field criticality declarations used in severity classification)
+- Active governance profile (for magnitude thresholds used in severity classification)
+
+**Outputs:**
+- Drift records (written to Drift Record Store — a lightweight operational store)
+- Drift events published to the Request Orchestrator: `drift.detected`, `drift.resolved`, `drift.severity_escalated`
+- Unsanctioned change events: `unsanctioned_change.detected`
+
+### 7.3 Comparison Algorithm
+
+```
+Discovery cycle completes → Discovered State snapshot written
+  │
+  ▼ Drift Reconciliation Component receives discovery.cycle_complete event
+  │
+  ▼ For each entity UUID in the discovery snapshot:
+  │
+  │   Load: latest Realized State snapshot for entity UUID
+  │   Load: Discovered State snapshot (just written)
+  │   Load: Resource Type Specification (field criticality per field)
+  │
+  ▼ Field-by-field comparison:
+  │   For each field in Realized State:
+  │     Does Discovered State contain this field?
+  │     If yes: are the values equal?
+  │     If no: field is absent — severity based on field criticality
+  │   For each field in Discovered State not in Realized State:
+  │     New field appeared — severity based on field criticality
+  │
+  ▼ Severity classification (per field):
+  │   Field criticality (from Resource Type Spec) × Change magnitude (profile-governed)
+  │   → severity matrix → minor | significant | critical
+  │   Unsanctioned? → elevate one level
+  │   Multiple drifted fields? → overall = highest individual severity
+  │
+  ▼ Unsanctioned check:
+  │   Is there a Requested State record that explains this change?
+  │   If yes: sanctioned change (may still be drift if realization didn't match)
+  │   If no: unsanctioned_change.detected event fired (in addition to drift.detected)
+  │
+  ├── No drift detected:
+  │     Update entity.last_discovered_at
+  │     Update entity.drift_status = clean
+  │     No drift record created
+  │
+  └── Drift detected:
+        Create drift record
+        Publish drift.detected to Request Orchestrator
+        Policy Engine evaluates → response action
+```
+
+### 7.4 Drift Record Structure
+
+```yaml
+drift_record:
+  uuid: 
+  entity_uuid: 
+  detected_at: 
+  discovery_snapshot_uuid:         # the Discovered State snapshot that triggered this
+  realized_state_uuid:             # the Realized State snapshot compared against
+
+  overall_severity: minor | significant | critical
+  unsanctioned: true | false             # true if no corresponding Requested State record
+
+  drifted_fields:
+    - field_path: "fields.memory_gb"
+      realized_value: 8
+      discovered_value: 16
+      field_criticality: medium           # from Resource Type Spec
+      change_magnitude: significant       # 100% increase, threshold: standard 10-50%
+      field_severity: significant
+      elevated_for_unsanctioned: true     # elevated from significant → critical
+
+  status: open | acknowledged | resolved | escalated
+  resolution:
+    resolved_at: 
+    resolution_type: reverted | updated_definition | accepted | escalated | null
+    resolved_by_requested_state_uuid: 
+```
+
+### 7.5 Drift Resolution Tracking
+
+Drift records are not resolved by the Drift Reconciliation Component — they are resolved by the Policy Engine's response actions. The Drift Reconciliation Component monitors for resolution:
+
+```
+REVERT action taken:
+  New Requested State submitted → provider reverts → new Realized State written
+  Next discovery cycle: Discovered State matches new Realized State
+  Drift Reconciliation: no drift detected → drift_record.status = resolved
+  drift.resolved event published
+
+UPDATE_DEFINITION action taken:
+  Consumer submits UPDATE_DEFINITION → new Realized State written with discovered values
+  Next discovery cycle: Discovered State matches new Realized State
+  Drift record.status = resolved with resolution_type: updated_definition
+
+Entity decommissioned:
+  Drift record.status = resolved with resolution_type: decommissioned
+```
+
+### 7.6 Governance Matrix Integration
+
+Before classifying a discovered change as drift, the Drift Reconciliation Component evaluates the governance matrix to determine if the change is expected:
+
+```
+Field value in Discovered State differs from Realized State
+  │
+  ▼ Check: Is there a governance matrix rule that permits this provider
+  │        to make this type of change to this field?
+  │
+  ├── Yes → This may be a Provider Update Notification that wasn't submitted
+  │   DCM logs a warning: "Provider changed field without submitting update notification"
+  │   Still treated as drift — provider should have submitted update notification
+  │
+  └── No → Standard drift detection; severity classification runs
+```
+
+### 7.7 Drift Reconciliation Policies
+
+| Policy | Rule |
+|--------|------|
+| `DRC-001` | The Drift Reconciliation Component never writes to the Realized Store. It produces drift records and events only. |
+| `DRC-002` | Drift detection runs after every discovery cycle. An entity with no corresponding Realized State record is an orphan candidate — not a drift event. |
+| `DRC-003` | Unsanctioned changes are always elevated one severity level above the matrix classification. An unsanctioned significant drift is reported as critical. |
+| `DRC-004` | Drift records are retained until the entity is decommissioned plus the configured audit retention period. They are not deleted on resolution — resolution is recorded within the record. |
+| `DRC-005` | Drift detection produces events into the Request Orchestrator. The Policy Engine determines the response action. The Drift Reconciliation Component does not initiate remediation directly. |
+
+
+## 8. Related Policies — Full Component Set
+
+| Policy | Rule |
+|--------|------|
+| `CTL-001` | The Request Orchestrator is the single event bus for all request lifecycle events. No component communicates directly with another component outside of events published to the Request Orchestrator. |
+| `CTL-002` | Policies ARE the orchestration. The Request Orchestrator does not contain hardcoded pipeline logic. |
+| `CTL-003` | Dynamic and static flows compose naturally. Static flows are Policy Groups with concern_type: orchestration_flow and ordered: true. |
+| `CTL-004` | Cost Analysis is not a billing system. It provides cost signals for placement and attribution. |
+| `CTL-005` | Cost Analysis unavailability never blocks placement. |
+| `PLC-001` through `PLC-006` | Placement Engine policies (see Section 4.7) |
+| `LCE-001` through `LCE-005` | Lifecycle Constraint Enforcer policies (see Section 5.7) |
+| `SIX-001` through `SIX-004` | Search Index policies (see Section 6.6) |
+| `DRC-001` through `DRC-005` | Drift Reconciliation policies (see Section 7.7) |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/26-accreditation-and-authorization-matrix.md b/content/docs/architecture/data-model/26-accreditation-and-authorization-matrix.md
new file mode 100644
index 0000000..4dd100d
--- /dev/null
+++ b/content/docs/architecture/data-model/26-accreditation-and-authorization-matrix.md
@@ -0,0 +1,646 @@
+# DCM Data Model — Accreditation, Data Authorization Matrix, and Zero Trust
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: DATA + POLICY**
+>
+> Data: Accreditation artifacts. Policy: Zero Trust posture as policy concern type
+
+
+**Related Documents:** [Policy Profiles](14-policy-profiles.md) | [Resource/Service Entities](06-resource-service-entities.md) | [DCM Federation](22-dcm-federation.md) | [Layering and Versioning](03-layering-and-versioning.md) | [Operational Models](24-operational-models.md)
+
+---
+
+> **Authentication Assurance Levels:** See [Standards and Compliance Catalog](40-standards-catalog.md) Section 7 for the NIST SP 800-63B AAL mapping per profile.
+
+## 1. Purpose
+
+This document defines three interconnected models that together govern how DCM handles trust, data handling obligations, and compliance verification across all interaction boundaries:
+
+1. **Accreditation Model** — how DCM records, verifies, and enforces third-party compliance certifications for providers, policy engines, and DCM deployments themselves
+2. **Data/Capability Authorization Matrix** — what data and capabilities are permitted across any DCM boundary given a component's accreditation level and the data's classification
+3. **Zero Trust Interaction Model** — the authentication, authorization, and verification requirements for every interaction in DCM, regardless of network position
+
+These three models compose: Zero Trust verifies identity and authorization on every call. Accreditation verifies compliance certification status. The Authorization Matrix declares what is permitted given that certification status. Together they ensure that no interaction in DCM is implicitly trusted — every boundary crossing is verified against all three models.
+
+---
+
+
+## 1b. Accreditation and the Scoring Model
+
+DCM distinguishes two distinct accreditation functions:
+
+**Required Accreditation (boolean gate):** Whether a provider holds a specific accreditation required for a particular request. PHI data requires an active BAA. This is a Governance Matrix enforcement — always boolean, never scored. A provider without the required accreditation is ineligible for that request regardless of any other score.
+
+**Accreditation Richness (placement score):** The breadth and depth of a provider's accreditation portfolio. A provider with ISO 27001 + SOC2 Type II + FedRAMP Moderate + HIPAA BAA is preferable for placement over one with only self-declaration, all else equal. This is a continuous scoring signal — it does not gate eligibility, it influences preference among eligible providers.
+
+Accreditation richness score contributes to:
+1. Placement tie-breaking (a richer portfolio is preferred)
+2. Request risk score Signal 5 (inversely — higher richness reduces provider risk contribution)
+
+See [Scoring Model](29-scoring-model.md) Section 4.5 for the richness score weights and normalization.
+
+
+## 2. Data Classification
+
+Data classification is a **first-class field-level metadata property** in the DCM data model. Every field in every payload carries a `data_classification` value. This classification is the primary axis of the authorization matrix and is the key input to sovereignty and compliance enforcement.
+
+### 2.1 Classification Levels
+
+| Level | Description | Examples |
+|-------|-------------|---------|
+| `public` | No restrictions; freely shareable | Resource display names, catalog item descriptions |
+| `internal` | Organization-internal; not for external disclosure | Configuration details, operational metadata |
+| `confidential` | Sensitive business data; restricted access | Cost data, business unit assignments |
+| `restricted` | Highly sensitive; regulated or contractually protected | Security group IDs, network topology details |
+| `phi` | Protected Health Information under HIPAA/HITECH | Patient IDs, diagnosis codes, treatment plans |
+| `pci` | Payment Card Industry data under PCI-DSS | Cardholder data, authentication data |
+| `sovereign` | Nationally classified or sovereignty-restricted data | Data subject to national security law |
+| `classified` | Government-classified information | Classified defense or intelligence data |
+
+### 2.2 Classification as Field Metadata
+
+Every field in a DCM payload carries data classification as part of its field metadata:
+
+```yaml
+field_definition:
+  field_name: patient_record_id
+  value: "PAT-00421"
+  data_classification: phi
+  classification_basis: "Contains patient identifier — HIPAA 45 CFR 164.514"
+  metadata:
+    override: immutable           # classification cannot be changed by policy
+    locked_by: system/compliance/hipaa-field-classifier
+```
+
+**Classification is declared in three places:**
+- **Resource Type Specification** — default classification per field for all instances of that type
+- **Data Layer** — classification applied across a domain (e.g., an org layer that marks all cost_center fields as `confidential`)
+- **Field-level override** — explicit classification on a specific field instance (highest precedence, immutable once set for `phi`, `sovereign`, `classified`)
+
+### 2.3 Classification Immutability
+
+Fields classified as `phi`, `sovereign`, or `classified` cannot be downgraded by any layer or policy — their classification is immutable once set. A GateKeeper policy attempting to downgrade a PHI field is rejected with a classification violation audit record.
+
+---
+
+## 3. Accreditation Model
+
+### 3.1 What Accreditation Is
+
+An **Accreditation** is a formal, versioned, time-bounded attestation that a DCM component — a Service Provider, a External Policy Evaluator, a data store, a notification service, or a DCM deployment itself — satisfies the requirements of a specific compliance framework. Accreditations are issued by an **Accreditor** and registered with DCM as first-class artifacts.
+
+Accreditation answers: **"Is this component certified to handle this type of data?"**
+
+### 3.2 Accreditation Types and Trust Levels
+
+| Type | Issued By | Trust Level | Examples |
+|------|-----------|-------------|---------|
+| `self_declared` | Component itself | Lowest | Dev/homelab; provider asserts own compliance |
+| `first_party` | DCM organization's own audit team | Low-Medium | Internal compliance review |
+| `third_party` | Independent certifying body | High | ISO 27001, SOC 2 Type II |
+| `qsa_assessment` | Qualified Security Assessor | High | PCI-DSS QSA report |
+| `baa` | Legal BAA with covered entity | High | HIPAA Business Associate Agreement |
+| `regulatory_certification` | Government regulatory body | Highest | FedRAMP P-ATO, DoD Provisional Authorization |
+| `sovereign_authorization` | National sovereignty authority | Highest | National cloud authorization |
+
+### 3.3 Accreditation Record Structure
+
+```yaml
+accreditation:
+  # Standard artifact metadata
+  artifact_metadata:
+    uuid: 
+    handle: "accreditations/providers/eu-west-prod-1/fedramp-high"
+    version: "1.0.0"
+    status: active
+    owned_by: { display_name: "Compliance Team" }
+
+  subject_uuid:           # what is being accredited
+  subject_type: service_provider | external_policy_evaluation | (prescribed infrastructure) |
+                service_provider | dcm_deployment
+
+  accreditation_type: 
+  framework: fedramp_high | fedramp_moderate | hipaa | pci_dss_v4 |
+             iso_27001 | soc2_type2 | dod_il4 | dod_il5 | dod_il6 |
+             sovereign | classified | 
+
+  accreditor:
+    uuid: 
+    name: "DISA" | "HHS OIG" | "PCI SSC" | "BSI" | 
+    type: government | regulatory_body | qsa | certification_body | internal | self
+    contact_url: 
+
+  # Validity
+  issued_at: 
+  expires_at:             # null = perpetual until revoked
+  renewal_warning_before: P90D
+  last_verified_at:             # when DCM last confirmed still active
+
+  # What the accreditation covers
+  scope:
+    data_classifications: [phi, restricted]   # which classifications this covers
+    capabilities: [data_at_rest, data_in_transit, access_control, audit_logging]
+    geographic_scope: [US, EU-WEST]
+    exclusions: []
+
+  # Evidence
+  certificate_ref: 
+  audit_report_ref: 
+  external_registry_id: "FR2024-0042"    # e.g., FedRAMP Marketplace ID
+
+  # Status
+  status: active | suspended | revoked | expired | pending_renewal | pending_review
+  revocation_reason: 
+  revoked_at: 
+
+  # Automated verification (see doc 47 — Accreditation Monitor)
+  verification:
+    tier: external_registry | document_currency | contract_webhook | expiry_only
+    stale_after: P7D                # max gap between verifications before stale_action fires
+    stale_action: warn | suspend | escalate   # profile-governed default: warn/suspend/escalate
+    verification_failure_count: 0
+    # tier-specific fields: see doc 47 Section 3 for full schema
+```
+
+> **Accreditation Monitor:** The `last_verified_at` field is maintained by the
+> Accreditation Monitor (doc 47), which continuously verifies accreditation status
+> against external registries, document currency checks, or contract system webhooks
+> depending on the `verification.tier`. See doc 47 for the complete monitoring
+> specification and framework-by-framework automation coverage.
+
+### 3.4 Accreditation Lifecycle
+
+```
+Accreditation submitted (via API or GitOps PR)
+  │
+  ▼ DCM validates structure and accreditor registration
+  │
+  ▼ status: proposed
+  │   Shadow mode: compliance policies use this accreditation in shadow evaluation
+  │   Platform admin reviews certificate_ref and audit_report_ref
+  │
+  ▼ Platform admin approves → status: active
+  │   Accreditation now enforced in compliance checks
+  │   All affected providers/deployments re-evaluated against new accreditation
+  │
+  ▼ Expiry monitoring:
+  │   At expires_at - renewal_warning_before:
+  │     notification.accreditation_expiring → Compliance Team, Platform Admin
+  │   At expires_at:
+  │     status → expired
+  │     Providers relying on this accreditation flagged: ACCREDITATION_GAP
+  │
+  ▼ External status change detected by Accreditation Monitor:
+  │   status → pending_review
+  │   Platform Admin notified (urgency: high)
+  │   Exception: external status = Revoked → immediate revocation (no review)
+  │
+  ▼ Revocation:
+      Accreditor or Platform Admin revokes
+      status → revoked
+      All active provider interactions using this accreditation suspended
+      notification.accreditation_revoked → Platform Admin (urgency: critical)
+```
+
+### 3.5 Accreditation Gap
+
+When a required accreditation is missing, expired, or revoked, DCM enters an **Accreditation Gap** state for the affected provider:
+
+```yaml
+accreditation_gap_record:
+  uuid: 
+  provider_uuid: 
+  required_framework: hipaa
+  required_for: [phi data fields in active requests]
+  gap_type: missing | expired | revoked | suspended | verification_stale
+  detected_at: 
+  severity: critical                    # accreditation gaps are always high or critical
+  affected_entity_uuids: [, ...]  # entities currently hosted at this provider
+  policy_response: 
+  # Default: NOTIFY_AND_WAIT for fsi/sovereign; ESCALATE for standard/prod
+```
+
+### 3.6 DCM Deployment Accreditation
+
+DCM deployments themselves can carry accreditations — a FedRAMP-authorized DCM deployment, for example. This enables cross-organization trust: a consuming organization's DCM can verify the providing organization's DCM deployment holds the required accreditation before federating with it.
+
+```yaml
+deployment_accreditation:
+  subject_type: dcm_deployment
+  subject_uuid: 
+  framework: fedramp_high
+  # The DCM deployment itself is accredited, not just the providers it manages
+```
+
+---
+
+
+> **Scope:** This document covers the accreditation model (Sections 2-3) and zero trust interaction model (Section 5). Data and capability boundary enforcement is specified in the [Unified Governance Matrix](27-governance-matrix.md) (doc 27), which consumes the accreditation and classification models defined here as inputs.
+
+## 4. Data/Capability Authorization Matrix
+
+### 4.1 Purpose
+
+The Data/Capability Authorization Matrix declares what data fields and provider capabilities are permitted across any DCM interaction boundary given the data's classification and the receiving component's accreditation level. It is the enforcement model that sits between compliance domain policies and the actual provider interaction.
+
+### 4.2 Matrix as a Policy Artifact
+
+The authorization matrix is a **Policy Group artifact** with `concern_type: data_authorization_boundary`. It is activated as part of the compliance domain group — enabling the HIPAA compliance domain automatically activates the HIPAA boundary matrix. Organizations extend or restrict matrices via their own policy groups at the Tenant level.
+
+```yaml
+data_authorization_matrix:
+  artifact_metadata:
+    uuid: 
+    handle: "system/matrix/hipaa-provider-boundary"
+    version: "1.0.0"
+    status: active
+  
+  concern_type: data_authorization_boundary
+  applicable_compliance_domains: [hipaa]
+
+  # OUTBOUND: what DCM may send to a provider
+  outbound_data_permissions:
+    - data_classification: phi
+      required_accreditation_type: baa
+      required_accreditation_framework: hipaa
+      on_missing_accreditation: DENY_REQUEST
+      # DENY_REQUEST: block the entire request (PHI is required; cannot strip)
+      # STRIP_FIELD: remove field and proceed (for optional PHI fields)
+      # WARN_AND_ALLOW: allow but audit (dev profile only)
+
+    - data_classification: restricted
+      required_accreditation_type: third_party
+      on_missing_accreditation: STRIP_FIELD
+
+    - data_classification: internal
+      required_accreditation_type: self_declared
+      on_missing_accreditation: WARN_AND_ALLOW   # always has self_declared minimum
+
+    - data_classification: [public, internal]
+      required_accreditation_type: self_declared
+      on_missing_accreditation: ALLOW
+
+  # CAPABILITY: what operations the provider may perform on classified data
+  capability_permissions:
+    - capability: STORE_AT_REST
+      data_classification: phi
+      required_accreditation_type: baa
+      required_scope: [data_at_rest]
+      on_missing_accreditation: DENY_CAPABILITY
+
+    - capability: REPLICATE_CROSS_REGION
+      data_classification: phi
+      required_accreditation_type: baa
+      additional_requirement: replication_target_has_baa
+      on_missing_accreditation: DENY_CAPABILITY
+
+    - capability: EXPORT_TO_EXTERNAL_SYSTEM
+      data_classification: [phi, restricted, sovereign]
+      required_accreditation_type: regulatory_certification
+      on_missing_accreditation: DENY_CAPABILITY
+
+    - capability: PROVIDER_UPDATE_NOTIFICATION
+      data_classification: phi
+      required_accreditation_type: baa
+      # Provider may only notify DCM of changes to PHI-containing resources
+      # if it holds a valid BAA
+      on_missing_accreditation: DENY_CAPABILITY
+
+  # INBOUND: what the provider may return to DCM
+  inbound_data_permissions:
+    - data_classification: phi
+      provider_must_strip_before_return: false
+      # DCM receives PHI in Realized State but access-controls it
+      consumer_visibility_requires_accreditation: baa
+      stored_in_partition: realized_store_phi
+      # PHI partition has additional encryption and access control
+```
+
+### 4.3 Federation Boundary Matrix
+
+A dedicated matrix governs what crosses DCM-to-DCM federation boundaries:
+
+```yaml
+federation_boundary_matrix:
+  artifact_metadata:
+    handle: "system/matrix/federation-boundary"
+  concern_type: data_authorization_boundary
+  applicable_to: federation_tunnel
+
+  outbound_data_permissions:
+    - data_classification: sovereign
+      on_missing_accreditation: DENY_REQUEST
+      # Sovereign data NEVER crosses a federation boundary
+      # This is a hard system constraint, not a configurable policy
+      hard_constraint: true
+
+    - data_classification: classified
+      on_missing_accreditation: DENY_REQUEST
+      hard_constraint: true
+
+    - data_classification: phi
+      required_accreditation_type: baa
+      on_missing_accreditation: DENY_REQUEST
+
+    - data_classification: restricted
+      required_accreditation_type: third_party
+      additional_requirement: remote_dcm_holds_equivalent_accreditation
+      on_missing_accreditation: STRIP_FIELD
+
+    - data_classification: [public, internal]
+      required_accreditation_type: self_declared
+      on_missing_accreditation: ALLOW
+```
+
+### 4.4 Matrix Enforcement Pipeline
+
+The authorization matrix check is a distinct pipeline step executed at every interaction boundary:
+
+```
+Outbound interaction assembled (DCM → Provider OR DCM → DCM)
+  │
+  ▼ Data Classification Inventory:
+  │   For every field in the payload:
+  │     Resolve data_classification (field metadata → layer → resource type spec default)
+  │     Record classification → field mapping
+  │
+  ▼ Accreditation Resolution:
+  │   Load active accreditations for the target component
+  │   For each required classification level in the payload:
+  │     Does the target hold an active, in-scope accreditation?
+  │     Is the accreditation within its expires_at date?
+  │
+  ▼ Matrix Evaluation (per field):
+  │   Look up data_classification × accreditation_level in active matrix
+  │   Determine: ALLOW | STRIP_FIELD | DENY_REQUEST | DENY_CAPABILITY | WARN_AND_ALLOW
+  │
+  ├── All ALLOW → proceed
+  │
+  ├── STRIP_FIELD → remove field from payload; write FIELD_STRIPPED audit record
+  │     If stripped field is required for service → escalate to DENY_REQUEST
+  │
+  ├── DENY_REQUEST → block interaction; entity enters PENDING_REVIEW
+  │     notification.accreditation_gap dispatched to owner + platform admin
+  │
+  └── WARN_AND_ALLOW → proceed but write ACCREDITATION_ADVISORY audit record
+                       (dev profile only; blocked in standard+)
+```
+
+---
+
+> **Internal component authentication:** See [Internal Component Authentication](36-internal-component-auth.md) for the complete internal auth model including component identity, Internal CA, bootstrap tokens, and ICOM-001–ICOM-009 policies.
+
+## 5. Zero Trust Interaction Model
+
+### 5.1 Principle
+
+**Network position grants zero trust.** A component inside the DCM control plane has no more implicit trust than one outside it. Every interaction — internal or external, synchronous or asynchronous — is authenticated, authorized, and verified as if the caller were an untrusted external party.
+
+Zero trust in DCM is not a network topology — it is a **per-interaction verification discipline** applied at every call, every event, every tunnel message.
+
+### 5.2 The Five-Check Boundary Model
+
+Every DCM interaction boundary applies five checks in sequence. All five must pass:
+
+```
+Interaction attempt
+  │
+  ▼ Check 1: Identity Verification
+  │   mTLS certificate verification (mutual — both sides present certificates)
+  │   Certificate chain validation against registered trust anchor
+  │   Certificate not in revocation list
+  │   Hardware attestation (fsi/sovereign profiles with hardware_attested posture)
+  │   → FAIL: connection refused; IDENTITY_VERIFICATION_FAILED audit record
+  │
+  ▼ Check 2: Authorization Verification
+  │   Does this identity have explicit permission for this operation type?
+  │   Is the presented credential scoped to this operation?
+  │   Has this credential been revoked or expired?
+  │   Does the scope match the minimum necessary for this call?
+  │   → FAIL: 403 Forbidden; AUTHORIZATION_DENIED audit record
+  │
+  ▼ Check 3: Accreditation Check
+  │   Does the target hold the required accreditation for the data classifications present?
+  │   Is the accreditation current and not suspended?
+  │   → FAIL: ACCREDITATION_GAP; recovery policy evaluates response
+  │
+  ▼ Check 4: Data/Capability Matrix Check
+  │   Is each field permitted to cross this boundary?
+  │   Is each capability permitted for this data classification?
+  │   → FAIL: FIELD_STRIPPED or DENY_REQUEST per matrix declaration
+  │
+  ▼ Check 5: Sovereignty Check
+  │   Is the target endpoint within the sovereignty boundary?
+  │   Does the interaction violate any sovereignty constraints?
+  │   BBQ-001 evaluation for external evaluation endpoints
+  │   → FAIL: SOVEREIGNTY_VIOLATION; platform admin notified
+  │
+  ▼ All checks pass → interaction proceeds
+  │
+  └── Audit record written regardless of outcome:
+        INTERACTION_AUTHORIZED or INTERACTION_DENIED_{CHECK}
+        All five check results recorded
+        Credential UUID, interaction UUID for correlation
+```
+
+### 5.3 Credential Model — Scoped, Short-Lived, Non-Transferable
+
+Zero trust requires that credentials are scoped to the minimum necessary operation and expire quickly:
+
+```yaml
+dcm_interaction_credential:
+  credential_uuid: 
+  issued_to: 
+  issued_at: 
+  expires_at:              # short-lived; typically PT15M to PT1H
+  operation_scope:
+    operation_type: dispatch | discovery | cancel | query | notify
+    entity_uuid:                # scoped to specific entity
+    provider_uuid:              # scoped to specific provider
+  non_transferable: true              # cannot be delegated or relayed
+  bound_to_ip:               # optional IP binding for fsi/sovereign
+```
+
+**Credential lifetimes by profile:**
+
+| Profile | Max credential lifetime | Renewal model |
+|---------|------------------------|---------------|
+| minimal | PT8H | Manual or long-lived |
+| dev | PT4H | Automatic refresh |
+| standard | PT1H | Automatic refresh |
+| prod | PT30M | Automatic refresh |
+| fsi | PT15M | Automatic refresh; dual approval for elevation |
+| sovereign | PT15M + hardware attestation | Hardware-bound; HSM-required |
+
+### 5.4 Zero Trust Posture as a Policy Group Concern Type
+
+`zero_trust_posture` is the sixth Policy Group concern type. Four posture levels:
+
+| Posture | Description | Profile Default |
+|---------|-------------|----------------|
+| `none` | No zero trust enforcement; perimeter model acceptable | minimal |
+| `boundary` | Zero trust at external boundaries (consumer→DCM, DCM→provider); internal components trust service mesh | dev, standard |
+| `full` | Zero trust everywhere including internal component communication; every call authenticated and authorized | prod, fsi |
+| `hardware_attested` | Full zero trust plus hardware attestation (TPM/HSM); component identity backed by hardware | sovereign |
+
+```yaml
+zero_trust_policy_group:
+  handle: "system/group/zt-full"
+  concern_type: zero_trust_posture
+  posture: full
+  policies:
+    - all_component_communication: mtls_required
+    - credential_lifetime: PT30M
+    - revocation_check: every_call          # not just at credential issuance
+    - session_continuation: re_verify_PT15M # re-verify identity during long operations
+    - failed_verification_response: terminate_and_alert
+```
+
+---
+
+## 6. Federation Zero Trust — The Tunnel Model
+
+### 6.1 Federation Tunnel as a Zero Trust Boundary
+
+A federation tunnel between DCM instances is a **mutually authenticated, encrypted, scoped channel** where both sides verify each other on every interaction. It is not a VPN — it does not establish perimeter trust. Every message crossing the tunnel is authenticated, authorized, and subject to the five-check model.
+
+**"Zero trust to any outside DCM/provider"** is implemented by: the remote DCM instance has no implicit access to local resources. Every cross-instance operation requires a scoped federation credential. The tunnel establishes secure transport — it does not establish trust.
+
+### 6.2 Federation Tunnel Structure
+
+```yaml
+federation_tunnel:
+  uuid: 
+  local_dcm_uuid: 
+  remote_dcm_uuid: 
+  tunnel_type: peer | parent_child | hub_spoke
+  trust_model: zero_trust               # always; non-negotiable
+
+  # Mutual authentication
+  authentication:
+    protocol: mtls
+    local_certificate_ref: 
+    remote_certificate_pin:   # pinned; not just chain-valid
+    trust_anchor:                # common or cross-signed CA
+    certificate_rotation_interval: P90D
+    revocation_check: ocsp_stapling       # real-time revocation check
+
+  # Per-message signing
+  message_integrity:
+    signing_algorithm: ed25519
+    local_signing_key_ref: 
+    remote_verification_key_ref: 
+    replay_protection: true               # nonce + timestamp window PT5M
+
+  # What the remote DCM may request from this DCM (inbound)
+  inbound_authorization:
+    - operation: catalog_query
+      permitted_resource_types: [Compute.VirtualMachine, Network.VLAN]
+      requires_cross_tenant_authorization: true
+    - operation: allocation_request
+      permitted_resource_types: [Network.IPAddress]
+      max_allocations_per_request: 10
+      requires_cross_tenant_authorization: true
+
+  # What this DCM may request from the remote (outbound)
+  outbound_authorization:
+    - operation: placement_query
+      permitted_resource_types: [Compute.VirtualMachine]
+    - operation: realized_state_query
+      permitted_entity_uuids: []   # scoped to specific entities
+
+  # Data classification boundary (hard constraints)
+  data_boundary:
+    max_outbound_classification: restricted  # never send sovereign/classified
+    max_inbound_classification: restricted
+    # sovereign profile: max_*_classification: internal
+    # classified profile: no federation permitted
+
+  # Sovereignty scope
+  sovereignty_scope:
+    local_jurisdiction: EU
+    remote_jurisdiction: EU
+    cross_jurisdiction_permitted: false   # fsi/sovereign: always false
+```
+
+### 6.3 Federation Credential Scoping
+
+Federation credentials are scoped to the specific operations declared in the tunnel authorization. A federation credential issued for `catalog_query` cannot be used for `allocation_request`:
+
+```yaml
+federation_credential:
+  credential_uuid: 
+  issued_by_dcm_uuid: 
+  issued_to_dcm_uuid: 
+  expires_at:              # PT15M for fsi/sovereign
+  operation_scope: catalog_query
+  scoped_resource_types: [Compute.VirtualMachine]
+  non_transferable: true
+  tunnel_uuid:                  # bound to specific tunnel
+```
+
+### 6.4 Zero Trust in Hub-Spoke Federation
+
+In hub-spoke federation, the Hub DCM coordinates Regional DCMs. Zero trust means:
+- The Hub DCM does not have root-level access to Regional DCMs — it has explicitly scoped federation credentials
+- A Regional DCM cannot impersonate the Hub DCM to another Regional DCM
+- Cross-Regional-DCM operations route through the Hub with the Hub's authorization, not the originating Regional DCM's authorization
+- The Hub DCM's accreditation is visible to Regional DCMs — Regional DCMs can verify the Hub before accepting federation messages
+
+```
+RegionalDCM-A → HubDCM:  authenticated; scoped to allocation_request
+HubDCM → RegionalDCM-B:  authenticated; scoped to realization_request
+                          Hub presents its own credential to RegionalDCM-B
+                          Not RegionalDCM-A's credential
+RegionalDCM-B verifies:  Hub certificate; Hub accreditation; data classification boundary
+```
+
+---
+
+## 7. Profile-Governed Zero Trust Enforcement
+
+Zero trust enforcement levels are bound to deployment profiles. The profile determines which zero trust posture group is active:
+
+| Profile | Zero Trust Posture | Data Boundary | Federation |
+|---------|-------------------|---------------|-----------|
+| `minimal` | none | public/internal only | Not recommended |
+| `dev` | boundary | up to confidential | Permitted with warnings |
+| `standard` | boundary | up to restricted (with third-party accreditation) | Permitted |
+| `prod` | full | up to restricted | Permitted with accreditation |
+| `fsi` | full | up to restricted (with regulatory cert) | Restricted to same jurisdiction |
+| `sovereign` | hardware_attested | sovereign stays sovereign (no crossing) | Zero crossing of sovereign data |
+
+The `sovereign` profile enforces the hardest constraint: **sovereign-classified data never crosses any boundary** — not to providers, not to federation tunnels, not to notification services with external endpoints. The enforcement is at the Data/Capability Matrix level as a `hard_constraint: true` rule that cannot be overridden by any policy.
+
+---
+
+## 8. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `ZT-001` | Network position grants zero trust. Every interaction is subject to the five-check model regardless of the caller's network location. |
+| `ZT-002` | All DCM interaction credentials are scoped, short-lived, and non-transferable. Credential lifetime is profile-governed. |
+| `ZT-003` | Data classified as `sovereign` or `classified` never crosses any DCM interaction boundary (provider dispatch, federation tunnel, notification delivery). This is a hard constraint enforced by the Data/Capability Matrix, not a configurable policy. |
+| `ZT-004` | Federation tunnels use mutual TLS with certificate pinning and per-message signing. A tunnel establishes secure transport, not implicit trust. |
+| `ZT-005` | Every interaction boundary check produces an audit record regardless of outcome. A denied interaction is audited as rigorously as a permitted one. |
+| `ACC-001` | Accreditations are first-class DCM artifacts. They follow the standard lifecycle (developing → proposed → active → deprecated → retired) and are subject to GitOps governance. |
+| `ACC-002` | Accreditation gaps (missing, expired, or revoked accreditations required for active interactions) are always high or critical severity. The Recovery Policy governs the response. |
+| `ACC-003` | PHI, sovereign, and classified field classifications are immutable once set. No policy may downgrade these classifications. |
+| `ACC-004` | The Data/Capability Authorization Matrix is enforced at every outbound interaction boundary before dispatch. Fields failing the matrix check are stripped (STRIP_FIELD) or the request is blocked (DENY_REQUEST) per the matrix declaration. |
+| `ACC-005` | DCM deployments themselves carry accreditations. A federation peer DCM can verify the remote DCM deployment's accreditation before accepting federation messages. |
+| `ACC-006` | `zero_trust_posture` is the sixth Policy Group concern type. Profile defaults are: minimal=none, dev/standard=boundary, prod/fsi=full, sovereign=hardware_attested. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/27-governance-matrix.md b/content/docs/architecture/data-model/27-governance-matrix.md
new file mode 100644
index 0000000..0103559
--- /dev/null
+++ b/content/docs/architecture/data-model/27-governance-matrix.md
@@ -0,0 +1,835 @@
+# DCM Data Model — Unified Governance Matrix
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference
+
+> **Foundation Document Reference**
+>
+> This document is a detailed reference for a specific domain of the DCM architecture.
+> The three foundational abstractions — Data, Provider, and Policy — are defined in
+> [00-foundations.md](00-foundations.md). All concepts in this document map to one or
+> more of those three abstractions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+>
+> **This document maps to: POLICY**
+>
+> The Policy abstraction — Governance Matrix Rule output schema for boundary control
+
+
+**Related Documents:** [Federated Contribution Model](28-federated-contribution-model.md) | [Accreditation and Authorization Matrix](26-accreditation-and-authorization-matrix.md) | [DCM Federation](22-dcm-federation.md) | [Policy Profiles](14-policy-profiles.md) | [Layering and Versioning](03-layering-and-versioning.md) | [Control Plane Components](25-control-plane-components.md)
+
+---
+
+> **Federated Contribution:** The Governance Matrix enforces contributor permission boundaries at artifact submission time. See the [Federated Contribution Model](28-federated-contribution-model.md) for the complete contributor permission table and the hard DENY rules applied to out-of-scope contributions.
+
+## 1. Purpose
+
+The Unified Governance Matrix is the **single, declarative, multi-dimensional control surface** that governs every cross-boundary interaction in DCM. It answers one question at every interaction point:
+
+> **Given this subject, this data, this target, and this context — is this interaction permitted, and under what conditions?**
+
+Previous DCM documents established several overlapping control mechanisms: the Data/Capability Authorization Matrix (doc 26), sovereignty constraints in federation tunnels (doc 22), BBQ-001 sovereignty checks (doc 14), and profile-governed data boundaries. The Governance Matrix unifies all of these into a single model with a single evaluation algorithm and a single enforcement point.
+
+The accreditation model and data classification model from doc 26 are inputs to the evaluation algorithm defined here. The accreditation model (Sections 2-3 of doc 26) and the zero trust interaction model (Section 5 of doc 26) remain current — the Governance Matrix consumes them as inputs.
+
+**Key properties of the Governance Matrix:**
+
+- **Fine-grained to broad** — rules can target a single field path on a specific entity, or broadly govern all data of a given classification. Both are first-class citizens of the same model.
+- **Profile-bound defaults** — every deployment profile ships with sensible default rules that are immediately operative. Operators configure overrides rather than building from scratch.
+- **Hard and soft enforcement** — hard rules cannot be relaxed by any downstream rule. Soft rules establish defaults that can be tightened but never relaxed.
+- **Single evaluation algorithm** — every interaction boundary runs the same algorithm against the same rule set. No parallel enforcement paths.
+- **Audited always** — every evaluation produces an audit record regardless of outcome.
+
+---
+
+
+## 1b. Governance Matrix and the Scoring Model
+
+The Governance Matrix is **always boolean**. This is not a design limitation — it is an explicit architectural decision.
+
+Governance Matrix decisions (ALLOW, DENY, ALLOW_WITH_CONDITIONS, STRIP_FIELD, REDACT, AUDIT_ONLY) govern whether data may cross a boundary. These are regulatory and legal facts — PHI either crosses a compliant boundary or it doesn't. "Mostly compliant" is not a legal defense. No Governance Matrix Rule may declare `scoring_weight` or `enforcement_class`.
+
+**The Governance Matrix fires before the Scoring Model evaluates.** If a Governance Matrix Rule produces DENY, the request is halted and no risk score is calculated. The score pipeline only runs for requests that have already passed all Governance Matrix checks.
+
+This ensures that scoring cannot be used to route around data sovereignty or regulatory boundaries. See [Scoring Model](29-scoring-model.md) Section 8 for the full pipeline sequence and SMX-004.
+
+
+## 2. The Four Matrix Axes
+
+Every governance matrix rule is expressed as a match across four axes. A rule fires when all declared axis conditions are satisfied.
+
+### 2.1 Axis 1 — Subject (Who)
+
+The subject is the entity initiating or involved in the interaction.
+
+```yaml
+subject:
+  type: 
+  # Subject types:
+  # actor                — human or service account making a request
+  # service_provider     — Service Provider sending/receiving data
+  # dcm_peer             — federated DCM instance
+  # external_policy_evaluation      — External Policy Evaluator receiving payload data for evaluation
+  # (prescribed infrastructure)     — data store receiving/returning state data
+  # service_provider — notification service receiving notification envelopes
+  # information_provider — Information Provider returning external data
+  # system               — DCM internal component (Request Orchestrator, etc.)
+
+  identity:
+    provider_uuid:               # specific provider instance
+    dcm_peer_uuid:               # specific federated DCM instance
+    trust_posture:   # for dcm_peer subjects
+    accreditation_level:         # accreditation type the subject holds
+    actor_role:                  # for actor subjects
+
+  tenant:
+    uuid:                        # specific Tenant
+    match: any_tenant | cross_tenant | system_tenant
+```
+
+### 2.2 Axis 2 — Data (What)
+
+The data axis declares what is being accessed, sent, or operated on. This is where field-level granularity lives.
+
+```yaml
+data:
+  # Broad controls — classification level
+  classification:
+    match:  | in: [] | minimum:  | maximum: 
+    # minimum: restricted means restricted and above (phi, sovereign, classified)
+    # maximum: internal means internal and below (public, internal)
+
+  # Resource-type scoping
+  resource_type:
+    match:  | category:  | any
+
+  # Fine-grained controls — specific field paths
+  field_paths:
+    mode: allowlist | blocklist | any
+    # allowlist: only these fields are permitted to cross the boundary
+    # blocklist: these fields are explicitly prohibited
+    # any: no field-level restriction (default)
+    paths:
+      - "fields.patient_id"
+      - "fields.diagnosis_code"
+      - "fields.treatment_plan"
+    # Supports wildcards: "fields.phi_*" matches all fields prefixed phi_
+
+  # Capability being exercised
+  capability:
+    match:  | in: [] | any
+    # Capabilities: read | write | store | replicate | export | notify |
+    #               execute | discover | query | federate
+```
+
+### 2.3 Axis 3 — Target (Where)
+
+The target is where the data is going — provider, peer DCM, storage, notification endpoint.
+
+```yaml
+target:
+  type: 
+  # service_provider | dcm_peer | (prescribed infrastructure) | service_provider |
+  # information_provider | external_policy_evaluation | external_endpoint
+
+  # Identity
+  provider_uuid:                   # specific provider
+  dcm_peer_uuid:                   # specific peer
+
+  # Sovereignty
+  sovereignty_zone:
+    match:  | in: [] | same_as_source | any
+    not_in: []                     # exclusion list
+
+  jurisdiction:
+    includes: []
+    excludes: []
+    intersects: []        # target jurisdiction overlaps with list
+
+  # Trust and accreditation
+  trust_posture:
+    match:  | minimum: 
+    # minimum: vouched means vouched or verified (not provisional)
+
+  accreditation_held:
+    includes: []              # target MUST hold these accreditations
+    not_includes: []          # target must NOT hold (exclusion pattern)
+    minimum_type:    # minimum trust level of accreditation
+```
+
+### 2.4 Axis 4 — Context (Under What Conditions)
+
+Context captures the operational conditions at the time of the interaction.
+
+```yaml
+context:
+  # Active deployment governance
+  profile:
+    deployment_posture:  | in: []
+    compliance_domains:
+      includes: []
+      not_includes: []
+
+  # Security posture
+  zero_trust_posture:
+    minimum:                      # none | boundary | full | hardware_attested
+  tls_mutual: 
+  hardware_attestation: 
+
+  # Interaction characteristics
+  federated: 
+  cross_jurisdiction: 
+  cross_tenant: 
+
+  # Time-based conditions
+  time_of_day:                    # for regulated maintenance windows
+  request_age_max:    # reject stale requests
+```
+
+---
+
+## 3. Rule Structure
+
+### 3.1 The Governance Matrix Rule
+
+```yaml
+governance_matrix_rule:
+  # Artifact metadata (standard DCM artifact)
+  artifact_metadata:
+    uuid: 
+    handle: "system/matrix/phi-federation-boundary"
+    version: "1.0.0"
+    status: active                       # developing | proposed | active | deprecated | retired
+    owned_by: { display_name: "Platform Security" }
+    tier: system | platform | tenant | resource_type | entity
+
+  description: "PHI must not cross to federated peers without HIPAA accreditation"
+  rationale: "HIPAA 45 CFR 164.502 — minimum necessary standard for PHI disclosure"
+
+  # Match conditions (all declared axes must match for rule to fire)
+  match:
+    subject: { ... }
+    data: { ... }
+    target: { ... }
+    context: { ... }
+
+  # Decision
+  decision: ALLOW | DENY | ALLOW_WITH_CONDITIONS | STRIP_FIELD | REDACT | AUDIT_ONLY
+
+  # Enforcement level
+  enforcement: hard | soft
+  # hard: cannot be relaxed by any downstream rule; ever
+  # soft: downstream rules at same or higher domain can tighten further
+
+  # Conditions that must be met for ALLOW_WITH_CONDITIONS
+  conditions:
+    - field: target.trust_posture
+      operator: minimum
+      value: verified
+    - field: context.tls_mutual
+      operator: equals
+      value: required
+    - field: target.accreditation_held
+      operator: includes
+      value: hipaa
+
+  # Field permission model (for ALLOW and ALLOW_WITH_CONDITIONS)
+  field_permissions:
+    mode: allowlist | blocklist | passthrough
+    paths: []
+    on_blocked_field: STRIP_FIELD | DENY_REQUEST | REDACT
+    # STRIP_FIELD: remove field and proceed (if field is optional)
+    # DENY_REQUEST: block entire interaction (if field is required)
+    # REDACT: replace field value with  in payload
+
+  # Audit and notification
+  audit_on: [ALLOW, DENY, STRIP_FIELD, REDACT]
+  notification_on: [DENY]
+  notification_urgency: low | medium | high | critical
+
+  # Metadata
+  applicable_profiles: [standard, prod, fsi, sovereign]    # which profiles activate this rule
+  compliance_basis: "HIPAA 45 CFR 164.502"                 # regulatory basis
+  review_required_before: "2027-01-01"                     # when rule should be reviewed
+```
+
+### 3.2 Decision Vocabulary
+
+| Decision | Meaning | Field behavior |
+|----------|---------|----------------|
+| `ALLOW` | Interaction permitted unconditionally (within field_permissions) | Fields per field_permissions |
+| `ALLOW_WITH_CONDITIONS` | Permitted only if all conditions are satisfied; DENY if conditions fail | Fields per field_permissions |
+| `DENY` | Interaction blocked; interaction does not proceed | N/A — entire interaction stopped |
+| `STRIP_FIELD` | Specific fields are removed from the payload; interaction proceeds with remaining fields | Named fields stripped |
+| `REDACT` | Specific field values replaced with ``; field presence is preserved | Named fields redacted |
+| `AUDIT_ONLY` | Interaction proceeds but is flagged in the audit trail; no blocking | All fields pass |
+
+### 3.3 Hard vs Soft Enforcement
+
+**Hard enforcement (`enforcement: hard`):**
+- The rule decision cannot be relaxed by any more-specific or higher-domain rule
+- A hard DENY is absolute — no Tenant-level, entity-level, or operator override can permit the interaction
+- Hard rules are reserved for: sovereign/classified data classification boundaries, regulatory hard requirements (HIPAA BAA requirement for PHI), and explicit security policies
+- Hard ALLOW is rare — it means this interaction is always permitted regardless of other rules (use with extreme caution)
+
+**Soft enforcement (`enforcement: soft`):**
+- The rule establishes a default that can be tightened by more-specific downstream rules
+- A soft ALLOW can be restricted to DENY or STRIP_FIELD by a more-specific rule
+- A soft DENY cannot be relaxed to ALLOW by a downstream rule (DENY always wins at the same level)
+- Most profile-level defaults are soft — they set sensible baselines that Tenants can restrict further
+
+---
+
+## 4. Evaluation Algorithm
+
+The governance matrix evaluates all matching rules and produces a single terminal decision.
+
+```
+Interaction attempt:
+  subject: { type: dcm_peer, trust_posture: verified, jurisdiction: [DE] }
+  data: { classification: phi, field_paths: [patient_id, diagnosis_code] }
+  target: { type: dcm_peer, accreditation_held: [], jurisdiction: [US] }
+  context: { federated: true, zero_trust_posture: full, tls_mutual: required }
+
+Step 1: Collect matching rules
+  Load all active governance matrix rules across all tiers
+  Evaluate match conditions for each rule against the four axes
+  Result: set of matching rules with their decisions and enforcement levels
+
+Step 2: Evaluate hard constraints first
+  For each hard DENY rule that matches:
+    → DENY immediately; record rule_uuid; no further evaluation
+  For each hard ALLOW rule that matches:
+    → Record as a hard allow candidate; still evaluate conditions
+  If hard DENY exists: terminal decision = DENY
+
+Step 3: Evaluate soft constraints by domain precedence
+  Sort matching soft rules: entity > resource_type > tenant > platform > system
+  For each precedence level, most restrictive decision wins:
+    DENY > STRIP_FIELD > REDACT > ALLOW_WITH_CONDITIONS > AUDIT_ONLY > ALLOW
+  If DENY at any level: terminal decision = DENY
+
+Step 4: Evaluate conditions for ALLOW_WITH_CONDITIONS
+  For each ALLOW_WITH_CONDITIONS rule that survived Steps 2-3:
+    Evaluate all declared conditions
+    If any condition fails: downgrade decision to DENY
+    If all conditions pass: decision remains ALLOW_WITH_CONDITIONS
+
+Step 5: Apply field permissions
+  If terminal decision is ALLOW or ALLOW_WITH_CONDITIONS:
+    Apply field_permissions from the governing rule:
+      allowlist mode: strip all fields not in the allowed list
+      blocklist mode: strip all fields in the blocked list
+      passthrough mode: all fields pass
+    For each stripped field:
+      If field is required: escalate to DENY_REQUEST
+      If field is optional: STRIP_FIELD (proceed without it)
+
+Step 6: Produce audit record
+  Record: interaction_uuid, all matching rules, terminal decision,
+          fields stripped or redacted, rule that governed the decision
+  Notification: if terminal decision is in rule's notification_on list
+
+Step 7: Enforce decision
+  ALLOW / ALLOW_WITH_CONDITIONS: interaction proceeds with permitted fields
+  DENY: interaction blocked; 403 response with governance_matrix_rule_uuid
+  STRIP_FIELD: interaction proceeds with stripped payload
+  REDACT: interaction proceeds with redacted field values
+  AUDIT_ONLY: interaction proceeds; flagged audit record written
+```
+
+---
+
+## 5. Sovereignty Zones
+
+Sovereignty zones are registered DCM artifacts that define geopolitical and regulatory boundaries. They are a first-class input to the governance matrix — rules reference zones, not raw country codes.
+
+```yaml
+sovereignty_zone:
+  artifact_metadata:
+    uuid: 
+    handle: "zones/eu-west-sovereign"
+    version: "1.0.0"
+    status: active
+    tier: system | platform
+
+  display_name: "EU Western Europe Sovereign Zone"
+  description: "GDPR-covered EU member states with NIS2 alignment"
+
+  jurisdictions: [DE, FR, NL, BE, AT, CH, LU]
+  excluded_jurisdictions: []             # explicit exclusions within the zone
+
+  data_residency_guarantee: EU           # GDPR Article 44 transfer basis
+  regulatory_frameworks: [GDPR, NIS2, eIDAS]
+
+  cross_zone_permitted: false            # data does not leave this zone by default
+  inter_zone_agreements:                 # zones this zone has data transfer agreements with
+    - zone_id: eu-north-sovereign
+      agreement_basis: "EU adequacy decision"
+      permitted_classifications: [public, internal, confidential]
+      # restricted, phi, sovereign: NOT included
+
+  # What accreditation providers must hold to operate in this zone
+  required_provider_accreditation: gdpr_adequacy | third_party
+  required_provider_accreditation_minimum_type: third_party
+```
+
+---
+
+## 6. Field-Level Controls — Complete Model
+
+### 6.1 Field Path Syntax
+
+Field paths use dot-notation to address specific fields within a DCM payload:
+
+```
+fields.                      # top-level field
+fields..           # nested field
+fields.phi_*                             # wildcard: all fields matching prefix
+fields.*                                 # all fields
+metadata.                    # metadata fields
+provenance.                  # provenance fields (rarely restricted)
+```
+
+### 6.2 Broad to Fine-Grained Rule Examples
+
+**Broadest — classification-level block:**
+```yaml
+# Block ALL phi fields from crossing to non-HIPAA peers
+match:
+  data.classification: phi
+  target.type: dcm_peer
+  target.accreditation_held.not_includes: hipaa
+decision: DENY
+enforcement: hard
+```
+
+**Mid-level — resource type + classification:**
+```yaml
+# For VM resources: restricted fields to EU zones only
+match:
+  data.classification: restricted
+  data.resource_type: Compute.VirtualMachine
+  target.sovereignty_zone.not_in: [eu-west-sovereign, eu-north-sovereign]
+decision: STRIP_FIELD
+field_permissions:
+  mode: blocklist
+  paths: ["fields.security_group_ids", "fields.network_interface_ids"]
+  on_blocked_field: STRIP_FIELD
+```
+
+**Fine-grained — specific fields:**
+```yaml
+# Allow federated phi-accredited peers to receive limited PHI fields only
+match:
+  data.classification: phi
+  target.type: dcm_peer
+  target.accreditation_held.includes: hipaa
+  target.trust_posture: verified
+decision: ALLOW_WITH_CONDITIONS
+conditions:
+  - field: context.tls_mutual
+    operator: equals
+    value: required
+  - field: context.zero_trust_posture
+    operator: minimum
+    value: full
+field_permissions:
+  mode: allowlist
+  paths:
+    - "fields.resource_type"
+    - "fields.lifecycle_state"
+    - "fields.provider_entity_id"
+    # PHI-containing fields explicitly NOT in allowlist:
+    # fields.patient_id, fields.diagnosis_code, fields.treatment_plan
+    # are stripped automatically
+  on_blocked_field: STRIP_FIELD
+```
+
+**Most specific — entity-level rule:**
+```yaml
+# Provider A: explicit PHI block regardless of accreditation
+match:
+  target.provider_uuid: provider-a-uuid
+  data.classification: phi
+decision: DENY
+enforcement: hard
+reason: "Provider A has unresolved data handling concerns — PHI explicitly prohibited"
+```
+
+### 6.3 Field-Level Redaction vs Stripping
+
+| Operation | Effect on payload | Use case |
+|-----------|------------------|----------|
+| `STRIP_FIELD` | Field entirely removed from payload | Field is optional; receiver has no need to know it exists |
+| `REDACT` | Field present with value `` | Receiver needs to know field exists but not its value (e.g., audit evidence that a field was present) |
+| `DENY_REQUEST` | Entire interaction blocked | Field is required for the operation to make sense; stripping would produce invalid state |
+
+---
+
+## 7. Profile-Bound Default Matrix Rules
+
+Every deployment profile activates a set of default governance matrix rules. These are soft rules (tightenable by Tenant/resource-type overrides) unless marked hard.
+
+### 7.1 minimal Profile Defaults
+
+```yaml
+profile_matrix_defaults:
+  profile: minimal
+  rules:
+    - handle: "system/matrix/minimal-sovereign-hard"
+      enforcement: hard
+      match:
+        data.classification: [sovereign, classified]
+        target.type: [dcm_peer, service_provider, service_provider]
+      decision: DENY
+      reason: "Sovereign and classified data never crosses any boundary — any profile"
+
+    - handle: "system/matrix/minimal-passthrough"
+      enforcement: soft
+      match:
+        data.classification: [public, internal]
+        target.type: any
+      decision: ALLOW
+      field_permissions:
+        mode: passthrough
+```
+
+### 7.2 dev Profile Defaults
+
+```yaml
+profile_matrix_defaults:
+  profile: dev
+  inherits: minimal
+  additional_rules:
+    - handle: "system/matrix/dev-confidential-allow"
+      enforcement: soft
+      match:
+        data.classification: confidential
+        target.type: [service_provider, dcm_peer]
+        target.trust_posture: [verified, vouched, provisional]
+      decision: ALLOW_WITH_CONDITIONS
+      conditions:
+        - field: context.tls_mutual
+          operator: equals
+          value: required
+      field_permissions:
+        mode: passthrough
+      # Dev allows confidential to flow broadly; standard+ tightens this
+```
+
+### 7.3 standard Profile Defaults
+
+```yaml
+profile_matrix_defaults:
+  profile: standard
+  inherits: minimal
+  additional_rules:
+    - handle: "system/matrix/standard-restricted-accreditation"
+      enforcement: soft
+      match:
+        data.classification: restricted
+        target.type: [service_provider, dcm_peer]
+      decision: ALLOW_WITH_CONDITIONS
+      conditions:
+        - field: target.accreditation_held
+          operator: minimum_type
+          value: third_party
+        - field: context.tls_mutual
+          operator: equals
+          value: required
+      field_permissions:
+        mode: passthrough
+
+    - handle: "system/matrix/standard-phi-deny-default"
+      enforcement: soft
+      match:
+        data.classification: phi
+        target.type: [service_provider, dcm_peer]
+      decision: DENY
+      # Tenants with HIPAA compliance domain active override this with their own rules
+```
+
+### 7.4 prod Profile Defaults
+
+```yaml
+profile_matrix_defaults:
+  profile: prod
+  inherits: standard
+  additional_rules:
+    - handle: "system/matrix/prod-federation-verified-only"
+      enforcement: soft
+      match:
+        data.classification: [confidential, restricted]
+        target.type: dcm_peer
+        target.trust_posture: [vouched, provisional]
+      decision: DENY
+      # prod: only verified peers receive confidential+ data
+
+    - handle: "system/matrix/prod-notification-restricted"
+      enforcement: soft
+      match:
+        data.classification: restricted
+        target.type: service_provider
+      decision: STRIP_FIELD
+      field_permissions:
+        mode: blocklist
+        paths: ["fields.*"]      # strip all payload fields from notifications
+        # Notification envelope metadata (entity_uuid, event_type) passes through
+        # Actual field values do not appear in notification payloads for restricted data
+```
+
+### 7.5 fsi Profile Defaults
+
+```yaml
+profile_matrix_defaults:
+  profile: fsi
+  inherits: prod
+  additional_rules:
+    - handle: "system/matrix/fsi-cross-jurisdiction-deny"
+      enforcement: hard
+      match:
+        data.classification: [restricted, phi, pci, sovereign]
+        target.type: [service_provider, dcm_peer]
+        context.cross_jurisdiction: true
+      decision: DENY
+      reason: "FSI profile: regulated data does not cross jurisdictional boundaries"
+
+    - handle: "system/matrix/fsi-phi-baa-required"
+      enforcement: hard
+      match:
+        data.classification: phi
+        target.type: [service_provider, dcm_peer]
+      decision: ALLOW_WITH_CONDITIONS
+      conditions:
+        - field: target.accreditation_held
+          operator: includes
+          value: hipaa_baa
+        - field: target.trust_posture
+          operator: minimum
+          value: verified
+        - field: context.zero_trust_posture
+          operator: minimum
+          value: full
+      field_permissions:
+        mode: passthrough          # HIPAA-accredited verified peers get full PHI scope
+        # Tenant-level rules can further restrict to specific field paths
+
+    - handle: "system/matrix/fsi-pci-qsa-required"
+      enforcement: hard
+      match:
+        data.classification: pci
+        target.type: [service_provider, dcm_peer]
+      decision: ALLOW_WITH_CONDITIONS
+      conditions:
+        - field: target.accreditation_held
+          operator: includes
+          value: pci_dss_qsa
+        - field: context.zero_trust_posture
+          operator: minimum
+          value: full
+```
+
+### 7.6 sovereign Profile Defaults
+
+```yaml
+profile_matrix_defaults:
+  profile: sovereign
+  inherits: fsi
+  additional_rules:
+    - handle: "system/matrix/sovereign-no-federation-sensitive"
+      enforcement: hard
+      match:
+        data.classification: [restricted, phi, pci, sovereign, classified]
+        target.type: dcm_peer
+      decision: DENY
+      reason: "Sovereign profile: sensitive data never crosses DCM federation boundaries"
+
+    - handle: "system/matrix/sovereign-internal-only-federation"
+      enforcement: hard
+      match:
+        data.classification: [public, internal]
+        target.type: dcm_peer
+        context.zero_trust_posture:
+          not_minimum: hardware_attested
+      decision: DENY
+      reason: "Sovereign profile: federation requires hardware attestation"
+
+    - handle: "system/matrix/sovereign-provider-sovereign-zone-only"
+      enforcement: hard
+      match:
+        data.classification: [restricted, phi, pci, sovereign, classified]
+        target.type: service_provider
+        target.sovereignty_zone.not_in: []
+      decision: DENY
+      reason: "Sovereign profile: sensitive data only to providers in declared sovereignty zone"
+```
+
+---
+
+## 8. Compliance Domain Matrix Rules
+
+When a compliance domain is active, its matrix rules are automatically added to the active rule set.
+
+### 8.1 HIPAA Compliance Domain Matrix
+
+```yaml
+compliance_domain_matrix:
+  domain: hipaa
+  rules:
+    - handle: "system/matrix/hipaa-phi-minimum-necessary"
+      enforcement: hard
+      match:
+        data.classification: phi
+        target.type: any
+      decision: ALLOW_WITH_CONDITIONS
+      conditions:
+        - principle: minimum_necessary    # only fields required for the specific purpose
+      field_permissions:
+        mode: blocklist                   # default: all fields except explicitly blocked
+        paths: []                         # Tenant adds specific field blocks
+        on_blocked_field: STRIP_FIELD
+
+    - handle: "system/matrix/hipaa-phi-no-export"
+      enforcement: hard
+      match:
+        data.classification: phi
+        data.capability: export
+      decision: DENY
+      reason: "HIPAA: PHI export to external systems requires explicit BAA and regulatory review"
+
+    - handle: "system/matrix/hipaa-audit-all-phi"
+      enforcement: hard
+      match:
+        data.classification: phi
+        target.type: any
+      decision: AUDIT_ONLY           # added to all PHI interactions — does not block
+      audit_on: [ALLOW, DENY, STRIP_FIELD]
+      # Every PHI interaction produces an audit record — HIPAA requirement
+```
+
+### 8.2 GDPR Compliance Domain Matrix
+
+```yaml
+compliance_domain_matrix:
+  domain: gdpr
+  rules:
+    - handle: "system/matrix/gdpr-eu-residency"
+      enforcement: hard
+      match:
+        data.classification: [restricted, phi]
+        target.sovereignty_zone.not_in: 
+        context.compliance_domains.includes: gdpr
+      decision: DENY
+      reason: "GDPR Article 44: personal data transfer outside EU requires adequacy decision"
+
+    - handle: "system/matrix/gdpr-right-to-erasure-fields"
+      enforcement: hard
+      match:
+        data.field_paths.includes: ["fields.personal_identifier_*", "fields.contact_*"]
+        data.capability: [store, replicate]
+        target.accreditation_held.not_includes: gdpr_adequacy
+      decision: STRIP_FIELD
+      field_permissions:
+        mode: blocklist
+        paths: ["fields.personal_identifier_*", "fields.contact_*"]
+```
+
+---
+
+## 9. Tenant and Resource-Type Override Rules
+
+Tenants and resource-type specifications declare additional rules that compose with system and profile rules per the standard precedence model.
+
+### 9.1 Tenant Override Rule
+
+```yaml
+# Tenant payments-team: additional restriction on PHI fields
+governance_matrix_rule:
+  artifact_metadata:
+    tier: tenant
+    handle: "tenant/payments/phi-field-restriction"
+  
+  match:
+    subject.tenant.uuid: payments-tenant-uuid
+    data.classification: phi
+    target.type: dcm_peer
+    target.accreditation_held.includes: hipaa
+  
+  decision: ALLOW_WITH_CONDITIONS
+  conditions:
+    - field: target.trust_posture
+      operator: equals
+      value: verified                    # only verified (not vouched)
+  
+  field_permissions:
+    mode: allowlist                      # tighter than the fsi default (passthrough)
+    paths:
+      - "fields.resource_type"
+      - "fields.lifecycle_state"
+      # PHI-containing fields not listed → automatically stripped
+    on_blocked_field: STRIP_FIELD
+```
+
+### 9.2 Resource-Type Override Rule
+
+```yaml
+# For Patient Record resources: maximum restriction regardless of Tenant settings
+governance_matrix_rule:
+  artifact_metadata:
+    tier: resource_type
+    handle: "resource-type/patient-record/no-federation"
+  
+  match:
+    data.resource_type: Healthcare.PatientRecord
+    target.type: dcm_peer
+  
+  decision: DENY
+  enforcement: hard
+  reason: "Patient Record entities are never federated — local only"
+```
+
+---
+
+## 10. Governance Matrix in the Registration Flow
+
+When a provider attempts to register with DCM, the governance matrix is evaluated before the registration is accepted. This answers: "Is a provider of this type, with these accreditations, in this sovereignty zone, permitted to register in this DCM deployment?"
+
+```
+Provider submits registration
+  │
+  ▼ Governance matrix evaluation:
+  │   subject: { type: , accreditation_held: [...], sovereignty_zone:  }
+  │   data: { capability: register }
+  │   target: { type: dcm_instance, sovereignty_zone:  }
+  │   context: { profile: , compliance_domains: [...] }
+  │
+  ├── DENY: registration rejected immediately
+  │   Provider type not permitted in this profile
+  │   Provider in excluded jurisdiction
+  │   Required accreditation not held
+  │
+  └── ALLOW / ALLOW_WITH_CONDITIONS: registration proceeds to validation pipeline
+```
+
+---
+
+## 11. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `GMX-001` | The Governance Matrix is the single enforcement point for all cross-boundary data and capability decisions. Parallel enforcement mechanisms (standalone sovereignty checks, standalone accreditation checks) are inputs to the matrix — not independent enforcement paths. |
+| `GMX-002` | Hard rules cannot be relaxed by any downstream rule at any domain level. Hard DENY is absolute. |
+| `GMX-003` | Soft rules establish defaults that can only be tightened by downstream rules. Soft DENY cannot be relaxed to ALLOW by a more-specific rule. |
+| `GMX-004` | Sovereign and classified data classifications carry hard DENY rules for all federation and external provider interactions in all profiles including minimal. This is the one rule that cannot be changed by any configuration. |
+| `GMX-005` | Every governance matrix evaluation produces an audit record regardless of outcome. |
+| `GMX-006` | Field-level stripping (STRIP_FIELD) is always audited with the field path and the rule_uuid that governed the stripping. |
+| `GMX-007` | Profile default matrix rules are soft unless explicitly marked hard. Tenant and resource-type rules can tighten profile defaults but cannot relax hard rules. |
+| `GMX-008` | Compliance domain matrix rules are automatically added to the active rule set when the compliance domain is active. They compose with profile rules — they do not replace them. |
+| `GMX-009` | The Governance Matrix is evaluated before provider dispatch, before federation tunnel data transmission, before notification delivery, and before any cross-boundary capability invocation. |
+| `GMX-010` | A STRIP_FIELD decision that removes a required field escalates to DENY_REQUEST automatically. Optional fields may be stripped without blocking the interaction. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/28-federated-contribution-model.md b/content/docs/architecture/data-model/28-federated-contribution-model.md
new file mode 100644
index 0000000..c5e7643
--- /dev/null
+++ b/content/docs/architecture/data-model/28-federated-contribution-model.md
@@ -0,0 +1,604 @@
+# DCM Data Model — Federated Contribution Model
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Read This First for Multi-User Data Governance
+**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Layering and Versioning](03-layering-and-versioning.md) | [Policy Profiles](14-policy-profiles.md) | [Registry Governance](20-registry-governance.md) | [DCM Federation](22-dcm-federation.md) | [Governance Matrix](27-governance-matrix.md) | [Consumer API](../specifications/consumer-api-spec.md)
+
+> **This document maps to: DATA + POLICY + PROVIDER**
+>
+> The federated contribution model governs how Data artifacts are created and managed across all contributor types. It extends the Data abstraction with explicit contributor identity, applies Policies to govern contribution permissions and review requirements, and uses the Provider abstraction for cross-instance federation of contributions.
+
+---
+
+> **Authority Tier Reference:** Contribution approval tiers (`reviewed`, `verified`, `authorized`) are named positions in the [Authority Tier Model](32-authority-tier-model.md) ordered list. Organizations may add custom tiers between existing ones. Changes to the tier registry that affect contribution approval requirements trigger impact detection (ATM-009–ATM-012).
+
+## 1. Purpose and Principle
+
+DCM is a multi-user, multi-contributor system. Platform admins are not the only actors who create data. Consumers define their own service configurations, resource groups, and policy overlays. Service Providers publish their own resource type specs and catalog items. Peer DCM instances contribute registry entries across federation boundaries. Organizations extend DCM with their own artifact types.
+
+**The federated contribution model** is the governing framework for how all of these actors create, review, activate, and lifecycle-manage DCM data artifacts. It extends the Data abstraction with one additional universal property:
+
+> **Every DCM data artifact has a contributor** — an actor or system that authored it — and that contributor's role determines what review is required before the artifact becomes active.
+
+This is not a special model for special cases. It is the same GitOps PR workflow, the same lifecycle (developing → proposed → active → deprecated → retired), and the same domain precedence (system → platform → tenant → resource_type → entity) — applied consistently across all contributor types.
+
+**The core principle:** DCM defaults to a federated model for data creation, import, usage, and lifecycle. Every authorized actor can contribute within the bounds their role permits. The Governance Matrix governs the boundaries. The GitOps PR flow provides the review mechanism. Profile-bound auto-approval policies determine what needs human review and what does not.
+
+---
+
+## 2. Contributor Types and Permissions
+
+### 2.1 The Four Contributor Types
+
+| Contributor | Examples | Default domain scope |
+|-------------|---------|---------------------|
+| **Platform Admin** | DCM operators, SRE team | system, platform — all artifact types |
+| **Consumer / Tenant** | Application teams, developers, Tenant admins | tenant — scoped to their Tenant |
+| **Service Provider** | Infrastructure teams, automation platforms | provider — resource types they offer |
+| **Peer DCM** | Federated DCM instances, Hub DCM, community registry | federated — governed by federation trust posture |
+
+### 2.2 What Each Contributor Can Contribute
+
+**Platform Admin:** All artifact types at all domain levels. No restrictions within the DCM deployment.
+
+**Consumer / Tenant:**
+- Tenant-domain policies (GateKeeper, Transformation, Recovery, Lifecycle, Orchestration Flow)
+- Resource groups and group memberships within their Tenant
+- Notification subscriptions for their Tenant
+- Webhook registrations for their Tenant
+- Custom catalog item definitions (within their Tenant's resource type scope)
+- Tenant-scoped data layers (Request Layer — directly attached to their requests)
+- Cross-tenant authorization records (requires counterpart Tenant acceptance)
+
+**Service Provider:**
+- Resource Type Specifications for resource types they offer (Organization or Verified Community tier)
+- Provider Catalog Items for their registered resource types
+- Service Layers for their offered resource types
+- Provider-specific GateKeeper and Validation policies (provider domain)
+- Cost metadata updates
+- Sovereignty declaration updates
+
+**Peer DCM:**
+- Registry entries (Resource Type Specs, provider type definitions) contributed through federation channels
+- Policy bundles contributed through verified federation relationships
+- Layer contributions through Hub DCM governance
+- Accreditation vouching for providers registered with the contributing DCM
+
+### 2.3 What Each Contributor Cannot Contribute
+
+| Contributor | Cannot contribute |
+|-------------|-----------------|
+| Consumer | System or platform domain policies; core layers; resource type specs (unless granted elevated role); provider catalog items for other providers |
+| Service Provider | Policies outside their resource type domain; core layers; other providers' catalog items; tenant-domain policies for specific Tenants |
+| Peer DCM | Artifacts above the federation trust level granted; system-domain policies without authorized approval; sovereignty zones for jurisdictions not in their declared scope |
+
+---
+
+## 3. Contribution Artifact Types
+
+Every DCM data artifact type has a declared set of contributor permissions. The following table specifies who can contribute each type and at what domain level:
+
+| Artifact Type | Platform Admin | Consumer/Tenant | Service Provider | Peer DCM |
+|--------------|---------------|-----------------|-----------------|---------|
+| Resource Type Specification | All tiers | ❌ | Org + Community tiers | Community tier (via federation) |
+| Provider Catalog Item | All | ❌ | Their resource types only | ❌ |
+| Core Layer | ✅ | ❌ | ❌ | ❌ |
+| Service Layer | ✅ | ❌ | Their resource types only | ❌ |
+| Request Layer | ✅ | Their requests only | ❌ | ❌ |
+| GateKeeper Policy | All domains | Tenant domain only | Provider domain only | Via federation governance |
+| Transformation Policy | All domains | Tenant domain only | Provider domain only | Via federation governance |
+| Recovery Policy | All domains | Tenant domain only | Provider domain only | Via federation governance |
+| Orchestration Flow Policy | All domains | Tenant domain only | ❌ | ❌ |
+| Governance Matrix Rule | All domains | Tenant domain only | ❌ | ❌ |
+| Lifecycle Policy | All domains | Tenant domain (on their entities) | ❌ | ❌ |
+| Accreditation | All | ❌ | Their own accreditations | Vouching for their providers |
+| Sovereignty Zone | ✅ | ❌ | ❌ | ❌ |
+| DCMGroup / Resource Group | All | Tenant domain only | ❌ | ❌ |
+| Notification Subscription | All | Their Tenant only | ❌ | ❌ |
+| Webhook Registration | All | Their Tenant only | ❌ | ❌ |
+
+---
+
+## 4. The Contribution Flow
+
+All contributions — regardless of contributor type — flow through the same GitOps PR model. What varies is:
+- **The target store** (which GitOps repository receives the PR)
+- **The review requirement** (auto-approval vs human review vs dual approval)
+- **The shadow mode behavior** (policies enter shadow mode automatically; other artifacts enter proposed status)
+
+### 4.1 The Universal Contribution Pipeline
+
+```
+Contributor authors a data artifact
+  │
+  │ Via one of three contribution surfaces:
+  ├── Flow GUI Canvas / Policy Authoring Interface
+  ├── Direct API (POST /api/v1/contribute/{artifact_type})
+  └── Git PR directly to the target repository
+  │
+  ▼ Artifact submitted → status: developing (local only)
+  │
+  ▼ Contributor submits for review → status: proposed
+  │   For policies: shadow mode activates automatically
+  │   For other artifacts: staged in proposed state
+  │
+  ▼ Governance Matrix evaluates the contribution:
+  │   Is this contributor permitted to contribute this artifact type?
+  │   Is the artifact in the correct domain for this contributor?
+  │   Does the artifact pass structural validation?
+  │   DENY → rejected with reason; no further processing
+  │
+  ▼ Review flow (per profile + artifact type):
+  │   auto:          artifact activates immediately
+  │   reviewed:  one platform admin or designated reviewer approves
+  │   verified: two independent reviewers approve
+  │   authorized:   N members of declared authority group record decisions via Admin API
+  │
+  ▼ On approval → status: active
+  │   For policies: shadow mode results reviewed; full enforcement begins
+  │   For resource type specs: available in registry
+  │   For catalog items: visible in service catalog (per RBAC)
+  │
+  ▼ Lifecycle managed by contributor (deprecate, retire)
+      Subject to platform admin override at any time
+```
+
+### 4.2 Review Requirements by Contributor and Artifact Type
+
+Review requirements are profile-governed. The table below shows defaults:
+
+| Artifact Type | Platform Admin | Consumer/Tenant | Service Provider |
+|--------------|---------------|-----------------|-----------------|
+| Tenant-domain policy | auto | reviewed (standard+) | reviewed |
+| Resource Type Spec (Org tier) | auto | ❌ | reviewed |
+| Resource Type Spec (Community tier) | reviewed | ❌ | verified |
+| Provider Catalog Item | auto | ❌ | reviewed |
+| Service Layer | auto | ❌ | reviewed |
+| Governance Matrix Rule (tenant) | auto | verified | ❌ |
+| Governance Matrix Rule (platform) | reviewed | ❌ | ❌ |
+| Accreditation | reviewed | ❌ | reviewed |
+
+**Profile overrides:**
+- `dev`: most contributions auto-approved; shadow mode optional
+- `standard`: consumer policies require reviewed; provider specs require reviewed
+- `prod`: consumer governance matrix rules require verified; provider specs require verified
+- `fsi`: all contributions require verified; community registry entries require authorized
+- `sovereign`: all contributions require authorized approval
+
+---
+
+## 5. Consumer Contribution Model
+
+### 5.1 Consumer as Policy Author
+
+Consumers are not passive requesters. Tenant admins and designated Tenant members with `policy_author` role can define and maintain their own Tenant-domain policies directly.
+
+**What this enables:**
+- A Payments team defining their own cost ceiling GateKeeper: "Reject any VM request over $500/month"
+- An Operations team defining their own expiry Transformation: "All dev VMs get a 30-day TTL injected"
+- A Security team defining their own governance matrix rule: "Our Tenant never sends confidential data to unaccredited providers"
+
+**The scope constraint is enforced by DCM, not by convention.** When a consumer submits a policy with `domain: tenant`, DCM validates that the contributing actor belongs to that Tenant. Attempts to submit platform or system domain policies are rejected by the Governance Matrix at contribution time.
+
+### 5.2 Consumer Contribution API
+
+```
+POST /api/v1/contribute/policy
+
+Authorization: Bearer 
+X-DCM-Tenant: 
+
+{
+  "policy_type": "gatekeeper",
+  "handle": "tenant/payments/gatekeeper/cost-ceiling",
+  "domain": "tenant",
+  "concern_type": "operational",
+  "enforcement": "soft",
+  "match": {
+    "payload_type": "request.policies_evaluated",
+    "conditions": [
+      { "field": "payload.cost_estimate.per_month", "operator": "gt", "value": 500 }
+    ]
+  },
+  "output": {
+    "decision": "deny",
+    "reason": "Estimated monthly cost exceeds Tenant budget ceiling of $500"
+  },
+  "shadow_mode": true,           # start in shadow mode (proposed status)
+  "commit_message": "Add monthly cost ceiling GateKeeper for Payments Tenant"
+}
+
+Response 202 Accepted:
+{
+  "contribution_uuid": "",
+  "artifact_type": "policy",
+  "policy_handle": "tenant/payments/gatekeeper/cost-ceiling",
+  "status": "proposed",
+  "shadow_mode": true,
+  "review_required": true,
+  "review_type": "reviewed",
+  "reviewer_group": "platform-admins",
+  "pr_url": "https://git.corp.example.com/dcm-policies/pulls/145",
+  "shadow_results_url": "/flow/api/v1/shadow/"
+}
+```
+
+### 5.3 Consumer Resource Group and Service Definitions
+
+Consumers can define their own resource groups and service compositions within their Tenant:
+
+```
+POST /api/v1/contribute/resource-group
+
+{
+  "handle": "tenant/payments/groups/prod-vms",
+  "display_name": "Production VMs — Payments",
+  "group_class": "resource_grouping",
+  "description": "All production VMs owned by the Payments team",
+  "membership_policy": {
+    "auto_include": {
+      "resource_type": "Compute.VirtualMachine",
+      "tags": { "team": "payments", "env": "production" }
+    }
+  }
+}
+```
+
+---
+
+## 6. Service Provider Contribution Model
+
+### 6.1 Provider as Resource Type Publisher
+
+**Resource Type Authority vs Service Provider Publisher — the distinction:**
+
+The **Resource Type Authority** (doc 05, Section 2.1c) is the team responsible for
+defining and maintaining the Resource Type Specification — the vendor-neutral contract
+all providers must implement. The authority may be a DCM Project maintainer (Tier 1),
+a named community maintainer (Tier 2), or an organization's domain team (Tier 3).
+
+A **Service Provider** implements that specification in their Catalog Item and publishes
+provider-specific extensions and Service Layers on top of it. A provider is the publisher
+of their catalog item — not necessarily the author of the underlying Resource Type Spec.
+
+In many cases they are the same team: a networking team may both define `Network.VLAN`
+as the Resource Type Authority AND register as the Service Provider that realizes VLANs.
+In other cases they are different: a platform team defines `Compute.VirtualMachine`
+as the Resource Type Authority, and multiple compute providers (Nutanix, VMware, bare
+metal) each independently register Catalog Items implementing that specification.
+
+Service Providers are not just execution targets — they are first-class contributors of the resource type definitions that consumers request. A provider registering a new virtual machine offering publishes the Resource Type Specification, the Catalog Item, and the Service Layer that consumers use to interact with it.
+
+**What this enables:**
+- A storage team publishing a new `Storage.DistributedVolume` resource type with its full schema, constraints, and cost model
+- A networking team publishing provider-specific VLAN configurations as a Catalog Item with their own Service Layer injecting provider-specific defaults
+- A platform team publishing an updated `Compute.VirtualMachine` spec with new fields and deprecating old ones
+
+**Provider contributions flow through the same registry governance as all other registry entries** — submitted as PRs to the organization registry, reviewed per profile requirements, activated when approved.
+
+### 6.2 Provider Contribution API
+
+```
+POST /api/v1/provider/contribute/resource-type-spec
+
+Authorization: mTLS + provider credential
+
+{
+  "resource_type_fqn": "Storage.DistributedVolume",
+  "tier": "organization",
+  "version": "1.0.0",
+  "schema": {
+    "fields": [
+      { "field_name": "capacity_gb", "type": "integer", "required": true },
+      { "field_name": "replication_factor", "type": "integer",
+        "default": 3, "constraint": { "min": 1, "max": 5 } },
+      { "field_name": "encryption_at_rest", "type": "boolean", "default": true }
+    ]
+  },
+  "portability_class": "provider_specific",
+  "commit_message": "Publish DistributedVolume resource type v1.0.0"
+}
+
+Response 202 Accepted:
+{
+  "contribution_uuid": "",
+  "resource_type_fqn": "Storage.DistributedVolume",
+  "status": "proposed",
+  "review_required": true,
+  "review_type": "reviewed",
+  "pr_url": "https://git.corp.example.com/dcm-registry/pulls/89"
+}
+```
+
+### 6.3 Provider Service Layer Contribution
+
+Providers contribute Service Layers that DCM applies during request assembly for their resource types:
+
+```
+POST /api/v1/provider/contribute/service-layer
+
+{
+  "resource_type_fqn": "Compute.VirtualMachine",
+  "layer_handle": "providers/eu-west-prod-1/layers/vm-defaults",
+  "layer_domain": "service",
+  "provider_uuid": "",
+  "version": "2.0.0",
+  "fields": {
+    "hypervisor": { "value": "KVM", "metadata": { "override": "immutable" } },
+    "network_segment": { "value": "prod-segment-01" },
+    "backup_enabled": { "value": true }
+  }
+}
+```
+
+---
+
+## 7. Federation Contribution Model
+
+### 7.1 Peer DCM as Contributor
+
+A federated peer DCM is a contributor to the receiving DCM's artifact stores, subject to the federation trust posture. This enables:
+
+- **Hub DCM contributing policy templates** to Regional DCMs — standard compliance policies distributed from a central Hub
+- **Community DCM registry contributions** — a community-maintained DCM instance publishing Verified Community resource type specs to subscribing organizations
+- **Provider contributions across DCM boundaries** — a provider registered with DCM-A contributing its resource type specs to DCM-B through a verified federation relationship
+
+### 7.2 Federation Contribution Trust Model
+
+Federation contributions inherit the federation trust posture of the contributing peer:
+
+| Peer trust posture | Contribution review requirement | Artifact types permitted |
+|-------------------|--------------------------------|------------------------|
+| `verified` | reviewed (standard+); auto (dev) | Registry entries, policy templates, service layers |
+| `vouched` | reviewed always | Registry entries, service layers only |
+| `provisional` | `authorized` tier approval | Registry entries only (no policies) |
+
+**Hard rule:** A peer DCM cannot contribute artifacts at a higher domain level than its trust posture permits. A `vouched` peer cannot contribute system-domain policies. This is enforced by the Governance Matrix at the federation contribution boundary.
+
+### 7.3 Federation Contribution Flow
+
+```
+Peer DCM publishes a contribution bundle:
+  Content: resource type specs, policy templates, or layers
+  Transport: federation tunnel (mTLS, signed, scoped credential)
+  Metadata: contributing_dcm_uuid, trust_posture, artifact_list
+
+Receiving DCM evaluates:
+  1. Governance Matrix: is this peer permitted to contribute this artifact type?
+  2. Signature verification: bundle signed by peer's private key?
+  3. Structural validation: artifacts conform to DCM schemas?
+  4. Domain scope check: artifacts within peer's permitted domain?
+
+On validation pass:
+  Artifacts enter proposed status in receiving DCM's policy/registry store
+  Review flow per receiving DCM's profile + peer trust posture
+
+On approval:
+  Artifacts become active in receiving DCM
+  Source attribution: contributed_by.dcm_uuid, contributed_by.trust_posture
+```
+
+### 7.4 Hub DCM Policy Distribution
+
+In a Hub-Spoke federation, the Hub DCM is the authoritative source for platform-wide policy templates. Regional DCMs subscribe to the Hub's policy distribution feed:
+
+```yaml
+hub_policy_distribution:
+  hub_dcm_uuid: 
+  distribution_type: push          # Hub pushes on policy change
+  auto_approve_from_hub:           # profile-governed; security-first: prod+ always requires review
+    minimal: true
+    dev: true
+    standard: true
+    prod: false         # reviewed required even from verified Hub
+    fsi: false          # verified required
+    sovereign: false    # authorized approval required
+  policy_handles_subscribed:
+    - "system/compliance/hipaa/*"
+    - "system/governance/drift-remediation"
+  # Regional DCM always reviews before activating
+  # Hub cannot force-activate policies on Regional DCMs
+```
+
+---
+
+## 8. Artifact Lifecycle Across Contributors
+
+### 8.1 Contributor Ownership and Transfer
+
+Every artifact is owned by its contributor at creation. Ownership can be transferred:
+- Consumer-authored policies transfer to a new Tenant admin when the original actor departs
+- Provider-contributed catalog items remain owned by the provider registration
+- Federation-contributed artifacts are owned by the contributing peer DCM
+
+Ownership transfer requires the receiving owner's explicit acceptance (same model as entity ownership transfer in the Consumer API).
+
+### 8.2 Platform Admin Override
+
+Platform admins can override any contributor's artifact lifecycle at any time:
+- Suspend an active consumer-authored policy that is causing harm
+- Retire a provider-contributed resource type spec that is no longer safe
+- Reject a proposed federation contribution without providing a public reason (security discretion)
+
+Override actions are always audited with the overriding admin's actor UUID and reason.
+
+### 8.3 Deprecation and Sunset
+
+Contributors deprecate their own artifacts. When a Service Provider deprecates a resource type spec:
+1. All consumers using that type receive deprecation notifications
+2. A sunset period is declared (minimum: P30D for standard profile; P90D for prod/fsi/sovereign)
+3. During sunset: new requests using the deprecated spec are warned; existing resources unaffected
+4. After sunset: new requests using the deprecated spec are blocked
+5. Platform admin must confirm final retirement
+
+### 8.4 Orphaned Artifacts
+
+When a contributor's access is revoked (actor departs, provider deregisters, peer DCM federation ends):
+- Active artifacts remain active — orphaned artifacts do not automatically deactivate
+- A platform admin is notified: "Artifact tenant/payments/gatekeeper/cost-ceiling has no active owner"
+- Platform admin assigns a new owner or explicitly retires the artifact
+- Auto-retire-on-orphan is configurable per profile (enabled in sovereign profile; disabled in standard)
+
+---
+
+## 9. The Contribution Store
+
+All contributed artifacts are stored in the GitOps store with contributor attribution. The directory structure reflects the contributor hierarchy:
+
+```
+dcm-policy-store/
+  system/                     # Platform admin authored; DCM built-in
+    compliance/
+    governance/
+    orchestration/
+  platform/                   # Platform admin authored; deployment-specific
+    security/
+    operations/
+  tenant/
+    /           # Consumer/Tenant authored
+      gatekeeper/
+      transformation/
+      groups/
+  provider/
+    /         # Provider authored
+      layers/
+      policies/
+  federated/
+    /           # Peer DCM contributed
+      registry/
+      policy-templates/
+
+dcm-registry/
+  core/                       # DCM project maintained
+  community/                  # Community contributed (via community DCM)
+    /
+  organization/               # Organization contributed
+    /
+```
+
+Every artifact in the store includes a `contributed_by` block in its artifact metadata:
+
+```yaml
+artifact_metadata:
+  uuid: 
+  handle: "tenant/payments/gatekeeper/cost-ceiling"
+  version: "1.0.0"
+  status: active
+  contributed_by:
+    contributor_type: consumer       # platform_admin | consumer | service_provider | peer_dcm
+    actor_uuid:                # for consumer/platform_admin contributions
+    tenant_uuid:               # for consumer contributions
+    provider_uuid:             # for provider contributions
+    peer_dcm_uuid:             # for federation contributions
+    contribution_method: api         # api | flow_gui | git_pr | federation_push
+    pr_url: "https://..."            # if submitted via PR
+    reviewed_by: []      # actors who approved
+    reviewed_at: 
+```
+
+---
+
+## 10. Profile-Governed Contribution Defaults
+
+Each deployment profile has a default contribution policy that governs auto-approval eligibility, required review, and shadow mode defaults:
+
+```yaml
+contribution_policy:
+  minimal:
+    consumer_policy_auto_approve: true             # ease of use: homelab auto-approves
+    provider_spec_auto_approve: true
+    federation_contribution_auto_approve: true    # homelab: federation auto-approved
+    shadow_mode_default: true                     # security: shadow always on even in minimal
+
+  dev:
+    consumer_policy_auto_approve: true
+    provider_spec_auto_approve: true
+    federation_contribution_auto_approve: false   # reviewed for federation
+    shadow_mode_default: true                     # shadow mode on by default
+
+  standard:
+    consumer_policy_auto_approve: false           # reviewed for all policies
+    provider_spec_auto_approve: false
+    federation_contribution_auto_approve: false
+    shadow_mode_default: true
+    shadow_review_period: P7D                     # 7 days of shadow before promotion
+
+  prod:
+    consumer_policy_auto_approve: false
+    consumer_governance_matrix_requires: verified
+    provider_spec_auto_approve: false
+    provider_spec_requires: reviewed
+    federation_contribution_requires: reviewed
+    shadow_mode_default: true
+    shadow_review_period: P14D
+
+  fsi:
+    consumer_policy_auto_approve: false
+    consumer_policy_requires: verified
+    consumer_governance_matrix_requires: verified
+    provider_spec_requires: verified
+    federation_contribution_requires: verified
+    shadow_mode_default: true
+    shadow_review_period: P30D
+    min_shadow_divergence_review: true            # must review all divergence cases
+
+  sovereign:
+    consumer_policy_requires: authorized
+    provider_spec_requires: authorized
+    federation_contribution_requires: authorized
+    shadow_mode_default: true
+    shadow_review_period: P30D
+    min_shadow_divergence_review: true
+    auto_retire_orphaned_artifacts: true          # orphaned artifacts retire automatically
+```
+
+---
+
+## 11. Governance Matrix Integration
+
+The Governance Matrix evaluates every contribution at submission time. This is the enforcement point for the contributor permission table in Section 2.3.
+
+**Contribution evaluation:**
+
+```yaml
+governance_matrix_rule:
+  handle: "system/matrix/consumer-policy-scope"
+  enforcement: hard
+  match:
+    subject.type: consumer
+    data.artifact_type: policy
+    data.domain: [system, platform]    # consumer attempting non-tenant domain
+  decision: DENY
+  reason: "Consumers may only contribute tenant-domain policies"
+
+governance_matrix_rule:
+  handle: "system/matrix/provider-spec-scope"
+  enforcement: hard
+  match:
+    subject.type: service_provider
+    data.artifact_type: resource_type_spec
+    data.resource_type_fqn:
+      not_in: subject.declared_resource_types   # provider contributing type they don't offer
+  decision: DENY
+  reason: "Providers may only contribute Resource Type Specs for resource types they offer"
+```
+
+---
+
+## 12. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `FCM-001` | Every DCM data artifact has a contributor. The contributor is recorded in artifact_metadata.contributed_by at creation and is immutable. |
+| `FCM-002` | Contributor permissions are enforced by the Governance Matrix at submission time. Domain scope violations are hard DENY — they cannot be overridden by the contributor. |
+| `FCM-003` | All contributions flow through the GitOps PR model. No contributor can write directly to the authoritative artifact store without a PR review (unless the active profile grants auto-approval for that contributor type and artifact type combination). |
+| `FCM-004` | Policies submitted by any contributor enter proposed (shadow) status by default. Shadow mode results must be available before the active profile's shadow_review_period expires. |
+| `FCM-005` | Platform admins may override any contributor's artifact lifecycle at any time. Override actions are audited. |
+| `FCM-006` | Orphaned artifacts (contributor access revoked) do not automatically deactivate. A platform admin assigns a new owner or explicitly retires them. Exception: sovereign profile auto-retires orphaned artifacts. |
+| `FCM-007` | Federation contributions from peer DCMs are scoped by the peer's federation trust posture. Verified peers: reviewed (standard+). Vouched peers: reviewed always. Provisional peers: authorized approval. |
+| `FCM-008` | Contributor-tier scope limits are absolute. A consumer-authored policy in the tenant domain cannot affect the system or platform domain regardless of the policy's declared match conditions. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/29-scoring-model.md b/content/docs/architecture/data-model/29-scoring-model.md
new file mode 100644
index 0000000..b731be6
--- /dev/null
+++ b/content/docs/architecture/data-model/29-scoring-model.md
@@ -0,0 +1,523 @@
+# DCM Data Model — Hybrid Scoring Model
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Scoring Model Specification
+**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) | [Policy Profiles](14-policy-profiles.md) | [Control Plane Components](25-control-plane-components.md) | [Governance Matrix](27-governance-matrix.md) | [Federated Contribution Model](28-federated-contribution-model.md)
+
+> **This document maps to: DATA + POLICY**
+>
+> The Scoring Model is an extension of the Policy abstraction. Scored signals are Data artifacts with lifecycle and provenance. Profile thresholds are Policy-governed configuration. The Governance Matrix remains a pure boolean gate — scoring never applies to cross-boundary data decisions.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+> > **See also:** [Authority Tier Model](32-authority-tier-model.md) — the ordered authority tier list, custom tier definition, dynamic threshold format, and ATM system policies.
+
+> **Design Priority:** The Scoring Model is the primary mechanism for Priority 2 (ease of use) in service of Priority 1 (security). The auto-approval threshold (SMX-008: ≤ 50) and compliance-class GateKeepers are non-negotiable security properties. Profile thresholds and signal weights are the ease-of-use scaling mechanism. See [Design Priorities](00-design-priorities.md).
+
+---
+
+## 1. Purpose and Governing Principle
+
+DCM uses a **hybrid scoring model**: some decisions are boolean gates (facts), others are scored signals (degrees). The governing principle is explicit:
+
+> **Questions of fact use boolean gates. Questions of degree use scoring.**
+
+A secondary test for any ambiguous decision:
+> **Can a regulator accept "the score was below threshold" as a complete explanation? If not, the decision must be boolean.**
+
+This document specifies the scoring half of the hybrid. For boolean decisions, see [Governance Matrix](27-governance-matrix.md) and the compliance enforcement model in [Policy Contract](B-policy-contract.md).
+
+### 1.1 What This Model Does
+
+The scoring model adds three capabilities to the existing architecture:
+
+1. **Operational GateKeeper policies** contribute a weighted risk score instead of producing a binary deny. The aggregate score drives approval routing.
+2. **Advisory Validation policies** produce a completeness score and warning list without blocking the request.
+3. **Five scoring signals** aggregate into a request risk score that determines approval routing tier — replacing the current per-policy approval flag with a continuous, profile-governed threshold system.
+
+### 1.2 What This Model Does Not Do
+
+The scoring model does **not**:
+- Apply to Governance Matrix decisions — these remain boolean always
+- Apply to compliance-class GateKeeper policies — PHI→BAA, sovereign data→sovereign provider remain hard gates
+- Apply to authentication, authorization, or five-check boundary enforcement
+- Apply to lifecycle state transitions
+- Replace the Policy Engine — it is a function within it
+
+---
+
+## 2. GateKeeper Enforcement Classes
+
+Every GateKeeper policy declares an `enforcement_class`. This is a required field in the Policy base contract (added in this document).
+
+```yaml
+enforcement_class: compliance | operational
+```
+
+### 2.1 Compliance Class
+
+Behavior: **boolean gate**. A compliance-class GateKeeper that fires produces a `deny` decision. The request is halted immediately. No score is produced.
+
+**Use for:**
+- Data classification boundary rules (PHI requires BAA accreditation)
+- Sovereignty violations (classified data leaving declared zone)
+- Security hard requirements (unencrypted data, expired certificates)
+- Regulatory mandates with no legitimate override path
+- Any rule where "score-around" creates legal or compliance liability
+
+```yaml
+# Example compliance-class GateKeeper
+policy_type: gatekeeper
+enforcement_class: compliance
+handle: "system/compliance/phi-baa-required"
+match:
+  payload_type: request.layers_assembled
+  conditions:
+    - field: payload.data_classification
+      operator: contains
+      value: phi
+    - field: payload.provider.accreditations
+      operator: not_contains
+      value: baa_active
+output:
+  decision: deny
+  reason: "PHI data requires provider with active BAA. Provider has no active BAA."
+  audit_required: true
+  notify_on: [DENY]
+```
+
+### 2.2 Operational Class
+
+Behavior: **risk score contribution**. An operational-class GateKeeper that fires contributes a weighted score to the request risk score. The request is not immediately halted. Instead the aggregate score determines routing.
+
+**Use for:**
+- Cost ceiling policies (request cost exceeds Tenant recommendation)
+- Resource sizing policies (CPU/memory above recommended maximums)
+- Unusual timing or context (off-hours request, unusual field combinations)
+- Quota pressure (Tenant approaching quota limit)
+- Business rule preferences that should escalate review, not block
+
+```yaml
+# Example operational-class GateKeeper
+policy_type: gatekeeper
+enforcement_class: operational
+handle: "tenant/payments/gatekeeper/cost-ceiling"
+scoring_weight: 35           # contribution to request risk score when fired
+match:
+  payload_type: request.layers_assembled
+  conditions:
+    - field: payload.cost_estimate.per_month
+      operator: gt
+      value: 500
+output:
+  risk_score_contribution: 35
+  reason: "Estimated monthly cost ${{payload.cost_estimate.per_month}} exceeds Tenant ceiling $500"
+  label: "cost_ceiling_exceeded"
+  audit_required: true
+```
+
+### 2.3 Profile-Level Enforcement Class Override
+
+Profiles can override the enforcement class of individual policies. This is the mechanism for making the scoring system tunable without touching individual policies.
+
+```yaml
+# In a profile definition:
+policy_enforcement_overrides:
+  - policy_handle: "tenant/payments/gatekeeper/cost-ceiling"
+    override_enforcement_class: compliance   # escalate to hard gate in this profile
+    rationale: "FSI profile: all cost violations are hard gates"
+
+  - policy_handle: "system/security/off-hours-request"
+    override_enforcement_class: operational  # demote to soft score in dev profile
+    rationale: "Dev profile: off-hours requests are expected; score but don't block"
+```
+
+**Hard constraint:** A profile can **only** override `operational → compliance` or `compliance → operational` for explicitly non-regulatory policies. Policies with `regulatory_mandate: true` in their metadata cannot be demoted to operational by any profile.
+
+---
+
+## 3. Validation Output Classes
+
+Every Validation policy declares an `output_class`. This is a required field.
+
+```yaml
+output_class: structural | advisory
+```
+
+### 3.1 Structural Class
+
+Behavior: **boolean pass/fail**. A structural Validation that fails halts the request. No score is produced.
+
+**Use for:**
+- Required field presence (missing required fields)
+- Type correctness (wrong field type)
+- Referential integrity (UUID references that don't resolve)
+- Format validation (malformed handle, invalid semver)
+- Schema conformance
+
+### 3.2 Advisory Class
+
+Behavior: **completeness score contribution + warning list**. An advisory Validation that fires contributes to the completeness score and adds a warning to the advisory_warnings list. The request is not halted.
+
+**Use for:**
+- Recommended fields absent (cost_center not provided)
+- Unusual values (memory_gb at 1 for a database VM — unusual but not invalid)
+- Low-confidence field values (field sourced from a provider with confidence < 0.5)
+- Naming convention violations (non-compliant resource name — advisory only)
+
+```yaml
+policy_type: validation
+output_class: advisory
+handle: "platform/advisory/cost-center-recommended"
+scoring_weight: 10
+match:
+  payload_type: request.layers_assembled
+output:
+  completeness_contribution: 10
+  warning_code: "recommended_field_absent"
+  warning_message: "cost_center not provided — cost attribution will use Tenant default"
+  field: "fields.cost_center"
+```
+
+---
+
+## 4. The Five Scoring Signals
+
+The request risk score is assembled from five independent signals. Each signal is normalized to 0–100. The aggregate is a weighted sum, also normalized to 0–100.
+
+### 4.1 Signal 1 — Operational GateKeeper Score
+
+**Source:** All operational-class GateKeeper policies that fired during policy evaluation.
+**Composition:** Sum of `risk_score_contribution` values from all fired operational GateKeepers.
+**Normalization:** Capped at 100 before weighting. Multiple GateKeepers can fire; their contributions accumulate.
+**Default weight in aggregate:** 0.45
+
+```yaml
+operational_gatekeeper_score:
+  fired_policies:
+    - handle: "tenant/payments/gatekeeper/cost-ceiling"
+      contribution: 35
+      reason: "Cost $620/month exceeds ceiling $500"
+    - handle: "platform/gatekeeper/off-hours"
+      contribution: 15
+      reason: "Request submitted outside business hours"
+  raw_score: 50     # sum of contributions
+  normalized: 50    # already within 0-100
+```
+
+### 4.2 Signal 2 — Policy Completeness Score
+
+**Source:** All advisory-class Validation policies that fired.
+**Composition:** Sum of `completeness_contribution` values from all fired advisory Validations.
+**Normalization:** Capped at 100. Score represents "how incomplete is this request" — higher = more warnings.
+**Default weight in aggregate:** 0.15
+
+### 4.3 Signal 3 — Actor Risk History Score
+
+**Source:** Decay-weighted history of the actor's previous request outcomes.
+**Composition:** Each historical event has a base score contribution and a time-decay multiplier.
+**Decay model:** `contribution × e^(-λt)` where `t` is days since event, `λ` = 0.1 (half-life ≈ 7 days).
+**Normalization:** 0–100. A clean history = 0. Recent consecutive failures approach 100.
+**Default weight in aggregate:** 0.20
+
+```yaml
+# Events that contribute to actor risk history score
+actor_risk_events:
+  - event: validation_failure        # base_contribution: 5
+  - event: gatekeeper_deny           # base_contribution: 10
+  - event: compliance_deny           # base_contribution: 20
+  - event: policy_override_requested # base_contribution: 8
+  - event: drift_caused              # base_contribution: 15
+  - event: decommission_forced       # base_contribution: 12
+  - event: request_abandoned         # base_contribution: 3
+```
+
+**Privacy constraint:** Actor risk history scores are never exposed in consumer-facing API responses beyond the actor's own history. They are available in the Admin API for platform admins and in the audit trail.
+
+### 4.4 Signal 4 — Tenant Quota Pressure Score
+
+**Source:** Current quota utilization for the resource type being requested.
+**Composition:** `max(0, (utilization_pct - free_threshold) / (1 - free_threshold)) × 100`
+**Free threshold:** 0.75 (quota pressure score = 0 below 75% utilization).
+**At 100% utilization:** quota pressure = 100, but the hard quota gate also fires (blocking the request regardless of score).
+**Default weight in aggregate:** 0.10
+
+```yaml
+quota_pressure_score:
+  resource_type: "Compute.VirtualMachine"
+  current_usage: 87
+  limit: 100
+  utilization_pct: 0.87
+  free_threshold: 0.75
+  score: 48   # (0.87 - 0.75) / (1 - 0.75) × 100 = 48
+```
+
+### 4.5 Signal 5 — Provider Accreditation Richness Score
+
+**Source:** Accreditation portfolio of the selected/candidate provider.
+**Composition:** Weighted sum of accreditation types held, normalized against the maximum possible portfolio.
+**Usage:** Used in placement tie-breaking (supplements existing tie-breaking algorithm). Also contributes inversely to request risk score — a richly accredited provider reduces risk.
+**Default weight in aggregate:** 0.10 (inverse — higher richness = lower risk contribution)
+
+```yaml
+accreditation_weights:
+  self_declared: 5
+  third_party_audit: 15
+  iso_27001: 20
+  soc2_type2: 20
+  fedramp_moderate: 30
+  fedramp_high: 40
+  hipaa_baa: 25
+  pci_dss: 25
+  sovereign_authorization: 50
+
+# richness_score = sum(weights for held accreditations) / max_possible × 1
+
+# Verification currency multipliers (applied per accreditation, see doc 47)
+# Multiplier reduces an accreditation's weight contribution based on how recently
+# it was externally verified by the Accreditation Monitor
+verification_multipliers:
+  external_registry_verified_within_P1D:  1.0   # full weight — verified today
+  external_registry_verified_within_P7D:  0.9
+  document_verified_within_P30D:          0.85
+  contract_webhook_active:                0.9
+  expiry_only_no_external_check:          0.7   # never been externally verified
+  verification_stale:                     0.4   # check overdue
+  verification_failed_threshold_reached:  0.1   # Monitor cannot reach registry00
+# risk_contribution = (1 - richness_score/100) × 10   [lower richness = higher risk]
+```
+
+### 4.6 Aggregate Request Risk Score
+
+```
+request_risk_score =
+  (operational_gatekeeper_score × 0.45) +
+  (completeness_score           × 0.15) +
+  (actor_risk_history_score     × 0.20) +
+  (quota_pressure_score         × 0.10) +
+  (provider_risk_contribution   × 0.10)
+
+# Normalized: 0–100
+# 0  = clean request, no concerns
+# 100 = maximum risk signal across all dimensions
+```
+
+Signal weights are profile-governed and can be adjusted per deployment. The weights above are the `standard` profile defaults.
+
+---
+
+## 5. Profile-Governed Thresholds
+
+Every profile declares scoring thresholds that map the continuous risk score to a discrete approval routing decision.
+
+```yaml
+# Approval routing uses named tier thresholds — see Authority Tier Model (doc 32)
+# Tier names are resolved from the ordered authority tier list; numeric weights are derived.
+scoring_thresholds:
+  approval_routing:
+    - tier: auto
+      max_score: 24          # score 0–24: auto-approve (SMX-008: never exceed 50)
+    - tier: reviewed
+      max_score: 59          # score 25–59: reviewed tier required
+    - tier: verified
+      max_score: 79          # score 60–79: verified tier required
+    - tier: authorized
+      max_score: 100         # score 80–100: authorized tier required
+  # Custom tiers (if defined) are inserted into this list; existing names unchanged.
+  # "authorized" means DCM holds the pipeline and notifies the declared DCMGroup;
+  # the review process and deliberation are the organization's responsibility.
+  # DCM records votes via Admin API; external systems (ServiceNow, Jira, Slack)
+  # may call the API on behalf of authorized group members. See [Design Priorities](00-design-priorities.md).
+  # Note: compliance-class GateKeeper deny always halts regardless of score
+```
+
+### 5.1 Per-Profile Threshold Defaults
+
+| Profile | auto_approve | reviewed | verified | authorized | signal_weights |
+|---------|-------------|-------------|--------------|-----------|----------------|
+| `minimal` | < 45 | 45–74 | 75–100 | — | default |
+| `dev` | < 40 | 40–69 | 70–100 | — | default |
+| `standard` | < 25 | 25–59 | 60–79 | 80–100 | default |
+| `prod` | < 15 | 15–49 | 50–74 | 75–100 | gatekeeper_weight: 0.50 |
+| `fsi` | < 10 | 10–39 | 40–69 | 70–100 | gatekeeper_weight: 0.55, actor_weight: 0.25 |
+| `sovereign` | < 5 | 5–29 | 30–59 | 60–100 | gatekeeper_weight: 0.60 |
+
+### 5.2 Resource-Type Threshold Overrides
+
+Profiles can declare tighter thresholds for specific resource types:
+
+```yaml
+resource_type_threshold_overrides:
+  - resource_type: "Compute.VirtualMachine"
+    # tier: auto, max_score: 20  # use named-tier threshold format    # tighter than profile default
+  - resource_type: "Network.VLAN"
+    # tier: auto, max_score: 10  # use named-tier threshold format    # VLANs require more scrutiny
+  - resource_type: "Storage.Volume"
+    # tier: verified, max_score: 40  # use named-tier threshold format   # storage changes escalate earlier
+```
+
+### 5.3 Tenant Threshold Overrides
+
+Platform admins can declare Tenant-level scoring threshold adjustments:
+
+```yaml
+tenant_scoring_config:
+  tenant_uuid: 
+  threshold_overrides:
+    # tier: auto, max_score: 15  # use named-tier threshold format    # more conservative for this Tenant
+  signal_weight_overrides:
+    actor_risk_history_weight: 0.30   # higher actor scrutiny for this Tenant
+  trusted_actors:
+    - actor_uuid: 
+      actor_risk_history_score_override: 0   # zero out risk history for trusted automation
+```
+
+### 5.4 Switching Between Scoring and Boolean Per Policy
+
+A profile can declare that a specific operational-class policy should behave as boolean (compliance-class) in that profile's context:
+
+```yaml
+# In profile definition:
+policy_enforcement_overrides:
+  - policy_handle: "platform/gatekeeper/cpu-size-limit"
+    override_enforcement_class: compliance
+    rationale: "Prod profile: CPU limit is a hard constraint, not a risk signal"
+    applies_to_resource_types: ["Compute.VirtualMachine"]
+```
+
+And conversely, a compliance-class policy that is **not** a regulatory mandate can be demoted to operational in lower-trust profiles:
+
+```yaml
+  - policy_handle: "platform/gatekeeper/naming-convention"
+    override_enforcement_class: operational
+    scoring_weight_override: 20
+    rationale: "Dev profile: naming violations are warnings, not blocks"
+    requires_regulatory_mandate_false: true   # safety check
+```
+
+---
+
+## 6. Score Lifecycle and Audit Trail
+
+### 6.1 Score Record Structure
+
+Every scored evaluation produces a Score Record stored in the Audit Store alongside the standard audit record:
+
+```yaml
+score_record:
+  score_record_uuid: 
+  request_uuid: 
+  entity_uuid: 
+  evaluated_at: 
+  
+  request_risk_score: 47
+  routing_decision: reviewed
+  routing_threshold_applied: 25      # the threshold that triggered this tier
+  profile_uuid: 
+  
+  signal_breakdown:
+    operational_gatekeeper:
+      score: 50
+      weight: 0.45
+      weighted_contribution: 22.5
+      fired_policies:
+        - handle: "tenant/payments/gatekeeper/cost-ceiling"
+          contribution: 35
+        - handle: "platform/gatekeeper/off-hours"
+          contribution: 15
+    completeness:
+      score: 20
+      weight: 0.15
+      weighted_contribution: 3.0
+      advisory_warnings: 2
+    actor_risk_history:
+      score: 30
+      weight: 0.20
+      weighted_contribution: 6.0
+      recent_events: 2
+    quota_pressure:
+      score: 48
+      weight: 0.10
+      weighted_contribution: 4.8
+    provider_risk:
+      score: 15
+      weight: 0.10
+      weighted_contribution: 1.5
+  
+  compliance_gates_evaluated: 3
+  compliance_gates_fired: 0         # if any > 0: request halted regardless of risk score
+```
+
+### 6.2 Score Immutability
+
+Score Records are immutable once written. Threshold changes do not retroactively alter historical Score Records. If thresholds change, requests evaluated before the change retain their original routing decisions in the audit trail.
+
+### 6.3 Human Override of Score-Based Routing
+
+A platform admin or reviewer can override a score-based routing decision with a recorded justification. The override is audited, but the Score Record is never modified — instead an Override Record is written referencing the original Score Record.
+
+---
+
+## 7. Score Exposure in APIs
+
+### 7.1 Consumer-Facing Score Exposure
+
+Consumers receive a simplified score view:
+- `risk_score` on request status (integer 0–100)
+- `routing_decision` (auto_approved | pending_review | pending_verified | pending_authorized)
+- `advisory_warnings` list from advisory Validation
+- `score_drivers` — human-readable list of the top 3 contributing factors (no raw weights)
+
+Consumers **do not** receive:
+- Actor risk history score breakdown (privacy)
+- Signal weights
+- Provider accreditation richness detail
+
+### 7.2 Platform Admin Score Exposure
+
+Platform admins receive full Score Record detail via the Admin API including all signal breakdowns, weights, and actor risk history detail.
+
+---
+
+## 8. Relationship to Existing Decision Model
+
+The scoring model slots into the existing pipeline without replacing any component:
+
+```
+Policy Engine evaluation run:
+  1. Evaluate all matching policies (existing behavior)
+  2. Compliance-class GateKeeper fires → HALT (existing deny behavior)
+  3. Structural Validation fails → HALT (existing fail behavior)
+  4. Governance Matrix DENY fires → HALT (existing behavior, unchanged)
+  5. NEW: Collect operational GateKeeper contributions → Signal 1
+  6. NEW: Collect advisory Validation contributions → Signal 2
+  7. NEW: Fetch actor risk history score → Signal 3
+  8. NEW: Calculate quota pressure score → Signal 4
+  9. NEW: Calculate provider accreditation richness → Signal 5
+  10. NEW: Aggregate → request_risk_score
+  11. NEW: Apply profile thresholds → routing_decision
+  12. NEW: Write Score Record to Audit Store
+  13. Route request: auto_approve | queue_for_review | queue_dual | queue_authorized
+```
+
+Steps 2–4 handle standard policy evaluation. Steps 5–13 extend the model with scoring and approval routing.
+
+---
+
+## 9. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `SMX-001` | Every GateKeeper policy must declare `enforcement_class: compliance` or `enforcement_class: operational`. Policies without a declared enforcement_class are treated as compliance-class. |
+| `SMX-002` | Every Validation policy must declare `output_class: structural` or `output_class: advisory`. Policies without a declared output_class are treated as structural. |
+| `SMX-003` | Compliance-class GateKeeper policies with `regulatory_mandate: true` cannot be overridden to operational by any profile. This flag is set by platform admins and is audited. |
+| `SMX-004` | The Governance Matrix is always boolean. No Governance Matrix Rule may declare a scoring weight or enforcement_class. |
+| `SMX-005` | Signal weights in a profile must sum to 1.00. Profiles with invalid weight sums fail validation at activation time. |
+| `SMX-006` | Score Records are immutable. Threshold changes do not retroactively alter historical Score Records. |
+| `SMX-007` | Actor risk history scores are not exposed to consumers beyond the actor's own history. Platform admins have full access. |
+| `SMX-008` | A profile's `auto_approve_below` threshold may not exceed 50. Auto-approving requests with risk scores above 50 is prohibited in all profiles. |
+| `SMX-009` | Operational-class GateKeeper `scoring_weight` values must be declared between 1 and 100. Weights above 100 are validation errors. The aggregate of all fired policies is capped at 100 before weighting. |
+| `SMX-010` | Score breakdown must be included in the audit trail for every request that receives a routing decision. A request with no Score Record is an audit integrity violation. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/30-meta-provider-model.md b/content/docs/architecture/data-model/30-meta-provider-model.md
new file mode 100644
index 0000000..5478c10
--- /dev/null
+++ b/content/docs/architecture/data-model/30-meta-provider-model.md
@@ -0,0 +1,497 @@
+# DCM Data Model — Meta Provider Composability Model
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Meta Provider Specification
+**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) | [Service Dependencies](07-service-dependencies.md) | [Four States](02-four-states.md) | [Operational Models](24-operational-models.md) | [Scoring Model](29-scoring-model.md) | [Control Plane Components](25-control-plane-components.md)
+
+> **This document maps to: DATA + PROVIDER**
+>
+> A Meta Provider is a typed Provider that declares a compound service composition to DCM. The compound service it delivers is Data — a Composite Entity across all four states. DCM's standard machinery (Placement Engine, Orchestration Flow Policy, Recovery Policy) handles everything beyond registration and constituent execution. The Meta Provider is not an orchestrator — it is a compound service definition plus a set of standard Service Providers that happen to serve multiple resource types within the same system.
+> See also: [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md)
+
+---
+
+## 1. What a Meta Provider Is
+
+### 1.1 The Core Model
+
+A Meta Provider is a **compound Service Provider** that uses other providers in the DCM catalog to fulfill a higher-order service request. Its defining characteristic is that it registers a **compound service definition** — a declaration of constituent resource types, their dependencies, and their delivery requirements — so that DCM has enough information to:
+
+1. **Select appropriate constituent providers** via the standard Placement Engine
+2. **Determine execution order** from the dependency graph
+3. **Govern rehydration sequence** using the same dependency information
+
+Beyond providing that definition, a Meta Provider operates as a standard Service Provider for each constituent resource type it owns. DCM's standard machinery handles everything else: placement, sequencing, failure handling, compensation, and audit.
+
+**A Meta Provider is not an orchestrator.** It does not:
+- Select constituent providers — the Placement Engine does this
+- Sequence execution rounds — the dependency graph informs DCM's Orchestration Flow Policy
+- Manage parallel execution — parallelism is derived from the dependency graph (resources with no unresolved dependencies execute simultaneously)
+- Run compensation — DCM's Recovery Policy executes compensation using the dependency graph in reverse
+- Make routing decisions — these are DCM policy decisions
+
+### 1.2 Why This Model Is Correct
+
+Every DCM design principle is preserved:
+- **Governance stays with DCM** — constituent provider selection goes through the Placement Engine, including sovereignty filtering, accreditation checking, and trust scoring
+- **Policy stays with DCM** — GateKeeper, Validation, and Transformation policies fire on the compound payload; the same policies govern each constituent sub-request
+- **Audit stays with DCM** — each constituent request is a standard DCM request with its own audit trail; the compound audit is assembled from constituent audit records
+- **Recovery stays with DCM** — the Recovery Policy handles constituent failures using the dependency graph; the Meta Provider does not make recovery decisions
+
+### 1.3 The Practical Meaning
+
+A Meta Provider registration tells DCM: "Here is a compound service called `ApplicationStack.WebApp`. To fulfill it, you will need a `Compute.VirtualMachine`, a `Network.IPAddress`, a `DNS.Record` (which depends on both), and a `Network.LoadBalancer` (which also depends on both). I can provide the DNS and LoadBalancer; you should place the VM and IP with appropriate compute and network providers."
+
+DCM then:
+- Creates a Composite Entity with one entity UUID
+- Runs the compound layer assembly to produce the full payload
+- Applies policies to the compound payload
+- Dispatches constituent sub-requests to the appropriate providers (compute provider for VM, network provider for IP, Meta Provider for DNS and LoadBalancer)
+- Sequences those sub-requests based on the declared dependency graph
+- Handles any constituent failures using Recovery Policy
+- Assembles the aggregate Realized State from all constituent realized states
+
+The Meta Provider's execution responsibility is limited to: naturalizing and realizing the constituent resource types it owns, then denaturalizing and returning the realized state — exactly as a standard Service Provider does.
+
+---
+
+## 2. Compound Service Definition
+
+The compound service definition is the primary contribution of compound resource type specifications to DCM. It is declared at registration and stored in the Resource Type Registry as a compound Resource Type Specification.
+
+### 2.1 Constituent Declaration
+
+```yaml
+resource_types_composed:
+  - fqn: ApplicationStack.WebApp
+    version: "2.0.0"
+
+    constituents:
+      - component_id: vm-primary
+        resource_type: Compute.VirtualMachine
+        provided_by: external    # DCM places this with an appropriate compute provider
+        depends_on: []
+        required_for_delivery: required
+
+      - component_id: ip-primary
+        resource_type: Network.IPAddress
+        provided_by: external    # DCM places this with an appropriate network provider
+        depends_on: []
+        required_for_delivery: required
+
+      - component_id: dns-primary
+        resource_type: DNS.Record
+        provided_by: self        # This Meta Provider handles DNS
+        depends_on: [vm-primary, ip-primary]
+        required_for_delivery: partial
+
+      - component_id: lb-frontend
+        resource_type: Network.LoadBalancer
+        provided_by: self        # This Meta Provider handles LoadBalancer
+        depends_on: [vm-primary, ip-primary]
+        required_for_delivery: partial
+
+    composition_visibility: selective   # opaque | transparent | selective
+    dcm_visible_sub_resources:
+      - resource_type: Compute.VirtualMachine
+        role: application_host
+      - resource_type: Network.LoadBalancer
+        role: ingress_endpoint
+```
+
+### 2.2 provided_by Declaration
+
+`provided_by` is the key field that tells DCM who is responsible for each constituent:
+
+| Value | Meaning |
+|-------|---------|
+| `self` | This Meta Provider handles this constituent. DCM dispatches it to the Meta Provider. |
+| `external` | DCM places this constituent with the best available provider via the standard Placement Engine. |
+| `` | DCM dispatches this constituent to a specific named provider. |
+
+For `external` constituents, DCM runs a full placement cycle — sovereignty filtering, accreditation checking, trust scoring, reserve query — exactly as it would for any standalone request.
+
+For `self` constituents, DCM dispatches to the Meta Provider using the standard Services API. The Meta Provider receives a standard constituent payload and responds with a standard realized state — no special handling required.
+
+### 2.3 Dependency Graph
+
+The `depends_on` list is the mechanism by which the Meta Provider informs DCM of execution ordering. DCM reads this graph and:
+
+- Identifies which constituents have no dependencies → dispatches these first (or simultaneously)
+- Identifies which constituents have dependencies on already-realized constituents → dispatches these when their dependencies are complete
+- Derives parallelism directly from the graph — constituents with no shared unresolved dependencies execute concurrently within DCM's standard pipeline
+
+**The Meta Provider does not manage this sequencing.** It declares the graph. DCM executes it.
+
+```
+depends_on: []                          → eligible for immediate dispatch
+depends_on: [vm-primary]                → dispatched after vm-primary is REALIZED
+depends_on: [vm-primary, ip-primary]    → dispatched after BOTH are REALIZED
+```
+
+### 2.4 required_for_delivery Classification
+
+Each constituent declares how its success or failure affects the compound service:
+
+| Classification | Failure effect |
+|----------------|---------------|
+| `required` | DCM halts the compound request; triggers Recovery Policy; unrealized constituents are not dispatched |
+| `partial` | DCM notes the failure; compound service continues; final status may be `DEGRADED` |
+| `optional` | DCM notes the failure; compound service continues unaffected |
+
+The Recovery Policy governs what happens on `required` constituent failure — the same Recovery Policy that governs any request failure. No special Meta Provider recovery logic exists.
+
+---
+
+## 3. Composite Entity — Four-State Representation
+
+A compound service request produces a **Composite Entity** — a single DCM entity that aggregates constituent sub-entities. The Composite Entity has one entity UUID that links it across all four states.
+
+### 3.1 Intent State
+
+The consumer submits one request against the compound catalog item. The intent payload contains consumer-declared fields for the compound service — not individual constituent fields.
+
+```yaml
+entity_uuid:           # assigned at intent creation; stable across all states
+catalog_item_uuid:     # ApplicationStack.WebApp
+fields:
+  app_name: payments-api
+  environment: production
+  region: EU-WEST
+  cpu_count: 4
+  memory_gb: 16
+  dns_hostname: payments-api.internal.corp.example
+```
+
+DCM does not expand this into constituent requests at Intent State. The intent is stored as-is.
+
+### 3.2 Requested State
+
+The Request Payload Processor expands the compound intent into the full constituent payload. This is where the compound service definition from the Meta Provider registration is applied.
+
+```yaml
+entity_uuid: 
+composite_entity: true
+compound_service_provider_uuid: 
+
+top_level:
+  app_name: payments-api
+  environment: production
+  region: EU-WEST
+
+constituents:
+  - component_id: vm-primary
+    resource_type: Compute.VirtualMachine
+    provided_by: external
+    provider_uuid: null        # resolved by Placement Engine
+    fields:
+      cpu_count: 4
+      memory_gb: 16
+      os_family: rhel
+    depends_on: []
+    required_for_delivery: required
+
+  - component_id: ip-primary
+    resource_type: Network.IPAddress
+    provided_by: external
+    provider_uuid: null        # resolved by Placement Engine
+    fields:
+      ip_version: 4
+      allocation_pool: prod-EU-WEST
+    depends_on: []
+    required_for_delivery: required
+
+  - component_id: dns-primary
+    resource_type: DNS.Record
+    provided_by: self
+    provider_uuid: 
+    fields:
+      hostname: payments-api.internal.corp.example
+      record_type: A
+    depends_on: [vm-primary, ip-primary]
+    required_for_delivery: partial
+
+  - component_id: lb-frontend
+    resource_type: Network.LoadBalancer
+    provided_by: self
+    provider_uuid: 
+    fields:
+      backend_component: vm-primary
+    depends_on: [vm-primary, ip-primary]
+    required_for_delivery: partial
+```
+
+### 3.3 Realized State
+
+The Realized State is assembled by DCM from the constituent realized payloads returned by each dispatched provider. DCM writes it as a unified composite record.
+
+```yaml
+entity_uuid: 
+composite_entity: true
+composite_status: DEGRADED    # REALIZED | DEGRADED | FAILED
+
+composite_fields:             # synthesized consumer-facing view
+  primary_ip: 10.1.45.23
+  vm_id: vm-0a1b2c3d
+  lb_endpoint: lb-7f8e9d.eu-west.corp
+  dns_name: null              # absent — dns constituent failed
+
+constituents_realized:
+  - component_id: vm-primary
+    status: REALIZED
+    provider_uuid: 
+    realized_fields:
+      vm_id: vm-0a1b2c3d
+      hypervisor_host: host-eu-w-04
+    required_for_delivery: required
+
+  - component_id: ip-primary
+    status: REALIZED
+    provider_uuid: 
+    realized_fields:
+      assigned_ip: 10.1.45.23
+    required_for_delivery: required
+
+  - component_id: dns-primary
+    status: FAILED
+    provider_uuid: 
+    failure_reason: "DNS service degraded — record not created"
+    required_for_delivery: partial
+
+  - component_id: lb-frontend
+    status: REALIZED
+    provider_uuid: 
+    realized_fields:
+      lb_id: lb-7f8e9d
+      endpoint: lb-7f8e9d.eu-west.corp
+    required_for_delivery: partial
+
+degradation_summary:
+  - component_id: dns-primary
+    impact: "DNS name resolution unavailable — access via IP only"
+    recovery_eligible: true
+```
+
+### 3.4 Discovered State
+
+Discovery for composite entities follows the composition visibility mode:
+
+- `opaque` — DCM schedules one discovery call to the Meta Provider; it returns the compound discovered state as a single payload
+- `transparent` — DCM schedules independent discovery calls to each constituent's provider; drift detection runs on each constituent independently
+- `selective` — DCM schedules discovery calls for DCM-visible constituents; opaque discovery for the rest via Meta Provider
+
+---
+
+## 4. What DCM Does vs What the Meta Provider Does
+
+This table is the definitive statement of responsibility.
+
+| Concern | DCM | Meta Provider |
+|---------|-----|---------------|
+| Compound catalog item presentation | ✅ — Service Catalog manages the compound item | Declares the compound service definition at registration |
+| Consumer-facing API | ✅ — Consumer API handles compound requests identically to simple requests | — |
+| Layer assembly | ✅ — Request Payload Processor assembles constituent blocks from compound service definition | — |
+| Policy evaluation on compound payload | ✅ — Same Policy Engine, same policies, same scoring model | May contribute provider-domain policies for its own constituents |
+| External constituent placement | ✅ — Placement Engine selects provider for each `provided_by: external` constituent | Declares what resource types each constituent needs so placement can filter appropriately |
+| Self constituent dispatch | ✅ — API Gateway dispatches to Meta Provider using standard Services API | Receives constituent payload; naturalizes; executes; denaturalizes; returns realized state |
+| Execution ordering from dependency graph | ✅ — Derived from `depends_on` declarations; DCM dispatches in order | Declares `depends_on` relationships for each constituent |
+| Parallelism | ✅ — Constituents with no unresolved dependencies execute concurrently within DCM's pipeline | Emerges from the dependency graph declaration; Meta Provider does not manage this |
+| Constituent failure handling | ✅ — Recovery Policy fires based on `required_for_delivery` classification | Declares `required_for_delivery` for each constituent |
+| Compensation (teardown of realized constituents on failure) | ✅ — Recovery Policy executes compensation using dependency graph in reverse | Implements standard decommission handling for `self` constituents |
+| Composite status determination | ✅ — Determined by DCM from constituent outcomes and `required_for_delivery` | — |
+| Realized State assembly | ✅ — DCM assembles composite Realized State from constituent realized payloads | Returns standard realized payload for `self` constituents |
+| Drift detection | ✅ — Standard drift detection per composition visibility mode | Implements standard discovery endpoint for `self` constituents |
+| Lifecycle management of Composite Entity | ✅ — Standard DCM entity lifecycle | Handles decommission of `self` constituents when decommission payload received |
+| Audit trail | ✅ — Each constituent request has its own audit record; composite audit assembled by DCM | — |
+
+### 4.1 The Compound Orchestration Scope Is Narrow
+
+For each `self` constituent, the Meta Provider:
+1. Receives a standard DCM constituent payload (already fully assembled by DCM)
+2. Naturalizes it to its native format
+3. Executes the realization
+4. Denaturalizes the result
+5. Returns a standard realized payload
+
+This is identical to what any Service Provider does. The Meta Provider is not special during execution — it is simply a Service Provider that happens to be registered for multiple resource types within the same underlying system.
+
+---
+
+## 5. Composition Visibility
+
+Introduced in [07-service-dependencies.md](07-service-dependencies.md), this section specifies the operational implications.
+
+| Mode | Consumer sees | DCM manages independently | Drift detection |
+|------|--------------|--------------------------|-----------------|
+| `opaque` | Top-level entity only | Composite entity only | Via Meta Provider discovery endpoint |
+| `transparent` | All constituents as DCM entities | All constituent entities individually | Per-constituent via each provider |
+| `selective` | Declared visible constituents | Declared visible constituents | Per-constituent for visible; via Meta Provider for rest |
+
+### 5.1 Transparent Mode Entity UUIDs
+
+In transparent mode, constituent entities receive stable DCM entity UUIDs:
+```
+constituent_entity_uuid = deterministic_uuid(parent_entity_uuid + component_id)
+```
+
+These UUIDs are stable across rehydration — the same compound entity always produces the same constituent UUIDs. This enables consistent audit trail linkage.
+
+### 5.2 Decommission Cascade
+
+When a composite entity is decommissioned:
+- DCM dispatches constituent decommission sub-requests in dependency-reverse order
+- Each constituent's provider (whether `self` or `external`) receives a standard decommission payload
+- The composite entity's lifecycle state transitions to DECOMMISSIONED only after all constituents confirm decommission
+
+---
+
+## 6. Rehydration
+
+Rehydration of a composite entity is the primary use case for the dependency graph declaration — which the Meta Provider provides precisely for this purpose.
+
+### 6.1 Rehydration Sequence
+
+DCM reads the `depends_on` graph and rehydrates constituents in dependency order:
+
+```
+Round 1: components with depends_on: []          → rehydrate first
+Round 2: components whose depends_on are realized → rehydrate next
+Round N: continue until all constituents complete
+```
+
+The compound spec's dependency declarations give DCM exactly the information it needs to sequence rehydration correctly without requiring any special Meta Provider involvement beyond standard constituent execution.
+
+### 6.2 Rehydration Provider Selection
+
+`provided_by: external` constituents are re-placed by the Placement Engine during rehydration — they may end up on a different provider than the original realization if the original provider is no longer eligible. `provided_by: self` constituents always return to the same Meta Provider.
+
+---
+
+## 7. Compound Request Pipeline
+
+A compound service request flows through DCM's standard pipeline with compound-specific extensions at the Request Payload Processor step.
+
+```
+Consumer submits compound request
+  │
+  ▼ Intent State captured (compound intent, no constituent expansion)
+  │
+  ▼ Request Payload Processor:
+  │   Layer assembly on compound payload
+  │   Expansion: compound service definition → constituent blocks
+  │     (resource_type, provided_by, fields, depends_on, required_for_delivery per constituent)
+  │   Policy evaluation on compound payload (GateKeeper, Validation, Transformation, scoring)
+  │   Placement: external constituents → Placement Engine
+  │             self constituents     → Meta Provider
+  │
+  ▼ Requested State written (full constituent specification with provider assignments)
+  │
+  ▼ Constituent dispatch — dependency-ordered by DCM:
+  │   Round 1 (no dependencies): dispatch vm-primary, ip-primary in parallel
+  │     vm-primary → compute_provider (standard Services API)
+  │     ip-primary → network_provider (standard Services API)
+  │
+  │   Round 2 (vm+ip REALIZED): dispatch dns-primary, lb-frontend in parallel
+  │     dns-primary → designated_service_provider (standard Services API)
+  │     lb-frontend → designated_service_provider (standard Services API)
+  │
+  │   Any constituent FAILS:
+  │     required  → Recovery Policy fires; unstarted constituents cancelled
+  │     partial   → noted as degraded; execution continues
+  │     optional  → noted; execution continues
+  │
+  ▼ Constituent realized payloads collected by DCM
+  │
+  ▼ Composite status determined: REALIZED | DEGRADED | FAILED
+  │
+  ▼ Realized State written (composite record assembled by DCM)
+  │
+  ▼ Consumer notified of compound request outcome
+```
+
+---
+
+## 8. Nested Meta Providers
+
+A Meta Provider may declare a constituent with `provided_by: external` where the appropriate provider is itself another Meta Provider. DCM's Placement Engine handles this transparently — it places the constituent with whichever registered provider best satisfies the constraints, whether that is a simple Service Provider or another Meta Provider.
+
+**Maximum nesting depth: 3** — enforced by DCM at placement time by checking the compound service definition chain depth. Deeper nesting creates dependency graph complexity that exceeds DCM's governance model.
+
+**The nested Meta Provider has no special awareness** that it is being called as a constituent of an outer compound service. It receives a standard constituent payload and responds with a standard realized state. Nesting is a DCM-level concept, not a provider-level one.
+
+---
+
+## 9. Scoring Model Integration
+
+Compound service requests are scored using the standard five-signal model with two compound-specific behaviors:
+
+**Operational GateKeepers** fire on the compound payload assembled from the top-level fields and declared constituent types. They do not fire per-constituent (constituent-level policy evaluation happens in each constituent sub-request's own pipeline).
+
+**Provider accreditation richness (Signal 5)** for compound entities uses the lowest richness score among all `required_for_delivery: required` constituents across all their assigned providers. The compound service is only as well-accredited as its least-accredited required constituent.
+
+---
+
+## 10. Meta Provider Registration Contract
+
+```yaml
+compound_service_capabilities:
+  # Resource types this Meta Provider handles as a self provider
+  resource_types_provided:
+    - DNS.Record
+    - Network.LoadBalancer
+
+  # Compound service definitions this Meta Provider offers
+  resource_types_composed:
+    - fqn: ApplicationStack.WebApp
+      version: "2.0.0"
+      constituents:
+        - component_id: vm-primary
+          resource_type: Compute.VirtualMachine
+          provided_by: external
+          depends_on: []
+          required_for_delivery: required
+        - component_id: ip-primary
+          resource_type: Network.IPAddress
+          provided_by: external
+          depends_on: []
+          required_for_delivery: required
+        - component_id: dns-primary
+          resource_type: DNS.Record
+          provided_by: self
+          depends_on: [vm-primary, ip-primary]
+          required_for_delivery: partial
+        - component_id: lb-frontend
+          resource_type: Network.LoadBalancer
+          provided_by: self
+          depends_on: [vm-primary, ip-primary]
+          required_for_delivery: partial
+      composition_visibility: selective
+      dcm_visible_sub_resources:
+        - resource_type: Compute.VirtualMachine
+          role: application_host
+        - resource_type: Network.LoadBalancer
+          role: ingress_endpoint
+
+  # Standard provider declarations apply as for any Service Provider
+  # (sovereignty, accreditations, capacity reporting, health check, etc.)
+```
+
+---
+
+## 11. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `MPX-001` | A Meta Provider's `self` constituents are dispatched using the standard Services API. The Meta Provider receives a standard constituent payload and returns a standard realized state. No special dispatch protocol exists for Meta Provider self-constituents. |
+| `MPX-002` | Constituent execution ordering is derived from the `depends_on` declaration by DCM. The Meta Provider does not sequence constituent dispatch. |
+| `MPX-003` | Parallelism in constituent execution is derived from the dependency graph. Constituents with no unresolved dependencies execute concurrently within DCM's standard pipeline. The Meta Provider does not manage this. |
+| `MPX-004` | Composite status determination (`REALIZED` / `DEGRADED` / `FAILED`) is performed by DCM based on constituent outcomes and `required_for_delivery` classifications. |
+| `MPX-005` | Recovery Policy governs all constituent failure handling and compensation. The Meta Provider does not make recovery decisions. It implements standard decommission handling for `self` constituents when a decommission payload arrives. |
+| `MPX-006` | `provided_by: external` constituents are placed by the Placement Engine using standard placement rules. The Meta Provider does not influence external constituent provider selection. |
+| `MPX-007` | In transparent composition visibility mode, constituent entity UUIDs are `deterministic_uuid(parent_entity_uuid + component_id)` — stable across rehydration. |
+| `MPX-008` | Maximum Meta Provider nesting depth is 3, enforced by DCM at placement time by checking the compound service definition chain depth. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/31-credential-management.md b/content/docs/architecture/data-model/31-credential-management.md
new file mode 100644
index 0000000..8af1f53
--- /dev/null
+++ b/content/docs/architecture/data-model/31-credential-management.md
@@ -0,0 +1,159 @@
+# DCM Data Model — Credential Management
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Specification — Credential lifecycle, prescribed secrets infrastructure, consumer credential services
+**Related Documents:** [Infrastructure Requirements](51-infrastructure-optimization.md) | [Internal Component Auth](36-internal-component-auth.md) | [Provider Callback Auth](43-provider-callback-auth.md)
+
+> **Foundation Document Reference**
+>
+> Credential management in DCM operates at two levels:
+> 1. **Prescribed infrastructure** — Vault-compatible secrets API for DCM's own operational secrets
+> 2. **Consumer-facing services** — service_providers that handle `Credential.*` resource types
+>
+> **This document maps to: DATA + PROVIDER**
+
+---
+
+## 1. Two Levels of Credential Management
+
+### 1.1 DCM Internal — Secrets Management
+
+DCM manages its own operational secrets using an Internal/External pattern (same as policy evaluation):
+
+**Internal mode (default):** Secrets are stored in DCM's PostgreSQL database in a `secrets` table using envelope encryption. Each value is encrypted with AES-256-GCM using a per-secret data encryption key (DEK). DEKs are encrypted with a master key (KEK) sourced from the deployment environment (environment variable for homelab, Kubernetes Secret for standard, HSM via PKCS#11 for sovereign). No external secrets infrastructure required.
+
+**External mode (optional):** Organizations with existing Vault infrastructure can register a Vault-compatible API endpoint. DCM calls the Vault HTTP API for all secret operations. Vault, OpenBao, or any API-compatible implementation works.
+
+DCM uses secrets management for:
+
+- **Provider authentication credentials** — the PCA model (doc 43) stores credential references in DCM, actual values encrypted in the secrets store
+- **Encryption keys** for data-at-rest on sensitive JSONB fields (PHI, PCI data classifications)
+- **Audit signing keys** for hash chain integrity
+- **Internal service credentials** — database connection strings, API keys for auth providers
+
+For mTLS between control plane services, cert-manager or the service mesh handles certificate issuance — DCM does not manage those directly.
+
+### 1.2 Consumer-Facing — Service Providers for Credential Resources
+
+When consumers request credential resources (API keys, certificates, SSH keys, secrets), those requests go through the standard DCM pipeline. A `service_provider` that declares `Credential.*` in its `supported_resource_types` handles them:
+
+```yaml
+provider:
+  provider_type: service_provider
+  supported_resource_types:
+    - "Credential.Secret"
+    - "Credential.Certificate"
+    - "Credential.SSHKey"
+    - "Credential.APIKey"
+  capability_extension:
+    hsm_support: true
+    rotation_protocol: automatic
+    max_secret_size_bytes: 65536
+    supported_algorithms: [rsa-2048, rsa-4096, ecdsa-p256, ecdsa-p384, ed25519]
+```
+
+The provider contract is the same as any service_provider: create, update (rotate), decommission (revoke), discover. The capability extension declares credential-specific capabilities.
+
+---
+
+## 2. Credential Types
+
+| Type | Resource Type | Description |
+|------|--------------|-------------|
+| API Key | `Credential.APIKey` | Opaque bearer token for API authentication |
+| JWT | `Credential.JWT` | JSON Web Token with claims, issued by auth_provider |
+| mTLS Certificate | `Credential.Certificate` | X.509 certificate for mutual TLS |
+| SSH Key | `Credential.SSHKey` | SSH public/private key pair |
+| Secret | `Credential.Secret` | Arbitrary secret value (password, connection string) |
+| Signing Key | `Credential.SigningKey` | Cryptographic key for signing operations |
+
+---
+
+## 3. Credential Lifecycle
+
+```
+PENDING → ACTIVE → ROTATING → ACTIVE (new value)
+                 → REVOKED
+                 → EXPIRED
+```
+
+| State | Description |
+|-------|------------|
+| `PENDING` | Credential requested, not yet issued |
+| `ACTIVE` | Credential is valid and in use |
+| `ROTATING` | New credential issued, old credential in grace period |
+| `REVOKED` | Credential permanently invalidated (security event or decommission) |
+| `EXPIRED` | Credential reached TTL without renewal |
+
+### 3.1 Rotation Model
+
+Credential rotation follows a two-phase model:
+
+1. **New credential issued** — new value active, old value enters grace period
+2. **Grace period expires** — old value revoked
+
+The grace period allows consumers to update their references without downtime. Grace period duration is configurable per credential type and deployment profile.
+
+**Rotation triggers:**
+- Scheduled (TTL-based) — credential approaches expiry
+- Security event — compromise detected, immediate rotation
+- Policy-driven — compliance requirement mandates rotation interval
+- Consumer-initiated — explicit rotation request
+
+### 3.2 Revocation Model
+
+Revocation is immediate and permanent. Revoked credentials cannot be reactivated.
+
+**Revocation triggers:**
+- Actor deprovisioned — all credentials for the actor are revoked
+- Security event — credential compromise, immediate revocation with no grace period
+- Provider deregistered — all credentials issued to the provider are revoked
+- Consumer request — explicit revocation via API
+
+---
+
+## 4. Cryptographic Algorithm Requirements
+
+| Profile | Minimum Key Size | Allowed Algorithms |
+|---------|-----------------|-------------------|
+| `minimal`, `dev` | RSA-2048, ECDSA P-256 | RSA, ECDSA, Ed25519 |
+| `standard`, `prod` | RSA-3072, ECDSA P-256 | RSA, ECDSA, Ed25519 |
+| `fsi`, `sovereign` | RSA-4096, ECDSA P-384 | RSA, ECDSA, Ed25519 (no RSA-2048) |
+
+Forbidden algorithms (all profiles): MD5, SHA-1, DES, 3DES, RC4, RSA-1024.
+
+HSM backing is required for `sovereign` profile signing keys. Vault's seal mechanism or transit engine backed by an HSM satisfies this requirement.
+
+---
+
+## 5. Credential Delivery to Consumers
+
+Consumers never receive credential values through the DCM API. Instead, DCM returns a **credential reference** — a Vault path or equivalent that the consumer's application resolves at runtime:
+
+```yaml
+# DCM returns this in the realized entity:
+credentials:
+  db_password:
+    ref: "vault:secret/data/tenant-alpha/pet-clinic-db/password"
+    type: Credential.Secret
+    rotation_schedule: "P90D"
+    expires_at: "2026-07-01T00:00:00Z"
+```
+
+The consumer's application uses its own Vault authentication (Kubernetes service account, AppRole, etc.) to retrieve the actual value. DCM's audit trail records that the credential reference was issued — not the credential value.
+
+---
+
+## 6. Profile-Governed Configuration
+
+| Setting | `minimal`/`dev` | `standard`/`prod` | `fsi`/`sovereign` |
+|---------|-----------------|-------------------|-------------------|
+| Default TTL | P365D | P90D | P30D |
+| Max TTL | Unlimited | P365D | P90D |
+| Rotation grace period | P7D | P3D | P1D |
+| HSM required | No | No | Yes (signing keys) |
+| Idle credential detection | Disabled | P90D warning | P30D auto-revoke |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/32-authority-tier-model.md b/content/docs/architecture/data-model/32-authority-tier-model.md
new file mode 100644
index 0000000..31e5878
--- /dev/null
+++ b/content/docs/architecture/data-model/32-authority-tier-model.md
@@ -0,0 +1,527 @@
+# DCM Data Model — Authority Tier Model
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Authority Tier Specification
+**Related Documents:** [Design Priorities](00-design-priorities.md) | [Scoring Model](29-scoring-model.md) | [Federated Contribution Model](28-federated-contribution-model.md) | [Policy Profiles](14-policy-profiles.md) | [Registry Governance](20-registry-governance.md)
+
+> **This document maps to: DATA + POLICY**
+>
+> The authority tier list is Data — a versioned, ordered registry entry. Tiers are referenced by name in Policies (scoring thresholds, contribution approval requirements, registration requirements). The ordered list resolves numeric weight at evaluation time.
+
+---
+
+## 1. The Core Model
+
+### 1.1 What an Authority Tier Is
+
+An authority tier declares the **required level of organizational decision gravity** for an action. It answers: "how consequential is this decision, and therefore how much authority must be engaged to approve it?"
+
+Tiers do **not** prescribe organizational mechanisms — who satisfies a tier, how many people are involved, or what tools they use. That is entirely the organization's definition. Tiers provide the vocabulary and enforcement gate; organizations provide the substance.
+
+### 1.2 The Ordered List
+
+Authority tiers are defined as a **named, ordered list**. Position in the list determines numeric weight. Names are stable references used throughout the system. New tiers can be inserted anywhere without changing existing names or breaking existing references.
+
+```yaml
+authority_tier_registry:
+  version: "1.0.0"
+  tiers:
+    - name: auto
+      position: 1              # derived from list order; do not hardcode
+      decision_gravity: none
+      description: >
+        No human judgment required. System confidence — scoring, validation,
+        governance matrix checks — is sufficient to proceed. DCM activates
+        automatically on pass.
+      dcm_gate: All structural and governance validation checks pass
+      organization_provides: Nothing — fully automated
+      dcmgroup_required: false
+
+    - name: reviewed
+      position: 2
+      decision_gravity: routine
+      description: >
+        Standard authority. A qualified reviewer in the relevant domain
+        must evaluate and record a decision. Routine operational decisions
+        that benefit from human oversight but do not require elevated authority.
+      dcm_gate: One actor with reviewer role records a decision via Admin API
+      organization_provides: >
+        Who constitutes a qualified reviewer for this action type;
+        the review process; recording via DCM Admin API or external system
+      dcmgroup_required: false
+      typical_use: Standard request approval; routine policy contributions; dev/standard provider registration
+
+    - name: verified
+      position: 3
+      decision_gravity: elevated
+      description: >
+        Elevated authority. Two independent, distinct reviewers must each
+        evaluate and record a decision. Enforces separation of duties —
+        the same actor cannot satisfy both requirements. Used for decisions
+        with operational or security significance requiring independent confirmation.
+      dcm_gate: Two distinct actors with reviewer role each record a decision via Admin API
+      organization_provides: >
+        Who constitutes qualified reviewers; both review processes;
+        may use external workflow tools that call the DCM Admin API
+      dcmgroup_required: false
+      typical_use: High-risk provider registration; elevated-score requests; significant policy changes
+
+    - name: authorized
+      position: 4
+      decision_gravity: critical
+      description: >
+        Highest authority weight. Reserved for decisions with organizational,
+        regulatory, or security consequence requiring the highest level of
+        deliberate authorization. Who constitutes sufficient authority is
+        entirely the organization's definition — a CTO, a CISO and legal
+        counsel, a change advisory board, a single person with delegated
+        authority. DCM enforces that the declared authority group engaged
+        and recorded their decision; it does not prescribe the group structure
+        or deliberation process.
+      dcm_gate: N members of a declared DCMGroup record decisions via Admin API (quorum threshold)
+      organization_provides: >
+        Authority group composition (declared as a DCMGroup);
+        quorum threshold (N of M); deliberation process; external tools
+        (ServiceNow, Jira, Slack bots may call Admin API on behalf of members)
+      dcmgroup_required: true
+      typical_use: Governance matrix changes; sovereign-profile actions; credential provider registration; federation policy
+```
+
+### 1.3 Numeric Weight Resolution
+
+The numeric weight of a tier is its **position in the ordered list**, resolved at evaluation time. It is never stored as a hardcoded number in configuration.
+
+```
+Given the default list: auto(1) → reviewed(2) → verified(3) → authorized(4)
+
+If an organization inserts a custom tier:
+  auto(1) → reviewed(2) → verified(3) → compliance_reviewed(4) → authorized(5)
+
+DCM resolves:
+  weight("reviewed")         = 2
+  weight("verified")         = 3
+  weight("compliance_reviewed") = 4
+  weight("authorized")       = 5
+
+All existing references to "authorized" continue to work.
+No configuration changes required for existing tiers.
+```
+
+### 1.4 decision_gravity Vocabulary
+
+`decision_gravity` is a stable, position-independent classification used by the scoring model and profile system to reason about tier severity without depending on tier names. It is declared on each tier and must be assigned when creating custom tiers.
+
+| Value | Meaning | Default DCM tiers |
+|-------|---------|------------------|
+| `none` | Automated; no human judgment | `auto` |
+| `routine` | Standard operational decision | `reviewed` |
+| `elevated` | Significant decision; separation of duties | `verified` |
+| `critical` | Highest consequence; maximum authority | `authorized` |
+
+Organizations creating custom tiers must assign one of these four gravity values. If a future need arises for a gravity level between `elevated` and `critical`, the vocabulary can be extended — but this is a DCM-level change, not an organization-level one.
+
+---
+
+## 2. Custom Tier Definition
+
+### 2.1 How Organizations Add Tiers
+
+Organizations can extend the authority tier list by contributing custom tier definitions through the standard contribution pipeline. Custom tiers are contributed at the organization or tenant domain scope.
+
+```yaml
+custom_tier_contribution:
+  name: compliance_reviewed          # unique within the deployment
+  insert_after: verified             # position declaration — inserts after this tier
+  decision_gravity: elevated         # must match or be consistent with position
+  description: >
+    Elevated authority with mandatory compliance officer sign-off.
+    Required for actions affecting regulated data domains (PII, PCI, HIPAA).
+    The compliance officer may be one person or a designated compliance team;
+    the organization defines who satisfies this role.
+  dcm_gate: One actor with compliance_officer role records a decision via Admin API
+  organization_provides: >
+    Who holds the compliance_officer role; compliance review process;
+    may be satisfied by external GRC system calling Admin API
+  dcmgroup_required: false           # single reviewer sufficient at this gravity
+  applicable_profiles: [standard, prod, fsi, sovereign]
+  contribution_requires: verified    # adding a custom tier requires verified-tier approval
+```
+
+### 2.2 Contribution Approval Requirements
+
+Custom tier contributions require `verified` tier approval (two independent reviewers) because they affect all pipeline decisions in the deployment. An organization cannot unilaterally add a tier that demotes an existing gravity level or bypasses the `authorized` tier for critical decisions.
+
+**Constraints on custom tiers:**
+- `decision_gravity` must be consistent with position (a tier inserted before `reviewed` cannot have `critical` gravity)
+- Custom tiers cannot be inserted before `auto` or after the highest `critical` gravity tier
+- A custom tier with `dcmgroup_required: true` must declare a valid DCMGroup at contribution time
+- Custom tiers cannot change the `dcm_gate` semantics of existing DCM tiers
+
+### 2.3 External Tier Registries
+
+For federation deployments, peer DCM instances may have different custom tier lists. When a federated request requires approval from a peer's tier, DCM resolves the equivalent gravity level from the local list:
+
+```yaml
+federation_tier_resolution:
+  strategy: gravity_match            # match by decision_gravity, not tier name
+  on_unknown_tier: escalate_to_gravity  # if peer tier unknown, use its declared gravity
+  fallback_tier: authorized          # if gravity unknown, apply highest local tier
+```
+
+---
+
+## 3. Profile Threshold Configuration
+
+### 3.1 Dynamic Threshold Format
+
+Profile approval thresholds are expressed as a named-tier list, not fixed column headers. This allows the threshold table to accommodate custom tiers without restructuring.
+
+```yaml
+# Standard profile — default DCM tiers
+approval_routing:
+  thresholds:
+    - tier: auto
+      max_score: 24          # score 0–24: auto-approve
+    - tier: reviewed
+      max_score: 59          # score 25–59: reviewed tier required
+    - tier: verified
+      max_score: 79          # score 60–79: verified tier required
+    - tier: authorized
+      max_score: 100         # score 80–100: authorized tier required
+
+# Organization adds compliance_reviewed between verified and authorized
+approval_routing:
+  thresholds:
+    - tier: auto
+      max_score: 24
+    - tier: reviewed
+      max_score: 59
+    - tier: verified
+      max_score: 74          # adjusted to make room
+    - tier: compliance_reviewed
+      max_score: 84          # new tier occupies this range
+    - tier: authorized
+      max_score: 100         # unchanged name; adjusted range
+```
+
+### 3.2 Default Profile Thresholds
+
+```yaml
+profile_approval_thresholds:
+  minimal:
+    - { tier: auto,       max_score: 44 }
+    - { tier: reviewed,   max_score: 100 }
+
+  dev:
+    - { tier: auto,       max_score: 39 }
+    - { tier: reviewed,   max_score: 69 }
+    - { tier: verified,   max_score: 100 }
+
+  standard:
+    - { tier: auto,       max_score: 24 }
+    - { tier: reviewed,   max_score: 59 }
+    - { tier: verified,   max_score: 79 }
+    - { tier: authorized, max_score: 100 }
+
+  prod:
+    - { tier: auto,       max_score: 14 }
+    - { tier: reviewed,   max_score: 49 }
+    - { tier: verified,   max_score: 74 }
+    - { tier: authorized, max_score: 100 }
+
+  fsi:
+    - { tier: auto,       max_score: 9 }
+    - { tier: reviewed,   max_score: 39 }
+    - { tier: verified,   max_score: 69 }
+    - { tier: authorized, max_score: 100 }
+
+  sovereign:
+    - { tier: auto,       max_score: 4 }
+    - { tier: reviewed,   max_score: 29 }
+    - { tier: verified,   max_score: 59 }
+    - { tier: authorized, max_score: 100 }
+```
+
+### 3.3 SMX-008 in the Dynamic Model
+
+SMX-008 (auto_approve_below ≤ 50) remains a hard constraint. In the dynamic model: the `auto` tier's `max_score` may never exceed 50 in any profile, regardless of custom tier additions. This constraint applies to the `auto` tier specifically, not to named thresholds.
+
+---
+
+## 4. Tier Evaluation in the Pipeline
+
+### 4.1 How DCM Resolves the Required Tier
+
+At request evaluation time, DCM:
+
+1. Computes the request risk score (0–100) from the scoring model
+2. Loads the active profile's threshold list
+3. Walks the list in order; the first tier whose `max_score ≥ risk_score` is the required tier
+4. Resolves the numeric weight of the required tier from the ordered tier list
+5. Creates an approval record with the required tier name and weight
+
+The tier name — not the weight — is what is stored in the approval record and what reviewers see. The weight is used for comparison operations (e.g., "is this action at least as significant as `verified`?").
+
+### 4.2 Approval Record
+
+```yaml
+approval_record:
+  approval_uuid: 
+  subject_uuid: 
+  subject_type: request | policy_contribution | provider_registration | federation_contribution
+  required_tier: verified           # tier name — stable reference
+  required_tier_weight: 3           # resolved at creation; stored for point-in-time audit
+  required_tier_gravity: elevated
+  dcmgroup_uuid:       # non-null only for dcmgroup_required: true tiers
+  quorum_threshold: 
+  status: pending_reviewed | pending_verified | pending_authorized | pending_
+  created_at: 
+  window_expires_at: 
+  decisions: []
+```
+
+---
+
+## 5. DCMGroup Assignment for Authorized Tier
+
+When a decision requires the `authorized` tier (or any custom tier with `dcmgroup_required: true`), the required DCMGroup and quorum threshold must be declared. This is configured in the profile or per-action-type:
+
+```yaml
+authorized_tier_configuration:
+  default_dcmgroup_handle: platform/security-council   # default group for authorized decisions
+  quorum_threshold: "2 of 5"                           # N of M
+  
+  # Per-action-type overrides
+  action_type_overrides:
+    - subject_type: provider_registration
+      provider_type: service_provider
+      dcmgroup_handle: platform/credential-governance
+      quorum_threshold: "3 of 5"
+    - subject_type: federation_contribution
+      dcmgroup_handle: platform/federation-council
+      quorum_threshold: "2 of 3"
+    - subject_type: policy_contribution
+      policy_domain: system
+      dcmgroup_handle: platform/policy-governance
+      quorum_threshold: "3 of 5"
+```
+
+Organizations define the DCMGroup membership. DCM enforces that the declared group and quorum were satisfied.
+
+---
+
+---
+
+## 7. Tier Registry Change Impact Detection
+
+When the authority tier registry is modified — a new tier inserted, a tier removed, a tier's `decision_gravity` changed, or a tier's position changed — DCM must evaluate the impact on all items that reference tier names before activating the change. This section specifies the detection model.
+
+> **Implementation note:** The impact detection pipeline described here is a required implementation component, not an optional audit feature. A tier registry change that creates security degradations must not activate until each degradation is explicitly acknowledged by a reviewer at `verified` tier or above. The detection mechanism itself is an implementation detail; this specification defines the required behavior and data model.
+
+### 7.1 Tier Impact Diff
+
+Before activating a tier registry change, DCM computes a **tier impact diff** by comparing the proposed ordered list to the current ordered list.
+
+```yaml
+tier_impact_diff:
+  registry_change_uuid: 
+  proposed_at: 
+  proposed_by: 
+  
+  tier_changes:
+    - tier_name: verified
+      change_type: POSITION_CHANGED    # NEW | REMOVED | POSITION_CHANGED | GRAVITY_CHANGED | UNCHANGED
+      old_position: 3
+      new_position: 4                  # something inserted before it
+      old_gravity: elevated
+      new_gravity: elevated            # gravity unchanged
+      net_effect: UPGRADED             # higher position = more weight = more scrutiny required
+      
+    - tier_name: compliance_reviewed   # newly inserted
+      change_type: NEW
+      old_position: null
+      new_position: 3
+      old_gravity: null
+      new_gravity: elevated
+      net_effect: NEW
+      
+    - tier_name: authorized
+      change_type: POSITION_CHANGED
+      old_position: 4
+      new_position: 5
+      old_gravity: critical
+      new_gravity: critical
+      net_effect: UPGRADED
+
+  security_degradations: []           # list of DEGRADED tier changes
+  profile_gaps: []                    # profiles whose threshold list is incomplete after change
+  broken_references: []               # tier names referenced in config that no longer exist
+```
+
+**Net effect classification:**
+
+| Net Effect | Condition | Risk |
+|-----------|-----------|------|
+| `UPGRADED` | Tier's position increased (higher weight) OR gravity increased | None — more scrutiny required than before |
+| `DEGRADED` | Tier's position decreased (lower weight) OR gravity decreased | **Security risk** — items referencing this tier now have lower effective authority requirement |
+| `NEW` | Tier inserted into registry | Low — no existing references; profile gap detection applies |
+| `REMOVED` | Tier deleted from registry | **Broken references** — any item referencing this tier name is now unresolvable |
+| `UNCHANGED` | Position and gravity identical | None |
+
+### 7.2 Affected Item Query
+
+After computing the tier impact diff, DCM queries for all items affected by each changed tier:
+
+```
+Affected item categories:
+
+PENDING APPROVAL RECORDS
+  Query: approval_records WHERE required_tier IN (changed_tier_names) AND status LIKE 'pending_%'
+  Impact: The tier name is stable; the weight at which the item was queued may differ from
+          the current weight. Compare stored_tier_weight (ATM-008) vs current_tier_weight.
+  
+PROFILE THRESHOLD CONFIGURATIONS
+  Query: all profiles WHERE tier_registry_version < new_registry_version
+  Impact: Profiles whose threshold list doesn't include newly added tiers have a gap —
+          requests that score into the new tier's range will fall back to the adjacent tier.
+          Flag as PROFILE_GAP; notify platform admin to update threshold list.
+
+PROVIDER REGISTRATION REQUIREMENTS
+  Query: provider_type_registry WHERE default_approval_method IN (changed_tier_names)
+         AND profile_registration_policy WHERE min_approval_method IN (changed_tier_names)
+  Impact: If the referenced tier's gravity decreased, the minimum requirement is now lower.
+
+FCM CONTRIBUTION POLICY REQUIREMENTS  
+  Query: contribution_policy WHERE any tier reference IN (changed_tier_names)
+  Impact: Same as provider registration — if gravity decreased, requirement is lower.
+
+ACTIVE POLICY SETS
+  Query: active_policies WHERE policy_content CONTAINS tier_name_reference
+  Impact: Policies that reason about tiers by name should be using dynamic resolution.
+          If a policy hardcodes a tier weight, it may now be stale.
+          Flag for policy owner review.
+```
+
+### 7.3 Impact Classification
+
+Each affected item receives one or more impact classifications:
+
+| Classification | Condition | Required Action |
+|---------------|-----------|----------------|
+| `SECURITY_DEGRADATION` | Item references a tier whose gravity decreased OR position decreased | **Blocks activation** — must be reviewed and accepted |
+| `SECURITY_UPGRADE` | Item references a tier whose gravity or position increased | Informational — logged and reported; does not block |
+| `BROKEN_REFERENCE` | Item references a tier name that no longer exists in registry | **Blocks activation** — must be resolved (tier restored, item updated, or item cancelled) |
+| `PROFILE_GAP` | Profile threshold list incomplete after new tier insertion | **Warning** — does not block activation; platform admin must update thresholds or acknowledge gap |
+| `STALE_WEIGHT` | Pending approval record's `stored_tier_weight` differs from current weight for same tier name | Informational — logged; record remains valid since tier name is stable |
+
+### 7.4 Degradation Review Gate
+
+Security degradations block tier registry activation. The blocking gate requires:
+
+1. Each `SECURITY_DEGRADATION` item is presented to a reviewer at `verified` tier or above
+2. The reviewer records an explicit acceptance decision for each degradation via the Admin API
+3. The acceptance includes a reason and is written to the audit trail
+4. Only after all degradations are accepted does the tier registry change activate
+
+This is the same pattern as the standard approval pipeline — DCM provides the gate; the organization provides the review process. The difference is that the required tier for the degradation review is always at least `verified`, regardless of the profile in use.
+
+```
+POST /api/v1/admin/tier-registry/{change_uuid}:accept-degradation
+
+{
+  "affected_item_uuid": "",
+  "affected_item_type": "provider_registration_requirement",
+  "degradation_classification": "SECURITY_DEGRADATION",
+  "acceptance_reason": "",
+  "accepted_by": ""       # must be verified-tier or above reviewer
+}
+```
+
+Broken references cannot be accepted — they must be resolved. DCM will not activate a tier registry change that leaves unresolvable tier references.
+
+### 7.5 Impact Report
+
+Whether or not the change requires a degradation review gate, DCM generates a tier registry impact report at proposed time and again at activation time:
+
+```yaml
+tier_registry_impact_report:
+  registry_change_uuid: 
+  report_generated_at: 
+  stage: proposed | accepted | activated
+
+  summary:
+    degradations: 0
+    upgrades: 3
+    new_tiers: 1
+    broken_references: 0
+    profile_gaps: 2
+    stale_weight_records: 4
+
+  degradations: []
+
+  upgrades:
+    - affected_item_uuid: 
+      affected_item_type: provider_registration_requirement
+      tier_name: verified
+      old_weight: 3
+      new_weight: 4
+      old_gravity: elevated
+      new_gravity: elevated
+      impact: "Effective authority requirement is higher — more scrutiny now required"
+
+  profile_gaps:
+    - profile: standard
+      missing_tiers: [compliance_reviewed]
+      gap_effect: >
+        Requests scoring between the verified and authorized thresholds will route
+        to verified tier until the profile threshold list is updated to include compliance_reviewed
+
+  notification_targets:
+    - platform_admin
+    - provider_owners       # for SECURITY_DEGRADATION items
+    - affected_actor_groups # DCMGroup members for authorized-tier items
+```
+
+The impact report is stored in the Audit Store and is linked to the tier registry version. Platform admins can query historical impact reports to understand what changed and when.
+
+### 7.6 Audit Trail Requirements
+
+Every tier registry change produces the following audit records, regardless of whether degradations exist:
+
+- Registry change proposal record (who proposed, what changed, when)
+- Tier impact diff record (all tier changes, all affected items, all classifications)
+- Per-degradation acceptance records (if any degradations exist)
+- Registry activation record (actual effective timestamp)
+- Per-affected-item notification records (who was notified, when)
+
+Historical approval records retain their `stored_tier_weight` from time of creation (ATM-008). The audit trail thus contains both the point-in-time weight (what authority level was required when the decision was made) and the current weight (what authority level the same tier name requires today), enabling auditors to identify decisions made under different governance regimes.
+
+### 7.7 System Policy Additions
+
+| Policy | Rule |
+|--------|------|
+| `ATM-009` | A tier registry change that produces one or more `SECURITY_DEGRADATION` items must not activate until each degradation is explicitly accepted by a reviewer at `verified` tier or above. |
+| `ATM-010` | A tier registry change that produces one or more `BROKEN_REFERENCE` items must not activate. Broken references must be resolved before the change can proceed. |
+| `ATM-011` | Every tier registry change must produce a tier impact report. The report is stored in the Audit Store and linked to the registry version. |
+| `ATM-012` | Profile threshold lists that become incomplete after a tier registry change (PROFILE_GAP) generate a warning notification to platform admins. The change may activate; platform admins must update threshold lists or explicitly acknowledge the gap within the profile's approval window. |
+
+## 6. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `ATM-001` | Authority tiers are identified by name, not numeric weight. Numeric weight is resolved from list position at evaluation time and is never hardcoded in configuration. |
+| `ATM-002` | The `auto` tier's `max_score` threshold may never exceed 50 in any profile (SMX-008 translated to dynamic model). |
+| `ATM-003` | Custom tiers must declare `decision_gravity` consistent with their position in the ordered list. A tier with lower gravity may not be inserted after a tier with higher gravity. |
+| `ATM-004` | Custom tier contributions require `verified` tier approval. Organizations cannot add tiers unilaterally. |
+| `ATM-005` | Custom tiers cannot alter the `dcm_gate` semantics of existing DCM system tiers (`auto`, `reviewed`, `verified`, `authorized`). |
+| `ATM-006` | For tiers with `dcmgroup_required: true`, the DCMGroup and quorum threshold must be declared in the profile configuration before the tier can be used as a routing target. |
+| `ATM-007` | The four default `decision_gravity` values (`none`, `routine`, `elevated`, `critical`) are DCM system vocabulary. New gravity values require a DCM-level change, not an organization-level contribution. |
+| `ATM-008` | Approval records store the tier name and the resolved weight at creation time. If the tier list changes after an approval record is created, the stored weight reflects the state at creation (point-in-time audit). |
+| `ATM-009` | A tier registry change that produces one or more `SECURITY_DEGRADATION` items must not activate until each degradation is explicitly accepted by a reviewer at `verified` tier or above. |
+| `ATM-010` | A tier registry change that produces one or more `BROKEN_REFERENCE` items must not activate. Broken references must be resolved before the change can proceed. |
+| `ATM-011` | Every tier registry change must produce a tier impact report stored in the Audit Store and linked to the registry version. |
+| `ATM-012` | `PROFILE_GAP` conditions generate a warning notification to platform admins. The change may activate; admins must update threshold lists or explicitly acknowledge the gap within the profile's approval window. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/33-event-catalog.md b/content/docs/architecture/data-model/33-event-catalog.md
new file mode 100644
index 0000000..ef6b031
--- /dev/null
+++ b/content/docs/architecture/data-model/33-event-catalog.md
@@ -0,0 +1,854 @@
+# DCM Data Model — Event Catalog
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Authoritative Event Catalog
+**Related Documents:** [Notification Model](23-notification-model.md) | [Webhooks and Messaging](18-webhooks-messaging.md) | [Universal Audit](16-universal-audit.md) | [credential management service Model](31-credential-provider-model.md) | [Authority Tier Model](32-authority-tier-model.md) | [Control Plane Components](25-control-plane-components.md)
+
+> **This is the single authoritative source for all DCM event types.**
+>
+> The Notification Model (doc 23) defines delivery pipeline, audience resolution, and urgency routing. The Webhooks doc (doc 18) defines the Message Bus integration. This document defines **what events exist, when they fire, and what their payloads contain**. Any document referencing an event type is authoritative only if it agrees with this catalog. Conflicts resolve in favor of this document.
+
+> **Implementation note:** Consumers (webhook receivers, notification services, Message Bus subscribers, audit tooling) must implement idempotency using `event_uuid`. Events are delivered at-least-once. Per-entity ordering is guaranteed; cross-entity ordering is not.
+
+---
+
+## 1. Base Envelope
+
+Every DCM event shares a common envelope. Event-specific fields are in the `payload` object.
+
+```yaml
+# DCM Event Envelope — all events
+event_uuid:                   # idempotency key; stable across retries
+event_type:                 # fully qualified: domain.event_name
+event_schema_version: "1.0"         # increments on breaking payload changes
+timestamp:                # from Commit Log — authoritative source of truth
+dcm_version:                # DCM instance version that generated the event
+dcm_instance_uuid:            # identifies the DCM instance (federation context)
+
+subject:
+  entity_uuid:         # primary entity this event concerns
+  entity_type:       # entity type FQN (e.g. Compute.VirtualMachine)
+  entity_handle:     # human-readable identifier
+  tenant_uuid:         # tenant scope; null for system-scope events
+  actor_uuid:          # actor who triggered the event; null for system events
+
+urgency: critical | high | medium | low | info   # governs notification routing
+
+payload: {}                         # event-specific fields — see Section 3+
+
+links:
+  self:                        # DCM API URL for the subject entity or record
+  audit_record:                # DCM API URL for the audit record for this event
+```
+
+### 1.1 Urgency Levels
+
+| Urgency | Meaning | Delivery expectation |
+|---------|---------|---------------------|
+| `critical` | Security or compliance event requiring immediate action | Push notification; page if configured |
+| `high` | Significant operational event; action likely required | Push notification |
+| `medium` | Notable event; review recommended | Standard delivery |
+| `low` | Informational; action unlikely required | Standard delivery |
+| `info` | Observational; no action expected | Batch or webhook only |
+
+### 1.2 Schema Versioning
+
+`event_schema_version` increments when breaking changes occur to the `payload` schema for an event type. Consumers should validate against the declared version. Non-breaking additions (new optional fields) do not increment the version.
+
+---
+
+## 2. Event Domain Index
+
+| Domain | Events | Description |
+|--------|--------|-------------|
+| `request.*` | 14 | Request pipeline lifecycle |
+| `entity.*` | 13 | Resource entity lifecycle |
+| `drift.*` | 4 | Drift detection and resolution |
+| `provider.*` | 5 | Provider registration and health |
+| `provider_update.*` | 5 | Provider-initiated update lifecycle |
+| `rehydration.*` | 5 | Entity rehydration lifecycle |
+| `policy.*` | 4 | Policy contribution lifecycle |
+| `credential.*` | 4 | Credential lifecycle |
+| `approval.*` | 4 | Approval pipeline |
+| `tier_registry.*` | 4 | Authority tier registry changes |
+| `audit.*` | 3 | Audit chain integrity |
+| `dependency.*` | 2 | Entity dependency events |
+| `stakeholder.*` | 1 | Stakeholder notifications |
+| `allocation.*` | 2 | Resource allocation events |
+| `ingestion.*` | 3 | Brownfield ingestion lifecycle |
+| `governance.*` | 3 | Catalog and profile governance |
+| `security.*` | 2 | Security and sovereignty events |
+| `sovereignty.*` | 2 | Sovereignty constraint events |
+| `federation.*` | 1 | Federation tunnel events |
+| `auth.*` | 1 | Authentication provider events |
+| **Total** | **82** | |
+
+---
+
+## 3. Request Events (`request.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `request.submitted` | info | Consumer submitted a request via API or UI |
+| `request.intent_captured` | info | Intent State created; entity UUID assigned |
+| `request.layers_assembled` | info | Layer assembly complete; compound payload ready for policy evaluation |
+| `request.policies_evaluated` | info | Policy evaluation complete; score computed; routing tier determined |
+| `request.requires_approval` | medium | Score routed to `reviewed`, `verified`, or `authorized` tier; pipeline holds |
+| `request.approved` | info | Required tier approval recorded; pipeline resumes |
+| `request.placement_complete` | info | Provider placement complete; Requested State committed |
+| `request.dispatched` | info | Payload dispatched to provider(s) |
+| `request.compound_assembled` | info | Compound service payload assembled (compound service compound request) |
+| `request.dependencies_resolved` | info | Constituent dependencies resolved (compound service) |
+| `request.realized` | medium | Provider confirmed realization; Realized State written |
+| `request.failed` | high | Request failed at any stage |
+| `request.gatekeeper_rejected` | high | GateKeeper policy denied the request |
+| `request.cancelled` | low | Consumer cancelled; pipeline terminated |
+| `request.progress_updated` | info | Provider sent interim progress update; constituent_status updated |
+
+### 3.1 Payload Schemas
+
+#### `request.submitted` / `request.intent_captured`
+```yaml
+payload:
+  request_uuid: 
+  catalog_item_uuid: 
+  catalog_item_handle: 
+  resource_type:             # FQN e.g. Compute.VirtualMachine
+  submitted_fields: {}               # consumer-declared fields (may be partial)
+```
+
+#### `request.layers_assembled` / `request.policies_evaluated`
+```yaml
+payload:
+  request_uuid: 
+  risk_score: <0-100>                # present after policies_evaluated
+  routing_tier: auto | reviewed | verified | authorized | 
+  score_drivers:                     # top contributing signals
+    - signal: operational_gatekeeper
+      contribution: 12
+```
+
+#### `request.requires_approval`
+```yaml
+payload:
+  request_uuid: 
+  approval_uuid: 
+  required_tier: reviewed | verified | authorized | 
+  required_tier_gravity: routine | elevated | critical
+  risk_score: <0-100>
+  window_expires_at: 
+  dcmgroup_uuid:        # non-null for authorized tier
+  quorum_required: 
+```
+
+#### `request.realized` / `request.failed`
+```yaml
+payload:
+  request_uuid: 
+  provider_uuid: 
+  outcome: realized | failed | degraded
+  failure_reason: 
+  realized_fields: {}                # key provider-returned values (IP, VM ID, etc.)
+  composite_status:   # for compound requests
+```
+
+#### `request.gatekeeper_rejected`
+```yaml
+payload:
+  request_uuid: 
+  policy_handle: 
+  enforcement_class: compliance | operational
+  rejection_reason: 
+  risk_score: <0-100>
+```
+
+---
+
+## 4. Entity Lifecycle Events (`entity.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `entity.realized` | medium | Entity first realized; Realized State written |
+| `entity.state_changed` | medium | Entity lifecycle state transition |
+| `entity.modified` | info | Entity fields updated (Day-2 operation) |
+| `entity.ttl_warning` | medium | TTL expires within declared warning window |
+| `entity.ttl_expired` | high | TTL reached; expiry action triggered |
+| `entity.suspended` | high | Entity entered SUSPENDED state |
+| `entity.resumed` | medium | Entity exited SUSPENDED state |
+| `entity.decommissioning` | medium | Decommission pipeline initiated |
+| `entity.decommissioned` | low | Entity fully decommissioned; resources released |
+| `entity.decommission_deferred` | medium | Decommission blocked by active stakes |
+| `entity.ownership_transferred` | medium | Ownership moved to a different Tenant |
+| `entity.pending_review` | medium | Entity entered PENDING_REVIEW state |
+| `entity.expired` | high | Entity reached terminal expired state |
+
+### 4.1 Payload Schemas
+
+#### `entity.realized`
+```yaml
+payload:
+  request_uuid: 
+  provider_uuid: 
+  realized_fields: {}                # key fields returned by provider
+  composite_entity:            # true for compound service compound services
+  composite_status: 
+```
+
+#### `entity.state_changed`
+```yaml
+payload:
+  previous_state: 
+  new_state: 
+  triggered_by: ttl | decommission | consumer | policy | provider | system
+  reason: 
+```
+
+#### `entity.ttl_warning` / `entity.ttl_expired`
+```yaml
+payload:
+  ttl_expires_at: 
+  expiry_action: decommission | suspend | notify_only
+  warning_window:   # e.g. P7D
+```
+
+#### `entity.decommissioning` / `entity.decommissioned`
+```yaml
+payload:
+  initiated_by: 
+  initiated_at: 
+  reason: 
+  stakes_resolved: 
+  credential_revocation_status: complete | partial | pending
+```
+
+#### `entity.decommission_deferred`
+```yaml
+payload:
+  blocking_stakes:
+    - stake_uuid: 
+      stake_type: required | management
+      stakeholder_tenant_uuid: 
+      stakeholder_entity_uuid: 
+  retry_after: 
+```
+
+#### `entity.ownership_transferred`
+```yaml
+payload:
+  previous_owner_tenant_uuid: 
+  new_owner_tenant_uuid: 
+  transfer_reason: 
+  transferred_by: 
+```
+
+---
+
+## 5. Drift Events (`drift.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `drift.detected` | high | Discovered State differs from Realized State |
+| `drift.severity_escalated` | high | Drift severity increased (e.g. minor → significant) |
+| `drift.resolved` | low | Drift resolved via REVERT or UPDATE_DEFINITION |
+| `drift.escalated` | high | Drift escalated to human review |
+
+### 5.1 Payload Schemas
+
+#### `drift.detected`
+```yaml
+payload:
+  drift_record_uuid: 
+  drift_severity: minor | moderate | significant | critical
+  drifted_fields:
+    - field:                 # field path e.g. "cpu_count"
+      realized_value: 
+      discovered_value: 
+  discovery_run_uuid: 
+  discovered_at: 
+```
+
+#### `drift.severity_escalated`
+```yaml
+payload:
+  drift_record_uuid: 
+  previous_severity: minor | moderate | significant | critical
+  new_severity: minor | moderate | significant | critical
+  escalation_trigger: time_elapsed | field_count | field_sensitivity
+```
+
+#### `drift.resolved`
+```yaml
+payload:
+  drift_record_uuid: 
+  resolution: REVERT | UPDATE_DEFINITION | MANUAL
+  resolved_by: 
+  resolved_at: 
+```
+
+---
+
+## 6. Provider Events (`provider.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `provider.registered` | info | Provider successfully registered and activated |
+| `provider.deregistered` | medium | Provider deregistered; active entities may be affected |
+| `provider.healthy` | info | Provider health check returned healthy after unhealthy period |
+| `provider.unhealthy` | high | Provider health check failed |
+| `provider.degraded` | high | Provider reporting degraded capacity |
+
+### 6.1 Payload Schemas
+
+#### `provider.registered` / `provider.deregistered`
+```yaml
+payload:
+  provider_uuid: 
+  provider_type: service_provider | compound_service | service_provider | auth_provider | ...
+  provider_handle: 
+  resource_types_affected: []  # on deregistered: types now unserviced
+  active_entity_count:            # on deregistered: entities at risk
+```
+
+#### `provider.unhealthy` / `provider.degraded`
+```yaml
+payload:
+  provider_uuid: 
+  health_check_uuid: 
+  failure_reason: 
+  consecutive_failures: 
+  last_healthy_at: 
+  affected_resource_types: []
+```
+
+---
+
+## 7. Provider Update Events (`provider_update.*`)
+
+Provider-initiated update notifications — when a provider reports a change to an entity it manages.
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `provider_update.submitted` | medium | Provider submitted an update notification for a realized entity |
+| `provider_update.requires_approval` | medium | Provider update requires consumer approval before applying |
+| `provider_update.approved` | info | Consumer approved; Realized State updated |
+| `provider_update.rejected` | medium | Consumer rejected; update becomes tracked drift |
+| `provider_update.auto_approved` | info | Update auto-approved per policy |
+
+### 7.1 Payload Schema
+
+```yaml
+payload:
+  provider_update_uuid: 
+  provider_uuid: 
+  update_type: patch | deprecation | security_advisory | capacity_change
+  update_summary: 
+  proposed_field_changes: {}         # what the provider wants to change
+  approval_required: 
+  approval_uuid: 
+```
+
+---
+
+## 8. Rehydration Events (`rehydration.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `rehydration.started` | info | Rehydration pipeline initiated |
+| `rehydration.paused` | medium | Rehydration paused (e.g. waiting on dependent constituent) |
+| `rehydration.interrupted` | high | Rehydration interrupted by error or cancellation |
+| `rehydration.completed` | medium | All constituents rehydrated; entity OPERATIONAL |
+| `rehydration.blocked` | high | Rehydration blocked — provider unavailable or policy prevents |
+
+### 8.1 Payload Schema
+
+```yaml
+payload:
+  rehydration_uuid: 
+  trigger: ttl_expiry | manual | drift_recovery | system
+  constituents_total: 
+  constituents_complete: 
+  block_reason:       # for rehydration.blocked
+```
+
+---
+
+## 9. Policy Events (`policy.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `policy.activated` | medium | Policy promoted from shadow to active |
+| `policy.deactivated` | medium | Policy deactivated |
+| `policy.evaluated` | info | Policy evaluated against a request payload (shadow or active) |
+| `policy.shadow_result` | info | Shadow evaluation diverged from expected outcome |
+
+### 9.1 Payload Schema
+
+#### `policy.activated` / `policy.deactivated`
+```yaml
+payload:
+  policy_uuid: 
+  policy_handle: 
+  policy_type: gatekeeper | validation | transformation | recovery | orchestration_flow
+  enforcement_class: compliance | operational    # for gatekeeper
+  shadow_period_days: 
+  approved_by: 
+```
+
+#### `policy.shadow_result`
+```yaml
+payload:
+  policy_uuid: 
+  request_uuid: 
+  shadow_decision: allow | deny | transform
+  active_decision: allow | deny | transform      # what active policies decided
+  diverged: 
+  divergence_detail: 
+```
+
+---
+
+## 10. Credential Events (`credential.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `credential.rotating` | medium | Rotation initiated; transition window open |
+| `credential.revoked` | high | Credential revoked; all holders must stop using |
+| `credential.idle` | medium | Credential not retrieved within profile threshold |
+| `credential.expired` | medium | Credential reached `expires_at`; no longer valid |
+
+### 10.1 Payload Schema
+
+#### `credential.rotating`
+```yaml
+payload:
+  credential_uuid:            # old credential
+  new_credential_uuid: 
+  rotation_trigger: pre_expiry | scheduled | security_event | actor_request
+  transition_window_ends: 
+  retrieval_url:               # where to retrieve new value
+```
+
+#### `credential.revoked`
+```yaml
+payload:
+  credential_uuid: 
+  revocation_trigger: actor_deprovisioned | entity_decommissioned | security_event | ...
+  revocation_reason: 
+  effective_at: 
+  entity_uuid: 
+```
+
+#### `credential.idle`
+```yaml
+payload:
+  credential_uuid: 
+  credential_type: 
+  issued_at: 
+  threshold_elapsed: 
+  retrieval_count: 0
+```
+
+---
+
+## 11. Approval Events (`approval.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `approval.decision_recorded` | info | A reviewer recorded an approve or reject decision |
+| `approval.quorum_reached` | medium | Authorized tier quorum satisfied; pipeline resuming |
+| `approval.window_expiring` | medium | Approval window approaching expiry (75% elapsed) |
+| `approval.expired` | high | Approval window expired without decision |
+
+### 11.1 Payload Schema
+
+#### `approval.decision_recorded`
+```yaml
+payload:
+  approval_uuid: 
+  subject_type: request | policy_contribution | provider_registration | federation_contribution
+  subject_uuid: 
+  required_tier: reviewed | verified | authorized | 
+  decision: approve | reject
+  voter_uuid: 
+  recorded_via: dcm_admin_ui | servicenow | jira | slack_bot | api_direct | other
+  votes_recorded: 
+  quorum_required: 
+  quorum_reached: 
+```
+
+#### `approval.expired`
+```yaml
+payload:
+  approval_uuid: 
+  subject_type: 
+  subject_uuid: 
+  required_tier: 
+  votes_recorded: 
+  quorum_required: 
+  expiry_action: reject | escalate
+```
+
+---
+
+## 12. Tier Registry Events (`tier_registry.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `tier_registry.proposed` | medium | Tier registry change proposed; impact assessment starting |
+| `tier_registry.impact_assessed` | medium | Tier impact diff complete; review may be required |
+| `tier_registry.degradation_detected` | high | SECURITY_DEGRADATION items found; activation blocked |
+| `tier_registry.activated` | medium | Tier registry change activated; new list in effect |
+
+### 12.1 Payload Schema
+
+#### `tier_registry.proposed`
+```yaml
+payload:
+  registry_change_uuid: 
+  proposed_by: 
+  tiers_added: []
+  tiers_removed: []
+  tiers_repositioned: []
+```
+
+#### `tier_registry.impact_assessed`
+```yaml
+payload:
+  registry_change_uuid: 
+  degradations: 
+  broken_references: 
+  profile_gaps: 
+  upgrades: 
+  activation_blocked: 
+```
+
+#### `tier_registry.degradation_detected`
+```yaml
+payload:
+  registry_change_uuid: 
+  affected_item_uuid: 
+  affected_item_type: 
+  tier_name: 
+  old_gravity: none | routine | elevated | critical
+  new_gravity: none | routine | elevated | critical
+  acceptance_required_by: 
+```
+
+---
+
+## 13. Audit Events (`audit.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `audit.chain_integrity_alert` | critical | Hash chain verification failed; audit trail may be compromised |
+| `audit.chain_break` | critical | Explicit break detected in audit hash chain |
+| `audit.forward_failed` | high | Audit record failed to forward to external audit sink |
+
+### 13.1 Payload Schema
+
+#### `audit.chain_integrity_alert`
+```yaml
+payload:
+  affected_record_uuid: 
+  expected_hash: 
+  actual_hash: 
+  chain_segment_start: 
+  chain_segment_end: 
+  records_in_segment: 
+```
+
+---
+
+## 14. Dependency and Stakeholder Events
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `dependency.state_changed` | medium | A dependency entity changed state; dependents may be affected |
+| `stakeholder.resource_decommissioning` | medium | Resource this actor has a stake in is being decommissioned |
+| `allocation.pool_capacity_low` | high | Allocation pool approaching capacity limit |
+| `allocation.released` | info | Allocation returned to pool |
+
+### 14.1 Payload Schemas
+
+#### `dependency.state_changed`
+```yaml
+payload:
+  dependency_entity_uuid: 
+  previous_state: 
+  new_state: 
+  dependent_entity_uuids: []
+  impact_assessment: degraded | blocked | unaffected
+```
+
+#### `stakeholder.resource_decommissioning`
+```yaml
+payload:
+  resource_entity_uuid: 
+  stake_type: required | management | informational
+  decommission_at: 
+  action_required:             # true for required stakes
+  action_url: 
+```
+
+---
+
+## 15. Ingestion Events (`ingestion.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `ingestion.transitional_created` | info | Brownfield entity created as Transitional entity |
+| `ingestion.enriched` | info | Transitional entity enriched with additional data |
+| `ingestion.promotion_approved` | medium | Transitional entity approved for promotion to full DCM entity |
+
+### 15.1 Payload Schema
+
+```yaml
+payload:
+  ingestion_record_uuid: 
+  source_system: 
+  entity_handle: 
+  confidence_level: high | medium | low
+  missing_fields: []         # for ingestion.enriched
+  promoted_entity_uuid:  # for ingestion.promotion_approved
+```
+
+---
+
+## 16. Governance Events (`governance.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `governance.catalog_item_deprecated` | medium | Service catalog item marked for deprecation |
+| `governance.profile_changed` | high | Active profile configuration changed |
+| `governance.policy_trust_elevated` | medium | Policy provider trust level elevated |
+
+### 16.1 Payload Schema
+
+#### `governance.profile_changed`
+```yaml
+payload:
+  previous_profile: 
+  new_profile: 
+  changed_by: 
+  effective_at: 
+  affected_threshold_tiers: []
+```
+
+---
+
+## 17. Security and Sovereignty Events
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `security.unsanctioned_provider_write` | critical | Provider wrote to an entity without a corresponding Requested State record |
+| `sovereignty.violation` | critical | Data or operation crossed a declared sovereignty boundary |
+| `sovereignty.migration_required` | high | Entity must migrate to comply with sovereignty constraints |
+| `federation.tunnel_degraded` | high | Federation tunnel to peer DCM degraded or unavailable |
+| `auth.provider_failover` | high | Auth Provider failed; failover to secondary |
+
+### 17.1 Payload Schemas
+
+#### `security.unsanctioned_provider_write`
+```yaml
+payload:
+  provider_uuid: 
+  entity_uuid: 
+  write_detected_at: 
+  changed_fields: []
+  discovery_run_uuid: 
+```
+
+#### `sovereignty.violation`
+```yaml
+payload:
+  violation_type: data_boundary | operation_boundary | residency_requirement
+  constraint_uuid: 
+  constraint_handle: 
+  triggering_operation: 
+  remediation_required: 
+```
+
+---
+
+## 18. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `EVT-001` | Every event must include the base envelope fields (`event_uuid`, `event_type`, `event_schema_version`, `timestamp`, `dcm_version`, `dcm_instance_uuid`, `urgency`). Events omitting required envelope fields are invalid and must not be published. |
+| `EVT-002` | `event_uuid` is the idempotency key. Consumers must treat duplicate `event_uuid` values as already-processed. DCM may re-deliver events on failure; this is not a bug. |
+| `EVT-003` | `timestamp` is sourced from the Commit Log Stage 1 write. It represents when the event was authoritatively recorded, not when it was delivered. |
+| `EVT-004` | `event_schema_version` must increment on any breaking change to a payload schema. Adding optional fields is not a breaking change. Removing fields, changing field types, or changing field semantics are breaking changes. |
+| `EVT-005` | Events with `urgency: critical` must be delivered via the push channel if the notification service supports it, regardless of consumer subscription preferences. |
+| `EVT-006` | This catalog is the authoritative source for event type names. Any event type not in this catalog is non-standard. Non-standard events may be published by providers or extensions but must use a reverse-DNS prefix (e.g. `com.acme.custom_event`). |
+| `EVT-007` | The `audit.*` events with `urgency: critical` are non-suppressable. They are delivered regardless of audience subscription rules and cannot be filtered by consumer preference. |
+
+---
+
+## 21. ITSM Events (`itsm.*`)
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `itsm.record_created` | info | ITSM integration successfully created a record in the external ITSM system |
+| `itsm.record_updated` | info | ITSM integration successfully updated an existing ITSM record |
+| `itsm.record_failed` | medium | ITSM integration failed to create/update a record; or `block_until_created` timeout reached |
+
+### 21.1 Payload Schema
+
+#### `itsm.record_created` / `itsm.record_updated`
+```yaml
+payload:
+  itsm_provider_uuid: 
+  itsm_system: servicenow | jira_service_management | ...
+  action: create_change_request | create_incident | ...
+  record_type: change_request | incident | cmdb_ci | service_request
+  record_id: ""    # e.g. CHG0012345, INC-4821
+  record_url: ""                  # deep link to record in ITSM system
+  policy_handle: ""            # which ITSM Policy triggered this
+  stored_on_entity: 
+
+```
+
+#### `itsm.record_failed`
+```yaml
+payload:
+  itsm_provider_uuid: 
+  action: 
+  failure_reason: 
+  timeout_expired:              # true if block_until_created timeout hit
+  policy_handle: 
+```
+
+---
+
+## 20. Accreditation Events (`accreditation.*`)
+
+Fired by the Accreditation Monitor (doc 47) when external verification
+of a registered accreditation produces a result or requires attention.
+
+| Event Type | Urgency | Description |
+|-----------|---------|-------------|
+| `accreditation.verified` | low | Periodic external confirmation — accreditation still active in external registry |
+| `accreditation.status_changed` | high or critical | External registry reports a different status than DCM records — requires platform admin review |
+| `accreditation.registry_mismatch` | high | External registry cannot find the accreditation by its `external_registry_id` — ID may need correction |
+| `accreditation.verification_stale` | varies | `last_checked_at` exceeds `stale_after` threshold — stale_action applied per configuration |
+| `accreditation.document_expired` | high | Evidence document (SOC 2 report, AoC) is older than `max_age` threshold — new document required |
+| `accreditation.contract_event` | varies | Contract management webhook received (BAA signed, amended, or terminated) |
+| `accreditation.expiry_approaching` | medium | Approaching `expires_at` within `renewal_warning_before` window (automated complement to TTL-based check) |
+
+### 20.1 Payload Schemas
+
+```yaml
+# accreditation.status_changed — the most critical event
+accreditation.status_changed:
+  accreditation_uuid: 
+  subject_uuid: 
+  framework: fedramp_high | iso_27001 | cmmc_2 | ...
+  from_status: authorized | active | certified
+  to_status: in_process | revoked | suspended | withdrawn
+  external_source: fedramp_marketplace | cmmc_ab | iaf_certsearch | contract_webhook
+  detected_at: 
+  action_taken: pending_review | immediate_revocation
+  # immediate_revocation when to_status is 'revoked' or 'terminated'
+
+# accreditation.verification_stale
+accreditation.verification_stale:
+  accreditation_uuid: 
+  subject_uuid: 
+  framework: 
+  last_checked_at: 
+  stale_after: P7D
+  stale_action_taken: warn | suspended | escalated
+  consecutive_failures: 
+
+# accreditation.contract_event
+accreditation.contract_event:
+  accreditation_uuid: 
+  subject_uuid: 
+  framework: hipaa | dod_il4 | 
+  contract_event_type: signed | amended | terminated | renewal_due | renewed
+  contract_id: 
+  effective_date: 
+  dcm_action_taken: activated | pending_review | revoked | none
+```
+
+### 20.2 System Policy
+
+| Policy | Rule |
+|--------|------|
+| `EVT-ACM-001` | `accreditation.status_changed` events with `action_taken: immediate_revocation` are non-suppressable — they are delivered to Compliance Team and Platform Admin regardless of notification preferences. |
+| `EVT-ACM-002` | `accreditation.verification_stale` urgency is profile-governed: `low` for dev/standard; `medium` for prod; `high` for fsi/sovereign. |
+
+---
+
+
+## 19. Event Type Quick Reference
+
+```
+request.submitted          request.intent_captured      request.layers_assembled
+request.policies_evaluated request.requires_approval    request.approved
+request.placement_complete request.dispatched            request.compound_assembled
+request.dependencies_resolved  request.realized          request.failed
+request.gatekeeper_rejected    request.cancelled
+
+entity.realized            entity.state_changed         entity.modified
+entity.ttl_warning         entity.ttl_expired           entity.suspended
+entity.resumed             entity.decommissioning       entity.decommissioned
+entity.decommission_deferred   entity.ownership_transferred  entity.pending_review
+entity.expired
+
+drift.detected             drift.severity_escalated     drift.resolved
+drift.escalated
+
+provider.registered        provider.deregistered        provider.healthy
+provider.unhealthy         provider.degraded
+
+provider_update.submitted  provider_update.requires_approval  provider_update.approved
+provider_update.rejected   provider_update.auto_approved
+
+rehydration.started        rehydration.paused           rehydration.interrupted
+rehydration.completed      rehydration.blocked
+
+policy.activated           policy.deactivated           policy.evaluated
+policy.shadow_result
+
+credential.rotating        credential.revoked           credential.idle
+credential.expired
+
+approval.decision_recorded approval.quorum_reached      approval.window_expiring
+approval.expired
+
+tier_registry.proposed     tier_registry.impact_assessed
+tier_registry.degradation_detected  tier_registry.activated
+
+audit.chain_integrity_alert  audit.chain_break          audit.forward_failed
+
+dependency.state_changed   stakeholder.resource_decommissioning
+allocation.pool_capacity_low  allocation.released
+
+ingestion.transitional_created  ingestion.enriched      ingestion.promotion_approved
+
+governance.catalog_item_deprecated  governance.profile_changed
+governance.policy_trust_elevated
+
+security.unsanctioned_provider_write
+sovereignty.violation      sovereignty.migration_required
+federation.tunnel_degraded
+auth.provider_failover
+```
+
+**Total: 85 event types across 21 domains**
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+### Additional Event Types
+
+| Event Type | Description | Key Fields | Consumers |
+|------------|-------------|-----------|----------|
+| `entity.deleted` | An entity has been fully decommissioned and removed from inventory | entity_uuid, from_state, to_state (where applicable) | LCM, AUD, OBS |
+| `entity.state_transition` | An entity lifecycle state has changed (e.g., OPERATIONAL → SUSPENDED) | entity_uuid, from_state, to_state (where applicable) | LCM, AUD, OBS |
+| `group.deleted` | A DCMGroup has been deleted | entity_uuid, from_state, to_state (where applicable) | LCM, AUD, OBS |
+| `group.member_added` | A member (actor or entity) has been added to a DCMGroup | entity_uuid, from_state, to_state (where applicable) | LCM, AUD, OBS |
+| `group.member_removed` | A member (actor or entity) has been removed from a DCMGroup | entity_uuid, from_state, to_state (where applicable) | LCM, AUD, OBS |
+| `authorization.granted` | A cross-tenant authorization has been granted | entity_uuid, from_state, to_state (where applicable) | LCM, AUD, OBS |
diff --git a/content/docs/architecture/data-model/34-api-versioning-strategy.md b/content/docs/architecture/data-model/34-api-versioning-strategy.md
new file mode 100644
index 0000000..b906171
--- /dev/null
+++ b/content/docs/architecture/data-model/34-api-versioning-strategy.md
@@ -0,0 +1,380 @@
+# DCM Data Model — API Versioning Strategy
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — API Versioning and Lifecycle
+**Related Documents:** [Consumer API Specification](../specifications/consumer-api-spec.md) | [Admin API Specification](../specifications/dcm-admin-api-spec.md) | [Operator Interface Specification](../specifications/dcm-operator-interface-spec.md) | [Event Catalog](33-event-catalog.md) | [Registry Governance](20-registry-governance.md) | [Design Priorities](00-design-priorities.md)
+
+> **This document governs all DCM API surfaces.** Every public API endpoint — Consumer, Admin, Operator Interface (Provider), Flow GUI — follows this versioning strategy. The strategy is designed to make the secure, compatible path the easy path: clients that do nothing get the version they requested; breaking changes are announced with sufficient lead time; the newest version is always the supported version.
+
+---
+
+## 1. Versioning Model
+
+### 1.1 URL-Based Major Version
+
+DCM APIs use **URL path versioning** for major versions. The version is the first path segment after the API surface prefix:
+
+```
+Consumer API:      https://{dcm-instance}/api/v1/
+Admin API:         https://{dcm-instance}/api/v1/admin/
+Provider API (OIS):https://{dcm-instance}/provider/api/v1/
+Flow GUI API:      https://{dcm-instance}/flow/api/v1/
+```
+
+The version segment (`v1`, `v2`, etc.) represents a major version. It increments only on breaking changes. Multiple major versions may coexist during a transition window (see Section 4).
+
+### 1.2 Version Granularity — Per-API, Not Per-Endpoint
+
+Versioning is **per-API surface**, not per-endpoint. When a breaking change occurs to any endpoint within an API surface, the entire surface increments to the next major version. This means:
+
+- `v2` of the Consumer API is a complete API surface, not a patchwork of versioned endpoints
+- All endpoints within a surface version are internally consistent
+- Clients target a single version for all their interactions with that surface
+
+Individual endpoints are not independently versioned. If a single endpoint needs a breaking change, the API surface version increments and all other endpoints continue unchanged under the new version.
+
+### 1.3 Minor and Revision Changes
+
+Non-breaking changes within a major version are documented in the API changelog but do not change the URL. Clients do not need to take any action for non-breaking changes.
+
+The changelog follows semantic versioning conventions:
+- **Minor change**: new optional fields, new endpoints, expanded enum values with version-compatible defaults
+- **Revision**: documentation corrections, clarifications, non-functional specification updates
+
+---
+
+## 2. Breaking Change Definition
+
+A change is **breaking** if it requires any existing client to modify its code or configuration to continue working correctly. The following changes are always breaking:
+
+**Request changes:**
+- Removing a field that was previously accepted
+- Changing a field from optional to required
+- Changing a field's type (e.g. string → integer)
+- Removing an accepted enum value
+- Changing URL path structure (endpoint rename or restructure)
+- Changing HTTP method for an existing operation
+- Removing an endpoint
+
+**Response changes:**
+- Removing a field from any response
+- Changing a field's type in any response
+- Changing a field's name in any response
+- Removing a previously returned enum value
+- Changing HTTP status code semantics (e.g. 200 → 202, or changing when 4xx vs 5xx is returned)
+- Changing the response envelope structure
+
+**Behavior changes:**
+- Changing default values in ways that alter existing behavior
+- Changing idempotency semantics
+- Removing a previously supported authentication method
+- Tightening validation (rejecting previously accepted inputs)
+- Changing pagination behavior in ways that break existing cursor patterns
+
+**The following are NOT breaking changes:**
+- Adding new optional request fields (with sensible defaults)
+- Adding new response fields (existing clients safely ignore unknown fields)
+- Adding new endpoints
+- Expanding an enum with new values (clients must handle unknown enum values gracefully)
+- Relaxing validation (accepting previously rejected inputs)
+- Adding new error codes (clients that handle errors generically are unaffected)
+- Performance improvements, infrastructure changes, security patches
+- Documentation improvements
+
+---
+
+## 3. Version Discovery
+
+Clients can discover available API versions and their status without prior knowledge:
+
+### 3.1 Well-Known Discovery Endpoint
+
+```
+GET https://{dcm-instance}/.well-known/dcm-api-versions
+
+Response 200:
+{
+  "dcm_version": "1.2.0",
+  "api_surfaces": {
+    "consumer": {
+      "current": "v2",
+      "supported": ["v1", "v2"],
+      "versions": {
+        "v1": {
+          "status": "deprecated",
+          "sunset_date": "2027-06-01",
+          "deprecation_date": "2026-06-01",
+          "base_url": "/api/v1/",
+          "changelog_url": "/api/v1/changelog"
+        },
+        "v2": {
+          "status": "stable",
+          "released_date": "2026-06-01",
+          "base_url": "/api/v2/",
+          "changelog_url": "/api/v2/changelog"
+        }
+      }
+    },
+    "admin": {
+      "current": "v1",
+      "supported": ["v1"],
+      "versions": {
+        "v1": { "status": "stable", "base_url": "/api/v1/admin/" }
+      }
+    },
+    "provider": {
+      "current": "v1",
+      "supported": ["v1"],
+      "versions": {
+        "v1": { "status": "stable", "base_url": "/provider/api/v1/" }
+      }
+    }
+  }
+}
+```
+
+### 3.2 Per-Version Changelog
+
+```
+GET /api/v1/changelog
+
+Response 200:
+{
+  "version": "v1",
+  "changes": [
+    {
+      "date": "2026-01-15",
+      "type": "minor",
+      "description": "Added optional `score_drivers` field to request status response",
+      "affected_endpoints": ["GET /api/v1/requests/{uuid}/status"]
+    }
+  ]
+}
+```
+
+---
+
+## 4. Deprecation and Sunset Lifecycle
+
+### 4.1 Deprecation Timeline
+
+When a new major version is released, the previous version enters a **deprecation period**. The deprecation timeline is profile-governed — production deployments require longer support windows than development environments:
+
+```yaml
+api_version_support_lifecycle:
+  minimal:
+    deprecation_notice_period: P90D    # 90 days notice before sunset
+    deprecated_version_support: P180D  # old version supported 180 days after deprecation
+    
+  dev:
+    deprecation_notice_period: P60D
+    deprecated_version_support: P90D
+
+  standard:
+    deprecation_notice_period: P180D
+    deprecated_version_support: P365D  # 1 year
+
+  prod:
+    deprecation_notice_period: P365D   # 1 year notice
+    deprecated_version_support: P730D  # 2 years support after deprecation
+
+  fsi:
+    deprecation_notice_period: P548D   # 18 months notice
+    deprecated_version_support: P1095D # 3 years support after deprecation
+
+  sovereign:
+    deprecation_notice_period: P730D   # 2 years notice
+    deprecated_version_support: P1460D # 4 years support after deprecation
+```
+
+**Deprecation ≠ Sunset.** A deprecated version continues to function. Sunset is when it stops working. The deprecation period is the window between "we recommend you migrate" and "you must migrate."
+
+### 4.2 Deprecation Headers
+
+When a client calls a deprecated API version, the response includes standard deprecation headers (per [RFC 8594](https://datatracker.ietf.org/doc/html/rfc8594) and [RFC 9745](https://datatracker.ietf.org/doc/html/rfc9745)):
+
+```http
+HTTP/1.1 200 OK
+Deprecation: @1749340800          # Unix timestamp when this version was deprecated
+Sunset: @1781049600               # Unix timestamp when this version will stop working
+Link: ; rel="successor-version"
+Link: ; rel="deprecation"
+```
+
+### 4.3 Deprecation Events
+
+When a version is deprecated or sunsetted, DCM fires notification events:
+
+- `governance.api_version_deprecated` — version entered deprecation; Sunset header begins appearing
+- `governance.api_version_sunset_warning` — 30 days before sunset; high urgency
+- `governance.api_version_sunset` — version has reached sunset date; calls now return 410 Gone
+
+Platform admins should configure notification routing for these events to ensure API consumers receive timely warning.
+
+### 4.4 Sunset Behavior
+
+After the sunset date, calls to the deprecated version return:
+
+```http
+HTTP/1.1 410 Gone
+Content-Type: application/json
+
+{
+  "error": "api_version_sunset",
+  "message": "API version v1 reached its sunset date on 2027-06-01. Migrate to v2.",
+  "successor_version": "v2",
+  "migration_guide_url": "/api/v2/migration-guide",
+  "sunset_date": "2027-06-01"
+}
+```
+
+---
+
+## 5. Version Negotiation
+
+### 5.1 How Clients Specify a Version
+
+The URL path is the primary versioning mechanism. No headers or query parameters are required — the URL is authoritative:
+
+```
+GET /api/v1/resources        → Consumer API v1
+GET /api/v2/resources        → Consumer API v2 (when available)
+```
+
+### 5.2 Version Preference Header (Optional)
+
+For clients that need to pin to a specific version or test against a new version before migrating, an optional `DCM-API-Version` header is supported:
+
+```http
+GET /api/v1/resources
+DCM-API-Version: v1          # explicit pin; returns 406 if v1 is sunsetted
+```
+
+If the header specifies a sunsetted version, the response is `406 Not Acceptable` with a migration guide reference.
+
+### 5.3 Latest-Version Alias
+
+```
+GET /api/latest/resources    # always routes to current stable version
+```
+
+The `latest` alias is provided for development and testing. It is **not recommended for production** — production clients should pin to a specific version to avoid inadvertent breaking changes when a new major version becomes `latest`.
+
+---
+
+## 6. Beta and Preview Endpoints
+
+New capabilities that are not yet stable may be released as **preview endpoints** within the current major version:
+
+```
+GET /api/v1/preview/new-feature
+```
+
+Preview endpoints:
+- Are not covered by the stability guarantees of the parent version
+- May change or be removed without a major version increment
+- Are marked in the API changelog and discovery endpoint as `status: preview`
+- Must not be used in production automation without explicit acknowledgment of instability
+
+```yaml
+# Discovery response for a preview endpoint
+"new-feature": {
+  "status": "preview",
+  "stability_commitment": "none",
+  "planned_graduation": "v2",
+  "feedback_url": "https://github.com/dcm-project/discussions"
+}
+```
+
+Preview endpoints graduate to stable when they are included in a new major version release.
+
+---
+
+## 7. Provider API (OIS) Versioning
+
+The Operator Interface Specification (OIS) governs how DCM calls providers. Provider implementations must support the version of the OIS they declare in their capability registration.
+
+### 7.1 OIS Version in Capability Registration
+
+```yaml
+provider_registration:
+  ois_version: "1.0"           # which OIS version this provider implements
+  ois_version_min: "1.0"       # minimum OIS version supported
+  ois_version_max: "1.x"       # maximum OIS version supported (x = any minor)
+```
+
+### 7.2 OIS Compatibility
+
+DCM maintains version-compatible with registered OIS versions during the support lifecycle. A DCM instance running OIS v2 must continue to dispatch to providers registered on OIS v1 until the version is sunset.
+
+When the OIS version is incremented:
+1. DCM announces the new OIS version via the event `governance.ois_version_released`
+2. Providers have the deprecation notice period to upgrade their implementation
+3. DCM dispatches using the appropriate OIS version per the provider's declared capability
+4. After sunset, providers still on deprecated OIS versions receive `410 Gone` on dispatch
+
+### 7.3 Provider-Initiated API Versioning
+
+Providers that expose their own management APIs (beyond the standard OIS surface) are responsible for their own versioning. DCM does not version-manage provider-internal APIs. Providers should follow the same breaking-change definition (Section 2) and announce breaking changes via `provider_update.submitted` events.
+
+---
+
+## 8. Version Upgrade Path
+
+When a new major API version is published, a machine-readable change log is available at:
+
+```
+GET /api/v{N}/migration-guide
+```
+
+This endpoint returns all breaking changes from the previous major version:
+
+```json
+{
+  "from_version": "v1",
+  "to_version": "v2",
+  "breaking_changes": [
+    {
+      "change_id": "BC-001",
+      "type": "field_removed",
+      "endpoint": "GET /api/v2/resources/{uuid}",
+      "description": "Field 'legacy_id' removed — use 'entity_uuid' instead"
+    }
+  ],
+  "new_capabilities": []
+}
+```
+
+Clients declare the API version they target via the `Accept-Version` header or URL prefix. DCM supports all non-sunset major versions simultaneously. When a version reaches sunset, responses include `Deprecation` and `Sunset` headers (RFC 8594) before support is withdrawn.
+
+---
+
+
+## 9. Internal API Versioning
+
+DCM internal component APIs (Control Plane components communicating with each other) follow a simpler model:
+
+- Internal APIs are not exposed externally and not subject to the external versioning lifecycle
+- Internal breaking changes require a coordinated deployment of all affected components
+- DCM release versions (e.g. `1.2.0`) cover the complete set of internal APIs for that release
+- Operators upgrading DCM must upgrade all components together per the release upgrade guide
+
+---
+
+## 10. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `VER-001` | All DCM public API surfaces use URL path versioning. The version path segment is the only authoritative version indicator. |
+| `VER-002` | A change is breaking if any existing client must modify code or configuration to continue working. When in doubt, treat a change as breaking. |
+| `VER-003` | Deprecated API versions must return `Deprecation`, `Sunset`, and `Link` headers on every response during the deprecation period (per RFC 8594 / RFC 9745). |
+| `VER-004` | Deprecated versions must remain fully functional until the sunset date. Bugs in deprecated versions are fixed; new features are not backported. |
+| `VER-005` | The deprecation notice period and deprecated version support window are profile-governed. Production deployments require longer windows than development. See Section 4.1. |
+| `VER-006` | The `latest` version alias is available but must not be recommended for production use. Production clients must pin to a specific version. |
+| `VER-007` | Preview endpoints are not stable. They may change or be removed without a major version increment. They are identified by the `/preview/` path segment. |
+| `VER-008` | Every new major version must publish a machine-readable migration guide at `/api/v{N}/migration-guide`. |
+| `VER-009` | DCM must maintain dispatch compatibility with providers registered on supported OIS versions until the OIS version is sunset. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/35-session-revocation.md b/content/docs/architecture/data-model/35-session-revocation.md
new file mode 100644
index 0000000..afb313e
--- /dev/null
+++ b/content/docs/architecture/data-model/35-session-revocation.md
@@ -0,0 +1,351 @@
+# DCM Data Model — Session Token Revocation
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Session Lifecycle and Revocation
+**Related Documents:** [Auth Providers](19-auth-providers.md) | [credential management service Model](31-credential-provider-model.md) | [Accreditation and Zero Trust](26-accreditation-and-authorization-matrix.md) | [Event Catalog](33-event-catalog.md) | [Design Priorities](00-design-priorities.md)
+
+> **This document maps to: DATA + POLICY**
+>
+> A session is a Data artifact with a UUID, lifecycle state, and audit trail. Session revocation is a Policy concern — it fires on triggers defined here and enforced by the Auth Provider and Ingress layer. This document extends the Auth Provider model (doc 19) with the explicit revocation lifecycle that was previously unspecified.
+>
+> **Relationship to credential revocation:** CPX-006 (doc 31) governs credential revocation — when an actor is deprovisioned, all credentials issued to that actor are revoked. This document governs the complementary concern: active *session tokens* must also be invalidated on the same trigger. Credential revocation and session revocation are parallel processes that both fire on actor deprovisioning.
+
+---
+
+## 1. What a Session Is
+
+A DCM session represents an authenticated actor's active interaction context. It is created when an actor successfully authenticates through an Auth Provider and is destroyed (or expires) when the session ends.
+
+```yaml
+session_record:
+  session_uuid: 
+  actor_uuid: 
+  auth_provider_uuid:            # which provider issued the session
+  auth_method: oidc | ldap | api_key | mtls | built_in
+  mfa_verified:                  # whether per-session MFA was completed
+  step_up_verified_at:   # last step-up MFA completion
+  
+  created_at: 
+  last_active_at: 
+  expires_at:                # absolute expiry (from token_ttl)
+  
+  refresh_token_uuid:     # if refresh_enabled: true
+  refresh_expires_at: 
+  
+  status: active | refreshing | revoked | expired
+  revocation_reason: 
+  revoked_at: 
+  revoked_by: 
+  
+  # Provenance
+  client_ip: 
+  user_agent: 
+  tenant_uuid: 
+  
+  # Concurrent session position
+  session_sequence:               # 1 = oldest active session for this actor
+```
+
+### 1.1 Session Store
+
+Active sessions are maintained in a **Session Store** — a fast-queryable, low-latency store separate from the Realized State Store. The Session Store is not GitOps-backed; it is operational state that does not need version history.
+
+```yaml
+session_store:
+  implementation: redis | postgres | in_memory   # profile-governed
+  ttl_enforcement: hard                          # sessions expire at expires_at regardless
+  revocation_index: true                         # fast lookup by session_uuid for revocation
+  actor_index: true                              # fast lookup by actor_uuid for bulk revocation
+```
+
+**Profile-governed defaults:**
+
+| Profile | Store | Session TTL | Refresh TTL | Max concurrent |
+|---------|-------|-------------|-------------|---------------|
+| `minimal` | in_memory or sqlite | PT8H | P7D | unlimited |
+| `dev` | redis or postgres | PT4H | P3D | 10 |
+| `standard` | redis or postgres | PT1H | P1D | 5 |
+| `prod` | redis or postgres | PT30M | PT8H | 3 |
+| `fsi` | redis or postgres | PT15M | PT1H | 2 |
+| `sovereign` | redis or postgres (HSM-backed) | PT15M | PT30M | 1 |
+
+---
+
+## 2. Revocation Triggers
+
+Session revocation invalidates a session immediately — regardless of its remaining TTL. The following triggers cause revocation:
+
+| Trigger | Scope | Who initiates | Behavior |
+|---------|-------|--------------|---------|
+| `actor_logout` | Single session | Actor (self) | Immediate; that session only |
+| `actor_logout_all` | All sessions for actor | Actor (self) | Immediate; all active sessions for this actor |
+| `actor_deprovisioned` | All sessions for actor | SCIM / Platform admin | Immediate; fires before deprovisioning acknowledged |
+| `actor_suspended` | All sessions for actor | Platform admin | Immediate |
+| `security_event` | Specified sessions or all | Platform admin / security automation | Immediate; emergency channel notification |
+| `concurrent_limit_exceeded` | Oldest session(s) | System | Oldest session revoked when new session created beyond limit |
+| `auth_provider_deregistered` | All sessions from that provider | Platform admin | Immediate; actors must re-authenticate via another provider |
+| `credential_compromised` | All sessions for actor | Security automation | Immediate; correlates with CPX emergency rotation |
+| `admin_forced_logout` | Specified session(s) | Platform admin | Immediate |
+
+---
+
+## 3. Revocation Lifecycle
+
+### 3.1 Standard Revocation
+
+```
+Revocation trigger fires
+  │
+  ▼ Session record status → revoked
+  │   revoked_at, revocation_reason, revoked_by written
+  │
+  ▼ Refresh token invalidated (if exists)
+  │   Cannot be exchanged; refresh endpoint returns 401
+  │
+  ▼ Session UUID added to Session Revocation Registry
+  │   (fast-queryable; all DCM components check this on every request)
+  │
+  ▼ Revocation event published to Message Bus
+  │   event_type: auth.session_revoked
+  │   session_uuid, actor_uuid, revocation_trigger, revoked_at
+  │
+  ▼ Audit record written
+      session_uuid, actor_uuid, revocation_trigger, revoked_by, revoked_at
+```
+
+### 3.2 Actor Deprovisioning Revocation (parallel with CPX-006)
+
+Actor deprovisioning fires both credential revocation (CPX-006) and session revocation simultaneously. Neither blocks the other; both must complete before the deprovisioning is acknowledged.
+
+```
+Actor deprovisioning initiated
+  │
+  ├──→ Credential revocation (CPX-006)
+  │     All credentials issued to actor_uuid → revoked
+  │     Credential Revocation Registry updated
+  │
+  └──→ Session revocation (this document)
+        All active sessions for actor_uuid → revoked
+        Session Revocation Registry updated
+        auth.session_revoked events published per session
+  │
+  ▼ Both complete → deprovisioning acknowledged
+    actor_deprovisioned event published
+    Audit record for deprovisioning written
+```
+
+### 3.3 Emergency Revocation (Security Event)
+
+Security events bypass the standard pipeline. Revocation is immediate with no grace period.
+
+```
+Security event detected
+  │
+  ▼ Target sessions determined
+  │   (single session, all sessions for actor, or all sessions from a provider)
+  │
+  ▼ Sessions → revoked immediately
+  │   Session Revocation Registry updated within SLA:
+  │     standard/prod: PT30S
+  │     fsi: PT10S
+  │     sovereign: PT5S
+  │
+  ▼ auth.security_session_revoked event published (critical urgency)
+  │   Routed to security team via configured notification service
+  │
+  ▼ Platform admin notified regardless of profile
+  │
+  ▼ All in-flight requests from these sessions → 401 Unauthorized
+```
+
+---
+
+## 4. Session Revocation Registry
+
+The Session Revocation Registry is the authoritative list of revoked-but-not-yet-expired session UUIDs. Every DCM component that accepts bearer tokens must check this registry on each request.
+
+```yaml
+session_revocation_registry:
+  # Session UUID → revocation record
+  # Fast in-memory cache with TTL equal to original session TTL
+  # After the original session TTL would have expired, the entry is
+  # removed (the session would have been invalid anyway)
+  
+  entry:
+    session_uuid: 
+    revoked_at: 
+    original_expires_at:     # entry removed after this time
+    revocation_trigger: 
+```
+
+**Cache refresh behavior by profile:**
+
+| Profile | Max cache age | Behavior on cache miss |
+|---------|--------------|----------------------|
+| `minimal` | PT5M | Check authoritative store; cache result |
+| `standard` | PT1M | Check authoritative store; cache result |
+| `prod` | PT30S | Check authoritative store; cache result |
+| `fsi` | PT10S | Check authoritative store; cache result |
+| `sovereign` | PT5S | No cache — always check authoritative store |
+
+---
+
+## 5. Token Introspection
+
+DCM's Ingress layer exposes a token introspection endpoint for internal components and external systems that need to validate a token without maintaining their own cache:
+
+```
+POST /api/v1/auth:introspect
+
+Authorization: Bearer 
+Content-Type: application/json
+
+{
+  "token": ""
+}
+
+Response 200 (active session):
+{
+  "active": true,
+  "session_uuid": "",
+  "actor_uuid": "",
+  "expires_at": "",
+  "mfa_verified": true,
+  "tenant_uuid": "",
+  "roles": ["consumer"],
+  "scopes": ["read", "write"]
+}
+
+Response 200 (revoked or expired):
+{
+  "active": false,
+  "reason": "revoked | expired | not_found"
+}
+```
+
+Session tokens use JWT format (RFC 7519). This introspection endpoint follows [RFC 7662 (OAuth 2.0 Token Introspection)](https://datatracker.ietf.org/doc/html/rfc7662).
+
+---
+
+## 6. Consumer API — Session Management Endpoints
+
+### 6.1 Logout (Single Session)
+
+```
+DELETE /api/v1/auth/session
+
+Response 204 No Content
+```
+
+Revokes the session corresponding to the bearer token in the `Authorization` header. No body required.
+
+### 6.2 Logout All Sessions
+
+```
+DELETE /api/v1/auth/sessions
+
+Response 204 No Content
+```
+
+Revokes all active sessions for the authenticated actor.
+
+### 6.3 List Active Sessions
+
+```
+GET /api/v1/auth/sessions
+
+Response 200:
+{
+  "items": [
+    {
+      "session_uuid": "",
+      "created_at": "",
+      "last_active_at": "",
+      "expires_at": "",
+      "auth_method": "oidc",
+      "client_ip": "",
+      "current": true    // true for the session making this request
+    }
+  ],
+  "total": 2
+}
+```
+
+### 6.4 Revoke Specific Session
+
+```
+DELETE /api/v1/auth/sessions/{session_uuid}
+
+Response 204 No Content
+Response 404: session not found or does not belong to this actor
+```
+
+### 6.5 Admin: Force Revoke Session(s)
+
+```
+POST /api/v1/admin/actors/{actor_uuid}:revoke-sessions
+
+{
+  "scope": "all | session",
+  "session_uuid": "",    // required if scope: session
+  "reason": ""               // required for audit trail
+}
+
+Response 204 No Content
+Response 404: actor not found
+```
+
+---
+
+## 7. Concurrent Session Enforcement
+
+When `concurrent_sessions: N` is declared and a new session would exceed the limit, the oldest active session is revoked automatically:
+
+```
+New authentication succeeds
+  │
+  ▼ Count active sessions for actor_uuid
+  │   If count >= concurrent_sessions limit:
+  │     Revoke oldest session (by created_at)
+  │     Trigger: concurrent_limit_exceeded
+  │
+  ▼ New session created
+```
+
+The evicted actor receives an `auth.session_revoked` notification if a notification service is configured with the actor's notification preferences. The event does not block the new session creation.
+
+---
+
+## 8. Relationship to the Credential Revocation Model
+
+Session revocation (this document) and credential revocation (doc 31, CPX-001–CPX-012) are parallel but distinct:
+
+| | Session Revocation | Credential Revocation |
+|--|---|---|
+| **What** | Bearer token / session cookie validity | API key, x509, SSH key, service account token |
+| **Store** | Session Revocation Registry | Credential Revocation Registry |
+| **Propagation** | Auth layer cache refresh | Message Bus → all components |
+| **Actor deprovision** | All sessions revoked | All credentials revoked |
+| **TTL** | Session TTL (minutes to hours) | Credential TTL (hours to years) |
+| **Emergency SLA** | PT5S–PT30S | PT30S–PT5M |
+| **Event** | `auth.session_revoked` | `credential.revoked` |
+
+**AUTH-016:** On actor deprovisioning, session revocation and credential revocation are parallel operations. The deprovisioning is not acknowledged until both are confirmed complete.
+
+---
+
+## 9. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `AUTH-016` | On actor deprovisioning, session revocation and credential revocation (CPX-006) are parallel operations. Deprovisioning is not acknowledged until both complete. |
+| `AUTH-017` | Session revocation must propagate to the Session Revocation Registry within the profile-governed SLA: minimal PT5M, standard PT1M, prod PT30S, fsi PT10S, sovereign PT5S. |
+| `AUTH-018` | All DCM components that accept bearer tokens must check the Session Revocation Registry on each request. Cache age must not exceed the profile-governed maximum (sovereign: no cache). |
+| `AUTH-019` | Emergency session revocation (security_event trigger) fires immediately with no grace period. The `auth.security_session_revoked` event has `urgency: critical` and is non-suppressable. |
+| `AUTH-020` | The token introspection endpoint (`POST /api/v1/auth:introspect`) must be authenticated. Access requires an actor or service account with the `introspection` scope. |
+| `AUTH-021` | When concurrent session limits are enforced, the oldest session is revoked before the new session is created. The evicted actor is notified via notification service if configured. |
+| `AUTH-022` | Refresh tokens are invalidated when their parent session is revoked. A revoked refresh token returns 401 on exchange; it cannot be used to create a new session. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/36-internal-component-auth.md b/content/docs/architecture/data-model/36-internal-component-auth.md
new file mode 100644
index 0000000..2683b4f
--- /dev/null
+++ b/content/docs/architecture/data-model/36-internal-component-auth.md
@@ -0,0 +1,369 @@
+# DCM Data Model — Internal Component Authentication
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Zero Trust Internal Auth
+**Related Documents:** [Accreditation and Zero Trust](26-accreditation-and-authorization-matrix.md) | [Deployment and Redundancy](17-deployment-redundancy.md) | [credential management service Model](31-credential-provider-model.md) | [Auth Providers](19-auth-providers.md) | [Session Revocation](35-session-revocation.md) | [Design Priorities](00-design-priorities.md)
+
+> **This document maps to: DATA + POLICY**
+>
+> Internal component identities are Data — each component has a UUID, certificate, and service account. Internal auth is Policy — the same five-check boundary model from doc 26 applies at every internal call boundary, with no exceptions for "trusted internal network." This document specifies how DCM's control plane components authenticate to each other in a distributed deployment.
+
+---
+
+## 1. The Core Principle
+
+**Network position grants zero trust.** This is stated in doc 26 for external interactions. It applies equally to internal component communication. A call from the Policy Engine to the Placement Engine receives the same boundary checks as a call from an external consumer. The service mesh enforces this at the infrastructure level; DCM enforces it at the application level.
+
+**Two-layer enforcement:**
+1. **Mesh layer (infrastructure):** mTLS mutual authentication (RFC 8446 TLS 1.3), certificate validation (RFC 5280), traffic policies — enforced by the service mesh (Istio or equivalent)
+2. **Application layer (DCM):** component identity verification, operation authorization, scoped interaction credentials — enforced by DCM's Ingress and Auth subsystems
+
+Neither layer alone is sufficient. The mesh layer prevents impersonation at the transport level; the application layer enforces what each component is permitted to do.
+
+---
+
+## 2. Component Identity Model
+
+Every DCM control plane component has a **component identity** — a stable, verifiable identity used for both mTLS and application-layer authorization.
+
+```yaml
+component_identity:
+  component_uuid:            # stable; assigned at deployment time
+  component_type: api_gateway | policy_engine | placement_engine | request_orchestrator |
+                  scoring_engine | drift_reconciler | lifecycle_enforcer | notification_router |
+                  audit_store | session_store | message_bus | service_provider_proxy
+  component_name:          # human-readable; e.g. "policy-engine-eu-west-1"
+  deployment_uuid:           # identifies the DCM deployment instance
+  
+  # Certificate identity
+  mtls_certificate:
+    subject: "CN=-,O=dcm-internal"
+    san: [, , ]
+    issuer_ca: 
+    issued_at: 
+    expires_at: 
+    
+  # Service account (application layer)
+  service_account_uuid:      # DCM actor of type "component_service_account"
+  allowed_operations: []   # what this component may call
+  allowed_targets: []      # which components it may call
+```
+
+### 2.1 Component Types and Communication Graph
+
+Not every component may call every other. The allowed communication graph is declared and enforced:
+
+```
+Consumer/Admin/Provider → API Gateway
+API Gateway → Request Orchestrator
+API Gateway → Policy Engine (for direct policy evaluation)
+API Gateway → Session Store (token validation)
+
+Request Orchestrator → Policy Engine
+Request Orchestrator → Placement Engine  
+Request Orchestrator → Scoring Engine
+Request Orchestrator → Audit Store
+Request Orchestrator → Message Bus
+
+Policy Engine → Audit Store
+Policy Engine → Message Bus (policy evaluation events)
+
+Placement Engine → Audit Store
+Placement Engine → Message Bus
+
+Scoring Engine → Audit Store
+
+Drift Reconciler → API Gateway (discovery dispatch)
+Drift Reconciler → Audit Store
+Drift Reconciler → Message Bus
+
+Lifecycle Enforcer → API Gateway (decommission dispatch)
+Lifecycle Enforcer → Audit Store
+Lifecycle Enforcer → Message Bus
+
+Notification Router → Message Bus (subscribe)
+Notification Router → credential management service Proxy (notification channel credentials)
+
+All components → Session Store (revocation check)
+All components → credential management service Proxy (interaction credential requests)
+```
+
+**ICOM-004:** Components may only call components declared in their `allowed_targets` list. A call from an unexpected source component is rejected with `403 Forbidden` and an audit record.
+
+---
+
+## 3. Certificate Issuance and Internal CA
+
+### 3.1 Certificate Authority for Internal Components
+
+Each DCM deployment uses a **registered Certificate Authority (CA)** for issuing component mTLS certificates. This may be:
+
+**Option A — Built-in Internal CA (default):** DCM operates its own CA per deployment. Simple to configure; no external dependencies; suitable for minimal through standard profiles.
+
+**Option B — External CA via credential management service:** An enterprise CA registered as a credential management service (HashiCorp Vault PKI, Venafi TLS Protect, EJBCA, AWS ACM Private CA, Azure Key Vault). The external CA issues component certificates using the standard credential management service interface — DCM requests certificates via the provider's API (ACME/EST/SCEP/CMP). See [credential management service Model](31-credential-provider-model.md) for registration. Recommended for fsi and sovereign profiles where the enterprise PKI chain must be maintained.
+
+Both options satisfy ICOM-001 (mTLS required). The distinction is who issues the certificates, not whether mTLS is used.
+
+**The registered CA's root certificate is installed in all component trust stores at deployment time.** For Option B, the credential management service's CA root (which may itself be a subordinate of an enterprise root) is the trust anchor.
+
+```yaml
+internal_ca:
+  ca_uuid: 
+  deployment_uuid: 
+  ca_type: built_in | external_service_provider
+  service_provider_uuid:   # if ca_type: external
+  external_ca_protocol: acme | est | scep | cmp | null  # if external
+  root_cert_fingerprint: 
+  certificate_lifetime: P90D           # profile-governed — see table below
+  renewal_trigger: P14D
+  algorithm: ECDSA-P-384               # FIPS-compliant; all profiles
+  crl_endpoint: 
+  ocsp_endpoint: 
+```
+
+```yaml
+internal_ca:
+  ca_uuid: 
+  deployment_uuid: 
+  root_cert_fingerprint: 
+  certificate_lifetime: P90D           # all component certs valid 90 days
+  renewal_trigger: P14D                # renew 14 days before expiry
+  algorithm: ECDSA-P-384               # FIPS-compliant for all profiles
+  crl_endpoint:          # revocation list for component certs
+  ocsp_endpoint:         # online status check
+```
+
+### 3.2 Profile-Governed Certificate Configuration
+
+| Profile | Cert lifetime | Renewal trigger | Bootstrap token TTL | Min key algorithm |
+|---------|--------------|-----------------|--------------------|--------------------|
+| `minimal` | P180D | P30D | PT4H | RSA-2048 (min) |
+| `dev` | P90D | P14D | PT1H | RSA-2048 (min) |
+| `standard` | P90D | P14D | PT1H | ECDSA-P-256 (min) |
+| `prod` | P90D | P14D | PT1H | ECDSA-P-384 |
+| `fsi` | P30D | P7D | PT30M | ECDSA-P-384 |
+| `sovereign` | P14D | P3D | PT15M | ECDSA-P-384 (HSM-backed if hardware_attested) |
+
+> **sovereign profile:** Certificates must be HSM-backed if the deployment posture is `hardware_attested`. The external CA option (Option B) using an HSM-backed Vault PKI backend satisfies this requirement.
+
+### 3.3 Certificate Lifecycle
+
+```
+Component starts
+  │
+  ▼ Does component have a valid certificate?
+  │   YES → Use existing certificate
+  │   NO (first start or expired) → Request certificate from Internal CA
+  │
+  ▼ Certificate request to Internal CA:
+  │   component_uuid, component_type, deployment_uuid
+  │   CSR signed with bootstrap key (see Section 5)
+  │
+  ▼ Internal CA issues certificate
+  │   Subject: CN=-,O=dcm-internal
+  │   SAN: component_uuid, component_name, internal DNS name
+  │   Valid for: P90D (profile-governed)
+  │
+  ▼ Component stores certificate; begins accepting mTLS connections
+  │
+  ▼ 14 days before expiry: auto-renewal
+      Background thread requests new certificate
+      Transition: both old and new cert valid for PT1H
+      Old cert retired after transition
+```
+
+---
+
+## 4. Application-Layer Authorization
+
+mTLS verifies **who** is calling. Application-layer authorization verifies **what** the caller is permitted to do.
+
+### 4.1 Interaction Credential for Internal Calls
+
+Every internal component call follows the same ZTS-002 scoped interaction credential model used for external provider dispatch:
+
+```
+Component A prepares to call Component B
+  │
+  ▼ Request interaction credential from credential management service Proxy:
+  │   credential_type: dcm_interaction
+  │   issued_to.component_uuid: 
+  │   scope.operations: []
+  │   scope.target_component: 
+  │   expires_at: 
+  │
+  ▼ Call Component B with:
+  │   mTLS certificate (transport identity)
+  │   Interaction credential in Authorization header (operation authorization)
+  │   Correlation ID (tracing)
+  │
+  ▼ Component B validates:
+  │   1. mTLS cert from Component A's known CA ✓
+  │   2. Interaction credential: not revoked, not expired, scoped to this operation ✓
+  │   3. Component A is in allowed_sources for this endpoint ✓
+  │   4. Operation matches declared scope ✓
+  │   → All pass: process request
+  │   → Any fail: 403 + audit record ICOM_AUTH_FAILURE
+```
+
+### 4.2 Internal Endpoint Authorization
+
+Each internal component endpoint declares which source components are permitted to call it:
+
+```yaml
+internal_endpoint:
+  component: policy_engine
+  endpoint: POST /internal/evaluate
+  allowed_sources:
+    - api_gateway
+    - request_orchestrator
+  required_scope: policy.evaluate
+  audit_every_call: true           # all internal calls are audited
+```
+
+**ICOM-003:** Internal endpoints that receive calls from unauthorized source components return 403 and write an `ICOM_UNAUTHORIZED_SOURCE` audit record. This audit record has urgency: high — unexpected internal call patterns are security signals.
+
+---
+
+## 5. Bootstrap — First Certificate
+
+The bootstrap problem: a new component needs a certificate, but it has no certificate yet to authenticate its request. DCM solves this with a **bootstrap token** mechanism.
+
+### 5.1 Bootstrap Token
+
+At deployment time, the platform admin generates a one-time bootstrap token for each component:
+
+```
+POST /api/v1/admin/components/bootstrap-tokens
+
+{
+  "component_type": "policy_engine",
+  "component_uuid": "",
+  "deployment_uuid": "",
+  "expires_at": ""     // short-lived: PT1H maximum
+}
+
+Response 201:
+{
+  "bootstrap_token": "",    // one-time use; stored as env var or secret
+  "component_uuid": "",
+  "expires_at": ""
+}
+```
+
+### 5.2 First Certificate Acquisition
+
+```
+New component starts with bootstrap_token in environment
+  │
+  ▼ POST /internal/ca/issue-certificate
+  │   Authorization: Bootstrap 
+  │   Body: { component_uuid, component_type, deployment_uuid, csr_pem }
+  │
+  ▼ Internal CA validates:
+  │   Bootstrap token not expired
+  │   Bootstrap token not previously used (one-time)
+  │   component_uuid matches token's declared component_uuid
+  │
+  ▼ Certificate issued
+  │   Bootstrap token invalidated immediately after use
+  │
+  ▼ Component uses certificate for all subsequent communication
+    No further need for bootstrap token
+```
+
+**ICOM-007:** Bootstrap tokens are one-time-use and must expire within PT1H of creation. A bootstrap token that is not used within PT1H is automatically invalidated. Platform admins must generate new tokens if a component fails to start within the window.
+
+### 5.3 Kubernetes Deployment Integration
+
+In Kubernetes deployments, bootstrap tokens are injected as Kubernetes Secrets and mounted as environment variables. The component reads the bootstrap token on startup, acquires its certificate, then deletes the Kubernetes Secret. This ensures the bootstrap credential is not persisted beyond initial use.
+
+```yaml
+# Kubernetes Secret (deleted by component after first cert acquisition)
+apiVersion: v1
+kind: Secret
+metadata:
+  name: dcm-policy-engine-bootstrap
+type: Opaque
+stringData:
+  DCM_BOOTSTRAP_TOKEN: ""
+  DCM_COMPONENT_UUID: ""
+  DCM_INTERNAL_CA_ENDPOINT: "https://dcm-internal-ca.dcm-system.svc.cluster.local"
+```
+
+---
+
+## 6. Certificate Compromise Response
+
+If a component certificate is compromised, the response follows the same emergency pattern as credential compromise:
+
+```
+Certificate compromise detected
+  │
+  ▼ Compromised cert added to Internal CA CRL
+  │   CRL update propagated to all components within SLA:
+  │     standard/prod: PT1M
+  │     fsi/sovereign: PT15S
+  │
+  ▼ Component identity suspended in DCM
+  │   All active interaction credentials for this component → revoked
+  │   ICOM_CERT_COMPROMISED audit record written
+  │
+  ▼ Platform admin notified (urgency: critical)
+  │
+  ▼ New certificate issued for legitimate component instance
+  │   Previous certificate remains in CRL permanently
+  │
+  ▼ Component resumes with new certificate
+```
+
+**ICOM-008:** Compromised internal component certificates are added to the Internal CA CRL immediately. All other components refresh their CRL cache within the profile-governed SLA and reject connections presenting the revoked certificate.
+
+---
+
+## 7. Deployment Architecture Summary
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│                    DCM Control Plane                             │
+│                                                                  │
+│  ┌──────────┐  mTLS+cred  ┌──────────────────┐                  │
+│  │API Gateway│───────────→│Request Orchestrator│                 │
+│  └──────────┘             └────────┬─────────┘                  │
+│                                    │ mTLS+cred (each call)       │
+│                    ┌───────────────┼───────────────┐            │
+│                    ↓               ↓               ↓            │
+│             ┌─────────────┐ ┌──────────┐ ┌──────────────┐      │
+│             │Policy Engine│ │Placement │ │Scoring Engine│      │
+│             └─────────────┘ │Engine    │ └──────────────┘      │
+│                             └──────────┘                        │
+│                                                                  │
+│  ┌──────────────────┐  ┌─────────────┐  ┌──────────────────┐   │
+│  │credential management service│  │Session Store│  │Internal CA       │   │
+│  │Proxy             │  │             │  │(cert authority)   │   │
+│  └──────────────────┘  └─────────────┘  └──────────────────┘   │
+│                                                                  │
+│  Service Mesh (Istio): mTLS enforcement at transport layer      │
+│  All calls: authenticated + authorized + audited                │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 8. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `ICOM-001` | All internal component-to-component communication must use mTLS with certificates issued by the deployment's Internal CA. Plaintext internal communication is prohibited in all profiles. |
+| `ICOM-002` | Every internal call must present a scoped interaction credential (ZTS-002) in addition to the mTLS certificate. The mTLS certificate proves identity; the interaction credential proves authorization for the specific operation. |
+| `ICOM-003` | Internal endpoints reject calls from components not in their `allowed_sources` list with 403 and an `ICOM_UNAUTHORIZED_SOURCE` audit record (urgency: high). |
+| `ICOM-004` | Components may only call components declared in their `allowed_targets` list. Attempts to call unauthorized components are rejected at the mesh layer (traffic policy) and, if they reach the application layer, at the application layer. |
+| `ICOM-005` | All internal component calls are audited: source component, target component, operation, interaction credential UUID, outcome. Internal audit records are written to the same Audit Store as external interactions. |
+| `ICOM-006` | Component certificates are issued by the Internal CA with a maximum validity of P90D and renewed automatically P14D before expiry. Component certificates may not be issued by external CAs. |
+| `ICOM-007` | Bootstrap tokens are one-time-use and expire within PT1H. A bootstrap token that has been used is immediately invalidated. Unused tokens are invalidated at expiry. |
+| `ICOM-008` | Compromised internal component certificates are added to the Internal CA CRL immediately. All components refresh their CRL cache within the profile-governed SLA. |
+| `ICOM-009` | The trust anchor for internal component mTLS is a registered root or intermediate CA whose certificate is installed in all component trust stores at deployment time. The trust anchor may be the built-in Internal CA or an external CA registered as a Certificate Provider (e.g. HashiCorp Vault PKI, Venafi, EJBCA) — see [credential management service Model](31-credential-provider-model.md) Section on External CAs. Components do not accept certificates from unregistered trust anchors. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/37-scheduled-requests.md b/content/docs/architecture/data-model/37-scheduled-requests.md
new file mode 100644
index 0000000..dad7660
--- /dev/null
+++ b/content/docs/architecture/data-model/37-scheduled-requests.md
@@ -0,0 +1,291 @@
+# DCM Data Model — Scheduled and Deferred Requests
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Request Scheduling
+**Related Documents:** [Resource and Service Entities](06-resource-service-entities.md) | [Operational Models](24-operational-models.md) | [Request Dependency Graph](38-request-dependency-graph.md) | [Event Catalog](33-event-catalog.md) | [Consumer API Specification](../specifications/consumer-api-spec.md)
+
+> **This document maps to: DATA + PROVIDER**
+>
+> A scheduled request is still a request — it goes through the same Intent → Requested → Realized pipeline. The only difference is when the pipeline's dispatch step fires. Scheduling is a field on the request, not a separate object type. The Request Orchestrator handles dispatch timing; the Policy Engine evaluates at declaration time (gatekeeping) and again at dispatch time (policy correctness at the moment of execution).
+
+---
+
+## 1. The Scheduling Model
+
+### 1.1 Core Concept
+
+Every DCM request has an implicit `schedule: immediate`. Scheduled requests make this explicit:
+
+```yaml
+# Standard immediate request (implicit)
+schedule:
+  dispatch: immediate
+
+# Deferred — dispatch at a specific time
+schedule:
+  dispatch: at
+  not_before: "2026-04-01T02:00:00Z"   # UTC; dispatch begins at or after this time
+  not_after: "2026-04-01T04:00:00Z"    # optional deadline; cancel if missed
+
+# Maintenance window — dispatch during the next matching window
+schedule:
+  dispatch: window
+  window_id:   # references a declared Maintenance Window
+  not_after: "2026-04-30T00:00:00Z"    # optional: cancel if no window occurs before this
+
+# Recurring — for decommission, TTL extension, or rehydration operations
+schedule:
+  dispatch: recurring
+  cron: "0 2 * * 0"                    # cron expression (UTC)
+  max_occurrences: 4                   # optional limit
+  not_after: "2026-12-31T00:00:00Z"   # optional end date
+```
+
+### 1.2 What Can Be Scheduled
+
+Scheduling applies to any request operation that results in a dispatch to a provider. This includes:
+
+| Operation | Scheduling supported | Notes |
+|-----------|---------------------|-------|
+| Resource creation | ✅ | Full scheduling model |
+| Resource update (PATCH) | ✅ | Full scheduling model |
+| Suspend | ✅ | Full scheduling model |
+| Resume | ✅ | Full scheduling model |
+| Decommission | ✅ | Full scheduling model; `not_after` recommended |
+| Rehydration | ✅ | Full scheduling model |
+| TTL extension | ✅ | Full scheduling model |
+| Discovery trigger | ❌ | Handled by Discovery Scheduling Model (doc 24 §4) |
+
+---
+
+## 2. Request State During Deferral
+
+A scheduled request moves through the four states with one additional intermediate status:
+
+```
+Submit request with schedule.dispatch: at
+  │
+  ▼ ACKNOWLEDGED (Intent State created)
+  │   entity_uuid assigned
+  │   schedule stored in Intent State
+  │
+  ▼ Policy evaluation at declaration time
+  │   GateKeeper policies run immediately
+  │   If rejected: request fails before entering queue
+  │   If approved: request enters scheduled queue
+  │
+  ▼ SCHEDULED (new status within Intent State)
+  │   stored in Request Scheduler queue
+  │   visible via GET /api/v1/requests/{uuid}/status
+  │   cancellable: DELETE /api/v1/requests/{uuid}
+  │
+  ▼ [at not_before time] → Policy re-evaluation at dispatch
+  │   Transformation policies re-run (data may have changed)
+  │   GateKeeper re-evaluation with current data
+  │   If still approved: proceed to LAYERS_ASSEMBLED → dispatch
+  │   If rejected at dispatch time: FAILED with reason schedule_policy_rejection
+  │
+  ▼ DISPATCHED → REALIZED (normal pipeline)
+```
+
+### 2.1 Why Policy Runs Twice
+
+Policies are evaluated at declaration time to catch obvious rejections early (fail fast). They run again at dispatch time because data may have changed — quota may be exhausted, a compliance policy may have been activated, the actor's role may have changed. The dispatch-time evaluation uses the current policy set, not the one in effect at declaration.
+
+**SCH-003:** Scheduled requests that fail dispatch-time policy re-evaluation enter FAILED state with `failure_reason: schedule_policy_rejection`. The consumer receives a `request.failed` event with the policy rejection detail.
+
+---
+
+## 3. Maintenance Windows
+
+A Maintenance Window is a reusable schedule artifact — a named recurrence that scheduled requests can reference. This allows operations teams to declare approved change windows once and have requests automatically slot into them.
+
+```yaml
+maintenance_window:
+  window_uuid: 
+  window_handle: "weekly-sunday-0200-utc"
+  description: "Weekly maintenance window — low traffic period"
+  
+  # Recurrence
+  cron: "0 2 * * 0"          # every Sunday at 02:00 UTC
+  duration: PT2H              # window is 2 hours long
+  
+  # Scope
+  tenant_uuid:   # null = platform-wide window
+  resource_types: []     # empty = all resource types
+  
+  # Approval
+  status: active | suspended
+  approved_by: 
+  effective_from: 
+  
+  # Metadata
+  created_at: 
+  created_by: 
+```
+
+### 3.1 Maintenance Window API
+
+```
+# Platform admin operations
+POST   /api/v1/admin/maintenance-windows
+GET    /api/v1/admin/maintenance-windows
+GET    /api/v1/admin/maintenance-windows/{window_uuid}
+PATCH  /api/v1/admin/maintenance-windows/{window_uuid}
+DELETE /api/v1/admin/maintenance-windows/{window_uuid}
+
+# Consumer operations
+GET    /api/v1/maintenance-windows          # list windows visible to consumer
+GET    /api/v1/maintenance-windows/{uuid}  # describe a specific window
+```
+
+---
+
+## 4. The Request Scheduler Component
+
+The Request Scheduler is a DCM control plane component responsible for managing the scheduled request queue and triggering dispatch at the appropriate time.
+
+```
+Request Scheduler responsibilities:
+  - Maintain a priority queue of SCHEDULED requests ordered by not_before
+  - Poll queue; dispatch requests when not_before is reached
+  - Check not_after deadlines; cancel expired requests with reason: schedule_deadline_missed
+  - Listen for maintenance_window events to trigger window-scheduled requests
+  - On dispatch: hand off to Request Orchestrator (same path as immediate requests)
+  - Write SCHEDULED status updates to Intent State
+  - Publish request.scheduled and request.schedule_cancelled events
+```
+
+### 4.1 Deadline Enforcement
+
+If a request has `not_after` set and the deadline passes before dispatch:
+
+```
+not_after reached without dispatch
+  │
+  ▼ Request status → FAILED
+  │   failure_reason: schedule_deadline_missed
+  │
+  ▼ request.failed event published (urgency: medium)
+  │   consumer notified
+  │
+  ▼ Intent State marked terminal — no further retries
+```
+
+---
+
+## 5. Consumer API Additions
+
+### 5.1 Submit Scheduled Request
+
+Scheduling is an optional `schedule` field on the existing request submission body:
+
+```
+POST /api/v1/requests
+
+{
+  "catalog_item_uuid": "",
+  "fields": { ... },
+  "schedule": {
+    "dispatch": "at",
+    "not_before": "2026-04-01T02:00:00Z",
+    "not_after": "2026-04-01T06:00:00Z"
+  }
+}
+
+Response 202:
+{
+  "request_uuid": "",
+  "entity_uuid": "",
+  "status": "SCHEDULED",
+  "scheduled_dispatch_at": "2026-04-01T02:00:00Z",
+  "schedule_deadline": "2026-04-01T06:00:00Z"
+}
+```
+
+### 5.2 List Scheduled Requests
+
+```
+GET /api/v1/requests?status=SCHEDULED
+
+Response 200:
+{
+  "items": [
+    {
+      "request_uuid": "",
+      "entity_uuid": "",
+      "status": "SCHEDULED",
+      "catalog_item_handle": "compute.vm.standard",
+      "scheduled_dispatch_at": "2026-04-01T02:00:00Z",
+      "schedule_deadline": "2026-04-01T06:00:00Z",
+      "created_at": ""
+    }
+  ],
+  "total": 3
+}
+```
+
+### 5.3 Cancel Scheduled Request
+
+Cancellation uses the existing endpoint — no new endpoint needed:
+
+```
+DELETE /api/v1/requests/{request_uuid}
+
+# Works on SCHEDULED requests; moves status to CANCELLED
+# Returns 409 if request is already dispatched (past SCHEDULED)
+
+Response 204 No Content
+```
+
+---
+
+## 6. New Events
+
+Two new event types for the Event Catalog (doc 33):
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `request.scheduled` | info | Request entered SCHEDULED queue |
+| `request.schedule_cancelled` | low | Scheduled request cancelled before dispatch |
+| `request.schedule_deadline_missed` | medium | not_after passed without dispatch |
+
+These add to the `request.*` domain. Updated domain total: 17 request events.
+
+---
+
+## 8. Profile-Governed Scheduling Configuration
+
+Scheduling constraints are profile-governed to reflect the operational risk tolerance of each deployment context:
+
+| Profile | Max scheduling horizon | Max concurrent scheduled/actor | Recurring max frequency | Maintenance window approval tier |
+|---------|----------------------|-------------------------------|------------------------|----------------------------------|
+| `minimal` | P365D | unlimited | PT1H | auto |
+| `dev` | P365D | 50 | PT1H | auto |
+| `standard` | P90D | 20 | PT4H | reviewed |
+| `prod` | P30D | 10 | PT12H | reviewed |
+| `fsi` | P14D | 5 | PT24H | verified |
+| `sovereign` | P7D | 3 | PT24H | authorized |
+
+**Max scheduling horizon:** How far in the future a `not_before` may be set. Requests with `not_before` beyond the profile limit are rejected (422) at submission.
+
+**Max concurrent scheduled/actor:** How many SCHEDULED (not yet dispatched) requests a single actor may have at one time. Exceeding this limit returns 429.
+
+**Recurring max frequency:** The minimum interval between recurring dispatches. A cron expression that would dispatch more frequently than this is rejected.
+
+**Maintenance window approval tier:** The authority tier required to create or modify a Maintenance Window (see ATM-001, doc 32).
+
+## 7. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `SCH-001` | Scheduled requests undergo GateKeeper policy evaluation at declaration time (to catch rejections early) and again at dispatch time (to validate against current state). Both evaluations must pass. |
+| `SCH-002` | The `not_before` field must be a future timestamp at submission time. DCM rejects scheduled requests with a past `not_before` (returns 422). |
+| `SCH-003` | Requests that fail dispatch-time policy re-evaluation enter FAILED state with `failure_reason: schedule_policy_rejection`. Consumers receive a `request.failed` event with the rejection detail. |
+| `SCH-004` | Scheduled requests are cancellable (DELETE /api/v1/requests/{uuid}) at any time before dispatch. Once the Request Orchestrator has accepted the handoff (status moves beyond SCHEDULED), cancellation follows the standard cancellation model. |
+| `SCH-005` | If `not_after` is set and passes without dispatch, the request enters FAILED state with `failure_reason: schedule_deadline_missed`. No retry is attempted. |
+| `SCH-006` | Maintenance Windows are platform-level or tenant-scoped artifacts requiring platform admin approval. Window schedules are versioned artifacts subject to standard DCM lifecycle. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/38-request-dependency-graph.md b/content/docs/architecture/data-model/38-request-dependency-graph.md
new file mode 100644
index 0000000..920bf20
--- /dev/null
+++ b/content/docs/architecture/data-model/38-request-dependency-graph.md
@@ -0,0 +1,305 @@
+# DCM Data Model — Consumer Request Dependency Graph
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Cross-Request Ordering
+**Related Documents:** [Service Dependencies](07-service-dependencies.md) | [Scheduled Requests](37-scheduled-requests.md) | [compound service definition Composability](30-meta-provider-model.md) | [Operational Models](24-operational-models.md) | [Consumer API Specification](../specifications/consumer-api-spec.md)
+
+> **Events:** Dependency resolution events (`request.dependencies_resolved`, `dependency.state_changed`) are defined in the [Event Catalog](33-event-catalog.md).
+
+> **This document maps to: DATA + PROVIDER**
+>
+> **Distinction from existing dependency models:**
+> - Doc 07 (Service Dependencies): *type-level* dependencies — DCM knows that a VM *type* requires an IP type. Resolved automatically during layer assembly.
+> - Doc 30 (compound service definition): *compound service* dependencies — a compound service definition declares its own constituents and DCM sequences them. Consumer does not manage this.
+> - **This document**: *consumer-declared cross-request ordering* — a consumer submitting multiple independent requests says "Request B may not dispatch until Request A is realized." These are requests for different resource types that have no type-level dependency; the consumer is expressing an ordering constraint for their specific deployment.
+
+---
+
+## 1. The Problem
+
+A consumer deploying a three-tier application submits three requests: a database VM, an application VM, and a load balancer. Without ordering, all three dispatch simultaneously. But the application VM's startup configuration needs the database's IP address, which only exists after the database is realized.
+
+This is not a type-level dependency (the VM type does not require a VM type). It is a *deployment-time ordering constraint* declared by the consumer for this specific deployment.
+
+compound service definition composition handles this when a platform team has pre-defined the compound service. But consumers also need to express ad-hoc ordering for their own deployments without requiring a compound service definition to exist.
+
+---
+
+## 2. The Request Dependency Graph
+
+A Request Dependency Group is a consumer-declared set of requests with ordering constraints between them.
+
+```yaml
+request_dependency_group:
+  group_uuid: 
+  group_handle: "three-tier-app-deploy"   # optional, consumer-defined
+  
+  requests:
+    - request_uuid:                # database VM
+      depends_on: []                     # no dependencies — dispatches immediately
+      
+    - request_uuid:                # application VM
+      depends_on:
+        - request_uuid: 
+          wait_for: realized             # dispatch only after db is REALIZED
+          inject_fields:                 # optional: inject realized fields into this request
+            - from_field: "realized_fields.primary_ip"
+              to_field: "fields.db_host"
+              
+    - request_uuid:                # load balancer
+      depends_on:
+        - request_uuid: 
+          wait_for: realized
+          inject_fields:
+            - from_field: "realized_fields.primary_ip"
+              to_field: "fields.backend_hosts[0]"
+  
+  # Group-level options
+  on_failure: cancel_remaining | continue   # what to do if a request fails
+  timeout: PT2H                             # group-level deadline
+```
+
+### 2.1 wait_for Values
+
+| Value | Meaning |
+|-------|---------|
+| `acknowledged` | Dispatch as soon as dependency has an entity_uuid |
+| `approved` | Dispatch when dependency has passed approval |
+| `dispatched` | Dispatch when dependency has been sent to its provider |
+| `realized` | Dispatch only when dependency is fully realized (default, most common) |
+
+### 2.2 Field Injection
+
+The `inject_fields` mechanism passes realized output fields from a dependency directly into a dependent request's fields — without the consumer having to poll and re-submit. The injection happens at dispatch time, after the dependency is realized.
+
+```
+Dependency realized → Realized State written
+  │
+  ▼ DCM reads inject_fields declarations for dependent requests
+  │   For each injection: extract from_field from Realized State
+  │   Inject into dependent request's field at to_field path
+  │
+  ▼ Dependent request proceeds to layer assembly with injected fields
+```
+
+Field injection is subject to the same transformation policies as any other field — if a policy transforms `db_host`, the injection result passes through it.
+
+---
+
+## 3. Submitting a Dependency Group
+
+### 3.1 Declare and Submit in One Call
+
+```
+POST /api/v1/request-groups
+
+{
+  "group_handle": "three-tier-app-deploy",
+  "on_failure": "cancel_remaining",
+  "timeout": "PT2H",
+  "requests": [
+    {
+      "ref": "db",                          # local reference within this submission
+      "catalog_item_uuid": "",
+      "fields": { "cpu_count": 8, "memory_gb": 32, "role": "database" }
+    },
+    {
+      "ref": "app",
+      "catalog_item_uuid": "",
+      "fields": { "cpu_count": 4, "memory_gb": 16, "role": "application" },
+      "depends_on": [
+        {
+          "ref": "db",
+          "wait_for": "realized",
+          "inject_fields": [
+            { "from_field": "realized_fields.primary_ip", "to_field": "fields.db_host" }
+          ]
+        }
+      ]
+    },
+    {
+      "ref": "lb",
+      "catalog_item_uuid": "",
+      "fields": { "backend_port": 8080 },
+      "depends_on": [
+        { "ref": "app", "wait_for": "realized",
+          "inject_fields": [
+            { "from_field": "realized_fields.primary_ip", "to_field": "fields.backend_hosts[0]" }
+          ]
+        }
+      ]
+    }
+  ]
+}
+
+Response 202:
+{
+  "group_uuid": "",
+  "group_handle": "three-tier-app-deploy",
+  "requests": [
+    { "ref": "db",  "request_uuid": "", "entity_uuid": "", "status": "ACKNOWLEDGED" },
+    { "ref": "app", "request_uuid": "", "entity_uuid": "", "status": "PENDING_DEPENDENCY" },
+    { "ref": "lb",  "request_uuid": "", "entity_uuid": "", "status": "PENDING_DEPENDENCY" }
+  ],
+  "estimated_completion": ""
+}
+```
+
+### 3.2 Add an Existing Request to a Group
+
+```
+POST /api/v1/request-groups/{group_uuid}/members
+
+{
+  "request_uuid": "",
+  "depends_on": [ ... ]
+}
+```
+
+### 3.3 Query Group Status
+
+```
+GET /api/v1/request-groups/{group_uuid}
+
+Response 200:
+{
+  "group_uuid": "",
+  "group_handle": "three-tier-app-deploy",
+  "status": "in_progress | completed | failed | cancelled",
+  "requests": [
+    { "request_uuid": "", "ref": "db",  "status": "REALIZED" },
+    { "request_uuid": "", "ref": "app", "status": "DISPATCHED" },
+    { "request_uuid": "", "ref": "lb",  "status": "PENDING_DEPENDENCY" }
+  ],
+  "created_at": "",
+  "timeout_at": ""
+}
+```
+
+### 3.4 Cancel a Group
+
+```
+DELETE /api/v1/request-groups/{group_uuid}
+
+# Cancels all PENDING_DEPENDENCY and ACKNOWLEDGED requests in the group
+# Already-dispatched requests follow standard cancellation model
+Response 204
+```
+
+---
+
+## 4. PENDING_DEPENDENCY Status
+
+A request in a dependency group that is waiting for its dependency to reach `wait_for` state has status `PENDING_DEPENDENCY`. This is a new status in the Intent State lifecycle:
+
+```
+ACKNOWLEDGED → PENDING_DEPENDENCY → [dependency met] → LAYERS_ASSEMBLED → ... → REALIZED
+```
+
+`PENDING_DEPENDENCY` requests:
+- Are visible in `GET /api/v1/requests` with `status=PENDING_DEPENDENCY`
+- Can be cancelled: `DELETE /api/v1/requests/{uuid}`
+- Receive the `request.pending_dependency` event (new, info urgency)
+- Do not time out independently — the group-level `timeout` governs
+
+---
+
+## 5. Failure Handling
+
+### 5.1 `on_failure: cancel_remaining`
+
+When a request in the group fails and `on_failure: cancel_remaining` is set:
+
+```
+Request fails
+  │
+  ▼ All PENDING_DEPENDENCY and ACKNOWLEDGED requests in group → CANCELLED
+  │   failure_reason: dependency_failed
+  │
+  ▼ request.failed event for the failing request
+  │   request.cancelled events for each cancelled dependent
+  │
+  ▼ Group status → failed
+```
+
+### 5.2 `on_failure: continue`
+
+Failed request is marked FAILED; dependents that depended on it are also marked FAILED with `dependency_failed`. Independent requests in the group continue unaffected.
+
+### 5.3 Group Timeout
+
+If the group `timeout` duration elapses without all requests reaching a terminal state:
+
+```
+Group timeout reached
+  │
+  ▼ All non-terminal requests → FAILED
+  │   failure_reason: group_timeout
+  │
+  ▼ request.failed events for each
+  │   Group status → failed
+```
+
+---
+
+## 6. Relationship to compound service definitions
+
+Request dependency groups and compound service definitions solve overlapping but distinct problems:
+
+| | Request Dependency Group | compound service definition |
+|--|---|---|
+| **Who declares** | Consumer at request time | Platform team at catalog time |
+| **Reusable** | No — ad hoc | Yes — catalog item |
+| **Type constraints** | None — any resources | Defined by compound service definition spec |
+| **Policy governance** | Standard consumer request policies | compound service definition policies (MPX-*) |
+| **Field injection** | Consumer-declared inject_fields | compound service definition handles internally |
+| **Use case** | Ad-hoc deployment ordering | Standard compound service |
+
+When a standard compound service exists as a compound service definition, consumers should use it. Request dependency groups are for deployments that don't fit a predefined compound service pattern.
+
+---
+
+## 7. New Events
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `request.pending_dependency` | info | Request entered PENDING_DEPENDENCY state |
+| `request.dependency_met` | info | Dependency reached wait_for state; request proceeding |
+| `request.group_completed` | medium | All requests in group reached terminal state |
+| `request.group_failed` | high | Group failed or timed out |
+
+---
+
+## 9. Profile-Governed Dependency Group Configuration
+
+| Profile | Max group size | Max group timeout | Field injection validation | Max nesting depth |
+|---------|---------------|-------------------|---------------------------|-------------------|
+| `minimal` | 100 | P30D | advisory (warn only) | 3 |
+| `dev` | 100 | P7D | advisory | 3 |
+| `standard` | 50 | P3D | enforced | 3 |
+| `prod` | 25 | P1D | enforced + audited | 3 |
+| `fsi` | 10 | PT8H | enforced + audited + policy gated | 2 |
+| `sovereign` | 5 | PT4H | enforced + audited + policy gated | 2 |
+
+**Max group size:** Maximum number of requests in a single dependency group. RDG-002 sets the absolute upper bound at 100; profiles may set lower limits.
+
+**Max group timeout:** Maximum value of the `timeout` field. Groups declaring a timeout beyond the profile limit are rejected (422).
+
+**Field injection validation:** `advisory` — warns if injected fields fail schema validation but proceeds; `enforced` — rejects dispatch if injected fields fail validation; `policy gated` — field injection also passes through GateKeeper policy evaluation.
+
+**Max nesting depth:** Maximum depth of `depends_on` chains. A→B→C is depth 2. Exceeding this is rejected at submission (422).
+
+## 8. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `RDG-001` | Circular dependencies within a request group are rejected at submission time (422 Unprocessable Entity). DCM validates the dependency graph is a DAG before acknowledging the group. |
+| `RDG-002` | Maximum group size is 50 requests. Groups exceeding this must use compound service definition composition or be split into multiple groups. |
+| `RDG-003` | Field injection (`inject_fields`) is subject to all active Transformation policies. Injected values are not exempt from policy evaluation. |
+| `RDG-004` | `PENDING_DEPENDENCY` requests count against the consumer's quota. Resources are reserved at group submission, not at dispatch time. |
+| `RDG-005` | Group-level `timeout` is measured from group submission. Individual requests do not have independent timeouts while in PENDING_DEPENDENCY status. |
+| `RDG-006` | A request may belong to at most one dependency group. Attempts to add a request to a second group return 409 Conflict. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/39-dcm-self-health.md b/content/docs/architecture/data-model/39-dcm-self-health.md
new file mode 100644
index 0000000..ce116e8
--- /dev/null
+++ b/content/docs/architecture/data-model/39-dcm-self-health.md
@@ -0,0 +1,449 @@
+# DCM Data Model — DCM Self-Health Endpoints
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference — Operational Health
+**Related Documents:** [Deployment and Redundancy](17-deployment-redundancy.md) | [Internal Component Authentication](36-internal-component-auth.md) | [Operator Interface Specification](../specifications/dcm-operator-interface-spec.md) | [Admin API Specification](../specifications/dcm-admin-api-spec.md)
+
+> **Events:** Health state change events fire as `provider.healthy` / `provider.unhealthy` for external systems, and `governance.profile_changed` when health thresholds are adjusted. See [Event Catalog](33-event-catalog.md).
+
+> **This document maps to: PROVIDER**
+>
+> DCM itself must expose the same health contract it requires of Service Providers (doc OIS §4). This document specifies DCM's own liveness, readiness, and component health endpoints — required for Kubernetes operator deployment, load balancer health checking, and operational monitoring.
+
+---
+
+## 1. Three Health Endpoints
+
+DCM exposes three distinct health endpoints, following Kubernetes conventions:
+
+| Endpoint | Purpose | Failure action | Authentication |
+|----------|---------|---------------|---------------|
+| `GET /livez` | Is DCM alive? | Kubernetes restarts the pod | None |
+| `GET /readyz` | Is DCM ready to serve traffic? | Kubernetes removes from load balancer | None |
+| `GET /api/v1/admin/health` | Detailed component status | Informational — no automatic action | Admin auth required |
+
+Liveness and readiness are unauthenticated because they must work before authentication infrastructure is operational (e.g. during startup).
+
+---
+
+## 2. Liveness — `/livez`
+
+Liveness answers: **is this DCM process alive?**
+
+A liveness failure means the process is deadlocked, in an unrecoverable state, or otherwise unable to continue. Kubernetes responds by restarting the pod.
+
+```http
+GET /livez HTTP/1.1
+
+HTTP/1.1 200 OK
+Content-Type: application/health+json
+
+{
+  "status": "pass"
+}
+```
+
+**Liveness checks (minimal — fast):**
+- Process is responding
+- No deadlock detected in core event loop
+- Internal CA is reachable (for deployments with component auth)
+
+**Liveness failure response:**
+```http
+HTTP/1.1 503 Service Unavailable
+Content-Type: application/health+json
+
+{
+  "status": "fail",
+  "failure_reason": "event_loop_deadlock | internal_ca_unreachable | oom_imminent"
+}
+```
+
+**Liveness SLA:** Must respond within PT5S. No external calls. No database reads.
+
+---
+
+## 3. Readiness — `/readyz`
+
+Readiness answers: **is this DCM instance ready to serve requests?**
+
+A readiness failure removes the instance from the load balancer rotation without restarting it. This handles startup, migration, and graceful drain scenarios.
+
+```http
+GET /readyz HTTP/1.1
+
+HTTP/1.1 200 OK
+Content-Type: application/health+json
+
+{
+  "status": "pass",
+  "checks": {
+    "session_store": "pass",
+    "audit_store": "pass",
+    "policy_engine": "pass",
+    "message_bus": "pass",
+    "auth_provider": "pass"
+  }
+}
+```
+
+**Readiness checks:**
+- Session Store: can write and read a test record
+- Audit Store: reachable and writable
+- Policy Engine: responding to internal health ping
+- Message Bus: connected and subscribed
+- Auth Provider: at least one Auth Provider is responding
+- Schema version: database schema matches running code version
+
+**Readiness failure** (any check fails):
+```http
+HTTP/1.1 503 Service Unavailable
+Content-Type: application/health+json
+
+{
+  "status": "fail",
+  "checks": {
+    "session_store": "pass",
+    "audit_store": "fail",
+    "policy_engine": "pass",
+    "message_bus": "pass",
+    "auth_provider": "pass"
+  },
+  "failing_checks": ["audit_store"]
+}
+```
+
+**Readiness SLA:** Must respond within PT10S. Performs lightweight connectivity checks — no heavy queries.
+
+### 3.1 Startup vs Operational Readiness
+
+During startup, DCM goes through a startup sequence before becoming ready:
+
+```
+Process starts
+  │
+  ▼ /livez → pass (process alive)
+  │   /readyz → fail (not ready yet)
+  │
+  ▼ Internal CA connects → component certs verified
+  ▼ Session Store connected → revocation registry loaded
+  ▼ Audit Store connected → schema version validated
+  ▼ Policy Engine ready → policies loaded and shadow mode initialized
+  ▼ Auth Providers connected → at least one responding
+  ▼ Message Bus connected → subscriptions established
+  │
+  ▼ /readyz → pass (ready to serve traffic)
+```
+
+`startupProbe` in Kubernetes uses `/readyz` with a longer `failureThreshold` to allow startup time before the liveness probe takes over.
+
+---
+
+## 4. Detailed Health — `/api/v1/admin/health`
+
+The detailed health endpoint provides per-component status for operational monitoring. Requires admin authentication.
+
+```http
+GET /api/v1/admin/health HTTP/1.1
+Authorization: Bearer 
+
+HTTP/1.1 200 OK
+Content-Type: application/health+json
+
+{
+  "status": "pass | warn | fail",
+  "dcm_version": "1.2.0",
+  "dcm_instance_uuid": "",
+  "deployment_profile": "prod",
+  "uptime_seconds": 864023,
+  "checked_at": "",
+
+  "components": {
+    "api_gateway": {
+      "status": "pass",
+      "latency_p99_ms": 12,
+      "requests_per_minute": 340
+    },
+    "request_orchestrator": {
+      "status": "pass",
+      "queue_depth": 3,
+      "in_flight": 7
+    },
+    "policy_engine": {
+      "status": "pass",
+      "active_policies": 42,
+      "shadow_policies": 3,
+      "evaluations_per_minute": 280
+    },
+    "placement_engine": {
+      "status": "pass"
+    },
+    "scoring_engine": {
+      "status": "pass",
+      "evaluations_per_minute": 280
+    },
+    "request_scheduler": {
+      "status": "pass",
+      "scheduled_requests_queued": 5,
+      "next_dispatch_at": ""
+    },
+    "drift_reconciler": {
+      "status": "pass",
+      "last_cycle_completed_at": "",
+      "open_drift_records": 2
+    },
+    "lifecycle_enforcer": {
+      "status": "pass",
+      "entities_monitored": 1240,
+      "ttl_warnings_pending": 3
+    },
+    "discovery_scheduler": {
+      "status": "pass",
+      "pending_jobs": 1,
+      "last_completed_at": ""
+    },
+    "notification_router": {
+      "status": "pass",
+      "providers_active": 2,
+      "delivery_backlog": 0
+    },
+    "session_store": {
+      "status": "pass",
+      "active_sessions": 47,
+      "revocation_registry_size": 3
+    },
+    "audit_store": {
+      "status": "pass",
+      "records_last_hour": 1840,
+      "chain_integrity": "verified"
+    },
+    "message_bus": {
+      "status": "pass",
+      "lag_consumer_group_ms": 12
+    },
+    "internal_ca": {
+      "status": "pass",
+      "certificates_active": 12,
+      "next_expiry_at": ""
+    }
+  },
+
+  "providers": {
+    "registered": 4,
+    "healthy": 4,
+    "degraded": 0,
+    "unhealthy": 0
+  },
+
+  "auth_providers": {
+    "registered": 2,
+    "healthy": 2,
+    "unhealthy": 0
+  }
+}
+```
+
+### 4.1 Status Semantics
+
+| Status | Meaning |
+|--------|---------|
+| `pass` | Component fully operational |
+| `warn` | Operational but degraded (high latency, reduced capacity, elevated error rate) |
+| `fail` | Component not operational; DCM degraded |
+
+The top-level `status` is the worst status across all components:
+- Any `fail` → top-level `fail`
+- Any `warn`, no `fail` → top-level `warn`
+- All `pass` → top-level `pass`
+
+---
+
+## 5. Kubernetes Manifest
+
+```yaml
+# Standard Kubernetes probe configuration for DCM
+livenessProbe:
+  httpGet:
+    path: /livez
+    port: 8443
+    scheme: HTTPS
+  initialDelaySeconds: 10
+  periodSeconds: 10
+  failureThreshold: 3
+  timeoutSeconds: 5
+
+readinessProbe:
+  httpGet:
+    path: /readyz
+    port: 8443
+    scheme: HTTPS
+  initialDelaySeconds: 30
+  periodSeconds: 5
+  failureThreshold: 6
+  timeoutSeconds: 10
+
+startupProbe:
+  httpGet:
+    path: /readyz
+    port: 8443
+    scheme: HTTPS
+  initialDelaySeconds: 10
+  periodSeconds: 10
+  failureThreshold: 30    # allow up to 300s for startup
+  timeoutSeconds: 10
+```
+
+---
+
+## 6. Prometheus Metrics
+
+DCM exposes Prometheus-compatible metrics alongside health endpoints:
+
+```
+GET /metrics      # Prometheus scrape endpoint (unauthenticated in cluster; 
+                  # configurable for external exposure)
+```
+
+Key metric families:
+
+```
+# Request pipeline
+dcm_requests_total{status, resource_type, profile}
+dcm_request_duration_seconds{quantile, resource_type}
+dcm_requests_pending_dependency_total
+dcm_requests_scheduled_total
+
+# Policy engine
+dcm_policy_evaluations_total{outcome, enforcement_class}
+dcm_policy_shadow_divergences_total
+
+# Sessions
+dcm_sessions_active_total
+dcm_session_revocations_total{trigger}
+
+# Drift
+dcm_drift_open_records_total{severity}
+dcm_drift_detected_total
+
+# Providers
+dcm_providers_registered_total
+dcm_providers_healthy_total
+dcm_provider_dispatch_duration_seconds{provider_type, quantile}
+
+# Internal
+dcm_internal_ca_certificates_active
+dcm_internal_ca_days_until_next_expiry
+```
+
+---
+
+## 7. Per-Provider Metrics Contract
+
+In addition to DCM control plane metrics, each registered Service Provider must
+expose a Prometheus-compatible `/metrics` endpoint meeting the contract defined in
+the Registration Specification (GATE-SP-05).
+
+### 7.1 Required Provider Metric Families
+
+```
+# Dispatch metrics — how many requests DCM sent to this provider
+dcm_provider_dispatches_total{resource_type="Compute.VirtualMachine", outcome="success|failed|timeout"}
+dcm_provider_dispatch_duration_seconds{resource_type="Compute.VirtualMachine", quantile="0.5|0.95|0.99"}
+
+# Realization metrics — outcomes of provisioning
+dcm_provider_realizations_total{resource_type="Compute.VirtualMachine", status="OPERATIONAL|FAILED"}
+
+# Health signal
+dcm_provider_health_status  # gauge: 1=healthy, 0.5=degraded, 0=unhealthy
+```
+
+### 7.2 Recommended Provider Metric Families
+
+```
+# Capacity
+dcm_provider_capacity_remaining{resource_type="Compute.VirtualMachine"}  # gauge
+dcm_provider_capacity_total{resource_type="Compute.VirtualMachine"}      # gauge
+
+# Queue depth (for async-only providers)
+dcm_provider_queue_depth{resource_type="Compute.VirtualMachine"}  # gauge
+
+# Tenant usage
+dcm_provider_active_resources_total{tenant_uuid="...", resource_type="..."}
+```
+
+### 7.3 DCM Control Plane Aggregated Provider Metrics
+
+The DCM control plane exposes aggregated provider metrics at its own `/metrics`
+endpoint alongside the control plane metrics from Section 6:
+
+```
+# Already in Section 6 — shown here for cross-reference
+dcm_providers_registered_total               # count of registered providers
+dcm_providers_healthy_total                  # count currently healthy
+dcm_provider_dispatch_duration_seconds{...}  # aggregated across all providers
+```
+
+### 7.4 Tenant Metadata Endpoint
+
+Service Providers in `standard` and above profiles (GATE-SP-04) must implement
+a tenant metadata endpoint that DCM calls to retrieve per-tenant usage summaries:
+
+```
+GET /api/v1/tenants/{tenant_uuid}/metadata
+Authorization: Bearer 
+
+Response 200:
+{
+  "tenant_uuid": "",
+  "active_resources": {
+    "Compute.VirtualMachine": 12,
+    "Storage.Block": 8
+  },
+  "capacity_consumed": {
+    "Compute.VirtualMachine": {
+      "cpu_cores": 96,
+      "ram_gb": 384
+    }
+  },
+  "quota_consumed_pct": {
+    "Compute.VirtualMachine": 48.0
+  }
+}
+```
+
+This data is used by DCM's Cost Analysis component and multi-tenant quota
+enforcement. It is also exposed to tenant administrators via the consumer API.
+
+---
+
+## 8. Profile-Governed Health Exposure
+
+| Profile | /livez | /readyz | /api/v1/admin/health | /metrics scraping |
+|---------|--------|---------|----------------------|-------------------|
+| `minimal` | Unauthenticated | Unauthenticated | Admin auth | Internal network only |
+| `dev` | Unauthenticated | Unauthenticated | Admin auth | Internal network only |
+| `standard` | Unauthenticated | Unauthenticated | Admin auth | mTLS client cert or auth token |
+| `prod` | Unauthenticated | Unauthenticated | Admin auth | mTLS client cert or auth token |
+| `fsi` | Unauthenticated | Unauthenticated | Admin auth (MFA required) | mTLS + authorized scraper registration |
+| `sovereign` | Unauthenticated within cluster | Unauthenticated within cluster | Admin auth (MFA + step-up) | Disabled externally; internal only |
+
+**Notes:**
+- `/livez` and `/readyz` are always unauthenticated *within the cluster* — Kubernetes probes cannot present auth credentials. However, at the ingress boundary (external load balancer), these paths may be network-restricted.
+- For `fsi` and `sovereign` profiles, `/api/v1/admin/health` requires MFA-verified sessions (mfa_verified: true). Step-up MFA is required for sovereign.
+- The `sovereign` profile does not expose `/metrics` externally. Prometheus must scrape from within the cluster network only.
+- Component-level detail in `/api/v1/admin/health` may be redacted in fsi/sovereign profiles based on the requesting actor's role — SRE sees full detail; read-only admin sees summary only.
+
+## 7. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `HLT-001` | DCM must expose `/livez` and `/readyz` endpoints on the same port as the API, unauthenticated, following RFC 8615 / IANA health+json format. |
+| `HLT-002` | `/livez` must respond within PT5S with no external calls or database reads. A non-response within PT5S is treated as liveness failure. |
+| `HLT-003` | `/readyz` returns `fail` if the Session Store, Audit Store, Policy Engine, Message Bus, or any Auth Provider is unreachable. It returns `warn` if any optional component is degraded. |
+| `HLT-004` | `GET /api/v1/admin/health` requires admin authentication and provides per-component status. It must include the DCM version, instance UUID, and deployment profile. |
+| `HLT-005` | DCM must expose Prometheus-compatible metrics at `GET /metrics`. Metrics must include request pipeline, policy engine, session, drift, and provider metrics at minimum. |
+| `HLT-006` | The startup sequence must be observable via `/readyz`. DCM must not report `pass` on `/readyz` until the Session Store, Audit Store, Policy Engine, Auth Provider, and Message Bus are all reachable. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/40-standards-catalog.md b/content/docs/architecture/data-model/40-standards-catalog.md
new file mode 100644
index 0000000..07ad983
--- /dev/null
+++ b/content/docs/architecture/data-model/40-standards-catalog.md
@@ -0,0 +1,293 @@
+# DCM Data Model — Standards and Compliance Catalog
+
+**Document Status:** 🔄 In Progress
+**Document Type:** Architecture Reference — Normative Standards
+**Purpose:** Single authoritative source for all RFCs, protocols, specifications, and compliance frameworks referenced by the DCM architecture. For each standard: what it is, where DCM uses it, and what obligation it places on implementations.
+
+> **How to read this document:**
+> - **Normative** — DCM implementations MUST comply with this standard in the specified context
+> - **Informative** — DCM draws on this standard as guidance or reference without strict compliance
+> - **Optional** — DCM supports this standard in applicable profiles or configurations
+
+---
+
+## 1. Internet Standards (IETF RFCs)
+
+### 1.1 Authentication and Authorization
+
+| RFC | Title | Use in DCM | Obligation |
+|-----|-------|-----------|-----------|
+| **RFC 7519** | JSON Web Token (JWT) | Bearer token format for session tokens and API key tokens; claims carry actor_uuid, roles, tenant_uuid, exp | Normative |
+| **RFC 7517** | JSON Web Key (JWK) | Public key format for Auth Provider OIDC verification keys; JWKS endpoint for key discovery | Normative |
+| **RFC 7662** | OAuth 2.0 Token Introspection | `POST /api/v1/auth:introspect` — validates bearer tokens; response format `{active, session_uuid, actor_uuid, exp, roles}` | Normative |
+| **RFC 6749** | OAuth 2.0 Authorization Framework | Authorization flow for OIDC Auth Providers; client credentials flow for service account API keys | Informative |
+| **RFC 4511** | Lightweight Directory Access Protocol (LDAP) | LDAP/FreeIPA/Active Directory Auth Provider integration; bind operations, search filters for group membership | Normative |
+| **RFC 7643** | SCIM 2.0 Core Schema | Actor and group provisioning schema for enterprise IdP integration; SCIM deprovision triggers session + credential revocation | Normative |
+| **RFC 7644** | SCIM 2.0 Protocol | SCIM REST API for actor provisioning; DELETE triggers AUTH-016 (session) and CPX-006 (credential) revocation in parallel | Normative |
+
+### 1.2 Transport Security
+
+| RFC | Title | Use in DCM | Obligation |
+|-----|-------|-----------|-----------|
+| **RFC 8446** | TLS 1.3 | All external API communication; preferred TLS version; mandatory cipher suite compliance | Normative |
+| **RFC 5246** | TLS 1.2 | Permitted TLS version for compatibility; minimum acceptable version; TLS 1.0/1.1 prohibited | Normative |
+| **RFC 5280** | X.509 PKI Certificate and CRL Profile | All DCM certificates (component mTLS, Internal CA, credential management service certs); CRL format for revocation; certificate chain validation | Normative |
+| **RFC 6960** | Online Certificate Status Protocol (OCSP) | Internal CA OCSP endpoint for real-time certificate status; Internal CA CRL supplement | Normative |
+
+### 1.3 Certificate Enrollment
+
+| RFC | Title | Use in DCM | Obligation |
+|-----|-------|-----------|-----------|
+| **RFC 7030** | Enrollment over Secure Transport (EST) | Certificate enrollment for Internal CA (alternative to bootstrap token); preferred for automated cert lifecycle | Informative |
+| **RFC 8555** | ACME — Automatic Certificate Management Environment | Automated certificate lifecycle for external-facing TLS certificates; provider certificates | Informative |
+| **RFC 8894** | Simple Certificate Enrolment Protocol (SCEP) | Legacy certificate enrollment for environments without EST/ACME support | Optional |
+| **RFC 4210** | Certificate Management Protocol (CMP) | X.509 PKI certificate management in enterprise PKI environments | Optional |
+
+### 1.4 API Lifecycle
+
+| RFC | Title | Use in DCM | Obligation |
+|-----|-------|-----------|-----------|
+| **RFC 8594** | The Sunset HTTP Header Field | Deprecated API version responses include `Sunset: ` header (VER-003); also `Deprecation` header | Normative |
+| **RFC 9745** | The Deprecation HTTP Header Field | Deprecated API version responses include `Deprecation: ` header paired with RFC 8594 Sunset | Normative |
+
+### 1.5 Service Discovery and Health
+
+| RFC | Title | Use in DCM | Obligation |
+|-----|-------|-----------|-----------|
+| **RFC 8615** | Well-Known Uniform Resource Identifiers | `GET /.well-known/dcm-api-versions` (version discovery); `/livez` and `/readyz` path conventions; IANA health+json media type | Normative |
+
+### 1.6 Data Formats
+
+| RFC | Title | Use in DCM | Obligation |
+|-----|-------|-----------|-----------|
+| **ISO 8601** | Date and Time Format | All timestamps in DCM: `created_at`, `expires_at`, `not_before`, `not_after`, event timestamps; durations as ISO 8601 periods (P90D, PT8H) | Normative |
+| **RFC 8259** | The JavaScript Object Notation (JSON) Data Interchange Format | All DCM API request/response bodies; all entity definitions in stores | Normative |
+
+---
+
+## 2. Identity and Access Protocols
+
+| Protocol | Specification | Use in DCM | Obligation |
+|----------|--------------|-----------|-----------|
+| **OIDC / OpenID Connect** | OpenID Foundation Core 1.0 | Primary enterprise Auth Provider type; ID token format; JWKS endpoint for key verification; userinfo endpoint for actor enrichment | Normative |
+| **SAML 2.0** | OASIS SAML 2.0 | Auth Provider type for organizations without OIDC; assertion format for role mapping | Optional |
+| **mTLS** | RFC 8446 + RFC 5280 | All internal component-to-component communication (ICOM-001); provider-to-DCM authentication in ZTS model | Normative |
+| **LDAP v3** | RFC 4511 | FreeIPA, Active Directory, OpenLDAP Auth Provider types; group membership queries for RBAC | Normative |
+| **SCIM 2.0** | RFC 7643 + RFC 7644 | Optional enterprise provisioning; actor creation, update, deprovision; deprovision triggers parallel session + credential revocation | Optional |
+
+---
+
+## 3. Cryptographic Standards
+
+| Standard | Use in DCM | Profiles | Obligation |
+|----------|-----------|---------|-----------|
+| **ECDSA P-384** | Internal CA certificates; component mTLS certs; preferred curve for all DCM-issued certificates | All profiles | Normative for Internal CA |
+| **ECDSA P-256** | Permitted for performance-constrained contexts where P-384 is not available | minimal, dev | Optional |
+| **RSA ≥ 2048** | Permitted for compatibility with legacy systems; RSA < 2048 prohibited | All profiles | Conditional |
+| **AES-256-GCM** | Credential encryption at rest; audit record encryption (sovereign); data classification-driven | standard+ | Normative |
+| **AES-128-GCM** | Permitted for minimal/dev profiles where performance matters | minimal, dev | Conditional |
+| **SHA-256** | Hash function for audit chain integrity; entity handle generation; minimum acceptable | All profiles | Normative |
+| **SHA-384 / SHA-512** | Preferred hash function for fsi/sovereign profiles | fsi, sovereign | Normative for fsi+ |
+| **FIPS 140-2 Level 1** | Minimum cryptographic module requirement for standard/prod | standard, prod | Normative |
+| **FIPS 140-2 Level 2** | Cryptographic module requirement for regulated environments | fsi, fedramp_moderate | Normative |
+| **FIPS 140-3 Level 3** | Cryptographic module requirement for sovereign deployments | sovereign, dod_il4 | Normative |
+| **TLS 1.3** | Preferred; mandatory cipher suites; forward secrecy required | All profiles | Normative (preferred) |
+| **TLS 1.2** | Minimum acceptable; TLS 1.0/1.1 strictly prohibited | All profiles | Normative (minimum) |
+
+### 3.1 Forbidden Algorithms
+
+DCM prohibits the following algorithms in all profiles:
+
+| Algorithm | Reason |
+|-----------|--------|
+| MD5 | Cryptographically broken |
+| SHA-1 | Deprecated; collision attacks demonstrated |
+| DES | 56-bit key; insecure |
+| 3DES / Triple-DES | Deprecated; Sweet32 attack |
+| RC4 | Cryptographically broken |
+| RSA < 2048 | Insufficient key length |
+| ECDSA curves weaker than P-256 | Insufficient security level |
+
+---
+
+## 4. Operational Standards and Protocols
+
+| Standard | Specification | Use in DCM | Obligation |
+|----------|--------------|-----------|-----------|
+| **Prometheus / OpenMetrics** | Prometheus exposition format; OpenMetrics spec | `GET /metrics` scrape endpoint; all DCM metric families (HLT-005); provider health metrics | Normative |
+| **OpenTelemetry (OTel)** | CNCF OpenTelemetry specification | Distributed tracing for request pipeline; X-DCM-Correlation-ID propagation; span context for audit provenance | Informative |
+| **Kubernetes API** | kubernetes.io API conventions | Resource type spec format mirrors k8s YAML; CRD-based DCM Operator integration; probe endpoints (/livez, /readyz) | Normative (k8s deployments) |
+| **GitOps / OpenGitOps** | OpenGitOps principles (v1.0) | All DCM data model artifacts stored in Git; PR-based contribution model; Git as source of truth for policy and layer definitions | Normative |
+| **Unix cron** | POSIX cron expression format | Recurring schedule expressions in scheduled requests (doc 37) and maintenance window definitions | Normative |
+| **IANA health+json** | RFC 8615 + IANA media type registry | Health response format for `/livez`, `/readyz`, `/api/v1/admin/health`, and OIS health endpoint | Normative |
+| **W3C Server-Sent Events (SSE)** | W3C Living Standard | `GET /api/v1/requests/{uuid}/stream` live request status stream; events: status_change, progress_updated, approval_required, approval_recorded, heartbeat; stream closes on terminal status; alternative to polling for browser/CLI consumers | Normative |
+| **OpenAPI 3.1** | OpenAPI Initiative 3.1 | REST API specification format for Consumer API, Admin API, and Operator Interface Specification; schema definitions for request/response bodies | Normative |
+| **SPIFFE** | CNCF SPIFFE Specification v1.0 | Workload identity framework that inspired DCM's internal component identity model (ICOM); each DCM component has a stable UUID and certificate analogous to a SPIFFE ID; Istio/Envoy enforce SPIFFE-compatible workload identity | Informative |
+| **Istio / Service Mesh** | Istio service mesh specification | Internal component mTLS enforcement; traffic policies; circuit breaking; observability; service-to-service authorization | Normative (distributed deployments) |
+
+| **HashiCorp Vault PKI** | HashiCorp Vault PKI Secrets Engine | External CA credential management service backend (optional); issues x509 component certificates via native API or EST/ACME; recommended for fsi/sovereign profiles where enterprise PKI chain is required; typically operates as a subordinate CA of the organization root | Optional |
+| **Venafi TLS Protect** | Venafi Platform | External CA credential management service backend (optional); enterprise certificate lifecycle management; ACME/EST/REST API integration | Optional |
+| **EJBCA** | Enterprise JavaBeans Certificate Authority | External CA credential management service backend (optional); ACME/CMP/SCEP integration | Optional |
+
+---
+
+## 5. Compliance Frameworks
+
+These frameworks drive specific DCM profiles, overlays, and policy constraints. DCM does not certify compliance — it provides the architectural primitives that enable compliant implementations.
+
+### 5.1 US Federal and Defense
+
+| Framework | Full Name | DCM Profile/Overlay | Key DCM Requirements |
+|-----------|-----------|-------------------|---------------------|
+| **NIST SP 800-53** | Security and Privacy Controls for Information Systems | `fedramp_moderate`, `fedramp_high` | Policy control families mapped to DCM policy domains; access control, audit, configuration management |
+| **NIST SP 800-63B** | Digital Identity Guidelines | All profiles (AAL mapping) | AAL1 (minimal/dev), AAL2 (standard/prod), AAL2+ (fsi), AAL3 (sovereign); MFA requirements per level |
+| **FedRAMP Moderate** | Federal Risk and Authorization Management Program — Moderate | `fedramp_moderate` overlay | NIST 800-53 Moderate baseline; FIPS 140-2 Level 1+; Federal data handling requirements |
+| **FedRAMP High** | Federal Risk and Authorization Management Program — High | `fedramp_high` overlay | NIST 800-53 High baseline; FIPS 140-2 Level 2+; enhanced audit retention |
+| **DoD IL4** | Department of Defense Impact Level 4 | `dod_il4` overlay | Controlled Unclassified Information; FIPS 140-2 Level 2; hardware attestation; enhanced logging |
+| **FIPS 140-2/140-3** | Federal Information Processing Standard — Cryptographic Modules | fsi+ profiles | Cryptographic module validation; forbidden algorithm enforcement; key management requirements |
+
+### 5.2 Industry Compliance
+
+| Framework | Full Name | DCM Profile/Overlay | Key DCM Requirements |
+|-----------|-----------|-------------------|---------------------|
+| **PCI DSS** | Payment Card Industry Data Security Standard | `pci_dss` overlay | Req 8.3.9: P90D maximum credential rotation; network segmentation via sovereignty constraints; cardholder data access logging; 12-month audit retention |
+| **HIPAA** | Health Insurance Portability and Accountability Act | `fsi` profile; `hipaa` overlay | PHI access logging; minimum necessary access (RBAC); audit controls; transmission security (TLS 1.2+); workforce authentication (MFA) |
+| **SOC 2** | Service Organization Control 2 | `standard`+ profiles | Type II audit trail requirements; availability, security, confidentiality trust service criteria; change management via GitOps |
+| **ISO 27001** | Information Security Management Systems | All profiles | Risk-based approach; asset management; access control; cryptography; operations security; incident management |
+
+### 5.3 Data Protection / Sovereignty
+
+| Framework | Full Name | DCM Feature | Key DCM Requirements |
+|-----------|-----------|------------|---------------------|
+| **GDPR** | General Data Protection Regulation (EU) | Sovereignty constraints; data classification | Data residency enforcement; right to erasure model (entity decommission + audit retention policy); data minimization via field-level classification; consent/purpose tracking via Governance Matrix |
+| **Schrems II** | CJEU ruling on EU-US data transfers | Sovereignty constraints; federation boundaries | Data transfer restrictions between DCM federation peers; sovereign profile enforcement |
+
+---
+
+## 6. CNCF Ecosystem
+
+DCM is designed for CNCF ecosystem compatibility. The following CNCF projects are referenced:
+
+| Project | CNCF Status | DCM Use |
+|---------|------------|---------|
+| **Kubernetes** | Graduated | Deployment target; CRD-based DCM Operator; resource model inspiration |
+| **Open Policy Agent (OPA)** | Graduated | Policy engine backend option; Rego policies for DCM GateKeeper and Validation policy types |
+| **Prometheus** | Graduated | Metrics exposition format; DCM scrape endpoint |
+| **OpenTelemetry** | Graduated | Distributed tracing; correlation ID propagation |
+| **Istio** | Graduated | Service mesh for internal mTLS; traffic policies |
+| **Argo CD / Flux** | Graduated | GitOps delivery for DCM layer definitions and policy artifacts |
+
+---
+
+## 7. Authentication Assurance Levels (NIST SP 800-63B)
+
+DCM maps profile security postures to NIST Authentication Assurance Levels:
+
+| Profile | AAL | Requirements |
+|---------|-----|-------------|
+| `minimal` | AAL1 | Single-factor authentication acceptable; password or API key |
+| `dev` | AAL1 | Single-factor authentication acceptable |
+| `standard` | AAL2 | MFA required for all actor sessions; phishing-resistant preferred |
+| `prod` | AAL2 | MFA required; TOTP, FIDO2, or hardware token |
+| `fsi` | AAL2+ | MFA required; phishing-resistant authenticator (FIDO2/hardware token) |
+| `sovereign` | AAL3 | Hardware-based authenticator required; verifier impersonation resistance; physical authenticator possession |
+
+---
+
+## 8. Standard Usage Map — Where Each Standard Appears
+
+| Standard | Documents |
+|----------|----------|
+| RFC 7519 (JWT) | 19-auth-providers, 35-session-revocation, consumer-api-spec |
+| RFC 7517 (JWK) | 19-auth-providers |
+| RFC 7662 (Token Introspection) | 35-session-revocation, consumer-api-spec |
+| RFC 7643/7644 (SCIM 2.0) | 19-auth-providers |
+| RFC 8446 (TLS 1.3) | 14-policy-profiles, 26-accreditation, 31-credential-provider, 36-internal-component-auth |
+| RFC 5280 (X.509/CRL) | 31-credential-provider, 36-internal-component-auth |
+| RFC 6960 (OCSP) | 36-internal-component-auth |
+| RFC 7030 (EST) | 31-credential-provider |
+| RFC 8555 (ACME) | 31-credential-provider |
+| RFC 8894 (SCEP) | 31-credential-provider |
+| RFC 4210 (CMP) | 31-credential-provider |
+| RFC 8594 (Sunset) | 34-api-versioning-strategy, consumer-api-spec |
+| RFC 9745 (Deprecation) | 34-api-versioning-strategy, consumer-api-spec |
+| RFC 8615 (Well-Known URIs) | 34-api-versioning-strategy, 39-dcm-self-health, dcm-operator-interface-spec |
+| RFC 8259 (JSON) | All specifications |
+| ISO 8601 (timestamps) | All documents |
+| OIDC / OpenID Connect | 19-auth-providers, consumer-api-spec |
+| LDAP (RFC 4511) | 19-auth-providers |
+| FIPS 140 | 14-policy-profiles, 31-credential-provider, 36-internal-component-auth |
+| NIST SP 800-63B (AAL) | 31-credential-provider |
+| NIST SP 800-53 | 14-policy-profiles |
+| HIPAA | 14-policy-profiles, 31-credential-provider |
+| PCI DSS | 14-policy-profiles, 31-credential-provider |
+| FedRAMP | 14-policy-profiles |
+| GDPR | 08-resource-grouping, 22-dcm-federation |
+| ISO 27001 | 14-policy-profiles, 26-accreditation |
+| Kubernetes | kubernetes-compatibility, dcm-operator-sdk-api |
+| OPA | dcm-opa-integration-spec |
+| Prometheus | 12-audit-provenance, 39-dcm-self-health |
+| OpenTelemetry | 12-audit-provenance |
+| Istio | 17-deployment-redundancy, 36-internal-component-auth |
+| GitOps/OpenGitOps | 00-context-and-purpose, 20-registry-governance, 28-federated-contribution |
+| W3C SSE (Server-Sent Events) | consumer-api-spec, 39-dcm-self-health |
+| OpenAPI 3.1 | dcm-operator-interface-spec, consumer-api-spec, dcm-admin-api-spec |
+| SPIFFE (conceptual) | 36-internal-component-auth |
+| HashiCorp Vault PKI | 31-credential-provider-model, 36-internal-component-auth |
+| RFC 7009 (Token Revocation) | 35-session-revocation |
+| NIST SP 800-63B (AAL) | 31-credential-provider-model, 14-policy-profiles |
+| SCH policies (scheduling) | 37-scheduled-requests |
+| RDG policies (dependency graph) | 38-request-dependency-graph |
+| HLT policies (self-health) | 39-dcm-self-health |
+| SES policies (session revocation) | 35-session-revocation |
+| ICOM policies (internal component auth) | 36-internal-component-auth |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+---
+
+## 10. ITSM Integration Standards
+
+These standards and protocols are used by ITSM integration implementations:
+
+| Standard / Protocol | Use in DCM ITSM Integration | Obligation |
+|--------------------|-----------------------------|------------|
+| **ServiceNow REST Table API** | Primary integration for ServiceNow; create/read/update change_request, incident, cmdb_ci tables | Normative for ServiceNow provider |
+| **Jira REST API v3** | Primary integration for Jira Service Management; issue create/update/transition | Normative for Jira provider |
+| **BMC AR REST API v1** | Integration for BMC Remedy/Helix ITSM; form-based create/update | Normative for BMC provider |
+| **PagerDuty Events API v2** | Incident creation and update for alert-type integrations | Normative for PagerDuty provider |
+| **HMAC-SHA256** | Inbound webhook signature verification for all ITSM systems; shared secret via credential management service | Normative |
+| **ITIL v4 Change Management** | Conceptual framework for DCM change record lifecycle mapping (Normal, Standard, Emergency change types) | Informative |
+| **JSON:API** | Used by several ITSM REST APIs for response formatting | Informative |
+| **JSONPath** | Template expression resolution for `generic_rest` action payloads; response field extraction | Normative for generic_rest |
+
+## 9. Policy Family to Standard Mapping
+
+Each DCM system policy family maps to one or more industry standards. This table supports compliance traceability.
+
+| Policy Family | Standards Basis | Key Policies |
+|--------------|----------------|-------------|
+| **AUTH-001–015** | RFC 6749, RFC 7519, OIDC Core, NIST SP 800-63B, RFC 7643/7644 | Auth Provider lifecycle; session TTL; MFA enforcement; SCIM provisioning |
+| **AUTH-016–022** | RFC 7662, RFC 6749 spirit, OAuth 2.0 best practices | Session revocation; token introspection; refresh token invalidation |
+| **CPX-001–012** | FIPS 140-2/3, RFC 5280, RFC 8555/7030/8894/4210, NIST SP 800-57 | Credential never stored; rotation; revocation; algorithm baseline |
+| **ATM-001–012** | ISO 27001 change management; organizational governance practices | Authority tier ordering; security degradation gate; profile gap detection |
+| **EVT-001–007** | OpenTelemetry, CNCF event-driven best practices | Event envelope; idempotency; non-suppressable audit events |
+| **VER-001–009** | RFC 8594, RFC 9745, industry API lifecycle practices | Breaking change definition; deprecation headers; migration guides |
+| **SES-001–005** | RFC 7662, RFC 7009, OAuth 2.0 security best practices | Session lifecycle; concurrent limits; emergency revocation |
+| **ICOM-001–009** | RFC 8446, RFC 5280, SPIFFE conceptual model, FIPS 140 | mTLS; component identity; Internal CA; bootstrap; certificate revocation |
+| **SCH-001–006** | Industry job scheduling practices; dual-evaluation pattern | Scheduled request dual policy evaluation; deadline enforcement |
+| **RDG-001–006** | DAG-based workflow ordering; dependency injection patterns | Circular dependency rejection; quota at group submission; field injection |
+| **HLT-001–006** | RFC 8615, Kubernetes probe conventions, Prometheus OpenMetrics | Liveness/readiness; unauthenticated probes; profile-governed metrics exposure |
+| **DPO-001–006** | Design-by-contract; security-first architecture principles | Design priority order; security as Priority 1 |
+| **ZTS-001–005** | Zero Trust Architecture (NIST SP 800-207); NIST SP 800-63B | Five-check boundary model; mTLS; scoped interaction credentials |
+| **MPX-001–008** | Service mesh composition patterns; dependency graph execution | compound service definition constituent orchestration; compensation |
+| **SMX-001–010** | Risk scoring; NIST RMF; organizational risk tolerance | Hybrid scoring; approval routing; enforcement class |
+| **FCM-001–008** | GitOps contribution model; CNCF governance practices | Federated policy contribution; shadow validation; trust levels |
+| **GMX-001–006** | Governance Matrix; policy-as-code; organizational controls | Cross-domain policy enforcement; data classification |
+| **ITSM-001–007 + ITSM-POL-001–004** | ITIL v4, ServiceNow/Jira/Remedy REST APIs, HMAC-SHA256, ITIL change management | ITSM integration registration; inbound webhook auth; ITSM Policy evaluation; blocking gate with timeout guarantee |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
\ No newline at end of file
diff --git a/content/docs/architecture/data-model/41-operational-reference.md b/content/docs/architecture/data-model/41-operational-reference.md
new file mode 100644
index 0000000..251187a
--- /dev/null
+++ b/content/docs/architecture/data-model/41-operational-reference.md
@@ -0,0 +1,521 @@
+# DCM Data Model — Operational Reference
+
+**Document Status:** 🔄 In Progress
+**Document Type:** SRE Reference — GitOps Scale, Store Migration, Disaster Recovery
+**Related Documents:** [data stores](11-storage-providers.md) | [Deployment and Redundancy](17-deployment-redundancy.md) | [Four States Model](02-four-states.md) | [Internal Component Authentication](36-internal-component-auth.md) | [DCM Self-Health Endpoints](39-dcm-self-health.md)
+
+> **Audience:** Platform engineers and SREs operating DCM in production. This document covers three operational concerns that require guidance beyond the architectural specifications: GitOps store partitioning at large scale, migrating between store implementations, and recovering from failure scenarios.
+
+---
+
+## 1. DCM database Scale and Partitioning
+
+### 1.1 When a Repo Becomes Too Large
+
+The default DCM GitOps layout uses one repository per store type (Intent, Requested, Layer, Policy). For most deployments this is correct. At large scale — tens of thousands of active entities or hundreds of active tenants — a single repository can exhibit:
+
+- Git operation latency (clone, fetch, log) growing beyond SLA
+- CI/CD pipeline fan-out delays as every write triggers the full repository
+- Access control granularity limits (all tenants share one repo)
+- Search index sync lag from large diffs
+
+**Thresholds that suggest partitioning:**
+
+| Signal | Threshold | Recommended action |
+|--------|-----------|-------------------|
+| Entities in Intent/Requested store | > 50,000 active | Consider tenant-shard partitioning |
+| Git clone time | > PT30S | Add shallow-clone depth; consider partitioning |
+| PR merge latency | > PT5M | Partition or add write buffer |
+| Tenant count | > 500 | Consider per-tenant repositories |
+| Repository size on disk | > 10 GB | Partition |
+
+These are guidelines, not hard limits. Profile, hardware, and Git host performance all affect the actual inflection point.
+
+### 1.2 Partitioning Strategies
+
+DCM supports three partitioning strategies. All are compatible with the storage contract — partitioning changes how stores are organized, not what the store contract requires.
+
+#### Strategy A — Tenant Shard Partitioning (recommended for most)
+
+Split each store type into N shard repositories, with tenants assigned to shards by a deterministic hash of `tenant_uuid`:
+
+```
+shard = hash(tenant_uuid) % N
+
+dcm-intent-shard-0/     ← tenants whose hash(uuid) % N == 0
+dcm-intent-shard-1/     ← tenants whose hash(uuid) % N == 1
+  tenants/
+    {tenant-uuid}/
+      requests/
+        {request-uuid}/
+          intent.yaml
+```
+
+**DCM configuration:**
+```yaml
+gitops_store:
+  intent_store:
+    partitioning: tenant_shard
+    shard_count: 8
+    shard_routing: hash_mod    # deterministic; no routing table needed
+    repositories:
+      - shard: 0
+        url: https://git.corp/dcm/dcm-intent-shard-0
+      - shard: 1
+        url: https://git.corp/dcm/dcm-intent-shard-1
+      # ...
+```
+
+**Operational implications:** Adding shards requires re-hashing. Plan shard counts for 3–5 years of expected growth; use a power of 2 to simplify future doubling.
+
+#### Strategy B — Per-Tenant Repositories (for strict isolation)
+
+Each tenant has its own set of store repositories. Used when:
+- Tenants are separate organizations (MSP model)
+- Compliance requires complete data isolation per tenant
+- Different retention policies per tenant
+
+```
+dcm-intent-{tenant-uuid}/    ← one repository per tenant
+  requests/
+    {request-uuid}/
+      intent.yaml
+```
+
+**Operational implications:** Repository count scales with tenant count. Requires automation for tenant onboarding (repository creation, access provisioning). Git host must support large numbers of repositories.
+
+#### Strategy C — Time-Based Archiving (for retention management)
+
+Active entities stay in the primary repository. Entities past a declared age threshold are archived to read-only archive repositories:
+
+```
+dcm-intent-active/           ← current entities (hot)
+dcm-intent-archive-2025/     ← entities from 2025 (cold, read-only)
+dcm-intent-archive-2024/     ← entities from 2024 (cold, read-only)
+```
+
+DCM's audit and search components are configured with both active and archive repository lists. Strategy C is typically combined with Strategy A or B.
+
+### 1.3 Large-Scale Layer Store Partitioning
+
+The Layer Store grows more slowly than the Intent/Requested stores (layers are reused across requests). Layer Store partitioning is by domain rather than by tenant:
+
+```
+dcm-layers-compute/        ← Compute.* resource type layers
+dcm-layers-network/        ← Network.* resource type layers
+dcm-layers-storage/        ← Storage.* resource type layers
+dcm-layers-platform/       ← Platform.* and cross-cutting layers
+dcm-policies-core/         ← System and core policies
+dcm-policies-tenant/       ← Tenant-contributed policies (per tenant or sharded)
+```
+
+Domain-partitioned Layer stores are configured in DCM's layer assembly engine:
+
+```yaml
+layer_store:
+  repositories:
+    - domain: Compute.*
+      url: https://git.corp/dcm/dcm-layers-compute
+      priority: provider_contribution  # provider contributions go here
+    - domain: Network.*
+      url: https://git.corp/dcm/dcm-layers-network
+    - domain: "*"                       # catch-all for uncategorized
+      url: https://git.corp/dcm/dcm-layers-platform
+```
+
+### 1.4 Shallow Clones and Read-Only Mirrors
+
+For read-heavy operations (audit, search index rebuild, drift reconciliation) that do not need full Git history:
+
+```yaml
+gitops_store:
+  read_operations:
+    clone_depth: 1           # shallow clone for read-only consumers
+    use_mirror: true         # read from read-only mirror; writes go to primary
+    mirror_url: https://git-mirror.corp/dcm/
+    mirror_sync_lag_max: PT5M  # alert if mirror is more than 5 minutes behind
+```
+
+---
+
+## 2. Store Migration
+
+### 2.1 Migration Principles
+
+DCM store migrations follow three invariants:
+
+1. **No data loss** — every record in the source store must exist in the target store after migration
+2. **Audit chain continuity** — the audit hash chain must be unbroken across the migration; audit records written before and after must chain correctly
+3. **Read availability during migration** — DCM continues serving read requests throughout; write availability may be briefly paused during cutover
+
+### 2.2 Migration Playbook Structure
+
+Every store migration follows this pattern regardless of source or target implementation:
+
+```
+Phase 1 — Prepare
+  │  Provision target store alongside source
+  │  Validate target store meets storage contract (health check, write test, read test)
+  │  Configure DCM to write to BOTH source and target (dual-write mode)
+  │
+Phase 2 — Backfill
+  │  Export all existing records from source
+  │  Import records to target in chronological order (preserving provenance timestamps)
+  │  Verify record counts match; spot-check content hashes
+  │
+Phase 3 — Validate
+  │  Run DCM's store validation suite against target
+  │  Verify audit chain integrity on target store
+  │  Verify search index can be rebuilt from target store
+  │
+Phase 4 — Cutover
+  │  Brief write pause (PT30S–PT5M depending on profile)
+  │  Disable dual-write; switch DCM to target as primary
+  │  Verify /readyz returns healthy
+  │  Resume writes to target only
+  │
+Phase 5 — Decommission source (after burn-in period)
+     Default burn-in: P30D (standard), P90D (fsi/sovereign)
+     Keep source in read-only mode during burn-in for rollback
+```
+
+### 2.3 Common Migration Paths
+
+#### SQLite → PostgreSQL (minimal/dev → standard)
+
+Typical trigger: scaling beyond single-node evaluation environment.
+
+```bash
+# Step 1: Export from SQLite
+dcm-admin store export \
+  --store realized \
+  --format jsonl \
+  --output realized-export.jsonl
+
+# Step 2: Import to PostgreSQL
+dcm-admin store import \
+  --store realized \
+  --source realized-export.jsonl \
+  --target postgres://pg-host:5432/dcm_realized \
+  --validate-chain
+
+# Step 3: Enable dual-write
+dcm-admin store dual-write enable \
+  --store realized \
+  --primary sqlite://dcm-realized.db \
+  --secondary postgres://pg-host:5432/dcm_realized
+
+# Step 4: Validate
+dcm-admin store validate \
+  --store realized \
+  --target postgres://pg-host:5432/dcm_realized \
+  --check-count --check-chain --check-spot-sample 0.05
+
+# Step 5: Cutover
+dcm-admin store cutover \
+  --store realized \
+  --target postgres://pg-host:5432/dcm_realized
+```
+
+#### PostgreSQL single-instance → CockroachDB / PostgreSQL HA
+
+Typical trigger: HA requirement for production; multi-region deployment.
+
+**Key difference from SQLite → PostgreSQL:** CockroachDB uses serializable isolation and distributed transactions. Test write throughput under realistic load before cutover — CockroachDB's latency profile differs from single-node PostgreSQL.
+
+```yaml
+# Pre-migration checklist
+migration_checklist:
+  - Load test target under realistic DCM write volume (PT4H minimum)
+  - Verify CockroachDB schema compatibility (DCM uses standard PostgreSQL wire protocol)
+  - Configure connection pooler (PgBouncer or similar) — CockroachDB default connection count
+  - Verify time synchronization (CockroachDB requires NTP within PT500MS across nodes)
+  - Test audit chain write under partition scenario
+```
+
+#### DCM database — Repo Restructuring
+
+Restructuring a GitOps repository (e.g. monorepo to sharded) requires special handling because Git history must be preserved.
+
+```
+Step 1: Enable write buffer — all new writes queue while migration proceeds
+Step 2: git filter-repo or git subtree to extract tenant directories to shard repos
+Step 3: Validate file counts and content hashes in each shard
+Step 4: Update DCM gitops_store configuration to point to shards
+Step 5: Drain write buffer — queued writes replay to new shard repos
+Step 6: Verify search index rebuild from shards
+Step 7: Archive or delete monorepo after burn-in period
+```
+
+### 2.4 Rollback Procedure
+
+If migration fails before cutover: disable dual-write, discard target, no impact to production.
+
+If migration fails after cutover (during burn-in):
+
+```
+1. Alert: /readyz reports degraded or source store discrepancy detected
+2. dcm-admin store rollback --store  --to source
+   (requires source still in read-only mode — NOT decommissioned)
+3. DCM restarts reads/writes from source
+4. Export any writes that reached target but not source (if any, during dual-write gap)
+5. Import gap records to source
+6. Re-enable source as primary
+```
+
+**This is why burn-in period exists.** Do not decommission source stores until burn-in completes.
+
+### 2.5 Profile-Governed Migration Constraints
+
+| Profile | Min dual-write duration | Max cutover pause | Burn-in period |
+|---------|------------------------|-------------------|----------------|
+| `minimal` | P1D | PT5M | P7D |
+| `dev` | P3D | PT5M | P14D |
+| `standard` | P7D | PT2M | P30D |
+| `prod` | P14D | PT1M | P30D |
+| `fsi` | P30D | PT30S | P90D |
+| `sovereign` | P60D | PT30S | P90D |
+
+---
+
+## 3. Disaster Recovery Runbook
+
+### 3.1 DCM Recovery Architecture
+
+DCM's recovery model is built on a key property: **all durable state is in the stores, not in the control plane.** Control plane components (Policy Engine, Request Orchestrator, etc.) are stateless and can be restarted without data loss. Recovery from most failures is component restart, not data restoration.
+
+The five DCM stores and their recovery characteristics:
+
+| Store | Implementation | Data durability | Recovery method |
+|-------|---------------|----------------|-----------------|
+| Intent Store | GitOps (Git) | Git replication + remote | Re-clone from remote |
+| Requested Store | GitOps or write-once | Git replication / DB replication | Re-clone or DB restore |
+| Layer Store | GitOps (Git) | Git replication + remote | Re-clone from remote |
+| Realized Store | Write-once (PostgreSQL/CockroachDB) | DB replication / WAL | DB failover or restore |
+| Audit Store | Append-only (Kafka/PostgreSQL) | Replication / WAL | Kafka failover or restore |
+
+### 3.2 Recovery Scenarios and Procedures
+
+#### Scenario 1: Single Component Failure (Most Common)
+
+**Symptoms:** One DCM component (e.g. Policy Engine) is unhealthy. `/readyz` shows degraded. Requests may be delayed but not lost.
+
+**RTO:** PT5M  
+**RPO:** 0 (no data loss — components are stateless)
+
+```
+1. Identify failing component via GET /api/v1/admin/health
+2. Check component logs for panic/OOM/deadlock
+3. Kubernetes: pod restart is automatic (liveness probe)
+   Manual: kubectl rollout restart deployment/dcm-policy-engine
+4. Monitor /readyz — should recover within PT2M of pod restart
+5. If component repeatedly fails: check Internal CA cert expiry (ICOM-006)
+   dcm-admin component cert-status --component policy-engine
+6. Write post-incident note to DCM audit store
+```
+
+#### Scenario 2: Store Failure (Database / Kafka)
+
+**Symptoms:** `/readyz` fails specific store check. Requests queue or fail depending on which store.
+
+**Realized Store failure (highest severity — blocks realization):**
+
+```
+RTO target: PT30M (standard), PT15M (prod), PT5M (fsi/sovereign)
+RPO: 0 for PostgreSQL HA (synchronous replication); near-zero for async
+
+1. Confirm store failure: GET /api/v1/admin/health → realized_store: fail
+2. If HA: check if automatic failover occurred
+   kubectl get pods -n dcm-stores | grep postgres
+   Check PostgreSQL replication lag / CockroachDB node status
+3. Manual failover if automatic did not trigger:
+   dcm-admin store failover --store realized --target replica-2
+4. Verify replication caught up: dcm-admin store lag --store realized
+5. Verify /readyz recovers
+6. Root cause analysis: WAL lag, disk full, network partition
+```
+
+**Audit Store failure:**
+
+```
+RTO: PT1H acceptable (audit trail can tolerate temporary buffering)
+RPO: profile-governed — see Audit Store write buffer policy
+
+1. DCM buffers audit records locally (Commit Log) during store outage
+   Write buffer capacity: profile-governed (PT1H standard, PT15M sovereign)
+2. Restore Kafka cluster from replica or snapshot
+3. DCM drains buffer to restored store automatically on reconnection
+4. Verify chain integrity: dcm-admin audit chain-verify --since 
+```
+
+**DCM database failure (Intent/Requested/Layer):**
+
+```
+RTO: PT30M (stores are remountable from Git remote)
+RPO: 0 (all writes go to Git remote; loss only if remote is also lost)
+
+1. Git remote unreachable: check network connectivity
+2. If Git host is down: DCM switches to cached/buffered mode
+   New requests queue in write buffer; reads served from local clone
+3. Write buffer capacity: PT4H (standard) — configure per deployment
+4. When Git host recovers: buffer drains automatically
+5. Force drain: dcm-admin store drain-buffer --store intent
+```
+
+#### Scenario 3: Full Control Plane Loss
+
+**Symptoms:** All DCM pods down. Stores intact. Users cannot submit requests.
+
+**RTO:** PT15M (kubernetes deployment restart)  
+**RPO:** 0 (stores are external — no data in pods)
+
+```
+1. Verify stores are healthy (connect directly):
+   dcm-admin store health-check --all --direct
+
+2. Verify Internal CA is available:
+   curl -k https://dcm-internal-ca.dcm-system.svc.cluster.local/health
+
+3. Restart DCM deployment (Kubernetes):
+   kubectl rollout restart deployment -n dcm-system
+
+4. Monitor /readyz — startup sequence should complete within PT3M:
+   watch -n 5 kubectl get pods -n dcm-system
+
+5. Verify session store recovers:
+   GET /api/v1/admin/health → session_store: pass
+
+6. Alert consumers: any in-flight requests at time of failure
+   are in ACKNOWLEDGED/DISPATCHED state and may need status check
+   dcm-admin requests find --status in-flight --since 
+```
+
+#### Scenario 4: Partial Region Loss (Multi-Region Deployments)
+
+**Symptoms:** One region's DCM instance degraded. Other regions operational.
+
+```
+1. DCM federation routes requests away from degraded region (automatic)
+   Verify: GET /api/v1/admin/health → federation peer status
+
+2. If region is sovereign-scoped (data must not leave): 
+   Alert: sovereignty.migration_required event fires
+   Consumers in that region may be blocked until region recovers
+
+3. For non-sovereign regions: traffic reroutes automatically
+   Monitor: dcm_requests_total{region} for traffic shift
+
+4. Region recovery: standard Scenario 3 procedure
+   After recovery: drift detection validates recovered state
+```
+
+#### Scenario 5: Complete Loss (Repave)
+
+The nuclear scenario: entire DCM installation destroyed. Git remote intact.
+
+**RTO:** PT4H–PT24H (depends on infrastructure provisioning speed)  
+**RPO:** 0 for GitOps stores; near-zero for Realized/Audit stores
+
+```
+1. Provision new Kubernetes cluster (or equivalent)
+
+2. Deploy DCM bootstrap installer:
+   helm install dcm-bootstrap dcm/dcm-bootstrap \
+     --set gitops.manifest_url=https://git.corp/dcm/dcm-deployment \
+     --set gitops.manifest_ref=
+
+3. DCM bootstrap reads dcm_deployment manifest from Git
+   Provisions itself: control plane, Internal CA, stores
+
+4. Restore Realized Store from backup:
+   dcm-admin store restore --store realized \
+     --from s3://dcm-backups/realized/latest \
+     --validate-chain
+
+5. Restore Audit Store from backup or Kafka snapshot:
+   dcm-admin store restore --store audit \
+     --from s3://dcm-backups/audit/latest \
+     --chain-verify
+
+6. Intent/Requested/Layer stores: re-clone from Git remote (already current)
+
+7. DCM rehydrates managed resources in dependency order:
+   dcm-admin rehydrate --all-tenants --dry-run  # verify plan first
+   dcm-admin rehydrate --all-tenants
+
+8. Drift detection validates recovered state matches declared state:
+   dcm-admin drift scan --all --post-recovery
+
+9. Re-issue Internal CA certificates for all components:
+   (handled automatically by bootstrap — components acquire new certs)
+
+10. Notify consumers: recovery complete; request status available
+```
+
+### 3.3 Recovery Time Objectives by Profile
+
+| Profile | Scenario 1 (component) | Scenario 2 (store) | Scenario 3 (full CP) | Scenario 5 (repave) |
+|---------|------------------------|--------------------|-----------------------|---------------------|
+| `minimal` | PT15M | PT2H | PT30M | PT24H |
+| `standard` | PT5M | PT30M | PT15M | PT8H |
+| `prod` | PT2M | PT15M | PT10M | PT4H |
+| `fsi` | PT2M | PT5M | PT5M | PT2H |
+| `sovereign` | PT1M | PT5M | PT5M | PT2H |
+
+### 3.4 Recovery Point Objectives
+
+| Store | Standard RPO | fsi/sovereign RPO | Notes |
+|-------|-------------|------------------|-------|
+| Intent Store | 0 | 0 | Git remote is source of truth |
+| Requested Store | 0 | 0 | Write-once; replicated |
+| Layer Store | 0 | 0 | Git remote is source of truth |
+| Realized Store | PT5M | PT1M | Async replication lag |
+| Audit Store | PT15M | PT1M | Kafka replication + write buffer |
+
+### 3.5 Backup Schedule
+
+DCM does not manage backups of infrastructure stores directly — that responsibility belongs to the storage platform. Recommended schedules by store:
+
+| Store | Backup method | Frequency | Retention |
+|-------|--------------|-----------|-----------|
+| Intent / Requested / Layer | Git push to offsite remote | Continuous | Per Git host policy |
+| Realized Store | PostgreSQL PITR + daily snapshot | Continuous WAL + P1D snapshot | P90D (standard), P365D (fsi/sovereign) |
+| Audit Store | Kafka topic snapshot | P4H | P365D (all profiles — regulatory minimum) |
+| Internal CA | Key material backup to HSM/Vault | On change | P7Y (key material outlives certs) |
+
+### 3.6 Post-Recovery Validation Checklist
+
+Run after any Scenario 3+ recovery:
+
+```
+□ /livez returns pass on all control plane pods
+□ /readyz returns pass (all 5 core dependencies green)
+□ GET /api/v1/admin/health shows all components pass
+□ dcm-admin audit chain-verify --full returns no broken links
+□ dcm-admin store validate --all returns no discrepancies
+□ dcm-admin drift scan --all returns no unexpected drift
+□ Internal CA certificates valid for all components (ICOM-006)
+□ At least one Auth Provider healthy (GET /api/v1/admin/health → auth_providers)
+□ Search index rebuild complete (if search index store was affected)
+□ Session Store empty (expected — all sessions expired during outage; users re-authenticate)
+□ Write post-incident note to audit store with recovery timeline
+□ Notify consumers of recovery completion
+```
+
+---
+
+## 4. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `OPS-001` | GitOps store partitioning strategy must be declared in the DCM deployment manifest. Changes to partitioning strategy require dual-write migration procedure (Section 2). |
+| `OPS-002` | Store migrations must maintain audit chain continuity across cutover. Audit records written to the source store before cutover and to the target store after cutover must form an unbroken chain. |
+| `OPS-003` | Source stores must remain accessible in read-only mode for the profile-governed burn-in period after cutover. Source stores must not be decommissioned until the burn-in period completes and rollback is confirmed unnecessary. |
+| `OPS-004` | Recovery from Scenario 3 (full control plane loss) must complete within the profile-governed RTO. If RTO cannot be met, the incident must be escalated and root cause must address the recovery path. |
+| `OPS-005` | The post-recovery validation checklist (Section 3.6) must be completed and its results written to the audit store before declaring an incident resolved. |
+| `OPS-006` | Audit Store backups must be retained for a minimum of P365D in all profiles, regardless of other data retention policies, to satisfy regulatory audit trail requirements. |
+| `OPS-007` | Git remote repositories serving as GitOps stores must be configured with push access from at least two geographically separated locations to prevent single-point-of-failure data loss. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/42-itsm-integration.md b/content/docs/architecture/data-model/42-itsm-integration.md
new file mode 100644
index 0000000..c9aa1fd
--- /dev/null
+++ b/content/docs/architecture/data-model/42-itsm-integration.md
@@ -0,0 +1,666 @@
+# DCM Data Model — ITSM Integration
+
+**Document Status:** 🔄 In Progress
+**Document Type:** Architecture Reference — ITSM integration Type and ITSM Policy Type
+**Related Documents:** [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) | [Notification Model](23-notification-model.md) | [Event Catalog](33-event-catalog.md) | [Authority Tier Model](32-authority-tier-model.md) | [Consumer API Specification](../specifications/consumer-api-spec.md)
+
+> **Design principle:** DCM is built to *replace* the infrastructure ticket as the primary provisioning mechanism. ITSM integration is additive — it enriches DCM entities with ITSM metadata, enables ITSM-initiated requests, and provides bidirectional lifecycle traceability for organizations that need it for compliance. **DCM never requires an ITSM system to function.**
+>
+> Two new additions to the DCM architecture:
+> 1. **ITSM integration** — a new Provider type (12th) that speaks ITSM system APIs bidirectionally
+> 2. **ITSM Policy** — a new Policy output type (8th) that triggers ITSM actions as a side-effect of DCM pipeline events
+
+---
+
+## 1. ITSM Integration
+
+### 1.1 What ITSM Integration Is
+
+ITSM integration connects DCM to an external IT Service Management system. It handles:
+
+- **Outbound**: DCM lifecycle events → ITSM records (create change requests, update CMDB CIs, close incidents, link tickets to entities)
+- **Inbound**: ITSM approvals and decisions → DCM (change approval recorded via approval vote API, request initiation from ITSM workflow)
+- **Sync**: ITSM record references stored on DCM entities as business data (bidirectional link)
+
+ITSM integration is **not** a separate provider type (it doesn't realize resources), **not** a notification service (though it may create notification-like records), and **not** a External Policy Evaluator (though ITSM approval status may inform DCM policies). It is its own type because it has a bidirectional contract, manages external record lifecycle, and requires specific capability declarations around ITSM system connectivity.
+
+### 1.2 Data Flow
+
+```
+DCM lifecycle event fires (e.g. request.dispatched)
+  │
+  ▼ ITSM Policy evaluates (see Section 3)
+  │   Determines: should an ITSM action fire? Which action?
+  │
+  ▼ ITSM integration receives action request
+  │   Translates to target system's API format
+  │   Calls ITSM system (ServiceNow, Jira, etc.)
+  │
+  ▼ ITSM system creates/updates record
+  │   Returns record ID (CHG0012345, INC-4821, etc.)
+  │
+  ▼ ITSM integration stores reference on DCM entity
+  │   entity.business_data.itsm_references[] updated
+  │
+  ▼ ITSM integration reports back to DCM
+      itsm_reference_created event published
+      External record ID in audit record
+
+─────────────────────────────────────────────
+
+ITSM system approves a change record
+  │
+  ▼ ITSM system calls DCM API (via webhook or polling)
+  │   POST /api/v1/admin/approvals/{uuid}:vote
+  │   { decision: "approve", recorded_via: "servicenow",
+  │     external_reference: "CHG0012345" }
+  │
+  ▼ DCM records approval vote
+  │   approval.decision_recorded event
+  │
+  ▼ Pipeline resumes if quorum/tier satisfied
+```
+
+### 1.3 Capability Declaration
+
+```yaml
+itsm_provider_capabilities:
+  itsm_system: servicenow | jira_service_management | bmc_remedy | bmc_helix |
+               freshservice | zendesk | pagerduty | opsgenie | manageengine |
+               cherwell | topdesk | generic_rest
+  
+  # What this provider can do
+  supported_actions:
+    - create_change_request      # create a change record for DCM provisioning events
+    - update_change_request      # update change record on state transitions
+    - close_change_request       # close change record on realization/failure
+    - create_incident            # create incident for failures, drift, security events
+    - update_incident            # update incident on resolution
+    - close_incident             # close incident on recovery
+    - update_cmdb_ci             # update CMDB configuration item record
+    - create_cmdb_ci             # create new CMDB CI for realized entities
+    - retire_cmdb_ci             # retire CMDB CI on decommission
+    - create_service_request     # create service request record
+    - link_parent_record         # link DCM entity to existing ITSM record
+    - inbound_approval           # accept approval decisions from ITSM system
+    - inbound_request_initiation # allow ITSM workflows to submit DCM requests
+  
+  # System connectivity
+  endpoint_url:             # ITSM system API base URL
+  api_version:           # system-specific API version
+  auth_credential_uuid:    # references credential management service
+  
+  # Bidirectional webhook (for inbound)
+  inbound_webhook:
+    enabled: 
+    secret_credential_uuid:    # HMAC secret for webhook verification
+    
+  # Field mappings (system-specific)
+  field_mapping_ref:   # path to field mapping YAML in Layer Store
+  
+  # CMDB CI type mapping
+  cmdb_ci_type_map:
+    - dcm_resource_type: Compute.VirtualMachine
+      itsm_ci_type: cmdb_ci_server        # ServiceNow CI class
+    - dcm_resource_type: Network.VLAN
+      itsm_ci_type: cmdb_ci_network_gear
+    - dcm_resource_type: Storage.Volume
+      itsm_ci_type: cmdb_ci_storage_device
+```
+
+### 1.4 Required API Endpoints (ITSM integration implements)
+
+```
+POST {provider_base}/actions              # DCM submits action requests
+GET  {provider_base}/actions/{action_id}  # DCM checks action status
+GET  {provider_base}/records/{record_id}  # DCM retrieves record status
+POST {provider_base}/inbound             # ITSM system sends inbound events
+GET  /health                             # standard OIS health check
+```
+
+### 1.5 DCM Entity ITSM References
+
+Realized entities gain an `itsm_references` block in business data:
+
+```yaml
+itsm_references:
+  - system: servicenow
+    provider_uuid: 
+    record_type: change_request
+    record_id: "CHG0012345"
+    record_url: "https://corp.service-now.com/nav_to.do?uri=change_request.do?sys_id=..."
+    created_at: 
+    status: approved        # DCM's view of the record status
+    last_synced_at: 
+    
+  - system: jira_service_management
+    provider_uuid: 
+    record_type: incident
+    record_id: "INC-4821"
+    record_url: "https://corp.atlassian.net/browse/INC-4821"
+    created_at: 
+    status: open
+    last_synced_at: 
+```
+
+---
+
+## 2. Supported ITSM Systems
+
+### 2.1 ServiceNow
+
+**API:** REST Table API (`/api/now/table/`), Business Rule webhooks, Flow Designer
+
+```yaml
+# ServiceNow ITSM integration registration
+itsm_provider_registration:
+  provider_handle: "servicenow-prod"
+  itsm_system: servicenow
+  endpoint_url: "https://corp.service-now.com"
+  api_version: "v2"
+  auth_credential_uuid:     # api_key or oauth2 credential
+  
+  supported_actions:
+    - create_change_request        # → change_request table
+    - update_change_request
+    - close_change_request
+    - create_incident              # → incident table
+    - update_cmdb_ci               # → cmdb_ci_server (or mapped class)
+    - create_cmdb_ci
+    - retire_cmdb_ci
+    - inbound_approval             # Change Advisory Board approval → DCM vote
+    
+  # ServiceNow-specific field mapping
+  change_request_template:
+    assignment_group: "Infrastructure Automation"
+    category: "Software"
+    risk: "2"                      # Low
+    impact: "3"                    # Low
+    # DCM fields injected at runtime:
+    short_description: "DCM: Provision {resource_type} '{entity_handle}'"
+    description: "Requested by: {actor_handle}\nTenant: {tenant_handle}\nDCM Request: {request_uuid}"
+    
+  # CAB approval → DCM vote mapping
+  inbound_approval:
+    webhook_url: "https://dcm.corp/api/v1/admin/approvals/{approval_uuid}:vote"
+    trigger_on: "change_request.state → 'Approved'"
+    decision_field: "state"
+    decision_map:
+      "Approved": "approve"
+      "Rejected": "reject"
+      "Cancelled": "reject"
+    external_reference_field: "number"    # → CHG0012345
+    
+  cmdb_ci_type_map:
+    - dcm_resource_type: Compute.VirtualMachine
+      itsm_ci_type: cmdb_ci_server
+    - dcm_resource_type: Network.VLAN
+      itsm_ci_type: cmdb_ci_netgear
+    - dcm_resource_type: Storage.Volume
+      itsm_ci_type: cmdb_ci_disk
+    - dcm_resource_type: Kubernetes.Cluster
+      itsm_ci_type: cmdb_ci_kubernetes_cluster
+```
+
+**Inbound: CAB approval flow**
+
+```
+ServiceNow Change Advisory Board approves CHG0012345
+  │
+  ▼ ServiceNow Business Rule fires on state change → "Approved"
+  │   Calls DCM webhook: POST /api/v1/admin/approvals/{uuid}:vote
+  │   Headers: X-ServiceNow-Signature: 
+  │   Body: { decision: "approve", recorded_via: "servicenow",
+  │           external_reference: "CHG0012345" }
+  │
+  ▼ DCM verifies HMAC signature against secret_credential_uuid
+  │   Records approval vote
+  │   Pipeline resumes if tier satisfied
+```
+
+### 2.2 Jira Service Management (Atlassian)
+
+**API:** REST API v3, Atlassian Connect webhooks, Automation rules
+
+```yaml
+itsm_provider_registration:
+  provider_handle: "jira-service-mgmt-prod"
+  itsm_system: jira_service_management
+  endpoint_url: "https://corp.atlassian.net"
+  api_version: "3"
+  auth_credential_uuid:     # API token or OAuth2
+  
+  supported_actions:
+    - create_change_request        # → Jira issue (Change type)
+    - update_change_request
+    - close_change_request
+    - create_incident              # → Jira issue (Incident type)
+    - create_service_request       # → Jira issue (Service Request type)
+    - inbound_approval             # Jira Change approval → DCM vote
+    
+  change_request_template:
+    project_key: "OPS"
+    issue_type: "Change"
+    summary: "DCM: {resource_type} '{entity_handle}'"
+    description: |
+      *Requested by:* {actor_handle}
+      *Tenant:* {tenant_handle}
+      *DCM Request UUID:* {request_uuid}
+      *Catalog Item:* {catalog_item_handle}
+    priority: "Medium"
+    labels: ["dcm-automated", "{tenant_handle}"]
+    
+  inbound_approval:
+    webhook_url: "https://dcm.corp/api/v1/admin/approvals/{approval_uuid}:vote"
+    trigger_on: "issue.status → 'Approved'"
+    decision_map:
+      "Approved": "approve"
+      "Declined": "reject"
+    external_reference_field: "key"    # → OPS-4821
+```
+
+### 2.3 BMC Remedy / Helix ITSM
+
+**API:** REST API (Remedy AR System REST), webhook callbacks
+
+```yaml
+itsm_provider_registration:
+  provider_handle: "bmc-helix-prod"
+  itsm_system: bmc_helix
+  endpoint_url: "https://remedy.corp.example.com/api/arsys/v1"
+  api_version: "v1"
+  
+  supported_actions:
+    - create_change_request        # → CHG:Infrastructure Change
+    - update_change_request
+    - close_change_request
+    - create_incident              # → HPD:Help Desk
+    - update_cmdb_ci               # → AST:Config Item
+    - inbound_approval
+    
+  change_request_template:
+    form: "CHG:Infrastructure Change"
+    Location_Company: "{tenant_handle}"
+    Summary: "DCM: Provision {resource_type} '{entity_handle}'"
+    Categorization_Tier_1: "Infrastructure"
+    Categorization_Tier_2: "Provisioning"
+    Change_Type: "Normal"
+```
+
+### 2.4 Freshservice
+
+```yaml
+itsm_provider_registration:
+  provider_handle: "freshservice-prod"
+  itsm_system: freshservice
+  endpoint_url: "https://corp.freshservice.com/api/v2"
+  
+  supported_actions:
+    - create_change_request
+    - update_change_request
+    - close_change_request
+    - create_incident
+    - create_service_request
+    
+  change_request_template:
+    type: "Normal"
+    risk: "Low"
+    impact: "Low"
+    subject: "DCM: {resource_type} '{entity_handle}'"
+    description: "Tenant: {tenant_handle} | Actor: {actor_handle} | Request: {request_uuid}"
+    group_id: 
+```
+
+### 2.5 PagerDuty (Incident Management)
+
+```yaml
+itsm_provider_registration:
+  provider_handle: "pagerduty-prod"
+  itsm_system: pagerduty
+  endpoint_url: "https://api.pagerduty.com"
+  
+  supported_actions:
+    - create_incident              # for DCM failures, drift, security events
+    - update_incident
+    - close_incident
+    
+  # PagerDuty Events API v2
+  incident_template:
+    service_id: 
+    escalation_policy_id: 
+    payload:
+      summary: "DCM {event_type}: {entity_handle}"
+      severity: "{{ drift_severity | map: critical→critical, significant→error, moderate→warning, minor→info }}"
+      source: "dcm"
+      custom_details:
+        entity_uuid: "{entity_uuid}"
+        tenant: "{tenant_handle}"
+        dcm_event: "{event_type}"
+```
+
+### 2.6 Generic REST (Custom ITSM)
+
+For ITSM systems not natively supported, the `generic_rest` type allows template-based HTTP calls:
+
+```yaml
+itsm_provider_registration:
+  provider_handle: "custom-itsm-prod"
+  itsm_system: generic_rest
+  endpoint_url: "https://itsm.corp.example.com/api"
+  
+  action_templates:
+    - action: create_change_request
+      method: POST
+      path: "/changes"
+      headers:
+        Content-Type: "application/json"
+        X-API-Key: "{{ credential_value }}"
+      body_template: |
+        {
+          "title": "DCM: {{ resource_type }} '{{ entity_handle }}'",
+          "requested_by": "{{ actor_handle }}",
+          "category": "Infrastructure",
+          "external_id": "{{ request_uuid }}"
+        }
+      response_id_path: "$.id"    # JSONPath to extract record ID from response
+      
+    - action: inbound_approval
+      inbound_field: "status"
+      decision_map:
+        "approved": "approve"
+        "rejected": "reject"
+```
+
+---
+
+## 3. ITSM Policy Type
+
+### 3.1 What an ITSM Policy Is
+
+An **ITSM Policy** is a new DCM Policy output type (8th, alongside GateKeeper, Validation, Transformation, Recovery, Orchestration Flow, Governance Matrix Rule, and Lifecycle Policy).
+
+It fires as a **side-effect policy** — it does not block pipeline execution (it is not a GateKeeper) and does not transform the payload. It fires on a DCM event and triggers an ITSM action via a registered ITSM integration. The pipeline continues whether or not the ITSM action succeeds; ITSM failures are logged and alerted but do not block DCM operations.
+
+**Key distinction:** An ITSM Policy is about *record-keeping and integration* with external governance systems. A GateKeeper Policy is about *allowing or blocking* operations. These are complementary, not competing.
+
+### 3.2 Output Schema
+
+```yaml
+# ITSM Policy output schema
+itsm_policy_output:
+  type: itsm_action            # new output type identifier
+  
+  # Required
+  itsm_provider_uuid:    # which ITSM integration to call
+  action: create_change_request | update_change_request | close_change_request |
+          create_incident | update_incident | close_incident |
+          update_cmdb_ci | create_cmdb_ci | retire_cmdb_ci |
+          create_service_request | link_parent_record
+          
+  # Payload — fields to pass to ITSM integration
+  # Supports template variables from the triggering event payload
+  action_payload:
+    : 
+    
+  # How to handle ITSM failure
+  on_failure: log_and_continue | alert_and_continue | alert_only
+  
+  # Store the ITSM record reference on the DCM entity (optional)
+  store_reference_on_entity: 
+  reference_label:     # human-readable label for the reference
+  
+  # Require ITSM record creation before dispatch (optional — see note)
+  block_until_created:   # default: false
+  block_timeout:   # max wait if block_until_created: true
+```
+
+> **`block_until_created`:** When `true`, the ITSM Policy behaves like a pre-dispatch gate — DCM waits for the ITSM record to be created before dispatching to the Service Provider. This is used when organizational policy requires a change record to exist before any provisioning begins. When `false` (default), the ITSM record is created in parallel with or after dispatch — suitable for notification-only use cases.
+
+### 3.3 Example Policies
+
+#### Policy 1: Create Change Request on Dispatch (ServiceNow)
+
+```yaml
+policy_handle: "create-change-on-dispatch"
+policy_type: itsm_action
+enforcement_level: soft
+status: active
+
+match:
+  payload_type: request.dispatched
+  conditions:
+    - field: resource_type
+      operator: in
+      value: [Compute.VirtualMachine, Storage.Volume, Network.VLAN]
+
+output:
+  type: itsm_action
+  itsm_provider_uuid: 
+  action: create_change_request
+  action_payload:
+    short_description: "DCM: Provision {{ resource_type }} '{{ entity_handle }}'"
+    description: |
+      Automated provisioning via DCM.
+      Request UUID: {{ request_uuid }}
+      Actor: {{ actor_handle }}
+      Tenant: {{ tenant_handle }}
+      Catalog Item: {{ catalog_item_handle }}
+    risk: "{{ risk_score | map: <25→'Low', <60→'Medium', else→'High' }}"
+  store_reference_on_entity: true
+  reference_label: "Change Request"
+  on_failure: alert_and_continue
+```
+
+#### Policy 2: Block Dispatch Until Change Record Exists (Compliance Gate)
+
+```yaml
+policy_handle: "require-change-record-before-dispatch"
+policy_type: itsm_action
+enforcement_level: hard
+status: active
+
+match:
+  payload_type: request.layers_assembled
+  conditions:
+    - field: tenant_handle
+      operator: in
+      value: [payments-team, pci-scope-team]
+
+output:
+  type: itsm_action
+  itsm_provider_uuid: 
+  action: create_change_request
+  action_payload:
+    short_description: "DCM: {{ resource_type }} provision — {{ tenant_handle }}"
+    change_type: "Normal"
+    assignment_group: "Change Advisory Board"
+  store_reference_on_entity: true
+  reference_label: "Change Request (PCI Scope)"
+  block_until_created: true
+  block_timeout: PT30M
+  on_failure: alert_and_continue
+```
+
+#### Policy 3: Update CMDB on Realization
+
+```yaml
+policy_handle: "sync-cmdb-on-realization"
+policy_type: itsm_action
+status: active
+
+match:
+  payload_type: entity.realized
+  conditions:
+    - field: resource_type
+      operator: in
+      value: [Compute.VirtualMachine, Compute.BareMetalServer]
+
+output:
+  type: itsm_action
+  itsm_provider_uuid: 
+  action: create_cmdb_ci
+  action_payload:
+    name: "{{ entity_handle }}"
+    ip_address: "{{ realized_fields.primary_ip }}"
+    os: "{{ realized_fields.os_family }}"
+    managed_by: "DCM"
+    environment: "{{ tenant_handle }}"
+    correlation_id: "{{ entity_uuid }}"
+  store_reference_on_entity: true
+  reference_label: "CMDB CI"
+  on_failure: alert_and_continue
+```
+
+#### Policy 4: Create Incident on Drift (Jira)
+
+```yaml
+policy_handle: "create-incident-on-critical-drift"
+policy_type: itsm_action
+status: active
+
+match:
+  payload_type: drift.detected
+  conditions:
+    - field: drift_severity
+      operator: in
+      value: [significant, critical]
+
+output:
+  type: itsm_action
+  itsm_provider_uuid: 
+  action: create_incident
+  action_payload:
+    summary: "DCM Drift: {{ entity_handle }} — {{ drift_severity }}"
+    description: |
+      DCM has detected significant configuration drift.
+      Entity: {{ entity_handle }} ({{ entity_uuid }})
+      Severity: {{ drift_severity }}
+      Drifted fields: {{ drifted_fields | count }} fields
+      Detected at: {{ discovered_at }}
+      View in DCM: https://dcm.corp/resources/{{ entity_uuid }}/drift
+    priority: "{{ drift_severity | map: critical→'Highest', significant→'High' }}"
+    labels: ["dcm-drift", "{{ resource_type | slugify }}"]
+  store_reference_on_entity: true
+  reference_label: "Drift Incident"
+  on_failure: log_and_continue
+```
+
+#### Policy 5: Retire CMDB CI on Decommission
+
+```yaml
+policy_handle: "retire-cmdb-on-decommission"
+policy_type: itsm_action
+status: active
+
+match:
+  payload_type: entity.decommissioned
+
+output:
+  type: itsm_action
+  itsm_provider_uuid: 
+  action: retire_cmdb_ci
+  action_payload:
+    correlation_id: "{{ entity_uuid }}"    # find CI by DCM entity UUID
+    install_status: "7"                    # ServiceNow: Retired
+    retired_at: "{{ event_timestamp }}"
+    decommission_reason: "DCM decommission — {{ actor_handle }}"
+  on_failure: alert_and_continue
+```
+
+#### Policy 6: Close Change Record on Completion
+
+```yaml
+policy_handle: "close-change-on-completion"
+policy_type: itsm_action
+status: active
+
+match:
+  payload_type: request.realized
+  conditions:
+    - field: entity.itsm_references[?(@.record_type=='change_request')].record_id
+      operator: exists
+
+output:
+  type: itsm_action
+  itsm_provider_uuid: 
+  action: close_change_request
+  action_payload:
+    state: "3"                              # ServiceNow: Closed
+    close_code: "Successful"
+    close_notes: "Provisioning completed successfully by DCM. Entity: {{ entity_uuid }}"
+  on_failure: log_and_continue
+```
+
+---
+
+## 4. ITSM integration System Policies
+
+| Policy | Rule |
+|--------|------|
+| `ITSM-001` | ITSM integrations implement the base Provider contract (PRV-001) including registration, health check, sovereignty declaration, and zero trust authentication. ITSM system connectivity credentials must reference a registered credential management service — no plaintext credentials in provider registration. |
+| `ITSM-002` | DCM does not require ITSM integration to function. ITSM Policies with `on_failure: alert_and_continue` (the default) never block DCM pipeline execution. Organizations must explicitly set `block_until_created: true` to gate pipeline on ITSM record creation. |
+| `ITSM-003` | Inbound events from ITSM systems must be authenticated. ITSM integrations must verify HMAC signatures or OAuth tokens on all inbound webhooks before forwarding to DCM. Unauthenticated inbound events are rejected and logged. |
+| `ITSM-004` | ITSM record references stored on DCM entities follow entity lifecycle — they are included in the Realized State record, preserved through updates, and retained in the decommissioned entity record for audit purposes. |
+| `ITSM-005` | ITSM Policies that use `block_until_created: true` must declare a `block_timeout`. If the ITSM system does not confirm record creation within the timeout, the policy fires `on_failure` behavior and the block is released — the pipeline continues. A blocked pipeline is never permanently stalled by ITSM unavailability. |
+| `ITSM-006` | Field mappings between DCM entities and ITSM CI types must be declared in the ITSM integration capability registration. Unmapped resource types are silently skipped for CMDB sync actions. |
+| `ITSM-007` | Template expressions in ITSM Policy `action_payload` fields must resolve using values from the triggering event payload. Template expressions that reference unavailable fields produce a warning in the audit record and substitute an empty string. They do not block ITSM action execution. |
+
+---
+
+## 5. ITSM Policy System Policies
+
+| Policy | Rule |
+|--------|------|
+| `ITSM-POL-001` | ITSM Policies follow the full Policy base contract (B-policy-contract.md): lifecycle (developing → proposed → active), shadow mode validation, audit obligation on every evaluation, domain precedence. |
+| `ITSM-POL-002` | ITSM Policies are side-effect policies — they do not produce pipeline decisions (allow/deny/transform). They may not be used as GateKeeper substitutes except through the explicit `block_until_created: true` mechanism, which has its own timeout guarantee (ITSM-005). |
+| `ITSM-POL-003` | ITSM Policy evaluation is recorded in the audit trail. The audit record includes: policy handle, matched event, ITSM provider UUID, action requested, ITSM record ID returned, and outcome (success/failure/timeout). |
+| `ITSM-POL-004` | Multiple ITSM Policies may fire on the same event. All fire independently — one policy's failure does not prevent other ITSM Policies from executing. |
+
+---
+
+## 6. Additions to the Foundations Document
+
+The foundations document provider type table gains a 12th row:
+
+| Provider Type | Capability | Data direction |
+|--------------|-----------|----------------|
+| **ITSM integration** | Bidirectional integration with ITSM systems; creates/updates ITSM records from DCM events; routes ITSM approvals back to DCM | DCM → ITSM (outbound) / ITSM → DCM (inbound) |
+
+The foundations document policy type table gains an 8th entry:
+
+| Policy Type | Output | Pipeline role |
+|------------|--------|---------------|
+| **ITSM Action** | Triggers action in connected ITSM system; optionally stores record reference on entity; optionally gates pipeline on record creation | Side-effect (non-blocking by default) |
+
+---
+
+## 7. Event Catalog Additions
+
+Two new events for the Event Catalog (doc 33):
+
+| Event Type | Urgency | Trigger |
+|-----------|---------|---------|
+| `itsm.record_created` | info | ITSM integration successfully created a record in external system |
+| `itsm.record_failed` | medium | ITSM integration failed to create/update record; `block_until_created` timeout reached |
+
+These extend the existing event catalog with a new `itsm.*` domain prefix.
+
+---
+
+## 8. Standards Catalog Addition
+
+ITSM integration standards and protocols used:
+
+| Standard | Use in DCM ITSM |
+|----------|----------------|
+| ServiceNow REST Table API | Primary integration for ServiceNow create/update/query |
+| Jira REST API v3 | Primary integration for Atlassian Jira Service Management |
+| BMC AR REST API v1 | Primary integration for BMC Remedy/Helix |
+| PagerDuty Events API v2 | Incident creation for alert-type ITSM integrations |
+| ITIL v4 Change Management | Conceptual framework for DCM change record lifecycle mapping |
+| JSON:API | Standard used by several ITSM REST APIs |
+| HMAC-SHA256 | Inbound webhook signature verification for all ITSM systems |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/43-provider-callback-auth.md b/content/docs/architecture/data-model/43-provider-callback-auth.md
new file mode 100644
index 0000000..2c5e50a
--- /dev/null
+++ b/content/docs/architecture/data-model/43-provider-callback-auth.md
@@ -0,0 +1,421 @@
+# DCM Data Model — Provider Callback Authentication
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference
+**Related Documents:** [Unified Provider Contract](A-provider-contract.md) | [credential management service Model](31-credential-provider-model.md) | [Accreditation, Auth Matrix, Zero Trust](26-accreditation-and-authorization-matrix.md) | [Internal Component Auth](36-internal-component-auth.md) | [Registration Specification](../specifications/dcm-registration-spec.md) | [Provider Callback API](../schemas/openapi/dcm-provider-callback-api.yaml)
+
+> **Foundation Document Reference**
+>
+> This document maps to: **PROVIDER** (authentication of the provider-to-DCM interaction boundary)
+> and **DATA** (the credential artifact that governs that boundary).
+>
+> See [00-foundations.md](00-foundations.md) · [A-provider-contract.md](A-provider-contract.md) · [B-policy-contract.md](B-policy-contract.md)
+
+---
+
+## 1. Purpose and Scope
+
+This document specifies how Service Providers authenticate inbound calls to the DCM control plane — specifically, calls to the Provider Callback API endpoints:
+
+- `POST /api/v1/providers` — Registration
+- `POST /api/v1/providers/{provider_uuid}/capacity` — Capacity reporting
+- `PUT /api/v1/instances/{resource_id}/status` — Realized state push
+- `POST /api/v1/provider/entities/{entity_uuid}/status` — Interim progress
+- `POST /api/v1/provider/entities/{entity_uuid}/update-notification` — Authorized state change notification
+- `GET /api/v1/provider/notifications/{notification_uuid}` — Notification status poll
+- `POST /api/v1/instances/{resource_id}/events` — Lifecycle event reporting
+
+The outbound model (DCM authenticating to providers) is specified in [doc 31 Section 4.2](31-credential-provider-model.md) and [doc 26 Section 5.3](26-accreditation-and-authorization-matrix.md). This document specifies the **inbound** model.
+
+---
+
+## 2. The Authentication Problem
+
+When DCM receives a callback at `PUT /api/v1/instances/{resource_id}/status`, it must verify:
+
+1. **Identity:** Is this call genuinely from the registered Service Provider for this resource?
+2. **Authorization:** Is this provider permitted to push realized state for this specific resource/entity?
+3. **Integrity:** Has the payload been tampered with in transit?
+4. **Freshness:** Is this a live call, not a replayed credential from a previous session?
+5. **Scope:** Is this credential permitted for this specific operation type?
+
+Network-level authentication alone (firewall rules, IP allowlisting) is insufficient under the DCM Zero Trust model — it establishes perimeter trust, not per-call identity. Every provider call to DCM must carry a credential that answers all five questions independently of network position.
+
+---
+
+## 3. Two-Layer Authentication Model
+
+DCM uses a **two-layer** model for provider-to-DCM calls. Both layers must pass:
+
+```
+Provider calls DCM:
+  │
+  ▼ Layer 1: Transport Identity (mTLS)
+  │   Provider presents its registered certificate
+  │   DCM verifies the certificate chain against the provider's registered CA
+  │   Proves: this connection is from the registered provider
+  │   Does NOT prove: authorization for this specific operation
+  │
+  ▼ Layer 2: Operation Authorization (Provider Callback Credential)
+  │   Provider presents a scoped short-lived credential in the Authorization header
+  │   DCM validates: credential is active, scoped to this provider, scoped to this operation type
+  │   Proves: this specific call is authorized for this specific operation
+  │   Does NOT replace mTLS — both layers are required
+  │
+  ▼ Both pass → five-check boundary model evaluates (doc 26 Section 5.2)
+  └── Audit record written regardless of outcome
+```
+
+**Why two layers?** mTLS proves the caller holds the private key for the registered certificate — it proves identity at the transport level. The interaction credential proves the specific call is authorized for the specific operation type and scope. A compromised credential without the private key cannot establish the mTLS connection. A valid mTLS connection without a valid credential cannot perform operations. The layers are complementary, not redundant.
+
+---
+
+## 4. Layer 1 — Transport Identity (mTLS)
+
+### 4.1 Provider Certificate Registration
+
+At registration, every provider declares a certificate:
+
+```yaml
+provider_registration:
+  certificate:
+    pem: 
+    ca_chain: 
+    rotation_interval: P90D
+```
+
+DCM validates:
+- Certificate chain is valid and trusted
+- Certificate is not in DCM's Credential Revocation Registry
+- Certificate `CN` or `SAN` matches the provider's declared `handle`
+- Certificate `expires_at` is not in the past
+
+DCM stores the certificate fingerprint. On every subsequent inbound connection, DCM validates the presented certificate against the stored fingerprint for this provider.
+
+### 4.2 Certificate Validation on Inbound Calls
+
+When a provider initiates a TLS connection to DCM:
+
+```
+Provider → DCM:
+  TLS ClientHello → ServerHello + DCM certificate
+  Provider verifies DCM certificate (validates DCM's identity)
+  Provider sends its certificate
+  DCM validates:
+    1. Certificate chain → registered CA for this provider
+    2. Certificate fingerprint → matches stored fingerprint for provider_uuid in path/payload
+    3. Certificate not in Credential Revocation Registry
+    4. Certificate expires_at not expired
+  If any check fails → TLS handshake rejected; connection refused
+```
+
+**Certificate rotation:** Providers must rotate certificates on the declared `rotation_interval`. DCM fires a `P14D` warning event when a certificate is approaching expiry. During the rotation transition window, DCM accepts both the current and new certificate simultaneously (P7D window). After the window, only the new certificate is accepted.
+
+### 4.3 Certificate Binding to Operations
+
+The mTLS certificate is **not sufficient alone** for operation authorization. Knowing a call came from Provider X does not mean Provider X is authorized to push realized state for entity Y owned by Tenant Z. The interaction credential (Layer 2) carries that authorization.
+
+---
+
+## 5. Layer 2 — Operation Authorization (Provider Callback Credential)
+
+### 5.1 Provider Callback Credential
+
+The **Provider Callback Credential** is a `dcm_interaction` type credential issued to the provider by DCM's credential management service at registration activation time. It is the mechanism by which providers prove authorization for specific callback operations.
+
+```yaml
+provider_callback_credential:
+  credential_uuid: 
+  credential_type: dcm_interaction
+  issued_to:
+    provider_uuid:       # the specific registered provider
+    provider_handle:   # for human-readable audit records
+  issued_at: 
+  expires_at:       # profile-governed lifetime; see Section 5.3
+  operation_scope:
+    allowed_operations:
+      - realized_state_push
+      - capacity_report
+      - interim_status
+      - update_notification
+      - lifecycle_event
+      - notification_poll
+      # Note: registration uses a registration_token, not this credential
+    # Scope is bound to the provider_uuid — cannot be used for other providers
+  non_transferable: true
+  bound_to_ip:        # fsi/sovereign profiles: IP-bound
+  revocation_check_url: 
+```
+
+**Presented as:** `Authorization: Bearer ` on all callback API calls.
+
+**Key property:** The credential is scoped to the `provider_uuid` — not to specific entities or operations within that provider. Entity-level scope is enforced separately (Section 6). This means a provider holding the credential can call any callback endpoint, but DCM enforces entity-level ownership checks per call.
+
+### 5.2 Credential Issuance
+
+Provider callback credentials are issued through the following lifecycle:
+
+```
+Registration approved (provider status → ACTIVE):
+  │
+  ▼ DCM API Gateway requests credential from credential management service:
+  │   credential_type: dcm_interaction
+  │   issued_to.provider_uuid: 
+  │   allowed_operations: [realized_state_push, capacity_report, interim_status,
+  │                         update_notification, lifecycle_event, notification_poll]
+  │   expires_at: 
+  │
+  ▼ credential management service issues credential
+  │   Returns credential_value (the bearer token)
+  │   Stores credential_record in Credential Store
+  │
+  ▼ DCM delivers credential to provider via the activation response:
+  │   POST /api/v1/admin/providers/{uuid}:approve
+  │   Response includes: credential_ref (UUID for retrieval)
+  │
+  ▼ Provider retrieves credential value via credential management service endpoint:
+  │   GET {service_provider_endpoint}/credentials/{credential_ref}/value
+  │   (Requires the registration token used at initial registration — one-time bootstrap)
+  │
+  ▼ Provider stores credential securely and uses it for all callback API calls
+```
+
+**Bootstrap case:** During initial registration (before activation), the provider uses the registration token to authenticate. After activation, the provider callback credential replaces the registration token for all subsequent calls. The registration token is single-use and expires after the first successful registration response.
+
+### 5.3 Credential Lifetime by Profile
+
+| Profile | Lifetime | Rotation trigger | IP binding |
+|---------|----------|-----------------|------------|
+| minimal | PT8H | Pre-expiry P1H | No |
+| dev | PT4H | Pre-expiry P30M | No |
+| standard | PT1H | Pre-expiry PT10M | No |
+| prod | PT30M | Pre-expiry PT5M | Optional |
+| fsi | PT15M | Pre-expiry PT3M | Required |
+| sovereign | PT15M + hardware attestation | Pre-expiry PT3M | Required; HSM-bound |
+
+**Pre-expiry rotation:** DCM initiates rotation automatically before the current credential expires. The transition window is 50% of the credential lifetime — the old credential remains valid during the window while the new one is delivered. Providers must implement credential refresh in their SDK or adapter.
+
+### 5.4 Credential Rotation Protocol
+
+```
+PT{rotation_trigger} before credential expiry:
+  │
+  ▼ DCM initiates rotation:
+  │   Requests new credential from credential management service
+  │   rotation_of: 
+  │   same allowed_operations scope; new expires_at
+  │
+  ▼ credential management service issues new credential
+  │   Old credential NOT yet revoked
+  │
+  ▼ DCM pushes rotation notification to provider:
+  │   POST {provider_health_endpoint}/credential-rotation (if provider supports it)
+  │   OR: credential.rotating event published to Message Bus
+  │   New credential_ref included; provider retrieves new value
+  │
+  ▼ Transition window opens:
+  │   DCM accepts BOTH old and new credential during transition window
+  │   Provider switches to new credential
+  │
+  ▼ Transition window closes:
+  │   Old credential revoked
+  │   Revocation event published → all components update revocation cache
+```
+
+If the provider fails to pick up the new credential before the transition window closes, the old credential is revoked and the provider's callback calls will return `403 Forbidden` with code `CREDENTIAL_EXPIRED`. The provider must re-register to obtain a new credential — this is a recoverable state.
+
+### 5.5 DCM Validation on Inbound Calls
+
+When DCM receives a callback call, Layer 2 validation performs these checks in order:
+
+```
+1. Extract credential_value from Authorization: Bearer header
+   → Missing or malformed: 401 Unauthorized; MISSING_CREDENTIAL audit record
+
+2. Look up credential_record by credential_value hash
+   → Not found: 401 Unauthorized; CREDENTIAL_NOT_FOUND audit record
+
+3. Check credential_record.status is 'active'
+   → Revoked: 403 Forbidden; code: CREDENTIAL_REVOKED
+   → Expired: 403 Forbidden; code: CREDENTIAL_EXPIRED
+
+4. Check credential_record.expires_at > now
+   → Expired: 403 Forbidden; code: CREDENTIAL_EXPIRED
+
+5. Check credential_record.issued_to.provider_uuid matches:
+   a. The provider_uuid in the URL path (where applicable)
+   b. The mTLS certificate's registered provider (Layer 1 binding)
+   → Mismatch: 403 Forbidden; code: CREDENTIAL_SCOPE_VIOLATION
+
+6. Check that the operation_type for this endpoint is in allowed_operations
+   → Not in scope: 403 Forbidden; code: OPERATION_NOT_IN_SCOPE
+
+7. If bound_to_ip is set: verify client IP matches
+   → Mismatch: 403 Forbidden; code: IP_BINDING_VIOLATION
+```
+
+All failures write an audit record with the credential_uuid, provider_uuid, endpoint, and failure reason. After 5 consecutive `CREDENTIAL_SCOPE_VIOLATION` or `IP_BINDING_VIOLATION` failures from the same provider within PT1H, DCM fires a `security.unsanctioned_provider_write` event and notifies the platform admin (urgency: critical).
+
+---
+
+## 6. Entity-Level Authorization
+
+The provider callback credential proves the caller is the registered provider. It does not prove the provider is authorized to act on a specific entity. Entity-level authorization is a separate check that applies on each call.
+
+### 6.1 Resource Ownership Binding
+
+For `realized_state_push` and `interim_status` calls, DCM validates:
+
+```
+PUT /api/v1/instances/{resource_id}/status
+
+DCM checks:
+  1. Look up the Requested State record for resource_id
+  2. Verify the credential's provider_uuid matches the provider_uuid
+     in the Requested State record (i.e., this was the provider DCM dispatched to)
+  3. Verify the entity is in a lifecycle state that permits this push
+     (PROVISIONING, UPDATING, or DECOMMISSIONING — not OPERATIONAL, not DECOMMISSIONED)
+  
+  → Mismatch on provider_uuid: 403 Forbidden; code: ENTITY_NOT_OWNED_BY_PROVIDER
+  → Wrong lifecycle state: 409 Conflict; code: INVALID_LIFECYCLE_STATE_FOR_PUSH
+```
+
+**Why this matters:** A provider that receives a `resource_id` (e.g., by observing network traffic or misconfiguration) cannot push realized state for an entity it was not dispatched to. The Requested State record binds the entity to the specific provider that received the dispatch.
+
+### 6.2 Update Notification Binding
+
+For `update_notification` calls, DCM validates:
+
+```
+POST /api/v1/provider/entities/{entity_uuid}/update-notification
+
+DCM checks:
+  1. Look up the Realized State record for entity_uuid
+  2. Verify the credential's provider_uuid matches the provider_uuid
+     in the most recent Realized State record
+  3. Verify the provider's registration includes the update_capability
+     declared in the notification_type field
+
+  → Provider not current owner: 403 Forbidden; code: ENTITY_NOT_OWNED_BY_PROVIDER
+  → Update type not declared at registration: 403 Forbidden;
+    code: UPDATE_TYPE_NOT_DECLARED
+    (provider must re-register to declare new update capability types)
+```
+
+### 6.3 Lifecycle Event Binding
+
+For `lifecycle_event` calls, DCM validates:
+
+```
+POST /api/v1/instances/{resource_id}/events
+
+DCM checks:
+  1. Verify the credential's provider_uuid matches the provider on record for resource_id
+  2. Verify the resource is in an operational state (not DECOMMISSIONED)
+  3. Verify the event_type is in the standard event catalog
+
+  → Provider not current owner: 403 Forbidden; code: ENTITY_NOT_OWNED_BY_PROVIDER
+  → Entity decommissioned: 409 Conflict; code: ENTITY_DECOMMISSIONED
+  → Unknown event_type: 400 Bad Request; code: UNKNOWN_EVENT_TYPE
+```
+
+---
+
+## 7. Registration Authentication (Special Case)
+
+The initial `POST /api/v1/providers` registration call cannot use the provider callback credential because no credential exists yet. Registration uses a different authentication mechanism:
+
+### 7.1 Registration Token
+
+The registration token is a short-lived, single-use credential issued by a platform admin before provider onboarding:
+
+```yaml
+registration_token:
+  token_uuid: 
+  token_value: 
+  issued_at: 
+  expires_at:    # typically PT72H
+  scope:
+    provider_type_id: service_provider
+    provider_handle_pattern: "eu-west-*"   # optional constraint
+    grants_auto_approval: true | false
+  used: false              # single-use; set to true after first successful use
+```
+
+The registration token is passed as `Authorization: Bearer ` on the initial `POST /api/v1/providers` call. After the first successful registration, the token is marked `used: true` and cannot be reused. If a provider needs to re-register (e.g., after a sovereignty declaration change), a new registration token is required.
+
+**mTLS still required for registration:** The mTLS layer (Layer 1) is enforced on the registration call. The provider must present the certificate declared in the registration payload. This ensures the entity performing the registration possesses the private key for the certificate it is claiming.
+
+### 7.2 Re-Registration
+
+For re-registration calls (same `name`, updating version or capabilities), the provider uses its active provider callback credential — not a new registration token. Re-registration that changes the sovereignty declaration requires a new registration token from the platform admin (treated as a new registration requiring a new approval).
+
+---
+
+## 8. Credential Revocation and Emergency Response
+
+### 8.1 Revocation Triggers
+
+| Trigger | What happens |
+|---------|-------------|
+| Provider deregistered | All callback credentials for that provider revoked immediately |
+| Security event detected (5+ scope violations in PT1H) | Provider suspended; credential revoked; platform admin notified |
+| Provider certificate expiry without rotation | Credential revoked at certificate expiry |
+| Platform admin explicit revocation | Immediate revocation; provider must re-register |
+| Provider compromise suspected | Emergency revocation; Recovery Policy evaluates affected entities |
+
+### 8.2 Emergency Revocation Flow
+
+```
+Platform admin triggers emergency revocation:
+  │
+  ▼ POST /api/v1/admin/providers/{provider_uuid}/revoke-credential
+  │   reason: 
+  │   suspend_provider: true | false
+  │
+  ▼ DCM revokes credential immediately:
+  │   credential_record.status → revoked
+  │   Revocation event → Message Bus
+  │   All DCM components update revocation cache (within PT30S)
+  │
+  ▼ If suspend_provider: true:
+  │   Provider status → SUSPENDED
+  │   New requests not routed to this provider
+  │   Active realizations enter PENDING_REVIEW state
+  │
+  ▼ Recovery Policy evaluates affected entities:
+      Entities currently hosted at provider: notify Tenant owners
+      In-progress operations: depends on Recovery Policy profile
+```
+
+### 8.3 Revocation Cache
+
+DCM components that validate inbound credentials maintain a local **Credential Revocation Cache**:
+
+- Cache is populated from the Message Bus `credential.revoked` event stream
+- Cache TTL matches the maximum credential lifetime for the active profile
+- On cache miss: remote check against Credential Store (prevents stale cache from accepting revoked credentials)
+- Cache invalidation is immediate on `credential.revoked` event receipt (not TTL-based)
+
+The revocation cache ensures revocation propagates within PT30S even without a cache miss triggering a remote lookup.
+
+---
+
+## 9. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `PCA-001` | All provider-to-DCM calls must present both a valid mTLS certificate (Layer 1) and a valid provider callback credential (Layer 2). Neither layer alone is sufficient. |
+| `PCA-002` | Provider callback credentials are scoped to the provider_uuid and cannot be used to act on entities hosted at other providers. |
+| `PCA-003` | Entity-level authorization is checked on every realized_state_push, update_notification, and lifecycle_event call, independent of credential validity. A valid credential does not grant access to entities the provider was not dispatched to. |
+| `PCA-004` | Five consecutive credential scope violations or IP binding violations from the same provider within PT1H triggers automatic provider suspension and platform admin notification. |
+| `PCA-005` | Provider callback credentials are issued by the credential management service, not directly by the DCM API Gateway. The credential management service is the authoritative source for all credential issuance, rotation, and revocation. |
+| `PCA-006` | Registration tokens are single-use. A registration token that has been used once is permanently invalidated regardless of its `expires_at` timestamp. |
+| `PCA-007` | Re-registration that changes the sovereignty declaration requires a new registration token and triggers a new approval pipeline. Version and capability updates do not require a new registration token. |
+| `PCA-008` | Provider callback credentials must be rotated before expiry. DCM initiates rotation automatically. If a credential expires without rotation, the provider enters a CREDENTIAL_EXPIRED state and must obtain a new credential via the platform admin. |
+| `PCA-009` | For fsi and sovereign profiles, provider callback credentials are IP-bound. A credential presented from an IP address that does not match the `bound_to_ip` field is rejected regardless of its validity. |
+| `PCA-010` | All inbound provider calls — including rejected calls — produce an audit record containing the credential_uuid, provider_uuid, endpoint, operation_type, outcome, and timestamp. There are no silent failures. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/44-kessel-integration-evaluation.md b/content/docs/architecture/data-model/44-kessel-integration-evaluation.md
new file mode 100644
index 0000000..c51b098
--- /dev/null
+++ b/content/docs/architecture/data-model/44-kessel-integration-evaluation.md
@@ -0,0 +1,470 @@
+# DCM — Kessel Integration Evaluation
+
+**Document Status:** 📋 Draft — For Discussion
+**Document Type:** Integration Evaluation — Pre-Implementation
+**Purpose:** This document evaluates the potential integration of DCM with the [Kessel project](https://github.com/project-kessel) for identity/access management and resource inventory. It is intended as a basis for discussion with the Kessel development team. **No architectural changes should be made to DCM based on this document until alignment with the Kessel team is confirmed.**
+
+**Related Documents:** [Auth Providers](19-auth-providers.md) | [Universal Group Model](15-universal-groups.md) | [Entity Relationships](09-entity-relationships.md) | [Four States](02-four-states.md) | [Accreditation and Zero Trust](26-accreditation-and-authorization-matrix.md) | [Control Plane Components](25-control-plane-components.md) | [Provider Callback Authentication](43-provider-callback-auth.md)
+
+**Related Projects:** [project-kessel](https://github.com/project-kessel) | [SpiceDB](https://github.com/authzed/spicedb) | [Google Zanzibar](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/)
+
+---
+
+## 1. Executive Summary
+
+Kessel is a Red Hat project providing two capabilities: **Kessel Relations** (Relationship-Based Access Control built on SpiceDB, a Google Zanzibar implementation) and **Kessel Asset Inventory** (a hybrid cloud resource state tracking service with a common Protobuf/gRPC API).
+
+DCM has architecturally similar needs in both areas. The evaluation concludes:
+
+- **Kessel Relations** has strong alignment with DCM's access control requirements. The permission model maps cleanly, and the operational benefits — Zanzibar-style consistency, scalable graph traversal, shared source of truth across Red Hat products — are meaningful. Integration path exists via DCM's Auth Provider abstraction.
+
+- **Kessel Inventory** has partial alignment with DCM's Discovered State store. The fit is real but narrower than it might appear: Kessel Inventory is a current-state snapshot system; DCM's inventory is a four-state lifecycle model with field-level provenance, drift detection, and append-only audit. Integration path exists via DCM's data store abstraction.
+
+**Recommended next step:** Discussion with the Kessel development team to validate assumptions, confirm schema extensibility for DCM-specific resource types, and understand the Kessel Relations API stability and sovereign/air-gapped deployment model.
+
+---
+
+## 2. What Kessel Provides
+
+### 2.1 Kessel Relations
+
+Kessel Relations is an authorization service built on [SpiceDB](https://github.com/authzed/spicedb), which implements the [Google Zanzibar](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/) consistent global authorization model.
+
+**Core model — Relationship-Based Access Control (ReBAC):**
+- Resources and subjects are defined in a typed schema
+- Relationships between subjects and resources are stored as tuples: `subject:X relation:Y object:Z`
+- Permissions are computed by evaluating the relationship graph: "does user X have permission `submit_request` on tenant T?" traverses all paths from X to T through groups, roles, and other relationships
+- Transitive relationships are handled natively: if user X is a member of group G, and group G has `admin` on tenant T, X inherits `admin` on T
+
+**Zanzibar consistency model:**
+- Snapshot reads: consistent reads at a point in time
+- Zookie tokens: causality tokens that guarantee "read your own writes" without requiring full global linearizability — after writing a relationship tuple, the response includes a zookie; subsequent reads with that zookie are guaranteed to observe the write
+
+**gRPC API surface (from the Kessel project):**
+- `CheckPermission(subject, permission, resource)` → allow/deny
+- `LookupResources(subject, permission, resource_type)` → list of resources subject has permission on
+- `LookupSubjects(resource, permission, subject_type)` → list of subjects that have permission on resource
+- `WriteRelationships(tuples)` → write relationship tuples
+- `DeleteRelationships(filter)` → remove relationship tuples
+
+### 2.2 Kessel Asset Inventory
+
+Kessel Asset Inventory is a resource tracking service designed to provide a unified inventory view across hybrid cloud infrastructure — OpenShift clusters, RHEL systems, edge devices, and other Red Hat-managed resources.
+
+**Core model:**
+- Resources are described using a common Protobuf schema with a typed `ResourceType` and a `Spec` for type-specific fields
+- Current state is tracked as an upsertable snapshot — last-write wins
+- gRPC streaming API for push (providers send state updates) and pull (consumers query current state)
+- Integration with Kessel Relations for auth-filtered inventory queries: "what resources of type X does subject Y have access to?"
+
+**Intended use case:** Giving tools like ACM (Advanced Cluster Management), Insights, and the Hybrid Cloud Console a single query surface for "what exists across my estate?"
+
+---
+
+## 3. DCM's Current Model — What Needs to Be Understood
+
+Before evaluating integration, it is important to characterize what DCM already has in both areas.
+
+### 3.1 DCM's Authorization Model
+
+DCM's current authorization model has five components working together:
+
+**Auth Providers** (doc 19) — DCM delegates authentication to registered Auth Providers (LDAP, OIDC, FreeIPA, Active Directory, mTLS). Auth Providers are registered through the standard Provider contract. Multiple Auth Providers can be active simultaneously. Auth Providers return: authenticated actor identity, group memberships, roles.
+
+**Universal Group Model** (doc 15) — DCM groups (`DCMGroup`) are typed by `group_class`. The classes relevant to authorization:
+- `tenant_boundary` — the ownership and isolation boundary; every resource entity belongs to exactly one tenant
+- `cross_tenant_authorization` — the formal mechanism for Tenant A to grant Tenant B access to a specific resource
+- `policy_collection` — groups that activate policy sets
+- DCMGroup membership is the basis for role resolution and policy application
+
+**RBAC via role mapping** — Auth Providers map external groups to DCM roles (`consumer`, `platform_admin`, `sre`, etc.). The Policy Engine uses roles + group membership to evaluate access.
+
+**Five-check boundary model** (doc 26) — Every interaction crosses five checks in sequence: identity verification → authorization → accreditation → data/capability matrix → sovereignty. Checks 1 and 2 are RBAC. Checks 3–5 are DCM-specific and involve accreditation records, data classification, and sovereignty zones.
+
+**Cross-tenant authorization records** — When Tenant A grants Tenant B access to a resource, a `cross_tenant_authorization` DCMGroup is created. The Policy Engine checks for the existence of this record when evaluating cross-tenant requests.
+
+**What DCM asks for in authorization decisions:**
+1. Does actor X have role Y within tenant T?
+2. What catalog items is actor X allowed to see? (RBAC-filtered list)
+3. Can actor X perform operation O on resource R? (role + tenant ownership)
+4. Does tenant T have a cross-tenant authorization to use resource R owned by tenant T2?
+5. Is actor X a member of DCMGroup G with the required quorum? (approval gates, `authorized` tier)
+
+### 3.2 DCM's Inventory Model
+
+DCM's inventory is the **Four States model** (doc 02). This is meaningfully different from a general-purpose resource inventory.
+
+**Intent State** — The consumer's declared desired state. Stored as a GitOps artifact (PR-based workflow). Immutable after creation. Not a snapshot — it is the authoritative record of what was requested and why.
+
+**Requested State** — The assembled payload after layer enrichment, policy evaluation, and placement resolution. Write-once. Contains the full data model payload that was dispatched to the provider, including field-level provenance tracing every value back to its source.
+
+**Realized State** — An append-only event stream of what the provider actually built. Every realization event is a new record — not an upsert. Contains field-level provenance from the provider. The relationship between a Realized State record and its corresponding Requested State record is explicit and mandatory.
+
+**Discovered State** — An ephemeral snapshot of what the provider currently reports as existing, obtained through active discovery polling. Used by the Drift Reconciliation Component to compare against Realized State.
+
+**What DCM asks for in inventory decisions:**
+1. What is the current lifecycle state of entity UUID X? (Realized State read)
+2. What resources does tenant T own? (indexed query over Realized State)
+3. What entities have relationship R to entity X? (Entity Relationship Graph, doc 09)
+4. What is the field-level provenance of field F on entity X? (Realized State metadata)
+5. What entities are currently drifted? (Drift Record Store, DRC component output)
+6. What did we discover vs what do we have as realized? (Drift comparison)
+7. What happened to entity X over its full lifecycle? (Audit Store, time-indexed)
+
+---
+
+## 4. Integration Analysis
+
+### 4.1 Kessel Relations — Authorization Backend
+
+#### Mapping DCM's Permission Model to SpiceDB
+
+DCM's five authorization questions map to SpiceDB as follows:
+
+```
+# Proposed SpiceDB schema for DCM
+definition user {}
+
+definition group {
+  relation member:      user | group#member
+  relation parent_group: group
+  permission member     = member + parent_group->member
+}
+
+definition tenant {
+  relation member:      user | group#member
+  relation admin:       user | group#member
+  relation platform_admin: user | group#member
+  permission submit_request  = member + admin + platform_admin
+  permission manage_resources = admin + platform_admin
+  permission administer       = platform_admin
+}
+
+definition resource {
+  relation owner_tenant:    tenant
+  relation authorized_tenant: tenant    # cross-tenant authorization
+  relation viewer:          user | group#member
+  permission read   = owner_tenant->member + authorized_tenant->member + viewer
+  permission modify = owner_tenant->admin
+  permission decommission = owner_tenant->admin
+}
+
+definition dcm_group {
+  relation member:    user | group#member
+  relation quorum_threshold: integer  # NOTE: see Section 4.1.2
+}
+```
+
+**Question 1** (does actor X have role Y in tenant T?) → `CheckPermission(user:X, permission:submit_request, tenant:T)`
+
+**Question 2** (what catalog items can actor X see?) → `LookupResources(user:X, permission:read, resource_type:catalog_item)`
+
+**Question 3** (can actor X do operation O on resource R?) → `CheckPermission(user:X, permission:modify, resource:R)`
+
+**Question 4** (does tenant T have cross-tenant authorization on resource R?) → `CheckPermission(tenant:T#member, permission:read, resource:R)` — satisfied if `authorized_tenant` relationship exists
+
+**Question 5** (approval gate quorum) — **Does not map cleanly to SpiceDB.** See Section 4.1.2.
+
+#### 4.1.2 Approval Gate Quorum — The Gap
+
+DCM's `authorized` tier approval requires N of M members of a declared DCMGroup to record decisions before an operation proceeds. SpiceDB is a membership and permission graph — it answers "does this subject have this permission?" but it does not count decisions or track quorum state across time.
+
+**Resolution:** The approval gate workflow stays in DCM's Policy Engine regardless of Kessel integration. Kessel Relations handles who *can* approve (membership in the DCMGroup); DCM's Approval Store tracks who *has* approved and whether quorum is reached.
+
+This is a clean boundary: Kessel answers the structural question ("is this actor authorized to vote?"); DCM answers the state question ("how many valid votes have been recorded?").
+
+#### 4.1.3 DCM's Entity Relationship Graph is NOT an Authorization Graph
+
+This is a critical distinction. DCM's entity relationships — `requires`, `constituent`, `shareable`, `allocated_from`, `peer` — are **operational relationships between infrastructure resources**, not access control relationships. They express: "VM X requires Storage Y", "Composite C has constituent VM X."
+
+These must **not** be stored in Kessel Relations. They are:
+- Semantically different from access control (lifecycle implications, not permissions)
+- DCM-specific (not meaningful to any other system consuming Kessel)
+- Owned by DCM's entity lifecycle model
+
+DCM's Entity Relationship Graph (doc 09) remains entirely in DCM regardless of Kessel integration.
+
+#### 4.1.4 Checks 3–5 of the Five-Check Boundary Model
+
+DCM's five-check boundary model (identity → authorization → accreditation → data matrix → sovereignty) maps to Kessel Relations only for checks 1 and 2. Checks 3–5 are DCM-specific:
+
+- **Accreditation** (check 3): Does the target provider hold the required accreditation for the data classification present? This involves DCM's Accreditation Registry and is not a subject/permission/resource question.
+- **Data/Capability Matrix** (check 4): Is each field permitted to cross this boundary given its classification? This involves DCM's Governance Matrix policies.
+- **Sovereignty** (check 5): Is the target endpoint within the sovereignty boundary? This involves DCM's Sovereignty Zone declarations.
+
+None of checks 3–5 can be delegated to Kessel Relations. They remain in DCM's Policy Engine.
+
+#### 4.1.5 Integration Path via Auth Provider Abstraction
+
+DCM's Auth Provider abstraction (doc 19) is the natural integration point. Kessel Relations would register as a DCM Auth Provider or External Policy Evaluator:
+
+```yaml
+kessel_relations_auth_provider:
+  provider_type: auth_provider
+  auth_mode: kessel_rebac
+  endpoint: https://kessel-relations.internal:9000
+  schema_ref: 
+  
+  # What this provider handles:
+  handles:
+    - check_permission       # CheckPermission calls
+    - lookup_resources       # LookupResources calls
+    - lookup_subjects        # LookupSubjects calls
+  
+  # What stays in DCM's Policy Engine:
+  does_not_handle:
+    - accreditation_checks
+    - data_classification_matrix
+    - sovereignty_checks
+    - approval_gate_quorum
+```
+
+DCM's Policy Engine calls the Kessel Relations provider for authorization questions (checks 1 and 2) and evaluates checks 3–5 internally. The five-check sequence is preserved; only the implementation of checks 1–2 changes.
+
+**Zookie handling:** DCM's API Gateway must thread zookie tokens through the request lifecycle: when a relationship is written (e.g., a new cross-tenant authorization is created), the resulting zookie is stored and used for subsequent permission checks in the same request context, guaranteeing consistency.
+
+---
+
+### 4.2 Kessel Inventory — Discovered State Store
+
+#### 4.2.1 The Fit
+
+Of DCM's four stores, **Discovered State** is the only one Kessel Inventory could plausibly replace. The reasons:
+
+- Discovered State is the most ephemeral store — it is overwritten on each discovery cycle
+- Discovered State does not require immutability or append-only semantics — it represents "what the provider reports right now"
+- Discovered State is the "current state of infrastructure" — exactly what Kessel Inventory is designed to track
+- Other Red Hat tools consuming Kessel Inventory would benefit from seeing the same discovered state that DCM uses for drift detection
+
+The other three stores — Intent, Requested, and Realized — **cannot** be replaced by Kessel Inventory:
+- Intent and Requested State require GitOps semantics (PR workflow, immutability, version history)
+- Realized State requires append-only event stream semantics with field-level provenance and hash chain integrity
+- None of DCM's lifecycle or audit requirements are in scope for Kessel Inventory
+
+#### 4.2.2 The Schema Alignment Question
+
+DCM's Discovered State uses the same unified data model format as Realized State — the DCM Resource Type Spec schema. Kessel Inventory uses a Protobuf-defined common resource schema.
+
+For standard resource types (Compute, Network, Storage that map to well-known infrastructure concepts), the alignment is likely achievable. For DCM-specific resource types (Automation.AnsiblePlaybook, Platform.KubernetesCluster, custom org-defined types), schema extension or mapping is required.
+
+**Open question for Kessel team:** How extensible is the Kessel Inventory resource type schema? Can DCM register custom resource types? Is there a type registry mechanism analogous to DCM's Resource Type Registry?
+
+#### 4.2.3 Drift Detection Logic Stays in DCM
+
+Kessel Inventory is a state store, not a drift detection system. Even if DCM uses Kessel Inventory as the Discovered State store, the Drift Reconciliation Component (doc 25, DRC domain) remains entirely in DCM:
+
+- DRC queries Kessel Inventory for current discovered state
+- DRC compares discovered state against DCM's Realized State
+- DRC classifies differences by field criticality and change magnitude
+- DRC produces Drift Records with SECURITY_DEGRADATION, BROKEN_REFERENCE, UNSANCTIONED_CHANGE classifications
+- DRC writes Drift Records to DCM's Drift Record Store
+
+Kessel Inventory's role is purely as the data source for the "what currently exists" side of the comparison. The intelligence stays in DCM.
+
+#### 4.2.4 Integration Path via data store Abstraction
+
+DCM's data store abstraction (doc 11) is the natural integration point. The Discovered Store would be implemented as a `storage_sub_type: snapshot_store` data store backed by Kessel Inventory:
+
+```yaml
+kessel_inventory_(prescribed infrastructure):
+  provider_type: (prescribed infrastructure)
+  storage_sub_type: snapshot_store
+  backend: kessel_inventory
+  endpoint: https://kessel-inventory.internal:9001
+  
+  # DCM uses this provider for:
+  used_for: discovered_state
+  
+  # Write contract: provider calls POST /api/v1/instances/{id}/status
+  # which DCM translates to Kessel Inventory upsert
+  write_model: upsert_current_state
+  
+  # Read contract: DRC queries Kessel for discovered state
+  read_model: streaming_query_by_type_and_tenant
+```
+
+This means the Kessel Inventory integration requires **no changes to DCM's data model** — only a new data store implementation. The Drift Reconciliation Component calls the same Discovered State Store interface; the underlying implementation happens to be Kessel Inventory.
+
+---
+
+## 5. Deployment and Sovereignty Considerations
+
+### 5.1 Air-Gapped and Sovereign Deployments
+
+DCM's `sovereign` profile requires air-gapped operation with no external dependencies. Any Kessel integration must support:
+
+- Local/on-premises Kessel deployment (not cloud-hosted)
+- Offline operation when Kessel is temporarily unavailable (cached authorization decisions for read-only operations)
+- mTLS between DCM and Kessel instances
+
+**Open question for Kessel team:** What is Kessel's deployment model for sovereign/air-gapped environments? Is there a supported on-premises deployment path? What is the operational footprint?
+
+### 5.2 Multi-Instance Federation
+
+DCM supports federation between multiple DCM instances (doc 22). A federated deployment may have multiple Kessel Relations instances (one per region or sovereignty zone) or a single shared instance.
+
+**Open question for Kessel team:** How does Kessel Relations handle multi-region replication? Can SpiceDB schema and relationship data be replicated across sovereignty boundaries? What are the consistency guarantees in a federated topology?
+
+### 5.3 Failure Mode Analysis
+
+If Kessel Relations is unavailable, DCM cannot evaluate authorization checks 1–2 of the five-check model, which means DCM cannot process any requests. This is a critical dependency.
+
+**Required mitigation strategies:**
+- Read-through cache for CheckPermission results (short TTL, profile-governed)
+- Circuit breaker: if Kessel is unavailable for >N consecutive checks, DCM enters a safe-deny mode (no new requests accepted) rather than a fail-open mode
+- Kessel Relations HA deployment is a prerequisite, not optional
+
+**Open question for Kessel team:** What HA and disaster recovery patterns are recommended for production Kessel Relations deployments?
+
+---
+
+## 6. Questions for the Kessel Team
+
+The following questions should be addressed before any integration work begins:
+
+### 6.1 Kessel Relations
+
+| # | Question | Why It Matters |
+|---|----------|----------------|
+| 1 | What is the current API stability level of the Kessel Relations gRPC API? Are breaking changes expected? | DCM needs a stable contract to build against |
+| 2 | Does Kessel Relations support on-premises / air-gapped deployment? What is the operational footprint? | Required for DCM's `sovereign` profile |
+| 3 | How does the SpiceDB schema evolve? Is there a migration path when the DCM permission model changes? | Schema evolution is a production concern |
+| 4 | Can Kessel Relations store relationships at the scale DCM requires? How many relationship tuples per tenant at what query latency? | DCM may have thousands of cross-tenant authorization records per deployment |
+| 5 | How does Kessel handle the zookie (consistency token) lifecycle? Are zookies scoped to a namespace/tenant, or global? | Relevant to DCM's multi-tenant isolation model |
+| 6 | Is Kessel Relations multi-tenant natively, or does DCM need to namespace its SpiceDB schema? | Critical for DCM's tenant isolation requirements |
+| 7 | What is the intended integration pattern for other Red Hat products (ACM, Insights)? How would DCM's usage interoperate? | Kessel's value to DCM is partly the shared source of truth across RH products |
+| 8 | Does Kessel Relations have a concept equivalent to DCM's "cross-tenant authorization"? How are trust grants between tenants modeled? | Core to DCM's resource sharing model |
+
+### 6.2 Kessel Inventory
+
+| # | Question | Why It Matters |
+|---|----------|----------------|
+| 9 | How extensible is Kessel Inventory's resource type schema? Can DCM register custom resource types? | DCM has domain-specific resource types not in Kessel's default schema |
+| 10 | What is the write model? Last-write-wins upsert, or versioned? Does Kessel Inventory support the discovered state pattern (full overwrite on each discovery cycle)? | DCM's Discovered Store is a full-replacement snapshot per discovery cycle |
+| 11 | How does Kessel Inventory integrate with Kessel Relations for auth-filtered queries? Is the integration already built, or planned? | Core to the value of using Kessel Inventory |
+| 12 | What is the data retention model? Does Kessel Inventory keep history or only current state? | DCM needs "current state" only for Discovered State; history is in DCM's Audit Store |
+| 13 | What is the API stability level for Kessel Inventory? | Same concern as #1 for Relations |
+| 14 | Is there a reference implementation of a Kessel Inventory provider for a Kubernetes/OpenShift resource type? | DCM would follow this pattern for its Service Providers |
+
+### 6.3 Joint Architecture Questions
+
+| # | Question | Why It Matters |
+|---|----------|----------------|
+| 15 | Is the Kessel project open to DCM contributing Resource Type definitions and SpiceDB schema extensions to the upstream? | Reduces divergence risk; benefits broader community |
+| 16 | How does Kessel handle sovereign data — data that must not cross jurisdictional boundaries? | Critical for DCM's sovereignty model |
+| 17 | What is the recommended pattern for bootstrapping the Kessel-DCM trust relationship? (mTLS? OIDC? Service account?) | Required for DCM's zero-trust model |
+| 18 | Does Kessel have a compatibility matrix for Red Hat platform versions (OpenShift, RHEL)? | DCM targets the same platforms |
+
+---
+
+## 7. Proposed Integration Architecture (Pending Kessel Alignment)
+
+This section describes the target architecture **conditional on positive answers to the questions in Section 6**. It should not be implemented until validated with the Kessel team.
+
+### 7.1 Kessel Relations as DCM Auth Provider
+
+```
+DCM Request Pipeline:
+  │
+  ▼ Auth Provider (Kessel Relations):
+  │   Check 1: identity verification via mTLS certificate
+  │   Check 2: CheckPermission(actor, operation, tenant/resource) via Kessel Relations gRPC
+  │            ← returns allow/deny + zookie token
+  │
+  ▼ DCM Policy Engine (internal):
+  │   Check 3: Accreditation check (DCM Accreditation Registry)
+  │   Check 4: Data/Capability Matrix (DCM Governance Matrix)
+  │   Check 5: Sovereignty check (DCM Sovereignty Zone registry)
+  │
+  ▼ All five checks pass → request proceeds to layer assembly
+```
+
+**Impact on DCM architecture:**
+- Auth Provider registration: new `auth_mode: kessel_rebac` in doc 19
+- Cross-tenant authorization DCMGroup: writes to both DCM Group Registry AND Kessel Relations tuple store
+- RBAC evaluation: replaced by Kessel Relations CheckPermission call for checks 1–2
+- Group membership sync: DCM Auth Providers (LDAP, OIDC) continue to manage authentication; group memberships are mirrored to Kessel Relations for use in permission evaluation
+
+### 7.2 Kessel Inventory as DCM Discovered State Store
+
+```
+Discovery Cycle:
+  │
+  ▼ Discovery Scheduler triggers provider discovery
+  │
+  ▼ Service Provider returns RealizedStatePayload stream
+  │   (current state in DCM Unified Data Model format)
+  │
+  ▼ Kessel Inventory data store:
+  │   Translates DCM format → Kessel Inventory Protobuf schema
+  │   Upserts to Kessel Inventory (replaces prior discovered state)
+  │
+  ▼ Drift Reconciliation Component (unchanged):
+  │   Queries Kessel Inventory for discovered state
+  │   Compares against DCM Realized State
+  │   Produces Drift Records (classification, severity, field detail)
+  │   Writes Drift Records to DCM Drift Record Store
+```
+
+**Impact on DCM architecture:**
+- Discovered State Store: implement as data store backed by Kessel Inventory
+- No changes to data model, drift detection logic, or Drift Reconciliation Component
+- Resource type mapping: DCM Resource Type Specs → Kessel Inventory resource types (new tooling required)
+
+---
+
+## 8. What Does Not Change Regardless of Integration
+
+The following DCM capabilities remain entirely in DCM regardless of how the Kessel integration develops:
+
+| Capability | Why it stays in DCM |
+|-----------|---------------------|
+| Intent State Store (GitOps) | GitOps semantics, PR workflow, immutability — not in scope for Kessel |
+| Requested State Store (write-once) | Assembled payload with full provenance — DCM-specific |
+| Realized State Store (append-only event stream) | Hash-chained, tamper-evident, field-level provenance — DCM-specific |
+| Approval gate quorum tracking | State-tracking across time — Kessel Relations answers membership, not quorum |
+| Five-check boundary model (checks 3–5) | Accreditation, data classification, sovereignty — DCM-specific |
+| Entity Relationship Graph | Operational relationships between resources — not access control |
+| Field-level provenance | Source tracking per field — not in scope for Kessel |
+| Drift detection logic and classification | DRC component — Kessel Inventory is a data source, not a drift engine |
+| Audit trail (hash chain) | Tamper-evident audit — DCM-specific requirement |
+| Resource lifecycle state machine | REQUESTED → OPERATIONAL → DECOMMISSIONED — DCM-specific |
+| Policy Engine | GateKeeper, Transformation, Recovery, Orchestration Flow policies — DCM-specific |
+| Authority Tier model | Approval routing — DCM-specific governance model |
+
+---
+
+## 9. System Policies (Proposed — Pending Validation)
+
+These policies should be reviewed and confirmed after Kessel team alignment:
+
+| Policy | Rule |
+|--------|------|
+| `KESSEL-001` | Kessel Relations, if registered as a DCM Auth Provider, handles authorization checks 1 and 2 of the five-check boundary model only. Checks 3–5 remain in DCM's Policy Engine and cannot be delegated. |
+| `KESSEL-002` | DCM's entity relationship graph (operational relationships between infrastructure resources) must never be stored in Kessel Relations. Only access-control relationships (actor→group→tenant→resource permissions) are stored in Kessel Relations. |
+| `KESSEL-003` | Kessel Inventory, if registered as a DCM data store for Discovered State, holds only ephemeral current-state snapshots. Intent, Requested, and Realized State stores remain in DCM-managed data stores. |
+| `KESSEL-004` | If Kessel Relations is unavailable, DCM enters safe-deny mode: no new requests are accepted. Fail-open behavior is not permitted under any profile. |
+| `KESSEL-005` | Zookie tokens from Kessel Relations CheckPermission responses must be threaded through the DCM request context to guarantee consistency across authorization checks within the same request. |
+| `KESSEL-006` | Cross-tenant authorization DCMGroups that are backed by Kessel Relations must be written atomically: the DCM Group Registry record and the Kessel Relations tuple must both succeed or both fail. Partial writes are treated as failures. |
+| `KESSEL-007` | DCM sovereign profile deployments require a locally-deployed Kessel instance. Cloud-hosted Kessel is not permitted for sovereign deployments. This requirement must be confirmed as feasible with the Kessel team. |
+
+---
+
+## 10. Open Items Before Integration Can Begin
+
+| # | Item | Owner | Blocking? |
+|---|------|-------|-----------|
+| 1 | Kessel team review of Section 6 questions | Kessel team | Yes |
+| 2 | Kessel Relations API stability confirmation | Kessel team | Yes |
+| 3 | Sovereign/air-gapped deployment validation | Kessel team | Yes (for sovereign profile) |
+| 4 | SpiceDB schema design review for DCM permission model | DCM + Kessel | Yes |
+| 5 | Kessel Inventory resource type extensibility confirmation | Kessel team | Yes (for inventory integration) |
+| 6 | HA/DR pattern review for production Kessel deployment | Kessel team | Yes |
+| 7 | DCM Auth Provider interface extension for `kessel_rebac` mode | DCM team | No (can design in parallel) |
+| 8 | DCM data store implementation for Kessel Inventory | DCM team | No (can design in parallel) |
+| 9 | Zookie lifecycle management design in DCM request pipeline | DCM team | No (can design in parallel) |
+| 10 | Resource type mapping: DCM Resource Type Specs → Kessel Inventory schema | DCM + Kessel | No (can design in parallel) |
+
+---
+
+*Document maintained by the DCM Project. For questions, contributions, or to schedule the Kessel alignment session see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/45-consistency-review.md b/content/docs/architecture/data-model/45-consistency-review.md
new file mode 100644
index 0000000..d4ef671
--- /dev/null
+++ b/content/docs/architecture/data-model/45-consistency-review.md
@@ -0,0 +1,257 @@
+# DCM — Consistency Review Findings
+
+**Document Status:** ✅ Complete
+**Document Type:** Review Record
+**Date:** 2026-03
+**Scope:** Full review of all 50 data model documents, 14 specifications, 11 schema files, and 4 OpenAPI specs for naming inconsistencies, field conflicts, API surface misalignments, and terminology drift.
+
+---
+
+## Summary
+
+| Category | Issues Found | Fixed | Notes |
+|----------|-------------|-------|-------|
+| API path format (AEP colon syntax) | 13 + 4 | ✅ All | admin-api-spec.md and consumer-api-spec.md had stale slash-verb paths |
+| Stale entity_type values | 2 | ✅ All | `allocated_resource` and `resource_entity` replaced with canonical values |
+| Stale threshold key format | 5 | ✅ All | `auto_approve_below`/`verified_above` in scoring doc examples replaced with named-tier format |
+| provider_id vs provider_uuid | 6 | ✅ All | Resolved to `provider_uuid` in all DCM API paths |
+| Four store naming | Variant | ⚠️ Noted | Multiple names in use — canonical list documented below |
+| Operation (LRO) shape completeness | 1 | ✅ | Polling section added to consumer spec |
+| lifecycle_state casing | Mixed | ⚠️ Noted | UPPERCASE in YAML examples, lowercase in prose — by design |
+| Resource Type naming | Mixed | ⚠️ Noted | `resource_type` (field) vs `resource_type_fqn` (format ref) — not a conflict |
+| Provider type count references | 1 | ✅ Already correct | All references say "eleven" or "11" |
+| Anti-vocabulary: 'widget' | 0 | N/A | Clean — eliminated in prior sessions |
+
+---
+
+## 1. API Path Format (AEP Colon Syntax)
+
+### 1.1 Findings
+
+The AEP colon syntax was applied to the OpenAPI YAML files in a prior session but **not propagated** to the narrative specification documents. This meant `dcm-admin-api-spec.md` and `dcm-consumer-api-spec.md` still used slash-verb paths while the normative OpenAPI specs used colon-verb paths.
+
+**Affected specs:**
+- `dcm-admin-api-spec.md` — 13 stale slash-verb paths
+- `dcm-consumer-api-spec.md` — 4 stale slash-verb paths (partially fixed but not fully)
+- `dcm-flow-gui-spec.md` — 2 stale paths (`:promote`, `:run` fixed in prior session)
+
+### 1.2 Resolution
+
+Applied the full set of colon conversions to both narrative specs. Both specs now include AEP alignment notes referencing the normative OpenAPI YAML files. Specific conversions:
+
+**Consumer spec:** `:suspend`, `:resume`, `:rehydrate`, `:rotate`, `:extend-ttl`, `:transfer`, `:bulk-decommission`, `:acknowledge`, `:revert`, `:accept`, `:reject`, `:approve`, `:read-all`
+
+**Admin spec:** `:approve`, `:reject`, `:suspend`, `:reinstate`, `:revoke-sessions`, `:reset`, `:vote`, `:rotate-credential`, `:trigger`, `:rebuild`, `:accept-degradation`, `:activate`
+
+### 1.3 Canonical Rule
+
+> Custom method paths use colon syntax: `POST /resources/{name}:verb`.
+> This applies to all narrative specs, OpenAPI YAML, and code examples.
+> Sub-resources (`.../status`, `.../stream`, `.../pending` as filtered list) keep slash notation.
+
+---
+
+## 2. Entity Type Values
+
+### 2.1 Findings
+
+Two documents used stale or incorrect values for the `entity_type` field:
+
+| Document | Stale Value | Correct Value |
+|----------|-------------|---------------|
+| `09-entity-relationships.md` | `allocated_resource` | `infrastructure_resource` (with `ownership_model: allocation`) |
+| `18-webhooks-messaging.md` | `resource_entity` | `infrastructure_resource` |
+
+The `related_entity_type: internal` and `related_entity_type: external` in doc 09 are **not** entity_type values — they are relationship scope descriptors and are correct as-is. They describe whether the related entity is managed within DCM or is an external reference.
+
+### 2.2 Resolution
+
+Both corrected in place. The `allocated_resource` correction includes a comment: `# ownership_model: allocation` to preserve the semantic intent of the original example.
+
+### 2.3 Canonical Values
+
+The three valid `entity_type` values are:
+- `infrastructure_resource` — persistent physical or virtual resource
+- `composite_resource` — compound service definition-orchestrated aggregate
+- `process_resource` — ephemeral execution (automation job, playbook)
+
+Pool resources and shared resources are `infrastructure_resource` entities with `ownership_model: whole_allocation` or `ownership_model: shareable`. There is no separate pool or shared entity type.
+
+---
+
+## 3. Scoring Model Threshold Keys
+
+### 3.1 Findings
+
+`29-scoring-model.md` contained 5 examples using the old fixed-column threshold key format (`auto_approve_below`, `verified_above`) in per-service-type override examples. The authority tier model (doc 32) replaced these with a named-tier list format in a prior session, but the scoring doc examples were not updated.
+
+**Old format (stale):**
+```yaml
+auto_approve_below: 20
+verified_above: 40
+```
+
+**Current format:**
+```yaml
+thresholds:
+  - { tier: auto,     max_score: 20 }
+  - { tier: verified, max_score: 40 }
+```
+
+### 3.2 Resolution
+
+Replaced stale threshold keys with comments pointing to the named-tier format. The SMX-008 policy row was verified as already using current terminology.
+
+---
+
+## 4. provider_id vs provider_uuid
+
+### 4.1 Findings
+
+The operator interface spec (`dcm-operator-interface-spec.md`) and provider callback auth doc (`43-provider-callback-auth.md`) used `provider_id` in some places where `provider_uuid` is the correct DCM term.
+
+The distinction is important:
+- **`provider_uuid`** — DCM-assigned UUID for the provider record. Used in all DCM API paths and payloads.
+- **`resource_id`** — Operator-assigned identifier for a specific resource instance. Used in operator-to-DCM callbacks to identify the resource being reported on.
+
+### 4.2 Resolution
+
+DCM API endpoint paths updated to use `provider_uuid` consistently:
+- `POST /api/v1/providers/{provider_uuid}/capacity`
+- Registration response field `provider_id` → `provider_uuid`
+
+`resource_id` in callback APIs is **intentionally different** from `entity_uuid`. It is the operator's own identifier for the resource (returned in the `CreateResponse`). DCM maps it to `entity_uuid` internally. This distinction is correct and remains unchanged.
+
+---
+
+## 5. Four Store Naming — Canonical Reference
+
+Multiple naming variants found across documents. The canonical names are:
+
+| Store | Canonical Name | Also Used (acceptable) | Do Not Use |
+|-------|---------------|----------------------|------------|
+| Intent State storage | **Intent Store** | DCM database (when emphasizing the implementation) | Intent State Store |
+| Requested State storage | **Requested Store** | — | Requested State Store |
+| Realized State storage | **Realized Store** | Realized State Store | Realization Store |
+| Discovered State storage | **Discovered Store** | — | Discovered State Store |
+
+No bulk renaming was performed — both "Intent Store" and "DCM database" are used accurately in different contexts (the former emphasizes the state model, the latter the implementation). The variation is acceptable context-dependent usage, not an error.
+
+---
+
+## 6. Operation (LRO) Shape
+
+### 6.1 Finding
+
+The `consumer-api-spec.md` applied LRO `Operation` responses to async endpoints but did not include a dedicated section explaining the `GET /api/v1/operations/{uuid}` polling endpoint or the complete Operation shape.
+
+### 6.2 Resolution
+
+Added an "Operations — Polling Long-Running Requests" section to `consumer-api-spec.md` covering:
+- The polling endpoint shape with in-progress, success, and failure states
+- Polling backoff guidance (1s → 2s → 5s → 10s → 30s)
+- Alternatives: webhook subscription (`request.progress_updated`), SSE stream
+
+---
+
+## 7. Lifecycle State Casing
+
+### 7.1 Finding
+
+Lifecycle state values appear in UPPERCASE in YAML examples (e.g., `lifecycle_state: OPERATIONAL`) and in lowercase in prose text (e.g., "the resource enters the operational state"). This is **by design**, not an inconsistency:
+
+- UPPERCASE in YAML/JSON — machine-readable, matches enum values in schemas
+- Lowercase in prose — natural language, matches how engineers write documentation
+
+No changes made.
+
+---
+
+## 8. resource_type vs resource_type_fqn
+
+### 8.1 Finding
+
+The JSON entity schema uses `$ref: resource_type_fqn` as a type reference (meaning the field value must be a Fully Qualified Name like `Compute.VirtualMachine`). Narrative YAML examples use `resource_type:` as the field name. This appears inconsistent but is not.
+
+`resource_type` is the **field name**. The value it holds must conform to the **FQN format** (`Category.TypeName`). The schema type reference is just documenting the format constraint.
+
+No changes made.
+
+---
+
+## 9. What Remains Acceptable (Not Fixed)
+
+These were identified but are not bugs — they are intentional or context-appropriate variation:
+
+| Item | Why It's Acceptable |
+|------|---------------------|
+| `Service Provider` (title case) vs `service_provider` (snake_case) | Title case in prose, snake_case in code/YAML — correct by context |
+| `Auth Provider` vs `auth_provider` | Same as above |
+| `Resource Type Spec` vs `Resource Type Specification` | Shortened form acceptable in prose; full form in formal definitions |
+| `Realized State` vs `realized state` | Title case for the formal concept, lowercase in general prose |
+| `related_entity_type: internal/external` in doc 09 | Not entity_type values — relationship scope descriptors, correct as-is |
+| Provider type count varies ("nine", "eleven", "11") | All refer to the same 11 types; "nine" may be a historical reference pre-two additions |
+
+---
+
+## 10. Remaining Items Requiring Action by Implementers
+
+These are not documentation issues but implementation decisions that need to be made explicit when building DCM:
+
+| Item | Decision Needed |
+|------|----------------|
+| `resource_type` field in API payloads accepts short names (`Compute.VirtualMachine`) or requires registry UUID? | Current spec allows both `resource_type` (FQN string) and `resource_type_uuid` — need to decide if UUID is required at dispatch or optional |
+| Operation polling endpoint `GET /api/v1/operations/{uuid}` — is operation status part of request status, or a separate Operation resource? | Currently: request status via `GET /api/v1/requests/{uuid}/status`; Operation via `GET operation.name`. Need to clarify if these are the same or different responses |
+| `provider_id` in operator-assigned resource IDs vs DCM `provider_uuid` — implementers must ensure they're not conflated at the API Gateway level | Code-level concern — the API Gateway maps `resource_id` (operator-assigned) to `entity_uuid` (DCM-assigned) |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+---
+
+## Second Review Pass — 2026-03
+
+A second comprehensive scan identified additional issues and confirmed no new architectural conflicts.
+
+### Additional Fixes Applied
+
+| Fix | Files Affected | Description |
+|-----|---------------|-------------|
+| Stale slash-verb paths | 14 files | Data model docs 00, 23, 24, 32, 35, 40, 42, 43 and specs dcm-admin-gui, dcm-consumer-gui, dcm-examples, dcm-registration, dcm-admin-api | All remaining colon-less custom method paths converted |
+| DELETE /api/v1/auth/session | dcm-consumer-api.yaml | Missing endpoint added to OpenAPI YAML (was in narrative spec only) |
+| GET /api/v1/operations/{uuid} | dcm-consumer-api.yaml | Operation polling endpoint added to OpenAPI YAML |
+| Missing event types | 33-event-catalog.md | 6 event types added: entity.deleted, entity.state_transition, group.deleted, group.member_added, group.member_removed, authorization.granted |
+| Prompt section numbering | DCM-AI-PROMPT.md | Duplicate section 78 resolved; sections 0–80 now sequential with no duplicates |
+
+### Confirmed Clean (No Issues)
+
+- **OpenAPI schemas**: All 4 specs (consumer/admin/operator/callback) — 0 schema conflicts
+- **Lifecycle state casing**: UPPERCASE in YAML/JSON examples, lowercase in prose — intentional
+- **`Ingress API` usage**: Two occurrences are contextually correct (infrastructure layer explanation)
+- **`resource_type` vs `resource_type_fqn`**: Field name vs format description — not a conflict
+- **`name:` vs `display_name:`**: Context-appropriate — `name:` is a property name, `display_name:` is a human label
+- **Policy type casing**: Uppercase in section headers, lowercase in code/YAML — by convention
+- **Provider type count**: All references to "eleven" or "11" provider types — consistent
+- **Realized State write authority**: Consistent across all docs — providers never write directly; DCM API Gateway is sole writer
+
+### Remaining Acceptable Variation
+
+These are not errors — they are deliberate context-dependent usage:
+
+| Pattern | Both Forms Correct | Reason |
+|---------|--------------------|--------|
+| `Service Provider` / `service_provider` | Title case in prose, snake_case in code | Convention |
+| `Auth Provider` / `auth_provider` | Same | Convention |
+| `Policy Engine` / `policy_engine` | Same | Convention |
+| Intent Store / DCM database | Both describe the same store | Different emphasis (state model vs implementation) |
+| `entity.created` / `resource.provisioned` | Different semantic levels | Entity creation vs provisioning completion are distinct events |
+
+### Implementation Decisions Still Outstanding
+
+These three items were identified in the prior review and remain open — they require implementation choices, not documentation changes:
+
+1. **`resource_type` at dispatch**: Accept FQN string (`Compute.VirtualMachine`) or require UUID? Current specs allow both; implementation must pick one canonical form.
+2. **Operation polling endpoint**: Is `GET /api/v1/operations/{uuid}` the same resource as `GET /api/v1/requests/{uuid}/status`, or a separate resource? Added to OpenAPI YAML as separate endpoint.
+3. **`resource_id` → `entity_uuid` mapping**: The API Gateway must map operator-assigned `resource_id` to DCM `entity_uuid` at the callback boundary. This is a code-level concern with no documentation gap.
+
diff --git a/content/docs/architecture/data-model/46-workload-analysis.md b/content/docs/architecture/data-model/46-workload-analysis.md
new file mode 100644
index 0000000..b25bc79
--- /dev/null
+++ b/content/docs/architecture/data-model/46-workload-analysis.md
@@ -0,0 +1,253 @@
+# DCM — Workload Analysis
+
+**Document Status:** 📋 Draft — Specification in Progress
+**Document Type:** Capability Specification
+**Related Documents:** [Ingestion Model](13-ingestion-model.md) | [Information Providers](10-information-providers.md) | [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Discovery and Drift](25-control-plane-components.md) | [Kubernetes Compatibility](../specifications/kubernetes-compatibility.md)
+
+> **AEP Alignment:** API endpoints follow [AEP](https://aep.dev) conventions.
+> Workload analysis results are delivered as Information Provider payloads
+> and stored as `process_resource_entity` instances of type `Analysis.WorkloadProfile`.
+
+---
+
+## 1. Purpose
+
+Workload Analysis is the DCM capability that actively classifies discovered resources
+by their operational characteristics — what they are, how they behave, what lifecycle
+model should apply to them, and which DCM Resource Type they best map to.
+
+It answers questions that passive discovery cannot:
+- *"This VM was discovered — is it a web server, a database, a batch processor?"*
+- *"This workload can be migrated to containers — what is its archetype?"*
+- *"This resource has no DCM UUID — what is the minimum viable Resource Type we can
+   assign it for lifecycle management?"*
+
+Workload Analysis is the bridge between **Discovered State** (what exists) and
+**Intent State** (what should be managed). Without it, brownfield ingestion stalls
+at the enrichment phase because the Tenant, Resource Type, and lifecycle ownership
+cannot be automatically determined.
+
+---
+
+## 2. Relationship to Existing Capabilities
+
+```
+Discovery (DRC)          Workload Analysis       Ingestion (doc 13)
+      │                        │                        │
+      ▼                        ▼                        ▼
+Discovered State     WorkloadProfile entity    INGESTED → ENRICHING
+(what physically      (what it IS and what      → PROMOTED → OPERATIONAL
+  exists today)        lifecycle applies)
+```
+
+**Workload Analysis is a DCM-managed process resource** (`Analysis.WorkloadProfile`)
+that fires as part of the brownfield ingestion pipeline. It is triggered by the
+Discovery Scheduler when a new resource enters Discovered State without a matching
+DCM entity in Realized State.
+
+It can also be triggered manually by a platform admin to re-classify a resource
+whose operational profile has changed (e.g., a VM that was a web server and is
+now a database).
+
+---
+
+## 3. WorkloadProfile Entity
+
+Workload Analysis produces a `process_resource_entity` of type `Analysis.WorkloadProfile`:
+
+```yaml
+workload_profile_entity:
+  entity_uuid: 
+  entity_type: process_resource
+  resource_type: Analysis.WorkloadProfile
+  lifecycle_state: OPERATIONAL       # while active; DECOMMISSIONED when superseded
+  
+  # Linked to the resource being analyzed
+  subject_entity_uuid:         # the VM, container, or other resource
+  subject_discovered_state_uuid: 
+  
+  classification:
+    resource_type_match:             # best-fit DCM Resource Type
+      primary:    Compute.VirtualMachine
+      confidence: high               # high | medium | low | undetermined
+      alternatives:
+        - resource_type: Platform.Container
+          confidence: medium
+          rationale: "Workload is containerizable per MTA assessment"
+    
+    workload_archetype:              # operational classification
+      type: web_server | database | batch_processor | message_broker |
+            api_gateway | cache | storage | monitoring | unknown
+      confidence: high
+      signals: [port_scan, process_list, resource_utilization_pattern]
+    
+    migration_readiness:             # if MTA integration is active
+      containerization_score: 7     # 1-10
+      blockers: []
+      suggested_target: Platform.KubernetesDeployment
+      mta_report_ref:           # link to MTA HTML report if available
+    
+    lifecycle_recommendation:
+      dcm_lifecycle_model: standard | stateful | ephemeral | infrastructure
+      rehydration_eligible: true
+      notes: "Application data on /data partition; OS on /; static replace eligible"
+    
+  analysis_metadata:
+    analyzed_at: 
+    analysis_version: "1.0.0"        # versioned analysis ruleset
+    information_providers_used:
+      - provider_uuid: 
+        provider_type: information_provider
+        data_types_used: [port_scan, process_list, os_metadata]
+    analyst_actor_uuid:        # null if automated; actor UUID if manual review
+```
+
+---
+
+## 4. Analysis Pipeline
+
+Workload Analysis is an Orchestration Flow Policy that fires when a discovered
+resource enters the enrichment phase:
+
+```
+discovery.new_entity_found
+  │
+  ▼ Orchestration Step 1: Create WorkloadProfile entity (INGESTED state)
+  │   Linked to discovered resource via 'operational' relationship
+  │
+  ▼ Orchestration Step 2: Gather signals from Information Providers
+  │   Port scan (network topology)
+  │   Process list (running services)
+  │   OS metadata (version, packages, mount points)
+  │   Resource utilization patterns (CPU/memory/disk I/O profile)
+  │   MTA assessment (if MTA Information Provider registered)
+  │
+  ▼ Orchestration Step 3: Apply classification ruleset (Policy Engine)
+  │   Transformation Policy: compute workload_archetype from signals
+  │   Transformation Policy: compute resource_type_match from archetype
+  │   Transformation Policy: compute migration_readiness from MTA signals
+  │   GateKeeper Policy: flag if confidence < medium for manual review
+  │
+  ▼ Orchestration Step 4: Write WorkloadProfile to Realized State
+  │   WorkloadProfile entity → OPERATIONAL
+  │
+  ▼ Orchestration Step 5: Trigger ingestion enrichment
+      WorkloadProfile classification informs:
+        - Tenant auto-assignment (if auto-assignment rules match)
+        - Resource Type assignment for the ingestion record
+        - Lifecycle model selection
+```
+
+---
+
+## 5. MTA (Migration Toolkit for Applications) Integration
+
+When the MTA Information Provider is registered, Workload Analysis invokes it
+as part of Step 2 above. MTA provides workload archetype classification and
+containerization readiness scores for discovered workloads.
+
+```yaml
+mta_information_provider_registration:
+  provider_type: information_provider
+  information_type: workload_analysis
+  display_name: "MTA — Migration Toolkit for Applications"
+  endpoint: https://mta.internal:8080/api/v1
+  
+  capabilities:
+    workload_archetypes:
+      - web_server
+      - database
+      - batch_processor
+      - message_broker
+    provides_containerization_score: true
+    provides_migration_blockers: true
+    provides_target_recommendations: true
+  
+  query_interface:
+    # MTA receives discovered state payload and returns analysis
+    input: discovered_state_payload
+    output: mta_workload_report
+    async: true
+    callback_supported: true
+```
+
+The MTA integration is the primary implementation path for Workload Analysis in
+Red Hat environments. In non-MTA environments, a custom Information Provider
+implementing the same `workload_analysis` information type can be registered.
+
+---
+
+## 6. Consumer API — Workload Analysis Endpoints
+
+```
+# Get the WorkloadProfile for a specific resource
+GET /api/v1/resources/{entity_uuid}/workload-profile
+
+Response 200:
+{
+  "workload_profile_uuid": "",
+  "subject_entity_uuid": "",
+  "classification": {
+    "resource_type_match": {
+      "primary": "Compute.VirtualMachine",
+      "confidence": "high"
+    },
+    "workload_archetype": {
+      "type": "web_server",
+      "confidence": "high"
+    },
+    "migration_readiness": {
+      "containerization_score": 7,
+      "blockers": [],
+      "suggested_target": "Platform.KubernetesDeployment"
+    },
+    "lifecycle_recommendation": {
+      "dcm_lifecycle_model": "standard",
+      "rehydration_eligible": true
+    }
+  },
+  "analyzed_at": ""
+}
+
+# Trigger a re-analysis of a resource
+POST /api/v1/resources/{entity_uuid}/workload-profile:analyze
+
+Request body:
+{
+  "reason": "Role change — web server migrated to database role",
+  "include_mta": true
+}
+
+Response 200 — returns Operation:
+{
+  "name": "/api/v1/operations/{request_uuid}",
+  "done": false,
+  "metadata": { "stage": "ANALYSIS_INITIATED", "resource_uuid": "{entity_uuid}" }
+}
+
+# List all resources with a given workload archetype (platform admin)
+GET /api/v1/admin/workload-analysis?archetype=web_server&confidence=high
+
+Response 200:
+{
+  "items": [ { "entity_uuid": "...", "resource_type": "...", "archetype": "..." } ],
+  "next_page_token": "..."
+}
+```
+
+---
+
+## 7. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `WLA-001` | Workload Analysis fires automatically for every entity entering Discovered State without a matching Realized State record. It is not optional — it is part of the brownfield ingestion pipeline. |
+| `WLA-002` | WorkloadProfile entities are versioned. When re-analysis produces a different classification, the old WorkloadProfile enters DECOMMISSIONED state and a new one is created. The chain is preserved for audit. |
+| `WLA-003` | If classification confidence is `low` or `undetermined`, the WorkloadProfile GateKeeper policy fires and the entity is routed to manual review before ingestion can proceed to PROMOTED. |
+| `WLA-004` | The MTA Information Provider is the reference implementation for workload_analysis information type in Red Hat environments. Custom implementations must provide the same output schema. |
+| `WLA-005` | Workload Analysis results are stored in Realized State as `process_resource_entity` instances. They are immutable once written — re-analysis creates a new entity, not an update. |
+| `WLA-006` | Migration readiness scores and archetype classifications are advisory — they inform human decision-making and Orchestration Flow Policies but do not automatically trigger migrations. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/47-accreditation-monitor.md b/content/docs/architecture/data-model/47-accreditation-monitor.md
new file mode 100644
index 0000000..298d1e7
--- /dev/null
+++ b/content/docs/architecture/data-model/47-accreditation-monitor.md
@@ -0,0 +1,496 @@
+# DCM Data Model — Accreditation Monitor
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Capability Specification
+**Related Documents:** [Accreditation and Authorization Matrix](26-accreditation-and-authorization-matrix.md) | [Information Providers](10-information-providers.md) | [Advanced Information Providers](21-information-providers-advanced.md) | [Standards Catalog](40-standards-catalog.md) | [Scoring Model](29-scoring-model.md) | [Event Catalog](33-event-catalog.md) | [Governance Matrix](27-governance-matrix.md)
+
+> **AEP Alignment:** API endpoints follow [AEP](https://aep.dev) conventions.
+> Custom methods use colon syntax. Async operations return `Operation` resources.
+> See `schemas/openapi/dcm-admin-api.yaml` for the normative admin API specification.
+
+---
+
+## 1. Purpose
+
+The Accreditation Monitor is a DCM **Information Provider** that continuously verifies the status of registered accreditations against authoritative external sources — compliance registries, certificate authority portals, and contract management systems. It answers the question DCM's existing accreditation lifecycle model cannot answer alone:
+
+> *"Is this accreditation still valid according to the issuing authority — not just according to the expiry date we were told?"*
+
+An accreditation can become invalid before its declared `expires_at` date: FedRAMP authorizations can be revoked mid-cycle, ISO 27001 certificates can be suspended by the certification body, CMMC certifications can be downgraded. Without external monitoring, DCM would continue treating a provider as accredited until the date it was told to check — which may be 12 months away.
+
+The Accreditation Monitor closes this gap by polling authoritative external registries on a configurable schedule and surfacing discrepancies to DCM's existing accreditation lifecycle model.
+
+---
+
+## 2. What Can Be Automated — Framework by Framework
+
+Automation depth varies significantly by framework. The Accreditation Monitor implements four verification tiers:
+
+### Tier 1 — Full External Verification (public registry API)
+
+DCM can query the authoritative registry directly. Status changes are detected without any manual intervention.
+
+| Framework | Authoritative Source | What Is Verified |
+|-----------|---------------------|-----------------|
+| **FedRAMP Moderate/High/LI-SaaS** | [marketplace.fedramp.gov/api](https://marketplace.fedramp.gov) | Authorization status (Authorized / In Process / Revoked), impact level, authorization date, annual assessment currency |
+| **StateRAMP** | [stateramp.org](https://stateramp.org) | Authorization status, status changes |
+| **CMMC 2.0** | [cyberab.org/catalog](https://cyberab.org/catalog) | Certification level (1/2/3), certification status, expiry date |
+| **IAF / ISO 27001** | [iaf.nu CertSearch](https://iaf.nu) | Certificate status (valid/suspended/withdrawn), expiry date, scope, issuing CB identity |
+
+**Required field at registration:** `external_registry_id` — the provider's identifier in the external registry (FedRAMP ID, CMMC certification number, ISO certificate number).
+
+### Tier 2 — Document Currency Verification
+
+DCM cannot query a central registry, but it can verify that the stored evidence document is current relative to the framework's required renewal cycle.
+
+| Framework | Verification Method | Currency Check |
+|-----------|---------------------|---------------|
+| **SOC 2 Type I / Type II** | Document metadata extraction from `audit_report_ref` | Report period end date must be within 12 months (configurable); examines PDF metadata or report header |
+| **PCI DSS** | QSA authorization check + AoC date | Verifies the issuing QSA firm is currently listed as authorized at PCI SSC; verifies stored AoC covers current period |
+| **ISO 27001** (when CB portal is unavailable) | Certificate document date + P3Y renewal cycle | Falls back to document-based verification if IAF CertSearch is unreachable |
+
+### Tier 3 — Contract Lifecycle Integration
+
+No external registry exists. Verification is driven by events from contract management systems.
+
+| Framework | Integration Mechanism | What Triggers Verification |
+|-----------|----------------------|--------------------------|
+| **HIPAA BAA** | Contract management webhook (DocuSign, Ironclad, Agiloft, custom) | BAA signed, amended, terminated, or approaching renewal |
+| **DoD IL2/IL4/IL5/IL6** | Manual submission + webhook on DISA action letter | Authorization letter received, amended, or revoked |
+| **Custom / Sovereign** | Manual submission + optional webhook | Platform admin triggers; or custom webhook from issuing authority |
+
+### Tier 4 — Expiry-Only Monitoring (no external verification available)
+
+No external API, no document currency check, no contract integration. DCM monitors only the declared `expires_at` date.
+
+| Framework | What Is Monitored |
+|-----------|-----------------|
+| **HIPAA BAA** (if no contract system) | Declared BAA expiry date |
+| **Internal / first_party accreditations** | Declared validity period |
+| **Self-declared** | Declared validity period |
+
+---
+
+## 3. Accreditation Record Additions
+
+The existing accreditation record (doc 26 Section 3.3) is extended with three new fields to support automated monitoring:
+
+```yaml
+accreditation:
+  # ... existing fields unchanged ...
+
+  # NEW — Verification model
+  verification:
+    tier: external_registry | document_currency | contract_webhook | expiry_only
+    # ── Tier 1 specific ──
+    registry_api:
+      provider: fedramp | stateramp | cmmc_ab | iaf_certsearch | custom
+      lookup_key:     # e.g., FedRAMP ID "FR2024-0042"
+      poll_interval: P1D                          # how often to check
+      last_checked_at: 
+      last_result: confirmed_active | status_changed | registry_unavailable | not_found
+    
+    # ── Tier 2 specific ──
+    document_check:
+      document_url: 
+      max_age: P365D                              # how old the document can be
+      date_extraction_method: pdf_metadata | report_header_parse | manual
+      last_checked_at: 
+      last_document_date: 
+    
+    # ── Tier 3 specific ──
+    contract_webhook:
+      contract_system: docusign | ironclad | agiloft | custom
+      contract_id: 
+      webhook_url: 
+      last_event_at: 
+    
+    # ── Shared ──
+    stale_after: P7D                              # how long before last_checked_at = stale
+    stale_action: warn | suspend | escalate       # what to do when stale
+    verification_failure_count: 0                 # consecutive failures; triggers escalation
+    verification_failure_threshold: 3
+```
+
+The `last_verified_at` field on the existing accreditation record (doc 26) is updated by the Accreditation Monitor on each successful verification. It remains the canonical "last confirmed active" timestamp used by the Governance Matrix and Scoring Model.
+
+---
+
+## 4. Accreditation Monitor as an Information Provider
+
+The Accreditation Monitor registers with DCM as an Information Provider with `information_type: accreditation_verification`. It is a separately deployable component — it does not require changes to the DCM control plane and can be upgraded independently.
+
+```yaml
+accreditation_monitor_registration:
+  provider_type: information_provider
+  information_type: accreditation_verification
+  display_name: "DCM Accreditation Monitor"
+  version: "1.0.0"
+  
+  # What it monitors
+  supported_tiers:
+    - external_registry
+    - document_currency
+    - contract_webhook
+    - expiry_only
+  
+  supported_registries:
+    - fedramp
+    - stateramp
+    - cmmc_ab
+    - iaf_certsearch
+    - pci_ssc_qsa      # QSA verification only
+  
+  supported_contract_systems:
+    - docusign
+    - ironclad
+    - custom_webhook
+  
+  # How it communicates results back to DCM
+  push_events: true
+  event_types:
+    - accreditation.verified           # periodic confirmation: still active
+    - accreditation.status_changed     # external registry shows different status
+    - accreditation.registry_mismatch  # external status != DCM recorded status
+    - accreditation.verification_stale # last_checked_at exceeds stale_after threshold
+    - accreditation.expiry_approaching # approaching expires_at (supplement to existing)
+    - accreditation.document_expired   # document_check: document older than max_age
+    - accreditation.contract_event     # contract_webhook: BAA signed/amended/terminated
+
+  health_check:
+    endpoint: /health
+    interval: PT5M
+```
+
+---
+
+## 5. Verification Flows
+
+### 5.1 Tier 1 — FedRAMP External Registry Verification
+
+```
+Accreditation Monitor poll cycle (default: P1D):
+  │
+  ▼ For each active Tier 1 accreditation:
+  │   Load accreditation record
+  │   Extract: framework, external_registry_id, last known status
+  │
+  ▼ Query external registry:
+  │   FedRAMP: GET marketplace.fedramp.gov/api/products?id={external_registry_id}
+  │   CMMC:    GET cyberab.org/api/certifications?cert_number={external_registry_id}
+  │   IAF:     GET iaf.nu/certsearch?cert={external_registry_id}
+  │
+  ├── Registry returns: status = Authorized, impact_level = High
+  │     Matches DCM record → no action
+  │     Update: last_checked_at, last_result: confirmed_active
+  │     Fire: accreditation.verified (urgency: low)
+  │
+  ├── Registry returns: status = In Process (was Authorized)
+  │     Status changed → MISMATCH
+  │     Fire: accreditation.status_changed (urgency: high)
+  │     Payload: {from: authorized, to: in_process, external_source: fedramp_marketplace}
+  │     DCM action: accreditation status → pending_review
+  │     Platform Admin notified — human must review and decide: suspend or retain
+  │
+  ├── Registry returns: status = Revoked
+  │     Fire: accreditation.status_changed (urgency: critical)
+  │     DCM action: accreditation status → revoked immediately
+  │     Accreditation Gap triggered for all affected providers
+  │     Recovery Policy evaluated
+  │
+  ├── Registry returns: 404 / not_found
+  │     May indicate ID change or deregistration
+  │     Fire: accreditation.registry_mismatch (urgency: high)
+  │     Increment verification_failure_count
+  │     Platform Admin notified to verify external_registry_id is correct
+  │
+  └── Registry unreachable (timeout, 5xx)
+        Update: last_result: registry_unavailable
+        Increment verification_failure_count
+        If count >= verification_failure_threshold:
+          Fire: accreditation.verification_stale (urgency: medium)
+        Do NOT change accreditation status on registry failure alone
+        (conservative: prefer false negative over false positive revocation)
+```
+
+### 5.2 Tier 2 — Document Currency Verification (SOC 2, PCI DSS AoC)
+
+```
+Verification cycle (default: P7D):
+  │
+  ▼ Fetch document from certificate_ref or audit_report_ref URL
+  │
+  ├── PDF: extract creation_date from PDF metadata
+  │         or parse report header for "Report Date: YYYY-MM-DD"
+  │
+  ├── HTML report: parse structured date field
+  │
+  └── Fallback: flag for manual review if date cannot be extracted
+  
+  ▼ Compare document date to max_age threshold (default P365D):
+  │
+  ├── Within threshold → update last_checked_at, last_document_date
+  │                      Fire: accreditation.verified (urgency: low)
+  │
+  └── Beyond threshold → Fire: accreditation.document_expired (urgency: high)
+                          Platform Admin notified: new report needed
+                          Accreditation status → pending_renewal
+```
+
+### 5.3 Tier 3 — Contract Webhook (HIPAA BAA, DoD IL)
+
+```
+Contract management system fires webhook to DCM:
+  POST /api/v1/admin/accreditations/{uuid}/contract-event
+  
+  Payload:
+  {
+    "contract_id": "",
+    "event_type": "signed | amended | terminated | renewal_due | renewed",
+    "effective_date": "",
+    "details": { ... contract-system-specific fields ... }
+  }
+  
+  DCM processes:
+  ├── signed   → accreditation status: active (if was pending)
+  ├── amended  → accreditation status: pending_review; Platform Admin notified
+  ├── terminated → accreditation status: revoked; Accreditation Gap triggered
+  ├── renewal_due → notification to Compliance Team (urgency: medium)
+  └── renewed  → accreditation status: active; expires_at updated; last_verified_at updated
+```
+
+### 5.4 Stale Verification Handling
+
+Regardless of tier, when `last_checked_at` is older than `stale_after`:
+
+```
+stale_action: warn     → Fire: accreditation.verification_stale (urgency: low)
+                          No change to accreditation status
+                          
+stale_action: suspend  → Fire: accreditation.verification_stale (urgency: high)
+                          Accreditation status → suspended
+                          Accreditation Gap triggered (gap_type: suspended)
+                          Platform Admin must manually verify and reactivate
+                          
+stale_action: escalate → Fire: accreditation.verification_stale (urgency: critical)
+                          Escalation chain notified (Compliance Team + Platform Admin)
+                          No automatic status change
+                          If not resolved within escalation_window: → suspend
+```
+
+`stale_action` defaults by profile: `warn` for dev/standard, `suspend` for prod, `escalate` for fsi/sovereign.
+
+---
+
+## 6. New Event Types (additions to doc 33)
+
+These events are added to the event catalog as domain `accreditation.*`:
+
+| Event Type | Urgency | Description | Key Payload Fields |
+|-----------|---------|-------------|-------------------|
+| `accreditation.verified` | low | Periodic external confirmation — accreditation still active | accreditation_uuid, framework, registry, checked_at |
+| `accreditation.status_changed` | high or critical | External registry shows a different status than DCM records | accreditation_uuid, framework, from_status, to_status, external_source |
+| `accreditation.registry_mismatch` | high | External registry cannot find the accreditation by its external_registry_id | accreditation_uuid, external_registry_id, registry, failure_detail |
+| `accreditation.verification_stale` | varies | last_checked_at exceeds stale_after threshold | accreditation_uuid, last_checked_at, stale_after, stale_action_taken |
+| `accreditation.document_expired` | high | Evidence document older than max_age threshold | accreditation_uuid, framework, document_url, document_date, max_age |
+| `accreditation.contract_event` | varies | Contract management webhook received | accreditation_uuid, contract_event_type, contract_id, effective_date |
+| `accreditation.expiry_approaching` | medium | Approaching expires_at within renewal_warning_before (supplement to existing TTL-based check) | accreditation_uuid, expires_at, days_remaining |
+
+---
+
+## 7. Accreditation Record Additions to doc 26
+
+The following fields are added to the accreditation record structure in doc 26 Section 3.3.
+These are non-breaking additions — existing records without these fields default to `tier: expiry_only`.
+
+```yaml
+# Additions to existing accreditation record (doc 26 Section 3.3)
+
+  verification:
+    tier: external_registry | document_currency | contract_webhook | expiry_only
+    
+    # For external_registry tier:
+    registry_api:
+      provider: fedramp | stateramp | cmmc_ab | iaf_certsearch | pci_ssc_qsa | custom
+      lookup_key:            # value to use as query key in registry
+      poll_interval: P1D             # ISO 8601 duration; how often Monitor checks
+      last_checked_at: 
+      last_result: confirmed_active | status_changed | registry_unavailable | not_found | pending
+    
+    # For document_currency tier:
+    document_check:
+      document_url:             # usually same as certificate_ref or audit_report_ref
+      max_age: P365D                 # maximum acceptable document age
+      date_extraction_method: pdf_metadata | report_header_parse | manual
+      last_checked_at: 
+      last_document_date: 
+    
+    # For contract_webhook tier:
+    contract_webhook:
+      contract_system: docusign | ironclad | agiloft | custom
+      contract_id:           # ID in the contract management system
+      webhook_configured: true | false
+      last_event_at: 
+    
+    # Shared across all tiers:
+    stale_after: P7D                 # max acceptable gap between verifications
+    stale_action: warn | suspend | escalate
+    verification_failure_count: 0
+    verification_failure_threshold: 3
+```
+
+---
+
+## 8. Impact on Scoring Model (doc 29)
+
+The Scoring Model's Signal 5 (Provider Accreditation Richness, doc 29 Section 4.5) is
+enhanced with a verification currency dimension. An accreditation that has been externally
+verified recently is worth more than one that has only ever been manually submitted.
+
+```yaml
+# Addition to accreditation_weights in doc 29:
+verification_multipliers:
+  # Applied to each accreditation's weight based on verification currency
+  external_registry_verified_within_P1D:  1.0    # full weight
+  external_registry_verified_within_P7D:  0.9    # slight discount
+  document_verified_within_P30D:          0.85
+  contract_webhook_active:                0.9
+  expiry_only_no_external_check:          0.7    # meaningful discount
+  verification_stale:                     0.4    # significant discount
+  verification_failed_threshold_reached:  0.1    # near-zero weight
+```
+
+This means a provider with a FedRAMP High accreditation that was externally verified
+yesterday scores higher in placement tie-breaking than a provider with the same
+accreditation whose verification check has been stale for 30 days.
+
+---
+
+## 9. Admin API Additions
+
+```
+# List all accreditations with their current verification status
+GET /api/v1/admin/accreditations
+  ?verification_status=stale|failed|confirmed|pending
+  &framework=fedramp_high|iso_27001|...
+  &subject_uuid={provider_uuid}
+  page_size=50&page_token=...
+
+# Trigger immediate re-verification of a specific accreditation
+POST /api/v1/admin/accreditations/{accreditation_uuid}:verify
+
+Response 200 — returns Operation:
+  {
+    "name": "/api/v1/operations/{uuid}",
+    "done": false,
+    "metadata": {
+      "stage": "VERIFICATION_INITIATED",
+      "accreditation_uuid": "{uuid}",
+      "verification_tier": "external_registry"
+    }
+  }
+
+# Register a contract webhook endpoint for a BAA or DoD IL accreditation
+POST /api/v1/admin/accreditations/{accreditation_uuid}:configure-webhook
+  {
+    "contract_system": "docusign",
+    "contract_id": "abc-def-123",
+    "webhook_secret": ""
+  }
+
+# Inbound webhook endpoint (called by contract management systems)
+POST /api/v1/admin/accreditations/{accreditation_uuid}/contract-event
+  Authorization: Bearer 
+  {
+    "contract_event_type": "terminated",
+    "effective_date": "2026-04-01",
+    "contract_id": "abc-def-123",
+    "details": {}
+  }
+```
+
+---
+
+## 10. Deployment and Configuration
+
+The Accreditation Monitor is deployed as a standalone container alongside the DCM control plane. It requires:
+
+- Network access to external registries (FedRAMP, CMMC AB, IAF CertSearch, PCI SSC)
+- Network access to the DCM API Gateway (to push accreditation events)
+- Access to `certificate_ref` and `audit_report_ref` document URLs (for Tier 2 checks)
+- Inbound webhook endpoint (for Tier 3 contract system integrations)
+
+**Air-gapped / sovereign deployments:** For deployments without external internet access, the Accreditation Monitor operates in Tier 4 (expiry-only) mode by default for all frameworks. Tier 2 checks can still work if documents are stored on internal object storage (certificate_ref points to an internal URL). Tier 3 contract webhooks work if the contract management system is internal. Tier 1 registry checks are disabled — a manual verification workflow applies instead, with platform admins periodically updating `last_verified_at` after out-of-band confirmation.
+
+```yaml
+accreditation_monitor_config:
+  # Per-registry enable/disable
+  registries:
+    fedramp:
+      enabled: true
+      poll_interval: P1D
+      timeout: PT30S
+    cmmc_ab:
+      enabled: true
+      poll_interval: P7D
+    iaf_certsearch:
+      enabled: true
+      poll_interval: P7D
+    stateramp:
+      enabled: false               # enable if state/local gov providers present
+  
+  # Document currency checks
+  document_checks:
+    enabled: true
+    default_max_age: P365D
+    extraction_timeout: PT60S
+  
+  # Air-gapped mode
+  air_gapped_mode: false
+  air_gapped_fallback: expiry_only | manual_workflow
+  
+  # Global escalation
+  global_stale_after: P7D
+  global_failure_threshold: 3
+  escalation_contact:
+    service_provider_uuid: 
+    urgency: critical
+```
+
+---
+
+## 11. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `ACM-001` | The Accreditation Monitor is the authoritative source for `last_verified_at` on accreditation records. Platform admins may update it manually only when the Monitor is unavailable or in air-gapped mode — all manual updates require a justification reason stored in the audit trail. |
+| `ACM-002` | An accreditation status change detected by the Monitor (external registry reports different status than DCM) does not automatically revoke the accreditation. It fires `accreditation.status_changed` and sets status to `pending_review`. A platform admin must confirm the change. The exception: if the external status is `Revoked` or `Terminated`, DCM immediately sets accreditation status to `revoked` without waiting for admin confirmation. |
+| `ACM-003` | Verification failure (registry unreachable, document inaccessible) does not revoke an accreditation. The Monitor increments `verification_failure_count`. At `verification_failure_threshold`, it fires `accreditation.verification_stale` and applies `stale_action`. Failure itself does not constitute a gap — only confirmed negative status does. |
+| `ACM-004` | Accreditations in `sovereign` and `fsi` profiles must have `verification.tier` declared at a level of `document_currency` or above. `expiry_only` is not permitted for sovereign/fsi accreditations unless `air_gapped_mode: true` is explicitly configured. |
+| `ACM-005` | The `verification_multipliers` in the Scoring Model (Signal 5) apply to all accreditations, including those submitted before the Accreditation Monitor was deployed. Legacy accreditations with no `last_checked_at` are treated as `verification_stale` and weighted at the stale multiplier (0.4) until the Monitor performs its first check. |
+| `ACM-006` | Inbound contract webhooks (Tier 3) must authenticate using a provider callback credential issued at accreditation configuration time. Unauthenticated webhook calls are rejected with `401 Unauthorized` and generate an audit record. |
+| `ACM-007` | All verification events (`accreditation.*`) are written to the Audit Store regardless of outcome. There are no silent verifications — every check, success or failure, has an audit record. |
+| `ACM-008` | In air-gapped mode, the Monitor operates in Tier 4 for registries it cannot reach. It does not repeatedly attempt unreachable external registries. After `air_gapped_retry_interval` (default P30D), it retries once to detect if network access has been restored. |
+
+---
+
+## 12. Relationship to Existing Architecture
+
+### doc 26 — Accreditation and Authorization Matrix
+The Accreditation Monitor extends but does not replace the accreditation lifecycle model in doc 26. The existing `proposed → active → expired/revoked` lifecycle is preserved. The Monitor adds automated transitions into `pending_review` and `pending_renewal` states, and provides the data that drives the existing `ACCREDITATION_GAP` logic.
+
+### doc 27 — Governance Matrix
+The Governance Matrix already evaluates accreditation status as part of Check 3 of the five-check boundary model. The Monitor improves the quality of that check: instead of relying solely on the declared `expires_at` date, the Governance Matrix now has access to externally verified current status via `last_verified_at` and `last_result`.
+
+### doc 29 — Scoring Model
+The `verification_multipliers` addition to Signal 5 (Provider Accreditation Richness) means placement decisions can prefer providers whose accreditations have been recently externally verified over those relying on self-declared or stale verifications. This is a conservative, progressive enhancement — it does not block placement, only refines tie-breaking.
+
+### doc 33 — Event Catalog
+Seven new `accreditation.*` events are added (Section 6 of this document). These follow the same envelope and urgency model as all other DCM events.
+
+### doc 40 — Standards Catalog
+The Accreditation Monitor is the operational implementation of the standards catalog's compliance framework entries. The standards catalog says *what* DCM recognizes; the Accreditation Monitor says *how* DCM verifies that recognition is still current.
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/48-location-topology-layers.md b/content/docs/architecture/data-model/48-location-topology-layers.md
new file mode 100644
index 0000000..c9d4702
--- /dev/null
+++ b/content/docs/architecture/data-model/48-location-topology-layers.md
@@ -0,0 +1,962 @@
+# DCM Data Model — Location Topology Layer Model
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Core Data Model Specification
+**Related Documents:** [Data Layers and Assembly](03-layering-and-versioning.md) | [Resource Type Hierarchy](05-resource-type-hierarchy.md) | [Control Plane Components](25-control-plane-components.md) | [Governance Matrix](27-governance-matrix.md) | [Scoring Model](29-scoring-model.md) | [Registry Governance](20-registry-governance.md) | [Service Catalog](05-resource-type-hierarchy.md)
+
+> **AEP Alignment:** API endpoints follow [AEP](https://aep.dev) conventions.
+> See `schemas/openapi/dcm-admin-api.yaml` for the normative admin API specification.
+
+---
+
+## 0. Pattern Context
+
+Location layers are one application of the **Reference Data Layer** pattern (doc 03,
+Section 3.7). The same pattern governs OS images, VM sizes, network zones, storage
+classes, and any other field where valid values are a governed, versioned, authority-owned
+set rather than a static list.
+
+The concepts in this document — layer instances as field constraint sources, hierarchy
+assembly, authority ownership, lifecycle governance — apply equally to all Reference Data
+Layer types. Location is specified in detail here because it has the richest internal
+hierarchy and the most complex assembly behaviour of all the standard reference data types.
+
+**How location layers appear to consumers:**
+
+In a catalog item response, the `location` field has a `layer_reference` constraint of
+type `location.data_center`. The `allowed_values` list is the set of active location
+layer instances the consumer is entitled to and that this resource type is eligible for.
+The consumer submits a layer UUID; DCM assembles the full ancestor chain.
+
+---
+
+## 1. Purpose
+
+Location topology layers are a subset of **Core Layers** (doc 03, Section 3.2) that encode
+where resources exist or may be allocated. They define the physical and logical hierarchy of
+an organization's infrastructure estate — from the broadest geographic unit down to the
+individual rack — and carry the authoritative data that governs resource placement, sovereign
+data routing, cost attribution, and capacity visibility.
+
+Every DCM resource allocation happens *somewhere*. That somewhere is not a string the
+consumer types — it is a structured, versioned, authority-owned layer. When a consumer
+selects a location via the service catalog or API, they are selecting from the registered
+location topology. DCM then assembles the appropriate location layers into the request
+payload, enriching it with all data associated with that location hierarchy.
+
+**What location topology layers answer:**
+- *Where can this resource be placed?* — the consumer selection surface
+- *What data applies at each level of that location?* — layer content injected into the payload
+- *Who is responsible for this location?* — authority and ownership model
+- *What constraints apply at this location?* — sovereignty, compliance, network, power
+- *What capacity is available here?* — provider capacity scoped to this location
+
+---
+
+## 2. Design Principles
+
+**Configurable names, standard types.** The names of topology nodes are organization-defined
+(your organization may call them "Sites" not "Data Centers"). The *types* — the classification
+of each level of the hierarchy — are standardized. Organizations configure instances of
+standard types; they can also add custom type levels where needed.
+
+**Hierarchical and composable.** Location types form a hierarchy from broadest to most
+specific. A resource allocated to a Rack inherits data from its Row, Hall, Data Center, Zone,
+Region, and Country layers — the full ancestry chain is assembled and merged.
+
+**Layer-first, not field-first.** Location is not a single `location: DC1-ZONE-A-RACK-12`
+string. It is a resolved chain of layers. Each layer in the chain carries structured data
+about that location level. The assembled payload contains the full context of the
+selected location.
+
+**Authority-owned.** Each location type has a designated owning authority (the team
+responsible for creating and maintaining instances of that type). The authority model
+is configurable but DCM ships with sensible industry defaults.
+
+**Linked to the service catalog.** Location topology nodes are exposed to consumers
+through the catalog as selection dimensions. When a consumer requests a resource,
+they select from available location nodes — filtered by their entitlements and the
+resource type's placement policies.
+
+---
+
+## 3. Standard Location Type Hierarchy
+
+DCM ships with nine standard location types. The hierarchy is ordered from broadest
+to most specific. Each type has a standard name, a short code for handles and references,
+and a set of standard data fields.
+
+```
+Level 1 — Country (CTY)
+Level 2 — Region (RGN)
+Level 3 — Zone / Availability Zone (AZ)
+Level 4 — Campus / Site (SITE)
+Level 5 — Data Center / Facility (DC)
+Level 6 — Hall / Pod / Row (HALL)      [optional]
+Level 7 — Cage / Enclosure (CAGE)     [optional]
+Level 8 — Rack (RACK)
+Level 9 — Unit / Slot (UNIT)           [optional — typically provider-managed]
+```
+
+Levels marked `[optional]` may be omitted if not relevant to an organization's estate.
+The hierarchy is still valid when levels are skipped — a Rack can be a direct child
+of a Data Center if Halls and Cages are not used.
+
+**Custom types** may be inserted at any level. For example, a Navy context might insert
+`Fleet / Ship` between Region and Data Center. Custom types follow the same format as
+standard types and are registered in the Location Type Registry.
+
+---
+
+## 4. Location Type Definitions
+
+Each standard type has a defined schema of fields. These fields become the data
+carried by location layer instances of that type.
+
+### 4.1 Country (CTY)
+
+```yaml
+location_type: country
+code: CTY
+level: 1
+
+standard_fields:
+  country_name:
+    type: string
+    required: true
+    example: "Germany"
+
+  iso_3166_1_alpha2:
+    type: string
+    pattern: '^[A-Z]{2}$'
+    required: true
+    example: "DE"
+
+  iso_3166_1_alpha3:
+    type: string
+    pattern: '^[A-Z]{3}$'
+    required: true
+    example: "DEU"
+
+  data_sovereignty_jurisdiction:
+    type: string
+    required: true
+    description: "Primary legal jurisdiction for data sovereignty"
+    example: "EU/GDPR"
+
+  regulatory_frameworks:
+    type: array
+    items: string
+    required: false
+    example: [GDPR, NIS2, eIDAS]
+
+  primary_currency:
+    type: string
+    format: ISO-4217
+    required: false
+    example: "EUR"
+
+  utc_offsets:
+    type: array
+    items: string
+    required: false
+    example: ["UTC+1", "UTC+2"]    # CET/CEST
+
+owning_authority_default: Platform Governance Team
+```
+
+### 4.2 Region (RGN)
+
+```yaml
+location_type: region
+code: RGN
+level: 2
+parent_type: country
+
+standard_fields:
+  region_name:
+    type: string
+    required: true
+    example: "EU West"
+
+  region_code:
+    type: string
+    required: true
+    example: "eu-west"
+
+  geographic_bounds:
+    type: object
+    required: false
+    description: "Approximate lat/lon bounding box"
+    properties:
+      lat_min: { type: number }
+      lat_max: { type: number }
+      lon_min: { type: number }
+      lon_max: { type: number }
+
+  primary_interconnect:
+    type: string
+    required: false
+    description: "Primary network interconnect or IXP serving this region"
+    example: "DE-CIX Frankfurt"
+
+  failover_region:
+    type: string
+    format: location-handle
+    required: false
+    description: "Handle of the designated DR failover region"
+    example: "regions/eu-north"
+
+  latency_profile:
+    type: object
+    required: false
+    properties:
+      intra_region_ms: { type: number, example: 2 }
+      to_regions:
+        type: object
+        additionalProperties: { type: number }
+        example: { "eu-north": 15, "us-east": 85 }
+
+owning_authority_default: Network Operations
+```
+
+### 4.3 Zone / Availability Zone (AZ)
+
+```yaml
+location_type: zone
+code: AZ
+level: 3
+parent_type: region
+
+standard_fields:
+  zone_name:
+    type: string
+    required: true
+    example: "EU West Zone A"
+
+  zone_code:
+    type: string
+    required: true
+    example: "eu-west-1a"
+
+  isolation_boundary:
+    type: string
+    enum: [independent_power, independent_cooling, independent_network, full]
+    required: true
+    description: "What failure domains this zone is isolated from"
+
+  high_availability_peer_zones:
+    type: array
+    items: { type: string, format: location-handle }
+    required: false
+    description: "Zones that form an HA pair with this zone"
+    example: ["zones/eu-west-1b", "zones/eu-west-1c"]
+
+  target_rpo_minutes:
+    type: integer
+    required: false
+    description: "Recovery Point Objective for resources in this zone"
+
+  target_rto_minutes:
+    type: integer
+    required: false
+    description: "Recovery Time Objective for resources in this zone"
+
+owning_authority_default: Data Center Operations
+```
+
+### 4.4 Campus / Site (SITE)
+
+```yaml
+location_type: site
+code: SITE
+level: 4
+parent_type: zone
+
+standard_fields:
+  site_name:
+    type: string
+    required: true
+    example: "Frankfurt Campus"
+
+  site_code:
+    type: string
+    required: true
+    example: "FRA-CAMPUS-01"
+
+  physical_address:
+    type: object
+    required: true
+    properties:
+      street:   { type: string }
+      city:     { type: string }
+      postal_code: { type: string }
+      country:  { type: string, format: iso-3166-1-alpha2 }
+
+  owned_or_leased:
+    type: string
+    enum: [owned, leased, colocation, shared]
+    required: true
+
+  security_tier:
+    type: integer
+    enum: [1, 2, 3, 4]
+    required: false
+    description: "Physical security tier (1=highest)"
+
+  noc_contact:
+    type: object
+    required: false
+    properties:
+      email:   { type: string }
+      phone:   { type: string }
+      escalation_url: { type: string }
+
+owning_authority_default: Facilities Management
+```
+
+### 4.5 Data Center / Facility (DC)
+
+```yaml
+location_type: data_center
+code: DC
+level: 5
+parent_type: site
+
+standard_fields:
+  dc_name:
+    type: string
+    required: true
+    example: "DC1 — Frankfurt Alpha"
+
+  dc_code:
+    type: string
+    required: true
+    example: "FRA-DC1"
+
+  tier_classification:
+    type: string
+    enum: [tier_1, tier_2, tier_3, tier_4]
+    required: false
+    description: "Uptime Institute Tier classification"
+
+  power_capacity_kw:
+    type: number
+    required: false
+    description: "Total available power in kilowatts"
+
+  cooling_capacity_kw:
+    type: number
+    required: false
+
+  pue_rating:
+    type: number
+    required: false
+    description: "Power Usage Effectiveness rating (1.0 = perfect)"
+    example: 1.35
+
+  redundancy_model:
+    type: string
+    enum: [N, N+1, 2N, 2N+1]
+    required: false
+    description: "Power and cooling redundancy model"
+
+  network_uplinks:
+    type: array
+    required: false
+    items:
+      type: object
+      properties:
+        carrier: { type: string }
+        bandwidth_gbps: { type: number }
+        redundant: { type: boolean }
+
+  on_site_contact:
+    type: object
+    required: false
+    properties:
+      role:  { type: string }
+      email: { type: string }
+      phone: { type: string }
+
+  dc_operations_team:
+    type: string
+    format: group-handle
+    required: true
+    description: "DCM group owning this data center"
+    example: "groups/dc-operations-fra"
+
+  certifications:
+    type: array
+    required: false
+    items:
+      type: object
+      properties:
+        standard: { type: string, example: "ISO 27001" }
+        expires_at: { type: string, format: date }
+
+owning_authority_default: Data Center Operations
+```
+
+### 4.6 Hall / Pod / Row (HALL) — Optional
+
+```yaml
+location_type: hall
+code: HALL
+level: 6
+parent_type: data_center
+optional: true
+
+standard_fields:
+  hall_name:
+    type: string
+    required: true
+    example: "Hall A — High Density"
+
+  hall_code:
+    type: string
+    required: true
+    example: "FRA-DC1-HALL-A"
+
+  network_segment:
+    type: string
+    required: false
+    description: "Primary network segment / VLAN for this hall"
+
+  power_phase:
+    type: string
+    required: false
+    description: "Power phase distribution (3-phase, single-phase)"
+
+  cooling_type:
+    type: string
+    enum: [air, liquid, rear_door, immersion]
+    required: false
+
+  max_rack_units:
+    type: integer
+    required: false
+    description: "Total rack units available in this hall"
+
+owning_authority_default: Data Center Operations
+```
+
+### 4.7 Cage / Enclosure (CAGE) — Optional
+
+```yaml
+location_type: cage
+code: CAGE
+level: 7
+parent_type: hall
+optional: true
+
+standard_fields:
+  cage_name:
+    type: string
+    required: true
+    example: "Cage 12 — Payments Isolated Zone"
+
+  cage_code:
+    type: string
+    required: true
+    example: "FRA-DC1-HALL-A-CAGE-12"
+
+  tenant_uuid:
+    type: string
+    format: uuid
+    required: false
+    description: "If this cage is dedicated to a specific DCM Tenant"
+
+  security_classification:
+    type: string
+    required: false
+    description: "Physical access classification for this cage"
+    example: "restricted"
+
+  access_control_system:
+    type: string
+    required: false
+    example: "Lenel S2"
+
+owning_authority_default: Data Center Operations
+```
+
+### 4.8 Rack (RACK)
+
+```yaml
+location_type: rack
+code: RACK
+level: 8
+parent_type: cage   # or hall or data_center if cage/hall levels are omitted
+
+standard_fields:
+  rack_name:
+    type: string
+    required: true
+    example: "Rack A-12-03"
+
+  rack_code:
+    type: string
+    required: true
+    example: "FRA-DC1-A-12-03"
+
+  rack_units:
+    type: integer
+    required: true
+    description: "Total rack units (U) capacity"
+    example: 42
+
+  rack_units_available:
+    type: integer
+    required: false
+    description: "Current available rack units (maintained by Data Center Operations)"
+
+  power_circuits:
+    type: array
+    required: false
+    items:
+      type: object
+      properties:
+        circuit_id: { type: string }
+        amperage:   { type: number }
+        phase:      { type: string }
+        redundant:  { type: boolean }
+
+  max_power_kw:
+    type: number
+    required: false
+    description: "Maximum power draw for this rack"
+
+  network_top_of_rack:
+    type: object
+    required: false
+    properties:
+      switch_model:    { type: string }
+      uplink_gbps:     { type: number }
+      port_count:      { type: integer }
+      vlan_range:      { type: string }
+
+  patch_panel_id:
+    type: string
+    required: false
+
+owning_authority_default: Data Center Operations
+```
+
+---
+
+## 5. Location Layer Instance Format
+
+Each location node is a **Core Layer** artifact stored in GitOps and registered in DCM.
+It follows the standard layer format (doc 03) with location-specific fields in its data block.
+
+```yaml
+layer:
+  artifact_metadata:
+    uuid: 
+    handle: "locations/dc/fra-dc1"      # standard handle pattern: locations/{type}/{code}
+    version: "1.2.0"
+    status: active
+    owned_by:
+      display_name: "Data Center Operations — Frankfurt"
+      group_handle: "groups/dc-operations-fra"
+      notification_endpoint: 
+    created_via: pr
+    created_at: 
+
+  # Layer classification
+  layer_type: core                        # always 'core' for location layers
+  location_type: data_center              # the standard type code from Section 4
+  scope: type_agnostic                    # location layers apply to all resource types
+
+  # Priority — location layers occupy a dedicated band in the priority space
+  priority:
+    value: "200.10.0"                     # see Section 7 for priority band allocation
+    label: "core.location.dc.fra-dc1"
+    category: core_location
+    rationale: "Data Center location layer for FRA-DC1"
+
+  # Hierarchy — parent location
+  location_hierarchy:
+    parent_handle: "locations/site/fra-campus-01"
+    parent_type: site
+    ancestors:
+      - handle: "locations/az/eu-west-1a"
+        type: zone
+      - handle: "locations/region/eu-west"
+        type: region
+      - handle: "locations/country/de"
+        type: country
+
+  # The location data — fields from the type definition (Section 4.5)
+  data:
+    dc_name: "DC1 — Frankfurt Alpha"
+    dc_code: "FRA-DC1"
+    tier_classification: tier_3
+    power_capacity_kw: 4000
+    pue_rating: 1.35
+    redundancy_model: "2N"
+    network_uplinks:
+      - carrier: "DE-CIX"
+        bandwidth_gbps: 100
+        redundant: true
+      - carrier: "NTT"
+        bandwidth_gbps: 100
+        redundant: true
+    dc_operations_team: "groups/dc-operations-fra"
+    certifications:
+      - standard: "ISO 27001"
+        expires_at: "2027-06-30"
+      - standard: "SOC 2 Type II"
+        expires_at: "2026-12-31"
+
+  # Sovereignty — consumed directly by the Governance Matrix
+  sovereignty:
+    zone_handle: "zones/eu-west-sovereign"
+    data_residency: EU
+    jurisdiction_codes: [DE]
+    cross_border_permitted: false
+
+  # Placement eligibility — which resource types may be placed here
+  placement:
+    eligible_resource_types: []         # empty = all resource types eligible
+    ineligible_resource_types: []       # explicit exclusions
+    max_data_classification: restricted # highest data classification accepted
+    requires_accreditations: []         # accreditations providers must hold to serve this DC
+
+  concern_tags: [location, data-center, frankfurt, eu-west, tier-3]
+```
+
+---
+
+## 6. Location Hierarchy Assembly
+
+When a consumer selects a location, DCM resolves the full ancestor chain and assembles
+all location layers into the request payload in hierarchy order (lowest precedence first —
+Country → Region → Zone → Site → Data Center → Hall → Cage → Rack).
+
+**Example: Consumer selects Rack FRA-DC1-A-12-03**
+
+```
+Layer resolution (Core Layer phase of assembly):
+
+  1. Country layer: locations/country/de
+     Injects: iso_3166_1_alpha2=DE, data_sovereignty_jurisdiction=EU/GDPR,
+              regulatory_frameworks=[GDPR, NIS2]
+
+  2. Region layer: locations/region/eu-west
+     Injects: region_code=eu-west, primary_interconnect=DE-CIX,
+              latency_profile.intra_region_ms=2
+
+  3. Zone layer: locations/az/eu-west-1a
+     Injects: zone_code=eu-west-1a, isolation_boundary=full,
+              target_rpo_minutes=15, target_rto_minutes=60
+
+  4. Site layer: locations/site/fra-campus-01
+     Injects: site_code=FRA-CAMPUS-01, physical_address={...},
+              security_tier=2
+
+  5. Data Center layer: locations/dc/fra-dc1
+     Injects: dc_code=FRA-DC1, tier_classification=tier_3,
+              network_uplinks=[...], certifications=[...]
+
+  6. Hall layer: locations/hall/fra-dc1-hall-a
+     Injects: hall_code=FRA-DC1-HALL-A, cooling_type=liquid,
+              network_segment=vlan-100
+
+  7. Rack layer: locations/rack/fra-dc1-a-12-03
+     Injects: rack_code=FRA-DC1-A-12-03, rack_units=42,
+              max_power_kw=20, network_top_of_rack={...}
+
+Assembled location context in payload:
+  location.country_code: DE
+  location.jurisdiction: EU/GDPR
+  location.regulatory_frameworks: [GDPR, NIS2]
+  location.region_code: eu-west
+  location.zone_code: eu-west-1a
+  location.dc_code: FRA-DC1
+  location.rack_code: FRA-DC1-A-12-03
+  location.sovereignty_zone: eu-west-sovereign
+  location.max_data_classification: restricted
+  location.certifications: [ISO 27001, SOC 2 Type II]
+  ... (all ancestor fields available to policies and providers)
+```
+
+Higher-precedence location layers override lower-precedence ones for the same field.
+A Rack layer declaring `max_data_classification: internal` overrides the DC layer's
+`restricted` — the most specific declaration wins.
+
+---
+
+## 7. Priority Band Allocation
+
+Location layers occupy a dedicated band in the Core Layer priority space (doc 03).
+
+```
+Priority bands for Core Location Layers:
+
+  100.xx.0 — Country layers
+  200.xx.0 — Region layers
+  300.xx.0 — Zone / Availability Zone layers
+  400.xx.0 — Site / Campus layers
+  500.xx.0 — Data Center layers
+  600.xx.0 — Hall / Pod / Row layers
+  700.xx.0 — Cage / Enclosure layers
+  800.xx.0 — Rack layers
+  900.xx.0 — Unit / Slot layers (provider-managed)
+
+  xx = sequence number within the level (01, 02, ... 99)
+  (Allows up to 99 instances at each level before a major priority change)
+```
+
+This ensures Country always has lower precedence than Region, which always has lower
+precedence than Zone, etc. The specific location is always the most specific (highest
+precedence) contributor to location data.
+
+---
+
+## 8. Consumer Selection Model
+
+Consumers do not interact with location layers directly via a `/locations` endpoint.
+Location selection is part of the **catalog item field schema**. When a consumer calls
+`GET /api/v1/catalog/{catalog_item_uuid}`, the `location` field constraint of type
+`layer_reference` includes the `allowed_values` list — the set of active location
+Data Center layer instances the consumer is entitled to and that this resource type
+is eligible for.
+
+Each entry in `allowed_values` carries the display data the GUI needs (name, code,
+zone, sovereignty, certifications, capacity status) and the layer UUID the consumer
+submits as the field value.
+
+**Consumer request:**
+
+```json
+POST /api/v1/requests
+{
+  "catalog_item_uuid": "",
+  "fields": {
+    "location": "layer-uuid-fra-dc1",     // DC-level layer UUID
+    "os_image": "layer-uuid-rhel-9-4",
+    "cpu_count": 4
+  }
+}
+```
+
+If the consumer wants to express location at a coarser level (Zone or Region rather
+than a specific DC), they submit the layer UUID of that level. DCM's Placement Engine
+refines downward to a specific DC during placement. The Placement Engine then assembles
+the full ancestor chain (Country → Region → Zone → Site → DC) into the payload.
+
+**Filtering allowed_values:**
+
+The catalog item declaration controls which location layer instances appear in
+`allowed_values` via the `filter` clause on the `layer_reference` constraint:
+
+```yaml
+# In the catalog item's field constraint declaration:
+constraint:
+  type: layer_reference
+  layer_type: location.data_center
+  filter:
+    tags: [production]              # only production DCs
+    min_tier: tier_3               # only Tier 3 and above
+    required_certifications: [iso_27001]   # only certified DCs
+```
+
+This means the Platform Team controls which DCs are eligible for each catalog item
+by configuring the filter — without changing the location layers themselves.
+
+---
+
+
+## 9. Authority and Ownership Model
+
+Each location type has a designated owning authority. The authority model determines:
+- Who can create, modify, and retire location layer instances
+- Who is notified when location layer data changes
+- Who approves capacity changes
+
+```yaml
+location_authority_model:
+
+  # Standard defaults — configurable per deployment
+  country:
+    creating_authority: Platform Governance Team
+    approval_required: true
+    approval_tier: platform_admin
+
+  region:
+    creating_authority: Network Operations
+    approval_required: true
+    approval_tier: platform_admin
+
+  zone:
+    creating_authority: Data Center Operations
+    approval_required: true
+    approval_tier: platform_admin
+
+  site:
+    creating_authority: Facilities Management
+    approval_required: true
+    approval_tier: team_lead
+
+  data_center:
+    creating_authority: Data Center Operations
+    approval_required: true
+    approval_tier: team_lead
+
+  hall:
+    creating_authority: Data Center Operations
+    approval_required: false
+    approval_tier: operator
+
+  cage:
+    creating_authority: Data Center Operations
+    approval_required: false
+    approval_tier: operator
+
+  rack:
+    creating_authority: Data Center Operations
+    approval_required: false
+    approval_tier: operator
+```
+
+All location layer changes follow the standard GitOps workflow — changes are submitted
+as PRs, reviewed by the owning authority, and merged. DCM picks up changes on the next
+policy/layer sync cycle. Location layers are **immutable once active** — a new version
+is created for any change, preserving the full history of location data over time.
+
+---
+
+## 10. Custom Location Types
+
+Organizations may define custom location types to extend the standard hierarchy. Custom
+types are registered in the Location Type Registry alongside standard types.
+
+**Example: Navy deployment with Fleet and Ship levels**
+
+```yaml
+custom_location_type:
+  type_name: fleet
+  code: FLEET
+  display_name: "Fleet"
+  level: 3.5            # inserted between Zone (3) and Site (4)
+  parent_type: zone
+  child_type: ship       # references the custom 'ship' type below
+
+  standard_fields:
+    fleet_name:      { type: string, required: true }
+    fleet_code:      { type: string, required: true }
+    command_node:    { type: string, required: false }
+    operating_area:  { type: string, required: false }
+
+  owning_authority: Fleet Operations Command
+
+---
+custom_location_type:
+  type_name: ship
+  code: SHIP
+  display_name: "Ship / Vessel"
+  level: 4.5            # inserted between Site (4) and Data Center (5)
+  parent_type: fleet
+  child_type: data_center
+
+  standard_fields:
+    vessel_name:     { type: string, required: true }
+    hull_number:     { type: string, required: true }
+    vessel_class:    { type: string, required: false }
+    home_port:       { type: string, required: false }
+    current_location_lat:  { type: number, required: false }
+    current_location_lon:  { type: number, required: false }
+    connectivity_profile:
+      type: string
+      enum: [satcom, fiber_pier, disconnected]
+      required: true
+
+  owning_authority: Fleet Data Center Operations
+```
+
+Custom type instances are created and managed exactly like standard type instances —
+GitOps PRs, owned by the designated authority, versioned and immutable.
+
+---
+
+## 11. Relationship to Placement Engine
+
+The Placement Engine (doc 25, Section 4) uses location topology data in Steps 1 and 3
+of the six-step placement algorithm:
+
+**Step 1 — Sovereignty Pre-Filter:**
+Location layers carry `sovereignty.zone_handle`. The Placement Engine eliminates any
+provider whose declared sovereignty zones do not include the zone associated with
+the requested location.
+
+**Step 3 — Capability Filter:**
+Location layers carry `placement.max_data_classification` and
+`placement.requires_accreditations`. Providers that cannot satisfy these
+location-level constraints are eliminated, even if they satisfy the global
+accreditation requirements.
+
+**Step 6 — Tie-breaking:**
+When multiple providers qualify, location-level `priority` declarations can be used
+as a tie-breaking preference (e.g., "prefer providers in the same DC over providers
+in a different DC in the same zone").
+
+Location layers also populate `location.*` fields in the assembled payload, which
+Placement policies (doc B) use in their constraint expressions:
+
+```rego
+# Example: Placement policy for PHI data
+placement if {
+    input.payload.location.jurisdiction == "EU/GDPR"
+    input.payload.location.max_data_classification == "restricted"
+    "hipaa_baa" in input.payload.location.required_accreditations
+}
+```
+
+---
+
+## 12. Location Layer Lifecycle
+
+Location layers follow the standard layer lifecycle (doc 03):
+
+```
+developing → proposed → active → deprecated → retired
+```
+
+**Special considerations for location layers:**
+
+**Decommissioning a location:** When a Data Center is being decommissioned, its location
+layer transitions to `deprecated`. During the deprecation window, the Placement Engine
+stops routing new requests to providers in that DC. Existing resources receive a
+`location.decommission_warning` notification. The layer transitions to `retired` when
+all resources have been migrated.
+
+**Location data changes:** When a DC gets a new network uplink or achieves a new
+certification, a new version of the location layer is published (minor version bump).
+The Requested State for all existing resources in that DC is not retroactively updated —
+provenance is preserved. Future requests and re-realizations will pick up the new data.
+
+**Capacity changes:** Rack-level `rack_units_available` is a mutable field — it is
+updated by Data Center Operations as capacity changes without a new version. All other
+location fields require a new version to change.
+
+---
+
+## 13. System Policies
+
+| Policy | Rule |
+|--------|------|
+| `LOC-001` | Every resource entity must have a resolved `location_uuid` at the DC level or below. Requests without a resolvable location are rejected at validation time. |
+| `LOC-002` | Location layers are Core Layers. They must not contain service-specific or provider-specific data. Location layers that include resource-type-scoped fields are invalid. |
+| `LOC-003` | The location hierarchy must be acyclic. A location node cannot be its own ancestor. DCM validates acyclicity at layer submission time. |
+| `LOC-004` | Location layer handles follow the pattern `locations/{type}/{code}`. Any location layer with a non-conforming handle is rejected at registration. |
+| `LOC-005` | When a consumer selects a location at a level above DC (e.g., Zone), the Placement Engine must resolve to a specific DC. A request may not remain at an abstract location level after dispatch. |
+| `LOC-006` | `max_data_classification` declared by a location layer is an upper bound. A request carrying data classified above the location's maximum is rejected by the Placement Engine's capability filter before provider contact. |
+| `LOC-007` | Location layer changes (new versions) are propagated to the Location Type Registry and the Service Catalog location list within the next sync cycle. Consumers see updated location data on next catalog query. |
+| `LOC-008` | Custom location types must declare their level as a decimal between the two standard levels they insert between. Level values must be unique across all registered types (standard and custom). |
+| `LOC-009` | All location layers must declare a `sovereignty.zone_handle` or explicitly declare `sovereignty: not_applicable`. A location layer with no sovereignty declaration is invalid for `standard` and above profiles. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/49-implementation-specifications.md b/content/docs/architecture/data-model/49-implementation-specifications.md
new file mode 100644
index 0000000..ab3dd14
--- /dev/null
+++ b/content/docs/architecture/data-model/49-implementation-specifications.md
@@ -0,0 +1,728 @@
+# DCM Data Model — Implementation Specifications
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Implementation Reference
+**Related Documents:** [Control Plane Components](25-control-plane-components.md) | [data stores](11-storage-providers.md) | [Universal Audit](16-universal-audit.md) | [credential management service Model](31-credential-provider-model.md) | [Deployment Redundancy](17-deployment-redundancy.md) | [Session Revocation](35-session-revocation.md)
+
+> **AEP Alignment:** API endpoints follow [AEP](https://aep.dev) conventions.
+> See `schemas/openapi/dcm-admin-api.yaml` and `dcm-consumer-api.yaml` for normative specs.
+
+---
+
+## 1. Purpose
+
+This document specifies the implementation mechanics for capabilities that are architecturally defined elsewhere but whose runtime behavior — enforcement location, algorithm, data structure — has not been fully specified. It closes implementation gaps identified in the architecture gap analysis.
+
+---
+
+## 2. Rate Limiting — Enforcement Implementation
+
+Rate limiting is defined at the interface level in the Consumer API Specification (§1.6) and the Admin API. This section specifies *how* it is enforced.
+
+### 2.1 Enforcement Location
+
+Rate limiting is enforced by the **API Gateway** component — the single ingress point for all consumer and admin API traffic. It is enforced before the request reaches any pipeline component. The Request Orchestrator never sees rate-limited requests.
+
+Rate limiting is **not** enforced at the network layer (load balancer) or application layer (Request Payload Processor). A single enforcement point at the API Gateway ensures:
+- Consistent limits across all consumer paths (Web UI, direct API, CI/CD)
+- No rate limit bypass via internal component calls
+- Single source of rate limit state for accurate tracking
+
+### 2.2 Token Bucket Algorithm
+
+DCM uses the **token bucket** algorithm with a per-actor bucket:
+
+```
+Actor makes request:
+  │
+  ▼ API Gateway looks up actor_uuid in rate limit store
+  │   (in-memory cache backed by a fast PostgreSQL store contract)
+  │
+  ▼ Current bucket state:
+  │   tokens_remaining: 
+  │   last_refill_at: 
+  │
+  ▼ Refill calculation:
+  │   elapsed_seconds = now - last_refill_at
+  │   tokens_to_add = elapsed_seconds × (rate_limit / 60)
+  │   tokens_remaining = min(bucket_max, tokens_remaining + tokens_to_add)
+  │   last_refill_at = now
+  │
+  ▼ Token check:
+  ├── tokens_remaining >= 1:
+  │     tokens_remaining -= 1
+  │     Request proceeds
+  │     Response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
+  │
+  └── tokens_remaining < 1:
+        Request rejected: 429 Too Many Requests
+        Response headers: Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
+        Audit record written: rate_limit_exceeded
+```
+
+### 2.3 Bucket Parameters by Profile
+
+| Profile | Rate (req/min) | Burst Max | Bucket Max |
+|---------|---------------|-----------|------------|
+| `minimal` | 60 | 20 | 80 |
+| `dev` | 120 | 40 | 160 |
+| `standard` | 300 | 100 | 400 |
+| `prod` | 600 | 200 | 800 |
+| `fsi` | 600 | 200 | 800 |
+| `sovereign` | 600 | 200 | 800 |
+
+System components (service accounts, provider callbacks) use `prod` bucket parameters regardless of profile.
+
+### 2.4 Rate Limit State Store
+
+The rate limit state is stored in a **dedicated in-memory cache** backed by a fast data store:
+- Cache TTL: 2× the rate limit window (120 seconds for 60 req/min rate)
+- PostgreSQL store contract: key-value (Redis or equivalent)
+- Consistency: eventual — brief over-counting tolerated to avoid distributed lock overhead
+- Cross-replica sharing: rate limit state is shared across all API Gateway replicas via the backing store
+
+### 2.5 Exemptions
+
+The following are exempt from consumer rate limits:
+- Admin API calls (separate rate limit bucket, 3× profile limit)
+- Provider callback endpoints (authenticated via provider callback credential; separate per-provider bucket)
+- Internal DCM component calls (authenticated via mTLS + interaction credential; not rate limited)
+- Health check endpoints (`/livez`, `/readyz`, `/metrics`)
+
+### 2.6 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `RLM-001` | Rate limiting is enforced at the API Gateway. No other component enforces rate limits. |
+| `RLM-002` | Rate limit buckets are per authenticated actor (actor_uuid). Unauthenticated requests are rejected at auth before reaching the rate limiter. |
+| `RLM-003` | All rate limit rejections produce an audit record with actor_uuid, endpoint, and timestamp. |
+| `RLM-004` | Rate limit parameters are governed by the active Profile. Operators may increase but not decrease profile-defined limits. |
+| `RLM-005` | Rate limit state is not persisted across API Gateway restarts. Buckets refill from empty after restart — brief over-serving is acceptable. |
+
+---
+
+## 3. Audit Log Hash Chain — Verification Schedule and Implementation
+
+The hash chain structure is defined in [Universal Audit](16-universal-audit.md) §8. This section specifies the verification schedule, triggering component, and response protocol.
+
+### 3.1 Hash Computation
+
+Each audit record's `record_hash` is computed as:
+
+```
+record_hash = SHA-256(
+    record_uuid ||
+    record_timestamp ||
+    entity_uuid ||
+    action ||
+    actor.immediate.uuid ||
+    subject_handle ||
+    chain_sequence ||
+    previous_record_hash
+)
+
+Where || denotes canonical concatenation with a field separator (0x1F — ASCII unit separator).
+The hash is stored as a lowercase hex string.
+```
+
+The `previous_record_hash` for the first record in an entity's chain is `SHA-256("GENESIS")` — a known constant, not null.
+
+### 3.2 Verification Schedule
+
+Hash chain verification runs on two schedules:
+
+**Continuous verification (per-write):**
+Every audit record write triggers an immediate verification of that record against its predecessor. This catches chain breaks at write time — before the record is committed. A write that would break the chain is rejected and triggers `audit.chain_integrity_alert`.
+
+**Periodic batch verification (scheduled):**
+The Audit component runs a full-chain verification sweep on a profile-governed schedule:
+
+| Profile | Sweep interval | Scope per sweep |
+|---------|---------------|-----------------|
+| `dev` | P7D | All entities modified in the last 7 days |
+| `standard` | P1D | All entities modified in the last 24 hours |
+| `prod` | PT12H | All entities modified in the last 12 hours |
+| `fsi` | PT6H | All entities + random 5% sample of all-time records |
+| `sovereign` | PT1H | All entities + random 10% sample of all-time records |
+
+### 3.3 Owning Component
+
+Hash chain verification is owned by the **Audit component** — the same component that writes audit records. It is not a separate service. The Audit component runs verification as a background goroutine with no external trigger required.
+
+For data store implementations: the Audit Store must support ordered range queries by `(entity_uuid, chain_sequence)` to enable efficient sweep verification.
+
+### 3.4 Breach Response Protocol
+
+```
+Chain break detected (during write-time or sweep verification):
+  │
+  ▼ Affected records flagged: integrity_status = chain_break
+  │   Break point: chain_sequence N where hash mismatch occurs
+  │   All records with chain_sequence > N for this entity: integrity_status = unverified
+  │
+  ▼ audit.chain_integrity_alert event fired (urgency: critical, non-suppressable)
+  │   payload: {entity_uuid, entity_type, break_at_sequence, break_detected_at, sweep_type}
+  │
+  ▼ Notifications dispatched:
+  │   → Platform Admin (urgency: critical)
+  │   → Security team (if configured in notification routing)
+  │
+  ▼ Affected entity flagged in audit dashboard
+  │   Consumer-visible: "Audit integrity alert — contact platform admin"
+  │
+  └── Human investigation required:
+        Normal resolution paths:
+        - store failure caused write corruption → data store replacement
+        - Clock skew between replicas caused ordering issue → Non-malicious; document and reseal
+        - Administrative error (direct DB edit) → Incident report, access review
+        - Malicious tampering → Security incident declared
+```
+
+### 3.5 Chain Resealing
+
+After a chain break is investigated and root cause documented, a platform admin may reseal the chain:
+
+```
+POST /api/v1/admin/audit/entities/{entity_uuid}:reseal-chain
+  {
+    "investigation_reference": "INC-2026-042",
+    "root_cause": "storage_failure",
+    "resolution_notes": "PostgreSQL WAL corruption during storage migration"
+  }
+
+Response:
+  {
+    "entity_uuid": "",
+    "chain_resealed_at": "",
+    "records_affected": 7,
+    "new_chain_anchor": "",
+    "audit_record_uuid": ""
+  }
+```
+
+The reseal itself produces an audit record that references the investigation. Chain integrity is restored from the reseal point forward.
+
+---
+
+## 4. Multi-Tenancy at the Storage Layer
+
+Tenant isolation in DCM is enforced at the data model level (every entity carries `tenant_uuid`) and at the API level (all consumer endpoints are scoped to the authenticated actor's tenant). This section specifies the storage-layer enforcement mechanisms.
+
+### 4.1 Isolation Strategy by Store Type
+
+| Store Type | Isolation Strategy | Implementation Notes |
+|-----------|-------------------|---------------------|
+| **DCM database** (Intent/Requested State) | Directory namespace per tenant | `/tenants/{tenant_uuid}/intents/`, `/tenants/{tenant_uuid}/requests/` — Git ACLs enforce read/write scope |
+| **pipeline_events table** (Audit) | Separate stream per tenant | `dcm.audit.{tenant_uuid}` stream; Kafka topic ACLs restrict producer/consumer access |
+| **realized data domain** (Realized State) | Row-level filter + column-level encryption | `tenant_uuid` column indexed; all queries mandatory-include `WHERE tenant_uuid = ?`; tenant-scoped encryption key |
+| **Search Index** | Index namespace per tenant | Separate index prefix `tenant_{uuid}_*`; query routing enforces tenant scope |
+| **Rate Limit Cache** | Key-namespaced per actor (actor carries tenant context) | `rl:{tenant_uuid}:{actor_uuid}` key structure |
+
+### 4.2 realized data domain — Row-Level Security Implementation
+
+The realized data domain (Realized State) uses row-level security as the primary isolation mechanism:
+
+```sql
+-- PostgreSQL row-level security policy
+CREATE POLICY tenant_isolation ON realized_state_records
+  USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid);
+
+-- Every connection sets tenant context before any query:
+SET LOCAL dcm.current_tenant_uuid = '';
+
+-- This makes it impossible to query across tenant boundaries,
+-- even with direct database access using the application credential.
+-- Platform admin access uses a separate role without the RLS policy.
+```
+
+### 4.3 Tenant-Scoped Encryption
+
+For `fsi` and `sovereign` profiles, realized state records are encrypted at rest using a per-tenant encryption key:
+
+```
+Tenant provisioned:
+  │
+  ▼ credential management service generates tenant encryption key (AES-256-GCM)
+  │   Key stored in: credential management service (e.g., Vault)
+  │   Key reference stored in: Tenant record as tenant_encryption_key_ref
+  │
+  ▼ On write to realized data domain:
+  │   API Gateway fetches tenant encryption key
+  │   Payload encrypted with tenant key before storage
+  │   data store stores ciphertext only
+  │
+  ▼ On read from realized data domain:
+  │   API Gateway fetches tenant encryption key
+  │   Decrypts payload in memory
+  │   Plaintext never written to data store logs
+  │
+  ▼ Tenant decommission:
+      Tenant encryption key revoked in credential management service
+      All tenant data becomes unreadable without external recovery
+      This is the cryptographic equivalent of data deletion
+```
+
+### 4.4 Cross-Tenant Query Prevention
+
+Platform admin endpoints that query across tenants use a separate database role with explicit permission grants — they do not bypass RLS, they use a role that has cross-tenant read permission with full audit logging. The principle is: cross-tenant reads are possible only through intentional, audited, privileged operations.
+
+### 4.5 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `STI-001` | Every query to a tenant-scoped store must include `tenant_uuid` as a mandatory predicate. Queries without tenant scope are rejected by the storage layer. |
+| `STI-002` | Row-level security is enabled on all relational snapshot stores. Disabling RLS requires platform admin action and produces an audit record. |
+| `STI-003` | For `fsi` and `sovereign` profiles, tenant-scoped encryption is mandatory. Key rotation is performed on a profile-governed schedule (P90D for fsi; P30D for sovereign). |
+| `STI-004` | data store implementations must declare their tenant isolation strategy at registration. DCM validates the declared strategy against the active profile's isolation requirements during the registration approval pipeline. |
+
+---
+
+## 5. Cross-Region Data Replication
+
+DCM's multi-region deployment model is specified in [Deployment Redundancy](17-deployment-redundancy.md). This section specifies the replication mechanics and sovereignty enforcement at the replication layer.
+
+### 5.1 What Replicates Where
+
+| Store Type | Replication Model | Sovereignty Constraint |
+|-----------|------------------|----------------------|
+| **DCM database** (Intent State) | Git push/pull — upstream-downstream replication | Intent records tagged with `sovereignty_zone`; replicated only to stores within the declared zone |
+| **pipeline_events table** (Audit) | Stream mirroring with lag monitoring | Audit records replicated to all authorized regions; cross-sovereignty replication requires explicit consent |
+| **realized data domain** (Realized State) | Synchronous within-zone; async cross-zone with consent | `sovereignty_zone` on entity governs which regions may hold a copy |
+| **Search Index** | Async replication; eventual consistency acceptable | Same sovereignty rules as realized data domain |
+
+### 5.2 Sovereignty-Aware Replication
+
+Every entity carries `sovereignty_zone` declarations that constrain which data store instances may hold copies:
+
+```yaml
+entity:
+  entity_uuid: 
+  sovereignty_zones:
+    - zone_id: EU-WEST
+      data_classifications: [restricted, phi]   # these classifications must stay in EU-WEST
+    - zone_id: "*"
+      data_classifications: [internal]           # internal data may replicate anywhere
+```
+
+The replication controller evaluates `sovereignty_zones` before routing any replication event. Replication to a non-authorized region for a given data classification is blocked at the replication layer — the storage provider receives a `SOVEREIGNTY_VIOLATION` rejection.
+
+### 5.3 Replication Lag Monitoring
+
+```
+data store declares: max_replication_lag: PT30S
+
+DCM monitoring:
+  Every PT10S: measure replication lag across all replica pairs
+  
+  If lag > max_replication_lag:
+    storage.replication_lag_exceeded event (urgency: medium)
+    
+  If lag > 5 × max_replication_lag:
+    storage.replication_degraded event (urgency: high)
+    Affected region marked: capacity_status = degraded
+    New requests avoid degraded region for placement
+    
+  If replica unreachable:
+    storage.replica_unavailable event (urgency: critical)
+    Affected region marked: capacity_status = unavailable
+    Requests that require this region: held pending recovery
+```
+
+### 5.4 Conflict Resolution
+
+DCM uses a **last-write-wins with causality tracking** model for cross-region conflicts:
+
+- All writes carry a vector clock `{region_id: sequence_number}`
+- Concurrent writes (same entity, different regions) are detected by vector clock comparison
+- Resolution: the write with higher aggregate sequence number wins
+- Losing write: preserved as a `conflict_record` in the Audit Store (never silently dropped)
+- Platform admin notified of conflicts above a configurable threshold
+
+---
+
+## 6. Secret Zero — Initial Credential Bootstrap
+
+The bootstrap sequence is specified in [Deployment Redundancy](17-deployment-redundancy.md) §6. This section specifies the credential bootstrap specifically — how DCM components authenticate to each other before the credential management service is running.
+
+### 6.1 The Bootstrap Credential Problem
+
+At day-0, no credential management service exists. DCM components need credentials to communicate. The resolution is a **declarative bootstrap manifest** that contains one-time bootstrap credentials, plus a mandatory rotation on first successful startup.
+
+### 6.2 Bootstrap Sequence — Credential Perspective
+
+```
+1. Bootstrap manifest contains:
+   bootstrap_credentials:
+     internal_ca:
+       cert_pem: 
+       key_pem:    # sealed with bootstrap passphrase
+       
+     bootstrap_admin:
+       username: bootstrap-admin
+       password_hash:   # operator sets this
+       
+     component_credentials:
+       # Pre-shared credentials for component-to-component auth
+       # until mTLS internal CA is operational
+       api_gateway:    {shared_secret: }
+       orchestrator:   {shared_secret: }
+       policy_engine:  {shared_secret: }
+       audit:          {shared_secret: }
+
+2. Bootstrap DCM starts:
+   - Internal CA initialized from bootstrap_credentials.internal_ca
+   - Components issued mTLS certificates from Internal CA
+   - Pre-shared secrets replaced by mTLS certificates on first successful CA handshake
+   - Pre-shared secrets deleted from memory and manifest after replacement
+
+3. credential management service starts:
+   - Bootstrapped with Internal CA certificate (trusts DCM's CA)
+   - Registered as the primary credential management service via bootstrap admin credential
+   - Takes ownership of internal CA key management
+   - Internal CA private key: transferred to credential management service, deleted from bootstrap manifest
+
+4. Bootstrap admin credential rotation (BOOT-002 — mandatory):
+   - Bootstrap admin password must be rotated on first login
+   - New credential issued by credential management service (not the bootstrap manifest)
+   - Old password hash deleted from manifest
+   - Manifest sealed: no more secrets, only configuration
+
+5. Bootstrap manifest after completion:
+   - Contains only: DCM deployment configuration, Git remote, profile
+   - No secrets remain in the manifest
+   - Manifest committed to Git (now safe, secret-free)
+```
+
+### 6.3 Air-Gapped Bootstrap
+
+For sovereign/air-gapped deployments where the credential management service requires network access to an external vault:
+
+```
+Option A — Embedded credential management service:
+  Use a locally-running credential management service (e.g., HashiCorp Vault in dev mode)
+  bootstrapped from the bootstrap manifest.
+  Upgrade to production Vault config post-bootstrap.
+
+Option B — Operator-held keys:
+  Bootstrap manifest contains encrypted key material.
+  Operator provides passphrase at bootstrap time via stdin or hardware token.
+  Keys are never stored unencrypted at rest.
+  
+Option C — HSM-backed bootstrap:
+  Internal CA private key is generated inside an HSM.
+  Bootstrap manifest contains only the HSM endpoint and slot reference.
+  Requires HSM to be available before DCM bootstrap begins.
+```
+
+### 6.4 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `BOOT-001` | The bootstrap manifest must not contain secrets after bootstrap completion. Any secret that persists in the manifest after first successful startup is a security violation. |
+| `BOOT-002` | The bootstrap admin credential must be rotated on first login. DCM enforces this — the bootstrap admin account is locked from normal use until rotation is complete. |
+| `BOOT-003` | Pre-shared component credentials must be replaced by mTLS certificates within PT5M of Internal CA startup. Any component still using pre-shared secrets after this window generates a security alert. |
+| `BOOT-004` | The Internal CA private key must be transferred to the credential management service on credential management service registration. The key must not remain in any component's memory or storage after transfer is confirmed. |
+
+---
+
+## 7. Ownership Ambiguities — Resolved
+
+### 7.1 Who Issues `operation_uuid`?
+
+**Decision: The API Gateway issues `operation_uuid` at request ingress.**
+
+Rationale: The API Gateway is the component that receives the POST request and must return the Operation response immediately. It assigns the UUID synchronously before any pipeline processing begins. The `operation_uuid` equals the `request_uuid` — they are the same UUID, assigned at ingress.
+
+```
+Consumer: POST /api/v1/requests {...}
+
+API Gateway:
+  1. Authenticates consumer (checks session token)
+  2. Assigns request_uuid = operation_uuid = UUID4()  ← here
+  3. Writes initial request record to Intent Store (status: INITIATED)
+  4. Publishes request.initiated event to Request Orchestrator (with request_uuid)
+  5. Returns Operation{name: /api/v1/operations/{request_uuid}, done: false}
+
+Request Orchestrator:
+  Receives request.initiated event with request_uuid
+  Uses the already-assigned request_uuid throughout the pipeline
+  Never assigns a new UUID
+```
+
+The Operation resource lives in a fast-queryable store owned by the API Gateway. When the pipeline progresses, the Request Orchestrator updates the Operation status by writing to this store (it has write access; the API Gateway reads from it for GET /api/v1/operations/{uuid} responses).
+
+### 7.2 Who Owns the Credential Revocation Registry?
+
+**Decision: The credential management service owns the Credential Revocation Registry.**
+
+Rationale: The credential management service is the authoritative source of credential lifecycle state. It issues credentials, rotates them, and revokes them. The revocation registry is a projection of that lifecycle state optimized for fast lookup.
+
+```
+Credential Revocation Registry:
+  Owner: credential management service
+  Storage: dedicated fast cache (Redis or equivalent)
+  Key structure: credential_uuid → {revoked_at, revocation_reason, effective_at}
+  TTL: max(credential_ttl, P90D)  — persists at minimum 90 days after revocation
+
+Access model:
+  Write: credential management service (on revocation event)
+  Read:  All DCM components (via credential management service query API)
+         OR via local cache synced from credential management service push events
+
+Cache sync protocol:
+  credential management service publishes: credential.revoked event (Message Bus)
+  All subscribed components update local revocation cache
+  Cache TTL: PT1M standard; PT30S fsi/sovereign
+  On cache miss: component queries credential management service directly (not the cache)
+
+Session Revocation Registry: separate, owned by the Auth component
+  (Session revocation is distinct from credential revocation)
+```
+
+---
+
+## 8. Security Posture Specifications
+
+### 8.1 Threat Model — Attack Surface Summary
+
+DCM's attack surface has five distinct boundaries. Each boundary has a specific trust model and mitigation set.
+
+**Boundary 1 — Consumer Ingress (Web UI, Consumer API)**
+- Threat: Credential theft / session hijacking
+- Mitigations: mTLS optional at consumer boundary; bearer tokens with short TTL (PT1H standard); session revocation registry checked on every request; rate limiting at API Gateway
+- Threat: Tenant escape (accessing another tenant's data)
+- Mitigations: All queries mandatory-include tenant_uuid; row-level security on storage; Governance Matrix enforced before any read
+
+**Boundary 2 — Provider Interface (Operator Interface, Callback API)**
+- Threat: Provider impersonation (malicious actor claims to be a legitimate provider)
+- Mitigations: mTLS required at provider boundary; provider callback credential required for callbacks; API Gateway validates dcm_entity_uuid in every callback against provider's registered entity scope
+- Threat: Malicious provider payload (provider sends crafted Realized State payload)
+- Mitigations: Realized State payloads validated against Resource Type Specification schema on receipt; GateKeeper policies evaluate provider-supplied data before it enters DCM state
+
+**Boundary 3 — Admin Interface (Admin API)**
+- Threat: Unauthorized platform admin action
+- Mitigations: Authority Tier model enforces multi-tier approval for high-impact actions; all admin actions produce non-suppressable audit records; emergency admin access (break-glass) triggers immediate security notification
+- Threat: Configuration injection via GitOps
+- Mitigations: All GitOps PRs require domain-appropriate review before merge; policy contributions enter shadow mode before activation; GateKeeper policies validate all contributions at submission
+
+**Boundary 4 — Internal Component Communication**
+- Threat: Component impersonation (compromised component issues requests as another)
+- Mitigations: mTLS with Internal CA for all component-to-component calls; interaction credentials checked on every call; Credential Revocation Registry queried on credential use
+- Threat: Lateral movement after component compromise
+- Mitigations: Each component holds minimum-scope interaction credentials; no component has write access to stores it does not own; audit records cannot be deleted by any component
+
+**Boundary 5 — Storage Layer**
+- Threat: Direct database access bypassing application controls
+- Mitigations: Row-level security enforces tenant isolation even with direct DB access using application credentials; platform admin credentials are separate, audited, and require MFA; data store provenance emission means all direct writes are detectable
+
+**Highest-risk paths (not mitigated by single control):**
+1. credential management service compromise → cascading trust failure. Mitigation: credential management service is air-gapped from consumer traffic; HSM-backed key storage for sovereign profiles; separate backup credential authority.
+2. Internal CA compromise → all component trust fails. Mitigation: CA private key held only in credential management service (HSM-backed for fsi/sovereign); CA certificate rotation procedure documented.
+
+### 8.2 Supply Chain Security
+
+**Provider OpenAPI Spec Signing:**
+All Service Provider OpenAPI specifications submitted at registration must be signed using the provider's private key (corresponding to the public key in their mTLS certificate). DCM verifies the signature before the spec is processed. Unsigned specs are rejected with `SPEC_UNSIGNED` at GATE-SP-01.
+
+**Operator Container Image Provenance:**
+The DCM reference implementation containers are signed using Sigstore (Cosign). Deployment manifests declare the expected image digest. Any container running a different digest triggers drift detection on DCM's own deployment.
+
+**DCM database Secrets Scanning:**
+All content committed to DCM's GitOps stores passes through a secrets scanner before being accepted. The scanner checks for:
+- High-entropy strings matching known secret patterns (API keys, tokens, private keys)
+- Known credential formats (AWS access keys, GitHub PATs, JWT secrets)
+- PEM-encoded private key blocks
+
+A commit containing detected secrets is rejected with `SECRETS_DETECTED` and an audit record is written. The committing actor is notified.
+
+**SBOM Declaration:**
+Service Providers must declare a Software Bill of Materials reference at registration (optional for Tier 1 `dev` profiles; required for `fsi` and `sovereign`). The SBOM reference is stored in the provider record and included in accreditation evidence.
+
+### 8.3 System Policies
+
+| Policy | Rule |
+|--------|------|
+| `SEC-001` | All provider OpenAPI specs submitted at registration must be signed. Signature verification is performed at GATE-SP-01. |
+| `SEC-002` | DCM GitOps stores enforce secrets scanning on all commits. Commits with detected secrets are rejected. |
+| `SEC-003` | For `fsi` and `sovereign` profiles, SBOM declaration is mandatory for all Service Providers before activation. |
+| `SEC-004` | The Internal CA private key must be stored in an HSM for `sovereign` profile deployments. Software-only key storage is not permitted at sovereign profile. |
+| `SEC-005` | Any direct database write to a DCM store that bypasses the application layer is detectable via data store provenance emission. Detection triggers `audit.chain_integrity_alert` for affected records. |
+
+---
+
+## 9. Experience Gap Specifications
+
+### 9.1 New Tenant Onboarding Flow
+
+```
+Platform Admin initiates tenant creation:
+  POST /api/v1/admin/tenants
+  {
+    "display_name": "Payments Platform",
+    "handle": "payments-platform",
+    "group_class": "tenant_boundary",
+    "initial_quota_profile": "standard",
+    "billing_contact": "payments-ops@corp.example.com",
+    "data_classifications_permitted": ["internal", "restricted"],
+    "sovereignty_zones": ["EU-WEST"]
+  }
+
+DCM auto-provisions:
+  1. Tenant entity created (tenant_uuid assigned)
+  2. Default resource groups created:
+     - payments-platform/default (general resources)
+     - payments-platform/admins (tenant admin group)
+  3. Initial quota applied per quota_profile declaration
+  4. Tenant admin actor created (if initial_admin_email provided):
+     - Actor record created
+     - Welcome notification dispatched with first-login credential
+  5. Tenant Git namespace provisioned in GitOps store:
+     - /tenants/payments-platform/ directory created
+     - Initial tenant-scope policy stubs committed (shadow mode)
+  6. Search index namespace initialized
+  7. Audit stream created: dcm.audit.{tenant_uuid}
+
+Tenant admin completes setup:
+  1. First login → mandatory credential rotation (BOOT-002 equivalent)
+  2. Configure Auth Provider (or inherit platform default)
+  3. Add tenant members (invite by email or LDAP group mapping)
+  4. Review and activate initial policy stubs
+  5. Submit first service request (onboarding validation complete)
+
+Onboarding event sequence:
+  tenant.created → Platform Admin
+  tenant.member_added × N → new members (welcome email)
+  tenant.quota_configured → Platform Admin
+  tenant.onboarding_complete → Platform Admin + Tenant Admin
+  (fired when first OPERATIONAL entity exists in the tenant)
+```
+
+### 9.2 Pre-Request Cost Estimation UX
+
+The consumer experience for cost estimation before committing a request:
+
+```
+Step 1: Consumer browses catalog
+  GET /api/v1/catalog/{catalog_item_uuid}
+  Response includes: cost_estimate: {monthly_usd: 45.00, basis: "declared_static"}
+
+Step 2: Consumer configures request fields (e.g., selects VM size)
+  POST /api/v1/cost/estimate
+  {
+    "catalog_item_uuid": "",
+    "fields": {"cpu": 8, "ram_gb": 32, "storage_gb": 200, "environment": "prod"}
+  }
+  Response:
+  {
+    "estimated_monthly_usd": 187.50,
+    "cost_basis": "dynamic",
+    "cost_breakdown": [
+      {"component": "compute", "monthly_usd": 120.00},
+      {"component": "storage", "monthly_usd": 40.00},
+      {"component": "network_egress", "monthly_usd": 27.50}
+    ],
+    "disclaimer": "Estimate based on declared provider rates. Actual costs may vary.",
+    "provider_uuid": null  // not yet placed; estimate is across eligible providers
+  }
+
+Step 3: Consumer submits request with dry_run: true (optional pre-flight)
+  POST /api/v1/requests
+  {
+    "catalog_item_uuid": "",
+    "fields": {...},
+    "dry_run": true    // evaluate policy and placement; do not dispatch
+  }
+  Response: Operation with metadata.dry_run_result:
+  {
+    "policy_result": "PASS",
+    "placement_result": {
+      "selected_provider": "eu-west-prod-1",
+      "cost_at_selected_provider": 182.00
+    },
+    "gatekeeper_gates": [],
+    "warnings": ["Storage class 'premium' requested; 'standard' also eligible at $35.00/mo"]
+  }
+
+Step 4: Consumer submits without dry_run → actual request
+```
+
+### 9.3 Provider Sandbox / Test Mode
+
+Providers can register in sandbox mode for development and certification testing without affecting production routing:
+
+```yaml
+provider_registration:
+  # ...standard registration fields...
+  sandbox_mode: true           # this provider never receives production requests
+  sandbox_profile: dev         # sandbox providers only activated under dev profile
+  
+  # Sandbox providers:
+  # - Appear in the provider registry with status: sandbox
+  # - Can be explicitly targeted by test requests (fields.target_provider_uuid)
+  # - Never appear in placement engine candidate selection for non-test requests
+  # - Produce full audit records (useful for certification evidence)
+  # - Subject to same API validation as production providers
+  # - Can graduate to production via standard registration approval flow
+```
+
+Test request targeting a sandbox provider:
+```
+POST /api/v1/requests
+{
+  "catalog_item_uuid": "",
+  "fields": { ... },
+  "_test_context": {
+    "target_provider_uuid": "",
+    "suppress_billing": true,
+    "test_label": "certification-run-2026-04-01"
+  }
+}
+```
+
+### 9.4 SLA/SLO Tracking
+
+DCM tracks service delivery against declared SLOs at the Resource Type level:
+
+```yaml
+# Declared in Resource Type Specification (doc 05)
+resource_type_slo:
+  resource_type: Compute.VirtualMachine
+  
+  slos:
+    - metric: time_to_operational
+      target_percentile: p95
+      target_value: PT30M       # 95% of VMs should be OPERATIONAL within 30 minutes
+      measurement_window: P7D   # measured over trailing 7 days
+      
+    - metric: uptime
+      target_percentile: p99
+      target_value: "99.5%"     # 99.5% uptime over trailing 30 days
+      measurement_window: P30D
+      
+    - metric: drift_detection_latency
+      target_percentile: p90
+      target_value: PT1H        # drift detected within 1 hour of occurrence
+      measurement_window: P7D
+```
+
+SLO breach detection:
+```
+DCM computes SLO metrics continuously from audit records and entity lifecycle events.
+
+When a metric crosses a threshold:
+  slo.breach_approaching (urgency: medium) — at 90% of SLO budget consumed
+  slo.breach_detected (urgency: high) — SLO violated
+    payload: {resource_type, slo_metric, target, actual, measurement_window}
+
+Consumer-facing:
+  GET /api/v1/resources/{entity_uuid}/slo-status
+  Returns: current SLO metrics for the entity's resource type
+  
+Platform admin:
+  GET /api/v1/admin/slo/report?resource_type=Compute.VirtualMachine&window=P7D
+  Returns: aggregate SLO performance across all entities of this type
+```
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/50-subscription-lifecycle.md b/content/docs/architecture/data-model/50-subscription-lifecycle.md
new file mode 100644
index 0000000..eee86ed
--- /dev/null
+++ b/content/docs/architecture/data-model/50-subscription-lifecycle.md
@@ -0,0 +1,602 @@
+# DCM Data Model — Subscription Lifecycle Management
+
+**Document Status:** 📋 Draft — Ready for Review
+**Document Type:** Architecture Specification — Subscription as First-Class Data
+**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Entity Types](01-entity-types.md) | [Four States](02-four-states.md) | [Resource/Service Entities](06-resource-service-entities.md) | [Provider Contract](A-provider-contract.md) | [Policy Contract](B-policy-contract.md) | [Scheduled Requests](37-scheduled-requests.md) | [Event Catalog](33-event-catalog.md) | [Scoring Model](29-scoring-model.md)
+
+---
+
+## 1. What a Subscription Is
+
+A **Subscription** is a first-class DCM Data artifact representing an ongoing, managed relationship between a Tenant and a set of capabilities delivered by one or more Providers. Unlike a one-time request that produces a resource and completes, a subscription persists — it has terms, renewal cycles, tier levels, and ongoing obligations that both DCM and the Provider must honor.
+
+A subscription manages the lifecycle of the **binding** — what the consumer is entitled to receive, under what terms, for how long. The **resources** that the subscription produces and maintains are standard DCM entities (infrastructure_resource, process_resource, composite_resource) with their own lifecycles. The subscription governs them; it is not them.
+
+**Examples of subscriptions:**
+- A managed database service — ongoing provisioning, patching, backup, scaling within subscription tier
+- A software license — entitlement to install, update, and receive patches for a software package
+- A monitoring service — ongoing collection, alerting, and retention of metrics for subscribed resources
+- A managed Kubernetes namespace — capacity allocation, policy enforcement, and upgrade management
+- A compliance scanning service — periodic scanning, reporting, and remediation of subscribed resources
+
+---
+
+## 2. Subscription as Data
+
+A Subscription follows every universal Data property defined in [00-foundations.md](00-foundations.md):
+
+- **UUID** — stable across full lifecycle
+- **Type** — `dcm.subscription` (registered in the Resource Type Registry)
+- **Lifecycle state** — exactly one state at any moment (see Section 4)
+- **Artifact metadata** — handle, version, status, owned_by, created_by
+- **Provenance** — field-level lineage on every modification
+- **Data classification** — per-field classification, inherited from managed entities and subscription terms
+- **Immutability if versioned** — subscription terms are versioned; published terms cannot be modified
+- **Audit trail** — every subscription operation produces an audit record
+
+### 2.1 Subscription Artifact Structure
+
+```yaml
+subscription:
+  # ── Identity ───────────────────────────────────────────────────
+  subscription_uuid: 
+  handle: "team-alpha/managed-postgres-prod"
+  display_name: "Production PostgreSQL — Team Alpha"
+
+  # ── Binding ────────────────────────────────────────────────────
+  tenant_uuid:                     # Who subscribes
+  catalog_item_uuid:               # What they subscribed to
+  resource_type: "Database.PostgreSQL"   # FQN of the subscribed resource type
+  provider_uuid:                   # Who fulfills the subscription
+
+  # ── Terms ──────────────────────────────────────────────────────
+  terms:
+    tier: "standard"                     # Subscription tier (from catalog item tiers)
+    consumption_model: "subscription"    # subscription | reserved | on_demand
+    billing_period: "monthly"
+    auto_renew: true
+    renewal_advance_notice: "P30D"       # Notify before renewal
+    started_at: "2026-04-01T00:00:00Z"
+    expires_at: "2027-04-01T00:00:00Z"  # null if auto_renew with no end date
+    terms_version: "1.0.0"              # Versioned — immutable once active
+
+  # ── Entitlements ───────────────────────────────────────────────
+  entitlements:
+    max_instances: 5                     # How many entities the subscription covers
+    resource_limits:                     # Per-entity limits within subscription
+      vcpus: 16
+      memory_gb: 64
+      storage_gb: 500
+    capabilities:                        # What the provider will do under this subscription
+      - provision
+      - patch
+      - backup
+      - scale_vertical
+      - monitor
+    update_channels:                     # What update streams the consumer subscribes to
+      - channel: "stable"
+        auto_apply: true                 # Provider applies updates automatically
+      - channel: "security"
+        auto_apply: true
+      - channel: "feature"
+        auto_apply: false                # Requires consumer approval
+
+  # ── Managed Entities ───────────────────────────────────────────
+  managed_entities:                       # Entities produced/maintained by this subscription
+    - entity_uuid: 
+      role: "primary"
+    - entity_uuid: 
+      role: "replica"
+
+  # ── Lifecycle ──────────────────────────────────────────────────
+  lifecycle_state: "ACTIVE"
+  
+  # ── Standard artifact metadata ─────────────────────────────────
+  version: "1.0.0"
+  created_at: "2026-04-01T00:00:00Z"
+  created_by: 
+  provenance: { ... }                    # Field-level provenance per universal model
+```
+
+### 2.2 Relationship to Existing Entity Types
+
+A Subscription is **not** a new entity type alongside infrastructure_resource, process_resource, and composite_resource. It is a **binding artifact** — a Data object that governs the lifecycle of entities. The entities it manages are standard DCM entities with their own types, states, and lifecycles.
+
+```
+Subscription (binding — terms, entitlements, renewal)
+    │
+    ├── manages → Entity A (infrastructure_resource — the database primary)
+    ├── manages → Entity B (infrastructure_resource — the database replica)
+    ├── manages → Entity C (process_resource — nightly backup job)
+    └── manages → Entity D (process_resource — monthly patching job)
+```
+
+The `manages` relationship uses the standard Entity Relationship model (doc 09) with relationship nature `subscription_binding`:
+
+```yaml
+relationship:
+  source_uuid: 
+  target_uuid: 
+  nature: subscription_binding
+  role: managed_entity
+  lifecycle_policy:
+    on_source_suspend: suspend           # Suspend entity when subscription suspends
+    on_source_cancel: decommission       # Decommission entity when subscription cancels
+    on_source_expire: notify             # Notify before decommissioning on expiry
+```
+
+---
+
+## 3. Subscription in the Catalog
+
+Catalog items declare whether they support subscription consumption by including subscription terms in their field schema. This is configurable per catalog item — not every resource type requires subscription support.
+
+### 3.1 Catalog Item with Subscription Support
+
+```yaml
+catalog_item:
+  handle: "managed-postgres"
+  resource_type: "Database.PostgreSQL"
+  consumption_models: ["on_demand", "subscription"]   # What models this item supports
+  
+  subscription_tiers:                                   # Available when consumption_model = subscription
+    - tier: "basic"
+      entitlements:
+        max_instances: 1
+        capabilities: [provision, monitor]
+        update_channels: [security]
+      cost:
+        monthly: 500
+        currency: "USD"
+    - tier: "standard"
+      entitlements:
+        max_instances: 5
+        capabilities: [provision, patch, backup, monitor]
+        update_channels: [stable, security]
+      cost:
+        monthly: 2000
+        currency: "USD"
+    - tier: "premium"
+      entitlements:
+        max_instances: 20
+        capabilities: [provision, patch, backup, scale_vertical, scale_horizontal, monitor, disaster_recovery]
+        update_channels: [stable, security, feature]
+      cost:
+        monthly: 8000
+        currency: "USD"
+
+  field_schema:
+    properties:
+      consumption_model:
+        type: string
+        enum: ["on_demand", "subscription"]
+        default: "on_demand"
+      subscription_tier:
+        type: string
+        enum: ["basic", "standard", "premium"]
+        depends_on:
+          consumption_model: "subscription"
+      auto_renew:
+        type: boolean
+        default: true
+        depends_on:
+          consumption_model: "subscription"
+```
+
+### 3.2 Catalog Items Without Subscription Support
+
+Catalog items that only support one-time requests simply omit `subscription_tiers` and set `consumption_models: ["on_demand"]`. The subscription machinery is not invoked. This is the default behavior — subscription support is opt-in per catalog item.
+
+---
+
+## 4. Subscription Lifecycle States
+
+```
+Consumer submits subscription request
+    │
+    ▼
+PENDING ──────────── Policy evaluation + approval routing
+    │
+    ▼
+PROVISIONING ─────── Provider creates initial managed entities
+    │
+    ▼
+ACTIVE ───────────── Ongoing — provider fulfills subscription obligations
+    │
+    ├──→ SUSPENDED ── Consumer or policy suspends; managed entities suspended
+    │       │
+    │       └──→ ACTIVE (resumed)
+    │
+    ├──→ RENEWAL_PENDING ── Approaching expiry with auto_renew
+    │       │
+    │       ├──→ ACTIVE (renewed — new terms_version)
+    │       └──→ EXPIRED (renewal failed or declined)
+    │
+    ├──→ TIER_CHANGE_PENDING ── Consumer requested tier change
+    │       │
+    │       └──→ ACTIVE (new tier applied)
+    │
+    ├──→ EXPIRED ──── Terms expired without renewal
+    │       │
+    │       └──→ DECOMMISSIONING (grace period passed)
+    │
+    └──→ CANCELLED ── Consumer or admin cancels
+            │
+            └──→ DECOMMISSIONING → DECOMMISSIONED
+```
+
+**Terminal states:** DECOMMISSIONED, CANCELLED (after entity cleanup).
+
+**Grace period:** When a subscription transitions to EXPIRED or CANCELLED, managed entities are not immediately decommissioned. A configurable grace period (default: P30D) allows the consumer to renew, export data, or transition to a different subscription. During the grace period, entities remain in OPERATIONAL but new provisioning under the subscription is blocked.
+
+### 4.1 State Transitions and Policy Triggers
+
+Every state transition fires a subscription lifecycle event (see Section 7) and triggers Policy Engine evaluation. Policies govern:
+
+- Whether auto-renewal is permitted for this tenant/tier/resource type
+- Whether tier upgrades require approval (GateKeeper)
+- Whether downgrades trigger capacity validation (Validation)
+- What happens to managed entities on suspension (Lifecycle Policy)
+- Cost attribution changes on tier change (Transformation)
+
+---
+
+## 5. Provider-Originated Changes Through Subscriptions
+
+This is the critical integration point. When a provider has updates to deliver under a subscription — a software patch, a version upgrade, a configuration change, a capacity adjustment — those changes flow through DCM as **provider-originated updates**, following the standard flow documented in [A-provider-contract.md](A-provider-contract.md).
+
+### 5.1 The Standard Provider-Originated Update Flow
+
+Subscriptions do not introduce a new flow — they create a **context** in which the existing flow fires more frequently and with pre-negotiated terms.
+
+```
+Provider has an update for a managed entity
+    │
+    ▼
+POST /api/v1/provider/entities/{entity_uuid}/update-notification
+    │
+    ▼
+DCM validates: Is this entity managed by an active subscription?
+    │
+    ├── Yes → Check subscription entitlements:
+    │         Is this update type within the subscription's capabilities?
+    │         Is this update channel auto_apply for this subscription?
+    │         │
+    │         ├── Auto-apply enabled → Pre-authorized update flow
+    │         │   → Policy Engine evaluates (pre-authorization GateKeeper)
+    │         │   → If within declared bounds → write Realized State snapshot
+    │         │   → Audit record: source_type = subscription_update
+    │         │   → Events: entity.modified, subscription.update_applied
+    │         │
+    │         └── Auto-apply disabled → Consumer approval required
+    │             → Consumer notification with update details
+    │             → Consumer approves/rejects via API or GitOps
+    │             → Standard provider_update approval flow
+    │
+    └── No → Standard provider-originated update (non-subscription context)
+```
+
+### 5.2 Pre-Authorization via Subscription Terms
+
+Subscriptions extend the existing pre-authorization model (RSE-012). When a provider registers subscription capabilities, the subscription terms define what categories of updates are pre-authorized:
+
+```yaml
+# In the subscription's update_channels:
+update_channels:
+  - channel: "security"
+    auto_apply: true           # Security patches are pre-authorized
+    max_downtime: "PT5M"       # Pre-authorized only if downtime ≤ 5 minutes
+  - channel: "feature"
+    auto_apply: false          # Feature updates require consumer approval
+    preview_period: "P7D"      # Consumer gets 7 days to review before deadline
+```
+
+The GateKeeper policy that evaluates pre-authorization checks:
+
+1. Is the update from a registered provider with an active subscription?
+2. Is the update channel declared in the subscription's `update_channels`?
+3. Is `auto_apply` true for this channel?
+4. Are the declared constraints (max_downtime, resource_limit bounds) satisfied?
+5. Does the update pass all active Validation policies?
+
+If all checks pass, the update is applied without consumer intervention. This is the "subscribe and forget" model for security patches — the consumer opted in at subscription time.
+
+### 5.3 Provider Obligations Under Subscription
+
+When a provider accepts a subscription (by acknowledging the subscription dispatch), it takes on contractual obligations:
+
+- **Delivery:** Provide the capabilities declared in the subscription tier
+- **Update delivery:** Push updates through DCM's standard callback API — never directly to managed entities
+- **Health reporting:** Report health of managed entities through the standard health endpoint
+- **Capacity honoring:** Stay within the resource limits declared in subscription entitlements
+- **Discovery:** Include managed entities in discovery responses so DCM can detect drift
+- **Decommission compliance:** Clean up managed entities when subscription is cancelled/expired after grace period
+
+These obligations are enforced by the existing Provider Contract mechanisms — health monitoring, drift detection, and the Governance Matrix. The subscription doesn't create new enforcement — it creates a context that existing enforcement applies to.
+
+---
+
+## 6. Subscription Request Pipeline
+
+A subscription request follows the standard DCM request pipeline with subscription-specific Transformation and GateKeeper policies:
+
+### 6.1 Consumer Submits Subscription Request
+
+Via API:
+```
+POST /api/v1/requests
+{
+  "catalog_item_uuid": "",
+  "fields": {
+    "consumption_model": "subscription",
+    "subscription_tier": "standard",
+    "auto_renew": true,
+    "display_name": "prod-postgres",
+    "vcpus": 8,
+    "memory_gb": 32
+  }
+}
+```
+
+Via GitOps:
+```yaml
+# intent-store/{tenant_uuid}/Database/PostgreSQL/{entity_uuid}/intent.yaml
+apiVersion: dcm/v1
+kind: SubscriptionRequest
+metadata:
+  handle: "team-alpha/prod-postgres"
+spec:
+  catalog_item: "managed-postgres"
+  consumption_model: subscription
+  subscription_tier: standard
+  auto_renew: true
+  fields:
+    vcpus: 8
+    memory_gb: 32
+```
+
+### 6.2 Pipeline Processing
+
+```
+Intent captured
+    │
+    ▼
+Layer Assembly (Request Processor)
+    │  ── Core layers (profile defaults)
+    │  ── Service layers (subscription tier defaults)
+    │  ── Consumer fields (overrides)
+    │  ── Subscription terms injected by Transformation Policy
+    ▼
+Policy Evaluation
+    │  ── Validation: tier exists, entitlements valid, resource limits within tier
+    │  ── GateKeeper: tenant authorized for subscription model, budget approval
+    │  ── Transformation: inject subscription_uuid, terms, renewal schedule
+    │  ── Scoring: aggregate risk score for approval routing
+    ▼
+Placement (if applicable — select provider)
+    ▼
+Subscription Created (PENDING → PROVISIONING)
+    │  ── Subscription artifact written to Subscription Store
+    │  ── Provider dispatched to create initial managed entities
+    ▼
+Provider Realizes Initial Entities
+    │  ── Standard realization flow for each managed entity
+    │  ── Each entity linked to subscription via subscription_binding relationship
+    ▼
+Subscription ACTIVE
+    │  ── Ongoing: provider pushes updates via callback
+    │  ── DCM applies updates per subscription terms
+    │  ── Discovery monitors managed entities for drift
+```
+
+---
+
+## 7. Subscription Events
+
+All subscription events use the standard DCM event envelope (doc 33) and are published to the `dcm-events` Kafka topic.
+
+| Event Type | Fires When | Urgency |
+|------------|-----------|---------|
+| `subscription.created` | New subscription enters PENDING | medium |
+| `subscription.activated` | Subscription transitions to ACTIVE | medium |
+| `subscription.suspended` | Subscription suspended (consumer or policy) | high |
+| `subscription.resumed` | Subscription resumes from SUSPENDED | medium |
+| `subscription.renewal_pending` | Approaching expiry with auto_renew | medium |
+| `subscription.renewed` | Renewal completed — new terms_version | medium |
+| `subscription.renewal_failed` | Renewal attempt failed (payment, policy, provider) | high |
+| `subscription.tier_changed` | Tier upgrade or downgrade applied | medium |
+| `subscription.update_applied` | Provider-originated update applied to managed entity | low |
+| `subscription.update_rejected` | Consumer rejected a provider-originated update | medium |
+| `subscription.expiry_approaching` | Within renewal_advance_notice of expiry | high |
+| `subscription.expired` | Subscription reached expiry without renewal | high |
+| `subscription.cancelled` | Consumer or admin cancelled subscription | high |
+| `subscription.decommissioning` | Grace period ended — entities being decommissioned | critical |
+| `subscription.decommissioned` | All managed entities decommissioned — terminal | medium |
+
+---
+
+## 8. Subscription Policies
+
+Subscriptions are governed by the same Policy Engine as all other DCM operations. No new policy types are needed — existing types apply:
+
+### 8.1 GateKeeper Policies
+
+- **Subscription authorization:** Which tenants/groups can use subscription consumption model
+- **Tier authorization:** Which tiers are available to which tenants (e.g., "premium" requires FSI profile)
+- **Budget gates:** Subscription cost within tenant budget allocation
+- **Renewal authorization:** Whether auto-renewal is permitted for this tenant/resource type
+
+### 8.2 Validation Policies
+
+- **Entitlement validation:** Requested instances within tier's max_instances
+- **Resource limit validation:** Requested resources within tier's limits
+- **Tier downgrade validation:** Ensure current usage fits within lower tier before allowing downgrade
+
+### 8.3 Transformation Policies
+
+- **Subscription term injection:** Inject subscription_uuid, terms, schedule, and entitlements into the request payload
+- **Cost attribution:** Attach subscription cost to tenant's cost allocation
+- **Update channel defaults:** Inject default update channel configuration based on tier
+
+### 8.4 Lifecycle Policies
+
+- **Managed entity lifecycle:** What happens to managed entities when subscription state changes
+- **Expiry action:** Trigger renewal, notification, or decommission on TTL expiry
+- **Cascade suspension:** Suspend all managed entities when subscription suspends
+
+### 8.5 Recovery Policies
+
+- **Renewal failure recovery:** What happens when auto-renewal fails (retry, notify, suspend, expire)
+- **Provider failure recovery:** What happens when the subscription's provider goes unhealthy
+
+---
+
+## 9. Consumer API Endpoints
+
+Subscription management is exposed through the Consumer API:
+
+| Method | Path | Description |
+|--------|------|-------------|
+| `POST` | `/api/v1/subscriptions` | Create a subscription (via request pipeline) |
+| `GET` | `/api/v1/subscriptions` | List tenant's subscriptions (paginated) |
+| `GET` | `/api/v1/subscriptions/{uuid}` | Get subscription details |
+| `PATCH` | `/api/v1/subscriptions/{uuid}` | Update subscription (tier change, auto_renew toggle) |
+| `POST` | `/api/v1/subscriptions/{uuid}:cancel` | Cancel subscription (starts grace period) |
+| `POST` | `/api/v1/subscriptions/{uuid}:renew` | Manually renew subscription |
+| `POST` | `/api/v1/subscriptions/{uuid}:suspend` | Suspend subscription |
+| `POST` | `/api/v1/subscriptions/{uuid}:resume` | Resume suspended subscription |
+| `GET` | `/api/v1/subscriptions/{uuid}/entities` | List managed entities under this subscription |
+| `GET` | `/api/v1/subscriptions/{uuid}/updates` | List pending and applied updates |
+| `POST` | `/api/v1/subscriptions/{uuid}/updates/{update_uuid}:approve` | Approve a pending update |
+| `POST` | `/api/v1/subscriptions/{uuid}/updates/{update_uuid}:reject` | Reject a pending update |
+
+Admin API extensions:
+
+| Method | Path | Description |
+|--------|------|-------------|
+| `GET` | `/api/v1/admin/subscriptions` | List all subscriptions across tenants |
+| `GET` | `/api/v1/admin/subscriptions/expiring` | Subscriptions approaching expiry |
+| `POST` | `/api/v1/admin/subscriptions/{uuid}:force-cancel` | Admin force cancellation |
+
+---
+
+## 10. Subscription Store
+
+Subscriptions are stored in the DCM operational store alongside other first-class artifacts. The store implementation follows the data store contract (doc 11).
+
+```sql
+CREATE TABLE subscriptions (
+    subscription_uuid       UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+    tenant_uuid             UUID NOT NULL REFERENCES tenants(tenant_uuid),
+    handle                  VARCHAR(256) NOT NULL,
+    display_name            VARCHAR(256) NOT NULL,
+    catalog_item_uuid       UUID NOT NULL,
+    resource_type           VARCHAR(256) NOT NULL,
+    provider_uuid           UUID NOT NULL,
+    lifecycle_state         VARCHAR(32) NOT NULL DEFAULT 'PENDING'
+                                CHECK (lifecycle_state IN (
+                                    'PENDING', 'PROVISIONING', 'ACTIVE',
+                                    'SUSPENDED', 'RENEWAL_PENDING', 'TIER_CHANGE_PENDING',
+                                    'EXPIRED', 'CANCELLED', 'DECOMMISSIONING', 'DECOMMISSIONED'
+                                )),
+    terms                   JSONB NOT NULL DEFAULT '{}',
+    entitlements            JSONB NOT NULL DEFAULT '{}',
+    update_channels         JSONB NOT NULL DEFAULT '[]',
+    terms_version           VARCHAR(32) NOT NULL DEFAULT '1.0.0',
+    started_at              TIMESTAMPTZ,
+    expires_at              TIMESTAMPTZ,
+    grace_period            INTERVAL NOT NULL DEFAULT '30 days',
+    auto_renew              BOOLEAN NOT NULL DEFAULT true,
+    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    updated_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    UNIQUE(tenant_uuid, handle)
+);
+
+CREATE INDEX idx_subscriptions_tenant ON subscriptions(tenant_uuid, lifecycle_state);
+CREATE INDEX idx_subscriptions_provider ON subscriptions(provider_uuid);
+CREATE INDEX idx_subscriptions_expiry ON subscriptions(expires_at) WHERE lifecycle_state = 'ACTIVE';
+
+-- Managed entity bindings
+CREATE TABLE subscription_entities (
+    subscription_uuid       UUID NOT NULL REFERENCES subscriptions(subscription_uuid),
+    entity_uuid             UUID NOT NULL,
+    role                    VARCHAR(64) NOT NULL DEFAULT 'managed',
+    bound_at                TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    PRIMARY KEY (subscription_uuid, entity_uuid)
+);
+
+-- Update tracking
+CREATE TABLE subscription_updates (
+    update_uuid             UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+    subscription_uuid       UUID NOT NULL REFERENCES subscriptions(subscription_uuid),
+    entity_uuid             UUID NOT NULL,
+    provider_uuid           UUID NOT NULL,
+    channel                 VARCHAR(64) NOT NULL,
+    status                  VARCHAR(32) NOT NULL DEFAULT 'PENDING'
+                                CHECK (status IN ('PENDING', 'APPROVED', 'REJECTED',
+                                                  'APPLIED', 'FAILED', 'EXPIRED')),
+    update_payload          JSONB NOT NULL DEFAULT '{}',
+    submitted_at            TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    decided_at              TIMESTAMPTZ,
+    decided_by              UUID,
+    applied_at              TIMESTAMPTZ,
+    auto_applied            BOOLEAN NOT NULL DEFAULT false
+);
+
+CREATE INDEX idx_sub_updates_subscription ON subscription_updates(subscription_uuid, status);
+CREATE INDEX idx_sub_updates_pending ON subscription_updates(status, submitted_at) WHERE status = 'PENDING';
+
+-- RLS
+ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
+ALTER TABLE subscription_entities ENABLE ROW LEVEL SECURITY;
+ALTER TABLE subscription_updates ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY tenant_isolation_subscriptions
+    ON subscriptions FOR ALL TO dcm_app
+    USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid);
+
+CREATE POLICY tenant_isolation_sub_entities
+    ON subscription_entities FOR ALL TO dcm_app
+    USING (subscription_uuid IN (
+        SELECT subscription_uuid FROM subscriptions
+        WHERE tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid
+    ));
+
+CREATE POLICY tenant_isolation_sub_updates
+    ON subscription_updates FOR ALL TO dcm_app
+    USING (subscription_uuid IN (
+        SELECT subscription_uuid FROM subscriptions
+        WHERE tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid
+    ));
+```
+
+---
+
+## 11. Capability Domain — SUB (Subscription Management)
+
+| ID | Capability | Consumer | Provider | Platform/Admin |
+|----|-----------|----------|----------|----------------|
+| SUB-001 | Create subscription | Submit subscription request with tier and terms | Acknowledge subscription, provision initial entities | Approve subscription creation |
+| SUB-002 | Manage subscription lifecycle | Suspend, resume, cancel own subscriptions | Report managed entity status | Force-cancel, view all subscriptions |
+| SUB-003 | Change subscription tier | Request tier upgrade/downgrade | Adjust capacity for managed entities | Approve tier changes that require admin |
+| SUB-004 | Subscription renewal | Approve/decline renewal; view renewal status | Honor renewed terms | Configure renewal policies |
+| SUB-005 | Provider-originated updates | Approve/reject non-auto updates | Submit updates via standard callback | View update audit trail |
+| SUB-006 | Update channel management | Configure auto_apply per channel | Declare available update channels | Set organization-wide channel policies |
+| SUB-007 | Subscription cost attribution | View subscription cost | Declare cost model per tier | Configure cost allocation rules |
+| SUB-008 | Entitlement enforcement | View remaining capacity | Stay within entitlement bounds | Override entitlements in exceptional cases |
+| SUB-009 | Grace period management | View grace period status; request extension | Continue health reporting during grace | Configure grace period defaults |
+| SUB-010 | Subscription audit trail | View own subscription audit history | — | View all subscription audit records |
+
+---
+
+## 12. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should subscription-to-subscription dependencies be supported (e.g., a monitoring subscription that depends on a compute subscription)? | Dependency model complexity | ✅ Resolved — use standard Entity Relationship model with `subscription_binding` nature; subscriptions can reference other subscriptions via relationship |
+| 2 | How does subscription interact with the compound service definition model — can a compound service definition compose subscriptions from multiple child providers? | Compound subscription complexity | ✅ Resolved — yes, a compound service definition subscription creates child subscriptions on constituent providers; parent subscription lifecycle governs children per standard compound service definition composition model (doc 30) |
+| 3 | Should subscription cost be pre-computed or dynamic? | Cost attribution accuracy | ✅ Resolved — both; pre-computed estimate at request time (displayed in catalog), actual cost tracked via ongoing cost attribution (Transformation Policy injects cost records on each billing period) |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/51-infrastructure-optimization.md b/content/docs/architecture/data-model/51-infrastructure-optimization.md
new file mode 100644
index 0000000..3fe4a81
--- /dev/null
+++ b/content/docs/architecture/data-model/51-infrastructure-optimization.md
@@ -0,0 +1,497 @@
+# DCM Data Model — Infrastructure Requirements and Provider Types
+
+**Document Status:** 📋 Draft — Ready for Review
+**Document Type:** Architecture Specification — Infrastructure dependencies, provider type definitions, policy evaluation modes, control plane service inventory
+**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Four States](02-four-states.md) | [Provider Contract](A-provider-contract.md) | [Implementation Specifications](49-implementation-specifications.md)
+
+---
+
+## 1. Design Principle
+
+DCM prescribes **data contracts** (schemas, immutability rules, versioning, hash chains) — not infrastructure products. Where a contract maps directly to a single well-understood infrastructure category, DCM prescribes the category and the contract, not an abstraction layer over it.
+
+Abstraction layers earn their place when the underlying implementations have genuinely different interaction contracts — different APIs, different lifecycle semantics, different operational models. When the implementations share a standard protocol (SQL, OIDC, AMQP), the protocol is the abstraction. Adding a DCM-specific abstraction on top of a standard protocol is unnecessary indirection.
+
+---
+
+## 2. Four Data Domains, One Required Store
+
+DCM tracks every resource through four lifecycle stages. These remain architecturally distinct — they represent different things, have different immutability rules, and serve different query patterns. But they do not require separate infrastructure.
+
+### 2.1 The Four Data Domains
+
+| Domain | What it represents | Immutability | Primary consumers |
+|--------|-------------------|-------------|-------------------|
+| **Intent** | What the consumer asked for — raw declaration before processing | Append-only. A new intent version creates a new record. Previous intents are never modified. | Audit, portability (re-process intent through new policies), request history |
+| **Requested** | What was approved and dispatched — assembled, policy-validated, placed | Append-only. Each policy evaluation produces a new version. Complete provenance chain. | Provider dispatch, audit, rollback comparison |
+| **Realized** | What the provider actually built — confirmed state with provider metadata | Append-only versioned. Each state change creates a new snapshot. `is_current` flag for latest. | Operational queries, drift comparison baseline, inventory |
+| **Discovered** | What actually exists right now — independently observed by discovery | Ephemeral. Each discovery run produces a fresh snapshot. Previous snapshots retained for trend analysis. | Drift detection (compare against Realized), capacity planning |
+
+### 2.2 Single Required Infrastructure
+
+All four domains live in **PostgreSQL** (or any PostgreSQL-compatible database: CockroachDB, Aurora PostgreSQL, Crunchy Postgres). The data contracts are enforced by:
+
+- **Append-only tables** with `REVOKE UPDATE, DELETE` for Intent, Requested, and Audit records
+- **Row versioning** with `version_major.minor.revision` and `is_current` flag on Realized records
+- **SHA-256 hash chain** on Audit records for tamper evidence
+- **RLS (Row-Level Security)** for tenant isolation (STI-001, STI-002)
+- **`LISTEN/NOTIFY`** for event-driven pipeline routing between control plane services
+- **Materialized views** for cached read-heavy queries (catalog browsing, placement lookups)
+
+### 2.3 Schema Design
+
+```sql
+-- ── Intent Domain ──────────────────────────────────────────────────────
+-- Append-only. Raw consumer declarations. Never modified after write.
+
+CREATE TABLE intent_records (
+    intent_uuid         UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+    entity_uuid         UUID NOT NULL,              -- Stable entity identity
+    tenant_uuid         UUID NOT NULL REFERENCES tenants(tenant_uuid),
+    catalog_item_uuid   UUID NOT NULL,
+    submitted_by        UUID NOT NULL,              -- Actor who submitted
+    submitted_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    submitted_via       VARCHAR(32) NOT NULL         -- api | gitops | cli | message_bus
+                            CHECK (submitted_via IN ('api', 'gitops', 'cli', 'message_bus')),
+    intent_version      INTEGER NOT NULL DEFAULT 1,  -- Increments on resubmission
+    fields              JSONB NOT NULL DEFAULT '{}', -- Consumer's raw field values
+    provenance          JSONB NOT NULL DEFAULT '{}'  -- Ingress context
+);
+
+CREATE INDEX idx_intent_entity ON intent_records(entity_uuid, intent_version);
+CREATE INDEX idx_intent_tenant ON intent_records(tenant_uuid, submitted_at);
+
+REVOKE UPDATE, DELETE ON intent_records FROM dcm_app;
+
+-- ── Requested Domain ───────────────────────────────────────────────────
+-- Append-only. Assembled, policy-evaluated, placed payloads.
+
+CREATE TABLE requested_records (
+    requested_uuid      UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+    entity_uuid         UUID NOT NULL,
+    tenant_uuid         UUID NOT NULL REFERENCES tenants(tenant_uuid),
+    operation_uuid      UUID NOT NULL REFERENCES operations(operation_uuid),
+    intent_uuid         UUID NOT NULL REFERENCES intent_records(intent_uuid),
+    resource_type       VARCHAR(256) NOT NULL,
+    provider_uuid       UUID NOT NULL,              -- Selected provider
+    assembled_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    assembled_payload   JSONB NOT NULL DEFAULT '{}', -- Full merged payload
+    layer_sources       JSONB NOT NULL DEFAULT '[]', -- Which layers contributed
+    policy_results      JSONB NOT NULL DEFAULT '{}', -- Policy evaluation outcomes
+    placement_result    JSONB NOT NULL DEFAULT '{}', -- Placement decision + score
+    provenance          JSONB NOT NULL DEFAULT '{}'  -- Field-level provenance
+);
+
+CREATE INDEX idx_requested_entity ON requested_records(entity_uuid);
+CREATE INDEX idx_requested_tenant ON requested_records(tenant_uuid);
+CREATE INDEX idx_requested_operation ON requested_records(operation_uuid);
+
+REVOKE UPDATE, DELETE ON requested_records FROM dcm_app;
+
+-- ── Realized Domain ────────────────────────────────────────────────────
+-- (Existing realized_entities table from 001-initial.sql — unchanged)
+-- Versioned rows, is_current flag, append-on-change semantics.
+
+-- ── Discovered Domain ──────────────────────────────────────────────────
+-- Ephemeral snapshots from provider discovery runs.
+
+CREATE TABLE discovered_records (
+    discovery_uuid      UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+    entity_uuid         UUID,                       -- Matched entity (null if orphan)
+    tenant_uuid         UUID REFERENCES tenants(tenant_uuid),
+    provider_uuid       UUID NOT NULL,
+    resource_type       VARCHAR(256) NOT NULL,
+    discovered_at       TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    discovery_run_uuid  UUID NOT NULL,              -- Groups records from same run
+    discovered_fields   JSONB NOT NULL DEFAULT '{}', -- What the provider reported
+    provider_native_id  VARCHAR(512),               -- Provider's identifier
+    match_confidence    VARCHAR(16) DEFAULT 'exact'
+                            CHECK (match_confidence IN ('exact', 'high', 'low', 'unmatched'))
+);
+
+CREATE INDEX idx_discovered_entity ON discovered_records(entity_uuid, discovered_at);
+CREATE INDEX idx_discovered_run ON discovered_records(discovery_run_uuid);
+CREATE INDEX idx_discovered_orphans ON discovered_records(entity_uuid) WHERE entity_uuid IS NULL;
+
+-- ── Pipeline Events ────────────────────────────────────────────────────
+-- Append-only event log. Replaces Kafka for pipeline routing in standard deployments.
+-- LISTEN/NOTIFY provides real-time notification to pipeline consumers.
+
+CREATE TABLE pipeline_events (
+    event_uuid          UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+    event_type          VARCHAR(128) NOT NULL,
+    entity_uuid         UUID,
+    request_uuid        UUID,
+    tenant_uuid         UUID,
+    actor_uuid          UUID,
+    payload             JSONB NOT NULL DEFAULT '{}',
+    published_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+    consumed_by         JSONB NOT NULL DEFAULT '[]',  -- Track which services consumed
+    consumed_at         TIMESTAMPTZ
+);
+
+CREATE INDEX idx_events_type ON pipeline_events(event_type, published_at);
+CREATE INDEX idx_events_entity ON pipeline_events(entity_uuid, published_at);
+CREATE INDEX idx_events_unconsumed ON pipeline_events(event_type, published_at) 
+    WHERE consumed_at IS NULL;
+
+REVOKE UPDATE, DELETE ON pipeline_events FROM dcm_app;
+
+-- Notify function for real-time pipeline routing
+CREATE OR REPLACE FUNCTION notify_pipeline_event() RETURNS TRIGGER AS $$
+BEGIN
+    PERFORM pg_notify('dcm_pipeline', json_build_object(
+        'event_uuid', NEW.event_uuid,
+        'event_type', NEW.event_type,
+        'entity_uuid', NEW.entity_uuid
+    )::text);
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER pipeline_event_notify
+    AFTER INSERT ON pipeline_events
+    FOR EACH ROW EXECUTE FUNCTION notify_pipeline_event();
+
+-- RLS on all new tables
+ALTER TABLE intent_records ENABLE ROW LEVEL SECURITY;
+ALTER TABLE requested_records ENABLE ROW LEVEL SECURITY;
+ALTER TABLE discovered_records ENABLE ROW LEVEL SECURITY;
+ALTER TABLE pipeline_events ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY tenant_isolation_intent ON intent_records
+    FOR ALL TO dcm_app
+    USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid);
+CREATE POLICY tenant_isolation_requested ON requested_records
+    FOR ALL TO dcm_app
+    USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid);
+CREATE POLICY tenant_isolation_discovered ON discovered_records
+    FOR ALL TO dcm_app
+    USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid);
+CREATE POLICY tenant_isolation_events ON pipeline_events
+    FOR SELECT TO dcm_app
+    USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid);
+```
+
+### 2.4 Why Not Separate Stores
+
+| Concern | Four-store answer | Single-store answer |
+|---------|------------------|-------------------|
+| Immutability | Git commits are immutable | Append-only tables + REVOKE UPDATE, DELETE + audit trigger |
+| Version history | Git log | Row versioning with semantic version fields |
+| Audit trail | Git commit metadata | SHA-256 hash chain (stronger — explicit cryptographic chain vs Git's graph integrity) |
+| PR-based review | Native Git workflow | DCM's Policy Engine + Scoring Model + Authority Tier routing (more sophisticated) |
+| Tamper evidence | Git SHA integrity | Hash chain with `previous_record_hash` per entity (per-record, not per-repo) |
+| Transactional consistency | Cross-store sync required | Native — intent + audit + operation in same transaction |
+| Sovereignty partitioning | Separate Git/Kafka/Redis per zone | Separate PostgreSQL instance per zone (one thing to deploy, not four) |
+| Air-gapped deployment | Git + Kafka + Redis + PostgreSQL (4 infra dependencies) | PostgreSQL only (1 dependency) |
+| Operations skill set | Git admin + Kafka admin + Redis admin + DBA | DBA only |
+
+---
+
+## 3. Provider Types
+
+### 3.1 Design Principle
+
+A provider **type** is justified when the provider's **interaction contract** — the operations it supports, the lifecycle it follows, the data it exchanges — is fundamentally different from other types. If two "types" follow the same create/update/decommission/discover contract and differ only in what resource types they handle, they're the same type with different capabilities.
+
+### 3.2 Provider Types (6)
+
+| Type | Contract distinction | Examples |
+|------|---------------------|----------|
+| `service_provider` | Full lifecycle CRUD on resources — create, update, decommission, discover. Naturalization/denaturalization of payloads. Capacity reporting. Health events. | KubeVirt (VMs), NSX (networks), Vault (credentials), SMTP (notifications), ACM (clusters) |
+| `information_provider` | Read-only data queries. No create/decommission. Authority levels, confidence models, scheduled/pushed/on-demand retrieval modes. | CMDB, LDAP directory, compliance scanner, cost data feed |
+
+| `auth_provider` | Authentication and identity services. Provides actor authentication, token issuance, group/role claims, MFA capabilities. Multiple auth providers can be registered — tenant routing determines which provider authenticates a given actor. | Keycloak, Okta, Azure AD, LDAP + Dex, SAML IdP |
+| `peer_dcm` | DCM-to-DCM federation protocol. Entity migration, cross-instance placement, distributed governance. | Regional DCM instances, sovereign DCM peers |
+| `process_provider` | Executes ephemeral workflows to completion. No persistent resources — produces a result and terminates. May orchestrate external automation (AAP, Tekton). | Software install, backup execution, migration job, compliance scan |
+
+### 3.3 Design Rationale — Why These Are Not Provider Types
+
+Some infrastructure categories that might seem like provider types are intentionally excluded because they lack a genuinely different interaction contract, or because they are internal infrastructure that DCM consumes rather than manages.
+
+| Category | Classification | Rationale |
+|----------|---------------|-----------|
+| Database (PostgreSQL) | **Prescribed infrastructure** | DCM's database is internal infrastructure, not a registered provider. DCM doesn't manage the DB's lifecycle; the DB stores DCM's data. |
+| Event streaming (Kafka) | **Optional infrastructure** | Kafka is a deployment enhancement, not a registered provider. `LISTEN/NOTIFY` handles pipeline routing in standard deployments. |
+| Resource Type Registry | **Internal component** | The Resource Type Registry is an internal function of the control plane, not an external provider. |
+| Policy evaluation engine | **Policy Manager capability** | External policy evaluation is a mode of the Policy Manager (see Section 3.4), not a separate provider type. |
+| Credential management (Vault) | **Service provider** | Vault is a service_provider that handles `Credential.*` resource types. Same contract — create, retrieve, rotate (update), revoke (decommission). HSM support and rotation protocols are capability declarations, not a different contract. |
+| Notification delivery | **Service provider** | Email/Slack/webhook delivery is a service_provider that handles `Notification.*` resource types. Same contract. |
+
+### 3.4 Policy Evaluation Modes
+
+DCM supports two policy evaluation modes. The distinction is whether DCM or an external system performs the evaluation — not how policies are delivered to the evaluator.
+
+**Two modes:**
+
+| Mode | Name | How it works | Examples |
+|------|------|-------------|----------|
+| **Internal** | DCM evaluates | Policies arrive through any delivery mechanism (API, GitOps, OPA bundle server, external schema with naturalization). DCM's Policy Manager evaluates all of them via OPA. Where OPA runs (embedded, sidecar, remote instance) is a deployment topology decision, not a mode. | Rego policies in DCM database; OPA bundle from bundle server; non-Rego policies translated to Rego by DCM |
+| **External** | External provider evaluates | DCM sends evaluation context to an external endpoint. External system returns structured results (pass/fail, score, enrichment fields). DCM does not see the policy logic — it trusts the results within scoped bounds. | Enterprise compliance engine; SaaS policy platform; organization-specific evaluation service |
+
+**Internal mode delivery mechanisms** (all equivalent — same evaluation, different transport):
+
+| Delivery | `delivery.mode` value | Description |
+|----------|----------------------|-------------|
+| API push/pull | `push` / `pull` / `webhook` | Policies stored in DCM's database, managed via API or GitOps adapter |
+| OPA bundle | `opa_bundle` | Standard OPA bundle protocol — point OPA at a bundle server |
+| External schema | `external_schema` | Policy in non-Rego format (e.g., XACML, custom JSON), DCM naturalizes to Rego before evaluation |
+
+**External mode governance** (BBQ-001 through BBQ-009 — unchanged):
+- Data sovereignty check before any query is sent
+- Data minimization — only declared fields sent
+- Full audit record per query-response cycle
+- Default failure behavior is `gatekeep` — unknown is not safe
+- Enrichment fields carry provenance with `source_type: external_external_policy_evaluator`
+- External evaluation requires minimum `verified` trust level; GateKeeper authority requires `trusted`
+
+### 3.5 Resource Type Categories as Capability Declarations
+
+What the former provider types expressed — "this provider handles credentials" — is now expressed by the resource types a `service_provider` declares in its `supported_resource_types`:
+
+```yaml
+# Vault registers as a service_provider that handles credential resources
+provider:
+  provider_type: service_provider
+  supported_resource_types:
+    - "Credential.Secret"
+    - "Credential.Certificate"
+    - "Credential.SSHKey"
+  capability_extension:
+    hsm_support: true
+    rotation_protocol: automatic
+    max_secret_size_bytes: 65536
+
+# SMTP gateway registers as a service_provider that handles notifications
+provider:
+  provider_type: service_provider
+  supported_resource_types:
+    - "Notification.Email"
+    - "Notification.Webhook"
+  capability_extension:
+    delivery_guarantee: at_least_once
+    sovereignty_aware_routing: true
+```
+
+---
+
+## 4. Prescribed Infrastructure Requirements
+
+Instead of provider registrations for infrastructure, DCM prescribes infrastructure categories with specific contracts:
+
+### 4.1 Required Infrastructure
+
+| Infrastructure | Contract | Examples |
+|---------------|----------|----------|
+| **PostgreSQL-compatible database** | SQL, RLS, `LISTEN/NOTIFY`, JSONB, triggers, append-only tables, `pgcrypto` for envelope encryption. Schema defined by DCM. Includes the `secrets` table for internal secrets management and the `actors` table for internal authentication. | PostgreSQL, CockroachDB, Aurora PostgreSQL, Crunchy Postgres |
+
+PostgreSQL is the only required external infrastructure. All other dependencies — identity, secrets, event streaming, caching, Git ingress — can either be handled internally by DCM or optionally delegated to external systems.
+
+### 4.2 Authentication — Internal and External
+
+DCM manages authentication using the same Internal/External pattern as policy evaluation and secrets management.
+
+**Internal mode (default):** Local user accounts stored in the `actors` table. Passwords stored as argon2id hashes. DCM issues its own JWT session tokens with configurable expiry. For a homelab deployment, you create a local admin account via the bootstrap CLI and you're done.
+
+**External mode (optional):** Organizations with existing identity infrastructure register `auth_provider` instances. DCM validates their tokens, extracts claims, and maps groups to DCM roles. OIDC, SAML, LDAP — whatever the auth_provider supports. Multiple auth_providers enable tenant-routed authentication (Tenant A through AD, Tenant B through Okta). External auth_providers are registered via the standard provider registration contract.
+
+| Feature | Internal | External (auth_provider) |
+|---------|----------|--------------------------|
+| User management | `actors` table — local accounts | External IdP manages users |
+| Authentication | Password → argon2id hash → DCM-issued JWT | External token → DCM validates → extracts claims |
+| MFA | TOTP (optional) | Delegated to IdP |
+| Group/role mapping | Direct role assignment in `actors` table | JWT group claims mapped to DCM roles |
+| Session management | `sessions` table with configurable TTL | Token lifetime governed by IdP + DCM validation |
+| Federation | N/A (single instance) | Multiple auth_providers, tenant routing |
+
+### 4.3 Secrets Management — Internal and External
+
+DCM manages secrets using the same Internal/External pattern as policy evaluation.
+
+**Internal mode (default):** Secrets are stored in DCM's PostgreSQL database in a `secrets` table using envelope encryption. Each secret value is encrypted with AES-256-GCM using a data encryption key (DEK). DEKs are encrypted with a master key (KEK) sourced from the deployment environment:
+
+| KEK Source | Deployment | Security Level |
+|------------|-----------|----------------|
+| Environment variable | Homelab, dev | Basic — protects against database theft, not host compromise |
+| Kubernetes Secret | Standard | Good — Kubernetes RBAC protects the KEK; etcd encryption at rest protects storage |
+| HSM via PKCS#11 | Sovereign | Strong — KEK never leaves the hardware security module |
+
+Internal mode requires no external secrets infrastructure. The `secrets` table has the same RLS, append-only audit, and tenant isolation as every other DCM table.
+
+**External mode (optional):** Organizations with existing secrets infrastructure register a Vault-compatible API endpoint. DCM calls the Vault HTTP API for all secret operations. Vault, OpenBao, or any API-compatible implementation works. This is an optional deployment enhancement — not a required component.
+
+| Feature | Internal | External (Vault) |
+|---------|----------|------------------|
+| Secret CRUD | PostgreSQL `secrets` table | Vault KV engine |
+| PKI certificates | cert-manager / service mesh | Vault PKI engine |
+| Dynamic DB credentials | Static credentials + rotation | Vault database engine |
+| Transit encryption | `pgcrypto` AES-256-GCM | Vault transit engine |
+| HSM backing | PKCS#11 for KEK only | Full HSM seal + transit |
+
+### 4.4 Optional Infrastructure (Deployment Enhancements)
+
+| Infrastructure | When to add | What it provides |
+|---------------|------------|-----------------|
+| **OIDC-compatible identity provider** | Organizations with existing identity infrastructure. Multi-tenant deployments requiring federated authentication across different IdPs. | External authentication — replaces internal PostgreSQL-based user management. Registered as `auth_provider`. |
+| **Vault-compatible secrets management** | Organizations with existing Vault infrastructure. Deployments requiring dynamic database credentials, PKI certificate issuance, or full HSM-backed transit encryption. | External secrets backend — replaces internal PostgreSQL-based secrets management. |
+| **Kafka-compatible event stream** | High-throughput deployments (>1000 events/sec). Multiple consumer groups needing independent replay. | Replaces `pipeline_events` table + `LISTEN/NOTIFY` for event routing. |
+| **Redis-compatible cache** | Read-heavy catalog/placement workloads. Geographically distributed read replicas. | Replaces materialized views in PostgreSQL. |
+| **Git repository** | CI/CD pipeline integration. Teams that want PR-based request ingress. | Adds Git as an ingress path alongside API/CLI. |
+| **Service mesh** | Production deployments requiring mTLS between control plane services. | Replaces application-level TLS configuration. |
+
+### 4.5 Deployment Profiles
+
+| Profile | Required | Optional |
+|---------|----------|----------|
+| **Minimal** (homelab/dev) | PostgreSQL (single instance) | — |
+| **Standard** (production) | PostgreSQL (HA) | Keycloak, Vault, Service mesh, Redis |
+| **Enterprise** (large scale) | PostgreSQL (HA + read replicas) | Keycloak (HA), Vault (HA + HSM), Service mesh, Kafka, Redis, Git |
+| **Sovereign** (air-gapped) | PostgreSQL (per-zone) | Keycloak (per-zone), Vault (per-zone + HSM seal), Service mesh |
+
+---
+
+## 5. Control Plane Services
+
+### 5.1 Design Principle
+
+A component is a separately deployable service when it has an independent scaling profile, a distinct failure domain, or a genuinely separate operational responsibility. Functions that share a scaling profile and failure domain with their host service are not separate deployments — they are internal modules.
+
+### 5.2 Deployable Services (9)
+
+| Service | Responsibility | Absorbs |
+|---------|---------------|---------|
+| **API Gateway** (Traefik) | Ingress routing, JWT validation, `X-DCM-Tenant` injection, `X-Request-ID` generation, rate limiting | — |
+| **Catalog Manager** | Catalog CRUD — ServiceType, CatalogItem, subscription tiers. Consumer browsing. | — |
+| **Service Provider Manager** | Provider registration, status lifecycle, capability tracking, health monitoring | — |
+| **Policy Manager** | Policy artifact CRUD, OPA-backed evaluation, scoring | `scoring_engine` (scoring is a function of policy evaluation, not a separate service) |
+| **Placement Manager** | Provider selection via specificity narrowing, capacity-aware placement | — |
+| **Request Orchestrator** | Pipeline routing — consumes events, dispatches to stage services, manages pipeline state | `lifecycle_enforcer` (lifecycle events are pipeline events), `notification_router` (notifications are pipeline side-effects), `request_scheduler` (scheduling is a pipeline function) |
+| **Request Processor** | Layer assembly — merges core/service/consumer layers into assembled payload | — |
+| **Audit Service** | Append-only audit records, hash chain integrity, audit queries, chain verification | — |
+| **Discovery Service** | Scheduled provider discovery, drift detection, orphan identification | `drift_reconciler` (drift is a discovery output), `discovery_scheduler` (scheduling is internal) |
+
+### 5.3 Internal Functions (Not Separate Services)
+
+These are functions within the services above — not independently deployed components.
+
+| Function | Lives in | Rationale |
+|-----------------|--------------|-----------|
+| `scoring_engine` | Policy Manager | Scoring is the output of policy evaluation. Same request, same data, same transaction. No independent scaling need. |
+| `lifecycle_enforcer` | Request Orchestrator | Lifecycle events (TTL expiry, state transitions) enter the same pipeline as any other event. The orchestrator routes them to policy evaluation. No separate service needed. |
+| `notification_router` | Request Orchestrator | Notifications are pipeline side-effects. When the orchestrator completes a pipeline stage, it emits notification events. A `service_provider` for `Notification.*` resources handles delivery. |
+| `session_store` | PostgreSQL table | Sessions are rows in a `sessions` table. JWT is stateless. Revocation is a status update. No separate infrastructure. |
+| `service_provider_proxy` | Standard service_provider call | Credential retrieval is a standard API call to whichever service_provider handles `Credential.*` resource types. No proxy needed. |
+| `drift_reconciler` | Discovery Service | Drift detection is the primary function of discovery. Compare Discovered vs Realized, produce drift records. Same data, same service. |
+| `request_scheduler` | Request Orchestrator | Scheduled requests are cron entries that produce pipeline events. The orchestrator's scheduler emits events at the scheduled time. |
+| `discovery_scheduler` | Discovery Service | Same pattern — built-in scheduler. |
+| `internal_ca` | Service mesh | mTLS between control plane services is a service mesh responsibility (Istio/OpenShift Service Mesh), not a DCM component. |
+| `message_bus` | `pipeline_events` table + `LISTEN/NOTIFY` | Standard deployments use PostgreSQL for event routing. Kafka added as enhancement for high-throughput. |
+
+### 5.4 Service Interaction Model (Simplified)
+
+```
+Consumer → API Gateway → Catalog Manager (browse)
+                       → Request Orchestrator (submit) → pipeline_events table
+                                                            │
+                           LISTEN/NOTIFY triggers:          │
+                           ┌────────────────────────────────┘
+                           ▼
+                    Request Processor (assemble)
+                           ▼
+                    Policy Manager (evaluate + score)
+                           ▼
+                    Placement Manager (select provider)
+                           ▼
+                    Request Orchestrator (dispatch to provider)
+                           ▼
+                    Service Provider (realize)
+                           ▼
+                    Request Orchestrator (write Realized, emit events)
+                           ▼
+                    Audit Service (append audit record)
+
+Discovery Service runs on schedule:
+    → Calls each registered service_provider's discover endpoint
+    → Compares against Realized domain
+    → Writes drift records
+    → Emits drift events → Policy Manager evaluates → actions
+```
+
+---
+
+## 6. Git as Ingress Adapter
+
+Git remains a valid and valuable ingress mechanism. But it is an ingress adapter — not a state store.
+
+### 6.1 How Git Ingress Works
+
+```
+Developer writes intent YAML → commits to Git repo → PR reviewed and merged
+    │
+    ▼
+GitOps Adapter (watches repo or triggered by CI/CD webhook)
+    │
+    ▼
+POST /api/v1/requests (same API as any other consumer)
+    │
+    ▼
+Standard pipeline: Intent → Assemble → Evaluate → Place → Dispatch → Realize
+```
+
+The GitOps Adapter is a lightweight service that:
+1. Watches a Git repository for merged PRs (or receives CI/CD webhooks)
+2. Parses intent YAML from the commit
+3. Submits it to the DCM API as a standard request with `submitted_via: gitops`
+4. The request enters the same pipeline as an API-submitted request
+
+The Git repository is the consumer's workspace — not DCM's state store. DCM's state lives in PostgreSQL. The Git repo provides the PR-based review workflow that some teams prefer, but the actual approval authority is DCM's Policy Engine.
+
+### 6.2 What the Git Repo Contains
+
+```
+dcm-intents/
+├── {tenant-handle}/
+│   ├── {resource-type}/
+│   │   ├── {entity-handle}/
+│   │   │   └── intent.yaml        ← Consumer's declaration
+│   │   └── ...
+│   └── ...
+└── ...
+```
+
+This is a consumer convenience — a structured workspace for declaring intent. It is not the source of truth for DCM state.
+
+---
+
+## 7. Design Summary
+
+| Aspect | Specification |
+|--------|--------------|
+| Required infrastructure | PostgreSQL-compatible DB |
+| Internal capabilities | Authentication (local accounts + JWT), secrets (envelope encryption), event routing (`LISTEN/NOTIFY`) |
+| Optional infrastructure | OIDC IdP (external auth), Vault (external secrets), Kafka (event streaming), Redis (caching), Git (ingress), Service mesh (mTLS) |
+| Provider types | 5: `service_provider`, `information_provider`, `auth_provider`, `peer_dcm`, `process_provider` |
+| Policy evaluation modes | 2: Internal (DCM evaluates via OPA, any delivery mechanism) and External (external provider evaluates) |
+| Control plane services | 9 deployable services |
+| Data domains | 4 logical domains (Intent, Requested, Realized, Discovered) in 1 database |
+| Minimum deployment | 1 infrastructure component (PostgreSQL) + control plane services |
+| Sovereign deployment | PostgreSQL per sovereignty zone (+ Keycloak, Vault optional) |
+
+### 7.1 Architectural Invariants
+
+These properties hold regardless of deployment profile or infrastructure choices:
+
+- **Four data domains** — Intent, Requested, Realized, Discovered remain architecturally distinct
+- **Append-only immutability** on Intent, Requested, and Audit records
+- **SHA-256 hash chain** on audit records
+- **RLS tenant isolation** (STI-001, STI-002)
+- **Provider Contract** — registration, health, capability, sovereignty, naturalization/denaturalization
+- **Policy Engine** — 8 policy types, OPA/Rego, scoring model
+- **AEP compliance** on all APIs
+- **331 capabilities** across 39 domains
+- **101 event payloads** across 22 domains
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/52-test-framework-specification.md b/content/docs/architecture/data-model/52-test-framework-specification.md
new file mode 100644
index 0000000..576ad9d
--- /dev/null
+++ b/content/docs/architecture/data-model/52-test-framework-specification.md
@@ -0,0 +1,427 @@
+# DCM Data Model — Test Framework Specification
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Specification — Automated test framework contract for data model and architecture validation
+**Related Documents:** [Foundations](00-foundations.md) | [Entity Types](01-entity-types.md) | [Four States](02-four-states.md) | [Policy Contract](B-policy-contract.md) | [Provider Contract](A-provider-contract.md) | [Infrastructure Requirements](51-infrastructure-optimization.md)
+
+> **Purpose:** This document defines the contract for an automated, self-reflecting test framework
+> that validates the DCM data model and architecture by generating random end-to-end use cases,
+> executing them against the pipeline, and verifying invariants. The framework is designed to
+> discover edge cases and feed required changes back into the architecture.
+
+---
+
+## 1. Framework Architecture
+
+The test framework operates as a closed loop:
+
+```
+Generate → Execute → Verify → Analyze → Enhance → Generate
+    ↑                                                 │
+    └─────────────────────────────────────────────────┘
+```
+
+**Generate:** Create random but valid test scenarios from the architecture rules.
+**Execute:** Run the scenario through the DCM pipeline (or a simulation of it).
+**Verify:** Check all invariants — every rule in this document must hold.
+**Analyze:** Identify failures, edge cases, and undocumented behaviors.
+**Enhance:** Propose architecture or data model changes to accommodate discovered gaps.
+
+---
+
+## 2. Architecture Fundamentals (Framework Must Know)
+
+### 2.1 Three Abstractions
+
+Everything in DCM maps to one of three abstractions:
+
+| Abstraction | What it governs | Key documents |
+|-------------|----------------|---------------|
+| **Data** | Entity lifecycle, four states, layering, versioning | docs 00-04, 11, 16 |
+| **Provider** | External system integration, naturalization, discovery | doc A, docs 10, 19, 22, 30 |
+| **Policy** | Evaluation, constraints, overrides, governance | doc B, doc 14 |
+
+### 2.2 Four Data Domains
+
+Every entity exists across four data domains simultaneously:
+
+| Domain | Table | Immutability | Test assertion |
+|--------|-------|-------------|----------------|
+| Intent | `intent_records` | Append-only, never modified | `UPDATE` and `DELETE` must fail |
+| Requested | `requested_records` | Append-only, never modified | `UPDATE` and `DELETE` must fail |
+| Realized | `realized_entities` | Versioned snapshots, `is_current` flag | Only one `is_current=true` per entity_uuid |
+| Discovered | `discovered_records` | Ephemeral, grouped by run | Records group by `discovery_run_uuid` |
+
+### 2.3 Pipeline Stages
+
+Every request flows through these stages in order:
+
+```
+1. Intent captured           → intent_records
+2. Layers assembled          → core + service + tenant layers merged with precedence
+3. Policies evaluated        → multi-pass convergence with Evaluation Context
+4. Placement selected        → provider chosen from constrained candidates
+5. Dispatched to provider    → payload sent via Operator Interface
+6. Provider callback         → realized state received
+7. Audit recorded            → Merkle tree leaf per mutation
+```
+
+### 2.4 Entity Types
+
+| Type | Has persistent resources? | Lifecycle |
+|------|--------------------------|-----------|
+| `infrastructure_resource` | Yes | Full CRUD lifecycle |
+| `composite_resource` | Yes (composed of children) | Parent lifecycle governs children |
+| `process_resource` | No — ephemeral | PENDING → EXECUTING → terminal |
+| `shared_resource` | Yes — multi-tenant | Stakeholder management |
+| `allocatable_pool` | Yes — capacity tracking | Allocation/release |
+
+### 2.5 Provider Types
+
+| Type | Test scenarios |
+|------|---------------|
+| `service_provider` | Provisioning, decommission, discovery, drift |
+| `information_provider` | Data queries, confidence scoring, staleness |
+| `compound_service` | Composition, dependency ordering, partial failure, compensation |
+| `auth_provider` | Login, token validation, group claim mapping, session revocation |
+| `peer_dcm` | Federation, cross-instance placement, entity migration |
+| `process_provider` | Job execution, timeout, cancellation, result reporting |
+
+### 2.6 Lifecycle Operation Types
+
+| Operation | When | Full pipeline? | Placement? |
+|-----------|------|---------------|------------|
+| `initial_provisioning` | New resource | Yes | Yes |
+| `update` | Modify existing | Partial — changed fields only | Only if placement-affecting fields changed |
+| `scale` | Capacity change | Partial | Only if zone/provider-affecting |
+| `rehydration` | Rebuild from state | Yes — all current policies | Yes |
+| `decommission` | Remove resource | Decommission pipeline | No |
+| `ownership_transfer` | Change tenant | Re-evaluate tenant policies | Possible |
+| `subscription_renewal` | Subscription lifecycle | Subscription policies | Possible |
+| `drift_remediation` | State divergence | Remediation policies | Possible |
+| `provider_migration` | Move provider | Yes — full re-evaluation | Yes |
+| `compliance_rescan` | Policy change | Re-evaluate all matching | Only if policy requires |
+
+---
+
+## 3. Policy Behavior Rules (Test Invariants)
+
+### 3.1 Match Rules
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| MATCH-001 | A policy with no conditions fires on every request | Create universal policy → verify it fires on 10 random requests |
+| MATCH-002 | A policy matching `resource_type: X` does not fire on resource_type Y | Create VM policy → verify it does not fire on Network.Port request |
+| MATCH-003 | `condition_logic: all` requires all conditions to match | Create 3-condition policy → verify it fires only when all 3 match |
+| MATCH-004 | `condition_logic: any` requires at least one condition | Create 3-condition policy with `any` → verify fires when 1 matches |
+| MATCH-005 | Policies can match on `operation.type` | Create policy scoped to `rehydration` → verify it does not fire on `update` |
+| MATCH-006 | `changed_field_filter` limits update/scale policy firing | Create policy with filter `["memory_gb"]` → verify it fires on memory change, not on tag change |
+| MATCH-007 | Policies can match on `context.constraints.*` | Create policy that fires when `zone_restriction` exists in context → verify ordering |
+
+### 3.2 Evaluation Rules
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| EVAL-001 | GateKeeper DENY blocks the request regardless of other policies | One ALLOW + one DENY → request blocked |
+| EVAL-002 | Domain precedence: more-specific domain wins at same concern | System allows, tenant denies → denied for that tenant |
+| EVAL-003 | Hard enforcement cannot be relaxed by downstream rules | System hard DENY → tenant ALLOW does not relax |
+| EVAL-004 | Multi-pass convergence: max 3 passes then fail | Create irreconcilable constraints → verify failure after 3 passes |
+| EVAL-005 | Constraint Type Registry: unregistered constraint types rejected at activation | Policy emitting unknown constraint type → activation fails |
+| EVAL-006 | Template parameter validation: wrong types rejected | Create policy from template with invalid params → rejected |
+| EVAL-007 | Evaluation Context is transient — does not persist between requests | Request A produces context → Request B does not see it |
+| EVAL-008 | All policies produce audit records regardless of outcome | 5 policies fire → 5 audit leaves created |
+
+### 3.3 Override Rules
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| OVRD-001 | Override Policy cannot target hard enforcement policy | Create override targeting hard policy → activation rejected |
+| OVRD-002 | Exception Grant targeting hard policy requires dual-approval | Create grant without dual-approval → rejected |
+| OVRD-003 | Manual Override of hard policy requires dual-approval | Submit manual override without second approver → rejected |
+| OVRD-004 | Manual Override is single-request scoped | Use manual override → verify second request is still blocked |
+| OVRD-005 | Exception Grant expires at declared time | Create grant expiring in 1 hour → verify grant inactive after expiry |
+| OVRD-006 | Exception Grant usage tracking increments | Use grant 3 times → verify usage_count = 3 |
+| OVRD-007 | fsi/sovereign profiles require dual-approval on ALL overrides | Standard-profile override without dual → accepted. Sovereign-profile → rejected |
+| OVRD-008 | Every override produces an audit leaf | Manual override → verify audit leaf with override_target and justification |
+| OVRD-009 | Compensating controls are enforced during exception grant | Grant requires enhanced-logging → verify field-granularity audit active during grant |
+| OVRD-010 | Blocked request enters PENDING_OVERRIDE when no automatic resolution exists | Hard deny with no override policy/grant → verify status == PENDING_OVERRIDE |
+| OVRD-011 | override.required event published when request enters PENDING_OVERRIDE | Block request → verify event published with blocking_policy and eligible_roles |
+| OVRD-012 | Pipeline resumes from blocked stage after approval | Block at policy eval → approve → verify placement runs (not full restart) |
+| OVRD-013 | Override timeout fails request with OVERRIDE_TIMEOUT | Block request → wait past timeout → verify status == OVERRIDE_TIMEOUT |
+| OVRD-014 | Dual-approval: first approval alone does not resume pipeline | Hard block → first approve → verify status still PENDING_OVERRIDE |
+| OVRD-015 | Dual-approval: same role cannot fill both slots | First approve as security_officer → second approve as security_officer → rejected |
+| OVRD-016 | Override notification routes to correct channels per config | Configure webhook → block request → verify webhook called with correct payload |
+| OVRD-017 | Escalation fires after configured delay with no response | Configure 1h escalation → block request → wait 1h → verify escalation notification |
+| OVRD-018 | Blocked request presents resolution guidance with compliant values | Block on zone policy → verify guidance includes allowed zones |
+| OVRD-019 | Consumer modify action re-enters pipeline from assembly | Block → modify to compliant value → verify pipeline resumes and succeeds |
+| OVRD-020 | Consumer cancel action moves request to CANCELLED | Block → cancel → verify status == CANCELLED with audit trail |
+| OVRD-021 | Block timeout auto-cancels request | Block request → wait past block timeout → verify status == CANCELLED |
+| OVRD-022 | Consumer cannot request override if profile prohibits it | Configure profile to prohibit consumer overrides → block → verify override option unavailable |
+
+### 3.4 Lifecycle Scope Rules
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| LSCOPE-001 | Policy with `lifecycle_scope: [initial_provisioning]` does not fire on update | Provision VM → update memory → verify policy fired on first, not second |
+| LSCOPE-002 | fsi/sovereign cannot downgrade GateKeeper lifecycle scope below `all` | Create GateKeeper with scope `[initial_provisioning]` in sovereign profile → rejected |
+| LSCOPE-003 | `changed_field_filter` is ignored for non-update operations | Initial provisioning with filter → filter ignored, policy fires |
+| LSCOPE-004 | Default lifecycle scopes apply when not explicitly declared | Transformation policy with no scope → fires on `initial_provisioning` and `rehydration`, not on `scale` |
+
+---
+
+## 4. Data Integrity Invariants
+
+### 4.1 Append-Only
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| DATA-001 | `intent_records` cannot be updated or deleted | Attempt UPDATE → verify failure |
+| DATA-002 | `requested_records` cannot be updated or deleted | Attempt DELETE → verify failure |
+| DATA-003 | `audit_records` cannot be updated or deleted | Attempt UPDATE by dcm_admin → verify failure (trigger guard) |
+
+### 4.2 Merkle Tree
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| MRKL-001 | Every audit leaf has a valid signature | Fetch N random leaves → verify signatures against known keys |
+| MRKL-002 | Inclusion proof verifies against current STH | Fetch random leaf → get inclusion proof → verify against latest STH |
+| MRKL-003 | Consistency proof verifies append-only | Get STH at time T1 and T2 → consistency proof must validate |
+| MRKL-004 | Request chain verification: output_hash[N] == input_hash[N+1] | Fetch all leaves for a request → verify hash chain |
+| MRKL-005 | Tampering detection: modify any leaf → verification fails | Insert, verify, modify, re-verify → failure detected |
+| MRKL-006 | Granularity level controls leaf count | Stage → ~6 leaves; mutation → ~15-30; field → same count, richer content |
+
+### 4.3 Tenant Isolation
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| RLS-001 | dcm_app with tenant A context cannot read tenant B data | Set context to A → query B's records → zero rows |
+| RLS-002 | RLS applies to all tenant-scoped tables | For each table with tenant_uuid: verify isolation |
+| RLS-003 | dcm_admin bypasses RLS (separately audited) | Admin query → returns cross-tenant data + audit record |
+
+---
+
+## 5. Provider Contract Invariants
+
+| Rule ID | Invariant | Test |
+|---------|-----------|------|
+| PROV-001 | Unregistered provider cannot receive dispatch | Attempt dispatch to unknown UUID → rejected |
+| PROV-002 | Provider health check failure → provider marked degraded | Stop health endpoint → verify status change |
+| PROV-003 | Provider callback must be signed with registered key | Callback with invalid signature → rejected |
+| PROV-004 | Naturalization/denaturalization preserves entity UUID | Dispatch → callback → verify entity_uuid unchanged |
+| PROV-005 | Meta provider failure triggers compensation | Fail constituent 2 of 3 → verify constituent 1 compensated |
+| PROV-006 | Discovery produces discovered_records grouped by run | Trigger discovery → verify records share discovery_run_uuid |
+| PROV-007 | Process provider reaches terminal state | Execute job → verify state is COMPLETED or FAILED, never OPERATIONAL |
+
+---
+
+## 6. Scenario Generation Rules
+
+The framework generates random scenarios by combining:
+
+### 6.1 Request Dimensions
+
+| Dimension | Random selection from |
+|-----------|---------------------|
+| Resource type | Any registered resource type in catalog |
+| Tenant | Random tenant from test tenant pool |
+| Actor | Random actor with varying roles |
+| Operation type | Random from 10 lifecycle operations |
+| Profile | Random deployment profile |
+| Data classification | Random from classification enum |
+| Sovereignty zone | Random from available zones |
+| Fields | Random field values within resource type schema |
+
+### 6.2 Policy Environment
+
+| Dimension | Random selection from |
+|-----------|---------------------|
+| Active policies | Random subset of policy library (1-20 policies) |
+| Policy domains | Random domain assignments (system, platform, tenant) |
+| Hard vs soft | Random enforcement levels |
+| Constraint types | Random constraint emissions |
+| Override state | Random active overrides (0-3 per scenario) |
+| Exception grants | Random active grants (0-2 per scenario) |
+| Profile constraints | Random profile with minimum lifecycle scope rules |
+
+### 6.3 Provider Environment
+
+| Dimension | Random selection from |
+|-----------|---------------------|
+| Available providers | Random subset (1-5 per resource type) |
+| Provider health | Random health states (healthy, degraded, unavailable) |
+| Capacity | Random capacity levels |
+| Sovereignty zones | Random zone assignments per provider |
+| Accreditation | Random accreditation states |
+
+### 6.4 Expected Outcome Determination
+
+For each generated scenario, the framework must be able to independently compute the expected outcome:
+
+1. Which policies should fire (based on match conditions + lifecycle scope + operation type)
+2. What constraints should be emitted (based on policy outputs)
+3. Whether conflicts exist (based on constraint compatibility)
+4. How conflicts resolve (based on on_conflict strategies)
+5. Whether the request should succeed, fail, or escalate
+6. What the audit trail should contain (based on granularity level)
+7. Which overrides (if any) modify the evaluation
+
+If the framework cannot determine the expected outcome from the architecture rules, that indicates an ambiguity in the architecture that needs resolution.
+
+---
+
+## 7. Edge Case Categories
+
+The framework must specifically test:
+
+### 7.1 Policy Interaction Edge Cases
+- Two policies emitting contradictory hard constraints with no resolution strategy
+- Policy A emits constraint → Policy B consumes it → Policy A changes on next pass → does B re-evaluate?
+- Override policy targeting a policy that was deprecated between override creation and evaluation
+- Exception grant expired mid-evaluation (between pass 1 and pass 2)
+- Manual override authorized by actor who is deprovisioned before the request completes
+- Compensating control policy that itself has a policy violation
+- Circular constraint dependencies (A constrains B, B constrains A)
+
+### 7.2 Provider Edge Cases
+- Provider becomes unavailable between placement and dispatch
+- Provider callback arrives after request timeout
+- Meta provider: constituent 3 fails, compensation for constituent 1 also fails
+- Discovery returns entity with confidence: unmatched while entity is in OPERATIONAL state
+- Two providers claim the same external resource ID
+
+### 7.3 Data Integrity Edge Cases
+- Concurrent requests for the same entity (race condition)
+- Audit leaf with timestamp before previous leaf (clock skew)
+- Request that produces 0 audit leaves (should be impossible — test it)
+- Merkle tree STH computed during concurrent writes
+- Sovereignty zone boundary crossed during multi-pass evaluation
+
+### 7.4 Lifecycle Edge Cases
+- Decommission request while update request is in-flight for same entity
+- Rehydration from intent where the original policy set no longer exists
+- Scale operation that triggers placement re-evaluation (crosses zone capacity threshold)
+- Ownership transfer where target tenant has different compliance profile
+- Subscription renewal where provider has since been deregistered
+
+---
+
+## 8. Framework Output
+
+### 8.1 Test Results
+
+Each test run produces:
+- Pass/fail for each invariant tested
+- Scenario description (generated inputs + expected outcome + actual outcome)
+- Mismatch details (expected vs actual with diff)
+- Audit trail verification results
+
+### 8.2 Architecture Enhancement Proposals
+
+When the framework discovers a gap (an edge case with no defined behavior), it produces:
+
+```yaml
+enhancement_proposal:
+  discovered_by: "test-run-2026-04-04-uuid"
+  scenario: "Description of the edge case"
+  gap: "What the architecture doesn't define"
+  affected_documents: ["B-policy-contract.md", "16-universal-audit.md"]
+  proposed_resolution: "Description of proposed fix"
+  invariant_to_add: "New rule ID and assertion"
+```
+
+These proposals feed back into the architecture review cycle. The framework is a continuous validation mechanism, not a one-time test suite.
+
+---
+
+## 9. Machine-Readable Architecture Summary
+
+This section provides the key facts in a format optimized for automated consumption.
+
+```yaml
+dcm_architecture:
+  version: "0.1.0-alpha"
+
+  required_infrastructure:
+    - type: postgresql
+      contract: "SQL, RLS, LISTEN/NOTIFY, JSONB, pgcrypto"
+
+  internal_capabilities:
+    authentication:
+      method: "argon2id + DCM-issued JWT"
+      table: actors
+    secrets:
+      method: "AES-256-GCM envelope encryption"
+      table: secrets
+    events:
+      method: "LISTEN/NOTIFY on pipeline_events"
+      table: pipeline_events
+
+  provider_types: [service, information, meta, auth, peer_dcm, process]
+
+  entity_types: [infrastructure_resource, composite_resource, process_resource, shared_resource, allocatable_pool]
+
+  data_domains:
+    - name: intent
+      table: intent_records
+      immutability: append_only
+    - name: requested
+      table: requested_records
+      immutability: append_only
+    - name: realized
+      table: realized_entities
+      immutability: versioned_snapshots
+    - name: discovered
+      table: discovered_records
+      immutability: ephemeral
+
+  lifecycle_operations:
+    - initial_provisioning
+    - update
+    - scale
+    - rehydration
+    - decommission
+    - ownership_transfer
+    - subscription_renewal
+    - drift_remediation
+    - provider_migration
+    - compliance_rescan
+
+  policy:
+    types: [gatekeeper, validation, transformation, recovery, orchestration_flow, governance_matrix_rule, lifecycle, itsm_action]
+    evaluation_modes: [internal, external]
+    match_sources: [request_payload, operation_context, evaluation_context, entity_metadata]
+    max_evaluation_passes: 3
+    constraint_resolution: [auto, escalate, deny]
+    override_mechanisms: [override_policy, exception_grant, manual_override, dual_approval, compensating_control]
+
+  audit:
+    structure: merkle_tree
+    standard: "RFC 9162"
+    granularity_levels: [stage, mutation, field]
+    verification: [inclusion_proof, consistency_proof, request_chain_verification]
+    signing_algorithm: "Ed25519 or ECDSA-P256"
+    hash_algorithm: "SHA-256"
+
+  control_plane_services:
+    - api_gateway
+    - service_provider_manager
+    - catalog_manager
+    - policy_manager
+    - placement_manager
+    - request_orchestrator
+    - request_processor
+    - audit_service
+    - discovery_service
+
+  sql_tables: 18
+  capabilities: 331
+  capability_domains: 39
+  consumer_api_paths: 74
+  admin_api_paths: 61
+  event_payloads: 109
+  event_domains: 23
+```
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/A-provider-contract.md b/content/docs/architecture/data-model/A-provider-contract.md
new file mode 100644
index 0000000..15a1634
--- /dev/null
+++ b/content/docs/architecture/data-model/A-provider-contract.md
@@ -0,0 +1,389 @@
+# DCM — Unified Provider Contract
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Foundation
+**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Policy Contract](B-policy-contract.md) | [Registration Specification](../specifications/dcm-registration-spec.md) | [Governance Matrix](27-governance-matrix.md) | [Accreditation](26-accreditation-and-authorization-matrix.md)
+
+---
+
+> > **Design Priority:** Provider types implement all four design priorities simultaneously. Security properties (mTLS, scoped credentials, sovereignty declarations, accreditation) are present in all provider registrations. The capability extension model (Priority 3) enables new provider types without changing the base contract. See [Design Priorities](00-design-priorities.md).
+
+## 1. The Unified Provider Contract
+
+Every Provider in DCM — regardless of type — implements a single base contract. What varies between provider types is the **capability extension**: the specific operations exposed, the data that flows in each direction, and the typed schemas for that exchange.
+
+```
+┌─────────────────────────────────────────────────────────┐
+│                BASE PROVIDER CONTRACT                    │
+│                                                          │
+│  Registration · Health · Sovereignty · Accreditation    │
+│  Governance Matrix · Zero Trust · Lifecycle              │
+│                                                          │
+│  ┌──────────────────────────────────────────────────┐   │
+│  │           CAPABILITY EXTENSION                   │   │
+│  │                                                  │   │
+│  │  What operations this provider type exposes.     │   │
+│  │  What data flows in which direction.             │   │
+│  │  What schemas govern the exchange.               │   │
+│  └──────────────────────────────────────────────────┘   │
+└─────────────────────────────────────────────────────────┘
+```
+
+**Adding a new provider type** = implement the base contract + define a capability extension. No changes to the core required.
+
+---
+
+## 2. Base Contract — Registration
+
+All providers register through the same pipeline. See [Registration Specification](../specifications/dcm-registration-spec.md) for the complete flow.
+
+```yaml
+provider_base_registration:
+  # Standard artifact metadata
+  artifact_metadata:
+    uuid: 
+    handle: "//"    # e.g., "org/compute/eu-west-prod-1"
+    version: "1.0.0"
+    status: submitted                      # submitted → validating → active
+    owned_by: { display_name: "" }
+
+  provider_type_id:                # from Provider Type Registry
+  display_name: ""
+  description: ""
+
+  # All providers declare these
+  sovereignty_declaration:
+    operating_jurisdictions: []
+    data_residency_zones: []
+    sub_processors: []                   # third parties with data access
+
+  accreditations:
+    - accreditation_uuid:          # reference to registered accreditation
+      framework: 
+      status: active
+
+  # Endpoints (which endpoints are required varies by type — see extensions)
+  health_endpoint: "https:///health"
+
+  # Zero trust identity
+  certificate:
+    pem: 
+    ca_chain: 
+    rotation_interval: P90D
+```
+
+**Registration lifecycle states:**
+```
+SUBMITTED → VALIDATING → PENDING_APPROVAL → ACTIVE
+                       ↘ REJECTED
+ACTIVE → SUSPENDED | DEREGISTERING → DEREGISTERED | FORCED_DEREGISTERED
+```
+
+---
+
+## 3. Base Contract — Health Check
+
+Every provider implements a health endpoint. DCM calls it on the declared interval.
+
+```
+GET {health_endpoint}
+
+Response 200:
+{
+  "status": "healthy | degraded | unhealthy",
+  "version": "",
+  "capabilities_available": [""],
+  "details": { }    # provider-specific; DCM treats as opaque
+}
+```
+
+**DCM response to health states:**
+- `healthy` → normal operations; next poll scheduled
+- `degraded` → reduced routing preference; platform admin notified (medium urgency)
+- `unhealthy` / no response → after `failure_threshold`: status → DEGRADED; new requests not routed
+- After 2× `failure_threshold`: status → UNAVAILABLE; drift detection triggered on all hosted entities
+
+---
+
+## 4. Base Contract — Governance Matrix Enforcement
+
+Every interaction with every provider is evaluated against the Governance Matrix before data crosses the boundary. This is not optional and not configurable per provider — it is a base contract requirement.
+
+```
+Outbound interaction (DCM → Provider):
+  1. Classify all fields in the payload by data_classification
+  2. Resolve provider's active accreditations
+  3. Evaluate Governance Matrix: permitted | strip_field | deny | redact
+  4. Apply field permissions
+  5. Audit record written (regardless of outcome)
+  6. If DENY: interaction blocked; entity enters PENDING_REVIEW if appropriate
+
+Inbound interaction (Provider → DCM):
+  1. Authenticate provider identity (mTLS)
+  2. Verify credential scope matches the operation
+  3. Accept payload; apply data_classification tags
+  4. Store in appropriate store per data_classification
+```
+
+---
+
+## 5. Base Contract — Zero Trust
+
+All provider interactions operate under the active zero trust posture. Minimum requirement for all providers at all profiles:
+
+- Mutual TLS authentication on every call (both sides present certificates)
+- Scoped, short-lived interaction credentials (not long-lived API keys)
+- Every call authenticated; no implicit trust from network position or prior calls
+- Certificate rotation on declared interval
+
+Higher profiles add: certificate pinning, per-message signing, hardware attestation.
+
+---
+
+## 6. Base Contract — Provider Lifecycle Events
+
+Providers must report state changes via lifecycle events. This is a base contract obligation — not optional:
+
+```json
+POST {dcm_lifecycle_endpoint}
+{
+  "event_uuid": "",
+  "event_type": "",
+  "provider_uuid": "",
+  "affected_entity_uuids": [""],
+  "event_timestamp": "",
+  "severity": "INFO | WARNING | CRITICAL"
+}
+```
+
+---
+
+
+## 7. Capability Extensions — Provider Types
+
+DCM defines five provider types. Each shares the base contract (Section 1–6) and adds a typed capability extension declaring what the provider can do.
+
+### 7.1 Service Provider
+
+**What it does:** Realizes infrastructure resources. Receives assembled payloads, provisions the resource, returns realized state. Service providers also cover credential management (Credential.* resource types via Vault or similar), notification delivery (Notification.* resource types), and ITSM integration (ITSM.* resource types) — these are service providers with specific resource type declarations, not separate provider types.
+
+**Additional endpoints:**
+```
+POST {dispatch_endpoint}         # receive and execute dispatch payload
+POST {cancel_endpoint}           # receive cancellation request (if supported)
+POST {discover_endpoint}         # receive discovery request; return discovered state
+GET  {capabilities_endpoint}     # return available options (networks, images, storage classes)
+```
+
+**Capability declaration extension:**
+```yaml
+service_provider_capabilities:
+  resource_types:
+    - fqn: Compute.VirtualMachine
+      spec_version: "2.1.0"
+      catalog_item_uuid: 
+  cancellation:
+    supports_cancellation: true
+    cancellation_supported_during: [DISPATCHED, PROVISIONING]
+  discovery:
+    supports_discovery: true
+    discovery_method: api_query | passive_event | hybrid
+  naturalization:
+    target_format: openstack_nova | vmware_vsphere | custom
+  cost_metadata:
+    opex_per_unit_per_hour: 0.28
+    currency: USD
+```
+
+**Data direction:** DCM sends assembled Requested State → Provider naturalizes → executes → denaturalizes → returns Realized State.
+
+---
+
+### 7.2 Information Provider
+
+**What it does:** Serves authoritative external data to enrich DCM's understanding of resources and business context.
+
+**Additional endpoints:**
+```
+POST {query_endpoint}            # receive query; return data in DCM unified format
+POST {write_back_endpoint}       # optional; receive DCM updates to push to source system
+```
+
+**Capability declaration extension:**
+```yaml
+information_provider_capabilities:
+  data_domains:
+    - domain: business_data
+      data_types: [business_unit, cost_center, product_owner]
+      authority_level: primary | secondary | supplementary
+  query_capacity:
+    max_queries_per_second: 100
+  confidence_model:
+    data_freshness_sla: PT1H
+  write_back_supported: false
+```
+
+**Data direction:** DCM sends lookup query → Provider returns data in DCM format → DCM enriches entity fields.
+
+---
+
+### 7.3 Compound Service Definitions (formerly Meta Provider)
+
+> **Architecture update:** Compound service composition is handled by the DCM control plane via Compound Resource Type Specifications in the Resource Type Registry. There is no separate Meta Provider type. Service providers fulfill individual constituents; DCM handles decomposition, dependency resolution, binding field injection, and compensation. See doc 05 (Resource Type Hierarchy) and doc 30 (Compound Resource Type Specifications) for the full model.
+
+**What it does:** Composes multiple child providers to deliver a compound service as a single catalog item. The Meta Provider declares a compound service definition — constituent resource types, dependencies, and delivery requirements — so DCM can place, sequence, and govern the constituents. For its own resource types (`provided_by: self`), the Meta Provider executes as a standard Service Provider. All orchestration, placement, sequencing, failure handling, and compensation is performed by DCM using the declared dependency graph.
+
+> **Full specification:** See [Meta Provider Composability Model](30-meta-provider-model.md) for the complete orchestration contract, four-state model, failure propagation, compensation, and system policies (MPX-001–MPX-008).
+
+**Capability declaration extension (summary — full schema in doc 30):**
+```yaml
+compound_service_capabilities:
+  constituent_provider_types: [service_provider, information_provider]
+  composition_model:
+    execution: dependency_ordered
+    max_concurrent_realizations: 10
+    max_constituent_count: 20
+    max_nesting_depth: 3
+  partial_delivery_supported: true
+  compensation_supported: true
+  resource_types_composed:
+    - fqn: ApplicationStack.WebApp
+      version: "2.0.0"
+      constituents:
+        - resource_type: Compute.VirtualMachine
+          required_for_delivery: required
+        - resource_type: Network.IPAddress
+          required_for_delivery: required
+        - resource_type: DNS.Record
+          required_for_delivery: partial
+      composition_visibility: selective
+```
+
+**Composite status determination:**
+- `REALIZED` — all required constituents succeeded
+- `DEGRADED` — required constituents succeeded; one or more partial constituents failed
+- `FAILED` — one or more required constituents failed → compensation executes
+
+**Data direction:** DCM sends fully assembled compound payload → Meta Provider orchestrates constituents in dependency order → aggregates realized states → returns compound realized state to DCM.
+
+---
+
+### 7.4 Auth Provider
+
+**What it does:** Authenticates actor identities and resolves their roles and group memberships. Multiple auth providers can be registered — tenant routing determines which provider authenticates a given actor.
+
+**Additional endpoints:**
+```
+POST {authenticate_endpoint}     # receive credentials; return auth token + claims
+POST {authorize_endpoint}        # receive token + operation; return allow/deny
+GET  {identity_endpoint}         # return actor claims for a token
+```
+
+**Capability declaration extension:**
+```yaml
+auth_provider_capabilities:
+  authentication_modes: [oidc, ldap, saml, mtls, hardware_token]
+  mfa_methods: [totp, push_notification, hardware_token]
+  rbac_model: flat | hierarchical | abac
+  step_up_supported: true
+  token_lifetime:
+    default: PT1H
+    max: PT8H
+  federation_capable: true
+  supports_session_revocation: true
+```
+
+**Data direction:** Consumer sends credentials → Auth Provider validates → returns token + claims → DCM extracts actor identity.
+
+---
+
+### 7.5 Peer DCM (Federation)
+
+**What it does:** Another DCM instance participating in federation. Treated as a typed Provider with a federation tunnel as the communication channel.
+
+**Capability declaration extension:**
+```yaml
+peer_dcm_capabilities:
+  dcm_version: "1.0.0"
+  tunnel_type: peer | parent_child | hub_spoke
+  deployment_accreditations: []
+  inbound_authorization:
+    - operation: catalog_query
+      resource_types: [Compute.VirtualMachine]
+  outbound_authorization:
+    - operation: placement_query
+      resource_types: [Compute.VirtualMachine]
+  data_boundary:
+    max_classification: restricted
+  trust_posture: verified | vouched | provisional
+```
+
+**Data direction:** Bidirectional within declared authorization scope. Federation tunnel with mTLS, certificate pinning, per-message signing.
+
+---
+
+### 7.6 Process Provider
+
+**What it does:** Executes ephemeral workflows to completion. Unlike service providers that manage persistent resource lifecycle (create → operate → decommission), process providers execute a job and report a result. No persistent resource is created — the entity type is `process_resource` which reaches a terminal state on completion.
+
+**Use cases:** Software installation, backup execution, compliance scan, data migration, certificate rotation, patch application, report generation.
+
+**Additional endpoints:**
+```
+POST {execute_endpoint}          # receive job payload; begin execution
+GET  {status_endpoint}/{job_id}  # poll execution status
+POST {cancel_endpoint}/{job_id}  # cancel running execution (if supported)
+```
+
+**Capability declaration extension:**
+```yaml
+process_provider_capabilities:
+  supported_process_types:
+    - "Process.SoftwareInstall"
+    - "Process.BackupExecution"
+    - "Process.ComplianceScan"
+    - "Process.DataMigration"
+  max_concurrent_executions: 10
+  timeout_default: PT30M
+  idempotent: true
+  cancellation_supported: true
+  automation_platform: aap | tekton | argo_workflows | direct_api
+```
+
+**Data direction:** DCM sends job payload → Process Provider executes → reports progress via status polling or callback → returns result payload on completion. Result payload follows standard denaturalization — provider-native output translated to DCM unified format.
+
+**Lifecycle:** `PENDING → EXECUTING → COMPLETED | FAILED | CANCELLED`. No ongoing lifecycle management — process resources reach a terminal state and stay there.
+
+---
+
+## 8. Provider Type Registry
+
+The Provider Type Registry is the authoritative list of provider types that a DCM deployment accepts registrations for. It follows the three-tier registry model (Core / Verified Community / Organization).
+
+```yaml
+provider_type_registry_entry:
+  provider_type_id: service_provider
+  tier: core
+  default_approval_method: reviewed   # auto | reviewed | verified | authorized
+  enabled_in_profiles: [minimal, dev, standard, prod, fsi, sovereign]
+  capability_extension_schema_ref: 
+```
+
+Profile-governed approval methods override provider type defaults. See [Registration Specification](../specifications/dcm-registration-spec.md) Section 3 for the complete approval method resolution model.
+
+---
+
+## 9. Related Policies
+
+| Policy | Rule |
+|--------|------|
+| `PRV-001` | All providers implement the base contract. No provider is exempt from registration, health check, sovereignty declaration, governance matrix enforcement, or zero trust authentication. |
+| `PRV-002` | Governance Matrix evaluation occurs before every provider interaction. It is not configurable per provider and cannot be bypassed. |
+| `PRV-003` | Provider capability declarations are verified at registration. Capabilities not declared at registration cannot be invoked after activation. |
+| `PRV-004` | Peer DCM instances are treated as typed providers. Federation is the Provider abstraction applied across DCM instances — not a separate abstraction. |
+| `PRV-005` | Adding a new provider type requires implementing the base contract and defining a capability extension. No changes to DCM core are required. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/B-policy-contract.md b/content/docs/architecture/data-model/B-policy-contract.md
new file mode 100644
index 0000000..c2c7c1b
--- /dev/null
+++ b/content/docs/architecture/data-model/B-policy-contract.md
@@ -0,0 +1,1344 @@
+# DCM — Unified Policy Contract
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Foundation
+**Related Documents:** [Foundational Abstractions](00-foundations.md) | [Provider Contract](A-provider-contract.md) | [Policy Profiles](14-policy-profiles.md) | [Governance Matrix](27-governance-matrix.md) | [OPA Integration](../specifications/dcm-opa-integration-spec.md)
+
+---
+
+## 1. The Unified Policy Contract
+
+Every Policy in DCM — regardless of type — implements a single base contract. What varies between policy types is the **output schema**: what the Policy produces when its match conditions are satisfied.
+
+```
+┌─────────────────────────────────────────────────────────┐
+│                 BASE POLICY CONTRACT                     │
+│                                                          │
+│  Match Conditions · Enforcement Level · Domain          │
+│  Lifecycle · Audit · Shadow Mode                         │
+│                                                          │
+│  ┌──────────────────────────────────────────────────┐   │
+│  │              OUTPUT SCHEMA                       │   │
+│  │                                                  │   │
+│  │  What this policy type produces when it fires.   │   │
+│  │  Eight typed output schemas.                     │   │
+│  └──────────────────────────────────────────────────┘   │
+└─────────────────────────────────────────────────────────┘
+```
+
+**Adding a new policy type** = define a new output schema. The base contract, evaluation algorithm, lifecycle, and audit obligations are inherited.
+
+---
+
+## 2. Base Contract — Match Conditions
+
+**A policy fires when the data says it should fire.** There is no pre-assignment of policies to resource types, no routing tables, no static configuration. A policy declares its match conditions against data fields. If the data matches, the policy evaluates. Any piece of data in the request can be an inclusion trigger for a policy.
+
+### 2.1 Four Match Sources
+
+Policies can match against data from four sources, all available during evaluation:
+
+| Source | What it contains | Examples |
+|--------|-----------------|---------|
+| **Request payload** | Consumer's declared fields + assembled layers + provenance | `resource_type`, `cpu_count`, `network_segment`, `sovereignty_zone`, `environment`, `application_uuid`, custom tags |
+| **Operation context** | What lifecycle operation is being performed and what changed | `operation.type`, `operation.changed_fields`, `operation.pipeline_stage` |
+| **Evaluation Context** | Constraints emitted by policies earlier in this evaluation pass | `allowed_zones`, `distribution_requirement`, `cost_ceiling`, `excluded_providers` |
+| **Entity metadata** | Tenant, actor, resource classification, lifecycle state | `tenant_uuid`, `actor.roles`, `data_classification`, `lifecycle_state`, `cost_center` |
+
+All four sources are addressable via dot-notation field paths.
+
+### 2.2 Lifecycle Operation Types
+
+Every request into DCM carries an `operation.type` that identifies what lifecycle operation is being performed. This is the primary dimension for scoping which policies fire on which operations.
+
+| Operation Type | When it occurs | Description |
+|---------------|---------------|-------------|
+| `initial_provisioning` | Day 0 — first request for a new resource | Full pipeline: assembly, all policies, placement, dispatch |
+| `update` | Day 2 — modify an existing operational resource | Partial pipeline: assembly with existing entity as base, policies evaluate changed fields |
+| `scale` | Day 2 — change capacity (replicas, CPU, memory, storage) | Subset of update — only scaling-related fields change |
+| `rehydration` | Rebuild from stored state (DR, migration, refresh) | Full pipeline: all current policies re-evaluate from intent or realized state |
+| `decommission` | Day N — request to remove a resource | Decommission pipeline: dependency checks, notification, revocation, provider teardown |
+| `ownership_transfer` | Transfer entity between tenants | Re-evaluates tenant-scoped policies under new tenant context |
+| `subscription_renewal` | Subscription approaching expiry or renewal | Subscription lifecycle policies fire; may trigger re-evaluation of placement |
+| `drift_remediation` | Discovered state diverges from realized state | Remediation policies fire; may re-provision or accept drift |
+| `provider_migration` | Move entity to a different provider | Full placement re-evaluation; sovereignty re-evaluation |
+| `compliance_rescan` | Policy change triggers re-evaluation of existing resources | All matching policies re-evaluate; no placement unless policy requires it |
+
+**`operation.changed_fields`** — For `update` and `scale` operations, this is an array of field paths that changed from the current realized state. This enables policies to fire only when relevant fields change:
+
+```yaml
+# Fire only when placement-affecting fields change
+match:
+  conditions:
+    - field: operation.type
+      operator: in
+      value: [initial_provisioning, rehydration, provider_migration]
+  condition_logic: any
+
+# OR: fire on updates, but only when network or zone fields changed
+match:
+  conditions:
+    - field: operation.type
+      operator: equals
+      value: "update"
+    - field: operation.changed_fields
+      operator: contains
+      value: "placement.zone"
+  condition_logic: all
+```
+
+### 2.3 Policy Lifecycle Scope
+
+Each policy declares which lifecycle operations it applies to. This is configured per policy via `lifecycle_scope`:
+
+```yaml
+policy_artifact:
+  handle: "placement/sovereignty-zone-check"
+  lifecycle_scope:
+    operations: [initial_provisioning, rehydration, provider_migration]
+    # This policy fires on: new resources, rebuilds, and provider moves
+    # Does NOT fire on: memory updates, scaling, decommissions
+
+policy_artifact:
+  handle: "validation/vm-size-limits"
+  lifecycle_scope:
+    operations: [initial_provisioning, update, scale]
+    changed_field_filter: ["cpu_count", "memory_gb", "storage_gb"]
+    # Fires on new resources AND on updates that change sizing fields
+    # Does NOT fire on: tag changes, owner changes, etc.
+
+policy_artifact:
+  handle: "sovereignty/data-residency"
+  lifecycle_scope:
+    operations: all                         # fires on EVERY lifecycle operation
+    # Sovereignty is always checked — no exceptions
+```
+
+**Default lifecycle scope by policy type:**
+
+| Policy Type | Default `lifecycle_scope.operations` | Rationale |
+|-------------|-------------------------------------|-----------|
+| GateKeeper | `all` | Gatekeeping rules should always be checked |
+| Validation | `[initial_provisioning, update, scale, rehydration]` | Structural validation on any data change |
+| Transformation | `[initial_provisioning, rehydration]` | Inject/enrich on new builds and rebuilds; skip on minor updates |
+| Recovery | `all` | Recovery posture always applicable |
+| Orchestration Flow | `[initial_provisioning, rehydration, decommission]` | Pipeline orchestration on major lifecycle transitions |
+| Governance Matrix Rule | `all` | Boundary controls always checked |
+| Lifecycle | `all` | TTL/expiry constraints always active |
+| ITSM Action | `[initial_provisioning, update, decommission]` | ITSM records on provisioning, changes, and teardown |
+
+These defaults can be overridden per policy. Profile-governed minimums prevent downgrading below security requirements:
+
+| Profile | Minimum lifecycle scope |
+|---------|------------------------|
+| `minimal`, `dev` | No minimum — any scope permitted |
+| `standard`, `prod` | GateKeeper and Governance Matrix must be `all` |
+| `fsi`, `sovereign` | GateKeeper, Governance Matrix, and sovereignty-concern policies must be `all` — cannot be scoped to skip any lifecycle operation |
+
+### 2.4 Specificity Spectrum
+
+The same match model expresses policies at every level of specificity:
+
+```yaml
+# Universal — fires on everything (no conditions)
+match:
+  conditions: []
+
+# Classification-scoped — fires on any resource handling PHI
+match:
+  conditions:
+    - field: request.data_classification
+      operator: in
+      value: ["phi", "pci"]
+
+# Resource-type scoped — fires on all VMs
+match:
+  conditions:
+    - field: request.resource_type
+      operator: equals
+      value: "Compute.VirtualMachine"
+
+# Zone + segment scoped — fires on any resource in DMZ in zone A
+match:
+  conditions:
+    - field: request.network_segment
+      operator: equals
+      value: "dmz"
+    - field: request.placement.zone
+      operator: equals
+      value: "zone-a"
+  condition_logic: all
+
+# Fully specific — VMs in DMZ in zone A for one application
+match:
+  conditions:
+    - field: request.resource_type
+      operator: equals
+      value: "Compute.VirtualMachine"
+    - field: request.network_segment
+      operator: equals
+      value: "dmz"
+    - field: request.placement.zone
+      operator: equals
+      value: "zone-a"
+    - field: request.application_uuid
+      operator: equals
+      value: "xxxx-xxxx-xxxx"
+  condition_logic: all
+
+# Context-aware — fires when sovereignty has restricted zones
+match:
+  conditions:
+    - field: context.constraints.zone_restriction
+      operator: exists
+  condition_logic: all
+```
+
+### 2.5 Match Operators
+
+| Operator | Description |
+|----------|------------|
+| `equals` | Exact match |
+| `not_equals` | Negation |
+| `in` | Value is in a list |
+| `not_in` | Value is not in a list |
+| `exists` | Field is present (any value) |
+| `not_exists` | Field is absent |
+| `minimum` | Numeric ≥ threshold |
+| `maximum` | Numeric ≤ threshold |
+| `contains` | String/array contains substring/element |
+| `matches` | Regex match |
+| `starts_with` | Field path prefix match (for hierarchical resource types) |
+
+`condition_logic: all` (default) requires all conditions to match. `condition_logic: any` requires at least one.
+
+### 2.6 Boundary Match Conditions (Governance Matrix)
+
+Governance Matrix Rules use a four-axis boundary model for matching — subject, data, target, and context. These are structurally the same as field conditions but organized by the four axes of the Governance Matrix:
+
+```yaml
+match:
+  subject:
+    type: 
+    identity: { ... }
+    tenant: { ... }
+  data:
+    classification: 
+    resource_type: 
+    field_paths: { mode: allowlist | blocklist, paths: [...] }
+  target:
+    type: 
+    sovereignty_zone: { match:  }
+    accreditation_held: { includes: [...] }
+    trust_posture: 
+  context:
+    profile: { deployment_posture:  }
+    zero_trust_posture: { minimum:  }
+    federated: true | false
+```
+
+---
+
+## 3. Base Contract — Enforcement Level
+
+```yaml
+enforcement: hard | soft
+
+# hard: cannot be relaxed by any downstream rule at any domain level
+#        A hard DENY cannot be overridden by any Tenant, entity, or operator override
+#        Reserved for: sovereign/classified data boundaries, regulatory hard requirements
+
+# soft: establishes a default that downstream rules can tighten
+#        A soft ALLOW can be restricted to DENY by a more-specific rule
+#        A soft DENY cannot be relaxed to ALLOW by a downstream rule
+```
+
+Most policies are soft. Hard enforcement is reserved for absolute security constraints.
+
+---
+
+## 4. Base Contract — Domain Precedence
+
+Policies operate within a domain hierarchy. More-specific domains win within the same concern type:
+
+```
+system (most trusted — DCM built-in)
+  └── platform (platform admin declared)
+        └── tenant (Tenant admin declared)
+              └── resource_type (per resource type spec)
+                    └── entity (per specific entity — most specific)
+```
+
+Within the same domain level, DENY wins over ALLOW. More-specific domain wins over less-specific.
+
+---
+
+## 5. Base Contract — Artifact Structure
+
+All policies are first-class DCM Data artifacts. They share the standard artifact metadata and lifecycle:
+
+```yaml
+policy_artifact:
+  # Standard DCM artifact metadata (all artifacts carry this)
+  artifact_metadata:
+    uuid: 
+    handle: "//"
+    version: "1.0.0"
+    status: developing | proposed | active | deprecated | retired
+    owned_by: { display_name: "", email: "" }
+    created_by: { display_name: "" }
+    created_via: pr | api | migration | system
+
+  # Policy classification
+  policy_type:                     # gatekeeper | validation | transformation |
+                                         # recovery | orchestration_flow |
+                                         # governance_matrix_rule | lifecycle
+  concern_type:                 # security | compliance | operational |
+                                         # recovery_posture | zero_trust_posture |
+                                         # data_authorization_boundary | orchestration_flow
+
+  domain: system | platform | tenant | resource_type | entity
+
+  # Match conditions (Model A or B — see Section 2)
+  match: { ... }
+
+  # Enforcement
+  enforcement: hard | soft
+
+  # Output schema (varies by policy_type — see Sections 8-14)
+  output: { ... }
+
+  # Audit
+  audit_on: [ALLOW, DENY, STRIP_FIELD]   # which decisions produce audit records
+  notification_on: [DENY]               # which decisions trigger notifications
+  notification_urgency: low | medium | high | critical
+
+  # Compliance reference
+  compliance_basis: ""
+  review_required_before: ""
+```
+
+---
+
+## 6. Base Contract — Lifecycle
+
+All policies follow the five-status lifecycle:
+
+| Status | Behavior |
+|--------|---------|
+| `developing` | Dev mode only. Not applied in any environment. |
+| `proposed` | Shadow mode: executes against real traffic; output captured but never applied. Used for safe validation. |
+| `active` | Applied to all matching requests. |
+| `deprecated` | Still active; replacement available; warning on evaluation. |
+| `retired` | Terminal; cannot be used. |
+
+**Shadow mode (proposed status):** The policy evaluates against real traffic. Its output is captured in the Validation Store. Platform admins review shadow results before promoting to active. This is the primary mechanism for safe policy change management.
+
+---
+
+## 7. Evaluation Model
+
+### 7.1 Evaluation Context
+
+Every request evaluation creates a transient **Evaluation Context** — a shared constraint space that policies read from and write to during evaluation. The context accumulates constraints, hints, and resolutions across evaluation passes.
+
+```yaml
+evaluation_context:
+  request_uuid: 
+  pass_number: 1
+  max_passes: 3                         # configurable; default 3
+
+  # Constraints accumulate — each has provenance
+  constraints:
+    - constraint_uuid: 
+      source_policy: ""
+      source_domain: system | platform | tenant | resource_type | entity
+      constraint_type:          # zone_restriction, distribution_requirement, cost_ceiling, etc.
+      field: ""
+      operator: restrict_to | require | prefer | exclude
+      value: 
+      binding: hard | soft              # hard = cannot be overridden; soft = preference
+      reason: ""
+      pass_added: 
+
+  # Hints flow between policies — transient, never persisted as entity data
+  hints:
+    - from_policy: ""
+      to_concern: ""      # placement, security, compliance, etc.
+      hint_type: 
+      value: 
+      pass_added: 
+
+  # Resolutions record how conflicts were handled
+  resolutions:
+    - conflict: ""
+      strategy:                 # from on_conflict declaration
+      result: ""
+      resolved_by: auto | human | escalation
+      pass_resolved: 
+
+  # Resolved constraint set — what downstream policies and placement use
+  resolved_constraints: { ... }
+```
+
+The Evaluation Context is **transient** — it exists only during request evaluation. Hints are ephemeral. But the complete context snapshot at each pass is captured in the audit record.
+
+### 7.2 Three-Phase Evaluation
+
+Each pass through the policy engine follows three phases:
+
+**Phase 1 — Constraint Collection.** All policies whose match conditions are satisfied evaluate and emit constraints into the Evaluation Context. Sovereignty writes `allowed_zones`. Tier policy writes `min_replicas` and `min_zones`. Cost policy writes `preferred_zones`. No final decisions — only constraint declarations.
+
+**Phase 2 — Constraint Resolution.** The Policy Engine examines collected constraints for conflicts. When constraints conflict, it checks if the conflicting policies declare resolution strategies via `on_conflict`. Auto-resolvable conflicts are resolved and recorded. Unresolvable conflicts are escalated (request paused for human decision).
+
+**Phase 3 — Application and Validation.** Transformations apply using the resolved constraint set. Placement uses the constrained parameters. GateKeepers re-validate the final assembled payload against the full constraint set. If validation fails, the failure is added as a new constraint and the system loops to the next pass.
+
+```
+Pass 1:
+  Phase 1: Collect constraints
+    → sovereignty: allowed_zones = [A, B]
+    → tier: min_replicas = 6, min_zones = 2, distribution = balanced
+  Phase 2: Resolve conflicts
+    → tier wants 3 zones, sovereignty allows 2
+    → tier declares on_conflict.zone_shortage: redistribute
+    → auto-resolve: 3/3 across zones A and B
+  Phase 3: Apply and validate
+    → transformations inject config
+    → placement distributes 3/3
+    → GateKeepers re-validate → PASS
+  → Converged.
+
+Pass 1 (loop scenario):
+  Phase 3: Validate FAILS → cost optimization placed all 6 in zone A
+    but tier requires balanced distribution
+  Pass 2:
+    New constraint: "zone-a max 4 replicas" (from tier validation failure)
+    Re-resolve → 3/3
+    Re-validate → PASS
+  → Converged on pass 2.
+
+Pass 1 (escalation scenario):
+  Phase 2: sovereignty allows 1 zone, tier requires 2+ zones
+    No auto-resolution → both are hard constraints
+  → Request paused. Escalation with conflict report.
+```
+
+Maximum passes is configurable (default 3). If the evaluation does not converge, the request fails with a full conflict report showing every constraint, every conflict, and every attempted resolution.
+
+### 7.3 Constraint Emission
+
+Policies declare what constraints they emit via `emits_constraints` in their artifact:
+
+```yaml
+policy_artifact:
+  handle: "sovereignty/eu-data-residency"
+  policy_type: gatekeeper
+  match:
+    conditions:
+      - field: request.data_classification
+        operator: in
+        value: ["restricted", "phi", "pci"]
+
+  # What this policy contributes to the evaluation context
+  emits_constraints:
+    - field: "placement.allowed_zones"
+      constraint_type: zone_restriction
+      binding: hard
+    - field: "placement.excluded_providers"
+      constraint_type: provider_exclusion
+      binding: hard
+
+  # How to handle conflicts with this policy's constraints
+  on_conflict:
+    default: deny                       # hard sovereignty — no auto-resolution
+```
+
+```yaml
+policy_artifact:
+  handle: "tier/tier-1-ha-distribution"
+  policy_type: transformation
+  match:
+    conditions:
+      - field: request.tier
+        operator: equals
+        value: "tier-1"
+
+  emits_constraints:
+    - field: "placement.zone_distribution"
+      constraint_type: distribution_requirement
+      binding: hard
+
+  on_conflict:
+    zone_shortage: redistribute         # auto-resolve: spread across available zones
+    replica_shortage: deny              # can't reduce replicas — deny
+    default: escalate
+```
+
+### 7.4 Evaluation Order
+
+Within a single pass:
+
+1. **Domain precedence** — system evaluates first, then platform, then tenant, then resource_type, then entity. More-specific domains evaluate after (and can override) less-specific.
+2. **Within a domain level** — policies evaluate in declared priority order.
+3. **Parallel evaluation** — policies with no data dependencies on each other evaluate concurrently within the same domain level and phase.
+4. **Context-dependent policies** — policies matching on `context.constraints.*` evaluate after the policies that emit those constraints.
+5. **DENY wins** — at the same domain level, any DENY blocks regardless of other policies at that level.
+
+### 7.5 Per-Pass Audit
+
+Every evaluation pass produces an audit record. The record captures the complete state — not just the outcome:
+
+```yaml
+policy_evaluation_audit:
+  request_uuid: 
+  pass_number: 
+  evaluation_context_snapshot: { ... }    # full context at this pass
+  policies_evaluated:
+    - policy_uuid: 
+      policy_version: ""
+      matched: true | false
+      output: { ... }
+      constraints_emitted: [...]
+      hints_emitted: [...]
+      duration_ms: 
+  conflicts_detected: [...]
+  resolutions_applied: [...]
+  pass_result: converged | loop | escalated | failed
+  total_duration_ms: 
+```
+
+Every pass, every constraint, every hint, every resolution — fully auditable. If an auditor asks "why 3/3 instead of 2/2/2?" the trail shows: sovereignty restricted zones, tier auto-resolved via redistribute, placement honored resolved constraints.
+
+---
+
+## 8. Constraint Type Registry
+
+Constraint types are the shared vocabulary of the Evaluation Context. Every constraint emitted by a policy and every context field matched by a policy must reference a registered constraint type. Freeform strings are not permitted — if two policies should interact, they must agree on the vocabulary, and the registry enforces that agreement.
+
+### 8.1 Registry Entry
+
+```yaml
+constraint_type:
+  handle: "zone_restriction"
+  version: "1.0.0"
+  tier: core                              # core (DCM built-in) | organization (custom)
+  schema:                                 # OpenAPI v3 schema for the constraint value
+    type: object
+    properties:
+      allowed:
+        type: array
+        items: { type: string }
+        description: "Zones where placement is permitted"
+      excluded:
+        type: array
+        items: { type: string }
+        description: "Zones where placement is prohibited"
+    additionalProperties: false
+  semantic: "Restricts which zones a resource may be placed in"
+  binding_levels: [hard, soft]
+  emittable_by: [gatekeeper, validation, governance_matrix_rule]
+  consumable_by: [transformation, gatekeeper, validation]
+```
+
+### 8.2 Built-In Constraint Types (Core Tier)
+
+| Constraint Type | Schema (key fields) | Emitted by | Consumed by |
+|----------------|---------------------|------------|-------------|
+| `zone_restriction` | `{allowed: [string], excluded: [string]}` | Sovereignty, compliance | Placement, distribution |
+| `provider_restriction` | `{allowed: [uuid], excluded: [uuid]}` | Sovereignty, accreditation | Placement |
+| `distribution_requirement` | `{min_replicas: int, min_zones: int, distribution: enum}` | Tier/HA policies | Placement |
+| `cost_ceiling` | `{max_per_unit_hour: decimal, currency: string}` | Budget policies | Placement, approval |
+| `network_restriction` | `{allowed_segments: [string], excluded: [string]}` | Security, compliance | Transformation, placement |
+| `resource_limits` | `{max_cpu: int, max_memory_gb: int, max_storage_gb: int}` | Tier policies, quotas | Validation |
+| `compliance_requirement` | `{frameworks: [string], controls: [string]}` | Compliance profiles | Validation, transformation |
+| `sovereignty_boundary` | `{data_residency: string, jurisdictions: [string]}` | Sovereignty | All downstream |
+| `approval_requirement` | `{required: bool, approvers: [string], quorum: int}` | Tier, compliance | Approval flow |
+| `scheduling_constraint` | `{maintenance_window: cron, blackout_periods: [range]}` | Operational | Placement, lifecycle |
+
+Organizations can register custom constraint types (same as custom resource types). A financial services org might register `trading_window_restriction` or `pci_scope_boundary`.
+
+### 8.3 Hint Types
+
+Hints are soft, advisory signals — not hard constraints. They follow the same registry pattern:
+
+```yaml
+hint_type:
+  handle: "cost_preference"
+  version: "1.0.0"
+  schema:
+    type: object
+    properties:
+      preferred_zones: { type: array, items: { type: string } }
+      reason: { type: string }
+  semantic: "Advisory preference for lower-cost zones"
+  emittable_by: [transformation, validation]
+  consumable_by: [gatekeeper, transformation]
+```
+
+### 8.4 Validation at Policy Activation
+
+When a policy is promoted from `developing` to `proposed` (shadow mode), the Policy Engine validates:
+
+1. Every `emits_constraints[].constraint_type` is a registered constraint type
+2. The emitted value structure matches the registered schema
+3. The emitting policy's type is in the constraint type's `emittable_by` list
+4. Every `match.conditions[].field` referencing `context.constraints.*` corresponds to a registered constraint type
+5. The consuming policy's type is in the constraint type's `consumable_by` list
+
+If any check fails, the policy cannot be activated. Vocabulary mismatches are caught at authoring time, not when a production request fails silently.
+
+---
+
+## 9. Policy Templates
+
+Policy templates separate reusable Rego logic from instance-specific configuration, following the OPA Gatekeeper ConstraintTemplate pattern. A template defines the logic and declares its parameter schema, emitted constraint types, and consumed constraint types. A policy artifact is an instance of a template with bound parameters and match conditions.
+
+### 9.1 Template Definition
+
+```yaml
+policy_template:
+  handle: "dcm.sovereignty.zone-restriction"
+  version: "1.0.0"
+  tier: core
+
+  # Parameters this template accepts (OpenAPI v3 schema)
+  parameter_schema:
+    type: object
+    required: [classification_levels, allowed_zones]
+    properties:
+      classification_levels:
+        type: array
+        items: { type: string }
+      allowed_zones:
+        type: array
+        items: { type: string }
+
+  # Registered constraint types this template emits
+  emits: [zone_restriction, provider_restriction]
+
+  # Registered constraint types this template reads from context
+  consumes: []
+
+  # Rego logic
+  rego: |
+    package dcm.sovereignty.zone_restriction
+    import data.dcm.constraint_types
+
+    emit_constraint[constraint] {
+      input.request.data_classification == input.parameters.classification_levels[_]
+      constraint := constraint_types.zone_restriction({
+        "allowed": input.parameters.allowed_zones,
+        "excluded": [],
+      })
+    }
+
+    deny[msg] {
+      not input.request.placement.zone == input.parameters.allowed_zones[_]
+      msg := sprintf("Zone %v not in allowed zones %v for %v data",
+        [input.request.placement.zone,
+         input.parameters.allowed_zones,
+         input.request.data_classification])
+    }
+```
+
+### 9.2 Policy Artifact (Template Instance)
+
+```yaml
+policy_artifact:
+  handle: "sovereignty/eu-data-residency"
+  template: "dcm.sovereignty.zone-restriction"
+  version: "1.0.0"
+  domain: system
+  policy_type: gatekeeper
+
+  parameters:
+    classification_levels: [restricted, phi, pci]
+    allowed_zones: [eu-west-1, eu-central-1]
+
+  match:
+    conditions:
+      - field: request.data_classification
+        operator: in
+        value: [restricted, phi, pci]
+
+  enforcement: hard
+  on_conflict:
+    default: deny
+```
+
+### 9.3 DCM Constraint Types Library
+
+DCM provides a Rego library (`data.dcm.constraint_types`) bundled with every OPA instance. It provides constructor functions for every registered constraint type that enforce the schema at compile time:
+
+```rego
+package dcm.constraint_types
+
+zone_restriction(params) = constraint {
+  is_array(params.allowed)
+  constraint := {
+    "constraint_type": "zone_restriction",
+    "value": params,
+  }
+}
+
+distribution_requirement(params) = constraint {
+  is_number(params.min_replicas)
+  is_number(params.min_zones)
+  constraint := {
+    "constraint_type": "distribution_requirement",
+    "value": params,
+  }
+}
+```
+
+This library is auto-generated from the Constraint Type Registry. Policy authors call `constraint_types.zone_restriction(...)` instead of crafting raw constraint objects. Wrong field names or types are caught at bundle compilation — not at runtime.
+
+### 9.4 Template Registration Validation
+
+When a template is registered:
+
+1. Rego compiles without errors
+2. Parameter schema is valid OpenAPI v3
+3. All emitted constraint types are registered in the Constraint Type Registry
+4. All consumed constraint types are registered
+5. Rego uses the `data.dcm.constraint_types` library for emissions (not raw objects)
+
+When a policy artifact is created from a template:
+
+1. Parameters validate against the template's parameter schema
+2. Match conditions reference valid field paths
+3. Emitted/consumed constraint types match the template's declarations
+
+---
+
+## 10. Output Schema — GateKeeper
+
+**Fires on:** Request payload at assembly time.
+**Produces:** An allow or deny decision for the request.
+
+```yaml
+gatekeeper_output:
+  decision: allow | deny
+  reason: ""
+  field_locks:                           # optional: lock specific fields as immutable
+    - field: 
+      lock_type: immutable | constrained
+      constraint_schema:    # if constrained
+  warnings: [""]
+```
+
+**Policy Engine behavior:**
+- `allow` → request proceeds; field_locks applied to payload
+- `deny` → request blocked; `reason` included in consumer error response
+- Any active GateKeeper producing `deny` → request blocked (all must allow)
+
+---
+
+## 11. Output Schema — Validation
+
+**Fires on:** Request payload; validates correctness of field values.
+**Produces:** Pass or fail with field-level detail.
+
+```yaml
+validation_output:
+  result: pass | fail
+  field_results:
+    - field: 
+      result: valid | invalid
+      message: ""
+      suggested_value:            # optional
+  advisory: [""]
+```
+
+**Policy Engine behavior:**
+- `pass` → request proceeds
+- `fail` → request blocked; `field_results` included in consumer error response
+
+---
+
+## 12. Output Schema — Transformation
+
+**Fires on:** Request payload; enriches, modifies, or injects field values.
+**Produces:** A set of field mutations to apply to the payload.
+
+```yaml
+transformation_output:
+  mutations:
+    - field: 
+      operation: set | append | delete | lock
+      value:                  # for set/append
+      reason: ""
+      source_type: enrichment | injection | normalization | correction
+```
+
+**Policy Engine behavior:** All mutations from all active Transformation policies are collected and applied to the payload. Each mutation is recorded in field-level provenance with the policy_uuid as source.
+
+---
+
+## 13. Output Schema — Recovery
+
+**Fires on:** A failure or ambiguity trigger condition (DISPATCH_TIMEOUT, PARTIAL_REALIZATION, CANCELLATION_FAILED, etc.).
+**Produces:** A recovery action and parameters.
+
+```yaml
+recovery_output:
+  action: DRIFT_RECONCILE | DISCARD_AND_REQUEUE | DISCARD_NO_REQUEUE |
+          ACCEPT_LATE_REALIZATION | COMPENSATE_AND_FAIL |
+          NOTIFY_AND_WAIT | ESCALATE | RETRY
+  action_parameters:
+    requeue_delay: PT0S                  # for DISCARD_AND_REQUEUE
+    max_attempts: 3                      # for RETRY
+    backoff: exponential                 # for RETRY
+    deadline: PT4H                       # for NOTIFY_AND_WAIT
+    on_deadline_exceeded: ESCALATE       # for NOTIFY_AND_WAIT
+  notify_before_action: true
+  notification_urgency: high
+```
+
+**Policy Engine behavior:** The first matching Recovery policy's action is executed. Recovery policies follow the same domain precedence — resource_type override wins over tenant override wins over profile default.
+
+---
+
+## 14. Output Schema — Orchestration Flow
+
+**The two-level orchestration model:**
+
+Orchestration in DCM operates at two levels that compose through the same Policy Engine:
+
+- **Level 1 — Named Workflow Artifacts:** Orchestration Flow Policies with `ordered: true` declare an explicit, visible, auditable sequence of steps. Each step references a payload type from the closed vocabulary. This is what operators see and reason about. Adding a step = adding to a workflow Policy.
+- **Level 2 — Dynamic Policies:** GateKeeper, Transformation, Recovery, and Governance Matrix Policies fire when their conditions match, within or alongside workflow steps, without being declared in the workflow. Adding conditional behavior = writing a dynamic policy.
+
+The Request Orchestrator (event bus) routes all payload type events through the Policy Engine. Both named workflow steps and dynamic policies evaluate against the same events. The workflow provides the skeleton; dynamic policies fill in conditional behavior.
+
+**Fires on:** Pipeline payload type events.
+**Produces:** A flow directive governing step ordering.
+
+```yaml
+orchestration_flow_output:
+  ordered: true | false
+  steps:
+    - step: 1
+      policy_handle: ""
+      condition: ""
+      on_fail: halt | skip | escalate
+  parallel_groups:                       # steps that may execute in parallel
+    - [step_1_id, step_2_id]
+```
+
+**Step vocabulary** — steps reference payload types from the closed vocabulary, mapping to control plane operations:
+
+| Payload type | Maps to |
+|-------------|---------|
+| `request.initiated` | Start of request pipeline |
+| `request.layers_assembled` | Layer assembly complete |
+| `request.policies_evaluated` | All policies evaluated |
+| `request.placement_complete` | Provider selected |
+| `request.dispatched` | Sent to provider |
+| `discovery.cycle_complete` | Discovery cycle done |
+| `drift.detected` | Drift found |
+| `recovery.timeout_fired` | Dispatch timeout |
+| `provider_update.received` | Provider update notification |
+
+Custom steps extend this vocabulary by publishing new payload types.
+
+**Policy Engine behavior:** When `ordered: true`, steps execute in declared sequence. When `ordered: false`, the Policy Engine executes steps in parallel where no data dependencies exist. Orchestration Flow policies compose with standard GateKeeper and Transformation policies — both types evaluate in the same pipeline.
+
+---
+
+## 15. Output Schema — Governance Matrix Rule
+
+**Fires on:** Any cross-boundary interaction (DCM → Provider, DCM → Peer DCM, Provider → DCM).
+**Produces:** A boundary control decision with optional field permissions.
+
+```yaml
+governance_matrix_output:
+  decision: ALLOW | DENY | ALLOW_WITH_CONDITIONS | STRIP_FIELD | REDACT | AUDIT_ONLY
+  conditions:                            # for ALLOW_WITH_CONDITIONS
+    - field: 
+      operator: 
+      value: 
+  field_permissions:
+    mode: allowlist | blocklist | passthrough
+    paths: ["", ...]
+    on_blocked_field: STRIP_FIELD | DENY_REQUEST | REDACT
+  audit_on: [ALLOW, DENY, STRIP_FIELD]
+  notification_on: [DENY]
+  notification_urgency: critical
+```
+
+**Policy Engine behavior:** Hard DENY evaluated first — any hard DENY is terminal. Soft decisions evaluated by domain precedence; DENY wins over ALLOW at the same level. Field permissions applied after decision determined. Audit record always written.
+
+---
+
+## 16. Output Schema — Lifecycle Policy
+
+**Fires on:** Relationship events (related entity state changes, relationship creation/release).
+**Produces:** A lifecycle action to apply to related entities.
+
+```yaml
+lifecycle_policy_output:
+  on_related_destroy: cascade | protect | detach | notify
+  on_related_suspend: cascade | ignore | notify
+  on_last_relationship_released: destroy | retain | notify
+  propagation_depth: 1 | 2 | N          # how many relationship hops to propagate
+  action_delay: PT0S                     # grace period before executing action
+```
+
+**Policy Engine behavior:** When a relationship event occurs, all matching Lifecycle policies on both related entities are evaluated. The most restrictive action wins (save beats destroy). Conflicts between policies at the same domain level produce a CONFLICT_ERROR at policy ingestion time.
+
+---
+
+## 17. Output Schema — ITSM Action
+
+The ITSM Action policy type triggers actions in connected ITSM systems as a side-effect of DCM pipeline events.
+
+```yaml
+itsm_action_output:
+  type: itsm_action
+  itsm_provider_uuid:        # registered ITSM integration UUID
+  action: create_change_request | update_change_request | close_change_request |
+          create_incident | update_incident | close_incident |
+          update_cmdb_ci | create_cmdb_ci | retire_cmdb_ci |
+          create_service_request | link_parent_record
+  action_payload:
+    : 
+  store_reference_on_entity:    # default: false
+  reference_label: 
+  block_until_created:          # default: false — see ITSM-005
+  block_timeout:   # required if block_until_created: true
+  on_failure: log_and_continue | alert_and_continue | alert_only
+```
+
+> **See [ITSM Integration](42-itsm-integration.md)** for full ITSM integration registration, capability declarations, supported ITSM systems (ServiceNow, Jira, Remedy, Freshservice, PagerDuty, generic REST), policy examples, and system policies (ITSM-001–007, ITSM-POL-001–004).
+
+**Key constraints:**
+- ITSM Action policies are side-effect only — they do not produce allow/deny decisions
+- `block_until_created: true` creates a pipeline gate with mandatory timeout (ITSM-005)
+- Multiple ITSM Action policies on the same event fire independently (ITSM-POL-004)
+- Full audit record produced on every evaluation (ITSM-POL-003)
+
+## 18. Policy Override Model
+
+When a policy blocks a request and the block needs to be overridden, DCM provides five override mechanisms organized by severity. Every override — regardless of mechanism — produces a Merkle tree audit leaf capturing the override justification, authorizer identity, and the policy that was overridden.
+
+### 18.1 Override Policy (Planned Exceptions)
+
+A policy artifact that explicitly overrides another policy for a defined scope. Goes through the standard policy lifecycle (developing → proposed → active) with review and shadow testing.
+
+```yaml
+policy_artifact:
+  handle: "override/payments-team-zone-c-exception"
+  policy_type: override
+  overrides: "sovereignty/eu-data-residency"
+  scope:
+    tenant_tags: ["payments"]
+    resource_types: ["Compute.VirtualMachine"]
+    operations: [rehydration, provider_migration]
+  effect: relax                              # relax a deny → allow
+  justification: "Payments team DR requires zone-c replica per BCP-2026-04"
+  expires_at: "2027-01-01"
+  review_required_before: "2026-10-01"
+  compensating_controls: []                  # optional — see Model 5
+```
+
+**Constraint:** Override policies cannot target `enforcement: hard` policies. Hard policies can only be overridden via Exception Grant or Manual Override with dual-approval. This is validated at policy activation — attempting to create an override policy targeting a hard policy is rejected.
+
+### 18.2 Exception Grant (Pre-Authorized Waivers)
+
+A pre-authorized, time-bounded, scope-limited waiver registered before any specific request needs it. Used for known upcoming needs (DR exercises, compliance transition periods, migration windows).
+
+```yaml
+exception_grant:
+  grant_uuid: 
+  policy_handle: "sovereignty/eu-data-residency"
+  scope:
+    tenants: ["payments-team"]
+    resource_types: ["Compute.VirtualMachine"]
+    operations: [rehydration, provider_migration]
+  effect: relax
+  compensating_controls:
+    - "audit/enhanced-logging"               # require field-granularity audit during grant
+    - "notification/security-team-alert"     # notify security team on every use
+  authorized_by: 
+  approved_by:                   # dual-approval required for hard policies
+  justification: "BCP-2026-04 DR requires zone-c capability during Q2"
+  effective_from: "2026-04-01"
+  expires_at: "2026-07-01"
+  review_required_before: "2026-06-15"
+  renewable: true
+  max_renewals: 2
+  usage_count: 0                             # tracks how many times this grant was used
+  max_usage: null                            # null = unlimited within time window
+```
+
+Exception Grants can target hard enforcement policies — but require dual-approval. Compensating controls are mandatory when overriding hard policies.
+
+### 18.3 Manual Override (Immediate Authorization)
+
+A human with override authority grants a one-time exception for a specific blocked request. Used when a request is blocked and needs to proceed immediately (DR scenario, production incident, time-sensitive deployment).
+
+```yaml
+manual_override:
+  override_uuid: 
+  request_uuid:                        # tied to a specific request
+  policy_overridden: "sovereignty/eu-data-residency"
+  authorized_by:                 # must have override authority role
+  approved_by:            # second approver (required for hard policies)
+  reason: "DR scenario — zone-a unavailable, need zone-c placement"
+  scope: single_request                      # this request only — cannot be reused
+  expires_at: "2026-04-05T00:00:00Z"         # expires if not used within window
+  compensating_controls:
+    - "audit/enhanced-logging"
+```
+
+**Constraint:** Manual overrides of `enforcement: hard` policies require dual-approval (see §18.4). A single person can never override a hard policy.
+
+### 18.4 Dual-Approval Escalation
+
+Not a separate override mechanism — a modifier on Exception Grants and Manual Overrides that applies whenever a `hard` enforcement policy is being overridden. Two authorized individuals from different roles must independently approve.
+
+```yaml
+dual_approval:
+  first_approver:
+    actor_uuid: 
+    role: security_officer                   # must be from security role
+    approved_at: 
+  second_approver:
+    actor_uuid: 
+    role: operations_lead                    # must be from operations role
+    approved_at: 
+  role_separation_enforced: true             # same role cannot fill both slots
+```
+
+Dual-approval is automatically required whenever:
+- A Manual Override targets a `hard` enforcement policy
+- An Exception Grant targets a `hard` enforcement policy
+- Profile is `fsi` or `sovereign` and any override mechanism is used
+
+### 18.5 Compensating Control Substitution
+
+Instead of overriding a policy, substitute a different set of controls that satisfies the same security/compliance intent through a different mechanism. The original policy is not relaxed — it is satisfied differently.
+
+```yaml
+compensating_control:
+  control_uuid: 
+  replaces_policy: "sovereignty/eu-data-residency"
+  when:
+    conditions:
+      - field: operation.type
+        operator: equals
+        value: rehydration
+  substitute_controls:
+    - policy_handle: "sovereignty/sovereign-grade-encryption"
+    - policy_handle: "audit/field-granularity-override"
+    - policy_handle: "notification/security-team-immediate"
+  justification: "Zone-c permitted for DR if sovereign-grade encryption and enhanced audit active"
+  validated_by:                  # compliance officer who validated equivalence
+```
+
+Compensating controls are the only mechanism that does not actually override the policy — it replaces the enforcement with equivalent protection. Not all policies have meaningful compensating controls (data residency laws cannot be compensated by encryption).
+
+### 18.6 Severity Spectrum Summary
+
+| Severity | Mechanism | When | Who | Can target hard? | Audit |
+|----------|-----------|------|-----|-----------------|-------|
+| **Planned** | Override Policy | Known recurring exception | Policy author + reviewer | No — caught at activation | Full policy lifecycle |
+| **Pre-authorized** | Exception Grant | Known upcoming need | Dual-approval authority | Yes — with dual-approval + compensating controls | Grant record + usage tracking |
+| **Immediate** | Manual Override | Blocked request, needs to proceed now | Override authority | Yes — with dual-approval | Override record + justification |
+| **Structural** | Compensating Control | Policy can be met differently | Policy author + compliance | N/A — policy is satisfied, not overridden | Substitute control record |
+| **Modifier** | Dual-Approval | Any override of hard policy | Two individuals, different roles | Required | Both approver records |
+
+### 18.7 Override Audit
+
+Every override produces its own Merkle tree leaf:
+
+```yaml
+audit_leaf:
+  stage: policy_override
+  source: "override/payments-team-zone-c-exception"  # or exception_grant/manual_override UUID
+  source_type: override_policy | exception_grant | manual_override | compensating_control
+  override_target: "sovereignty/eu-data-residency"
+  override_effect: relax
+  authorized_by: 
+  approved_by: 
+  dual_approval: true | false
+  compensating_controls: [...]
+  justification: "..."
+  input_payload_hash: 
+  output_payload_hash: 
+  before_context_hash: 
+  after_context_hash: 
+  signature: 
+```
+
+An auditor asking "why did this request bypass sovereignty?" gets the complete answer: which override mechanism was used, who authorized it, what compensating controls were active, and the cryptographic proof that the override was properly authorized.
+
+### 18.8 Policy Block Resolution
+
+When a policy blocks a request and no automatic resolution exists, DCM does not silently enter an override queue. The consumer is notified with actionable guidance: what blocked the request, why, and what their options are to resolve it.
+
+**Pipeline behavior when a policy blocks a request:**
+
+```
+Policy Engine evaluates request
+  → Policy blocks request
+  → Policy Engine checks automatic resolution:
+      1. Active Override Policy covering this scope? → Apply, continue pipeline
+      2. Active Exception Grant covering this scope? → Apply, continue pipeline
+      3. Active Compensating Control covering this scope? → Substitute, continue pipeline
+  → No automatic resolution available:
+      4. Request enters POLICY_BLOCKED status
+      5. Policy Engine builds resolution guidance (what blocked, why, options)
+      6. request.policy_blocked event published with guidance
+      7. Consumer notified with blocking details and resolution options
+      8. Consumer chooses a resolution action
+      9. Pipeline proceeds based on chosen action
+```
+
+**Resolution options presented to the consumer:**
+
+| Option | Action | What happens |
+|--------|--------|-------------|
+| **Modify request** | Consumer changes the blocked fields to be compliant | Request re-enters pipeline from assembly with updated fields. All policies re-evaluate. |
+| **Request override** | Consumer requests an override with justification | Request enters PENDING_OVERRIDE. Override approval flow begins (§18.9). |
+| **Cancel request** | Consumer abandons the request | Request moves to CANCELLED. Audit trail records cancellation with blocking context. |
+| **Escalate** | Consumer requests review by the responsible policy domain owner | Notification routed to the role responsible for the blocking policy domain (e.g., sovereignty admin, security admin, cost admin) with full context. The domain owner can modify policies, register exception grant, or advise consumer. Routing is configurable per policy domain and profile. |
+
+**Resolution guidance (built by Policy Engine):**
+
+```yaml
+policy_block_resolution:
+  request_uuid: 
+  status: POLICY_BLOCKED
+  blocking_details:
+    - policy_handle: "sovereignty/eu-data-residency"
+      enforcement: hard
+      reason: "Zone eu-west-3 not in allowed zones [eu-west-1, eu-central-1]"
+      blocking_fields:
+        - field: placement.zone
+          current_value: "eu-west-3"
+          allowed_values: ["eu-west-1", "eu-central-1"]
+
+  resolution_options:
+    modify:
+      available: true
+      guidance:
+        - field: placement.zone
+          suggestion: "Change to eu-west-1 or eu-central-1 to comply with data residency policy"
+          compliant_values: ["eu-west-1", "eu-central-1"]
+    request_override:
+      available: true                        # false if profile prohibits consumer-initiated overrides
+      requires: dual_approval                # derived from enforcement level and profile
+      eligible_approver_roles: [security_officer, operations_lead]
+    cancel:
+      available: true                        # always available
+    escalate:
+      available: true
+      routes_to: [platform_admin]
+
+  timeout_at:                      # how long the request stays in POLICY_BLOCKED before auto-cancel
+```
+
+DCM builds the `compliant_values` guidance from the blocking policy's constraint output. If the sovereignty policy says `allowed_zones: [eu-west-1, eu-central-1]`, DCM includes those as the suggestion. If a GateKeeper says `max_cpu: 32` and the request asked for 64, DCM suggests values ≤ 32. For complex constraints (multi-policy interactions), DCM provides what it can determine and indicates when manual review is needed.
+
+**Consumer actions via API:**
+
+```
+# View blocking details and resolution options
+GET /api/v1/requests/{request_uuid}/resolution
+
+# Modify and resubmit blocked request
+POST /api/v1/requests/{request_uuid}:resolve
+Body: {
+  action: "modify",
+  modifications: { "placement.zone": "eu-west-1" }
+}
+
+# Request override (enters override approval flow)
+POST /api/v1/requests/{request_uuid}:resolve
+Body: {
+  action: "request_override",
+  justification: "DR scenario — zone-a unavailable",
+  compensating_controls: ["audit/enhanced-logging"]
+}
+
+# Cancel blocked request
+POST /api/v1/requests/{request_uuid}:resolve
+Body: { action: "cancel" }
+
+# Escalate to responsible policy domain owner
+POST /api/v1/requests/{request_uuid}:resolve
+Body: {
+  action: "escalate",
+  context: "Need guidance on zone placement for DR scenario"
+}
+```
+
+**Events:**
+
+| Event | Published when | Payload |
+|-------|---------------|---------|
+| `request.policy_blocked` | Pipeline blocked, no automatic resolution | request_uuid, blocking_details, resolution_options, timeout_at |
+| `request.resolution_chosen` | Consumer selects a resolution action | request_uuid, action (modify/override/cancel/escalate) |
+| `request.modified_resubmit` | Consumer modifies and resubmits | request_uuid, modified_fields, re-enters pipeline |
+| `override.requested` | Consumer chooses request_override | request_uuid, justification, enters override approval flow |
+| `override.first_approval` | First approver acts (dual-approval flow) | override_request_uuid, approver, awaiting second approval |
+| `override.approved` | All required approvals received | override_request_uuid, approvers, compensating_controls |
+| `override.rejected` | Approver explicitly rejects | override_request_uuid, rejector, reason |
+| `override.expired` | Timeout reached with no decision | override_request_uuid, timeout_at |
+
+### 18.9 Override Approval Flow
+
+When the consumer chooses "request override," the request moves from `POLICY_BLOCKED` to `PENDING_OVERRIDE` and the approval flow begins. DCM provides the approval gate, the audit trail, and the API. The organization provides the deliberation process.
+
+**Override request record:**
+
+```yaml
+override_request:
+  override_request_uuid: 
+  request_uuid: 
+  blocking_policy_handle: "sovereignty/eu-data-residency"
+  blocking_policy_enforcement: hard | soft
+  blocking_reason: "Zone eu-west-3 not in allowed zones [eu-west-1, eu-central-1]"
+  required_approval_type: single | dual
+  eligible_approver_roles: [security_officer, operations_lead]
+  consumer_justification: "DR scenario — zone-a unavailable"
+  consumer_compensating_controls: ["audit/enhanced-logging"]
+  status: pending | approved | rejected | expired
+  timeout_at: 
+  created_at: 
+  first_approval:
+    actor_uuid: 
+    role: 
+    justification: "Approved — DR scenario confirmed, enhanced logging active"
+    approved_at: 
+  second_approval:
+    actor_uuid: 
+    role: 
+    approved_at: 
+```
+
+**Approver actions via Admin API:**
+
+```
+# Approve override
+POST /api/v1/admin/overrides/{request_uuid}/approve
+Body: { justification, compensating_controls[], role }
+
+# Reject override
+POST /api/v1/admin/overrides/{request_uuid}/reject
+Body: { reason }
+
+# Query pending overrides (approver dashboard)
+GET /api/v1/admin/overrides?status=pending&role={role}
+
+# Query override history
+GET /api/v1/admin/overrides/{request_uuid}
+```
+
+**Notification routing (configurable per profile):**
+
+```yaml
+block_notification:
+  # Consumer notification (always enabled)
+  consumer:
+    channels: [internal]                     # Consumer Portal shows blocked requests
+    include_guidance: true                   # Include resolution options and compliant values
+
+  # Override approver notification (when consumer requests override)
+  override_approvers:
+    channels:
+      - type: internal                       # Consumer Portal approver dashboard
+        enabled: true
+      - type: webhook                        # External system integration
+        url: "https://servicenow.example.com/api/dcm/override"
+        enabled: true
+      - type: webhook
+        url: "https://slack.example.com/api/dcm/override"
+        enabled: true
+    routing:
+      by_policy_domain:
+        sovereignty: [security_officer, compliance_manager]
+        platform: [operations_lead, platform_engineer]
+        tenant: [tenant_admin]
+      by_enforcement:
+        hard: [security_officer, operations_lead]
+        soft: [operations_lead]
+
+  # Escalation notification
+  escalation:
+    routes_to: [platform_admin]
+    channels: [internal, webhook]
+
+  # Override approval escalation (no response within window)
+  override_escalation:
+    if_no_response_after: PT1H
+    escalate_to: [platform_admin]
+```
+
+**Timeout behavior (profile-governed):**
+
+| Profile | Block timeout (auto-cancel) | Override timeout | Override escalation |
+|---------|---------------------------|-----------------|-------------------|
+| `minimal` | PT48H | PT24H | PT12H |
+| `dev` | PT48H | PT24H | PT12H |
+| `standard` | PT8H | PT4H | PT2H |
+| `prod` | PT8H | PT4H | PT1H |
+| `fsi` | PT4H | PT1H | PT30M |
+| `sovereign` | PT4H | PT1H | PT30M |
+
+Block timeout: how long a `POLICY_BLOCKED` request waits for any consumer action before auto-cancelling. Override timeout: how long a `PENDING_OVERRIDE` request waits for approver action. Both are configurable per profile.
+
+**Pipeline resume behavior:**
+
+When the consumer modifies and resubmits, the request re-enters the pipeline from assembly. Policies re-evaluate against the modified fields. The modification is recorded as a Merkle tree audit leaf.
+
+When an override is approved, the pipeline resumes from the exact stage where it was blocked. The assembled payload, earlier policy evaluations, and Evaluation Context are preserved. The approved override is injected into the Evaluation Context as a constraint modification before the blocked policy re-evaluates.
+
+Both paths produce audit leaves:
+- Modify path: modification leaf (what changed) + full re-evaluation leaves
+- Override path: override approval leaf (§18.7) + re-evaluation leaf
+
+---
+
+## 19. Policy Composition
+
+Policies compose through four mechanisms:
+
+**Domain precedence** (Section 4) — more-specific domains override less-specific:
+```
+System policy (GateKeeper: cpu_count max 64)
+  └── Platform policy (GateKeeper: prod VMs require manager approval)
+        └── Tenant policy (GateKeeper: payments team max cpu_count 32)
+              └── Resource-type policy (Transformation: inject monitoring)
+```
+
+**Evaluation Context** (Section 7) — policies inform each other through constraints and hints. Sovereignty emits zone restrictions; tier distribution reads them and adjusts. Conflicts are detected and resolved automatically or escalated.
+
+**Policy Groups** — Data artifacts that group related policies by concern_type. Profiles activate Policy Groups. "Apply the HIPAA profile" activates the HIPAA compliance domain's Policy Group.
+
+**Override Model** (Section 18) — Override policies, exception grants, manual overrides, compensating controls, and dual-approval provide governed mechanisms for exceptions. Every override is auditable and time-bounded.
+
+For a single request, all active matching policies at all domain levels evaluate across multiple passes if needed. Active overrides modify the evaluation: an override policy relaxes or tightens a specific policy's effect; an exception grant temporarily suspends a policy for a scoped set of requests; a manual override suspends a policy for one request. Overrides participate in the Evaluation Context — downstream policies see the override's effect.
+
+---
+
+## 20. Related Policies
+
+| Policy | Rule |
+|--------|------|
+| `POL-001` | All DCM policy types implement the unified base contract. The output schema is the only thing that varies. |
+| `POL-002` | Every policy evaluation produces an audit record. No evaluation is silent. |
+| `POL-003` | Hard enforcement policies require dual-approval to override. Override policies (Model 1) cannot target hard policies — caught at activation. |
+| `POL-004` | Policies in `proposed` status execute in shadow mode — output is captured and never applied. Shadow mode is the primary mechanism for safe policy change management. |
+| `POL-005` | The Policy Engine is the sole evaluator of all policies. No component bypasses the Policy Engine to enforce rules directly. |
+| `POL-006` | Adding a new policy type requires defining a new output schema. The base contract, evaluation algorithm, lifecycle, and audit obligations are inherited. |
+| `POL-007` | Policies ARE the orchestration. Pipeline steps are Policies firing on payload type events. Static flows are Orchestration Flow Policies with `ordered: true`. |
+| `POL-008` | Every override produces a Merkle tree audit leaf. Override justification, authorizer identity, compensating controls, and the overridden policy are captured. |
+| `POL-009` | Exception Grants and Manual Overrides must have expiry dates. Permanent overrides are not permitted — use an Override Policy with a review cycle instead. |
+| `POL-010` | Compensating controls must be validated by a compliance officer role before activation. Self-validation is not permitted. |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/data-model/_index.md b/content/docs/architecture/data-model/_index.md
new file mode 100644
index 0000000..39efdfe
--- /dev/null
+++ b/content/docs/architecture/data-model/_index.md
@@ -0,0 +1,39 @@
+---
+title: Data Model
+type: docs
+weight: 2
+sidebar:
+  open: true
+---
+
+# DCM Data Model
+
+The DCM Data Model is the foundational layer that governs how all data in DCM is
+represented, versioned, assembled, and governed. It is the single source of truth
+for all resources across the full lifecycle — from consumer intent through
+realization, operation, and decommission.
+
+{{< cards >}}
+  {{< card link="context-and-purpose" title="Context and Purpose" icon="information-circle" subtitle="Why the data model exists, core principles, field-level provenance, and the artifact metadata standard." >}}
+  {{< card link="four-states" title="Four States" icon="arrows-expand" subtitle="Intent, Requested, Realized, and Discovered states. Rehydration, drift detection, and CI/CD integration." >}}
+  {{< card link="layering-and-versioning" title="Data Layers and Assembly" icon="collection" subtitle="Layer types, assembly process, override control, Layer Domain/Handle/Priority, and conflict detection." >}}
+  {{< card link="resource-type-hierarchy" title="Resource Type Hierarchy" icon="cube" subtitle="The Resource Type Registry, catalog model, portability classification, and provider hierarchy." >}}
+  {{< card link="resource-service-entities" title="Resource and Service Entities" icon="server" subtitle="Entity definitions, ownership models, lifecycle states, and process resources." >}}
+  {{< card link="service-dependencies" title="Service Dependencies" icon="share" subtitle="Dependency rehydration ordering and failure handling on the entity relationship graph." >}}
+  {{< card link="resource-grouping" title="Resource Grouping" icon="user-group" subtitle="Tenant model, Resource Groups, grouping model, and multi-tenancy." >}}
+  {{< card link="entity-relationships" title="Entity Relationships" icon="link" subtitle="Universal bidirectional relationship model for all entity connections — internal and external." >}}
+  {{< card link="information-providers" title="Information Providers" icon="globe" subtitle="External data providers, information types, lookup model, and verification." >}}
+  {{< card link="storage-providers" title="Storage Providers" icon="archive" subtitle="Storage provider contracts for GitOps stores, event streams, audit, and observability." >}}
+  {{< card link="audit-provenance-observability" title="Audit, Provenance, and Observability" icon="eye" subtitle="The three distinct concerns of audit, data lineage, and operational observability." >}}
+  {{< card link="ingestion-model" title="Ingestion Model" icon="inbox-in" subtitle="Unified V1 migration and brownfield ingestion: ingest, enrich, promote. Transitional Tenant and ING system policies." >}}
+  {{< card link="policy-profiles" title="Policy Organization" icon="collection" subtitle="Policy Groups, Profiles, and Providers. Built-in profiles from minimal to sovereign. Lifecycle constraints. Cross-tenancy authorization." >}}
+  {{< card link="universal-groups" title="Universal Group Model" icon="user-group" subtitle="One DCMGroup with group_class. Composite groups, nested Tenants, federated Tenants. All grouping constructs unified." >}}
+  {{< card link="universal-audit" title="Universal Audit Model" icon="shield-check" subtitle="Two-stage audit: synchronous Commit Log + async Audit Store. Reference-based retention. Tamper-evident hash chains. AUD-001 through AUD-013." >}}
+  {{< card link="deployment-redundancy" title="Deployment and Redundancy" icon="server" subtitle="Redundant by default. Everything containerized. Profile-governed replicas. Self-hosting. Quorum writes. RED-001 through RED-010." >}}
+  {{< card link="webhooks-messaging" title="Webhooks and Messaging" icon="arrows-expand" subtitle="Inbound and outbound webhooks. Message Bus Provider. Universal ingress/egress actor model. Credential Provider. WHK and ING policies." >}}
+  {{< card link="auth-providers" title="Auth Providers" icon="lock-closed" subtitle="Eight provider types. Auth ladder from API key to air-gapped OIDC. LDAP, FreeIPA, AD, OIDC, mTLS. No anonymous access. AUTH policies." >}}
+  {{< card link="registry-governance" title="Registry Governance" icon="collection" subtitle="Three-tier registry. PR-based proposals. Default deprecation policies. Cost-aware placement. Federated with air-gap signed bundles." >}}
+  {{< card link="information-providers-advanced" title="Information Provider Confidence" icon="chart-bar" subtitle="0-100 confidence scoring. Authority as layer data. Ingestion-time conflict detection. Write-back. Schema versioning. INF-001 through INF-008." >}}
+  {{< card link="dcm-federation" title="DCM Federation" icon="globe" subtitle="Peer, parent-child, hub relationships. Provider federation eligibility. DCM Provider (ninth type). Always mTLS. Cross-DCM confidence scoring. DCM-001 through DCM-008." >}}
+  {{< card link="universal-groups" title="Universal Group Model" icon="view-grid" subtitle="Unified grouping: Tenants, Resource Groups, Policy Groups as one model. Composite groups, nested and federated Tenants, permanent membership history." >}}
+{{< /cards >}}
diff --git a/content/docs/architecture/overview.md b/content/docs/architecture/overview.md
new file mode 100644
index 0000000..bb7bdb0
--- /dev/null
+++ b/content/docs/architecture/overview.md
@@ -0,0 +1,333 @@
+---
+title: Architecture Overview
+type: docs
+weight: 1
+---
+
+# DCM High Level Design
+
+
+---
+
+## What is DCM?
+
+DCM (Data Center Management) is an open-source **governing framework** for enterprise on-premises and sovereign cloud infrastructure. It provides a hyperscaler-like cloud experience — the operational model and self-service capabilities of a public cloud provider — on infrastructure that organizations own and control.
+
+DCM is **not a provisioning tool**. It is the management plane that sits above provisioning tools, governing what gets requested, approved, built, owned, and decommissioned. Provisioning tools (Ansible, Terraform, Kubernetes operators) become Service Providers that DCM orchestrates.
+
+**Mission:** Seamlessly manage the complete lifecycle of all data center infrastructure by providing a policy-governed, data-driven, and unified platform to enable and ensure sovereignty.
+
+---
+
+
+## Design Priority Order
+
+Every design decision in DCM is evaluated against this hierarchy. When priorities conflict, higher priorities win.
+
+| Priority | Principle | What it means |
+|----------|-----------|--------------|
+| **1. Security** | Industry best practices are the baseline | Security properties present in ALL profiles; profiles control enforcement strictness, not whether security applies |
+| **2. Ease of use** | The secure path must be the easy path | Auto-approval for ordinary requests; profile defaults eliminate configuration burden; secure path is easy path |
+| **3. Extensibility** | Adaptable through configuration, not code | New requirements as policy additions; new contexts as profile configuration; new providers as contract implementations; custom authority tiers inserted into ordered list without breaking existing references |
+| **4. Fit for purpose** | Always required | Everything serves the lifecycle management mission: request → provision → operate → decommission |
+
+**The `minimal` profile is "security with minimal operational overhead" — not "minimal security."** All security properties are architecturally present in every profile. What varies is automation level, enforcement thresholds, and acceptable manual intervention.
+
+---
+## Session Token Revocation and Internal Component Authentication
+
+**Session token revocation** defines the complete lifecycle for actor sessions — how tokens are created, refreshed, and revoked. Revocation is profile-governed: `minimal` allows PT5M propagation SLA; `sovereign` requires PT5S. The Session Revocation Registry is checked on every authenticated request — no component may skip this check (AUTH-018). Emergency revocation (security event) fires a `critical` urgency event that is non-suppressable. See [Session Token Revocation](data-model/session-revocation/).
+
+**Internal component authentication** closes the zero trust model at the internal boundary. Every call between DCM control plane components — API Gateway to Request Orchestrator, Policy Engine to Storage Provider, etc. — requires both a mTLS certificate from the Internal CA and a scoped interaction credential (ZTS-002). Components may only call targets declared in their `allowed_targets` list (ICOM-004). Bootstrap tokens are one-time-use and expire within PT1H (ICOM-007). See [Internal Component Authentication](data-model/internal-component-auth/).
+
+---
+
+## Authority Tier Model
+
+DCM governs decisions through an extensible **authority tier model** — a named, ordered list where each tier expresses a required level of organizational decision gravity. The default tiers are `auto → reviewed → verified → authorized`, but organizations can insert custom tiers between existing ones. Tier weight is derived from list position at evaluation time; existing tier name references always resolve correctly.
+
+When the tier registry changes, DCM computes a **tier impact diff** identifying any items whose effective authority requirement changed. Security degradations (lower gravity than before) block activation until explicitly accepted by a verified-tier reviewer. See [Authority Tier Model](data-model/authority-tier-model/).
+
+---
+
+## The Problem DCM Solves
+
+| Challenge | DCM Response |
+|-----------|-------------|
+| **Fragmented operations** — disparate tools, no unified control | Single control plane — one API, one data model, one policy engine |
+| **No source of truth** — multiple CMDBs diverge | Four-state model provides authoritative record of intent, request, realized, and discovered state |
+| **High time-to-market** — provisioning a VM may require dozens of teams | Self-service catalog with policy-governed automation — any authorized actor can request any service |
+| **Drift and state discrepancy** — no reconciliation between intended and actual | Continuous drift detection comparing realized vs discovered state with automated or human-directed remediation |
+| **Sovereignty requirements** — data residency, compliance, audit evidence | Unified Governance Matrix with sovereignty zone enforcement, complete provenance chain, and accreditation management |
+| **Siloed governance** — platform admins bottleneck policy changes | Federated contribution model — consumers, providers, and peer DCMs all contribute within their permitted scope |
+
+---
+
+## Core Principles
+
+| Principle | Meaning |
+|-----------|---------|
+| **Declarative** | Data describes what should exist, not how to achieve it |
+| **API-First** | Every capability is available via a standard API |
+| **Policy-Governed** | All business logic flows through the Policy Engine — never hard-coded |
+| **Idempotent** | Applying the same data multiple times always produces the same result |
+| **Immutable if Versioned** | Published versions never change — changes produce new versions |
+| **Provider-Agnostic** | DCM defines contracts, not implementations |
+| **GitOps-Native** | Intent and policy artifacts are Git-native — branched, reviewed, versioned |
+| **Federated by Default** | All authorized actor types contribute data within their permitted scope |
+| **AI-Ready** | Standalone architecture designed with AIOps layering in mind |
+
+---
+
+## The Three Foundational Abstractions
+
+Every concept in DCM maps to one of three foundational abstractions. There is no fourth.
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│                          DATA                                    │
+│  Everything that exists, is stored, has a lifecycle.            │
+│  Entities, layers, policies, accreditations, audit records,     │
+│  groups, relationships — all Data.                              │
+└──────────────────────────┬──────────────────────────────────────┘
+                           │ flows through
+              ┌────────────┴────────────┐
+              ▼                         ▼
+┌─────────────────────┐   ┌─────────────────────────────────────┐
+│      PROVIDER        │   │              POLICY                  │
+│  Every external      │   │  Every rule that fires on Data,      │
+│  component DCM       │   │  decides what happens, transforms    │
+│  calls or that       │   │  values, or enforces constraints.    │
+│  calls DCM.          │   │  Seven typed output schemas.         │
+│  Eleven typed        │   │  One evaluation algorithm.           │
+│  capability          │   │  Same lifecycle for all.             │
+│  extensions.         │   │                                      │
+│  One base contract.  │   │                                      │
+└─────────────────────┘   └─────────────────────────────────────┘
+```
+
+**The runtime loop:**
+```
+Event (Data state change)
+  → Policy Engine evaluates all matching Policies
+  → Policies produce decisions / mutations / actions
+  → Actions invoke Providers or produce new Data
+  → New Data triggers new Events → repeat
+```
+
+See [Foundational Abstractions](data-model/foundations/) for the complete model.
+
+---
+
+## The Four States
+
+Every resource entity in DCM has four independently maintained lifecycle stages stored in specialized stores:
+
+| State | What it records | Store |
+|-------|----------------|-------|
+| **Intent** | What did the consumer declare? | GitOps — immutable, PR-reviewed |
+| **Requested** | What was assembled, validated, and dispatched? | Write-once snapshot — full provenance |
+| **Realized** | What did the provider confirm it built? | Snapshot store — append-only |
+| **Discovered** | What actually exists right now, independently observed? | Ephemeral stream — ground truth for drift |
+
+The **entity UUID** links the entity across all four states throughout its entire lifecycle.
+
+---
+
+## Provider Model
+
+DCM defines contracts, not implementations. Eleven provider types all implement the **unified Provider base contract** (registration, health, sovereignty, accreditation, governance matrix enforcement, zero trust). What varies is the capability extension.
+
+| Provider Type | Capability |
+|--------------|-----------|
+| **Service Provider** | Realizes infrastructure resources (VMs, networks, storage, containers) |
+| **Information Provider** | Serves authoritative external data (CMDB, HR, Finance) |
+| **Storage Provider** | Persists DCM state (GitOps stores, event streams, audit) |
+| **Meta Provider** | Composes multiple providers into compound services |
+| **Policy Provider** | Evaluates policies externally (OPA sidecar, Mode 1–4) |
+| **Credential Provider** | Issues and rotates secrets and credentials |
+| **Auth Provider** | Authenticates actor identities |
+| **Notification Provider** | Delivers notifications via configured channels |
+| **Message Bus Provider** | Async event streaming |
+| **Registry Provider** | Serves the Resource Type Registry |
+| **Peer DCM** | Another DCM instance — federation is the Provider abstraction applied across instances |
+
+See [Unified Provider Contract](data-model/provider-contract/) for the base contract and all capability extensions.
+
+---
+
+## Policy Model
+
+Policies are the orchestration in DCM. Seven typed output schemas, one base contract, one evaluation algorithm.
+
+| Policy Type | Output | Fires on |
+|-------------|--------|---------|
+| **GateKeeper** | allow/deny (compliance) or risk score contribution (operational) | Request payload |
+| **Validation** | pass/fail (structural) or completeness score + warnings (advisory) | Request payload |
+| **Transformation** | field mutations | Request payload |
+| **Recovery** | action + parameters | Failure/timeout trigger |
+| **Orchestration Flow** | step sequence | Pipeline events (named workflows) |
+| **Governance Matrix Rule** | ALLOW / DENY / STRIP_FIELD / REDACT | Any cross-boundary interaction — always boolean |
+| **Lifecycle Policy** | action on related entity | Relationship events |
+
+### Hybrid Scoring Model
+
+DCM uses a **hybrid model**: questions of fact use boolean gates; questions of degree use scoring.
+
+**GateKeeper policies declare `enforcement_class`:**
+- `compliance` — boolean deny gate. Used for regulatory requirements (PHI→BAA, sovereign data boundaries). Cannot be scored around.
+- `operational` — contributes a weighted `risk_score_contribution` to the aggregate request risk score. Used for operational policies (cost ceilings, size limits, quota pressure).
+
+**Validation policies declare `output_class`:**
+- `structural` — boolean pass/fail. Missing required fields, type errors.
+- `advisory` — completeness score contribution + warning list. Recommended fields absent, unusual values.
+
+**Five scoring signals** aggregate into a request risk score (0–100): operational GateKeeper contributions (45%), actor risk history (20%), completeness warnings (15%), quota pressure (10%), provider accreditation richness (10%).
+
+**Profile-governed thresholds** map the score to approval routing: auto / reviewed / verified / authorized (+ custom tiers). Thresholds use a named-tier dynamic list; see [Authority Tier Model](data-model/authority-tier-model/). Thresholds are tunable per profile without touching individual policies. Profiles can also override enforcement class per policy — escalating operational policies to compliance-class, or demoting non-regulatory compliance policies to operational.
+
+The Governance Matrix is **always boolean** — scoring never applies to cross-boundary data decisions.
+
+See [Scoring Model](data-model/scoring-model/) for the complete specification.
+
+**Two-level orchestration:**
+- **Level 1 — Named Workflow Artifacts:** Orchestration Flow Policy with `ordered: true` — explicit, visible, auditable step sequence. This is the named pipeline skeleton.
+- **Level 2 — Dynamic Policies:** GateKeeper, Transformation, Recovery policies fire when conditions match, alongside workflow steps, without being declared in the workflow.
+
+Both levels are evaluated by the same Policy Engine through the same event bus. See [Unified Policy Contract](data-model/policy-contract/).
+
+---
+
+## Unified Governance Matrix
+
+The Governance Matrix is the single enforcement point for all cross-boundary data and capability decisions. It governs every interaction between DCM and any provider, peer DCM, or external endpoint.
+
+**Four axes per rule:** Subject (who) · Data (what — including field-level paths) · Target (where — sovereignty zone, jurisdiction, accreditation) · Context (profile, zero trust posture, TLS state)
+
+**Decision vocabulary:** ALLOW · DENY · ALLOW_WITH_CONDITIONS · STRIP_FIELD · REDACT · AUDIT_ONLY
+
+**Hard vs soft enforcement:** Hard rules cannot be relaxed by any downstream rule. `sovereign` and `classified` data never crossing any boundary is always hard.
+
+**Profile-bound defaults:** Each profile activates a set of default matrix rules. Organizations tighten (never relax) with Tenant and resource-type overrides.
+
+See [Unified Governance Matrix](data-model/governance-matrix/).
+
+---
+
+## Federated Contribution Model
+
+DCM defaults to a federated model for data creation. Every authorized actor type can contribute Data artifacts within their permitted scope — all via the same GitOps PR model with profile-governed review.
+
+| Contributor | Can contribute |
+|-------------|---------------|
+| **Platform Admin** | All artifact types at all domains |
+| **Consumer / Tenant** | Tenant-domain policies, resource groups, notification subscriptions, service definitions |
+| **Service Provider** | Resource Type Specs (their types), catalog items, service layers, provider policies |
+| **Peer DCM** | Registry entries, policy templates, service layers (scoped by federation trust posture) |
+
+Contributor scope is enforced by the Governance Matrix as a hard DENY — a consumer cannot contribute system-domain policies regardless of what they declare.
+
+See [Federated Contribution Model](data-model/federated-contribution-model/).
+
+---
+
+## Control Plane Components
+
+Nine internal components implement the three abstractions at runtime:
+
+| Component | Role |
+|-----------|------|
+| **Request Orchestrator** | Event bus — no pipeline logic; policies define all behavior |
+| **Policy Engine** | Evaluates all policy types using the same algorithm |
+| **Placement Engine** | Six-step provider selection (sovereignty → accreditation → capability → reserve query → tie-breaking → confirm) |
+| **Cost Analysis** | Pre-request estimation and ongoing attribution |
+| **Lifecycle Constraint Enforcer** | Monitors TTL/expiry; fires expiry actions through the standard pipeline |
+| **Discovery Scheduler** | Schedules and dispatches discovery requests to Service Providers |
+| **Notification Router** | Resolves notification audiences from the relationship graph |
+| **Drift Reconciliation** | Compares Discovered vs Realized state; produces drift records; never writes to Realized Store |
+| **Search Index** | Non-authoritative queryable projection of GitOps stores; always rebuildable |
+
+---
+
+## Zero Trust and Security
+
+DCM operates on a network-position-grants-zero-trust model. Every interaction boundary applies five checks regardless of the caller's network location:
+
+```
+Identity verification (mTLS)
+  → Authorization verification (scoped credential)
+    → Accreditation check (does the target hold required certs?)
+      → Governance Matrix check (are the fields permitted to cross?)
+        → Sovereignty check (does the endpoint satisfy constraints?)
+```
+
+All five checks produce audit records regardless of outcome. Profile-governed zero trust posture: none (minimal) → boundary (dev/standard) → full (prod/fsi) → hardware_attested (sovereign).
+
+---
+
+## Request Lifecycle
+
+A complete path from consumer intent to realized resource:
+
+```
+Consumer submits request (API, Web UI, or Git PR)
+  │
+  ▼ Intent State captured — versioned GitOps artifact
+  │ Policy pre-validation (shadow mode); cost estimate; sovereignty check
+  │
+  ▼ Request Payload Processor:
+  │   1–4: Layer assembly (Base → Core → Service → Request Layer)
+  │   5:   Pre-placement policies (Transformation, Validation, GateKeeper)
+  │   6:   Placement Engine — sovereignty filter → accreditation filter →
+  │         capability filter → parallel reserve queries → tie-breaking →
+  │         confirm selection
+  │   7:   Post-placement policies (provider-aware enrichment)
+  │   8:   Requested State written to write-once store (full provenance)
+  │   9:   Provider dispatch
+  │
+  ▼ Service Provider:
+  │   Naturalize (DCM format → provider native)
+  │   Execute (provision the resource)
+  │   Denaturalize (provider native → DCM unified format)
+  │   Return Realized State
+  │
+  ▼ Realized State written — confirmed by provider
+  │
+  ▼ Continuous discovery → Drift Reconciliation
+      Discovered State vs Realized State
+      Drift: field-level detail, severity classification, unsanctioned detection
+      Response: REVERT | ACCEPT_DRIFT | NOTIFY_AND_WAIT | ESCALATE
+```
+
+---
+
+## Capabilities Summary
+
+134 capabilities across 21 domains. Full detail in the [Capabilities Matrix](../capabilities-matrix/).
+
+**Minimum viable end-to-end set (21 capabilities):**
+IAM-001 → IAM-002 → IAM-003 → IAM-007 → CAT-001 → REQ-001 → REQ-002 → REQ-003 → REQ-004 → REQ-005 → REQ-006 → REQ-007 → PRV-001 → PRV-002 → PRV-003 → PRV-004 → PRV-005 → LCM-001 → DRF-001 → DRF-002 → AUD-001
+
+---
+
+## APIs and Interfaces
+
+| Interface | Purpose |
+|-----------|---------|
+| **Consumer API** | Service catalog, request submission, resource management, drift, groups, notifications, cost, quota, contribution endpoints |
+| **Admin API** | Tenant management, provider review, accreditation approval, discovery triggers, orphan resolution, quota, Search Index management |
+| **Operator Interface** | What Service Providers implement — dispatch, cancel, discover, health |
+| **Flow GUI** | Visual policy composer — execution graph, canvas, simulation, shadow mode, authoring |
+| **Flow GUI API** | Backend serving the Flow GUI — graph data, simulation, shadow promotion, canvas PR creation |
+
+---
+
+## Related Documents
+
+- **[Foundational Abstractions](data-model/foundations/)** — Data, Provider, Policy — read this first
+- **[Unified Provider Contract](data-model/provider-contract/)** — base contract + 11 typed extensions
+- **[Unified Policy Contract](data-model/policy-contract/)** — base contract + 7 output schemas
+- **[Federated Contribution Model](data-model/federated-contribution-model/)** — who contributes what and how
+- **[Data Model](data-model/)** — complete 28-document data model reference
+- **[Specifications](specifications/)** — Consumer API, Admin API, Operator Interface, OPA Integration, Flow GUI, Registration, Examples, Kubernetes compatibility, SDK, CNCF strategy
+- **[Capabilities Matrix](../capabilities-matrix/)** — 134 capabilities across 21 domains
diff --git a/content/docs/architecture/specifications/_index.md b/content/docs/architecture/specifications/_index.md
new file mode 100644
index 0000000..76f9d0e
--- /dev/null
+++ b/content/docs/architecture/specifications/_index.md
@@ -0,0 +1,21 @@
+---
+title: Specifications
+type: docs
+weight: 3
+sidebar:
+  open: true
+---
+
+# DCM Specifications
+
+Formal technical specifications for DCM integration interfaces, the Kubernetes
+superset strategy, and community engagement.
+
+{{< cards >}}
+  {{< card link="operator-interface-spec" title="Operator Interface Specification" icon="puzzle" subtitle="Formal contract for Kubernetes operators integrating with DCM. Conformance levels, registration, field mapping, and SDK." >}}
+  {{< card link="kubernetes-compatibility" title="Kubernetes Compatibility" icon="chip" subtitle="Formal Kubernetes-to-DCM concept mappings. DCM as a superset of Kubernetes." >}}
+  {{< card link="operator-sdk-api" title="Operator SDK API" icon="code" subtitle="Go library API design for the DCM Operator SDK — Level 1 conformance in one day." >}}
+  {{< card link="cncf-strategy" title="CNCF Strategy" icon="globe-alt" subtitle="CNCF submission strategy, community engagement plan, and operator ecosystem approach." >}}
+{{< /cards >}}
+
+{{< card link="consumer-api-spec" title="Consumer API Specification" icon="code" subtitle="REST API specification for DCM consumers — service catalog, request submission, resource management, and audit access." >}}
diff --git a/content/docs/architecture/specifications/cncf-strategy.md b/content/docs/architecture/specifications/cncf-strategy.md
new file mode 100644
index 0000000..d296b2c
--- /dev/null
+++ b/content/docs/architecture/specifications/cncf-strategy.md
@@ -0,0 +1,256 @@
+# DCM — CNCF Strategy and Community Engagement Plan
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Strategy Document
+
+
+
+
+**Version:** 0.1.0-draft  
+**Status:** Draft  
+**Document Type:** Strategic Planning  
+**Maintainers:** Red Hat FlightPath Team  
+**Last Updated:** 2026-03
+
+---
+
+## 1. Strategic Intent
+
+DCM's goal is to become the community standard for enterprise data center and private cloud management — a neutral, open standard that the industry adopts the way it adopted CSI, CNI, and CRI. This requires DCM to exist in a community-trusted home, not as a vendor product.
+
+The CNCF (Cloud Native Computing Foundation) is the appropriate home. It provides the neutral governance model, the community infrastructure, and the ecosystem relationships needed to drive broad adoption. The FSI consortium already engaging with DCM (leading FSI consortium members and others) provides the multi-organization sponsorship and real production use case evidence needed for a credible CNCF proposal.
+
+---
+
+## 2. CNCF Landscape and Positioning
+
+### 2.1 Where DCM Fits
+
+The CNCF landscape has strong coverage of Kubernetes runtime concerns — container runtimes, networking, storage, service mesh, observability. It has weaker coverage of the management plane — the layer above Kubernetes that governs what gets provisioned, owned, and decommissioned across multiple clusters and infrastructure types.
+
+DCM fills this gap. It is not competing with existing CNCF projects — it extends and governs them.
+
+**Related CNCF projects and how DCM relates:**
+
+| CNCF Project | Relationship to DCM |
+|-------------|---------------------|
+| **Kubernetes** | DCM is a superset — extends Kubernetes upward to the management plane |
+| **Crossplane** | Complementary — Crossplane provisions cloud resources via Kubernetes CRDs; DCM governs what Crossplane provisions and adds the management plane |
+| **Cluster API (CAPI)** | DCM can manage Kubernetes clusters via CAPI as a Service Provider |
+| **Argo CD / Flux** | Complementary — DCM governs provisioning requests; GitOps manages deployment |
+| **OpenCost** | DCM's cost analysis is a superset — OpenCost data can feed DCM cost attribution |
+| **Kessel** | Shares inventory and relationship goals — potential collaboration or alignment |
+| **OPA/Gatekeeper** | DCM's Policy Engine uses OPA internally; Gatekeeper is the cluster-level enforcement |
+
+### 2.2 The Gap DCM Fills
+
+No current CNCF project addresses all of:
+- Multi-cluster, multi-infrastructure lifecycle management from a single control plane
+- First-class multi-tenancy with Tenant ownership model
+- Policy governance with field-level override control across the full request lifecycle
+- Data sovereignty and compliance evidence for regulated industries
+- Service catalog with self-service consumer experience
+- Cost attribution across heterogeneous infrastructure
+
+This is the gap DCM fills. The positioning is not "another Kubernetes tool" — it is "the management plane that governs your entire data center, of which Kubernetes is one component."
+
+---
+
+## 3. CNCF Submission Path
+
+### 3.1 CNCF Maturity Levels
+
+CNCF accepts projects at three maturity levels:
+
+| Level | Requirements | DCM Target Timeline |
+|-------|-------------|---------------------|
+| **Sandbox** | Alignment with CNCF mission, basic governance, active development | Target for initial submission |
+| **Incubating** | Production users, healthy contributor base, defined governance, security audit | 12-18 months post-Sandbox |
+| **Graduated** | Broad adoption, stable API, long-term maintainer commitment | 24-36 months post-Sandbox |
+
+### 3.2 Sandbox Submission Requirements
+
+For CNCF Sandbox acceptance, DCM needs:
+
+**Technical requirements:**
+- Clear alignment with CNCF's cloud native mission
+- Open source license (Apache 2.0 — already in place)
+- Publicly accessible source code (GitHub — already in place)
+- Documented roadmap
+- Basic security practices (vulnerability disclosure process, etc.)
+
+**Governance requirements:**
+- Defined governance model (maintainers, decision process)
+- Code of conduct
+- Multi-organization contributor base (this is the key requirement — Red Hat alone is insufficient)
+
+**Community requirements:**
+- Evidence of community interest beyond the founding organization
+- At least one non-founding organization actively contributing
+
+**DCM's strong position:**
+The FSI consortium provides exactly the multi-organization evidence CNCF requires. Having leading FSI consortium members as active contributors or committed users is an unusually strong foundation for a Sandbox proposal. Most projects submit to Sandbox without any production users — DCM can submit with evidence of production interest from systemically important financial institutions.
+
+### 3.3 Recommended Submission Path
+
+**Step 1 — CNCF TAG (Technical Advisory Group) engagement**
+Before formal submission, engage with CNCF TAG App Delivery and TAG Runtime. These groups review cloud native tooling proposals and can provide informal feedback before the formal Due Diligence process. Presenting DCM at a TAG meeting builds awareness and surfaces concerns early.
+
+**Step 2 — Prepare the Due Diligence document**
+The CNCF Due Diligence document is a detailed technical and governance questionnaire. Key sections: project description, statement on alignment with CNCF mission, comparison to similar projects, security practices, roadmap, adopters. The FSI consortium adopters section will be a significant differentiator.
+
+**Step 3 — TOC sponsor identification**
+CNCF Technical Oversight Committee (TOC) members sponsor project proposals. Red Hat's relationships in the Kubernetes community make identifying a TOC sponsor feasible. Target TOC members with expertise in multi-cluster management or enterprise Kubernetes.
+
+**Step 4 — Sandbox vote**
+TOC votes on Sandbox acceptance. With a strong Due Diligence document, FSI adopter evidence, and a TOC sponsor, acceptance probability is high.
+
+---
+
+## 4. Community Engagement Strategy
+
+### 4.1 Operator Ecosystem — The Primary Leverage Point
+
+The DCM Operator Interface Specification is the primary community artifact for driving ecosystem adoption. The strategy is to make conformance attractive enough that operator maintainers want to implement it.
+
+**Priority operator communities for engagement:**
+
+| Operator | Community | Why Priority | Engagement Approach |
+|----------|-----------|-------------|---------------------|
+| **KubeVirt** | Red Hat/Community | Active DCM development already | Direct contribution — DCM team contributes Level 2 support |
+| **CloudNativePG** | CNPG Community | High FSI adoption — databases in regulated environments | Present DCM at CNPG community calls, contribute SDK example |
+| **Strimzi (Kafka)** | Red Hat/Community | Messaging infrastructure — DCM Message Bus use case | Direct contribution via Red Hat maintainership |
+| **Cert-Manager** | Jetstack/Venafi | Security resources — every DCM deployment needs certificates | SDK contribution, present at KubeCon |
+| **ACM** | Red Hat | Cluster management — natural DCM complement | Direct — internal Red Hat alignment |
+| **Rook (Ceph)** | CNCF | Storage operator — core DCM service provider use case | CNCF relationship — present at SIG Storage |
+
+### 4.2 KubeCon Strategy
+
+KubeCon is the primary conference for Kubernetes ecosystem influence. DCM needs a presence at KubeCon North America and Europe:
+
+**KubeCon NA (target — next edition):**
+- Submit a talk: "DCM — Managing the Management Plane: Kubernetes as a Component of Enterprise Infrastructure"
+- Submit a contribfest session: hands-on DCM Operator SDK implementation workshop
+- Engage Kubernetes SIG Cluster Lifecycle about CAPI integration
+
+**KubeCon EU (following year):**
+- Present CNCF Sandbox submission (if accepted by then)
+- Case study talk with FSI consortium member (FSI consortium members presenting their DCM deployment)
+- Operator Interface Specification BOF (Birds of a Feather) session
+
+### 4.3 The Developer Value Proposition — What We Need to Communicate
+
+The community message must be concrete and compelling, not abstract. Avoid "unified management plane" as the opener — lead with what operators get:
+
+**For operator developers:**
+> "Add DCM support to your operator and your users get self-service catalog, multi-tenancy, cost attribution, and cross-cluster management — for free. It takes one day using our SDK."
+
+**For platform engineering teams:**
+> "Manage your entire data center from one control plane. VMs, databases, Kubernetes clusters, networking — all with the same declarative model, the same policy engine, and the same audit trail."
+
+**For FSI/regulated industry teams:**
+> "Every provisioning request produces a complete audit chain — who asked for what, what policies applied, what was approved, what was built. Sovereignty constraints enforced at the management plane, not bolted on afterward."
+
+### 4.4 Contributor Onboarding
+
+A project cannot become a standard without contributors beyond the founding organization. The contributor onboarding strategy:
+
+**Good first issues:**
+Maintain a curated list of well-scoped, well-documented issues labeled `good-first-issue`. These should be achievable in a few hours without deep DCM knowledge — documentation improvements, test coverage, example implementations, SDK feature additions.
+
+**Operator SDK examples:**
+Each operator SDK example is a potential contributor touchpoint. An operator maintainer who wants to add DCM support to their operator is a natural contributor. The example for their specific CRD framework (kubebuilder, operator-sdk, raw controller-runtime) lowers the barrier.
+
+**RFC process:**
+Establish a lightweight RFC (Request for Comments) process for significant changes to the DCM Operator Interface Specification. This gives external contributors a formal path to influence the specification direction — which is essential for community trust.
+
+**Monthly community calls:**
+Regular community calls (video, recorded, published) signal active project health and give contributors a forum to discuss ideas. Target: bi-weekly during active development, monthly once stable.
+
+---
+
+## 5. Standards Positioning
+
+Beyond CNCF, DCM should engage with relevant standards bodies where appropriate:
+
+### 5.1 DMTF (Distributed Management Task Force)
+DMTF maintains the TOSCA (Topology and Orchestration Specification for Cloud Applications) and other cloud management standards. DCM's data model has some conceptual overlap with TOSCA. Rather than competing, DCM should position as a Kubernetes-native, GitOps-native evolution of the same problem TOSCA addressed — bringing the conversation into the cloud native era.
+
+### 5.2 FinOS Foundation
+FinOS is the open source community for financial services. The FSI consortium involvement makes FinOS a natural secondary community for DCM. Presenting DCM at FinOS events reaches exactly the regulated industry audience that benefits most from DCM's sovereignty and compliance capabilities.
+
+### 5.3 OpenInfra Foundation
+The OpenInfra Foundation hosts OpenStack, Kata Containers, and StarlingX — all relevant to DCM's target environments (private cloud, edge, regulated infrastructure). DCM should present at OpenInfra Summit to the platform engineering teams who manage these environments.
+
+---
+
+## 6. What Needs to Exist Before CNCF Submission
+
+The following artifacts must be ready before a CNCF Sandbox submission is credible:
+
+| Artifact | Status | Owner | Target |
+|----------|--------|-------|--------|
+| DCM Operator Interface Specification v1.0 | 🔄 Draft | DCM Project | Ready |
+| DCM Operator SDK v0.1.0 (Level 1 + Level 2) | 📋 Not started | DCM Project | 3-6 months |
+| KubeVirt reference implementation (Level 2) | 🔄 In progress | DCM/KubeVirt teams | 3-6 months |
+| Conformance test suite (Level 1 + Level 2) | 📋 Not started | DCM Project | 3-6 months |
+| CNCF Due Diligence document | 📋 Not started | DCM Project | 6 months |
+| Governance model document | 📋 Not started | Red Hat/Consortium | 3 months |
+| Security vulnerability disclosure process | 📋 Not started | Red Hat Security | 1 month |
+| FSI consortium adopter statements | 📋 Not started | Consortium members | 3 months |
+| Second non-Red Hat maintainer | 📋 Not started | Community | 6 months |
+
+---
+
+## 7. Risk Considerations
+
+| Risk | Likelihood | Impact | Mitigation |
+|------|-----------|--------|------------|
+| CNCF TAG sees overlap with Crossplane | Medium | Medium | Prepare clear differentiation — DCM governs, Crossplane provisions; they are complementary |
+| Operator communities resist specification adoption | Medium | High | Lead with SDK ease, reference implementations, concrete value; don't mandate, make it attractive |
+| Red Hat perceived as controlling the standard | Medium | High | Establish CNCF governance early, actively recruit non-Red Hat maintainers, FSI consortium co-ownership |
+| Specification fragmentation — forks or competing standards | Low | High | CNCF neutral governance prevents this; be the first mover in this space |
+| Key contributor departure | Low | Medium | CNCF governance ensures project continuity beyond any single contributor |
+
+---
+
+## 8. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should the CNCF submission be for DCM as a whole or for the DCM Operator Interface Specification as a standalone standard? | Scope of submission | ✅ Resolved |
+| 2 | Which FSI consortium members are willing to be named as public adopters in the CNCF submission? | Submission strength | ✅ Resolved |
+| 3 | Is there a TOC member with relevant expertise who could sponsor the DCM proposal? | Submission path | ✅ Resolved |
+| 4 | Should DCM engage with the Kubernetes SIG structure before or after CNCF Sandbox submission? | Community positioning | ✅ Resolved |
+| 5 | What is the timeline for the KubeVirt reference implementation reaching Level 2 conformance? | Readiness milestone | ✅ Resolved |
+
+---
+
+
+
+## Resolution Notes
+
+**Q1:** Submit the DCM Operator Interface Specification as a CNCF specification project first. CNCF Sandbox project submission for DCM as a whole follows once a reference implementation reaches Level 2 conformance. Submitting the specification standard separately lowers the implementation bar for initial acceptance and establishes the interface contract independently of any single implementation.
+
+**Q2:** Identify a minimum of two named production evaluators and one FSI design partner before submission. At least one named organization should be willing to go on record. This is a project team action item — the architecture does not determine who those organizations are.
+
+**Q3:** Target the App Delivery TAG and Runtime TAG for initial sponsor identification. Engage SIG App Delivery and SIG Cluster Lifecycle before submission — SIG members frequently become TOC sponsors. Project team action item.
+
+**Q4:** SIG engagement comes before Sandbox submission. SIG App Delivery and SIG Cluster Lifecycle are the primary targets. The Cluster API overlap specifically must be addressed with SIG Cluster Lifecycle before submission. Pre-submission SIG engagement surfaces conflicts, identifies sponsors, and positions DCM as a collaborative project rather than a competing one.
+
+**Q5:** Level 2 conformance requires: full dispatch/cancel/discover cycle, full realized state reporting, governance matrix enforcement at the provider boundary, and health check compliance. These requirements are now formally defined in the Operator Interface Specification. The project team estimates timeline based on available engineering resources against this defined scope.
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+## Red Hat Developer Hub / Backstage Integration
+
+DCM implements a Backstage plugin suite (`@dcm/backstage-plugin-*`) for deployment as RHDH Dynamic Plugins. This is the primary consumer-facing deployment model. See [RHDH Integration Specification](dcm-rhdh-integration-spec.md) for the complete architecture.
+
+**CNCF alignment:** Backstage is a CNCF incubating project. DCM's RHDH integration follows CNCF best practices for developer portals and internal developer platforms (IDPs).
+
+| Component | CNCF Status | DCM Use |
+|-----------|------------|---------|
+| Backstage | Incubating | Primary consumer GUI platform |
+| Backstage Software Templates | Backstage feature | Auto-generated from DCM catalog items |
+| Backstage Catalog | Backstage feature | DCMService and DCMResource entity kinds |
+| Backstage Permission Framework | Backstage feature | DCM role → Backstage permission bridge |
diff --git a/content/docs/architecture/specifications/consumer-api-spec.md b/content/docs/architecture/specifications/consumer-api-spec.md
new file mode 100644
index 0000000..8254535
--- /dev/null
+++ b/content/docs/architecture/specifications/consumer-api-spec.md
@@ -0,0 +1,2235 @@
+# DCM Consumer API Specification
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** API Narrative Specification
+
+
+> **📋 Draft**
+>
+> This specification covers the full Consumer API surface. Endpoint paths, request/response structures, and authentication flows represent design intent and will be refined as implementation proceeds. Feedback and contributions welcome via [GitHub Issues](https://github.com/dcm-project/issues).
+
+**Version:** 0.1.0-draft
+**Status:** Draft — Ready for implementation feedback
+**Document Type:** Technical Specification
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [DCM Operator Interface Specification](dcm-operator-interface-spec.md) | [Four States](../data-model/02-four-states.md) | [Auth Providers](../data-model/19-auth-providers.md) | [Webhooks and Messaging](../data-model/18-webhooks-messaging.md)
+
+---
+
+## Abstract
+
+This specification defines the interface by which consumers interact with the DCM Control Plane. It is the counterpart to the [Operator Interface Specification](dcm-operator-interface-spec.md), which covers what Service Providers implement. This specification covers what consumers call.
+
+The Consumer API is the boundary between the Application domain and the Control Plane. It exposes DCM's service catalog, request submission, resource management, and audit capabilities in a unified interface. All operations are authenticated, authorized against the actor's Tenant scope, and fully audited.
+
+---
+
+> **AEP Alignment:** This specification follows [AEP](https://aep.dev) conventions.
+> Custom methods use colon syntax (`POST /resources/{name}:suspend`).
+> Async operations return an `Operation` resource (AEP-136 LRO) — poll `operation.name` for completion.
+> List pagination uses `page_size` and `page_token` parameters.
+> See the normative OpenAPI specification: `schemas/openapi/dcm-consumer-api.yaml`
+
+## 1. Introduction
+
+### 1.1 Scope
+
+This specification covers:
+- Authentication and session management
+- Service Catalog browsing and discovery
+- Resource request submission (all ingress paths)
+- Resource lifecycle management (updates, suspension, decommission, rehydration)
+- Request and resource status
+- Audit trail access
+
+It does not cover:
+- Platform administration operations (covered by future Admin API spec)
+- Service Provider registration and management (covered by Operator Interface Spec)
+- Webhook and Message Bus subscription management (covered by doc 18)
+
+### 1.2 Ingress Surfaces
+
+The Consumer API is accessible via three ingress surfaces. All three are authenticated. All three run the same governance pipeline. The ingress surface affects the review workflow, never governance.
+
+| Surface | Protocol | Review Model | Use Case |
+|---------|----------|-------------|----------|
+| **REST API** | HTTPS REST | Synchronous acknowledgment; async realization | Programmatic consumers, automation, Terraform providers |
+| **Web UI** | Browser | Interactive PR-like review flow | Human consumers, Service Catalog browsing |
+| **Git PR Ingress** | Git + webhook | Full GitOps PR workflow | GitOps-native teams, infrastructure-as-code workflows |
+
+This specification primarily documents the REST API surface. The Git PR ingress YAML structure is documented in [Worked Examples](../data-model/04-examples.md), Section 2.
+
+### 1.3 Base URL and Versioning
+
+```
+https://{dcm-instance}/api/v1/
+```
+
+All Consumer API endpoints are versioned. Breaking changes increment the major version segment (`v1` → `v2`). Non-breaking additions do not change the version.
+
+> **Full versioning strategy:** See [API Versioning Strategy](../data-model/34-api-versioning-strategy.md) for the complete definition of breaking changes, deprecation timeline, version discovery, sunset behavior, deprecation headers, and VER-001–VER-009 system policies.
+
+**Key rules for Consumer API consumers:**
+- Pin to a specific version (`/api/v1/`) in production — do not use the `/api/latest/` alias
+- When a version is deprecated, responses include `Deprecation` and `Sunset` headers (RFC 8594/RFC 9745)
+- Deprecated versions remain functional until the sunset date — bugs fixed, features not backported
+- Version discovery: `GET /.well-known/dcm-api-versions`
+- Migration guide: `GET /api/v{N}/migration-guide`
+
+**What is a breaking change in the Consumer API:**
+Removing a field, changing a field type, removing an endpoint, changing HTTP status semantics, tightening validation, changing URL structure. New optional fields, new endpoints, and expanded enums are not breaking.
+
+**Support windows (profile-governed):**
+- `minimal`: 90 days notice, 180 days deprecated support
+- `standard`: 180 days notice, 365 days deprecated support
+- `prod`: 365 days notice, 730 days (2 years) deprecated support
+- `fsi`: 18 months notice, 3 years deprecated support
+- `sovereign`: 2 years notice, 4 years deprecated support
+
+### 1.4 Content Type
+
+All requests and responses use `application/json`. The DCM Unified Data Model is expressed as JSON throughout the Consumer API.
+
+### 1.5 Idempotency
+
+DCM's request model provides built-in idempotency for `POST /api/v1/requests`. Each request submission produces an `entity_uuid` at Intent State creation. If a client retries a request submission (e.g. after a network timeout), it may receive a duplicate Intent State — but DCM's deduplication layer detects identical payloads from the same actor within a 5-minute window and returns the existing request record rather than creating a second one.
+
+For operations where explicit idempotency control is needed, clients may supply an `Idempotency-Key` header:
+
+```http
+POST /api/v1/requests
+Idempotency-Key: 
+```
+
+If DCM receives two requests with the same `Idempotency-Key` from the same authenticated actor within PT24H, the second request returns the response from the first. The idempotency key is stored for PT24H then discarded.
+
+**Which endpoints support `Idempotency-Key`:**
+- `POST /api/v1/requests` — resource request submission
+- `POST /api/v1/credentials/{uuid}:rotate` — credential rotation request
+- `POST /api/v1/resources/{uuid}:rehydrate` — rehydration trigger
+
+### 1.6 Rate Limiting
+
+Rate limits are profile-governed and apply per authenticated actor:
+
+| Profile | Requests/minute | Burst allowance | Rate page_size header |
+|---------|----------------|-----------------|-------------------|
+| `minimal` | 60 | 20 | Yes |
+| `standard` | 300 | 100 | Yes |
+| `prod` | 600 | 200 | Yes |
+| `fsi` | 600 | 200 | Yes |
+| `sovereign` | 600 | 200 | Yes |
+
+When rate limited, DCM returns:
+
+```http
+HTTP/1.1 429 Too Many Requests
+Retry-After: 12
+X-RateLimit-Limit: 300
+X-RateLimit-Remaining: 0
+X-RateLimit-Reset: 1749340800
+
+{
+  "error": "rate_limit_exceeded",
+  "message": "Request rate page_size exceeded. Retry after 12 seconds.",
+  "retry_after_seconds": 12
+}
+```
+
+### 1.7 Request and Correlation IDs
+
+Every DCM API response includes:
+
+```http
+X-DCM-Request-ID:        # unique ID for this HTTP request; use for support
+X-DCM-Correlation-ID:    # links related requests across the pipeline
+```
+
+Include `X-DCM-Request-ID` when contacting support. Use `X-DCM-Correlation-ID` to trace a request through the audit trail (`GET /api/v1/audit/correlation/{correlation_id}`).
+
+---
+
+### 1.8 Standard Response Envelopes
+
+**List responses** always use this envelope:
+```json
+{
+  "items": [...],           // always "items" regardless of resource type
+  "total": 142,             // total matching records (before pagination)
+  "page_size": 25,
+  "next_page_token": "" // null if no more pages; use as ?page_token= on next request
+}
+```
+
+**Single resource responses** return the resource object directly (no wrapper).
+
+**Error responses** always use:
+```json
+{
+  "error": "",    // machine-readable snake_case code
+  "message": "",      // human-readable description
+  "request_id": "",     // matches X-DCM-Request-ID header
+  "details": {}               // optional: field-level validation errors etc.
+}
+```
+
+## 2. Authentication
+
+### 2.1 Token Acquisition
+
+Consumers obtain a session token from the Auth Provider. The token acquisition method depends on the configured Auth Provider:
+
+```
+POST /api/v1/auth/token
+
+# OIDC flow (most common):
+{
+  "grant_type": "authorization_code",
+  "code": "",
+  "redirect_uri": ""
+}
+
+# API key flow (service accounts):
+{
+  "grant_type": "api_key",
+  "api_key": ""
+}
+
+# Response:
+{
+  "token": "",
+  "token_type": "Bearer",
+  "expires_at": "",
+  "actor_uuid": "",
+  "mfa_verified": true,
+  "scopes": ["read:catalog", "request:compute", "manage:owned"]
+}
+```
+
+### 2.2 Request Authentication
+
+All requests carry the session token as a Bearer token:
+
+```
+Authorization: Bearer 
+```
+
+### 2.3 Tenant Context
+
+Actors may have access to multiple Tenants. The Tenant context for a request is declared in the header:
+
+```
+X-DCM-Tenant: 
+```
+
+If omitted and the actor has access to exactly one Tenant, that Tenant is used. If the actor has access to multiple Tenants and no Tenant header is provided, the request is rejected with `400 Bad Request` — Tenant ambiguity is never resolved silently.
+
+### 2.4 Step-Up MFA
+
+Some operations require step-up MFA regardless of session MFA status. When a step-up challenge is required, the API returns `403 Forbidden` with a challenge token:
+
+```json
+{
+  "error": "step_up_required",
+  "challenge_token": "",
+  "challenge_expires_at": "",
+  "allowed_methods": ["totp", "push_notification"]
+}
+```
+
+The consumer completes the MFA challenge and retries the request with the completed challenge token:
+
+```
+X-DCM-StepUp-Token: 
+```
+
+---
+
+### 2.5 Session Management
+
+DCM issues a session token on successful authentication. Sessions can be managed and revoked by the authenticated actor.
+
+```http
+# Log out current session
+DELETE /api/v1/auth/session
+Authorization: Bearer 
+
+Response 204 No Content
+```
+
+```http
+# Log out all sessions for this actor
+DELETE /api/v1/auth/sessions
+Authorization: Bearer 
+
+Response 204 No Content
+```
+
+```http
+# List active sessions for this actor
+GET /api/v1/auth/sessions
+Authorization: Bearer 
+
+Response 200:
+{
+  "items": [
+    {
+      "session_uuid": "",
+      "created_at": "",
+      "expires_at": "",
+      "auth_method": "oidc",
+      "mfa_verified": true,
+      "last_active_at": "",
+      "is_current": true
+    }
+  ],
+  "total": 2
+}
+```
+
+```http
+# Revoke a specific session
+DELETE /api/v1/auth/sessions/{session_uuid}
+Authorization: Bearer 
+
+Response 204 No Content
+```
+
+```http
+# Token introspection (RFC 7662) — for internal components and trusted integrations
+POST /api/v1/auth:introspect
+Authorization: Bearer 
+
+{ "token": "" }
+
+Response 200:
+{
+  "active": true,
+  "session_uuid": "",
+  "actor_uuid": "",
+  "tenant_uuid": "",
+  "expires_at": "",
+  "mfa_verified": true,
+  "auth_method": "oidc"
+}
+```
+
+> **Session revocation model:** See [Session Token Revocation](../data-model/35-session-revocation.md) for the complete session lifecycle, revocation triggers, revocation registry, profile-governed TTLs, and AUTH-016–AUTH-022 system policies.
+
+
+## 3. Service Catalog
+
+### 3.1 List Catalog Items
+
+Returns catalog items available to the authenticated actor in their Tenant, filtered by RBAC.
+
+```
+GET /api/v1/catalog
+
+Query parameters:
+  category=    e.g., Compute, Network, Storage
+  search=                      full-text search across name and description
+  tag=                         filter by tag (repeatable)
+  page=                           pagination (default: 1)
+  page_size=                      results per page (default: 25, max: 100)
+
+Response 200:
+{
+  "catalog_items": [
+    {
+      "catalog_item_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "provider_uuid": "",
+      "display_name": "Standard Linux VM",
+      "description": "General-purpose virtual machine with standard OS images",
+      "tier": 1,
+      "portability_class": "portable",
+      "estimated_cost": {
+        "unit": "per-hour",
+        "amount": 0.32,
+        "currency": "USD",
+        "cost_confidence": "high"
+      },
+      "tags": ["compute", "linux", "general-purpose"],
+      "deprecated": false
+    }
+  ],
+  "total": 47,
+  "page": 1,
+  "page_size": 25
+}
+```
+
+### 3.2 Describe Catalog Item
+
+Returns the full schema for a catalog item — all fields, constraints, editability declarations, dependencies, and cost estimate.
+
+```
+GET /api/v1/catalog/{catalog_item_uuid}
+
+Response 200:
+{
+  "catalog_item_uuid": "",
+  "resource_type": "Compute.VirtualMachine",
+  "resource_type_spec_version": "2.1.0",
+  "display_name": "Standard Linux VM",
+
+  "schema": {
+    "fields": [
+
+      // ── Static constraint fields ───────────────────────────────────────────
+      {
+        "field_name": "cpu_count",
+        "display_name": "CPU Cores",
+        "type": "integer",
+        "required": true,
+        "editable_post_realization": false,
+        "constraint": {
+          "visibility": "full",
+          "type": "enum",
+          "allowed_values": [1, 2, 4, 8, 16, 32],
+          "default": 4,
+          "reason": "CPU counts must be powers of 2 for NUMA alignment"
+        }
+      },
+
+      // ── Layer-referenced field: OS image ───────────────────────────────────
+      // Allowed values come from active os_image Reference Data Layers.
+      // Each entry carries the full structured layer data the GUI needs.
+      // Consumer submits the layer UUID; DCM injects all image metadata into payload.
+      {
+        "field_name": "os_image",
+        "display_name": "Operating System Image",
+        "type": "string",
+        "required": true,
+        "editable_post_realization": false,
+        "constraint": {
+          "visibility": "full",
+          "type": "layer_reference",
+          "layer_type": "os_image",
+          "allowed_values": [
+            {
+              "value": "layer-uuid-rhel-9-4",
+              "display_name": "RHEL 9.4",
+              "os_family": "rhel",
+              "version": "9.4",
+              "fips_compliant": true,
+              "eol_date": "2032-05-31",
+              "approved_for_classifications": ["public","internal","confidential","restricted"]
+            },
+            {
+              "value": "layer-uuid-ubuntu-24-04",
+              "display_name": "Ubuntu 24.04 LTS",
+              "os_family": "ubuntu",
+              "version": "24.04",
+              "fips_compliant": false,
+              "eol_date": "2029-04-30",
+              "approved_for_classifications": ["public","internal"]
+            }
+          ]
+        }
+      },
+
+      // ── Layer-referenced field: location ───────────────────────────────────
+      // Allowed values come from active location.data_center layers the
+      // consumer is entitled to and that this catalog item is eligible for.
+      // Selecting a location causes the full location layer chain
+      // (Country → Region → Zone → Site → DC) to assemble into the payload.
+      {
+        "field_name": "location",
+        "display_name": "Allocation Location",
+        "type": "string",
+        "required": true,
+        "editable_post_realization": false,
+        "constraint": {
+          "visibility": "full",
+          "type": "layer_reference",
+          "layer_type": "location.data_center",
+          "allowed_values": [
+            {
+              "value": "layer-uuid-fra-dc1",
+              "display_name": "DC1 — Frankfurt Alpha",
+              "code": "FRA-DC1",
+              "zone": "eu-west-1a",
+              "region": "EU West",
+              "sovereignty": "EU/GDPR",
+              "certifications": ["ISO 27001", "SOC 2 Type II"],
+              "max_data_classification": "restricted",
+              "capacity_status": "available"
+            },
+            {
+              "value": "layer-uuid-ams-dc2",
+              "display_name": "DC2 — Amsterdam Beta",
+              "code": "AMS-DC2",
+              "zone": "eu-west-1b",
+              "region": "EU West",
+              "sovereignty": "EU/GDPR",
+              "certifications": ["ISO 27001"],
+              "max_data_classification": "confidential",
+              "capacity_status": "limited"
+            }
+          ]
+        }
+      },
+
+      // ── Layer-referenced field: approved_size ──────────────────────────────
+      // Allowed values come from active vm_size Reference Data Layers.
+      // Selecting a size injects CPU, RAM, and storage defaults into the payload
+      // (which the consumer can override within the size's declared constraints).
+      {
+        "field_name": "size",
+        "display_name": "VM Size",
+        "type": "string",
+        "required": false,
+        "editable_post_realization": false,
+        "constraint": {
+          "visibility": "full",
+          "type": "layer_reference",
+          "layer_type": "vm_size",
+          "allowed_values": [
+            {
+              "value": "layer-uuid-small",
+              "display_name": "Small (2 CPU / 8 GB)",
+              "cpu_count": 2, "memory_gb": 8, "storage_gb": 40
+            },
+            {
+              "value": "layer-uuid-medium",
+              "display_name": "Medium (8 CPU / 32 GB)",
+              "cpu_count": 8, "memory_gb": 32, "storage_gb": 80
+            },
+            {
+              "value": "layer-uuid-large",
+              "display_name": "Large (16 CPU / 64 GB)",
+              "cpu_count": 16, "memory_gb": 64, "storage_gb": 160
+            }
+          ]
+        }
+      },
+
+      // ── Policy-injected hidden field ───────────────────────────────────────
+      {
+        "field_name": "monitoring_agent",
+        "type": "string",
+        "required": false,
+        "editable_post_realization": false,
+        "constraint": {
+          "visibility": "hidden",
+          "override": "immutable",
+          "note": "Injected by policy — consumer cannot set or view"
+        }
+      }
+
+    ]
+  },
+
+  "dependencies": [
+    {
+      "resource_type": "Network.IPAddress",
+      "relationship": "requires",
+      "fulfillment": "automatic",          # DCM auto-allocates; consumer does not need to request separately
+      "count": 1
+    }
+  ],
+
+  "estimated_cost": {
+    "breakdown": [
+      { "component": "compute", "unit": "per-hour", "amount": 0.28, "currency": "USD" },
+      { "component": "ip-allocation", "unit": "per-hour", "amount": 0.04, "currency": "USD" }
+    ],
+    "total_per_hour": 0.32,
+    "currency": "USD"
+  },
+
+  "sovereignty": {
+    "available_in_regions": ["EU-WEST", "EU-NORTH"],
+    "data_residency_guarantee": "EU"
+  },
+  "accreditations": [
+    {
+      "framework": "hipaa",
+      "accreditation_type": "baa",
+      "status": "active",
+      "expires_at": "",
+      "max_data_classification": "phi"
+    }
+  ],
+  "zero_trust_posture": "full",
+  "max_data_classification_accepted": "phi"
+}
+```
+
+### 3.3 Catalog Search
+
+```
+GET /api/v1/catalog/search?q=
+
+Returns catalog items matching the query across name, description, resource type, and tags.
+Same response shape as List Catalog Items.
+```
+
+---
+
+## 4. Request Submission and Lifecycle
+
+### 4.1 Submit Resource Request
+
+Submits a new resource request. Returns immediately with an acknowledgment — realization is asynchronous.
+
+```
+POST /api/v1/requests
+
+Request body:
+{
+  "catalog_item_uuid": "",
+  "fields": {
+    "cpu_count": 4,
+    "memory_gb": 8,
+    "storage_gb": 100,
+    "os_family": "rhel",
+    "name": "payments-api-server-01"
+  },
+  "options": {
+    "auto_approve": true,              # request auto-approval if policy permits
+    "notify_on_completion": true,
+    "notification_endpoint": "https://my-system.example.com/dcm/webhook"
+  }
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "request_uuid": "",
+  "entity_uuid": "",            # the UUID the entity will have when realized
+  "status": "ACKNOWLEDGED",
+  "intent_state_ref": "",
+  "estimated_completion": "",
+  "status_url": "/api/v1/requests/{request_uuid}/status",
+  "dry_run_result": null,             # null if auto-approve; populated if review required
+  "risk_score": 47,                   # aggregate request risk score (0–100)
+  "routing_decision": "reviewed", # auto_approved | pending_review | pending_verified | pending_authorized
+  "score_drivers": [                  # top 3 contributing factors (human-readable)
+    "Estimated monthly cost exceeds Tenant ceiling",
+    "Request submitted outside business hours",
+    "Actor has 2 recent validation failures"
+  ],
+  "advisory_warnings": [              # from advisory-class Validation policies
+    {
+      "warning_code": "recommended_field_absent",
+      "warning_message": "cost_center not provided — cost attribution will use Tenant default",
+      "field": "fields.cost_center"
+    }
+  ]
+}
+
+Response 200 OK (if policy requires pre-validation report before submission):
+{
+  "dry_run": true,
+  "validation_result": {
+    "policies_evaluated": [...],
+    "gatekeeper_decisions": [{ "policy": "vm-size-limits", "result": "approved" }],
+    "estimated_cost": {...},
+    "sovereignty_check": { "satisfied": true, "constraints": ["data_residency: EU"] },
+    "would_auto_approve": true
+  }
+}
+```
+
+### 4.2 Request Status
+
+```
+GET /api/v1/requests/{request_uuid}/status
+
+Response 200:
+{
+  "request_uuid": "",
+  "entity_uuid": "",
+  "status": "PROVISIONING",
+  "status_history": [
+    { "status": "ACKNOWLEDGED", "at": "2026-03-15T09:00:00Z" },
+    { "status": "ASSEMBLING",   "at": "2026-03-15T09:00:02Z" },
+    { "status": "DISPATCHED",   "at": "2026-03-15T09:00:47Z" },
+    { "status": "PROVISIONING", "at": "2026-03-15T09:01:05Z" }
+  ],
+  "current_step": "Provider is provisioning the resource",
+  "estimated_completion": "2026-03-15T09:05:00Z"
+}
+
+# Terminal status response:
+{
+  "request_uuid": "",
+  "entity_uuid": "",
+  "status": "COMPLETED",
+  "completed_at": "2026-03-15T09:03:12Z",
+  "resource_url": "/api/v1/resources/{entity_uuid}"
+}
+
+# Failed request:
+{
+  "request_uuid": "",
+  "entity_uuid": "",
+  "status": "FAILED",
+  "failed_at": "2026-03-15T09:02:45Z",
+  "failure_reason": "Provider capacity exhausted — all eligible providers at capacity",
+  "retry_eligible": true,
+  "retry_after": "PT15M"
+}
+```
+
+### 4.3 Live Request Status Stream (Server-Sent Events)
+
+For consumers that want live status updates without polling, DCM exposes a Server-Sent Events (SSE) stream per request:
+
+```
+GET /api/v1/requests/{request_uuid}/stream
+Accept: text/event-stream
+Authorization: Bearer 
+
+# Response: HTTP 200, Content-Type: text/event-stream
+# Connection stays open; events pushed as state changes
+
+event: status_change
+data: {"status":"PROVISIONING","at":"2026-04-01T02:00:05Z","current_step":"Configuring network interfaces"}
+
+event: progress_updated
+data: {"step_current":3,"step_total":7,"step_label":"Configuring network interfaces","constituent_status":[{"ref":"vm","status":"REALIZED"},{"ref":"dns","status":"PROVISIONING"}]}
+
+event: status_change
+data: {"status":"COMPLETED","at":"2026-04-01T02:03:12Z"}
+
+# Stream closes on terminal status (COMPLETED, FAILED, CANCELLED)
+```
+
+**SSE events on this stream:**
+
+| Event name | When | Data fields |
+|------------|------|-------------|
+| `status_change` | Request status changes | status, at, current_step |
+| `progress_updated` | Provider sends interim progress | step_current, step_total, step_label, constituent_status |
+| `approval_required` | Request routed to approval tier | approval_uuid, required_tier, window_expires_at |
+| `approval_recorded` | A reviewer votes | votes_recorded, quorum_required, quorum_reached |
+| `heartbeat` | Every 30s (keep-alive) | ts |
+
+**Constituent status** (for compound/compound service definition requests):
+```json
+{
+  "constituent_status": [
+    { "ref": "vm",      "status": "REALIZED",     "entity_uuid": "" },
+    { "ref": "ip",      "status": "REALIZED",     "entity_uuid": "" },
+    { "ref": "dns",     "status": "PROVISIONING", "entity_uuid": null },
+    { "ref": "storage", "status": "PENDING",      "entity_uuid": null }
+  ]
+}
+```
+
+**Fallback:** Consumers that cannot use SSE (e.g. some proxy configurations) should use polling via `GET /api/v1/requests/{uuid}/status` with an appropriate interval.
+
+**Connection limits:** One SSE stream per request_uuid per actor. Opening a second stream closes the first.
+
+
+### 4.4 Consumer Request Status Lifecycle
+
+```
+ACKNOWLEDGED            → request received; intent created
+ASSEMBLING              → Request Payload Processor running layer assembly
+AWAITING_APPROVAL       → policy requires human review before dispatch
+APPROVED                → proceeding to assembly and dispatch
+DISPATCHED              → provider received payload; awaiting confirmation
+PROVISIONING            → provider executing
+COMPLETED               → provider confirmed realization; Realized State written
+FAILED                  → terminal; failure_reason and retry_eligible populated
+CANCELLED               → consumer-initiated cancellation; clean terminal
+CANCELLING              → cancellation in progress; provider notified
+TIMEOUT_PENDING         → dispatch timeout fired; recovery policy evaluating
+LATE_REALIZATION_PENDING → provider responded after timeout; recovery decision pending
+INDETERMINATE_REALIZATION → state ambiguous; drift detection resolving
+COMPENSATION_IN_PROGRESS → compound service rollback underway
+COMPENSATION_FAILED     → rollback failed; platform admin notified; orphan detection active
+PENDING_REVIEW          → conflict detected requiring human resolution
+```
+
+### 4.5 Cancel Request
+
+Cancellation is only available before the PROVISIONING state. Once a provider is executing, cancellation moves to CANCELLING and depends on provider support.
+
+```
+DELETE /api/v1/requests/{request_uuid}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "request_uuid": "",
+  "status": "CANCELLING",
+  "message": "Cancellation requested. Provider will be notified if dispatch has occurred."
+}
+
+Response 409 Conflict (if cancellation not possible):
+{
+  "error": "cancellation_not_available",
+  "reason": "Resource is in PROVISIONING state. Cancellation requires provider support.",
+  "provider_supports_cancellation": false
+}
+```
+
+---
+
+### 4.5 Request Groups (Dependency Graph)
+
+Consumers can declare an ordered dependency graph across independent requests using request groups. DCM dispatches constituent requests in dependency order.
+
+```http
+# Create a request group
+POST /api/v1/request-groups
+Authorization: Bearer 
+
+{
+  "label": "provision-web-stack",
+  "requests": [
+    { "request_uuid": "", "depends_on": [] },
+    { "request_uuid": "", "depends_on": [""] },
+    { "request_uuid": "", "depends_on": [""] }
+  ]
+}
+
+Response 201:
+{
+  "request_group_uuid": "",
+  "label": "provision-web-stack",
+  "status": "pending",
+  "requests": [...]
+}
+```
+
+```http
+# Get request group status
+GET /api/v1/request-groups/{group_uuid}
+Authorization: Bearer 
+
+Response 200:
+{
+  "request_group_uuid": "",
+  "status": "in_progress",
+  "requests": [
+    { "request_uuid": "", "status": "realized", "dispatched_at": "..." },
+    { "request_uuid": "", "status": "dispatched", "dispatched_at": "..." },
+    { "request_uuid": "", "status": "pending_dependency", "blocked_by": [""] }
+  ]
+}
+```
+
+> **Request dependency graph model:** See [Request Dependency Graph](../data-model/38-request-dependency-graph.md) for cycle detection, partial failure handling, and RDG-001–RDG-006 system policies.
+
+
+## 5. Resource Management
+
+### 5.1 List Owned Resources
+
+```
+GET /api/v1/resources
+
+Query parameters:
+  resource_type=
+  lifecycle_state=
+  drift_status=
+  tag=
+  page=
+  page_size=
+
+Response 200:
+{
+  "resources": [
+    {
+      "entity_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "display_name": "payments-api-server-01",
+      "lifecycle_state": "OPERATIONAL",
+      "drift_status": "clean",
+      "owned_by_tenant_uuid": "",
+      "created_at": "",
+      "provider_uuid": "",
+      "estimated_cost_per_hour": 0.32
+    }
+  ],
+  "total": 12
+}
+```
+
+### 5.2 Describe Resource
+
+```
+GET /api/v1/resources/{entity_uuid}
+
+Response 200:
+{
+  "entity_uuid": "",
+  "resource_type": "Compute.VirtualMachine",
+  "lifecycle_state": "OPERATIONAL",
+  "drift_status": "clean",
+  "last_discovered_at": "",
+
+  "fields": {
+    "cpu_count": {
+      "value": 4,
+      "confidence": { "band": "very_high", "score": 98 },
+      "editable": false
+    },
+    "memory_gb": {
+      "value": 8,
+      "confidence": { "band": "very_high", "score": 98 },
+      "editable": false
+    }
+  },
+
+  "relationships": [
+    {
+      "type": "attached_to",
+      "related_entity_uuid": "",
+      "related_entity_type": "Network.VLAN",
+      "stake_strength": "required"
+    }
+  ],
+
+  "cost": {
+    "current_billing_state": "billable",
+    "estimated_cost_per_hour": 0.32,
+    "currency": "USD"
+  },
+
+  "rehydration_constraints": {
+    "min_auth_level": "oidc_mfa"
+  },
+
+  "data_classification_summary": {
+    "fields_with_phi": 0,
+    "fields_with_restricted": 2,
+    "highest_classification": "restricted"
+  },
+
+  "pending_provider_notifications": [
+    {
+      "notification_uuid": "",
+      "notification_type": "auto_scale",
+      "submitted_at": "",
+      "status": "pending_approval",
+      "changed_fields": ["memory_gb"],
+      "approval_url": "/api/v1/resources/{entity_uuid}/provider-notifications/{notification_uuid}:approve"
+    }
+  ]
+}
+```
+
+### 5.3 Update Editable Fields (Targeted Delta)
+
+Updates one or more editable fields on a realized resource. Does not re-run layer assembly — only the declared changes are dispatched to the provider.
+
+```
+PATCH /api/v1/resources/{entity_uuid}
+
+Request body:
+{
+  "updates": {
+    "name": "payments-api-server-01-renamed"
+  },
+  "reason": "Renamed to align with new naming convention"
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "update_request_uuid": "",
+  "entity_uuid": "",
+  "status": "DISPATCHED",
+  "fields_updated": ["name"],
+  "status_url": "/api/v1/requests/{update_request_uuid}/status"
+}
+
+Response 422 Unprocessable (if field is not editable):
+{
+  "error": "field_not_editable",
+  "field": "cpu_count",
+  "reason": "cpu_count is not declared as editable post-realization for this resource type"
+}
+```
+
+### 5.4 Suspend Resource
+
+```
+POST /api/v1/resources/{entity_uuid}:suspend
+
+Request body:
+{
+  "reason": "Taking offline for maintenance window",
+  "auto_resume_at": "2026-03-16T06:00:00Z"    # optional
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "entity_uuid": "",
+  "status": "SUSPENDING",
+  "auto_resume_at": "2026-03-16T06:00:00Z"
+}
+```
+
+### 5.5 Decommission Resource
+
+```
+DELETE /api/v1/resources/{entity_uuid}
+
+Request body:
+{
+  "reason": "Project completed — resource no longer needed",
+  "force": false    # true to force even if non-required stakes/relationships exist
+                    # cannot force decommission if required stakes exist
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "entity_uuid": "",
+  "status": "DECOMMISSIONING"
+}
+
+Response 409 Conflict (required stakes or dependencies active):
+{
+  "error": "decommission_deferred",
+  "reason": "Resource has active required stake relationships",
+  "active_required_stakes": [
+    {
+      "stakeholder_entity_uuid": "",
+      "stakeholder_resource_type": "Compute.VirtualMachine",
+      "stake_strength": "required"
+    }
+  ],
+  "resolution": "Release all required stakes before decommissioning, or request stakeholders to migrate"
+}
+```
+
+### 5.6 Trigger Rehydration
+
+```
+POST /api/v1/resources/{entity_uuid}:rehydrate
+
+Request body:
+{
+  "source": "realized",              # intent | requested | realized
+  "placement": {
+    "re_evaluate": false
+  },
+  "governance": {
+    "policy_version": "current"
+  },
+  "reason": "Provider migration — EU-WEST-Prod-1 being decommissioned"
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "rehydration_request_uuid": "",
+  "entity_uuid": "",
+  "status": "ACKNOWLEDGED",
+  "lease_uuid": "",
+  "status_url": "/api/v1/requests/{rehydration_request_uuid}/status"
+}
+
+Response 409 Conflict (rehydration lease already held):
+{
+  "error": "rehydration_lease_held",
+  "lease_held_since": "",
+  "lease_expires_at": "",
+  "retry_after": "PT2H"
+}
+
+Response 403 Forbidden (step-up MFA required):
+{
+  "error": "step_up_required",
+  "reason": "Entity min_auth_level requires hardware_token_mfa for rehydration"
+}
+```
+
+---
+
+
+### 5.7 Provider Update Notification Approval
+
+When a provider submits an update notification that requires consumer approval, the consumer receives a notification and the resource enters `PENDING_REVIEW` state. The consumer approves or rejects via this endpoint.
+
+```
+GET /api/v1/resources/{entity_uuid}/provider-notifications
+
+Response 200:
+{
+  "notifications": [
+    {
+      "notification_uuid": "",
+      "notification_type": "auto_scale",
+      "provider_uuid": "",
+      "submitted_at": "",
+      "status": "pending_approval",
+      "change_summary": "Provider reports memory_gb increased from 8 to 16",
+      "change_reason": "Auto-scale policy triggered at 85% memory utilization",
+      "changed_fields": {
+        "memory_gb": { "previous_value": 8, "new_value": 16 }
+      }
+    }
+  ]
+}
+
+POST /api/v1/resources/{entity_uuid}/provider-notifications/{notification_uuid}:approve
+{
+  "decision": "approve | reject",
+  "reason": ""
+}
+
+Response 202 Accepted:
+{
+  "notification_uuid": "",
+  "decision": "approve",
+  "processed_at": "",
+  "realized_state_uuid": ""
+}
+```
+
+**On approval:** A new Requested State record is created (`source_type: provider_update`, actor: consumer approver). A new Realized State snapshot is written. The entity exits PENDING_REVIEW.
+
+**On rejection:** The notification is rejected. The discrepancy between provider state and DCM Realized State becomes a drift event. The entity exits PENDING_REVIEW with an active drift record.
+
+
+
+### 5.8 Recovery Decisions
+
+When a recovery policy fires `NOTIFY_AND_WAIT`, the entity owner can query and respond to the pending decision.
+
+```
+GET /api/v1/resources/{entity_uuid}/recovery-decisions
+
+Response 200:
+{
+  "recovery_decision_uuid": "",
+  "trigger": "DISPATCH_TIMEOUT",
+  "entity_uuid": "",
+  "entity_state": "TIMEOUT_PENDING",
+  "deadline": "",
+  "deadline_action": "ESCALATE",
+  "context": {
+    "timeout_fired_at": "",
+    "cancellation_sent": true,
+    "cancellation_status": "unknown"
+  },
+  "available_actions": [
+    {
+      "action": "DRIFT_RECONCILE",
+      "description": "Let discovery determine actual state and reconcile automatically"
+    },
+    {
+      "action": "DISCARD_AND_REQUEUE",
+      "description": "Best-effort cleanup; new request cycle created immediately"
+    },
+    {
+      "action": "DISCARD_NO_REQUEUE",
+      "description": "Best-effort cleanup only; no automatic requeue"
+    }
+  ]
+}
+
+POST /api/v1/resources/{entity_uuid}/recovery-decisions/{recovery_decision_uuid}
+{
+  "action": "DISCARD_AND_REQUEUE",
+  "reason": "Provider was known degraded; clean restart preferred"
+}
+
+Response 202 Accepted:
+{
+  "recovery_decision_uuid": "",
+  "action_taken": "DISCARD_AND_REQUEUE",
+  "new_request_uuid": ""    # the new request cycle UUID
+}
+```
+
+**Note:** Recovery decisions are only available when the active recovery profile includes `NOTIFY_AND_WAIT`. With other profiles (automated-reconciliation, discard-and-requeue) the system acts automatically and no decision endpoint is exposed.
+
+
+
+
+### 5.13 Bulk Decommission
+
+Decommissions all resources matching a filter. Creates individual decommission requests for each resource. Useful for teardown of environments or project cleanup.
+
+```
+POST /api/v1/resources:bulk-decommission
+
+Request body:
+{
+  "filter": {
+    "group_uuid": "",           # all resources in a group
+    "tag": "environment:dev",         # all resources with a tag
+    "resource_type": "Compute.VirtualMachine"   # combined with other filters
+  },
+  "reason": "Dev environment teardown — project complete",
+  "dry_run": true,                    # true: return what would be decommissioned; no action taken
+  "force": false
+}
+
+Response 200 (dry_run=true):
+{
+  "dry_run": true,
+  "would_decommission": [
+    { "entity_uuid": "", "display_name": "dev-vm-01", "resource_type": "Compute.VirtualMachine" },
+    { "entity_uuid": "", "display_name": "dev-vm-02", "resource_type": "Compute.VirtualMachine" }
+  ],
+  "blocked": [
+    {
+      "entity_uuid": "",
+      "display_name": "shared-vlan-01",
+      "reason": "Active required stakes from resources outside the decommission set"
+    }
+  ]
+}
+
+Response 200 OK — returns `Operation` (dry_run=false):
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "bulk_decommission_uuid": "",
+  "decommission_requests": [
+    { "entity_uuid": "", "request_uuid": "" },
+    { "entity_uuid": "", "request_uuid": "" }
+  ],
+  "blocked_count": 1
+}
+```
+
+
+### 5.9 Resume Resource
+
+Resumes a suspended resource. The resource must be in SUSPENDED lifecycle state.
+
+```
+POST /api/v1/resources/{entity_uuid}:resume
+
+Request body:
+{
+  "reason": "Maintenance window complete"
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "entity_uuid": "",
+  "status": "RESUMING"
+}
+
+Response 409 Conflict:
+{
+  "error": "not_suspended",
+  "reason": "Resource is not in SUSPENDED state",
+  "current_state": "OPERATIONAL"
+}
+```
+
+---
+
+### 5.10 Ownership Transfer
+
+Transfers ownership of a resource entity to a different Tenant. Both Tenants must have an active cross-tenant authorization record permitting the transfer. The receiving Tenant admin must confirm the transfer.
+
+```
+POST /api/v1/resources/{entity_uuid}:transfer
+
+Request body:
+{
+  "target_tenant_uuid": "",
+  "reason": "Project moving from Dev to Production Tenant",
+  "notify_target_tenant_admin": true
+}
+
+Response 202 Accepted:
+{
+  "transfer_uuid": "",
+  "entity_uuid": "",
+  "from_tenant_uuid": "",
+  "to_tenant_uuid": "",
+  "status": "PENDING_TARGET_ACCEPTANCE",
+  "expires_at": ""    # transfer offer expires after PT72H
+}
+
+Response 403 Forbidden:
+{
+  "error": "transfer_not_authorized",
+  "reason": "No cross-tenant authorization record between source and target Tenant"
+}
+```
+
+Target Tenant admin accepts or rejects:
+
+```
+POST /api/v1/resources/transfers/{transfer_uuid}:accept
+POST /api/v1/resources/transfers/{transfer_uuid}:reject
+{
+  "reason": ""
+}
+```
+
+---
+
+### 5.11 Extend Resource TTL
+
+Extends the TTL of a resource entity that has a lifecycle time constraint declared. Extension is subject to policy — a GateKeeper may reject or cap the extension.
+
+```
+POST /api/v1/resources/{entity_uuid}:extend-ttl
+
+Request body:
+{
+  "extend_by": "P30D",           # ISO 8601 duration
+  "reason": "Project deadline extended by one month"
+}
+
+Response 200:
+{
+  "entity_uuid": "",
+  "previous_expiry": "",
+  "new_expiry": "",
+  "extension_granted": "P30D"
+}
+
+Response 422 Unprocessable:
+{
+  "error": "ttl_extension_rejected",
+  "reason": "Policy limits maximum TTL extension to P14D for this resource type",
+  "max_extension": "P14D",
+  "policy_uuid": ""
+}
+
+Response 404 Not Found:
+{
+  "error": "no_ttl_constraint",
+  "reason": "Resource has no declared lifecycle time constraint"
+}
+```
+
+---
+
+### 5.12 List Expiring Resources
+
+Returns resources approaching their TTL expiry, sorted by time remaining.
+
+```
+GET /api/v1/resources/expiring
+
+Query parameters:
+  within=    resources expiring within this duration (default: P7D)
+  resource_type=
+  page=
+  page_size=
+
+Response 200:
+{
+  "expiring_resources": [
+    {
+      "entity_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "display_name": "lab-server-01",
+      "expires_at": "",
+      "time_remaining": "P2DT4H",
+      "on_expiry_action": "decommission",
+      "extend_url": "/api/v1/resources/{entity_uuid}:extend-ttl"
+    }
+  ],
+  "total": 3
+}
+```
+
+---
+
+## 6. Drift Management
+
+### 5b.1 List Drift Records for a Resource
+
+```
+GET /api/v1/resources/{entity_uuid}/drift
+
+Query parameters:
+  status=
+  severity=
+  page=
+  page_size=
+
+Response 200:
+{
+  "drift_records": [
+    {
+      "drift_uuid": "",
+      "detected_at": "",
+      "overall_severity": "significant",
+      "unsanctioned": true,
+      "status": "open",
+      "drifted_fields": [
+        {
+          "field_path": "fields.memory_gb",
+          "realized_value": 8,
+          "discovered_value": 16,
+          "field_severity": "significant"
+        }
+      ],
+      "available_actions": ["REVERT", "ACCEPT_DRIFT", "ESCALATE"]
+    }
+  ],
+  "total": 1
+}
+```
+
+### 5b.2 Acknowledge Drift Record
+
+Marks a drift record as acknowledged. The entity remains drifted — this signals the owner has reviewed it.
+
+```
+POST /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:acknowledge
+{
+  "reason": "Reviewing with provider before deciding on action"
+}
+
+Response 200:
+{
+  "drift_uuid": "",
+  "status": "acknowledged",
+  "acknowledged_at": ""
+}
+```
+
+### 5b.3 Accept Drift (Update Definition)
+
+Accepts the discovered state as the new authoritative desired state. Creates a new Requested State and Realized State snapshot reflecting the discovered values. Resolves the drift record.
+
+```
+POST /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:accept
+{
+  "accept_all_fields": true,         # accept all drifted fields
+  "accept_fields": ["fields.memory_gb"],   # or select specific fields
+  "reason": "Auto-scale event was legitimate; accepting new memory configuration"
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "drift_uuid": "",
+  "status": "resolved",
+  "resolution_type": "updated_definition",
+  "new_realized_state_uuid": ""
+}
+```
+
+### 5b.4 Revert Drift
+
+Submits a revert request — dispatches a new request to restore the resource to its Realized State values.
+
+```
+POST /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:revert
+{
+  "reason": "Unauthorized change — reverting to declared state"
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "drift_uuid": "",
+  "revert_request_uuid": "",
+  "status": "DISPATCHED",
+  "status_url": "/api/v1/requests/{revert_request_uuid}/status"
+}
+```
+
+---
+
+## 7. Groups and Relationships
+
+### 5c.1 List Resource Groups
+
+Returns all Resource Groups in the actor's Tenant.
+
+```
+GET /api/v1/groups
+
+Query parameters:
+  group_class=
+  tag=
+  page=
+  page_size=
+
+Response 200:
+{
+  "groups": [
+    {
+      "group_uuid": "",
+      "handle": "tenant/payments/prod-vms",
+      "display_name": "Production VMs — Payments",
+      "group_class": "resource_grouping",
+      "member_count": 12,
+      "tags": ["production", "payments"]
+    }
+  ],
+  "total": 4
+}
+```
+
+### 5c.2 Describe Group
+
+```
+GET /api/v1/groups/{group_uuid}
+
+Response 200:
+{
+  "group_uuid": "",
+  "handle": "tenant/payments/prod-vms",
+  "display_name": "Production VMs — Payments",
+  "group_class": "resource_grouping",
+  "members": [
+    {
+      "entity_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "display_name": "payments-api-01",
+      "membership_expires_at": null     # null = permanent membership
+    }
+  ],
+  "tags": ["production", "payments"]
+}
+```
+
+### 5c.3 Add Resource to Group
+
+```
+POST /api/v1/groups/{group_uuid}/members
+{
+  "entity_uuid": "",
+  "expires_at": "2026-12-31T23:59:59Z"   # optional; null = permanent
+}
+
+Response 201 Created:
+{
+  "group_uuid": "",
+  "entity_uuid": "",
+  "membership_created_at": ""
+}
+```
+
+### 5c.4 Remove Resource from Group
+
+```
+DELETE /api/v1/groups/{group_uuid}/members/{entity_uuid}
+
+Response 204 No Content
+```
+
+### 5c.5 View Resource Relationships
+
+```
+GET /api/v1/resources/{entity_uuid}/relationships
+
+Query parameters:
+  relationship_type=     filter by relationship type
+  direction=   default: both
+
+Response 200:
+{
+  "relationships": [
+    {
+      "relationship_uuid": "",
+      "relationship_type": "attached_to",
+      "direction": "outbound",
+      "related_entity_uuid": "",
+      "related_entity_type": "Network.VLAN",
+      "related_entity_display_name": "VLAN-100",
+      "stake_strength": "required",
+      "nature": "operational"
+    }
+  ],
+  "total": 3
+}
+```
+
+---
+
+## 8. Requests and Approvals
+
+### 6b.1 List Requests
+
+```
+GET /api/v1/requests
+
+Query parameters:
+  status=               filter by lifecycle status (see 4.3)
+  resource_type=
+  from=
+  to=
+  page=
+  page_size=
+
+Response 200:
+{
+  "requests": [
+    {
+      "request_uuid": "",
+      "entity_uuid": "",
+      "catalog_item_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "status": "COMPLETED",
+      "submitted_at": "",
+      "completed_at": ""
+    }
+  ],
+  "total": 47
+}
+```
+
+### 6b.2 List Pending Approvals (as Approver)
+
+Returns requests awaiting approval where the authenticated actor is an eligible approver (by role or group membership).
+
+```
+GET /api/v1/approvals/pending
+
+Response 200:
+{
+  "pending_approvals": [
+    {
+      "approval_uuid": "",
+      "request_uuid": "",
+      "entity_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "requester": { "uuid": "", "display_name": "Bob Smith" },
+      "estimated_cost_per_month": 230.40,
+      "submitted_at": "",
+      "deadline": "",
+      "risk_score": 47,
+      "risk_score_explanation": {
+        "score_drivers": [
+          "Estimated monthly cost exceeds Tenant ceiling (+35)",
+          "Request submitted outside business hours (+15)",
+          "Actor has 2 recent validation failures (+15)"
+        ],
+        "routing_threshold": 25,
+        "profile": "standard"
+      },
+      "advisory_warnings": 1,
+      "policy_name": "scoring-threshold: standard/reviewed"
+    }
+  ],
+  "total": 2
+}
+```
+
+### 6b.3 Approve or Reject a Request
+
+This endpoint is used by reviewers with the appropriate role. It is designed to be callable by external systems (ServiceNow, Jira workflow integrations, Slack bots) that act on behalf of a reviewer — the `Authorization` header identifies which reviewer is recording the decision. DCM provides the gate and audit trail; the review process is the organization's responsibility. See [Design Priorities — Approval Tier Model](../data-model/00-design-priorities.md).
+
+```
+POST /api/v1/approvals/{approval_uuid}
+{
+  "decision": "approve | reject",
+  "reason": "",
+  "recorded_via": "dcm_ui | servicenow | jira | slack_bot | api_direct | other",
+  "external_reference": ""
+}
+
+Response 202 Accepted:
+{
+  "approval_uuid": "",
+  "decision": "approve",
+  "processed_at": "",
+  "request_uuid": "",
+  "request_status": "ASSEMBLING"   # pipeline resumes on approve
+}
+```
+
+---
+
+## 9. Cost and Quota
+
+### 7b.1 Get Cost Estimate (Pre-Submission)
+
+Returns a cost estimate for a hypothetical request without submitting it.
+
+```
+POST /api/v1/cost/estimate
+{
+  "catalog_item_uuid": "",
+  "fields": {
+    "cpu_count": 4,
+    "memory_gb": 8
+  }
+}
+
+Response 200:
+{
+  "estimated_cost": {
+    "breakdown": [
+      { "component": "compute", "unit": "per-hour", "amount": 0.28, "currency": "USD" },
+      { "component": "ip-allocation", "unit": "per-hour", "amount": 0.04, "currency": "USD" }
+    ],
+    "total_per_hour": 0.32,
+    "total_per_month": 230.40,
+    "currency": "USD"
+  },
+  "cost_confidence": "high"
+}
+```
+
+### 7b.2 Get Cost Actuals for a Resource
+
+```
+GET /api/v1/resources/{entity_uuid}/cost
+
+Query parameters:
+  from=    start of billing period (default: start of current month)
+  to=      end of billing period (default: now)
+
+Response 200:
+{
+  "entity_uuid": "",
+  "billing_state": "billable",
+  "period": {
+    "from": "2026-03-01T00:00:00Z",
+    "to": "2026-03-28T15:00:00Z"
+  },
+  "actuals": {
+    "total": 168.96,
+    "currency": "USD",
+    "breakdown": [
+      { "component": "compute", "hours": 651, "amount": 182.28 },
+      { "component": "ip-allocation", "hours": 651, "amount": 26.04 }
+    ]
+  },
+  "current_rate_per_hour": 0.32
+}
+```
+
+### 7b.3 View Quota Usage
+
+Returns current quota consumption for the authenticated Tenant.
+
+```
+GET /api/v1/quota
+
+Response 200:
+{
+  "tenant_uuid": "",
+  "quotas": [
+    {
+      "resource_type": "Compute.VirtualMachine",
+      "limit": 100,
+      "current_usage": 47,
+      "percent_used": 47,
+      "reserved": 3           # in-flight requests consuming quota
+    },
+    {
+      "resource_type": "Network.IPAddress",
+      "limit": 500,
+      "current_usage": 189,
+      "percent_used": 37.8,
+      "reserved": 0
+    }
+  ]
+}
+```
+
+---
+
+## 10. Notifications and Webhooks
+
+### 7c.1 List Notifications
+
+Returns notifications delivered to the authenticated actor, most recent first.
+
+```
+GET /api/v1/notifications
+
+Query parameters:
+  status=   default: unread
+  urgency=
+  event_type=
+  page=
+  page_size=
+
+Response 200:
+{
+  "notifications": [
+    {
+      "notification_uuid": "",
+      "event_type": "entity.decommissioning",
+      "urgency": "high",
+      "status": "unread",
+      "delivered_at": "",
+      "entity_uuid": "",
+      "entity_display_name": "VLAN-100",
+      "audience_role": "stakeholder",
+      "summary": "VLAN-100 is being decommissioned. Your resource VM-A is attached.",
+      "action_url": "/api/v1/resources/"
+    }
+  ],
+  "total_unread": 3,
+  "total": 47
+}
+```
+
+### 7c.2 Mark Notification Read
+
+```
+POST /api/v1/notifications/{notification_uuid}/read
+
+Response 200:
+{
+  "notification_uuid": "",
+  "status": "read",
+  "read_at": ""
+}
+
+POST /api/v1/notifications:read-all    # mark all unread as read
+
+Response 200:
+{
+  "marked_read": 3
+}
+```
+
+### 7c.3 Manage Webhook Subscriptions
+
+```
+GET /api/v1/webhooks
+
+Response 200:
+{
+  "subscriptions": [
+    {
+      "webhook_uuid": "",
+      "endpoint_url": "https://my-system.example.com/dcm/events",
+      "events": ["entity.provisioned", "entity.decommissioned", "drift.detected"],
+      "status": "active",
+      "created_at": ""
+    }
+  ]
+}
+
+POST /api/v1/webhooks
+{
+  "endpoint_url": "https://my-system.example.com/dcm/events",
+  "events": ["entity.provisioned", "entity.decommissioned"],
+  "hmac_secret": "",   # used for payload signing
+  "description": "Production event sink"
+}
+
+Response 201 Created:
+{
+  "webhook_uuid": "",
+  "status": "active",
+  "test_event_sent": true
+}
+
+DELETE /api/v1/webhooks/{webhook_uuid}
+Response 204 No Content
+```
+
+---
+
+## 11. Search
+
+### 8b.1 Cross-Resource Search
+
+Full-text and structured search across all resources in the actor's Tenant. Served from the Search Index — non-authoritative but fast.
+
+```
+GET /api/v1/search
+
+Query parameters:
+  q=                  full-text query
+  resource_type=
+  lifecycle_state=
+  drift_status=
+  tag=                repeatable
+  compliance_domain=
+  data_classification= filter by highest data classification
+  page=
+  page_size=
+
+Response 200:
+{
+  "results": [
+    {
+      "entity_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "display_name": "payments-api-server-01",
+      "lifecycle_state": "OPERATIONAL",
+      "drift_status": "clean",
+      "tags": ["production", "payments"],
+      "resource_url": "/api/v1/resources/{entity_uuid}",
+      "score": 0.98           # relevance score for text queries
+    }
+  ],
+  "total": 3,
+  "search_index_staleness_seconds": 12,
+  "authoritative_store_ref": "/api/v1/resources?..."   # fallback URL if stale
+}
+```
+
+
+## 12. Audit Trail
+
+### 6.1 Query Audit Records for a Resource
+
+```
+GET /api/v1/resources/{entity_uuid}/audit
+
+Query parameters:
+  from=         start of time range
+  to=           end of time range
+  action=         filter by action type
+  actor_type=
+  page=
+  page_size=
+
+Response 200:
+{
+  "audit_records": [
+    {
+      "record_uuid": "",
+      "timestamp": "",
+      "action": "PROVISION",
+      "actor": {
+        "uuid": "",
+        "type": "human",
+        "display_name": "Jane Smith"
+      },
+      "summary": "VirtualMachine provisioned via EU-WEST-Prod-1",
+      "correlation_id": ""
+    }
+  ],
+  "total": 47,
+  "chain_integrity": "verified"      # verified | unverifiable | compromised
+}
+```
+
+### 6.2 Follow Correlation ID
+
+For cross-state correlation — following a request from Intent through all states:
+
+```
+GET /api/v1/audit/correlation/{correlation_id}
+
+Response 200:
+{
+  "correlation_id": "",
+  "entity_uuid": "",
+  "timeline": [
+    { "state": "intent",     "record_uuid": "", "timestamp": "..." },
+    { "state": "requested",  "record_uuid": "", "timestamp": "..." },
+    { "state": "realized",   "record_uuid": "", "timestamp": "..." }
+  ],
+  "cross_dcm_refs": []               # cross-DCM records if federation involved
+}
+```
+
+---
+
+## 13. Error Model
+
+All error responses follow a consistent structure:
+
+```json
+{
+  "error": "",
+  "message": "",
+  "request_id": "",
+  "timestamp": "",
+  "details": {}                      # error-specific additional context
+}
+```
+
+**Standard error codes:**
+
+| HTTP Status | Error Code | Meaning |
+|-------------|-----------|---------|
+| 400 | `invalid_request` | Malformed request or missing required fields |
+| 400 | `tenant_ambiguous` | Actor has multiple Tenants; X-DCM-Tenant header required |
+| 401 | `authentication_required` | No token or expired token |
+| 403 | `authorization_denied` | Token valid but insufficient permissions |
+| 403 | `step_up_required` | Operation requires step-up MFA challenge |
+| 404 | `not_found` | Entity, catalog item, or request not found |
+| 409 | `decommission_deferred` | Decommission blocked by active stakes or dependencies |
+| 409 | `rehydration_lease_held` | Entity already being rehydrated |
+| 409 | `field_not_editable` | Targeted delta attempted on non-editable field |
+| 409 | `not_suspended` | Resume attempted on a non-suspended resource |
+| 409 | `transfer_not_authorized` | No cross-tenant authorization between source and target Tenant |
+| 409 | `no_ttl_constraint` | TTL extension attempted on resource with no time constraint |
+| 422 | `policy_rejected` | GateKeeper policy rejected the request |
+| 422 | `constraint_violated` | Field value violates declared constraint |
+| 422 | `ttl_extension_rejected` | Policy rejected or capped the TTL extension request |
+| 429 | `rate_limit_exceeded` | Actor has exceeded request rate page_size |
+| 503 | `assembly_unavailable` | Request Payload Processor temporarily unavailable |
+| 503 | `search_index_degraded` | Search index unavailable; use authoritative_store_ref fallback |
+
+---
+
+
+---
+
+## 14. Consumer Contributions
+
+Consumers with `policy_author` or `tenant_admin` role can contribute tenant-scoped artifacts directly via the Consumer API. All contributions flow through the GitOps PR model — DCM generates a PR and activates the artifact after the required review period. See [Federated Contribution Model](../data-model/28-federated-contribution-model.md) for the complete contributor permission table.
+
+### 9.1 Submit Policy Contribution
+
+```
+POST /api/v1/contribute/policy
+X-DCM-Tenant: 
+
+{
+  "policy_type": "gatekeeper | transformation | recovery | lifecycle | orchestration_flow | governance_matrix_rule",
+  "handle": "tenant/{tenant-handle}/gatekeeper/{name}",
+  "domain": "tenant",
+  "concern_type": "operational | security | compliance",
+  "enforcement": "soft | hard",
+  "match": { ... },
+  "output": { ... },
+  "shadow_mode": true,
+  "commit_message": ""
+}
+
+Response 202 Accepted:
+{
+  "contribution_uuid": "",
+  "policy_handle": "tenant/payments/gatekeeper/cost-ceiling",
+  "status": "proposed",
+  "shadow_mode": true,
+  "review_required": true,
+  "review_type": "reviewed",
+  "pr_url": "https://git.corp.example.com/dcm-policies/pulls/145",
+  "shadow_results_url": "/flow/api/v1/shadow/"
+}
+```
+
+### 9.2 Submit Resource Group Definition
+
+```
+POST /api/v1/contribute/resource-group
+X-DCM-Tenant: 
+
+{
+  "handle": "tenant/{tenant-handle}/groups/{name}",
+  "display_name": "",
+  "group_class": "resource_grouping",
+  "description": "",
+  "membership_policy": {
+    "auto_include": {
+      "resource_type": "Compute.VirtualMachine",
+      "tags": { "team": "payments", "env": "production" }
+    }
+  }
+}
+
+Response 201 Created:
+{
+  "group_uuid": "",
+  "handle": "tenant/payments/groups/prod-vms",
+  "status": "active"             # resource groups activate immediately (no policy review)
+}
+```
+
+### 9.3 List Contributions
+
+```
+GET /api/v1/contribute
+X-DCM-Tenant: 
+
+Query parameters:
+  artifact_type=
+  status=
+
+Response 200:
+{
+  "contributions": [
+    {
+      "contribution_uuid": "",
+      "artifact_type": "policy",
+      "handle": "tenant/payments/gatekeeper/cost-ceiling",
+      "status": "proposed",
+      "shadow_mode": true,
+      "pr_url": "https://...",
+      "submitted_at": "",
+      "review_status": "pending"
+    }
+  ]
+}
+```
+
+### 9.4 Withdraw Contribution
+
+```
+DELETE /api/v1/contribute/{contribution_uuid}
+
+Response 200:
+{
+  "contribution_uuid": "",
+  "status": "withdrawn",
+  "pr_closed": true
+}
+```
+
+
+
+---
+
+## 15. Credential Management
+
+### 9b.1 List Credentials for a Resource
+
+```
+GET /api/v1/resources/{entity_uuid}/credentials
+
+Response 200:
+{
+  "credentials": [
+    {
+      "credential_uuid": "",
+      "credential_type": "ssh_key",
+      "status": "active",
+      "issued_at": "",
+      "expires_at": "",
+      "scope": { "operations": ["ssh_access"] },
+      "retrieval": {
+        "endpoint": "/api/v1/credentials//value",
+        "auth_required": "step_up_mfa",
+        "retrieval_count": 1,
+        "last_retrieved_at": ""
+      },
+      "rotation_schedule": {
+        "next_rotation_at": "",
+        "rotation_trigger": "scheduled"
+      }
+    }
+  ]
+}
+```
+
+### 9b.2 Retrieve Credential Value
+
+```
+GET /api/v1/credentials/{credential_uuid}/value
+X-DCM-StepUp-Token:   # if auth_required: step_up_mfa
+
+Response 200:
+{
+  "credential_uuid": "",
+  "credential_type": "ssh_key",
+  "value": { "private_key": "...", "public_key": "...", "username": "dcm-provisioned" },
+  "expires_at": "",
+  "retrieval_uuid": ""
+}
+
+Response 410 Gone:  { "error": "credential_revoked_or_expired" }
+```
+
+### 9b.3 Request Credential Rotation
+
+```
+POST /api/v1/credentials/{credential_uuid}:rotate
+{
+  "reason": "Scheduled rotation per security policy"
+}
+
+Response 200 OK — returns `Operation`:
+> Returns `Operation` resource. Poll `operation.name` for completion.
+{
+  "old_credential_uuid": "",
+  "new_credential_uuid": "",
+  "transition_window_ends": "",
+  "new_retrieval_url": "/api/v1/credentials//value"
+}
+```
+
+
+## 16. Conformance Levels
+
+The Consumer API defines three conformance levels, mirroring the Operator Interface Specification model:
+
+**Level 1 — Read-Only:** Catalog browsing, resource listing, status queries, search, cost estimates, quota views, and notification listing. No request submission or resource management. Suitable for reporting, dashboards, and read-only portal integrations.
+
+**Level 2 — Standard:** All Level 1 operations plus request submission, status tracking, approvals, basic resource management (update editable fields, suspend/resume, decommission, bulk decommission, TTL extension, group management), and consumer contribution endpoints (policy authoring, resource group definitions). Required for all self-service portal implementations.
+
+**Level 3 — Full:** All Level 2 operations plus rehydration, ownership transfer, drift management (acknowledge, accept, revert), audit trail access, correlation queries, webhook subscription management, and cost actuals. Required for ITSM integrations, compliance tooling, and full GitOps automation.
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+
+## Operations — Polling Long-Running Requests
+
+All async mutating operations return an `Operation` resource. The `operation.name` field
+is the stable polling URL: `GET /api/v1/operations/{operation_uuid}`.
+
+**Key relationship:** `operation_uuid == request_uuid`. The same UUID is used in both the
+AEP-standard Operation endpoint and the DCM-native Request Status endpoint. Two polling
+views are available — use whichever fits your client:
+
+| Endpoint | Schema | Best for |
+|----------|--------|----------|
+| `GET /api/v1/operations/{uuid}` | `Operation` — `done`, `metadata`, `response/error` | AEP-compatible clients, simple polling |
+| `GET /api/v1/requests/{uuid}/status` | `RequestStatus` — `pipeline_stage`, full status history, `entity_uuid` | DCM-native clients, debugging, rich UI |
+
+Both endpoints reflect the same underlying state. When `done: true`, `operation.response`
+contains the realized entity (same as the resource returned by `GET /api/v1/resources/{entity_uuid}`).
+
+```
+POST /api/v1/requests
+
+Response 200 OK — returns Operation:
+{
+  "name": "/api/v1/operations/{request_uuid}",
+  "done": false,
+  "metadata": {
+    "stage": "INITIATED",
+    "resource_uuid": "{entity_uuid}",   // set immediately on entity creation
+    "request_uuid": "{request_uuid}"    // == operation_uuid
+  }
+}
+```
+
+**Polling `GET /api/v1/operations/{operation_uuid}`:**
+
+```
+# While in progress:
+{
+  "name": "/api/v1/operations/{uuid}",
+  "done": false,
+  "metadata": {
+    "stage": "PROVISIONING",
+    "progress_pct": 45,
+    "resource_uuid": "{entity_uuid}",
+    "request_uuid": "{uuid}"
+  }
+}
+
+# On success:
+{
+  "name": "/api/v1/operations/{uuid}",
+  "done": true,
+  "metadata": { "stage": "OPERATIONAL", "resource_uuid": "{entity_uuid}", "request_uuid": "{uuid}" },
+  "response": { ... }   // the realized entity
+}
+
+# On failure:
+{
+  "name": "/api/v1/operations/{uuid}",
+  "done": true,
+  "metadata": { "stage": "FAILED", "request_uuid": "{uuid}" },
+  "error": {
+    "code": "PROVIDER_TIMEOUT",
+    "message": "Provider did not respond within the configured timeout",
+    "details": []
+  }
+}
+```
+
+**Polling guidance:** Use exponential backoff (1s → 2s → 5s → 10s → 30s).
+For push-based updates, subscribe to the `request.progress_updated` webhook event.
+For real-time browser monitoring, use the SSE stream: `GET /api/v1/requests/{uuid}/stream`.
+
+**Cancellation:** `DELETE /api/v1/requests/{uuid}` cancels an in-progress operation.
+The request enters CANCELLING state; cancellation success depends on provider support.
+
diff --git a/content/docs/architecture/specifications/dcm-admin-api-spec.md b/content/docs/architecture/specifications/dcm-admin-api-spec.md
new file mode 100644
index 0000000..beaf459
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-admin-api-spec.md
@@ -0,0 +1,984 @@
+# DCM Admin API Specification
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** API Narrative Specification
+
+
+> **📋 Draft**
+>
+> This specification has been promoted from Work in Progress to Draft status. Complete Admin API covering all platform admin operations with request/response examples. It is ready for implementation feedback but has not yet been formally reviewed for final release.
+>
+> This specification defines the DCM Admin API — the platform administration interface. Published to share design direction and invite feedback. Do not build production integrations against this specification until it reaches draft status.
+
+**Version:** 0.1.0-draft
+**Status:** Draft — Ready for implementation feedback
+**Document Type:** Technical Specification
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [Consumer API Specification](consumer-api-spec.md) | [DCM Operator Interface Specification](dcm-operator-interface-spec.md) | [Control Plane Components](../data-model/25-control-plane-components.md) | [Accreditation and Authorization Matrix](../data-model/26-accreditation-and-authorization-matrix.md)
+
+---
+
+## Abstract
+
+The Admin API is the platform administration interface for DCM. It is served through the same Ingress API as the Consumer API and Provider API but is restricted to actors with `platform_admin` or `tenant_admin` roles. It covers operations that consumers cannot perform — Tenant lifecycle management, provider registration review, accreditation approval, quota administration, discovery management, orphan resolution, recovery decision escalation, and bootstrap operations.
+
+---
+
+
+> **AEP Alignment:** This specification follows [AEP](https://aep.dev) conventions.
+> Custom methods use colon syntax (`POST /admin/providers/{uuid}:approve`).
+> Async operations return an `Operation` resource (AEP-136 LRO).
+> List pagination uses `page_size` and `page_token` parameters.
+> See the normative OpenAPI specification: `schemas/openapi/dcm-admin-api.yaml`
+
+## 1. Authentication and Authorization
+
+All Admin API endpoints require Bearer token authentication (same as Consumer API). Role requirements are declared per endpoint:
+
+| Role | Scope |
+|------|-------|
+| `platform_admin` | All Admin API operations across all Tenants |
+| `tenant_admin` | Tenant-scoped Admin API operations for their own Tenant only |
+
+Base URL: `/api/v1/admin/`
+
+> **Versioning:** See [API Versioning Strategy](../data-model/34-api-versioning-strategy.md). Breaking changes increment the major version. The Admin API follows the same deprecation lifecycle as the Consumer API, with profile-governed support windows.
+
+Step-up MFA is required for destructive operations (Tenant decommission, accreditation revocation, bootstrap credential rotation) regardless of session MFA status.
+
+---
+
+### 1.1 Rate Limiting
+
+Admin API endpoints have separate rate limits from the Consumer API, applied per authenticated admin actor:
+
+| Profile | Requests/minute | Burst |
+|---------|----------------|-------|
+| All profiles | 120 | 40 |
+
+Rate-limited responses include `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining` headers.
+
+### 1.2 Request and Correlation IDs
+
+All responses include `X-DCM-Request-ID` and `X-DCM-Correlation-ID` headers (same model as Consumer API).
+
+### 1.3 Response Envelopes
+
+List responses use `{"items": [...], "total": N, "next_cursor": "..."}`. Single resources returned directly. Errors use `{"error": "...", "message": "...", "request_id": "..."}`.
+
+---
+
+## 2. Tenant Management
+
+### 2.1 List Tenants
+
+```
+GET /api/v1/admin/tenants
+Role: platform_admin
+
+Query params: status=, page, page_size
+
+Response 200:
+{
+  "tenants": [
+    {
+      "tenant_uuid": "",
+      "handle": "payments-team",
+      "display_name": "Payments Platform",
+      "status": "active",
+      "deployment_posture": "prod",
+      "compliance_domains": ["hipaa"],
+      "recovery_profile": "notify-and-wait",
+      "entity_count": 142,
+      "created_at": ""
+    }
+  ],
+  "total": 12
+}
+```
+
+### 2.2 Create Tenant
+
+```
+POST /api/v1/admin/tenants
+Role: platform_admin
+
+{
+  "handle": "new-team",
+  "display_name": "New Team",
+  "deployment_posture": "standard",
+  "compliance_domains": [],
+  "recovery_profile_override": null,
+  "initial_admin_actor_uuid": ""
+}
+
+Response 201 Created:
+{
+  "tenant_uuid": "",
+  "status": "active"
+}
+```
+
+### 2.3 Suspend / Reinstate Tenant
+
+```
+POST /api/v1/admin/tenants/{tenant_uuid}:suspend
+POST /api/v1/admin/tenants/{tenant_uuid}:reinstate
+Role: platform_admin
+
+{
+  "reason": "",
+  "notify_tenant_admin": true
+}
+```
+
+### 2.4 Decommission Tenant
+
+```
+DELETE /api/v1/admin/tenants/{tenant_uuid}
+Role: platform_admin
+Requires: step-up MFA
+
+{
+  "reason": "",
+  "force": false,          # true: decommission even if active entities remain
+  "notify_tenant_admin": true
+}
+
+Response 409 Conflict (if active entities and force=false):
+{
+  "error": "tenant_has_active_entities",
+  "active_entity_count": 47,
+  "resolution": "Decommission all entities first, or use force=true"
+}
+```
+
+---
+
+## 3. Provider Management
+
+### 3.1 List Registered Providers
+
+```
+GET /api/v1/admin/providers
+Role: platform_admin
+
+Query params: type=, status=
+
+Response 200:
+{
+  "providers": [
+    {
+      "provider_uuid": "",
+      "handle": "eu-west-prod-1",
+      "provider_type": "service",
+      "status": "active",
+      "health": "healthy",
+      "accreditation_count": 2,
+      "max_data_classification": "phi"
+    }
+  ]
+}
+```
+
+### 3.2 Review Provider Registration
+
+New provider registrations in `proposed` status require platform admin review:
+
+```
+GET /api/v1/admin/providers/pending
+Role: platform_admin
+
+POST /api/v1/admin/providers/{provider_uuid}:approve
+POST /api/v1/admin/providers/{provider_uuid}:reject
+{
+  "reason": ""
+}
+```
+
+### 3.3 Suspend Provider
+
+```
+POST /api/v1/admin/providers/{provider_uuid}:suspend
+Role: platform_admin
+
+{
+  "reason": "",
+  "affect_existing_entities": "notify_only | block_new_requests | migrate"
+}
+```
+
+---
+
+## 4. Accreditation Management
+
+### 4.1 List Accreditations
+
+```
+GET /api/v1/admin/accreditations
+Role: platform_admin
+
+Query params: subject_type, framework, status=
+
+Response 200:
+{
+  "accreditations": [
+    {
+      "accreditation_uuid": "",
+      "subject_uuid": "",
+      "subject_type": "service_provider",
+      "framework": "hipaa",
+      "accreditation_type": "baa",
+      "status": "active",
+      "expires_at": "",
+      "days_until_expiry": 89
+    }
+  ]
+}
+```
+
+### 4.2 Approve Accreditation
+
+```
+POST /api/v1/admin/accreditations/{accreditation_uuid}:approve
+Role: platform_admin
+Requires: step-up MFA
+
+{
+  "review_notes": "",
+  "certificate_verified": true
+}
+```
+
+### 4.3 Revoke Accreditation
+
+```
+DELETE /api/v1/admin/accreditations/{accreditation_uuid}
+Role: platform_admin
+Requires: step-up MFA
+
+{
+  "revocation_reason": "",
+  "affected_entity_action": "notify_only | block_new_requests | migrate_entities"
+}
+```
+
+---
+
+## 5. Discovery Management
+
+### 5.1 Trigger Discovery
+
+```
+POST /api/v1/admin/discovery:trigger
+Role: platform_admin | tenant_admin
+
+{
+  "scope": "entity | resource_type | provider | tenant",
+  "entity_uuid": "",
+  "resource_type": "Compute.VirtualMachine",
+  "provider_uuid": "",
+  "tenant_uuid": "",
+  "reason": "incident investigation",
+  "priority": "high | standard | background"
+}
+
+Response 202 Accepted:
+{
+  "discovery_job_uuid": "",
+  "status": "queued",
+  "priority": "high",
+  "estimated_start": ""
+}
+```
+
+### 5.2 Discovery Job Status
+
+```
+GET /api/v1/admin/discovery/jobs/{discovery_job_uuid}
+
+Response 200:
+{
+  "discovery_job_uuid": "",
+  "status": "running | completed | failed",
+  "entities_discovered": 47,
+  "new_entities_found": 2,
+  "started_at": "",
+  "completed_at": "",
+  "orphan_candidates_found": 1
+}
+```
+
+---
+
+## 6. Orphan Management
+
+### 6.1 List Orphan Candidates
+
+```
+GET /api/v1/admin/orphans
+Role: platform_admin
+
+Query params: provider_uuid, status=
+
+Response 200:
+{
+  "orphan_candidates": [
+    {
+      "orphan_candidate_uuid": "",
+      "provider_uuid": "",
+      "provider_entity_id": "vm-0a1b2c3d",
+      "suspected_request_uuid": "",
+      "resource_type": "Compute.VirtualMachine",
+      "discovered_at": "",
+      "status": "under_review"
+    }
+  ]
+}
+```
+
+### 6.2 Resolve Orphan Candidate
+
+```
+POST /api/v1/admin/orphans/{orphan_candidate_uuid}/resolve
+Role: platform_admin
+
+{
+  "resolution": "manual_decommission | adopt_into_dcm | mark_false_positive",
+  "reason": "",
+  "target_tenant_uuid": ""    # required if resolution=adopt_into_dcm
+}
+```
+
+---
+
+## 7. Recovery Decision Management
+
+Platform admins can resolve pending recovery decisions for any entity:
+
+```
+GET /api/v1/admin/recovery-decisions/pending
+Role: platform_admin
+
+Response 200:
+{
+  "pending_decisions": [
+    {
+      "recovery_decision_uuid": "",
+      "entity_uuid": "",
+      "trigger": "DISPATCH_TIMEOUT",
+      "entity_state": "TIMEOUT_PENDING",
+      "deadline": "",
+      "tenant_uuid": ""
+    }
+  ]
+}
+
+POST /api/v1/admin/recovery-decisions/{recovery_decision_uuid}
+Role: platform_admin
+
+{
+  "action": "DRIFT_RECONCILE | DISCARD_AND_REQUEUE | DISCARD_NO_REQUEUE",
+  "reason": ""
+}
+```
+
+---
+
+## 8. Quota Management
+
+### 8.1 View Tenant Quotas
+
+```
+GET /api/v1/admin/tenants/{tenant_uuid}/quotas
+Role: platform_admin | tenant_admin
+
+Response 200:
+{
+  "quotas": [
+    {
+      "resource_type": "Compute.VirtualMachine",
+      "limit": 100,
+      "current_usage": 47,
+      "policy_uuid": ""
+    }
+  ]
+}
+```
+
+### 8.2 Update Quota
+
+```
+PUT /api/v1/admin/tenants/{tenant_uuid}/quotas/{resource_type}
+Role: platform_admin
+
+{
+  "new_limit": 150,
+  "reason": "Q2 capacity increase approved by FinOps"
+}
+```
+
+---
+
+## 9. Search Index Management
+
+```
+POST /api/v1/admin/search-index:rebuild
+Role: platform_admin
+
+{
+  "scope": "full | tenant | resource_type",
+  "tenant_uuid": "",
+  "reason": "Recovery after index corruption"
+}
+
+Response 202 Accepted:
+{
+  "rebuild_job_uuid": "",
+  "estimated_duration": "PT2H",
+  "degraded_during_rebuild": true
+}
+
+GET /api/v1/admin/search-index/status
+
+Response 200:
+{
+  "status": "healthy | degraded | rebuilding | unavailable",
+  "staleness_seconds": 42,
+  "last_full_rebuild": "",
+  "entity_count": 8421
+}
+```
+
+---
+
+## 10. Bootstrap Operations
+
+### 10.1 Rotate Bootstrap Admin Credential
+
+```
+POST /api/v1/admin/bootstrap:rotate-credential
+Role: platform_admin
+Requires: step-up MFA (hardware_token_mfa for fsi/sovereign)
+
+{
+  "new_credential_ref": "",
+  "reason": "Initial bootstrap credential rotation"
+}
+```
+
+### 10.2 Deployment Health
+
+```
+GET /api/v1/admin/health
+
+Response 200:
+{
+  "overall": "healthy | degraded | critical",
+  "components": [
+    { "component": "request_orchestrator", "status": "healthy" },
+    { "component": "policy_engine", "status": "healthy" },
+    { "component": "placement_engine", "status": "healthy" },
+    { "component": "lifecycle_constraint_enforcer", "status": "healthy" },
+    { "component": "discovery_scheduler", "status": "healthy" },
+    { "component": "notification_router", "status": "healthy" },
+    { "component": "cost_analysis", "status": "healthy" },
+    { "component": "search_index", "status": "degraded", "staleness_seconds": 180 },
+    { "component": "intent_store", "status": "healthy" },
+    { "component": "requested_store", "status": "healthy" },
+    { "component": "realized_store", "status": "healthy" }
+  ],
+  "active_profile": {
+    "deployment_posture": "prod",
+    "compliance_domains": ["hipaa"],
+    "recovery_posture": "notify-and-wait",
+    "zero_trust_posture": "full"
+  }
+}
+```
+
+---
+
+## 13. DCM Self-Health Endpoints
+
+DCM exposes three health endpoints, each with a distinct purpose:
+
+```http
+# Liveness — is the process alive? (Kubernetes liveness probe)
+GET /livez
+# No auth required. Max response time: PT5S.
+# Returns 200 OK with {"status":"ok"} if alive.
+# Returns 503 if process is deadlocked or unresponsive.
+
+# Readiness — is DCM ready to serve traffic? (Kubernetes readiness probe)
+GET /readyz
+# No auth required. Max response time: PT10S.
+# Returns 200 OK with {"status":"ready"} if all required stores are reachable.
+# Returns 503 with {"status":"not_ready","reasons":["store_unreachable"]} otherwise.
+
+# Operational health — rich health for operators and monitoring systems
+GET /api/v1/admin/health
+Authorization: Bearer 
+
+Response 200:
+{
+  "dcm_version": "",
+  "profile": "prod",
+  "status": "healthy",           // healthy | degraded | critical
+  "components": {
+    "request_orchestrator": { "status": "healthy" },
+    "policy_engine":        { "status": "healthy" },
+    "placement_engine":     { "status": "healthy" },
+    "service_provider":  { "status": "degraded", "reason": "rotation_pending" }
+  },
+  "stores": {
+    "intent_store":    { "status": "healthy", "latency_p99_ms": 12 },
+    "requested_store": { "status": "healthy", "latency_p99_ms": 8 },
+    "realized_store":  { "status": "healthy", "latency_p99_ms": 9 }
+  },
+  "providers": {
+    "total": 4,
+    "healthy": 3,
+    "degraded": 1,
+    "unhealthy": 0
+  }
+}
+
+# Prometheus metrics
+GET /metrics
+# Unauthenticated (secured by network policy in production).
+# Returns Prometheus text format metrics.
+```
+
+> **Full model:** See [DCM Self-Health](../data-model/39-dcm-self-health.md) — HLT-001–HLT-006.
+
+
+## 12. Session Management (Admin)
+
+Platform admins can force-revoke sessions for any actor — used on actor compromise, policy violation, or deprovisioning.
+
+```http
+# Force-revoke all sessions for an actor
+POST /api/v1/admin/actors/{actor_uuid}:revoke-sessions
+Authorization: Bearer 
+
+{
+  "reason": "security_event",        // REQUIRED
+  "notify_actor": true               // send notification event
+}
+
+Response 202 Accepted:
+{
+  "sessions_revoked": 3,
+  "actor_uuid": "",
+  "revocation_propagated_at": ""
+}
+```
+
+```http
+# List active sessions for any actor (admin view)
+GET /api/v1/admin/actors/{actor_uuid}/sessions
+Authorization: Bearer 
+
+Response 200:
+{
+  "items": [
+    {
+      "session_uuid": "",
+      "created_at": "",
+      "expires_at": "",
+      "auth_method": "ldap",
+      "mfa_verified": true,
+      "status": "active"
+    }
+  ],
+  "total": 1
+}
+```
+
+**Error codes specific to session management:**
+
+| Error Code | HTTP | When |
+|-----------|------|------|
+| `actor_not_found` | 404 | Actor UUID not found |
+| `no_active_sessions` | 404 | Actor has no active sessions |
+
+> **Full model:** See [Session Token Revocation](../data-model/35-session-revocation.md) — AUTH-016–AUTH-022.
+
+
+## 11. Error Model
+
+All Admin API errors use the same envelope as the Consumer API:
+
+```json
+{
+  "error": "",    // machine-readable snake_case code
+  "message": "",      // human-readable description
+  "request_id": "",     // matches X-DCM-Request-ID header
+  "details": {}               // optional: field-level details
+}
+```
+
+**Admin-specific error codes:**
+
+| Error Code | HTTP Status | When |
+|-----------|-------------|------|
+| `insufficient_admin_role` | 403 | Actor lacks required admin role |
+| `tenant_not_found` | 404 | Tenant UUID not found |
+| `provider_not_found` | 404 | Provider UUID not found |
+| `approval_already_voted` | 409 | Actor has already voted on this approval |
+| `approval_window_expired` | 410 | Approval window has passed |
+| `degradation_already_accepted` | 409 | Degradation item already accepted |
+| `tier_registry_blocked` | 409 | Registry change has unresolved blocking items |
+| `quota_below_current_usage` | 422 | New quota would be below current consumption |
+
+All error responses include `X-DCM-Request-ID` and `X-DCM-Correlation-ID` headers.
+
+
+## Scoring Model Administration
+
+> Approval routing thresholds use named-tier dynamic format. See [Authority Tier Model](../data-model/32-authority-tier-model.md) for the complete specification.
+
+### Get Scoring Thresholds for Profile
+
+```
+GET /api/v1/admin/profiles/{profile_name}/scoring
+
+Response 200:
+{
+  "profile": "standard",
+  "scoring_thresholds": {
+    "auto_approve_below": 25,
+    "approval_routing": [
+      { "tier": "reviewed", "max_score": 59 },
+      { "tier": "verified", "max_score": 79 },
+      { "tier": "authorized", "max_score": 100 }
+    ]
+  },
+  "signal_weights": {
+    "operational_gatekeeper": 0.45,
+    "completeness": 0.15,
+    "actor_risk_history": 0.20,
+    "quota_pressure": 0.10,
+    "provider_risk": 0.10
+  },
+  "policy_enforcement_overrides": []
+}
+```
+
+### Update Scoring Thresholds
+
+```
+PATCH /api/v1/admin/profiles/{profile_name}/scoring
+{
+  "scoring_thresholds": {
+    "auto_approve_below": 20,
+    "approval_routing": [
+      { "tier": "reviewed", "max_score": 59 },
+      { "tier": "verified", "max_score": 79 },
+      { "tier": "authorized", "max_score": 100 }
+    ]
+  }
+}
+
+Response 200: { "profile": "standard", "updated_at": "", "effective_immediately": true }
+Response 422: { "error": "threshold_invalid", "reason": "auto_approve_below exceeds maximum of 50 (SMX-008)" }
+```
+
+### Add Policy Enforcement Override
+
+```
+POST /api/v1/admin/profiles/{profile_name}/scoring/overrides
+{
+  "policy_handle": "platform/gatekeeper/cpu-size-limit",
+  "override_enforcement_class": "compliance",
+  "rationale": "Prod profile: CPU limit is a hard constraint",
+  "applies_to_resource_types": ["Compute.VirtualMachine"]
+}
+
+Response 201 Created:
+{ "override_uuid": "", "policy_handle": "...", "effective_immediately": true }
+```
+
+### Actor Risk History
+
+```
+GET /api/v1/admin/actors/{actor_uuid}/risk-history
+
+Response 200:
+{
+  "actor_uuid": "",
+  "current_score": 30,
+  "events": [
+    {
+      "event_type": "validation_failure",
+      "occurred_at": "",
+      "request_uuid": "",
+      "base_contribution": 5,
+      "decayed_contribution": 3.2,
+      "days_ago": 4
+    }
+  ],
+  "decay_lambda": 0.1,
+  "score_half_life_days": 7
+}
+
+POST /api/v1/admin/actors/{actor_uuid}/risk-history:reset
+{
+  "reason": "Actor confirmed as trusted automation account",
+  "audit_note": "Reviewed and approved by platform admin"
+}
+```
+
+### Score Audit Trail
+
+```
+GET /api/v1/admin/scoring/audit
+
+Query parameters:
+  from=
+  to=
+  routing_decision=
+  risk_score_above=
+  actor_uuid=
+  resource_type=
+
+Response 200:
+{
+  "score_records": [
+    {
+      "score_record_uuid": "",
+      "request_uuid": "",
+      "risk_score": 47,
+      "routing_decision": "reviewed",
+      "signal_breakdown": { ... },
+      "evaluated_at": ""
+    }
+  ]
+}
+```
+
+
+---
+
+## Approval Management
+
+DCM provides approval gates for requests, policy contributions, provider registrations, and federation contributions. The Admin API is the integration point for recording decisions — it is designed to be called by both human reviewers in the DCM UI and by external systems (ServiceNow, Jira, Slack bots, workflow automation).
+
+### List Pending Approvals
+
+```
+GET /api/v1/admin/approvals/pending
+
+Query parameters:
+  approval_type=
+  tier=
+  reviewer_uuid=       # approvals where this actor is an eligible reviewer
+
+Response 200:
+{
+  "pending_approvals": [
+    {
+      "approval_uuid": "",
+      "approval_type": "policy_contribution",
+      "tier": "authorized",
+      "subject_uuid": "",
+      "subject_handle": "tenant/payments/gatekeeper/cost-ceiling",
+      "required_dcmgroup_uuid": "",     # for authorized tier
+      "quorum_required": 3,
+      "votes_recorded": 1,
+      "submitted_at": "",
+      "window_expires_at": "",
+      "submitted_by": { "uuid": "", "display_name": "Bob Smith" }
+    }
+  ]
+}
+```
+
+### Record an Approval Decision
+
+```
+POST /api/v1/admin/approvals/{approval_uuid}:vote
+
+{
+  "decision": "approve | reject",
+  "reason": "",
+  "recorded_via": "dcm_admin_ui | servicenow | jira | slack_bot | api_direct | other",
+  "external_reference": ""
+}
+
+Response 200:
+{
+  "approval_uuid": "",
+  "voter_uuid": "",
+  "decision": "approve",
+  "votes_recorded": 2,
+  "quorum_required": 3,
+  "quorum_reached": false,
+  "pipeline_status": "pending_authorized"
+}
+
+# When quorum is reached or reviewed/verified satisfied:
+{
+  "approval_uuid": "",
+  "voter_uuid": "",
+  "decision": "approve",
+  "votes_recorded": 3,
+  "quorum_required": 3,
+  "quorum_reached": true,
+  "pipeline_status": "activating"
+}
+
+Response 403: actor is not a member of the required authority group (authorized tier) or not in reviewer role
+Response 409: actor has already voted on this approval (verified and authorized tiers enforce distinct voters)
+Response 410: approval window has expired
+```
+
+### Get Approval Detail
+
+```
+GET /api/v1/admin/approvals/{approval_uuid}
+
+Response 200:
+{
+  "approval_uuid": "",
+  "approval_type": "authorized",
+  "subject_uuid": "",
+  "tier": "authorized",
+  "required_dcmgroup_uuid": "",
+  "quorum_required": 3,
+  "window_expires_at": "",
+  "votes": [
+    {
+      "voter_uuid": "",
+      "voter_display_name": "Alice Chen",
+      "decision": "approve",
+      "recorded_at": "",
+      "recorded_via": "servicenow",
+      "external_reference": "CHG0012345"
+    }
+  ],
+  "status": "pending_authorized",
+  "quorum_reached": false
+}
+```
+
+
+---
+
+## Authority Tier Registry Management
+
+> **Implementation note:** The tier registry change impact detection pipeline is specified in [Authority Tier Model](../data-model/32-authority-tier-model.md) Section 7. The endpoints below are the Admin API surface for proposing, reviewing, and activating tier registry changes. The detection mechanism (tier impact diff computation, affected item query, degradation gate) is an implementation responsibility.
+
+### Propose a Tier Registry Change
+
+```
+POST /api/v1/admin/tier-registry/changes
+
+{
+  "proposed_tiers": [
+    { "name": "auto",                 "insert_after": null,       "decision_gravity": "none" },
+    { "name": "reviewed",             "insert_after": "auto",     "decision_gravity": "routine" },
+    { "name": "verified",             "insert_after": "reviewed", "decision_gravity": "elevated" },
+    { "name": "compliance_reviewed",  "insert_after": "verified", "decision_gravity": "elevated" },
+    { "name": "authorized",           "insert_after": "compliance_reviewed", "decision_gravity": "critical" }
+  ],
+  "reason": "Adding compliance_reviewed tier for PCI-DSS regulated actions"
+}
+
+Response 202 Accepted:
+{
+  "registry_change_uuid": "",
+  "status": "impact_assessment_pending",
+  "estimated_ready_at": ""
+}
+```
+
+### Get Tier Registry Impact Report
+
+```
+GET /api/v1/admin/tier-registry/changes/{change_uuid}/impact
+
+Response 200:
+{
+  "registry_change_uuid": "",
+  "status": "impact_assessed | pending_degradation_review | ready_to_activate | blocked",
+  "summary": {
+    "degradations": 0,
+    "upgrades": 3,
+    "new_tiers": 1,
+    "broken_references": 0,
+    "profile_gaps": 2
+  },
+  "degradations": [],
+  "upgrades": [ ... ],
+  "profile_gaps": [
+    {
+      "profile": "standard",
+      "missing_tiers": ["compliance_reviewed"],
+      "gap_effect": "Requests scoring in the compliance_reviewed range will route to verified tier until threshold list is updated"
+    }
+  ],
+  "blocking_items": []
+}
+```
+
+### Accept a Security Degradation
+
+```
+POST /api/v1/admin/tier-registry/changes/{change_uuid}:accept-degradation
+
+{
+  "affected_item_uuid": "",
+  "affected_item_type": "provider_registration_requirement",
+  "acceptance_reason": "",
+  "accepted_by": ""
+}
+
+Response 200:
+{
+  "acceptance_uuid": "",
+  "degradation_accepted": true,
+  "remaining_degradations": 0,
+  "change_status": "ready_to_activate"
+}
+
+Response 403: actor does not hold verified or authorized tier reviewer role
+Response 409: degradation already accepted
+```
+
+### Activate a Tier Registry Change
+
+```
+POST /api/v1/admin/tier-registry/changes/{change_uuid}:activate
+
+Response 200:
+{
+  "registry_change_uuid": "",
+  "activated_at": "",
+  "new_registry_version": "1.1.0",
+  "impact_report_uuid": ""
+}
+
+Response 409: change has unresolved blocking items (broken_references or unaccepted degradations)
+```
+
+### List Historical Registry Changes
+
+```
+GET /api/v1/admin/tier-registry/changes?status=activated&page_size=20
+
+Response 200:
+{
+  "changes": [
+    {
+      "registry_change_uuid": "",
+      "status": "activated",
+      "activated_at": "",
+      "proposed_by": { "uuid": "", "display_name": "Alice Chen" },
+      "summary": { "degradations": 0, "upgrades": 2, "new_tiers": 1 },
+      "impact_report_uuid": ""
+    }
+  ]
+}
+```
+
diff --git a/content/docs/architecture/specifications/dcm-admin-api.yaml b/content/docs/architecture/specifications/dcm-admin-api.yaml
new file mode 100644
index 0000000..cacadd8
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-admin-api.yaml
@@ -0,0 +1,1861 @@
+openapi: "3.1.0"
+
+info:
+  title: DCM Admin API
+  version: "1.0.0"
+  description: |
+    The DCM Admin API is used by platform engineers, SREs, and system administrators to manage
+    the DCM control plane itself. All endpoints require platform admin or higher authority.
+
+    **Key principles:**
+    - Requires `verified` or `authorized` tier authority for most operations
+    - Mutating operations against the governance model (tier registry, profiles) require
+      `authorized` tier with a declared DCMGroup quorum
+    - All actions produce audit records
+    - Destructive or degrading operations have explicit confirmation steps
+
+
+
+    **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions:
+    custom methods use colon syntax (`POST /resources/{name}:suspend`),
+    async operations return an `Operation` resource (AEP-136 LRO),
+    and list pagination uses `page_size`/`page_token` parameters.
+
+  contact:
+    name: DCM Project
+    url: https://github.com/dcm-project
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0
+
+servers:
+  - url: https://{dcm-host}/
+    description: DCM Control Plane
+    variables:
+      dcm-host:
+        default: dcm.example.com
+
+security:
+  - BearerAuth: []
+
+tags:
+  - name: health
+    description: DCM control plane health and readiness
+  - name: tenants
+    description: Tenant lifecycle management
+  - name: actors
+    description: Actor session and risk management
+  - name: providers
+    description: Provider registration approval and management
+  - name: accreditations
+    description: Provider accreditation review
+  - name: discovery
+    description: Brownfield discovery scheduling and monitoring
+  - name: drift
+    description: Drift orphan management and recovery decisions
+  - name: quotas
+    description: Tenant quota management
+  - name: search
+    description: Search index operations
+  - name: bootstrap
+    description: Bootstrap credential management
+  - name: scoring
+    description: Risk scoring model configuration
+  - name: approvals
+    description: Platform-level approval management
+  - name: tier-registry
+    description: Authority tier registry management
+
+paths:
+
+  # ─── HEALTH ───────────────────────────────────────────────────────────────
+
+  /livez:
+    get:
+      tags: [health]
+      operationId: liveness
+      summary: Kubernetes-style liveness probe
+      security: []
+      responses:
+        "200": { description: Control plane process is alive }
+        "503": { description: Control plane unhealthy }
+
+  /readyz:
+    get:
+      tags: [health]
+      operationId: readiness
+      summary: Kubernetes-style readiness probe
+      security: []
+      responses:
+        "200": { description: Control plane ready to serve requests }
+        "503": { description: Control plane not ready (stores unavailable, bootstrap incomplete, etc.) }
+
+  /metrics:
+    get:
+      tags: [health]
+      operationId: metrics
+      summary: Prometheus metrics endpoint
+      security: []
+      responses:
+        "200":
+          description: Prometheus text format metrics
+          content:
+            text/plain:
+              schema: { type: string }
+
+  /api/v1/admin/health:
+    get:
+      tags: [health]
+      operationId: getAdminHealth
+      summary: Detailed control plane health including component and provider status
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/AdminHealthResponse" }
+
+  # ─── TENANTS ──────────────────────────────────────────────────────────────
+
+  /api/v1/admin/tenants:
+    get:
+      tags: [tenants]
+      operationId: listTenants
+      summary: List all Tenants
+      parameters:
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+        - name: status
+          in: query
+          schema: { type: string, enum: [active, suspended, decommissioned] }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/TenantList" }
+    post:
+      tags: [tenants]
+      operationId: createTenant
+      summary: Create a new Tenant
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/TenantCreate" }
+      responses:
+        "201":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/Tenant" }
+        "409": { description: Tenant handle already exists }
+
+  /api/v1/admin/tenants/{tenant_uuid}:suspend:
+    post:
+      tags: [tenants]
+      operationId: suspendTenant
+      summary: Suspend a Tenant (blocks all new requests; active resources remain)
+      parameters:
+        - { $ref: "#/components/parameters/tenant_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: { type: string }
+      responses:
+        "200": { description: Tenant suspended }
+        "409": { description: Tenant already suspended or decommissioned }
+
+  /api/v1/admin/tenants/{tenant_uuid}:reinstate:
+    post:
+      tags: [tenants]
+      operationId: reinstateTenant
+      summary: Reinstate a suspended Tenant
+      parameters:
+        - { $ref: "#/components/parameters/tenant_uuid" }
+      responses:
+        "200": { description: Tenant reinstated }
+
+  /api/v1/admin/tenants/{tenant_uuid}:
+    delete:
+      tags: [tenants]
+      operationId: decommissionTenant
+      summary: Decommission a Tenant (must have zero active resources)
+      parameters:
+        - { $ref: "#/components/parameters/tenant_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason, confirmation]
+              properties:
+                reason:       { type: string }
+                confirmation: { type: string, const: "DECOMMISSION", description: "Must be the string 'DECOMMISSION'" }
+      responses:
+        "200":
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/Operation" }
+        "409": { description: Tenant has active resources — cannot decommission }
+
+  /api/v1/admin/tenants/{tenant_uuid}/quotas:
+    get:
+      tags: [quotas]
+      operationId: getTenantQuotas
+      summary: Get quota configuration for a Tenant
+      parameters:
+        - { $ref: "#/components/parameters/tenant_uuid" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/TenantQuotas" }
+
+  /api/v1/admin/tenants/{tenant_uuid}/quotas/{resource_type}:
+    put:
+      tags: [quotas]
+      operationId: setTenantQuota
+      summary: Set or update quota for a specific resource type on a Tenant
+      parameters:
+        - { $ref: "#/components/parameters/tenant_uuid" }
+        - name: resource_type
+          in: path
+          required: true
+          schema: { type: string }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/QuotaUpdate" }
+      responses:
+        "200": { description: Quota updated }
+
+  # ─── ACTORS ───────────────────────────────────────────────────────────────
+
+  /api/v1/admin/actors/{actor_uuid}:revoke-sessions:
+    post:
+      tags: [actors]
+      operationId: revokeActorSessions
+      summary: Revoke all active sessions for an actor (emergency session revocation)
+      parameters:
+        - { $ref: "#/components/parameters/actor_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: { type: string }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  sessions_revoked: { type: integer }
+
+  /api/v1/admin/actors/{actor_uuid}/sessions:
+    get:
+      tags: [actors]
+      operationId: getActorSessions
+      summary: List all active sessions for an actor
+      parameters:
+        - { $ref: "#/components/parameters/actor_uuid" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  sessions: { type: array, items: { type: object } }
+
+  /api/v1/admin/actors/{actor_uuid}/risk-history:
+    get:
+      tags: [actors, scoring]
+      operationId: getActorRiskHistory
+      summary: Get risk score history for an actor (full detail for admin)
+      parameters:
+        - { $ref: "#/components/parameters/actor_uuid" }
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/RiskHistory" }
+
+  /api/v1/admin/actors/{actor_uuid}/risk-history:reset:
+    post:
+      tags: [actors]
+      operationId: resetActorRiskHistory
+      summary: Reset risk score history for an actor (requires verified tier)
+      parameters:
+        - { $ref: "#/components/parameters/actor_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: { type: string }
+      responses:
+        "200": { description: Risk history reset }
+
+  # ─── PROVIDERS ────────────────────────────────────────────────────────────
+
+  /api/v1/admin/location-types:
+    get:
+      tags: [locations]
+      operationId: listLocationTypes
+      summary: List registered location types (standard and custom)
+      security:
+        - bearerAuth: []
+      responses:
+        "200":
+          description: All registered location types
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  location_types:
+                    type: array
+                    items: { type: object, additionalProperties: true }
+    post:
+      tags: [locations]
+      operationId: registerCustomLocationType
+      summary: Register a custom location type
+      security:
+        - bearerAuth: []
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { type: object, additionalProperties: true }
+      responses:
+        "200":
+          description: Operation initiated
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/Operation" }
+
+  /api/v1/admin/locations:
+    get:
+      tags: [locations]
+      operationId: adminListLocations
+      summary: List all location nodes (admin — no entitlement filter)
+      parameters:
+        - { name: level, in: query, schema: { type: string } }
+        - { name: page_size, in: query, schema: { type: integer, default: 100 } }
+        - { name: page_token, in: query, schema: { type: string } }
+      security:
+        - bearerAuth: []
+      responses:
+        "200":
+          description: All location nodes
+          content:
+            application/json:
+              schema: { type: object, additionalProperties: true }
+
+  /api/v1/admin/locations/{location_uuid}:
+    patch:
+      tags: [locations]
+      operationId: updateLocationCapacity
+      summary: Update mutable location fields (e.g., rack_units_available)
+      parameters:
+        - { name: location_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      security:
+        - bearerAuth: []
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { type: object, additionalProperties: true }
+      responses:
+        "200":
+          description: Location updated
+          content:
+            application/json:
+              schema: { type: object, additionalProperties: true }
+        "404": { $ref: "#/components/responses/NotFound" }
+
+
+  /api/v1/admin/providers:
+    get:
+      tags: [providers]
+      operationId: listProviders
+      summary: List all registered providers
+      parameters:
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+        - name: provider_type
+          in: query
+          schema: { type: string }
+        - name: status
+          in: query
+          schema: { type: string }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderList" }
+
+  /api/v1/admin/providers/pending:
+    get:
+      tags: [providers]
+      operationId: listPendingProviders
+      summary: List providers awaiting approval
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderList" }
+
+  /api/v1/admin/providers/{provider_uuid}:approve:
+    post:
+      tags: [providers]
+      operationId: approveProvider
+      summary: Approve a provider registration
+      parameters:
+        - { $ref: "#/components/parameters/provider_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason:             { type: string }
+                conditions:         { type: string, description: "Any conditions attached to approval" }
+                external_reference: { type: string }
+      responses:
+        "200": { description: Provider approved and activated }
+        "409": { description: Provider not in PENDING_APPROVAL state }
+
+  /api/v1/admin/providers/{provider_uuid}:reject:
+    post:
+      tags: [providers]
+      operationId: rejectProvider
+      summary: Reject a provider registration
+      parameters:
+        - { $ref: "#/components/parameters/provider_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: { type: string }
+      responses:
+        "200": { description: Provider rejected }
+
+  /api/v1/admin/providers/{provider_uuid}:suspend:
+    post:
+      tags: [providers]
+      operationId: suspendProvider
+      summary: Suspend a provider (no new requests routed; existing resources unaffected)
+      parameters:
+        - { $ref: "#/components/parameters/provider_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason:   { type: string }
+                duration: { type: string, description: "ISO 8601 duration; null = indefinite" }
+      responses:
+        "200": { description: Provider suspended }
+
+  # ─── ACCREDITATIONS ───────────────────────────────────────────────────────
+
+  /api/v1/admin/accreditations:
+    get:
+      tags: [accreditations]
+      operationId: listAccreditations
+      summary: List provider accreditations (pending, active, expiring soon)
+      parameters:
+        - name: status
+          in: query
+          schema: { type: string, enum: [pending, active, expired, expiring_soon] }
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/AccreditationList" }
+
+  /api/v1/admin/accreditations/{accreditation_uuid}:approve:
+    post:
+      tags: [accreditations]
+      operationId: approveAccreditation
+      summary: Approve a submitted accreditation
+      parameters:
+        - { name: accreditation_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason:             { type: string }
+                external_reference: { type: string }
+      responses:
+        "200": { description: Accreditation approved }
+
+  /api/v1/admin/accreditations/{accreditation_uuid}:
+    delete:
+      tags: [accreditations]
+      operationId: revokeAccreditation
+      summary: Revoke an active accreditation
+      parameters:
+        - { name: accreditation_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: { type: string }
+      responses:
+        "200": { description: Accreditation revoked; affected entities notified }
+
+  # ─── DISCOVERY ────────────────────────────────────────────────────────────
+
+  /api/v1/admin/discovery:trigger:
+    post:
+      tags: [discovery]
+      operationId: triggerDiscovery
+      summary: Trigger an immediate discovery cycle for one or all providers
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                provider_uuid: { type: string, format: uuid, description: "Omit to trigger all active providers" }
+                scope:         { type: string, enum: [full, targeted], default: full }
+      responses:
+        "202":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  discovery_job_uuid: { type: string, format: uuid }
+
+  /api/v1/admin/discovery/jobs/{discovery_job_uuid}:
+    get:
+      tags: [discovery]
+      operationId: getDiscoveryJob
+      summary: Get status of a discovery job
+      parameters:
+        - { name: discovery_job_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/DiscoveryJobStatus" }
+
+  # ─── DRIFT / ORPHANS ──────────────────────────────────────────────────────
+
+  /api/v1/admin/orphans:
+    get:
+      tags: [drift]
+      operationId: listOrphans
+      summary: List discovered entities with no matching Requested State record
+      parameters:
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/OrphanList" }
+
+  /api/v1/admin/orphans/{orphan_candidate_uuid}/resolve:
+    post:
+      tags: [drift]
+      operationId: resolveOrphan
+      summary: Resolve an orphan candidate (ingest, ignore, or decommission)
+      parameters:
+        - { name: orphan_candidate_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [resolution]
+              properties:
+                resolution:        { type: string, enum: [ingest, ignore, decommission] }
+                target_tenant_uuid: { type: string, format: uuid, description: "Required for 'ingest'" }
+                reason:            { type: string }
+      responses:
+        "200": { description: Orphan resolved }
+
+  /api/v1/admin/recovery-decisions/pending:
+    get:
+      tags: [drift]
+      operationId: listPendingRecoveryDecisions
+      summary: List platform-level recovery decisions awaiting admin resolution
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  decisions: { type: array, items: { type: object } }
+
+  /api/v1/admin/recovery-decisions/{recovery_decision_uuid}:
+    post:
+      tags: [drift]
+      operationId: resolveRecoveryDecision
+      summary: Resolve a platform-level recovery decision
+      parameters:
+        - { name: recovery_decision_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [decision, reason]
+              properties:
+                decision: { type: string, enum: [approve, reject, escalate] }
+                reason:   { type: string }
+      responses:
+        "200": { description: Decision recorded }
+
+  # ─── SEARCH INDEX ─────────────────────────────────────────────────────────
+
+  /api/v1/admin/search-index:rebuild:
+    post:
+      tags: [search]
+      operationId: rebuildSearchIndex
+      summary: Trigger a full search index rebuild
+      responses:
+        "202":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  job_uuid: { type: string, format: uuid }
+
+  /api/v1/admin/search-index/status:
+    get:
+      tags: [search]
+      operationId: getSearchIndexStatus
+      summary: Get current search index status and last rebuild time
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  status:       { type: string }
+                  entity_count: { type: integer }
+                  last_rebuilt: { type: string, format: date-time }
+
+  # ─── BOOTSTRAP ────────────────────────────────────────────────────────────
+
+  /api/v1/admin/bootstrap:rotate-credential:
+    post:
+      tags: [bootstrap]
+      operationId: rotateBootstrapCredential
+      summary: Rotate the bootstrap credential (zero-day trust credential rotation)
+      description: |
+        Rotates the bootstrap credential used for initial DCM trust establishment.
+        Requires the current credential to be presented and records the rotation in
+        the Audit Store. New credential is returned once; cannot be retrieved again.
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [current_credential_ref, reason]
+              properties:
+                current_credential_ref: { type: string }
+                reason:                 { type: string }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  new_credential: { type: string, description: "New bootstrap credential. Shown once." }
+                  rotated_at:     { type: string, format: date-time }
+
+  # ─── SCORING ──────────────────────────────────────────────────────────────
+
+  /api/v1/admin/profiles/{profile_name}/scoring:
+    get:
+      tags: [scoring]
+      operationId: getProfileScoring
+      summary: Get scoring model configuration for a profile
+      parameters:
+        - { $ref: "#/components/parameters/profile_name" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ScoringConfiguration" }
+
+    patch:
+      tags: [scoring]
+      operationId: updateProfileScoring
+      summary: Update scoring configuration for a profile (threshold adjustments)
+      parameters:
+        - { $ref: "#/components/parameters/profile_name" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/ScoringConfigurationUpdate" }
+      responses:
+        "200": { description: Scoring configuration updated }
+        "422": { description: Configuration violates SMX-008 hard constraint (auto max_score ≤ 50) }
+
+  /api/v1/admin/profiles/{profile_name}/scoring/overrides:
+    post:
+      tags: [scoring]
+      operationId: addScoringOverride
+      summary: Add a per-policy enforcement class override for a profile
+      parameters:
+        - { $ref: "#/components/parameters/profile_name" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/ScoringOverride" }
+      responses:
+        "201": { description: Override added }
+
+  /api/v1/admin/scoring/audit:
+    get:
+      tags: [scoring]
+      operationId: getScoringAudit
+      summary: Get scoring evaluation audit records for review
+      parameters:
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+        - name: actor_uuid
+          in: query
+          schema: { type: string, format: uuid }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  records: { type: array, items: { type: object } }
+
+  # ─── APPROVALS ────────────────────────────────────────────────────────────
+
+  /api/v1/admin/approvals/pending:
+    get:
+      tags: [approvals]
+      operationId: listPlatformApprovals
+      summary: List all platform-level approvals pending admin action
+      parameters:
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+        - name: tier
+          in: query
+          schema: { type: string }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  approvals: { type: array, items: { $ref: "#/components/schemas/ApprovalRecord" } }
+
+  /api/v1/admin/approvals/{approval_uuid}:
+    get:
+      tags: [approvals]
+      operationId: getApproval
+      summary: Get full detail for an approval record including all decisions
+      parameters:
+        - { name: approval_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ApprovalDetail" }
+
+  /api/v1/admin/approvals/{approval_uuid}:vote:
+    post:
+      tags: [approvals]
+      operationId: recordAdminApprovalVote
+      summary: Record a platform admin approval vote
+      parameters:
+        - { name: approval_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [decision, reason]
+              properties:
+                decision:           { type: string, enum: [approve, reject, abstain] }
+                reason:             { type: string }
+                external_reference: { type: string }
+      responses:
+        "200": { description: Vote recorded; approval gate re-evaluated }
+
+  # ─── AUTHORITY TIER REGISTRY ──────────────────────────────────────────────
+
+  /api/v1/admin/tier-registry/changes:
+    post:
+      tags: [tier-registry]
+      operationId: proposeTierRegistryChange
+      summary: Propose a change to the authority tier registry
+      description: |
+        Submits a proposed updated tier list. DCM computes the tier impact diff immediately —
+        comparing the proposed ordered list to the current one and classifying every changed
+        tier as SECURITY_DEGRADATION, SECURITY_UPGRADE, BROKEN_REFERENCE, PROFILE_GAP, or
+        STALE_WEIGHT. Returns the impact report UUID for inspection.
+        The change cannot activate until all SECURITY_DEGRADATION and BROKEN_REFERENCE items
+        are explicitly accepted. See 32-authority-tier-model.md Section 7.
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/TierRegistryChangeProposal" }
+      responses:
+        "202":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  change_uuid:        { type: string, format: uuid }
+                  impact_report_uuid: { type: string, format: uuid }
+                  blocking_items:     { type: integer, description: "Number of SECURITY_DEGRADATION or BROKEN_REFERENCE items" }
+                  status:             { type: string, enum: [pending_review, ready_to_activate] }
+
+    get:
+      tags: [tier-registry]
+      operationId: listTierRegistryChanges
+      summary: List tier registry change proposals
+      parameters:
+        - name: status
+          in: query
+          schema: { type: string, enum: [pending_review, ready_to_activate, activated, rejected] }
+        - { $ref: "#/components/parameters/page_size" }
+        - { $ref: "#/components/parameters/page_token" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  changes: { type: array, items: { type: object } }
+
+  /api/v1/admin/tier-registry/changes/{change_uuid}/impact:
+    get:
+      tags: [tier-registry]
+      operationId: getTierRegistryImpact
+      summary: Get the full impact report for a proposed tier registry change
+      parameters:
+        - { name: change_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/TierImpactReport" }
+
+  /api/v1/admin/tier-registry/changes/{change_uuid}:accept-degradation:
+    post:
+      tags: [tier-registry]
+      operationId: acceptTierDegradation
+      summary: Accept a specific SECURITY_DEGRADATION item (requires verified tier)
+      description: |
+        Accepts a single SECURITY_DEGRADATION item identified in the impact report.
+        The accepting actor must be at `verified` tier or above and must provide a reason
+        describing what compensating controls justify the degradation.
+        The change cannot activate until ALL SECURITY_DEGRADATION items are accepted.
+      parameters:
+        - { name: change_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [degradation_item_uuid, reason, compensating_controls]
+              properties:
+                degradation_item_uuid: { type: string, format: uuid }
+                reason:                { type: string, minLength: 20, description: "Why this degradation is acceptable" }
+                compensating_controls: { type: string, minLength: 20, description: "What compensating controls are in place" }
+      responses:
+        "200": { description: Degradation accepted; impact report updated }
+        "403": { description: Actor does not meet required tier (verified or above)" }
+        "409": { description: Item already accepted or change not in pending_review state }
+
+  /api/v1/admin/tier-registry/changes/{change_uuid}:activate:
+    post:
+      tags: [tier-registry]
+      operationId: activateTierRegistryChange
+      summary: Activate a proposed tier registry change
+      description: |
+        Activates the proposed tier list as the new authoritative authority tier registry.
+        Returns 409 if any SECURITY_DEGRADATION or BROKEN_REFERENCE items remain unaccepted.
+        Impact report is stored in the Audit Store at activation time.
+        Requires `authorized` tier.
+      parameters:
+        - { name: change_uuid, in: path, required: true, schema: { type: string, format: uuid } }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: { type: string }
+      responses:
+        "200": { description: Tier registry updated; new ordered list now active }
+        "403": { description: Actor does not meet required tier (authorized)" }
+        "409":
+          description: Blocking items remain unaccepted
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  blocking_items: { type: array, items: { type: object } }
+
+# ─── COMPONENTS ────────────────────────────────────────────────────────────────
+
+  # ── Workload Analysis (Admin — aggregate view) ────────────────────────────────
+  /api/v1/admin/workload-analysis:
+    get:
+      tags: [workload-analysis]
+      operationId: listWorkloadProfiles
+      summary: List all workload profiles across all tenants (admin aggregate view)
+      description: |
+        Returns workload profiles platform-wide. Useful for capacity planning,
+        workload-type distribution reporting, and migration readiness assessment.
+        Filtered by archetype, confidence, or resource type.
+      parameters:
+        - {name: archetype, in: query, schema: {type: string, enum: [web_server, database, batch_processor, message_broker, api_gateway, cache, storage, monitoring, unknown]}}
+        - {name: confidence, in: query, schema: {type: string, enum: [high, medium, low, undetermined]}}
+        - {name: resource_type, in: query, schema: {type: string}, description: "FQN e.g. Compute.VirtualMachine"}
+        - {name: tenant_uuid, in: query, schema: {type: string, format: uuid}}
+        - {name: containerization_score_min, in: query, schema: {type: integer, minimum: 1, maximum: 10}}
+        - {name: page_size, in: query, schema: {type: integer, default: 50}}
+        - {name: page_token, in: query, schema: {type: string}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Workload profile list
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  items:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        entity_uuid: {type: string, format: uuid}
+                        tenant_uuid: {type: string, format: uuid}
+                        resource_type: {type: string}
+                        workload_archetype: {type: string}
+                        confidence: {type: string}
+                        containerization_score: {type: integer}
+                        analyzed_at: {type: string, format: date-time}
+                  total_count: {type: integer}
+                  archetype_distribution:
+                    type: object
+                    description: Count per archetype across all profiles in result set
+                    additionalProperties: {type: integer}
+                  next_page_token: {type: string}
+        "401": {$ref: "#/components/responses/Unauthorized"}
+        "403": {$ref: "#/components/responses/Forbidden"}
+
+  # ── Accreditation Monitor ─────────────────────────────────────────────────────
+  /api/v1/admin/accreditations/{accreditation_uuid}:verify:
+    post:
+      tags: [accreditation]
+      operationId: triggerAccreditationVerification
+      summary: Trigger immediate external verification of an accreditation
+      parameters:
+        - {name: accreditation_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                override_reason: {type: string, description: "Required when manually overriding last_verified_at in air-gapped mode"}
+      responses:
+        "200":
+          description: Operation initiated
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/Operation"}
+        "404": {$ref: "#/components/responses/NotFound"}
+
+  /api/v1/admin/accreditations/{accreditation_uuid}:configure-webhook:
+    post:
+      tags: [accreditation]
+      operationId: configureAccreditationWebhook
+      summary: Configure a contract management webhook for Tier 3 accreditation verification
+      description: |
+        Used for BAA and DoD IL accreditations. Registers the contract management
+        system (DocuSign, Ironclad, etc.) to send lifecycle events to DCM when
+        the underlying contract is signed, amended, or terminated.
+      parameters:
+        - {name: accreditation_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [contract_system, contract_id]
+              properties:
+                contract_system: {type: string, enum: [docusign, ironclad, agiloft, custom]}
+                contract_id: {type: string, description: ID in the contract management system}
+                webhook_secret: {type: string, description: HMAC secret for webhook authentication}
+      responses:
+        "200":
+          description: Webhook configured; webhook_url returned for registration in contract system
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  accreditation_uuid: {type: string, format: uuid}
+                  webhook_url: {type: string, format: uri, description: "Register this URL in your contract management system"}
+                  webhook_secret_set: {type: boolean}
+
+  /api/v1/admin/accreditations/{accreditation_uuid}/contract-event:
+    post:
+      tags: [accreditation]
+      operationId: receiveAccreditationContractEvent
+      summary: Inbound webhook — receive contract lifecycle event from contract management system
+      description: |
+        Called by contract management systems (DocuSign, Ironclad, etc.) when
+        the underlying BAA or authorization contract changes state.
+        Authenticated via HMAC signature using the webhook_secret.
+      parameters:
+        - {name: accreditation_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{webhookHmac: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [contract_event_type, contract_id, effective_date]
+              properties:
+                contract_event_type: {type: string, enum: [signed, amended, terminated, renewal_due, renewed]}
+                contract_id: {type: string}
+                effective_date: {type: string, format: date-time}
+                details: {type: object, additionalProperties: true}
+      responses:
+        "200":
+          description: Event received and processed
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  accreditation_uuid: {type: string, format: uuid}
+                  dcm_action_taken: {type: string, enum: [activated, pending_review, revoked, none]}
+        "401": {$ref: "#/components/responses/Unauthorized"}
+        "404": {$ref: "#/components/responses/NotFound"}
+
+  # ── Maintenance Windows ───────────────────────────────────────────────────────
+  /api/v1/admin/maintenance-windows:
+    get:
+      tags: [scheduling]
+      operationId: listMaintenanceWindows
+      summary: List declared maintenance windows
+      parameters:
+        - {name: status, in: query, schema: {type: string, enum: [active, upcoming, expired]}}
+        - {name: page_size, in: query, schema: {type: integer, default: 50}}
+        - {name: page_token, in: query, schema: {type: string}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Maintenance window list
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  items: {type: array, items: {$ref: "#/components/schemas/MaintenanceWindow"}}
+                  next_page_token: {type: string}
+    post:
+      tags: [scheduling]
+      operationId: createMaintenanceWindow
+      summary: Declare a new maintenance window
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: {$ref: "#/components/schemas/MaintenanceWindowCreate"}
+      responses:
+        "200":
+          description: Maintenance window created
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/MaintenanceWindow"}
+        "422": {$ref: "#/components/responses/UnprocessableEntity"}
+
+  /api/v1/admin/maintenance-windows/{window_uuid}:
+    get:
+      tags: [scheduling]
+      operationId: getMaintenanceWindow
+      summary: Get maintenance window details including scheduled requests in queue
+      parameters:
+        - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Maintenance window detail
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/MaintenanceWindow"}
+        "404": {$ref: "#/components/responses/NotFound"}
+    patch:
+      tags: [scheduling]
+      operationId: updateMaintenanceWindow
+      summary: Update maintenance window schedule or description
+      parameters:
+        - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: {$ref: "#/components/schemas/MaintenanceWindowPatch"}
+      responses:
+        "200":
+          description: Maintenance window updated
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/MaintenanceWindow"}
+    delete:
+      tags: [scheduling]
+      operationId: deleteMaintenanceWindow
+      summary: Delete a maintenance window (cancels queued requests if policy dictates)
+      parameters:
+        - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Deleted; queued_request_disposition indicates what happened to waiting requests
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  window_uuid: {type: string, format: uuid}
+                  queued_requests_affected: {type: integer}
+                  queued_request_disposition: {type: string, enum: [cancelled, reassigned, held]}
+
+  /api/v1/admin/maintenance-windows/{window_uuid}/scheduled-requests:
+    get:
+      tags: [scheduling]
+      operationId: listWindowScheduledRequests
+      summary: List requests queued for this maintenance window
+      parameters:
+        - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+        - {name: page_size, in: query, schema: {type: integer, default: 50}}
+        - {name: page_token, in: query, schema: {type: string}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Queued requests
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  items: {type: array, items: {type: object, additionalProperties: true}}
+                  next_page_token: {type: string}
+
+  # ── Federation Management ────────────────────────────────────────────────────
+  /api/v1/admin/federation/peers:
+    get:
+      tags: [federation]
+      operationId: listFederationPeers
+      summary: List all registered federation peer DCM instances
+      parameters:
+        - {name: trust_posture, in: query, schema: {type: string, enum: [verified, vouched, untrusted]}}
+        - {name: status, in: query, schema: {type: string, enum: [active, suspended, pending]}}
+        - {name: page_size, in: query, schema: {type: integer, default: 50}}
+        - {name: page_token, in: query, schema: {type: string}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: List of federation peer registrations
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  items: {type: array, items: {$ref: "#/components/schemas/FederationPeer"}}
+                  next_page_token: {type: string}
+        "401": {$ref: "#/components/responses/Unauthorized"}
+        "403": {$ref: "#/components/responses/Forbidden"}
+    post:
+      tags: [federation]
+      operationId: registerFederationPeer
+      summary: Register a new federation peer DCM instance
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: {$ref: "#/components/schemas/FederationPeerRegistration"}
+      responses:
+        "200":
+          description: Operation initiated — peer registration pending trust verification
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/Operation"}
+        "422": {$ref: "#/components/responses/UnprocessableEntity"}
+
+  /api/v1/admin/federation/peers/{peer_uuid}:
+    get:
+      tags: [federation]
+      operationId: getFederationPeer
+      summary: Get federation peer details and current trust status
+      parameters:
+        - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Federation peer record
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/FederationPeer"}
+        "404": {$ref: "#/components/responses/NotFound"}
+    delete:
+      tags: [federation]
+      operationId: deregisterFederationPeer
+      summary: Deregister a federation peer (graceful tunnel teardown)
+      parameters:
+        - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Operation initiated
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/Operation"}
+
+  /api/v1/admin/federation/peers/{peer_uuid}:set-trust-posture:
+    post:
+      tags: [federation]
+      operationId: setFederationPeerTrustPosture
+      summary: Set the trust posture for a federation peer
+      parameters:
+        - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [trust_posture, reason]
+              properties:
+                trust_posture: {type: string, enum: [verified, vouched, untrusted]}
+                reason: {type: string, description: Justification for trust posture change}
+      responses:
+        "200":
+          description: Trust posture updated
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/FederationPeer"}
+
+  /api/v1/admin/federation/peers/{peer_uuid}:suspend:
+    post:
+      tags: [federation]
+      operationId: suspendFederationPeer
+      summary: Suspend federation tunnel (stop routing; preserve registration)
+      parameters:
+        - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: {type: string}
+      responses:
+        "200":
+          description: Peer suspended
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/FederationPeer"}
+
+  /api/v1/admin/federation/peers/{peer_uuid}/routed-requests:
+    get:
+      tags: [federation]
+      operationId: listFederationRoutedRequests
+      summary: List requests routed through this federation peer
+      parameters:
+        - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+        - {name: status, in: query, schema: {type: string}}
+        - {name: page_size, in: query, schema: {type: integer, default: 50}}
+        - {name: page_token, in: query, schema: {type: string}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Federated request list
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  items: {type: array, items: {type: object, additionalProperties: true}}
+                  next_page_token: {type: string}
+
+  /api/v1/admin/federation/config:
+    get:
+      tags: [federation]
+      operationId: getFederationConfig
+      summary: Get this DCM instance's federation configuration (identity, capabilities, trust policy)
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Federation configuration
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/FederationConfig"}
+    patch:
+      tags: [federation]
+      operationId: updateFederationConfig
+      summary: Update federation configuration (enable/disable federation, set scope)
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: {$ref: "#/components/schemas/FederationConfigPatch"}
+      responses:
+        "200":
+          description: Configuration updated
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/FederationConfig"}
+
+components:
+
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
+
+  parameters:
+
+    tenant_uuid:
+      name: tenant_uuid
+      in: path
+      required: true
+      schema: { type: string, format: uuid }
+
+    provider_uuid:
+      name: provider_uuid
+      in: path
+      required: true
+      schema: { type: string, format: uuid }
+
+    actor_uuid:
+      name: actor_uuid
+      in: path
+      required: true
+      schema: { type: string, format: uuid }
+
+    profile_name:
+      name: profile_name
+      in: path
+      required: true
+      schema: { type: string, enum: [minimal, dev, standard, prod, fsi, sovereign] }
+
+    limit:
+      name: page_size
+      in: query
+      schema: { type: integer, minimum: 1, maximum: 1000, default: 50 }
+
+    cursor:
+      name: page_token
+      in: query
+      schema: { type: string }
+
+  schemas:
+
+
+
+    MaintenanceWindow:
+      type: object
+      properties:
+        window_uuid: {type: string, format: uuid}
+        handle: {type: string, description: "Stable reference used in schedule.window_id"}
+        display_name: {type: string}
+        description: {type: string}
+        schedule:
+          type: object
+          description: Cron expression or recurrence rule for this window
+          properties:
+            rrule: {type: string, description: "RFC 5545 RRULE e.g. FREQ=WEEKLY;BYDAY=SA;BYHOUR=2"}
+            duration: {type: string, description: "ISO 8601 duration e.g. PT4H"}
+            timezone: {type: string, description: "IANA timezone e.g. UTC, America/New_York"}
+        status: {type: string, enum: [active, suspended, expired]}
+        next_opens_at: {type: string, format: date-time}
+        next_closes_at: {type: string, format: date-time}
+        queued_request_count: {type: integer}
+        created_at: {type: string, format: date-time}
+        owned_by_actor_uuid: {type: string, format: uuid}
+
+    MaintenanceWindowCreate:
+      type: object
+      required: [handle, display_name, schedule]
+      properties:
+        handle: {type: string, pattern: "^[a-z][a-z0-9-]{2,63}$"}
+        display_name: {type: string}
+        description: {type: string}
+        schedule:
+          type: object
+          required: [rrule, duration, timezone]
+          properties:
+            rrule: {type: string}
+            duration: {type: string}
+            timezone: {type: string}
+
+    MaintenanceWindowPatch:
+      type: object
+      properties:
+        display_name: {type: string}
+        description: {type: string}
+        status: {type: string, enum: [active, suspended]}
+        schedule:
+          type: object
+          properties:
+            rrule: {type: string}
+            duration: {type: string}
+            timezone: {type: string}
+
+    FederationPeer:
+      type: object
+      properties:
+        peer_uuid: {type: string, format: uuid}
+        display_name: {type: string}
+        dcm_instance_uuid: {type: string, format: uuid}
+        endpoint: {type: string, format: uri}
+        trust_posture: {type: string, enum: [verified, vouched, untrusted]}
+        status: {type: string, enum: [active, suspended, pending, deregistered]}
+        sovereignty_declarations: {type: array, items: {type: string}}
+        registered_at: {type: string, format: date-time}
+        last_heartbeat_at: {type: string, format: date-time}
+        routed_request_count: {type: integer}
+
+    FederationPeerRegistration:
+      type: object
+      required: [display_name, endpoint, public_key_pem]
+      properties:
+        display_name: {type: string}
+        endpoint: {type: string, format: uri, description: mTLS endpoint of the peer DCM instance}
+        public_key_pem: {type: string, description: Public key for mTLS identity verification}
+        initial_trust_posture: {type: string, enum: [verified, vouched, untrusted], default: untrusted}
+        scope_declaration:
+          type: object
+          properties:
+            resource_types_accessible: {type: array, items: {type: string}}
+            sovereignty_constraints: {type: array, items: {type: string}}
+
+    FederationConfig:
+      type: object
+      properties:
+        federation_enabled: {type: boolean}
+        this_instance_uuid: {type: string, format: uuid}
+        this_instance_display_name: {type: string}
+        this_instance_endpoint: {type: string, format: uri}
+        default_trust_posture: {type: string, enum: [verified, vouched, untrusted]}
+        peer_count: {type: integer}
+        active_peer_count: {type: integer}
+
+    FederationConfigPatch:
+      type: object
+      properties:
+        federation_enabled: {type: boolean}
+        default_trust_posture: {type: string, enum: [verified, vouched, untrusted]}
+        this_instance_display_name: {type: string}
+
+    Operation:
+      type: object
+      description: |
+        AEP-136 Long-Running Operation. Returned by async operations instead of 202 Accepted.
+        Poll GET {operation.name} until done is true.
+        The operation.name is a stable resource path: /api/v1/operations/{uuid}
+      required: [name, done]
+      additionalProperties: false
+      properties:
+        name:
+          type: string
+          description: "Stable resource path for this operation. Poll this URL for status."
+          example: /api/v1/operations/a1b2c3d4-e5f6-7890-abcd-ef1234567890
+        done:
+          type: boolean
+          description: True when the operation has reached a terminal state (success or error)
+          default: false
+        metadata:
+          type: object
+          description: Operation-specific progress metadata
+          additionalProperties: false
+          properties:
+            stage:
+              type: string
+              description: Current pipeline stage
+            progress_pct:
+              type: integer
+              minimum: 0
+              maximum: 100
+            resource_uuid:
+              type: string
+              format: uuid
+              description: UUID of the resource being created/modified (set as soon as assigned)
+            created_at:
+              type: string
+              format: date-time
+            updated_at:
+              type: string
+              format: date-time
+        response:
+          type: object
+          description: Present when done is true and the operation succeeded. Contains the result resource.
+          additionalProperties: true
+        error:
+          type: object
+          description: Present when done is true and the operation failed.
+          additionalProperties: false
+          properties:
+            code:    { type: string }
+            message: { type: string }
+            details: { type: array, items: { type: object } }
+
+
+    Error:
+      type: object
+      required: [error]
+      properties:
+        error:
+          type: object
+          required: [code, message, request_id]
+          properties:
+            code:       { type: string }
+            message:    { type: string }
+            request_id: { type: string, format: uuid }
+            rule_uuid:  { type: string, format: uuid }
+            fields:     { type: array, items: { type: object } }
+
+    AdminHealthResponse:
+      type: object
+      properties:
+        status:        { type: string, enum: [healthy, degraded, unhealthy] }
+        version:       { type: string }
+        uptime:        { type: string }
+        components:
+          type: array
+          items:
+            type: object
+            properties:
+              name:   { type: string }
+              status: { type: string }
+              detail: { type: string }
+        providers:
+          type: object
+          properties:
+            total:    { type: integer }
+            healthy:  { type: integer }
+            degraded: { type: integer }
+            unavailable: { type: integer }
+
+    Tenant:
+      type: object
+      properties:
+        uuid:         { type: string, format: uuid }
+        handle:       { type: string }
+        display_name: { type: string }
+        status:       { type: string }
+        created_at:   { type: string, format: date-time }
+        updated_at:   { type: string, format: date-time }
+        resource_count: { type: integer }
+        compliance_domains: { type: array, items: { type: string } }
+
+    TenantCreate:
+      type: object
+      required: [handle, display_name]
+      properties:
+        handle:       { type: string }
+        display_name: { type: string }
+        compliance_domains: { type: array, items: { type: string } }
+
+    TenantList:
+      type: object
+      properties:
+        items:      { type: array, items: { $ref: "#/components/schemas/Tenant" } }
+        pagination: { type: object }
+
+    TenantQuotas:
+      type: object
+      properties:
+        tenant_uuid: { type: string, format: uuid }
+        quotas:
+          type: array
+          items:
+            type: object
+            properties:
+              resource_type: { type: string }
+              page_size:         { type: integer }
+              used:          { type: integer }
+              remaining:     { type: integer }
+
+    QuotaUpdate:
+      type: object
+      required: [limit]
+      properties:
+        page_size:  { type: integer, minimum: 0 }
+        reason: { type: string }
+
+    ProviderList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            type: object
+            properties:
+              uuid:             { type: string, format: uuid }
+              display_name:     { type: string }
+              provider_type_id: { type: string }
+              status:           { type: string }
+              trust_score:      { type: number }
+              health_status:    { type: string }
+              accreditations:   { type: array, items: { type: object } }
+        pagination: { type: object }
+
+    AccreditationList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            type: object
+            properties:
+              accreditation_uuid: { type: string, format: uuid }
+              provider_uuid:      { type: string, format: uuid }
+              framework:          { type: string }
+              status:             { type: string }
+              expires_at:        { type: string, format: date-time }
+        pagination: { type: object }
+
+    DiscoveryJobStatus:
+      type: object
+      properties:
+        discovery_job_uuid: { type: string, format: uuid }
+        status:     { type: string, enum: [running, completed, failed] }
+        started_at: { type: string, format: date-time }
+        completed_at: { type: string, format: date-time }
+        providers_queried: { type: integer }
+        entities_discovered: { type: integer }
+        drift_records_created: { type: integer }
+        orphans_detected: { type: integer }
+
+    OrphanList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            type: object
+            properties:
+              orphan_candidate_uuid: { type: string, format: uuid }
+              provider_uuid: { type: string, format: uuid }
+              resource_type: { type: string }
+              discovered_at: { type: string, format: date-time }
+              provider_entity_id: { type: string }
+        pagination: { type: object }
+
+    ScoringConfiguration:
+      type: object
+      properties:
+        profile: { type: string }
+        approval_routing:
+          type: array
+          items:
+            type: object
+            properties:
+              tier:      { type: string }
+              max_score: { type: integer }
+        smx008_auto_cap:
+          type: integer
+          description: "Hard cap on auto-approve max_score (≤ 50 always enforced)"
+
+    ScoringConfigurationUpdate:
+      type: object
+      properties:
+        approval_routing:
+          type: array
+          items:
+            type: object
+            required: [tier, max_score]
+            properties:
+              tier:      { type: string }
+              max_score: { type: integer, minimum: 0, maximum: 100 }
+
+    ScoringOverride:
+      type: object
+      required: [policy_uuid, enforcement_class_override]
+      properties:
+        policy_uuid: { type: string, format: uuid }
+        enforcement_class_override: { type: string, enum: [compliance, operational] }
+        reason: { type: string }
+
+    RiskHistory:
+      type: object
+      properties:
+        actor_uuid: { type: string, format: uuid }
+        records:
+          type: array
+          items:
+            type: object
+            properties:
+              score:          { type: integer }
+              routing_tier:   { type: string }
+              evaluated_at:   { type: string, format: date-time }
+              request_uuid:   { type: string, format: uuid }
+              stored_tier_weight: { type: integer }
+
+    ApprovalRecord:
+      type: object
+      properties:
+        approval_uuid:    { type: string, format: uuid }
+        subject_type:     { type: string }
+        subject_uuid:     { type: string, format: uuid }
+        required_tier:    { type: string }
+        required_quorum:  { type: integer }
+        decisions:
+          type: array
+          items:
+            type: object
+            properties:
+              actor_uuid:   { type: string, format: uuid }
+              decision:     { type: string }
+              reason:       { type: string }
+              recorded_at:  { type: string, format: date-time }
+        expires_at:  { type: string, format: date-time }
+        created_at:  { type: string, format: date-time }
+        status:      { type: string, enum: [open, approved, rejected, expired] }
+
+    ApprovalDetail:
+      allOf:
+        - { $ref: "#/components/schemas/ApprovalRecord" }
+        - type: object
+          properties:
+            subject_detail: { type: object }
+            stored_tier_weights:
+              type: object
+              description: "Tier weights recorded at approval creation time (ATM-008)"
+              additionalProperties: { type: integer }
+
+    TierRegistryChangeProposal:
+      type: object
+      required: [proposed_tier_list, reason]
+      properties:
+        proposed_tier_list:
+          type: array
+          minItems: 1
+          items:
+            type: object
+            required: [name, decision_gravity]
+            properties:
+              name:             { type: string, description: "Stable tier name (e.g., auto, reviewed, verified, authorized)" }
+              decision_gravity: { type: string, enum: [none, routine, elevated, critical] }
+              description:      { type: string }
+              dcm_gate:         { type: string }
+              organization_provides: { type: string }
+              dcmgroup_required: { type: boolean }
+              dcmgroup_uuid:    { type: string, format: uuid }
+              quorum_threshold: { type: integer, minimum: 1 }
+        reason: { type: string }
+
+    TierImpactReport:
+      type: object
+      properties:
+        impact_report_uuid: { type: string, format: uuid }
+        change_uuid:        { type: string, format: uuid }
+        computed_at:        { type: string, format: date-time }
+        blocking_items:     { type: integer }
+        items:
+          type: array
+          items:
+            type: object
+            properties:
+              item_uuid:       { type: string, format: uuid }
+              classification:
+                type: string
+                enum: [SECURITY_DEGRADATION, SECURITY_UPGRADE, BROKEN_REFERENCE, PROFILE_GAP, STALE_WEIGHT]
+              tier_name:       { type: string }
+              old_position:    { type: integer }
+              new_position:    { type: integer }
+              old_gravity:     { type: string }
+              new_gravity:     { type: string }
+              affected_items:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    item_type:   { type: string }
+                    item_uuid:   { type: string, format: uuid }
+                    description: { type: string }
+              accepted:        { type: boolean }
+              accepted_by:     { type: string, format: uuid }
+              accepted_reason: { type: string }
diff --git a/content/docs/architecture/specifications/dcm-admin-gui-spec.md b/content/docs/architecture/specifications/dcm-admin-gui-spec.md
new file mode 100644
index 0000000..9f1fb06
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-admin-gui-spec.md
@@ -0,0 +1,253 @@
+# DCM Admin Web GUI Specification
+
+> **AEP Alignment:** Admin API endpoints referenced in this spec follow [AEP](https://aep.dev) conventions — custom methods use colon syntax (`POST /admin/providers/{uuid}:approve`). See `schemas/openapi/dcm-admin-api.yaml` for the normative specification.
+
+
+**Document Status:** 🔄 In Progress
+**Document Type:** Specification — Platform Administration Interface
+**Related Documents:** [Admin API Specification](dcm-admin-api-spec.md) | [Consumer GUI Specification](dcm-consumer-gui-spec.md) | [Provider GUI Specification](dcm-provider-gui-spec.md) | [Flow GUI Specification](dcm-flow-gui-spec.md)
+
+> **Status:** Draft — Ready for implementation feedback
+>
+> The Admin GUI is the platform operations console for Platform Admins, SREs, Policy Owners, Security teams, and Auditors. It wraps the Admin API and exposes all platform management capabilities. Like the Consumer GUI, it participates in the same session model — same login, role-gated access to the admin panel.
+
+---
+
+## 1. Architecture
+
+### 1.1 Unified Shell Model
+
+The Admin GUI is not a separate application. It is an **additional surface within the DCM web application**, revealed when the authenticated actor holds a platform-level role (`platform_admin`, `sre`, `auditor`, `security`, `policy_owner`).
+
+```
+DCM Web Application
+├── Consumer Portal (visible to all authenticated actors)
+│     └── [section 2–11 of Consumer GUI spec]
+│
+├── Admin Panel (visible to platform-level role holders)
+│     └── [this spec — section 2–11]
+│
+├── Provider Management (visible to provider owner roles)
+│     └── [Provider GUI spec — dcm-provider-gui-spec.md]
+│
+└── Flow GUI (linked / embedded for policy_owner and sre)
+      └── [dcm-flow-gui-spec.md]
+```
+
+Navigation adapts to the actor's highest privilege level. A Platform Admin sees all three surfaces. A consumer-only actor sees only the Consumer Portal.
+
+### 1.2 Authentication and Role Mapping
+
+The Admin GUI requires the session to carry a platform-level role. The role check is performed client-side on every page load and server-enforced by the Admin API on every request.
+
+| Role | Admin sections available |
+|------|--------------------------|
+| `platform_admin` | All Admin sections |
+| `sre` | Health, Discovery, Orphan Management, Scoring, Session Management |
+| `security` | Audit, Session Management (force revoke), Accreditation |
+| `policy_owner` | Policy management, Approval management, Tier Registry |
+| `auditor` | Audit (read-only), Scoring audit trail |
+| `finops` | Quota management, Cost aggregation (if FinOps module enabled) |
+
+---
+
+## 2. Platform Overview Dashboard
+
+Landing page for all platform-level roles. Data aggregated from `GET /api/v1/admin/health` and related endpoints.
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│  DCM Platform Health                              ● All Systems ✅│
+│  ─────────────────────────────────────────────────────────────  │
+│  Control Plane                    Providers                      │
+│  API Gateway      ✅ pass         Registered:    12             │
+│  Policy Engine    ✅ pass         Healthy:       11             │
+│  Scoring Engine   ✅ pass         Degraded:       1  ⚠️         │
+│  Request Sched.   ✅ pass         Unhealthy:      0             │
+│  Drift Reconciler ✅ pass                                        │
+│                                   Auth Providers                 │
+│  Stores                           Registered:     2             │
+│  Session Store    ✅ pass         Healthy:        2             │
+│  Audit Store      ✅ pass                                        │
+│  ─────────────────────────────────────────────────────────────  │
+│  Pending Approvals: 3 🔔  |  Open Drift Records: 7  |  Orphans: 0│
+└─────────────────────────────────────────────────────────────────┘
+```
+
+Dashboard widgets (configurable per role):
+- Control plane component health grid
+- Provider health summary with degraded/unhealthy callouts
+- Pending approvals count (with link to approval queue)
+- Open drift records by severity
+- Active sessions count
+- Request pipeline throughput (requests/minute, last 1 hour)
+- Scheduled requests queue depth
+
+---
+
+## 3. Tenant Management
+
+**API:** `GET /api/v1/admin/tenants`, `POST /api/v1/admin/tenants`, `POST /api/v1/admin/tenants/{uuid}:suspend`, `POST /api/v1/admin/tenants/{uuid}:reinstate`, `DELETE /api/v1/admin/tenants/{uuid}`
+
+- Tenant list with status, member count, resource count, quota utilization
+- Create tenant form: name, description, initial quota set, initial admin member
+- Tenant detail: members, resource count by type, quota view, active sessions count
+- Suspend / reinstate / decommission with confirmation dialog requiring typed tenant name
+- Tenant audit trail: all admin actions taken on this tenant
+
+---
+
+## 4. Provider Management
+
+**API:** `GET /api/v1/admin/providers`, `GET /api/v1/admin/providers/pending`, `POST /api/v1/admin/providers/{uuid}:approve`, `POST /api/v1/admin/providers/{uuid}:reject`, `POST /api/v1/admin/providers/{uuid}:suspend`
+
+> **Full provider management** (configuration, capacity, entity lists, type-specific management) is in the **[Provider GUI](dcm-provider-gui-spec.md)**. This section covers the admin-level registration approval workflow.
+
+- Pending registrations list: provider type, submitter, submission time, capability declaration summary
+- Registration review: full capability declaration YAML, validation result, automated checks passed/failed
+- Approve / reject with comment (recorded in audit trail)
+- Active providers list: health status, entity count, last health check time
+- Suspend provider: warns if active entities will be affected and shows count
+
+---
+
+## 5. Accreditation Management
+
+**API:** `GET /api/v1/admin/accreditations`, `POST /api/v1/admin/accreditations/{uuid}:approve`, `DELETE /api/v1/admin/accreditations/{uuid}`
+
+- Pending accreditations queue with submission detail
+- Approve / revoke with required comment
+- Accreditation expiry calendar: upcoming renewals in P90D window highlighted
+- Accreditation gap detection: data classifications that require accreditations not currently held
+
+---
+
+## 6. Discovery and Orphan Management
+
+**API:** `POST /api/v1/admin/discovery:trigger`, `GET /api/v1/admin/discovery/jobs/{uuid}`, `GET /api/v1/admin/orphans`, `POST /api/v1/admin/orphans/{uuid}/resolve`
+
+### 6.1 Discovery Console
+
+- Trigger on-demand discovery by resource type and provider
+- Discovery job status with progress (resources scanned, new discoveries, changes detected)
+- Discovery history: recent jobs with outcome summary
+
+### 6.2 Orphan Resolution Queue
+
+- List orphan candidates: resources discovered in provider that have no DCM Realized State record
+- Per-orphan action: Ingest (create Realized State record), Decommission (instruct provider to delete), Ignore (mark as known-unmanaged)
+- Bulk actions for same-type orphans
+
+---
+
+## 7. Quota Management
+
+**API:** `GET /api/v1/admin/tenants/{uuid}/quotas`, `PUT /api/v1/admin/tenants/{uuid}/quotas/{resource_type}`
+
+- Per-tenant quota view: current limits vs current usage vs projected usage
+- Inline edit quota values; save triggers `PUT /api/v1/admin/tenants/{uuid}/quotas/{resource_type}`
+- Quota utilization heatmap across all tenants (who is using the most of what)
+- Quota alert configuration: threshold for "approaching limit" notifications
+
+---
+
+## 8. Scoring Model Administration
+
+**API:** `GET /api/v1/admin/profiles/{name}/scoring`, `PATCH /api/v1/admin/profiles/{name}/scoring`, `POST /api/v1/admin/profiles/{name}/scoring/overrides`, `GET /api/v1/admin/scoring/audit`, `GET /api/v1/admin/actors/{uuid}/risk-history`
+
+### 8.1 Profile Scoring Configuration
+
+- Score threshold table per profile: approval routing tiers vs score ranges
+- Visual slider interface for threshold adjustment (guardrail: auto_approve_below ≤ 50 enforced — slider hard-stops at 50)
+- Signal weight editor: operational_gatekeeper (45%), completeness (15%), actor_risk_history (20%), quota_pressure (10%), provider_accreditation (10%) — weights must sum to 100%
+- Preview: submit a sample request to see the score it would receive under current config
+
+### 8.2 Policy Enforcement Override
+
+- Per-policy enforcement class override: escalate operational → compliance, or demote compliance → operational
+- Required justification field; change recorded in audit trail
+- Shadow policies table with divergence rates — promotes action for high-divergence policies
+
+### 8.3 Actor Risk History
+
+- Search by actor UUID or handle
+- Risk signal history timeline: what events contributed to elevated risk score
+- Manual reset capability (requires `platform_admin` role + comment)
+
+---
+
+## 9. Approval Management
+
+**API:** `GET /api/v1/admin/approvals/pending`, `POST /api/v1/admin/approvals/{uuid}:vote`, `GET /api/v1/admin/approvals/{uuid}`
+
+- All pending approvals across all tenants (Platform Admin view) vs own queue (approver view)
+- Filter by tier (reviewed / verified / authorized), resource type, tenant, age
+- Approval detail: request payload, risk score breakdown, policy evaluation results, existing votes
+- Vote with comment; authorized-tier quorum tracker
+- Expired approvals: review and optionally reopen
+
+---
+
+## 10. Authority Tier Registry
+
+**API:** `POST /api/v1/admin/tier-registry/changes`, `GET /api/v1/admin/tier-registry/changes/{uuid}/impact`, `POST /api/v1/admin/tier-registry/changes/{uuid}:accept-degradation`, `POST /api/v1/admin/tier-registry/changes/{uuid}:activate`
+
+- Current tier registry: ordered list display (auto → reviewed → verified → authorized → [custom tiers])
+- Propose change: drag-and-drop reordering with add/remove custom tier
+- Impact report: automatically fetched after proposal; displays SECURITY_DEGRADATION (red), BROKEN_REFERENCE (orange), PROFILE_GAP (yellow), SECURITY_UPGRADE (green)
+- Degradation acceptance: per-item accept flow with required compensating control rationale
+- Activate button disabled until all blocking items are resolved
+
+---
+
+## 11. Audit and Compliance
+
+**API:** `GET /api/v1/audit/...` (admin-scoped, cross-tenant)
+
+Visible to `auditor` and `platform_admin` roles.
+
+- **Platform-wide audit trail**: all DCM actions across all tenants; filterable by actor, tenant, resource type, operation, date range
+- **Compliance reports**: pre-built reports for common frameworks (SOC 2 Type II, FedRAMP, HIPAA) — export to PDF/CSV
+- **Audit chain integrity status**: last verification timestamp; trigger re-verification; alert on chain break
+- **Cross-tenant correlation**: enter correlation ID to trace a request end-to-end across tenants and providers
+
+---
+
+## 12. Session and Security Management
+
+**API:** `GET /api/v1/admin/actors/{uuid}/...`, `POST /api/v1/admin/actors/{uuid}:revoke-sessions`
+
+Visible to `security` and `platform_admin` roles.
+
+- **Active sessions**: all active sessions across all actors; filterable by auth provider, role, tenant
+- **Force revoke**: select one or all sessions for an actor; requires reason (logged to audit)
+- **Security events**: real-time feed of auth.security_session_revoked and ICOM_UNAUTHORIZED_SOURCE events
+- **Internal component certificates**: table of component cert expiry dates; alert on certs expiring within P14D; ICOM-006 compliance view
+
+---
+
+## 13. Health and Operations
+
+**API:** `GET /api/v1/admin/health`, `GET /api/v1/admin/discovery:trigger`, `POST /api/v1/admin/search-index:rebuild`
+
+Visible to `sre` and `platform_admin` roles.
+
+- Full component health detail (Section 2 dashboard expanded view)
+- Prometheus metrics viewer (embedded Grafana or linked)
+- Manual operations: trigger discovery, rebuild search index, rotate bootstrap credential
+- Deployment info: DCM version, instance UUID, profile, uptime
+- **Runbook links**: direct links to doc 41 (Operational Reference) scenarios from health page
+
+---
+
+## 14. Flow GUI Integration
+
+The Flow GUI (policy authoring tool — separate spec) is accessible to actors with `policy_owner` or `sre` roles:
+
+- **Link** from the Admin Panel navigation to the Flow GUI
+- **Embedded iframe** option for deployments that want a unified navigation experience
+- Active profile and policy summary widgets from Flow GUI embeddable on the Admin dashboard
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/specifications/dcm-consumer-api.yaml b/content/docs/architecture/specifications/dcm-consumer-api.yaml
new file mode 100644
index 0000000..3770e2a
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-consumer-api.yaml
@@ -0,0 +1,2301 @@
+openapi: 3.1.0
+info:
+  title: DCM Consumer API
+  version: 1.0.0
+  description: 'The DCM Consumer API provides the interface used by application teams,
+    Tenant owners,
+
+    and automated tooling to interact with the DCM control plane. All interactions
+    are
+
+    authenticated, Tenant-scoped, and governed by the Policy Engine.
+
+
+    **Key principles:**
+
+    - All requests require a valid session token (Bearer) obtained via `/api/v1/auth/token`
+
+    - All responses are Tenant-scoped — actors only see entities they are authorized
+    to see
+
+    - Policy denials return 403 with a `rule_uuid` identifying the governing rule
+
+    - All mutating operations produce an audit record
+
+    - List endpoints support cursor-based pagination via `page_size` and `page_token`
+    query parameters
+
+
+    **API Versioning:** DCM uses URL path versioning (`/api/v1/`). Version discovery
+    is available
+
+    at `/.well-known/dcm-api-versions`. See `34-api-versioning-strategy.md`.
+
+
+
+
+    **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions:
+
+    custom methods use colon syntax (`POST /resources/{name}:suspend`),
+
+    async operations return an `Operation` resource (AEP-136 LRO),
+
+    and list pagination uses `page_size`/`page_token` parameters.
+
+    '
+  contact:
+    name: DCM Project
+    url: https://github.com/dcm-project
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0
+servers:
+- url: https://{dcm-host}/
+  description: DCM Control Plane
+  variables:
+    dcm-host:
+      description: Hostname of the DCM control plane deployment
+      default: dcm.example.com
+security:
+- BearerAuth: []
+tags:
+- name: discovery
+  description: API version discovery and migration guides
+- name: authentication
+  description: Session management, token issuance, and introspection
+- name: catalog
+  description: Service catalog browsing and search
+- name: requests
+  description: Service request submission and lifecycle
+- name: resources
+  description: Resource entity management and lifecycle operations
+- name: drift
+  description: Drift detection, acknowledgement, and remediation
+- name: groups
+  description: Resource group management
+- name: approvals
+  description: Approval workflow for pending decisions
+- name: cost
+  description: Cost estimation and attribution
+- name: notifications
+  description: Notification inbox management
+- name: webhooks
+  description: Outbound webhook subscription management
+- name: search
+  description: Cross-entity search
+- name: audit
+  description: Audit trail access
+- name: contributions
+  description: Federated contribution submission (policies, resource groups)
+- name: credentials
+  description: Credential retrieval and rotation
+paths:
+  /.well-known/dcm-api-versions:
+    get:
+      tags:
+      - discovery
+      operationId: getApiVersions
+      summary: List supported API versions
+      security: []
+      responses:
+        '200':
+          description: Supported API versions and deprecation status
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  versions:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        version:
+                          type: string
+                        status:
+                          type: string
+                          enum:
+                          - current
+                          - supported
+                          - deprecated
+                          - sunset
+                        sunset_date:
+                          type: string
+                          format: date
+  /api/v1/migration-guide:
+    get:
+      tags:
+      - discovery
+      operationId: getMigrationGuide
+      summary: Get migration guide for API version transitions
+      security: []
+      parameters:
+      - name: from
+        in: query
+        required: true
+        schema:
+          type: string
+          example: '1'
+      - name: to
+        in: query
+        required: true
+        schema:
+          type: string
+          example: '2'
+      responses:
+        '200':
+          description: Migration guide document
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  from_version:
+                    type: string
+                  to_version:
+                    type: string
+                  breaking_changes:
+                    type: array
+                    items:
+                      type: object
+                  guidance:
+                    type: string
+  /api/v1/auth/token:
+    post:
+      tags:
+      - authentication
+      operationId: createSession
+      summary: Authenticate and obtain a session token
+      security: []
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - grant_type
+              properties:
+                grant_type:
+                  type: string
+                  enum:
+                  - password
+                  - client_credentials
+                  - oidc_code
+                username:
+                  type: string
+                password:
+                  type: string
+                  format: password
+                client_id:
+                  type: string
+                client_secret:
+                  type: string
+                  format: password
+                code:
+                  type: string
+                redirect_uri:
+                  type: string
+                  format: uri
+      responses:
+        '200':
+          description: Session token issued
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SessionTokenResponse'
+        '401':
+          $ref: '#/components/responses/Unauthorized'
+        '403':
+          $ref: '#/components/responses/Forbidden'
+  /api/v1/auth/sessions:
+    get:
+      tags:
+      - authentication
+      operationId: listSessions
+      summary: List active sessions for the authenticated actor
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  sessions:
+                    type: array
+                    items:
+                      $ref: '#/components/schemas/SessionSummary'
+      description: List active sessions
+    delete:
+      tags:
+      - authentication
+      operationId: revokeAllSessions
+      summary: Revoke all sessions for the authenticated actor (except the current
+        session)
+      responses:
+        '204':
+          description: All other sessions revoked
+  /api/v1/auth/sessions/{session_uuid}:
+    delete:
+      tags:
+      - authentication
+      operationId: revokeSession
+      summary: Revoke a specific session
+      parameters:
+      - $ref: '#/components/parameters/session_uuid'
+      responses:
+        '204':
+          description: Session revoked
+        '404':
+          $ref: '#/components/responses/NotFound'
+  /api/v1/auth:introspect:
+    post:
+      tags:
+      - authentication
+      operationId: introspectToken
+      summary: Introspect a token and return actor identity and roles
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - token
+              properties:
+                token:
+                  type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/TokenIntrospection'
+        '401':
+          $ref: '#/components/responses/Unauthorized'
+  /api/v1/auth/session:
+    delete:
+      tags:
+      - auth
+      operationId: logoutCurrentSession
+      summary: Logout current session
+      description: Terminates the calling actor's current session and invalidates
+        the bearer token.
+      security:
+      - bearerAuth: []
+      responses:
+        '204':
+          description: Session terminated
+        '401':
+          $ref: '#/components/responses/Unauthorized'
+  /api/v1/catalog:
+    get:
+      tags:
+      - catalog
+      operationId: listCatalogItems
+      summary: List catalog items available to the authenticated actor (RBAC-filtered)
+      parameters:
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      - name: category
+        in: query
+        schema:
+          type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CatalogItemList'
+  /api/v1/catalog/{catalog_item_uuid}:
+    get:
+      tags:
+      - catalog
+      operationId: getCatalogItem
+      summary: Get full schema and details for a catalog item including field constraints
+      parameters:
+      - $ref: '#/components/parameters/catalog_item_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CatalogItem'
+        '404':
+          $ref: '#/components/responses/NotFound'
+  /api/v1/catalog/search:
+    get:
+      tags:
+      - catalog
+      operationId: searchCatalog
+      summary: Search catalog by keyword, resource type, or tag
+      parameters:
+      - name: q
+        in: query
+        required: true
+        schema:
+          type: string
+          minLength: 1
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CatalogItemList'
+  /api/v1/requests:
+    post:
+      tags:
+      - requests
+      operationId: submitRequest
+      summary: Submit a service request
+      description: 'Submits a resource request. The request is stored as an Intent
+        State artifact, processed
+
+        through layer assembly and policy evaluation, placed with a provider, and
+        dispatched.
+
+        Returns immediately with a request_uuid for status polling or SSE streaming.
+
+        '
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/ServiceRequest'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+        '400':
+          $ref: '#/components/responses/BadRequest'
+        '403':
+          $ref: '#/components/responses/Forbidden'
+        '422':
+          $ref: '#/components/responses/UnprocessableEntity'
+    get:
+      tags:
+      - requests
+      operationId: listRequests
+      summary: List requests submitted by the authenticated actor
+      parameters:
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      - name: status
+        in: query
+        schema:
+          type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/RequestList'
+  /api/v1/requests/{request_uuid}/status:
+    get:
+      tags:
+      - requests
+      operationId: getRequestStatus
+      summary: Poll request status and pipeline stage
+      parameters:
+      - $ref: '#/components/parameters/request_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/RequestStatus'
+        '404':
+          $ref: '#/components/responses/NotFound'
+  /api/v1/requests/{request_uuid}/stream:
+    get:
+      tags:
+      - requests
+      operationId: streamRequestStatus
+      summary: Stream real-time request status updates (Server-Sent Events)
+      parameters:
+      - $ref: '#/components/parameters/request_uuid'
+      responses:
+        '200':
+          description: SSE stream of RequestStatus events
+          content:
+            text/event-stream:
+              schema:
+                type: string
+  /api/v1/requests/{request_uuid}:
+    delete:
+      tags:
+      - requests
+      operationId: cancelRequest
+      summary: Cancel a pending request (before provider dispatch)
+      parameters:
+      - $ref: '#/components/parameters/request_uuid'
+      responses:
+        '204':
+          description: Request cancelled
+        '409':
+          description: Request already dispatched — cannot cancel
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /api/v1/request-groups:
+    post:
+      tags:
+      - requests
+      operationId: createRequestGroup
+      summary: Submit a group of related requests (bulk or dependency-ordered)
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/RequestGroup'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+  /api/v1/request-groups/{group_uuid}:
+    get:
+      tags:
+      - requests
+      operationId: getRequestGroup
+      summary: Get status of all requests in a group
+      parameters:
+      - $ref: '#/components/parameters/group_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/RequestGroupStatus'
+  /api/v1/resources:
+    get:
+      tags:
+      - resources
+      operationId: listResources
+      summary: List resources owned by the authenticated actor's Tenant
+      parameters:
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      - name: resource_type
+        in: query
+        schema:
+          type: string
+      - name: lifecycle_state
+        in: query
+        schema:
+          type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ResourceList'
+  /api/v1/resources/{entity_uuid}:
+    get:
+      tags:
+      - resources
+      operationId: getResource
+      summary: Get full entity detail for a resource
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ResourceEntity'
+        '404':
+          $ref: '#/components/responses/NotFound'
+    patch:
+      tags:
+      - resources
+      operationId: updateResource
+      summary: Update editable fields on a realized resource (delta only)
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/ResourceUpdate'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+        '400':
+          $ref: '#/components/responses/BadRequest'
+        '403':
+          $ref: '#/components/responses/Forbidden'
+    delete:
+      tags:
+      - resources
+      operationId: decommissionResource
+      summary: Initiate resource decommission
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+        '409':
+          description: Resource has active dependents — cannot decommission
+  /api/v1/resources/{entity_uuid}:suspend:
+    post:
+      tags:
+      - resources
+      operationId: suspendResource
+      summary: Suspend an OPERATIONAL resource
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+        '409':
+          description: Resource not in OPERATIONAL state
+  /api/v1/resources/{entity_uuid}:resume:
+    post:
+      tags:
+      - resources
+      operationId: resumeResource
+      summary: Resume a SUSPENDED resource
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+        '409':
+          description: Resource not in SUSPENDED state
+  /api/v1/resources/{entity_uuid}:rehydrate:
+    post:
+      tags:
+      - resources
+      operationId: rehydrateResource
+      summary: Rehydrate a resource to a new provider or context
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/RehydrateRequest'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+        '403':
+          $ref: '#/components/responses/Forbidden'
+  /api/v1/resources/{entity_uuid}:extend-ttl:
+    post:
+      tags:
+      - resources
+      operationId: extendTtl
+      summary: Extend or modify the TTL of a resource
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - new_ttl
+              properties:
+                new_ttl:
+                  type: string
+                  description: ISO 8601 duration or datetime
+      responses:
+        '200':
+          description: TTL updated
+  /api/v1/resources/expiring:
+    get:
+      tags:
+      - resources
+      operationId: listExpiringResources
+      summary: List resources expiring within a time window
+      parameters:
+      - name: within
+        in: query
+        schema:
+          type: string
+          description: ISO 8601 duration (e.g., P30D)
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ResourceList'
+  /api/v1/resources/{entity_uuid}:transfer:
+    post:
+      tags:
+      - resources
+      operationId: initiateOwnershipTransfer
+      summary: Initiate ownership transfer to another Tenant
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - target_tenant_uuid
+              - reason
+              properties:
+                target_tenant_uuid:
+                  type: string
+                  format: uuid
+                reason:
+                  type: string
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+  /api/v1/resources/transfers/{transfer_uuid}:accept:
+    post:
+      tags:
+      - resources
+      operationId: acceptOwnershipTransfer
+      summary: Accept an incoming ownership transfer
+      parameters:
+      - name: transfer_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Transfer accepted; ownership updated
+  /api/v1/resources/transfers/{transfer_uuid}:reject:
+    post:
+      tags:
+      - resources
+      operationId: rejectOwnershipTransfer
+      summary: Reject an incoming ownership transfer
+      parameters:
+      - name: transfer_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Transfer rejected
+  /api/v1/resources:bulk-decommission:
+    post:
+      tags:
+      - resources
+      operationId: bulkDecommission
+      summary: Decommission multiple resources in dependency-safe order
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - entity_uuids
+              properties:
+                entity_uuids:
+                  type: array
+                  items:
+                    type: string
+                    format: uuid
+                  minItems: 1
+                dry_run:
+                  type: boolean
+                  default: false
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+  /api/v1/resources/{entity_uuid}/relationships:
+    get:
+      tags:
+      - resources
+      operationId: getResourceRelationships
+      summary: Get all relationships for a resource entity
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  relationships:
+                    type: array
+                    items:
+                      type: object
+  /api/v1/resources/{entity_uuid}/provider-notifications:
+    get:
+      tags:
+      - resources
+      operationId: getProviderNotifications
+      summary: List pending provider-initiated state change notifications requiring
+        approval
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  notifications:
+                    type: array
+                    items:
+                      type: object
+  /api/v1/resources/{entity_uuid}/provider-notifications/{notification_uuid}:approve:
+    post:
+      tags:
+      - resources
+      operationId: approveProviderNotification
+      summary: Approve a provider-initiated state change
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      - name: notification_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Provider notification approved
+  /api/v1/resources/{entity_uuid}/recovery-decisions:
+    get:
+      tags:
+      - resources
+      operationId: getRecoveryDecisions
+      summary: List pending recovery decisions for a resource (for notify_and_wait
+        policies)
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  decisions:
+                    type: array
+                    items:
+                      type: object
+  /api/v1/resources/{entity_uuid}/recovery-decisions/{recovery_decision_uuid}:
+    post:
+      tags:
+      - resources
+      operationId: resolveRecoveryDecision
+      summary: Resolve a pending recovery decision
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      - name: recovery_decision_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - decision
+              properties:
+                decision:
+                  type: string
+                  enum:
+                  - approve
+                  - reject
+                  - escalate
+                reason:
+                  type: string
+      responses:
+        '200':
+          description: Decision recorded
+  /api/v1/resources/{entity_uuid}/audit:
+    get:
+      tags:
+      - audit
+      operationId: getResourceAudit
+      summary: Get audit trail for a resource entity
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/AuditList'
+  /api/v1/resources/{entity_uuid}/drift:
+    get:
+      tags:
+      - drift
+      operationId: getResourceDrift
+      summary: Get current drift records for a resource
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DriftRecordList'
+  /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:acknowledge:
+    post:
+      tags:
+      - drift
+      operationId: acknowledgeDrift
+      summary: Acknowledge a drift record (suppress notification without resolving)
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      - name: drift_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Drift acknowledged
+  /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:accept:
+    post:
+      tags:
+      - drift
+      operationId: acceptDrift
+      summary: Accept the drifted state as the new intended state (update Requested
+        State)
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      - name: drift_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Drifted state accepted; Requested State updated
+  /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:revert:
+    post:
+      tags:
+      - drift
+      operationId: revertDrift
+      summary: Revert the resource to its Requested State (dispatch remediation)
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      - name: drift_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+  /api/v1/groups:
+    get:
+      tags:
+      - groups
+      operationId: listGroups
+      summary: List groups the authenticated actor's Tenant has access to
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  groups:
+                    type: array
+                    items:
+                      type: object
+  /api/v1/groups/{group_uuid}:
+    get:
+      tags:
+      - groups
+      operationId: getGroup
+      summary: Get group detail and member list
+      parameters:
+      - name: group_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+  /api/v1/groups/{group_uuid}/members:
+    post:
+      tags:
+      - groups
+      operationId: addGroupMember
+      summary: Add an entity to a group
+      parameters:
+      - name: group_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - entity_uuid
+              properties:
+                entity_uuid:
+                  type: string
+                  format: uuid
+      responses:
+        '201':
+          description: Entity added to group
+  /api/v1/groups/{group_uuid}/members/{entity_uuid}:
+    delete:
+      tags:
+      - groups
+      operationId: removeGroupMember
+      summary: Remove an entity from a group
+      parameters:
+      - name: group_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '204':
+          description: Entity removed from group
+  /api/v1/approvals/pending:
+    get:
+      tags:
+      - approvals
+      operationId: listPendingApprovals
+      summary: List approval decisions pending the authenticated actor's action
+      parameters:
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  approvals:
+                    type: array
+                    items:
+                      $ref: '#/components/schemas/ApprovalRecord'
+  /api/v1/approvals/{approval_uuid}:
+    post:
+      tags:
+      - approvals
+      operationId: recordApprovalDecision
+      summary: Record an approval decision (approve/reject/abstain)
+      description: 'Designed to be called by both humans via the UI and by external
+        systems (ITSM, Slack bots,
+
+        CI/CD pipelines) that have been authorized to record decisions on behalf of
+        the organization.
+
+        DCM enforces the gate and records the audit trail; the deliberation process
+        is the
+
+        organization''s responsibility.
+
+        '
+      parameters:
+      - name: approval_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+              - decision
+              - reason
+              properties:
+                decision:
+                  type: string
+                  enum:
+                  - approve
+                  - reject
+                  - abstain
+                reason:
+                  type: string
+                  minLength: 1
+                external_reference:
+                  type: string
+                  description: ITSM ticket ID, Jira issue key, etc.
+      responses:
+        '200':
+          description: Decision recorded; approval gate re-evaluated
+        '403':
+          $ref: '#/components/responses/Forbidden'
+        '409':
+          description: Approval already in terminal state
+  /api/v1/cost/estimate:
+    post:
+      tags:
+      - cost
+      operationId: estimateCost
+      summary: Get cost estimate before submitting a request
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/ServiceRequest'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CostEstimate'
+  /api/v1/resources/{entity_uuid}/cost:
+    get:
+      tags:
+      - cost
+      operationId: getResourceCost
+      summary: Get cost actuals and attribution for a resource
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CostActuals'
+  /api/v1/operations/{operation_uuid}:
+    get:
+      tags:
+      - operations
+      operationId: getOperation
+      summary: Poll a Long-Running Operation for status (AEP-136)
+      description: 'Returns the current state of an async operation. Poll until `done`
+        is `true`.
+
+
+        **Note:** `operation_uuid == request_uuid`. All async operations initiated
+        via
+
+        POST /api/v1/requests, PATCH /api/v1/resources/{uuid}, etc., return an Operation
+
+        whose UUID is the same as the request UUID.
+
+
+        Two polling views are available:
+
+        - This endpoint: AEP-standard (done, metadata, response/error)
+
+        - GET /api/v1/requests/{uuid}/status: DCM-native rich view (pipeline_stage,
+        full history)
+
+
+        Both reflect the same underlying operation state.
+
+        '
+      parameters:
+      - name: operation_uuid
+        in: path
+        required: true
+        description: The operation UUID (same as the request UUID)
+        schema:
+          type: string
+          format: uuid
+      security:
+      - bearerAuth: []
+      responses:
+        '200':
+          description: Operation status (check `done` field for completion)
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+        '404':
+          $ref: '#/components/responses/NotFound'
+        '401':
+          $ref: '#/components/responses/Unauthorized'
+        '403':
+          $ref: '#/components/responses/Forbidden'
+  /api/v1/quota:
+    get:
+      tags:
+      - cost
+      operationId: getQuota
+      summary: Get quota status for the authenticated actor's Tenant
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/QuotaStatus'
+  /api/v1/notifications:
+    get:
+      tags:
+      - notifications
+      operationId: listNotifications
+      summary: List notifications in the authenticated actor's inbox
+      parameters:
+      - name: unread_only
+        in: query
+        schema:
+          type: boolean
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  notifications:
+                    type: array
+                    items:
+                      type: object
+  /api/v1/notifications/{notification_uuid}/read:
+    post:
+      tags:
+      - notifications
+      operationId: markNotificationRead
+      summary: Mark a notification as read
+      parameters:
+      - name: notification_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Marked as read
+  /api/v1/notifications:read-all:
+    post:
+      tags:
+      - notifications
+      operationId: markAllNotificationsRead
+      summary: Mark all notifications as read
+      responses:
+        '204':
+          description: All notifications marked as read
+  /api/v1/webhooks:
+    get:
+      tags:
+      - webhooks
+      operationId: listWebhooks
+      summary: List outbound webhook subscriptions for the authenticated actor
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  webhooks:
+                    type: array
+                    items:
+                      $ref: '#/components/schemas/WebhookSubscription'
+    post:
+      tags:
+      - webhooks
+      operationId: createWebhook
+      summary: Create an outbound webhook subscription
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WebhookSubscription'
+      responses:
+        '201':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WebhookSubscription'
+  /api/v1/webhooks/{webhook_uuid}:
+    delete:
+      tags:
+      - webhooks
+      operationId: deleteWebhook
+      summary: Delete a webhook subscription
+      parameters:
+      - name: webhook_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '204':
+          description: Webhook deleted
+  # ── Workload Analysis ─────────────────────────────────────────────────────────
+  /api/v1/resources/{entity_uuid}/workload-profile:
+    get:
+      tags: [workload-analysis]
+      operationId: getWorkloadProfile
+      summary: Get the Workload Analysis profile for a resource
+      description: |
+        Returns the most recent WorkloadProfile entity for the given resource.
+        WorkloadProfiles are created automatically during brownfield ingestion
+        and can be refreshed on demand via the :analyze custom method.
+      parameters:
+        - {name: entity_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      responses:
+        "200":
+          description: Workload profile
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/WorkloadProfile"}
+        "404": {$ref: "#/components/responses/NotFound"}
+        "403": {$ref: "#/components/responses/Forbidden"}
+
+  /api/v1/resources/{entity_uuid}/workload-profile:analyze:
+    post:
+      tags: [workload-analysis]
+      operationId: analyzeWorkload
+      summary: Trigger re-analysis of a resource's workload profile
+      description: |
+        Initiates a new Workload Analysis pass for the resource. The existing
+        WorkloadProfile is superseded when the new analysis completes. Useful
+        when a resource's role has changed since initial classification.
+      parameters:
+        - {name: entity_uuid, in: path, required: true, schema: {type: string, format: uuid}}
+      security: [{bearerAuth: []}]
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [reason]
+              properties:
+                reason: {type: string, description: Why re-analysis is needed}
+                include_mta: {type: boolean, default: true, description: Include MTA containerization assessment}
+      responses:
+        "200":
+          description: Operation initiated — poll for completion
+          content:
+            application/json:
+              schema: {$ref: "#/components/schemas/Operation"}
+        "404": {$ref: "#/components/responses/NotFound"}
+
+  /api/v1/search:
+    get:
+      tags:
+      - search
+      operationId: search
+      summary: Cross-entity search (resources, groups, catalog items)
+      parameters:
+      - name: q
+        in: query
+        required: true
+        schema:
+          type: string
+          minLength: 1
+      - name: types
+        in: query
+        schema:
+          type: array
+          items:
+            type: string
+        style: form
+        explode: false
+      - $ref: '#/components/parameters/page_size'
+      - $ref: '#/components/parameters/page_token'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SearchResults'
+  /api/v1/audit/correlation/{correlation_id}:
+    get:
+      tags:
+      - audit
+      operationId: getAuditByCorrelation
+      summary: Get all audit records for a correlation ID (cross-resource trace)
+      parameters:
+      - name: correlation_id
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/AuditList'
+  /api/v1/contribute/policy:
+    post:
+      tags:
+      - contributions
+      operationId: contributePolicy
+      summary: Submit a policy contribution via the federated contribution pipeline
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/PolicyContribution'
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+  /api/v1/contribute/resource-group:
+    post:
+      tags:
+      - contributions
+      operationId: contributeResourceGroup
+      summary: Submit a resource group contribution
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+  /api/v1/contribute:
+    get:
+      tags:
+      - contributions
+      operationId: listContributions
+      summary: List contributions submitted by the authenticated actor
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  contributions:
+                    type: array
+                    items:
+                      type: object
+  /api/v1/contribute/{contribution_uuid}:
+    delete:
+      tags:
+      - contributions
+      operationId: withdrawContribution
+      summary: Withdraw a pending contribution
+      parameters:
+      - name: contribution_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '204':
+          description: Contribution withdrawn
+        '409':
+          description: Contribution already activated — cannot withdraw
+  /api/v1/resources/{entity_uuid}/credentials:
+    get:
+      tags:
+      - credentials
+      operationId: listResourceCredentials
+      summary: List credentials associated with a resource (metadata only — no values)
+      parameters:
+      - $ref: '#/components/parameters/entity_uuid'
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  credentials:
+                    type: array
+                    items:
+                      $ref: '#/components/schemas/CredentialSummary'
+  /api/v1/credentials/{credential_uuid}/value:
+    get:
+      tags:
+      - credentials
+      operationId: getCredentialValue
+      summary: Retrieve the current credential value (audited; step-up auth may be
+        required)
+      parameters:
+      - name: credential_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CredentialValue'
+        '403':
+          $ref: '#/components/responses/Forbidden'
+  /api/v1/credentials/{credential_uuid}:rotate:
+    post:
+      tags:
+      - credentials
+      operationId: rotateCredential
+      summary: Initiate credential rotation
+      parameters:
+      - name: credential_uuid
+        in: path
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        '200':
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Operation'
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
+      description: Session token obtained from POST /api/v1/auth/token
+  parameters:
+    entity_uuid:
+      name: entity_uuid
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+      description: UUID of the resource entity
+    request_uuid:
+      name: request_uuid
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+    catalog_item_uuid:
+      name: catalog_item_uuid
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+    group_uuid:
+      name: group_uuid
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+    session_uuid:
+      name: session_uuid
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+    limit:
+      name: page_size
+      in: query
+      schema:
+        type: integer
+        minimum: 1
+        maximum: 1000
+        default: 50
+    cursor:
+      name: page_token
+      in: query
+      schema:
+        type: string
+      description: Opaque page token from previous response (use next_page_token from
+        response)
+  responses:
+    BadRequest:
+      description: Invalid request syntax
+      content:
+        application/json:
+          schema:
+            $ref: '#/components/schemas/Error'
+    Unauthorized:
+      description: Missing or invalid authentication token
+      content:
+        application/json:
+          schema:
+            $ref: '#/components/schemas/Error'
+    Forbidden:
+      description: Authenticated but not authorized. rule_uuid identifies the governing
+        policy.
+      content:
+        application/json:
+          schema:
+            $ref: '#/components/schemas/Error'
+    NotFound:
+      description: Resource not found or not visible to authenticated actor
+      content:
+        application/json:
+          schema:
+            $ref: '#/components/schemas/Error'
+    UnprocessableEntity:
+      description: Request syntax valid but semantically invalid (e.g., field validation
+        failure)
+      content:
+        application/json:
+          schema:
+            $ref: '#/components/schemas/Error'
+  schemas:
+    Operation:
+      type: object
+      description: "AEP-136 Long-Running Operation. Returned by async operations instead\
+        \ of 202 Accepted.\nPoll GET {operation.name} until done is true.\nThe operation.name\
+        \ is a stable resource path: /api/v1/operations/{uuid}\nNote: operation_uuid\
+        \ == request_uuid. Two polling endpoints are available:\n  - GET /api/v1/operations/{uuid}\
+        \ — AEP-standard thin view (done, metadata, response/error)\n  - GET /api/v1/requests/{uuid}/status\
+        \ — DCM-native rich view (pipeline_stage, full status history)\nBoth endpoints\
+        \ reflect the same underlying operation state.\n"
+      required:
+      - name
+      - done
+      additionalProperties: false
+      properties:
+        name:
+          type: string
+          description: Stable resource path for this operation. Poll this URL for
+            status.
+          example: /api/v1/operations/a1b2c3d4-e5f6-7890-abcd-ef1234567890
+        done:
+          type: boolean
+          description: True when the operation has reached a terminal state (success
+            or error)
+          default: false
+        metadata:
+          type: object
+          description: Operation-specific progress metadata
+          additionalProperties: false
+          properties:
+            stage:
+              type: string
+              description: Current pipeline stage
+            progress_pct:
+              type: integer
+              minimum: 0
+              maximum: 100
+            resource_uuid:
+              type: string
+              format: uuid
+              description: UUID of the resource being created/modified (set as soon
+                as assigned)
+            request_uuid:
+              type: string
+              format: uuid
+              description: The DCM request UUID. operation_uuid == request_uuid. Use
+                GET /api/v1/requests/{request_uuid}/status for the full DCM-native
+                pipeline view.
+            created_at:
+              type: string
+              format: date-time
+            updated_at:
+              type: string
+              format: date-time
+        response:
+          type: object
+          description: Present when done is true and the operation succeeded. Contains
+            the result resource.
+          additionalProperties: true
+        error:
+          type: object
+          description: Present when done is true and the operation failed.
+          additionalProperties: false
+          properties:
+            code:
+              type: string
+            message:
+              type: string
+            details:
+              type: array
+              items:
+                type: object
+    Error:
+      type: object
+      required:
+      - error
+      properties:
+        error:
+          type: object
+          required:
+          - code
+          - message
+          - request_id
+          properties:
+            code:
+              type: string
+            message:
+              type: string
+            request_id:
+              type: string
+              format: uuid
+            rule_uuid:
+              type: string
+              format: uuid
+              description: Present on POLICY_DENIED and GOVERNANCE_DENIED
+            fields:
+              type: array
+              items:
+                type: object
+                properties:
+                  field:
+                    type: string
+                  issue:
+                    type: string
+    SessionTokenResponse:
+      type: object
+      required:
+      - access_token
+      - token_type
+      - expires_in
+      - session_uuid
+      properties:
+        access_token:
+          type: string
+        token_type:
+          type: string
+          const: bearer
+        expires_in:
+          type: integer
+          description: Seconds until expiry
+        session_uuid:
+          type: string
+          format: uuid
+        refresh_token:
+          type: string
+    SessionSummary:
+      type: object
+      properties:
+        session_uuid:
+          type: string
+          format: uuid
+        created_at:
+          type: string
+          format: date-time
+        expires_at:
+          type: string
+          format: date-time
+        ip_address:
+          type: string
+        user_agent:
+          type: string
+        current:
+          type: boolean
+    TokenIntrospection:
+      type: object
+      properties:
+        active:
+          type: boolean
+        actor_uuid:
+          type: string
+          format: uuid
+        actor_type:
+          type: string
+        display_name:
+          type: string
+        tenant_uuids:
+          type: array
+          items:
+            type: string
+            format: uuid
+        roles:
+          type: array
+          items:
+            type: string
+        groups:
+          type: array
+          items:
+            type: string
+        expires_at:
+          type: string
+          format: date-time
+    CatalogItem:
+      type: object
+      properties:
+        uuid:
+          type: string
+          format: uuid
+        resource_type:
+          description: Resource type — FQN string (e.g., 'Compute.VirtualMachine')
+            or Registry UUID. DCM resolves either form internally.
+          oneOf:
+          - type: string
+            pattern: ^[A-Z][a-zA-Z0-9]+\.[A-Z][a-zA-Z0-9]+$
+            description: FQN form (recommended)
+          - type: string
+            format: uuid
+            description: UUID form
+        display_name:
+          type: string
+        description:
+          type: string
+        version:
+          type: string
+        status:
+          type: string
+        provider_uuid:
+          type: string
+          format: uuid
+        field_schema:
+          type: object
+          description: 'JSON Schema for the request body fields. Each field includes
+            a constraint block with type: range|enum|pattern|layer_reference|layer_reference_list.
+            For layer_reference constraints, allowed_values is resolved at render
+            time from active Reference Data Layer instances of the declared layer_type.'
+        cost_estimate:
+          type: object
+        dependencies:
+          type: array
+          items:
+            type: object
+    CatalogItemList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            $ref: '#/components/schemas/CatalogItem'
+        pagination:
+          type: object
+    ServiceRequest:
+      type: object
+      required:
+      - catalog_item_uuid
+      - fields
+      properties:
+        catalog_item_uuid:
+          type: string
+          format: uuid
+        fields:
+          type: object
+          description: Resource-type-specific field values
+          additionalProperties: true
+        group_uuid:
+          type: string
+          format: uuid
+          description: Assign to a resource group on creation
+        scheduled_at:
+          type: string
+          format: date-time
+          description: Defer request execution to this time
+        depends_on:
+          type: array
+          items:
+            type: string
+            format: uuid
+          description: Request UUIDs that must complete first
+        dry_run:
+          type: boolean
+          default: false
+          description: Evaluate policy and placement without submitting
+    RequestAccepted:
+      type: object
+      properties:
+        request_uuid:
+          type: string
+          format: uuid
+        status:
+          type: string
+        stream_url:
+          type: string
+          format: uri
+    RequestStatus:
+      type: object
+      properties:
+        request_uuid:
+          type: string
+          format: uuid
+        status:
+          type: string
+          enum:
+          - ACKNOWLEDGED
+          - ASSEMBLING
+          - AWAITING_APPROVAL
+          - APPROVED
+          - DISPATCHED
+          - PROVISIONING
+          - COMPLETED
+          - FAILED
+          - CANCELLED
+          - SCHEDULED
+          - PENDING_DEPENDENCY
+          description: "Consumer request lifecycle status. COMPLETED/FAILED/CANCELLED are terminal."
+        pipeline_stage:
+          type: string
+        entity_uuid:
+          type: string
+          format: uuid
+          description: Set once realization begins
+        created_at:
+          type: string
+          format: date-time
+        updated_at:
+          type: string
+          format: date-time
+        error:
+          $ref: '#/components/schemas/Error'
+    RequestList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            $ref: '#/components/schemas/RequestStatus'
+        pagination:
+          type: object
+    RequestGroup:
+      type: object
+      required:
+      - requests
+      properties:
+        requests:
+          type: array
+          items:
+            $ref: '#/components/schemas/ServiceRequest'
+          minItems: 1
+        ordered:
+          type: boolean
+          default: false
+          description: Execute in declared order with dependency waiting
+        group_name:
+          type: string
+    RequestGroupAccepted:
+      type: object
+      properties:
+        group_uuid:
+          type: string
+          format: uuid
+        request_uuids:
+          type: array
+          items:
+            type: string
+            format: uuid
+    RequestGroupStatus:
+      type: object
+      properties:
+        group_uuid:
+          type: string
+          format: uuid
+        status:
+          type: string
+        requests:
+          type: array
+          items:
+            $ref: '#/components/schemas/RequestStatus'
+    ResourceEntity:
+      type: object
+      description: Resource entity as returned by the Consumer API (may be field-filtered
+        by governance matrix)
+      properties:
+        uuid:
+          type: string
+          format: uuid
+        entity_type:
+          type: string
+        resource_type:
+          type: string
+        lifecycle_state:
+          type: string
+        owned_by_tenant_uuid:
+          type: string
+          format: uuid
+        provider_uuid:
+          type: string
+          format: uuid
+        drift_status:
+          type: string
+        billing_state:
+          type: string
+        ttl_expires_at:
+          type: string
+          format: date-time
+        created_at:
+          type: string
+          format: date-time
+        updated_at:
+          type: string
+          format: date-time
+        fields:
+          type: object
+          additionalProperties: true
+          description: Resource-type-specific realized fields
+        relationships:
+          type: array
+          items:
+            type: object
+    ResourceList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            $ref: '#/components/schemas/ResourceEntity'
+        pagination:
+          type: object
+    ResourceUpdate:
+      type: object
+      required:
+      - fields
+      properties:
+        fields:
+          type: object
+          additionalProperties: true
+          description: Editable fields and their new values (delta only)
+        reason:
+          type: string
+    RehydrateRequest:
+      type: object
+      properties:
+        target_provider_uuid:
+          type: string
+          format: uuid
+        reason:
+          type: string
+    DriftRecord:
+      type: object
+      properties:
+        drift_uuid:
+          type: string
+          format: uuid
+        entity_uuid:
+          type: string
+          format: uuid
+        detected_at:
+          type: string
+          format: date-time
+        severity:
+          type: string
+          enum:
+          - minor
+          - significant
+          - critical
+        unsanctioned:
+          type: boolean
+        status:
+          type: string
+          enum:
+          - open
+          - acknowledged
+          - resolved
+          - escalated
+        field_differences:
+          type: array
+          items:
+            type: object
+    DriftRecordList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            $ref: '#/components/schemas/DriftRecord'
+        pagination:
+          type: object
+    ApprovalRecord:
+      type: object
+      properties:
+        approval_uuid:
+          type: string
+          format: uuid
+        subject_type:
+          type: string
+        subject_uuid:
+          type: string
+          format: uuid
+        required_tier:
+          type: string
+        required_quorum:
+          type: integer
+        decisions_so_far:
+          type: integer
+        expires_at:
+          type: string
+          format: date-time
+        created_at:
+          type: string
+          format: date-time
+    CostEstimate:
+      type: object
+      properties:
+        estimated_monthly_cost:
+          type: number
+        currency:
+          type: string
+        breakdown:
+          type: array
+          items:
+            type: object
+        confidence:
+          type: string
+          enum:
+          - high
+          - medium
+          - low
+    CostActuals:
+      type: object
+      properties:
+        entity_uuid:
+          type: string
+          format: uuid
+        period_start:
+          type: string
+          format: date-time
+        period_end:
+          type: string
+          format: date-time
+        total_cost:
+          type: number
+        currency:
+          type: string
+        breakdown:
+          type: array
+          items:
+            type: object
+    QuotaStatus:
+      type: object
+      properties:
+        tenant_uuid:
+          type: string
+          format: uuid
+        quotas:
+          type: array
+          items:
+            type: object
+    WebhookSubscription:
+      type: object
+      required:
+      - endpoint_url
+      - events
+      properties:
+        webhook_uuid:
+          type: string
+          format: uuid
+          readOnly: true
+        endpoint_url:
+          type: string
+          format: uri
+        events:
+          type: array
+          items:
+            type: string
+          description: Event types from the DCM Event Catalog
+        secret:
+          type: string
+          description: HMAC-SHA256 signing secret for signature verification
+        active:
+          type: boolean
+          default: true
+        created_at:
+          type: string
+          format: date-time
+          readOnly: true
+    SearchResults:
+      type: object
+      properties:
+        results:
+          type: array
+          items:
+            type: object
+        pagination:
+          type: object
+    AuditRecord:
+      type: object
+      properties:
+        audit_uuid:
+          type: string
+          format: uuid
+        entity_uuid:
+          type: string
+          format: uuid
+        event_type:
+          type: string
+        actor:
+          type: object
+        recorded_at:
+          type: string
+          format: date-time
+        correlation_id:
+          type: string
+          format: uuid
+        payload:
+          type: object
+    AuditList:
+      type: object
+      properties:
+        items:
+          type: array
+          items:
+            $ref: '#/components/schemas/AuditRecord'
+        pagination:
+          type: object
+    PolicyContribution:
+      type: object
+      required:
+      - policy_artifact
+      properties:
+        policy_artifact:
+          type: object
+          description: DCM policy artifact per B-policy-contract schema
+        rationale:
+          type: string
+        shadow_first:
+          type: boolean
+          default: true
+    ContributionAccepted:
+      type: object
+      properties:
+        contribution_uuid:
+          type: string
+          format: uuid
+        status:
+          type: string
+        shadow_mode:
+          type: boolean
+    CredentialSummary:
+      type: object
+      properties:
+        credential_uuid:
+          type: string
+          format: uuid
+        credential_type:
+          type: string
+        entity_uuid:
+          type: string
+          format: uuid
+        expires_at:
+          type: string
+          format: date-time
+        rotated_at:
+          type: string
+          format: date-time
+    CredentialValue:
+      type: object
+      properties:
+        credential_uuid:
+          type: string
+          format: uuid
+        credential_type:
+          type: string
+        value:
+          type: object
+          description: Credential-type-specific value (e.g., kubeconfig, token, certificate)
+        expires_at:
+          type: string
+          format: date-time
+
+    WorkloadProfile:
+      type: object
+      description: "Workload analysis classification for a DCM resource entity (doc 46)"
+      properties:
+        workload_profile_uuid: {type: string, format: uuid}
+        subject_entity_uuid: {type: string, format: uuid}
+        analyzed_at: {type: string, format: date-time}
+        analysis_version: {type: string}
+        classification:
+          type: object
+          properties:
+            resource_type_match:
+              type: object
+              properties:
+                primary: {type: string}
+                confidence: {type: string, enum: [high, medium, low, undetermined]}
+            workload_archetype:
+              type: object
+              properties:
+                type: {type: string, enum: [web_server, database, batch_processor, message_broker, api_gateway, cache, storage, monitoring, unknown]}
+                confidence: {type: string, enum: [high, medium, low, undetermined]}
+            migration_readiness:
+              type: object
+              properties:
+                containerization_score: {type: integer, minimum: 1, maximum: 10}
+                blockers: {type: array, items: {type: string}}
+                suggested_target: {type: string}
+            lifecycle_recommendation:
+              type: object
+              properties:
+                dcm_lifecycle_model: {type: string}
+                rehydration_eligible: {type: boolean}
+                notes: {type: string}
diff --git a/content/docs/architecture/specifications/dcm-consumer-gui-spec.md b/content/docs/architecture/specifications/dcm-consumer-gui-spec.md
new file mode 100644
index 0000000..278a112
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-consumer-gui-spec.md
@@ -0,0 +1,922 @@
+# DCM Consumer Web GUI Specification
+
+> **AEP Alignment:** Consumer API endpoints referenced in this spec follow [AEP](https://aep.dev) conventions — custom methods use colon syntax, async operations return `Operation` resources, and `operation_uuid == request_uuid`. See `schemas/openapi/dcm-consumer-api.yaml` for the normative specification.
+
+
+**Document Status:** 🔄 In Progress
+**Document Type:** Specification — Consumer Web Interface
+**Related Documents:** [RHDH Integration Specification](dcm-rhdh-integration-spec.md) | [Consumer API Specification](consumer-api-spec.md) | [Admin GUI Specification](dcm-admin-gui-spec.md) | [Provider GUI Specification](dcm-provider-gui-spec.md) | [Flow GUI Specification](dcm-flow-gui-spec.md) | [Auth Providers](../data-model/19-auth-providers.md) | [Session Revocation](../data-model/35-session-revocation.md)
+
+> **Status:** Draft — Ready for implementation feedback
+>
+> **Primary deployment target:** Red Hat Developer Hub (RHDH) or upstream Backstage. The DCM Consumer Portal is implemented as a Backstage plugin suite. The standalone SPA mode is an alternative for deployments that do not run RHDH. See [RHDH Integration Specification](dcm-rhdh-integration-spec.md) for the complete Backstage plugin architecture.
+>
+> **Design goals (in priority order):**
+> 1. **Low time to market** — RHDH brings auth, search, TechDocs, RBAC, GitOps integration, and a full component library pre-built. DCM plugins extend rather than rebuild.
+> 2. **Ease of use** — familiar mental models (PatternFly/OpenShift language); task-oriented navigation; zero-ticket provisioning.
+> 3. **Extensible** — plugin architecture means new resource types surface automatically; no GUI code changes for new catalog items.
+> 4. **Security and governance by design** — tenancy, RBAC, audit, and policy constraints are architectural, not add-ons.
+
+---
+
+## 1. Deployment Models
+
+### 1.1 RHDH / Backstage Mode (Primary)
+
+DCM is implemented as a Backstage plugin suite loaded into an RHDH or Backstage instance. DCM capabilities appear as a first-class section within the existing RHDH navigation.
+
+```
+RHDH Instance
+├── Pre-built RHDH capabilities (Catalog, Create, TechDocs, Search, ...)
+│     └── These work unchanged — DCM augments them
+│
+└── DCM Plugin Suite (loaded as Dynamic Plugins)
+      ├── @dcm/plugin                 frontend plugin — nav + pages + entity tabs
+      ├── @dcm/plugin-backend         backend plugin — API proxy, SSE relay, auth
+      ├── @dcm/plugin-catalog-backend catalog processor + entity provider
+      ├── @dcm/plugin-scaffolder-backend custom scaffolder actions
+      └── @dcm/permission-policy      DCM → Backstage permission bridge
+```
+
+**Why RHDH first:**
+- Auth (RHSSO/Keycloak/OIDC) is already configured — no auth plumbing
+- RBAC plugin already provides no-code permission management
+- Search indexes DCM entities alongside existing catalog entities
+- TechDocs renders DCM data model docs and runbooks in-portal
+- Dynamic Plugins — DCM plugins load without rebuilding the RHDH image (OCI or npm)
+- Existing integrations: ArgoCD/GitOps (layer store visibility), Tekton (scaffolding pipelines), AAP (DCM providers that use Ansible), OCM (cluster management alongside DCM service catalog)
+
+### 1.2 Standalone SPA Mode (Alternative)
+
+For deployments without RHDH, DCM ships a standalone React SPA using PatternFly components. The same plugin modules are used; the host application is a lightweight Backstage-compatible shell rather than full RHDH.
+
+```
+Standalone DCM App
+├── PatternFly Page shell (Header, Sidebar, Content)
+├── Auth: DCM Auth Provider (OIDC/LDAP/built-in)
+└── DCM plugin modules (same packages as RHDH mode)
+```
+
+The standalone mode provides feature parity. RHDH mode is recommended because it provides broader platform capabilities without additional investment.
+
+---
+
+## 2. Navigation Architecture
+
+### 2.1 Design Principles
+
+**Organized by what users want to accomplish, not by API object.** A user who wants to "check on my database VM" thinks "My Resources" — not "Realized State Entity Management." The navigation labels match the user's mental model.
+
+**PatternFly grouped hierarchical left nav.** Following OpenShift console and PatternFly's recommended pattern for administrative interfaces with multiple entity types. Consistent with what Red Hat users already know.
+
+**Stable groups, role-gated items.** Group headings are always visible; items within them hide (not disable) based on the actor's roles. A user who gains the `approver` role sees Approvals appear without any change to the application.
+
+**Tenant context in the header, not in the nav.** Following RHDH's namespace/group context selector pattern — the active tenant is ambient context shown in the masthead, switchable without navigating away.
+
+### 2.2 Navigation Structure
+
+```
+[RHDH Masthead]
+┌──────────────────────────────────────────────────────────────┐
+│ ⬡ Red Hat Developer Hub     [Search]    👤 User  🏢 Tenant  │
+└──────────────────────────────────────────────────────────────┘
+
+[Sidebar — DCM section within RHDH nav]
+━━━━━━━━━━━━━━━━━━
+ DCM                           ← Section header in RHDH sidebar
+━━━━━━━━━━━━━━━━━━
+ 🏪 Service Catalog
+
+ MY WORK                       ← NavGroup (non-clickable group label)
+   📋 Requests
+   🖥  Resources
+   🔗 Dependency Groups
+
+ 🔔 Approvals        [3]       ← NavItem with NotificationBadge (orange, count)
+
+ GOVERNANCE                    ← NavGroup
+   💰 Cost & Quota
+   📤 Contributions            ← hidden if not contributor role
+
+ SETTINGS                      ← NavGroup
+   🔔 Notifications
+   🔐 Sessions
+━━━━━━━━━━━━━━━━━━
+```
+
+**PatternFly components used:**
+- `Nav` with `variant="default"` — left sidebar navigation
+- `NavGroup` — non-clickable group labels (MY WORK, GOVERNANCE, SETTINGS)
+- `NavItem` — clickable navigation items with optional ``
+- `NavItemSeparator` — visual divider between major areas
+- `PageHeader` with `Masthead` — tenant context selector (ContextSelector component)
+
+### 2.3 Tenant Context Selector
+
+The active tenant is displayed in the masthead as a `ContextSelector` (PatternFly):
+
+```
+[Masthead right side]
+  👤 alice@corp.com    🏢 Payments Team ▾
+                            ├── Payments Team      ← current
+                            ├── Platform Team
+                            └── ─────────────
+                                All My Tenants
+```
+
+- Single-tenant actors: selector hidden; tenant name displayed static
+- Multi-tenant actors: dropdown triggers `X-DCM-Tenant` header change; page data refreshes
+- Tenant switch does not navigate; current page re-fetches with new tenant context
+
+### 2.4 Role-Gating Rules
+
+| NavItem | Visible when actor has |
+|---------|----------------------|
+| Service Catalog | Any role (always visible) |
+| Requests | `consumer` or any role |
+| Resources | `consumer` or any role |
+| Dependency Groups | `consumer` or any role |
+| Approvals + badge | `approver` role |
+| Cost & Quota | `consumer` or `tenant_admin` |
+| Contributions | `contributor` role |
+| Notifications | Any role |
+| Sessions | Any role |
+
+Items not shown for a role are **hidden entirely** — no disabled states in the nav.
+
+---
+
+## 3. Service Catalog
+
+**Route:** `/dcm/catalog`  
+**API:** `GET /api/v1/catalog`, `GET /api/v1/catalog/{uuid}`, `GET /api/v1/catalog/search`
+
+### 3.1 In RHDH Mode — Software Templates Integration
+
+In RHDH, DCM catalog items are exposed as **Backstage Software Templates**. The standard RHDH "Create" page becomes the DCM service catalog.
+
+```
+RHDH "Create" page
+└── DCM Templates category
+      ├── 🖥  Standard VM (t-shirt sizes)
+      ├── 🗄  Database — PostgreSQL
+      ├── 🌐  Virtual Network
+      ├── 🔒  TLS Certificate
+      └── 📦  [More DCM catalog items...]
+```
+
+Templates are auto-generated by `@dcm/plugin-catalog-backend` — it reads `GET /api/v1/catalog` and emits one Template entity per DCM catalog item. The template's input schema is derived directly from the DCM catalog item's field schema. No manual template authoring needed when a new resource type appears.
+
+DCM-specific catalog view at `/dcm/catalog` provides additional DCM context:
+- Cost estimate per item (calls `POST /api/v1/cost/estimate` with default values)
+- Availability indicator (quota headroom)
+- Dependency graph preview
+- Provider badge and SLA indicators
+
+### 3.2 Catalog Browser
+
+- **Card grid** — PatternFly `Gallery` with `GalleryItem` cards
+- **Filter toolbar** — PatternFly `Toolbar` with category chips, tag filter, search input
+- Card shows: name, description, provider type badge, cost estimate, availability
+- Quick-request button opens the scaffolder template directly from the card
+
+### 3.3 Catalog Item Detail
+
+- Full description, field schema viewer, dependency declaration, TechDocs link
+- Live cost estimate — updates as user adjusts quantity/size fields (debounced)
+- Quota check: remaining quota for this resource type in active tenant
+- "Request This Service" → opens Scaffolder wizard
+
+---
+
+## 4. Request Submission — Scaffolder Integration
+
+**In RHDH mode:** Request submission uses the Backstage Scaffolder. DCM does not build a separate request form. The Scaffolder is the request form.
+
+### 4.1 Template Structure
+
+Each DCM catalog item generates a Backstage Software Template:
+
+```yaml
+apiVersion: scaffolder.backstage.io/v1beta3
+kind: Template
+metadata:
+  name: dcm-compute-vm-standard
+  title: "Standard VM"
+  description: "Provision a standard virtual machine"
+  annotations:
+    dcm.io/catalog-item-uuid: ""
+    dcm.io/resource-type: "Compute.VirtualMachine"
+  tags: [dcm, compute, infrastructure]
+spec:
+  type: dcm-resource
+  parameters:
+    # Auto-generated from DCM catalog item field schema
+    - title: "Configure Your VM"
+      required: [cpu_count, memory_gb, os_family, name]
+      properties:
+        cpu_count:
+          title: CPU Cores
+          type: integer
+          enum: [2, 4, 8, 16, 32]
+          default: 4
+          ui:widget: select
+        memory_gb:
+          title: Memory (GB)
+          type: integer
+          enum: [8, 16, 32, 64, 128]
+          default: 16
+        name:
+          title: Resource Name
+          type: string
+          pattern: "^[a-z0-9-]{3,63}$"
+          ui:help: "Lowercase letters, numbers, hyphens. 3-63 characters."
+
+    - title: "Scheduling (Optional)"
+      properties:
+        dispatch:
+          title: When to provision
+          type: string
+          enum: [immediate, at, window]
+          default: immediate
+        not_before:
+          title: Not before (UTC)
+          type: string
+          format: date-time
+          ui:widget: datetime
+          ui:if: "dispatch === 'at'"
+        window_id:
+          title: Maintenance Window
+          type: string
+          ui:field: dcm:MaintenanceWindowPicker
+          ui:if: "dispatch === 'window'"
+
+    - title: "Review"
+      # Auto-populated review step showing cost estimate and policy pre-check
+
+  steps:
+    - id: dcm-cost-estimate
+      name: "Estimate cost"
+      action: dcm:request:estimate
+      input:
+        catalogItemUuid: "${{ parameters.catalog_item_uuid }}"
+        fields: "${{ parameters }}"
+
+    - id: dcm-submit
+      name: "Submit request"
+      action: dcm:request:submit
+      input:
+        catalogItemUuid: "${{ parameters.catalog_item_uuid }}"
+        fields: "${{ parameters }}"
+        schedule:
+          dispatch: "${{ parameters.dispatch }}"
+          notBefore: "${{ parameters.not_before }}"
+
+    - id: dcm-wait
+      name: "Waiting for provisioning..."
+      action: dcm:request:wait
+      input:
+        requestUuid: "${{ steps.dcm-submit.output.requestUuid }}"
+        timeoutMinutes: 30
+
+    - id: register
+      name: "Register in catalog"
+      action: dcm:catalog:refresh
+      input:
+        entityUuid: "${{ steps.dcm-submit.output.entityUuid }}"
+
+  output:
+    links:
+      - title: "View Resource"
+        url: "${{ steps.dcm-wait.output.entityUrl }}"
+      - title: "View Request"
+        url: "${{ steps.dcm-submit.output.requestUrl }}"
+```
+
+### 4.2 Custom Scaffolder Actions
+
+Provided by `@dcm/plugin-scaffolder-backend`:
+
+| Action | Description |
+|--------|-------------|
+| `dcm:request:estimate` | Call `POST /api/v1/cost/estimate`; output cost breakdown for review step |
+| `dcm:request:submit` | `POST /api/v1/requests`; output requestUuid, entityUuid |
+| `dcm:request:wait` | Poll `GET /api/v1/requests/{uuid}/status` (or SSE); surface live status in Scaffolder log; resolve on terminal state |
+| `dcm:request:group` | `POST /api/v1/request-groups`; submit multiple requests with dependency graph |
+| `dcm:catalog:refresh` | Trigger entity provider refresh for new entity; output entityUrl for output links |
+| `dcm:approval:notify` | Fire notification to approver group when approval required |
+
+### 4.3 Live Status in Scaffolder
+
+The `dcm:request:wait` action streams progress to the Scaffolder log panel using the Scaffolder's built-in log streaming UI:
+
+```
+⏳  Waiting for provisioning...
+    09:00:05  Status: ACKNOWLEDGED
+    09:00:07  Status: ASSEMBLING layers
+    09:00:12  Status: DISPATCHED to provider
+    09:01:05  Status: PROVISIONING
+              Step 3/7: Configuring network interfaces
+              ████████████░░░░░░░░  3 of 7 complete
+    09:03:12  ✅ Status: REALIZED
+              Resource: payments-api-server-01
+              IP: 10.42.0.105
+```
+
+This reuses the Scaffolder's existing log streaming rather than building a custom progress component.
+
+---
+
+## 5. My Work — Requests
+
+**Route:** `/dcm/requests`  
+**API:** `GET /api/v1/requests`, `GET /api/v1/requests/{uuid}/status`, `GET /api/v1/requests/{uuid}/stream`, `DELETE /api/v1/requests/{uuid}`
+
+### 5.1 Requests List
+
+PatternFly `Table` with Toolbar filter:
+
+```
+[MY REQUESTS]
+Filter: [Status ▾] [Resource Type ▾] [Date Range ▾]   [Search name...]
+
+  Name                    Type         Status           Submitted
+  ─────────────────────────────────────────────────────────────────
+  payments-api-server-01  Compute.VM   ● REALIZED       2h ago
+  dev-db-postgres-02      Database     ⏳ PROVISIONING  15m ago   [Live ▶]
+  uat-lb-internal         LoadBalancer ⚠ REQUIRES APPR. 1h ago   [Review →]
+  batch-runner-scheduled  Compute.VM   🕐 SCHEDULED     Tomorrow
+```
+
+Status badges use PatternFly `Label` component:
+- `● REALIZED` — green Label
+- `⏳ PROVISIONING` — blue Label + spinner
+- `⚠ REQUIRES APPROVAL` — orange Label
+- `🕐 SCHEDULED` — grey Label
+- `✗ FAILED` — red Label
+
+"Live ▶" button opens an inline `Drawer` (PatternFly) showing the SSE status stream — no page navigation required.
+
+### 5.2 Live Status Drawer
+
+Clicking "Live ▶" opens a right-side Drawer anchored to the page:
+
+```
+┌─── Request Status ───────────────────────────────── ✕ ─┐
+│  dev-db-postgres-02            ⏳ PROVISIONING          │
+│                                                          │
+│  Timeline:                                              │
+│  ✅ 09:00:00  ACKNOWLEDGED                              │
+│  ✅ 09:00:02  ASSEMBLING                                │
+│  ✅ 09:00:47  DISPATCHED → provider-postgres-prod       │
+│  ⏳ 09:01:05  PROVISIONING                              │
+│                                                          │
+│  Progress: Step 3 of 7                                  │
+│  Configuring network interfaces                          │
+│  ████████████░░░░░░░░░░░░  43%                          │
+│                                                          │
+│  Est. completion: ~4 minutes                            │
+│                               [Cancel Request]          │
+└──────────────────────────────────────────────────────────┘
+```
+
+Powered by `GET /api/v1/requests/{uuid}/stream` (SSE). Drawer closes automatically on terminal status, replaces status badge in the table row.
+
+---
+
+## 6. My Work — Resources
+
+**Route:** `/dcm/resources`  
+**API:** `GET /api/v1/resources`, resource sub-resource endpoints
+
+### 6.1 Resource List
+
+```
+[MY RESOURCES]
+Filter: [State ▾] [Type ▾] [Group ▾] [Tag ▾]       [Search...]    [Table/Card ▾]
+
+  ● 3 resources with drift detected  [View Drift Report →]
+
+  Name                  Type        State        Provider     TTL
+  ────────────────────────────────────────────────────────────────
+  payments-api-01  ⚡  Compute.VM   ✅ OPER.      k8s-prod    87d   [···]
+  dev-db-001            Database    ✅ OPER.      db-prod     —     [···]
+  uat-loadbalancer  🔴  Network.LB  ⚠ DRIFT      net-prod    —     [···]
+```
+
+Icons:
+- `⚡` — TTL warning (< 14 days)
+- `🔴` dot — open drift record
+- `···` — action menu (Suspend, Update, Extend TTL, Transfer, Decommission)
+
+State badges use PatternFly `Label`:
+- `✅ OPERATIONAL` — green
+- `🟡 SUSPENDED` — yellow
+- `🔵 MAINTENANCE` — blue
+- `⚠ DRIFT` — orange (custom — shows worst drift severity)
+- `✗ FAILED` — red
+
+### 6.2 Resource Entity Page
+
+Clicking a resource opens its **Backstage entity page** (standard RHDH pattern). DCM contributes tabs via entity page tab extensions:
+
+```
+[Entity Header]
+payments-api-server-01                               ✅ OPERATIONAL
+Compute.VirtualMachine  |  provider: k8s-prod  |  owner: payments-team
+
+[Tabs]
+  Overview │ Drift 🔴 │ Audit │ Cost │ Credentials │ Relations │ Docs
+```
+
+**Overview tab:** IP address, hostname, CPU, memory, OS, provider, realized date, TTL, all realized fields from provider.
+
+**Drift tab 🔴** (badge shows drift severity):
+```
+Open Drift Records (1 significant, 1 minor)
+
+  Field          Realized Value    Discovered Value    Severity
+  cpu_count      4                 8                   ⚠ significant
+  memory_gb      16                16                  ✅ no drift
+  tags           [app:api]         [app:api, env:prod]  ℹ minor
+
+  Actions: [Revert All]  [Accept Changes (Update Definition)]  [Acknowledge Minor]
+```
+
+**Cost tab:** Actual cost from `GET /api/v1/resources/{uuid}/cost`. PatternFly `ChartBar` for cost by billing dimension.
+
+**Credentials tab:**
+```
+  Credential           Type         Expires      Last Retrieved
+  ssh-key-payments-01  ssh_key      2027-06-01   2 days ago
+  api-key-svc-acct     api_key      never         —
+
+  [Retrieve Value →]   ← triggers inline step-up MFA prompt
+  [Request Rotation →]
+```
+
+**Relations tab:** PatternFly `TopologyView` or a simple dependency graph showing what this resource depends on and what depends on it.
+
+### 6.3 Drift Report — Cross-Resource View
+
+**Route:** `/dcm/resources/drift`  
+Accessible via "View Drift Report →" banner on resource list when drift exists.
+
+```
+[DRIFT REPORT — Payments Team]           3 resources with open drift
+
+  Severity  Resource              Field           Realized    Discovered  Since
+  ──────────────────────────────────────────────────────────────────────────────
+  ● Critical  prod-db-primary     replication      enabled     disabled    4h
+  ● Signif.   uat-lb-internal     cpu_count        4           8           2d
+  ● Minor     dev-app-01          tag:env          staging     (missing)   1w
+
+  [Revert All Critical]   [Export Drift Report]   [Configure Drift Alerts]
+
+  Auto-remediation status:
+  prod-db-primary: REVERT_PENDING — waiting for maintenance window
+```
+
+Severity filter chips at top (PatternFly `ChipGroup`). Clicking a row opens the resource entity Drift tab.
+
+
+---
+
+## 7. My Work — Dependency Groups
+
+**Route:** `/dcm/dependency-groups`  
+**API:** `GET /api/v1/request-groups`, `GET /api/v1/request-groups/{uuid}`, `DELETE /api/v1/request-groups/{uuid}`
+
+```
+[MY DEPENDENCY GROUPS]
+
+  three-tier-app-deploy          in_progress          Started 30m ago
+  ├── db          ✅ REALIZED    payments-db-01        09:01
+  ├── app         ⏳ DISPATCHED  (pending db IP → db_host injected)
+  └── lb          ○ PENDING      (waiting for app)
+
+  [Cancel Group]  [View All Requests]
+```
+
+Progress bar shows N of M constituents realized.
+
+---
+
+## 8. ITSM Integration
+
+DCM is designed to eliminate the infrastructure ticket as the primary provisioning mechanism. However, organizations that operate ITSM systems (ServiceNow, Jira Service Management, Remedy) still require bidirectional traceability — change records for compliance, incident linkage for troubleshooting, and CMDB accuracy.
+
+DCM's ITSM integration is a **bridge, not a dependency.** DCM does not require an ITSM system to function. ITSM integration is additive — it enriches DCM entities with ITSM metadata and notifies ITSM of DCM lifecycle events.
+
+### 8.1 ITSM Reference Linking
+
+Every DCM resource entity supports an optional `itsm_references` metadata block in its business data:
+
+```
+[Resource Entity Header]
+payments-api-server-01                                ✅ OPERATIONAL
+
+[ITSM References — visible as a metadata card on Overview tab]
+  Change Record    CHG0012345    Approved 2026-03-15   [View in ServiceNow ↗]
+  Incident         INC0048291    (linked on creation)  [View in Jira ↗]
+  CMDB Item        CI-VM-08821   Auto-synced           [View in CMDB ↗]
+```
+
+References are stored as business data fields on the entity — they follow the entity through its full lifecycle and appear in audit records. They are **not** required for DCM to function.
+
+### 8.2 ITSM Event Webhook Integration
+
+DCM fires lifecycle events to the Message Bus. An ITSM notification service subscribes to these events and creates/updates ITSM records accordingly:
+
+| DCM Event | ITSM Action (configurable) |
+|-----------|---------------------------|
+| `request.requires_approval` | Create Change Request draft in ServiceNow / Jira |
+| `request.realized` | Close Change Request; update CMDB CI |
+| `request.failed` | Create Incident; link to Change Request |
+| `entity.state_changed` | Update CMDB CI state |
+| `drift.detected` (significant/critical) | Create Incident in ITSM |
+| `entity.decommissioned` | Retire CMDB CI; close related Change Requests |
+
+This is implemented as a **notification service** registered in DCM — a webhook consumer that translates DCM events to ITSM API calls. No changes to DCM core are needed.
+
+### 8.3 ITSM Ticket as Approval Mechanism
+
+For organizations that require ITSM change board approval, DCM's `authorized` tier approval mechanism accepts votes recorded via the ITSM system:
+
+```
+[Request reaches authorized tier → approval required]
+  │
+  ▼ DCM fires request.requires_approval
+  │   notification service creates Change Request in ServiceNow
+  │
+  ▼ Change Board reviews in ServiceNow (existing process unchanged)
+  │   Approval decision → ServiceNow calls DCM Admin API:
+  │   POST /api/v1/admin/approvals/{uuid}:vote
+  │   { "decision": "approve", "recorded_via": "servicenow", "voter_uuid": "..." }
+  │
+  ▼ DCM records vote; quorum tracked by DCM
+  │   Audit trail includes: who voted, via which system, at what time
+```
+
+The approval decision is made in ServiceNow using the organization's existing CAB process. DCM records the outcome. Neither system depends on the other's internal workflow model.
+
+### 8.4 ITSM Reference UI
+
+In the Consumer GUI, ITSM references surface in two places:
+
+**On resource entity Overview tab** — ITSM References card (shown only when references exist):
+```
+ITSM References
+  ┌────────────────────────────────────────────────────────┐
+  │  CHG0012345  Change Request   Approved  [Open ↗]      │
+  │  INC0048291  Incident         Resolved  [Open ↗]      │
+  └────────────────────────────────────────────────────────┘
+  [Add Reference]  (opens modal: type, ID, system URL)
+```
+
+**On request status page** — when a request is at `requires_approval` and an ITSM reference is attached:
+```
+⏳ REQUIRES APPROVAL — verified tier
+   Approval being tracked via ServiceNow Change Board
+   CHG0012345 [View in ServiceNow ↗]
+   Quorum: 0 / 2 votes recorded
+```
+
+### 8.5 CMDB Sync
+
+DCM entities are the **system of record** for realized state. The CMDB is a **consumer** of DCM data, not a producer. CMDB sync flows one way: DCM → CMDB.
+
+The sync is implemented via the notification service subscription to `entity.*` events. A CMDB sync notification service maps DCM entity fields to CMDB CI attributes and calls the CMDB API on every state change.
+
+**CMDB field mapping** is declared in the provider registration — it is not hardcoded. Different CMDB systems (ServiceNow CMDB, iTop, Device42) use the same event subscription pattern with different field mapping configurations.
+
+---
+
+## 9. Approvals
+
+**Route:** `/dcm/approvals`  
+**API:** `GET /api/v1/approvals/pending`, `POST /api/v1/approvals/{uuid}`
+
+Visible only to actors with `approver` role. NavItem shows orange badge with pending count.
+
+```
+[PENDING APPROVALS — 3]
+
+  Request                  Tenant      Tier       Risk   Expires
+  ────────────────────────────────────────────────────────────────
+  Large GPU VM (8x A100)   AI Team     verified    72/100  2h 14m
+  Prod DB 32-core          Data Team   reviewed    41/100  23h
+  Bulk decommission (12)   Platform    authorized  88/100  45m  ← quorum 1/3
+
+  [Review →]
+```
+
+Clicking "Review →" opens the Approval Detail page:
+- Full request payload summary
+- Risk score breakdown (signal chart)
+- Policy evaluation results (which policies flagged this)
+- Existing votes (for authorized tier quorum tracking)
+- [Approve] [Reject] buttons with required comment field
+- Approver cannot vote on own requests (blocked UI-side + server-side)
+
+---
+
+## 10. Governance — Cost and Quota
+
+**Route:** `/dcm/cost`
+
+```
+[COST & QUOTA — Payments Team]   This month: $12,450   vs budget: $15,000
+
+  Quota Utilization
+  Compute.VM          ████████████████░░░░  80%  (16/20 allocated)  ⚠
+  Storage.Block       ████░░░░░░░░░░░░░░░░  22%  (11/50 TB)
+  Network.LB          ██░░░░░░░░░░░░░░░░░░   8%  (2/25 units)
+
+  Cost by Resource Type (last 30 days)
+  [PatternFly ChartDonut or ChartBar]
+
+  Top Cost Resources
+  payments-api-01    Compute.VM    $3,200/mo
+  payments-db-01     Database      $2,800/mo
+  ...
+
+  [Export CSV]  [View All Resources]
+```
+
+---
+
+## 11. Governance — Audit Trail
+
+**Route:** `/dcm/audit`  
+**API:** `GET /api/v1/resources/{uuid}/audit`, `GET /api/v1/audit/correlation/{correlation_id}`
+
+Visible to all actors for their own resources. Cross-tenant audit is in the Admin Panel.
+
+```
+[MY AUDIT TRAIL]
+Filter: [Resource Type ▾] [Operation ▾] [Date Range ▾]   [Correlation ID search...]
+
+  Time          Resource                Operation        Actor
+  ─────────────────────────────────────────────────────────────────────
+  5m ago        payments-api-01         PATCH            alice@corp.com
+  2h ago        dev-db-001              DRIFT_REVERT     system
+  Yesterday     uat-lb-internal         REALIZED         alice@corp.com
+  2 days ago    batch-runner-01         DECOMMISSIONED   bob@corp.com
+
+  [View Full Record →]   [Export CSV]
+```
+
+**Record detail drawer:** Operation, Resource, Actor (with auth method), Time, Correlation ID trace, fields changed, policy evaluations, risk score.
+
+**Correlation ID trace:** links to full request pipeline view — Intent → Requested → Dispatch → Realized → Provider chain — for the actor's own requests.
+
+---
+
+## 12. Governance — Contributions
+
+**Route:** `/dcm/contributions`  
+**API:** `GET /api/v1/contribute`, `POST /api/v1/contribute/policy`, `DELETE /api/v1/contribute/{uuid}`
+
+Visible only to actors with `contributor` role.
+
+```
+[MY CONTRIBUTIONS]
+
+  Handle                     Type              Status         Submitted
+  ─────────────────────────────────────────────────────────────────────
+  payments-network-policy    policy            ● active       3 months ago
+  gpu-quota-validator        policy            🔵 shadow      1 week ago   [Divergence: 2.3%]
+  ml-resource-group          resource_group    ⏳ reviewing   2 days ago
+
+  [Submit New Policy]  [Submit Resource Group]
+```
+
+Shadow divergence percentage shown for shadow-mode contributions — clicking opens divergence case viewer.
+
+---
+
+## 13. Settings — Notifications
+
+**Route:** `/dcm/notifications`  
+**API:** `GET /api/v1/notifications`, `GET /api/v1/webhooks`, `POST /api/v1/webhooks`
+
+```
+[NOTIFICATIONS]     [Mark All Read]
+
+  🔔 Request REALIZED: payments-api-server-01          2m ago   [View →]
+  ⚠  Drift detected: uat-loadbalancer (significant)    1h ago   [View →]
+  ⏰ TTL Warning: dev-db-001 expires in 7 days          3h ago   [Extend →]
+
+  ─────────────────
+  [WEBHOOK SUBSCRIPTIONS]
+
+  URL                                  Events                Status
+  https://my-system/dcm-webhook        request.*, drift.*    ✅ active  [Test] [Delete]
+
+  [Add Webhook]
+```
+
+---
+
+## 14. Settings — Sessions
+
+**Route:** `/dcm/sessions`  
+**API:** `GET /api/v1/auth/sessions`, `DELETE /api/v1/auth/sessions/{uuid}`
+
+```
+[ACTIVE SESSIONS]
+
+  Device                    Auth Method   Created         Last Active
+  ──────────────────────────────────────────────────────────────────
+  This session ●            OIDC          Today 09:00     Active now
+  Chrome / Mac (10.0.1.5)   OIDC          Yesterday       2h ago       [Revoke]
+  API Client (svc-acct)     api_key       3 days ago      1h ago       [Revoke]
+
+  [Sign Out All Other Sessions]
+```
+
+---
+
+## 15. In RHDH — TechDocs Integration
+
+DCM publishes TechDocs for:
+- Service catalog item documentation (from DCM layer store)
+- Resource type specifications
+- DCM data model documentation (this spec set)
+
+TechDocs are indexed by RHDH search — users can search "how do I provision a GPU cluster" and find both the catalog item and the documentation.
+
+DCM entity pages link to TechDocs automatically via `backstage.io/techdocs-ref` annotation on generated catalog entities.
+
+---
+
+## 16. Search Integration
+
+In RHDH mode, DCM entities are indexed in Backstage search:
+
+- DCM catalog items indexed as `DCMService` entities — searchable by name, description, tags
+- DCM realized resources indexed as `DCMResource` entities — searchable by handle, IP, type
+- TechDocs content indexed — searchable documentation
+- Search collator provided by `@dcm/plugin-catalog-backend`
+
+Search result rendering:
+```
+  🖥 payments-api-server-01    DCMResource    Compute.VM    ● OPERATIONAL
+     10.42.0.105 · k8s-prod · payments-team
+```
+
+---
+
+
+### 16.1 Global Search Bar
+
+PatternFly `SearchInput` in masthead — keyboard shortcut `/` to focus. Searches across catalog items, resources, requests, and TechDocs (RHDH mode).
+
+### 16.2 Search Results Page
+
+**Route:** `/dcm/search?q=`
+
+```
+[SEARCH RESULTS: "payments db"]                    12 results
+
+  [All ▾] [Catalog Items] [Resources] [Requests] [Docs]   ← filter chips
+
+  CATALOG ITEMS (2)
+  ● Database.PostgreSQL   Managed PostgreSQL instance   [Request →]
+  ● Database.MySQL        Managed MySQL instance        [Request →]
+
+  MY RESOURCES (3)
+  ● payments-db-primary   Database.PostgreSQL  ✅ OPERATIONAL  [View →]
+  ● payments-db-replica   Database.PostgreSQL  ✅ OPERATIONAL  [View →]
+
+  MY REQUESTS (1)
+  ● payments-db-archive   SCHEDULED  Tomorrow 02:00  [View →]
+```
+
+Results render incrementally per category with PatternFly `Spinner` while loading. Keyboard navigation through results supported.
+
+## 17. Path to Production — Effort Reduction Summary
+
+| Traditional Workflow | DCM + RHDH |
+|---------------------|------------|
+| Submit infrastructure ticket | Browse catalog → click "Request" → Scaffolder wizard |
+| Wait for human review (hours/days) | Policy engine auto-approves in seconds; escalates if needed |
+| Infrastructure team provisions manually | DCM dispatches to provider automatically |
+| Receive confirmation email | SSE live status in Scaffolder; notification in portal |
+| Manually update CMDB | Entity appears in RHDH catalog automatically after REALIZED |
+| Track cost in spreadsheet | Cost tab on every resource entity page |
+| Periodic compliance audits | Drift detection continuous; audit trail always current |
+| Decommission via ticket | Self-service Delete with stake resolution |
+| Document runbooks separately | TechDocs in same portal, linked from entity pages |
+| Separate access management | RHDH RBAC + DCM permission policy — one place |
+
+**Time to first production resource (new user):**
+1. Log in (SSO — already have corporate credentials)
+2. Browse catalog or search for service
+3. Fill in Scaffolder form (5–10 fields, schema-driven, cost estimate live)
+4. Submit → watch live provisioning log
+5. Resource appears in catalog and "My Resources"
+
+**Target: < 10 minutes from login to operational resource.**
+
+---
+
+## 18. Security Model
+
+### 16.1 Tenancy Enforcement
+
+- Active tenant: RHDH Group context → `X-DCM-Tenant` header on all DCM API calls
+- All data filtered server-side by DCM Consumer API (defense in depth)
+- Entity ownership in RHDH catalog: `spec.owner = group:` — users see only own-tenant entities
+
+### 16.2 RBAC
+
+- RHDH RBAC plugin maps Backstage permissions to DCM roles
+- DCM roles in session token drive nav visibility (client-side, defense-in-depth)
+- DCM Consumer API enforces roles server-side on every request
+- New roles propagate from IdP via SCIM or OIDC claims — no manual assignment
+
+### 16.3 Step-Up MFA
+
+Credential retrieval, ownership transfer, and bulk decommission trigger inline MFA:
+- PatternFly `Modal` overlay — no page navigation
+- User completes second factor in RHDH auth provider
+- Step-up token cached PT10M (per session model)
+
+### 16.4 Content Security Policy (Standalone Mode)
+
+- `default-src 'self'`
+- `connect-src 'self'  `
+- No inline scripts; no eval(); SRI on external assets
+
+### 16.5 Governance and Regulatory Compliance
+
+All compliance constraints are enforced by the DCM control plane — the GUI is a client. The GUI surfaces compliance state:
+- Data classification badges on entity detail pages (from `data_classification` fields)
+- Sovereignty constraint indicators on entities with cross-boundary restrictions
+- Accreditation status visible in entity overview
+- Audit trail always accessible; chain integrity status shown
+
+The GUI cannot be used to bypass policy — every action goes through the Consumer API which applies the full five-check boundary model (doc 26), scoring, GateKeeper, and policy evaluation.
+
+---
+
+## 19. Extensibility
+
+### 17.1 New Resource Types — Zero GUI Code
+
+When a new resource type is registered in DCM:
+1. `@dcm/plugin-catalog-backend` detects it via the resource type registry
+2. A new Software Template is auto-generated from the field schema
+3. A new `DCMService` catalog entity appears in RHDH
+4. The template's input form renders from JSON Schema — no UI code needed
+5. The resource entity page uses the same Overview/Drift/Audit/Cost/Credentials/Relations tabs — no new tabs needed for standard resource types
+
+Resource type-specific UI extensions are possible via entity page tab contributions if a resource type requires specialized visualization (e.g. a topology view for network types).
+
+### 17.2 Custom Plugin Extensions
+
+Teams can contribute additional entity page tabs, catalog cards, or nav items via standard Backstage plugin extension points. DCM plugin APIs for contributing extensions:
+
+```typescript
+// Register a custom entity tab for a specific resource type
+dcmPlugin.registerEntityTab({
+  resourceType: 'Network.VirtualNetwork',
+  component: NetworkTopologyTab,
+  title: 'Topology',
+  icon: NetworkIcon,
+});
+
+// Register a custom catalog card
+dcmPlugin.registerCatalogCard({
+  component: GPUAvailabilityCard,
+  position: 'right',
+});
+```
+
+### 17.3 Multi-Portal Deployments
+
+Large organizations with multiple RHDH instances (one per business unit) can:
+- Point multiple RHDH instances at the same DCM control plane
+- Each RHDH instance filters DCM entities to its tenant scope
+- DCM federation handles cross-instance resource visibility where permitted
+
+---
+
+## 20. Conformance
+
+A conforming Consumer GUI implementation must:
+
+1. Implement all DCM nav groups: Service Catalog, My Work (Requests/Resources/Groups), Approvals, Governance (Cost & Quota/Contributions), Settings
+2. Implement SSE-based live status with constituent tracking (with polling fallback)
+3. Enforce tenancy context (`X-DCM-Tenant`) on all API calls
+4. Hide (not disable) role-gated navigation items
+5. Implement step-up MFA inline for gated operations
+6. Surface drift, audit, cost, credentials, and relationships as entity page tabs
+7. In RHDH mode: implement all six plugin packages with the specified capabilities
+8. Auto-generate Software Templates from DCM catalog item schemas (RHDH mode)
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/specifications/dcm-examples.md b/content/docs/architecture/specifications/dcm-examples.md
new file mode 100644
index 0000000..e40d504
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-examples.md
@@ -0,0 +1,3381 @@
+# DCM — Examples and Use Cases
+
+**Document Status:** ✅ Complete
+**Status:** Draft — Examples document - no WIP status needed; always current with architecture.
+**Document Type:** Reference Examples
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [Provider Contract](../data-model/A-provider-contract.md) | [Policy Contract](../data-model/B-policy-contract.md) | [Consumer API](consumer-api-spec.md) | [Admin API](dcm-admin-api-spec.md) | [Registration](dcm-registration-spec.md) | [OPA Integration](dcm-opa-integration-spec.md)
+
+---
+
+## Overview
+
+This document provides end-to-end worked examples for the most important DCM use cases. Each example shows the complete interaction — payloads, state transitions, API calls, and Rego policies where applicable — so implementors can trace exactly what happens at each step.
+
+Examples are organized by the three foundational abstractions:
+- **Section 1** — Orchestration examples (Policy)
+- **Section 2** — Provider interaction examples (Provider)
+- **Section 3** — API interaction examples (Consumer API, Admin API)
+- **Section 4** — Registration flow examples
+
+---
+
+# Section 1 — Orchestration Examples
+
+## 1.1 Basic Request Lifecycle (End-to-End)
+
+The complete path for a consumer requesting a VM. Shows all payload type events, which policies fire at each step, and the state transitions.
+
+### Setup: Active artifacts
+
+```yaml
+# Named workflow (Level 1 orchestration)
+Orchestration Flow Policy: system/workflows/request-lifecycle
+  ordered: true
+  steps: [request.initiated, request.intent_captured,
+          request.layers_assembled, request.placement_complete,
+          request.dispatched]
+
+# Dynamic policies (Level 2 orchestration)
+GateKeeper:      org/gatekeeper/vm-size-limits         (fires on request.layers_assembled)
+Transformation:  org/transformation/inject-monitoring  (fires on request.layers_assembled)
+GateKeeper:      system/gatekeeper/sovereignty-check   (fires on request.placement_complete)
+```
+
+### Step-by-step
+
+**Step 1 — Consumer submits request:**
+```
+POST /api/v1/requests
+{ "catalog_item_uuid": "vm-standard-uuid",
+  "fields": { "cpu_count": 4, "memory_gb": 8, "os_family": "rhel" } }
+
+→ Response 202: { "request_uuid": "req-001", "entity_uuid": "ent-001",
+                  "status": "ACKNOWLEDGED" }
+→ Event published: { "type": "request.initiated", "entity_uuid": "ent-001",
+                     "payload": { "fields": {...} } }
+→ Intent State written to Intent Store
+```
+
+**Step 2 — Layer assembly:**
+```
+Event: request.initiated
+→ Named workflow step 1 fires: capture-intent policy acknowledges
+→ New event: request.intent_captured
+
+Event: request.intent_captured
+→ Named workflow step 2 fires: assemble-layers policy runs
+→ Base layer applied: data_center = "EU-WEST-DC1"
+→ Org layer applied: monitoring_agent = "datadog-agent:7.42"
+→ Policy layer applied: backup_policy = "daily-30d-eu-west"
+→ New event: request.layers_assembled
+  payload now includes all merged fields with provenance
+```
+
+**Step 3 — Dynamic policies fire on request.layers_assembled:**
+```
+Event: request.layers_assembled
+→ [PARALLEL] All policies matching this payload type evaluate simultaneously:
+
+  GateKeeper vm-size-limits evaluates:
+    input.payload.fields.cpu_count.value = 4
+    4 <= 32 → allow: true
+
+  Transformation inject-monitoring evaluates:
+    monitoring_endpoint not in payload → mutation:
+    { field: "fields.monitoring_endpoint",
+      operation: "set",
+      value: "https://metrics.internal.prod.example.com" }
+
+→ All GateKeepers: allow
+→ Transformations applied to payload
+→ New event: request.policies_evaluated
+```
+
+**Step 4 — Placement:**
+```
+Event: request.policies_evaluated
+→ Named workflow step 3: run-placement policy
+
+→ Placement Engine:
+  Step 1: Sovereignty filter — EU-WEST-DC1 requirement → 3 providers eligible
+  Step 2: Accreditation filter — no PHI in payload → all 3 pass
+  Step 3: Capability filter — all support VirtualMachine → all 3 pass
+  Step 4: Reserve query → parallel queries to EU-WEST-Prod-1,2,3
+    EU-WEST-Prod-1: confirmed, utilization 42%, cost $0.32/hr
+    EU-WEST-Prod-2: confirmed, utilization 61%, cost $0.32/hr
+    EU-WEST-Prod-3: insufficient capacity
+  Step 5: Tie-break → Step 4 cost equal → Step 5 least loaded → Prod-1 wins
+  Step 6: Confirm Prod-1; release holds on Prod-2
+
+→ Requested State written to Requested Store
+  (requested.yaml + assembly-provenance.yaml + placement.yaml + dependencies.yaml)
+→ New event: request.placement_complete
+```
+
+**Step 5 — Dispatch and realization:**
+```
+Event: request.placement_complete
+→ Governance Matrix evaluated: payload data_classification = internal/public → ALLOW
+→ Named workflow step 4: dispatch policy
+→ Provider EU-WEST-Prod-1 receives dispatch payload
+→ Provider naturalizes to OpenStack Nova format
+→ OpenStack provisions VM
+→ Provider denaturalizes result → DCM unified format
+→ Realized State written (with provider_entity_id: "vm-0a1b2c3d")
+→ Status callback: COMPLETED
+
+Consumer polls: GET /api/v1/requests/req-001/status
+→ { "status": "COMPLETED", "entity_uuid": "ent-001" }
+```
+
+---
+
+## 1.2 Human Approval Gate (Conditional Step Insertion)
+
+A production VM request that requires manager approval before dispatch. Shows how a GateKeeper policy inserts a waiting step without modifying the named workflow.
+
+### Setup: Additional active policy
+
+```rego
+# GateKeeper fires on request.policies_evaluated for prod VMs over $100/month
+package dcm.gatekeeper.prod_vm_approval_gate
+
+deny contains reason if {
+    input.payload.type == "request.policies_evaluated"
+    input.deployment.deployment_posture == "prod"
+    input.payload.cost_estimate.per_month > 100
+    not input.payload.approvals["manager_approval"]
+    reason := "Production VMs over $100/month require manager approval"
+}
+
+# Signal that approval is the resolution path (not a permanent reject)
+requires_approval := true if count(deny) > 0
+approval_type := "manager_approval" if count(deny) > 0
+```
+
+### Step-by-step
+
+```
+After Step 3 (dynamic policies evaluate):
+→ GateKeeper prod_vm_approval_gate fires
+→ deny: ["Production VMs over $100/month require manager approval"]
+→ requires_approval: true, approval_type: "manager_approval"
+
+→ Policy Engine sees GateKeeper deny WITH requires_approval flag
+→ Entity enters AWAITING_APPROVAL state (not FAILED)
+→ Notification dispatched:
+    audience: manager (from actor's group membership via relationship graph)
+    event_type: request.requires_approval
+    action_url: /api/v1/requests/req-001:approve
+    action_deadline: PT24H
+
+Manager approves:
+POST /api/v1/requests/req-001:approve
+{ "approval_type": "manager_approval", "approver_uuid": "mgr-001" }
+
+→ payload.approvals["manager_approval"] = { approved: true, by: "mgr-001" }
+→ GateKeeper re-evaluates: approval present → allow
+→ Pipeline resumes from request.policies_evaluated
+→ Placement → Dispatch → Realization (same as 1.1 Steps 4-5)
+```
+
+---
+
+## 1.3 Policy-Gated Request — Hard Block with Clear Error
+
+Shows a request blocked by a hard GateKeeper with a consumer-visible error message.
+
+```rego
+package dcm.gatekeeper.approved_os_images
+
+deny contains reason if {
+    input.payload.type == "request.layers_assembled"
+    not input.payload.fields.os_family.value in {"rhel", "ubuntu-lts", "coreos"}
+    reason := sprintf(
+        "OS '%s' is not in the approved image list. Approved: rhel, ubuntu-lts, coreos",
+        [input.payload.fields.os_family.value]
+    )
+}
+```
+
+```
+Consumer submits: { "os_family": "windows-server" }
+
+→ request.layers_assembled fires
+→ GateKeeper approved_os_images: deny
+→ Entity → FAILED (no requires_approval flag → hard block)
+
+Consumer response:
+{ "status": "FAILED",
+  "failure_reason": "OS 'windows-server' is not in the approved image list.",
+  "retry_eligible": true,
+  "policy_uuid": "gatekeeper-approved-os-uuid",
+  "suggestion": "Resubmit with os_family: rhel, ubuntu-lts, or coreos" }
+```
+
+---
+
+## 1.4 Compound Service — compound service definition with Dependency Ordering
+
+A web application stack provisioned as a single catalog item: VM + IP + DNS + LoadBalancer.
+
+### Named workflow for compound service
+
+```rego
+package dcm.orchestration.webapp_stack
+
+steps := [
+    {"step": 1, "payload_type": "request.initiated",
+     "policy_handle": "system/orchestration/capture-intent", "on_fail": "halt"},
+    {"step": 2, "payload_type": "request.intent_captured",
+     "policy_handle": "system/orchestration/assemble-compound", "on_fail": "halt"},
+    {"step": 3, "payload_type": "request.compound_assembled",
+     "policy_handle": "system/orchestration/resolve-dependencies", "on_fail": "halt"},
+    {"step": 4, "payload_type": "request.dependencies_resolved",
+     "policy_handle": "system/orchestration/dispatch-constituents", "on_fail": "compensate"}
+]
+
+ordered := true
+```
+
+### Execution
+
+```
+compound service definition receives compound dispatch payload:
+  component.ip:  { resource_type: Network.IPAddress, depends_on: [] }
+  component.vm:  { resource_type: Compute.VirtualMachine, depends_on: [] }
+  component.dns: { resource_type: DNS.Record, depends_on: [ip, vm], required: partial }
+  component.lb:  { resource_type: Network.LoadBalancer, depends_on: [vm, ip], required: partial }
+
+Dependency-ordered execution:
+  Round 1 (no dependencies): ip, vm  → provisioned in parallel
+    ip  → REALIZED: 10.1.45.23/32
+    vm  → REALIZED: vm-0a1b2c3d
+
+  Round 2 (depend on ip+vm): dns, lb → provisioned in parallel
+    dns → FAILED (DNS service degraded)
+    lb  → REALIZED: lb-7f8e9d
+
+  Compound evaluation:
+    dns: required_for_delivery = partial → DEGRADED, not FAILED
+    lb: required_for_delivery = partial → REALIZED
+
+Compound entity state: DEGRADED (dns failed; vm+ip+lb realized)
+Notification: owner notified "WebApp Stack provisioned in degraded state — DNS unavailable"
+
+Recovery policy fires (PARTIAL_REALIZATION trigger):
+  profile=prod → NOTIFY_AND_WAIT
+  Consumer sees notification with options: accept degraded | trigger dns retry
+```
+
+---
+
+## 1.5 Drift Detection and Automated Remediation
+
+Discovery finds VM memory has changed without a DCM request. Shows the full drift → policy → revert flow.
+
+```
+Scheduled discovery (PT15M interval):
+→ Provider queried for vm-0a1b2c3d
+→ Discovered: memory_gb = 16
+→ Realized State: memory_gb = 8
+→ No Requested State record explains the change
+
+Drift Reconciliation Component:
+  field: memory_gb
+  realized_value: 8, discovered_value: 16
+  change_magnitude: 100% increase → "significant" (standard profile: 10-50% threshold)
+  field_criticality: medium (from Resource Type Spec)
+  unsanctioned: true → elevate one level → "critical"
+
+Drift record created:
+  overall_severity: critical
+  unsanctioned: true
+
+Policy Engine evaluates drift record:
+  Active drift response policy (standard profile, critical severity, unsanctioned):
+    action: ESCALATE → notify platform admin + SRE + owner
+
+Notifications dispatched:
+  Owner: "Critical unsanctioned change on vm-0a1b2c3d: memory_gb 8→16"
+  Platform Admin: same (urgency: critical)
+  SRE on-call: same (via PagerDuty notification service)
+
+If consumer submits: REVERT
+→ New request submitted from Realized State (memory_gb: 8)
+→ Full governance pipeline → new Requested State → dispatch → revert
+→ Next discovery: memory_gb = 8 → drift.resolved event
+```
+
+---
+
+## 1.6 Recovery Flow — Dispatch Timeout with NOTIFY_AND_WAIT
+
+Provider does not respond within PT30M. Profile is `prod` → `recovery-notify-and-wait`.
+
+```
+T+0:    Request dispatched to EU-WEST-Prod-1
+T+30M:  Dispatch timeout fires
+        Entity → TIMEOUT_PENDING
+        Recovery trigger: DISPATCH_TIMEOUT
+
+Recovery Policy (prod profile → recovery-notify-and-wait):
+  action: NOTIFY_AND_WAIT
+  deadline: PT4H
+  on_deadline_exceeded: ESCALATE
+
+Notifications dispatched:
+  Owner: "Request req-001 timed out. Choose how to proceed by T+4H."
+  action_url: /api/v1/resources/ent-001/recovery-decisions
+
+Consumer queries:
+GET /api/v1/resources/ent-001/recovery-decisions
+→ { "trigger": "DISPATCH_TIMEOUT",
+    "deadline": "...",
+    "available_actions": [
+      { "action": "DRIFT_RECONCILE",
+        "description": "Let discovery determine actual state" },
+      { "action": "DISCARD_AND_REQUEUE",
+        "description": "Clean up and retry" }
+    ] }
+
+T+45M:  Provider responds (late response) with realized payload
+        Entity in TIMEOUT_PENDING → LATE_RESPONSE_RECEIVED fires
+
+Recovery policy for LATE_RESPONSE_RECEIVED (prod → notify-and-wait):
+  action: NOTIFY_AND_WAIT (same — human decides whether to accept late work)
+  notification updated: "Provider completed after timeout. Accept or discard?"
+
+Consumer POSTs: { "action": "DISCARD_AND_REQUEUE" }
+→ Best-effort cleanup sent to provider
+→ Entity → FAILED
+→ New request cycle created (same entity_uuid)
+→ Orphan detection triggered for EU-WEST-Prod-1
+```
+
+---
+
+## 1.7 Federation-Routed Request
+
+Consumer in Regional DCM A requests a resource that gets placed on a provider registered with Regional DCM B via Hub DCM.
+
+```
+Consumer → Regional DCM A:
+  POST /api/v1/requests { resource_type: Compute.VirtualMachine, ... }
+
+Regional DCM A Placement Engine:
+  Step 1: Sovereignty filter → local providers all at capacity
+  Step 2: Query Hub DCM (Peer DCM provider) for available regional capacity
+    → Hub responds: Regional DCM B has EU-WEST-Prod-2 with capacity
+
+Governance Matrix check (Regional DCM A → Hub DCM):
+  subject: dcm_peer (Regional DCM A)
+  data.classification: internal (assembled payload fields)
+  target: dcm_peer (Hub DCM), trust_posture: verified
+  → Decision: ALLOW (internal data, verified peer)
+
+Hub DCM routes to Regional DCM B:
+  Governance Matrix check (Hub → Regional DCM B):
+    same: ALLOW
+  Regional DCM B forwards to EU-WEST-Prod-2
+
+Realized State flows back:
+  Provider → Regional DCM B → Hub DCM → Regional DCM A
+  Each hop: Governance Matrix evaluated
+  Final Realized State written to Regional DCM A's Realized Store
+  entity_uuid preserved throughout
+  provider_entity_id: "vm-eu-west-b-0012"
+```
+
+---
+
+## 1.8 Brownfield Ingestion Workflow
+
+An existing VM discovered by a provider that DCM did not provision.
+
+```rego
+# Orchestration Flow Policy for brownfield ingestion
+package dcm.orchestration.brownfield_ingestion
+
+steps := [
+    {"step": 1, "payload_type": "discovery.new_entity_found",
+     "policy_handle": "system/ingestion/create-transitional-record"},
+    {"step": 2, "payload_type": "ingestion.transitional_created",
+     "policy_handle": "system/ingestion/enrich-from-information-providers"},
+    {"step": 3, "payload_type": "ingestion.enriched",
+     "policy_handle": "system/ingestion/await-operator-promotion"},
+    {"step": 4, "payload_type": "ingestion.promotion_approved",
+     "policy_handle": "system/ingestion/promote-to-tenant"}
+]
+ordered := true
+```
+
+```
+Discovery cycle finds vm-legacy-0001 (no matching Realized State UUID):
+
+Step 1: Event: discovery.new_entity_found
+→ INGEST: create Transitional entity in __transitional__ Tenant
+  entity_uuid assigned
+  lifecycle_state: INGESTION_PENDING
+  data_classification: internal (default)
+
+Step 2: Event: ingestion.transitional_created
+→ ENRICH: Information Providers queried:
+  CMDB (authority: primary):
+    business_unit: "Payments Platform"
+    cost_center: "PAYM-4421"
+    product_owner: "Jane Smith"
+    compliance_scope: "PCI-DSS"
+  HR System (authority: secondary):
+    team: "payments-platform-eng"
+
+Step 3: Event: ingestion.enriched
+→ Notification to Platform Admin:
+  "Brownfield entity discovered. Review and assign to Tenant."
+  action_url: /api/v1/admin/ingestion/ing-001:promote
+
+Step 4: Operator approves:
+POST /api/v1/admin/ingestion/ing-001:promote
+{ "target_tenant_uuid": "payments-tenant-uuid",
+  "compliance_overlay": "pci-dss" }
+
+→ Entity moved from __transitional__ to payments-tenant
+→ Intent State created from discovered configuration
+→ drift detection activated
+→ lifecycle_state: OPERATIONAL
+```
+
+---
+
+
+## 1.9 VM Lifecycle — Static Replace
+
+Re-provision a VM using its existing Requested State payload exactly as it was
+dispatched, without re-running layer enrichment or policy evaluation. The result
+is a functionally identical resource to the one being replaced.
+
+**When to use Static Replace vs Rehydration:**
+
+| | Static Replace | Rehydration |
+|-|---------------|-------------|
+| Uses | Original Requested State (the exact dispatch payload) | Original Intent State (what the consumer asked for) |
+| Policy re-evaluation | No — payload is used as-is | Yes — full layer enrichment + policy evaluation runs again |
+| Standards compliance | Reflects policies at time of original provisioning | Reflects current policies and current layers |
+| Use case | Known-good rebuild, emergency restore, hardware swap | Standards refresh, datacenter migration, DR in a new zone |
+| AEP endpoint | `POST /api/v1/resources/{entity_uuid}:rehydrate` with `mode: static` | `POST /api/v1/resources/{entity_uuid}:rehydrate` with `mode: intent` |
+
+**Preconditions:**
+- Entity exists in DCM with status `OPERATIONAL` or `SUSPENDED`
+- A Requested State record exists (all DCM-provisioned resources have one)
+- The Service Provider that originally provisioned the resource is still registered and healthy
+- The target resource type has not had a breaking schema change (VER-009)
+
+**Workflow:**
+
+```
+Consumer: POST /api/v1/resources/{entity_uuid}:rehydrate
+  {
+    "mode": "static",
+    "reason": "Hardware failure on host — replacing on equivalent host in same zone",
+    "target_zone": null,             // null = same zone as original
+    "retain_entity_uuid": true,      // DCM entity UUID is preserved
+    "pre_rehydration_backup": true   // optional: snapshot before proceeding
+  }
+
+Response 200 OK — returns Operation:
+  {
+    "name": "/api/v1/operations/{request_uuid}",
+    "done": false,
+    "metadata": {
+      "stage": "REHYDRATION_INITIATED",
+      "resource_uuid": "{entity_uuid}",
+      "rehydration_mode": "static",
+      "source_requested_state_uuid": "{original_requested_state_uuid}"
+    }
+  }
+```
+
+**What DCM does:**
+
+```
+1. Retrieve the most recent Requested State record for entity_uuid
+   (this is the exact payload that was dispatched at original provisioning time)
+
+2. Entity enters REHYDRATING lifecycle state
+   (incoming traffic should be shifted away at LTM/GTM level before initiating)
+
+3. Decommission the existing resource via the Service Provider
+   DELETE /{resource_id} → operator removes the resource
+   Decommission callback received → entity status: DECOMMISSIONED (transient)
+
+4. Re-dispatch the original Requested State payload to the Service Provider
+   POST / with the original CreateRequest body
+   No layer enrichment — payload is used verbatim
+   No policy re-evaluation — payload is used verbatim
+   NOTE: resource_type_uuid and resource_type_name are still validated
+         against the current Resource Type Registry (VER-009 compatibility check)
+
+5. Realization callback received → entity returns to OPERATIONAL
+   A new Realized State record is written (linked to the original Requested State)
+   The entity_uuid is preserved — external references remain valid
+
+6. Operation reaches done: true
+   operation.response contains the realized entity
+```
+
+**The key distinction from Rehydration (intent mode):**
+Static Replace bypasses the entire layer assembly and policy evaluation pipeline.
+It takes the already-assembled, already-approved Requested State and re-executes it.
+This makes it deterministic — the result is the same resource on equivalent hardware.
+If standards have changed since the original provisioning and you need the resource
+to comply with current standards, use `mode: intent` (Rehydration) instead.
+
+**Precaution — IaC parity:**
+For Static Replace to be reliable, the application and its data must be on a separate
+partition or external storage. The VM's OS and configuration layers are what Static
+Replace rebuilds. Application data on the OS volume will be lost. This mirrors the
+assumption stated in the PDF architecture: *"Application install and data exist on
+separate partition."*
+
+**Orchestration Flow Policy (Static Replace):**
+
+```rego
+package dcm.orchestration.static_replace
+
+# Fired when consumer requests static rehydration
+steps := [
+    {"step": 1, "payload_type": "lifecycle.rehydration_requested",
+     "condition": "payload.mode == 'static'",
+     "policy_handle": "system/lifecycle/validate-static-replace-preconditions"},
+    {"step": 2, "payload_type": "lifecycle.static_replace_validated",
+     "policy_handle": "system/provider/decommission-for-replace"},
+    {"step": 3, "payload_type": "lifecycle.decommission_confirmed",
+     "policy_handle": "system/provider/dispatch-original-requested-state"},
+    {"step": 4, "payload_type": "realization.completed",
+     "policy_handle": "system/lifecycle/restore-operational-state"},
+]
+```
+
+**Related use cases:** See Section 1.8 (Brownfield Ingestion) for bringing existing
+resources under DCM management. See Section 1.5 (Drift Detection) for reconciling
+drift rather than replacing. See the Ingestion Model (doc 13) for the `mode: intent`
+Rehydration flow (replaying intent through current policies).
+
+---
+
+## 1.10 VM Lifecycle — In-Place Upgrade (Leapp / IPU Pattern)
+
+Upgrade the OS of a running VM in-place, managed as a DCM lifecycle event.
+This preserves the VM entity UUID, Requested State, and Realized State chain —
+the VM is the same DCM entity before and after the upgrade.
+
+**Preconditions:**
+- Entity is `OPERATIONAL`
+- The upgrade automation exists as a registered Process Resource Type
+  (e.g., `Process.LeappUpgrade`, `Process.OSUpgrade`)
+- A backup or snapshot policy is active for this entity
+
+**Workflow:**
+
+```
+Consumer: POST /api/v1/requests
+  {
+    "catalog_item_uuid": "{leapp-upgrade-catalog-item-uuid}",
+    "fields": {
+      "target_entity_uuid": "{vm-entity-uuid}",
+      "target_os_version": "RHEL 9.4",
+      "pre_upgrade_snapshot": true,
+      "maintenance_window_uuid": "{mw-uuid}"   // optional
+    }
+  }
+```
+
+**What DCM does:**
+
+```
+1. Policy Engine validates:
+   - Target entity is OPERATIONAL
+   - Target OS version is in the approved versions list (Core Policy)
+   - Maintenance window is active (if required by policy)
+   - Pre-upgrade snapshot capability exists on the provider
+
+2. Entity enters UPDATING lifecycle state
+   (DCM marks entity in maintenance — routing at LTM layer should drain)
+
+3. Process Resource entity created (Process.LeappUpgrade)
+   UUID assigned; linked to the VM entity via 'operational' relationship
+   Dispatched to the Service Provider as a process execution request
+
+4. Service Provider executes upgrade automation
+   Interim status callbacks update the Process Resource entity status
+   VM entity remains UPDATING throughout
+
+5. Post-upgrade validation runs:
+   - Health checks pass
+   - OS version matches target_os_version in the Realized State
+
+6. New Realized State record written for the VM entity
+   delta_fields: {os_version: "RHEL 9.4", last_upgraded_at: }
+   The Process Resource entity moves to DECOMMISSIONED (process complete)
+
+7. Entity returns to OPERATIONAL
+   Maintenance mode released — routing restored
+```
+
+**Key DCM properties preserved:**
+The VM entity UUID does not change. The Requested State (original intent) does not
+change. The upgrade is recorded as a new Realized State record with `delta_fields`
+carrying the changed values, linked to the prior Realized State. The full provenance
+chain is intact for audit.
+
+---
+
+
+# Section 2 — Provider Interaction Examples
+
+## 2.1 Service Provider — Full Dispatch Cycle
+
+```
+DCM sends dispatch payload to Service Provider endpoint:
+
+POST https://provider.example.com/dispatch
+Authorization: mTLS + scoped credential (scope: dispatch, entity: ent-001, ttl: PT15M)
+Content-Type: application/json
+
+{
+  "dispatch_uuid": "disp-001",
+  "entity_uuid": "ent-001",
+  "requested_state_uuid": "req-state-001",
+  "payload": {
+    "resource_type": "Compute.VirtualMachine",
+    "fields": {
+      "cpu_count": { "value": 4, "provenance": {...} },
+      "memory_gb": { "value": 8, "provenance": {...} },
+      "os_family": { "value": "rhel", "provenance": {...} },
+      "monitoring_endpoint": {
+        "value": "https://metrics.internal.prod.example.com",
+        "provenance": { "origin": { "source_type": "policy",
+                                    "source_uuid": "transform-inject-monitoring" } }
+      }
+    }
+  }
+}
+
+Provider naturalizes (DCM → OpenStack Nova):
+{
+  "server": {
+    "name": "ent-001",
+    "flavorRef": "m1.xlarge",    # 4 vCPU, 8GB
+    "imageRef": "rhel-9.2-latest",
+    "metadata": { "dcm_entity_uuid": "ent-001",
+                  "dcm_requested_state": "req-state-001",
+                  "monitoring_endpoint": "https://metrics..." }
+  }
+}
+
+OpenStack provisions → returns server object.
+
+Provider denaturalizes (OpenStack → DCM unified):
+{
+  "realized_state_uuid": "real-001",
+  "entity_uuid": "ent-001",
+  "corresponding_requested_state_uuid": "req-state-001",
+  "source_type": "initial_realization",
+  "fields": {
+    "cpu_count": { "value": 4, ... },
+    "memory_gb": { "value": 8, ... },
+    "provider_entity_id": { "value": "vm-0a1b2c3d" },
+    "assigned_ip_address": { "value": "10.1.45.23" },
+    "hypervisor_host": { "value": "compute-07.eu-west" }
+  }
+}
+
+DCM receives → writes to Realized Store.
+```
+
+## 2.2 Information Provider — Assembly Enrichment
+
+```
+During layer assembly Step 2 (layer resolution), DCM queries CMDB Information Provider:
+
+POST https://cmdb.corp.example.com/query
+Authorization: mTLS
+{
+  "query_uuid": "qry-001",
+  "data_type": "business_data",
+  "lookup_key": { "type": "actor_uuid", "value": "actor-payments-001" }
+}
+
+Response:
+{
+  "data": {
+    "business_unit": { "value": "Payments Platform",
+                       "confidence": { "band": "very_high", "score": 97 },
+                       "authority_level": "primary" },
+    "cost_center": { "value": "PAYM-4421",
+                     "confidence": { "band": "very_high", "score": 97 } },
+    "product_owner": { "value": "Jane Smith",
+                       "confidence": { "band": "high", "score": 85 } }
+  },
+  "data_freshness": "2026-03-15T08:00:00Z"
+}
+
+DCM injects into assembled payload as a data layer:
+  business_unit.provenance.origin.source_type = "information_provider"
+  business_unit.provenance.origin.source_uuid = "cmdb-provider-uuid"
+```
+
+## 2.3 Internal Policy Evaluation — OPA Sidecar
+
+```
+Assembly reaches Step 5 (pre-placement policy processing):
+
+DCM sends payload to OPA sidecar:
+POST http://opa-sidecar:8181/v1/data/dcm/gatekeeper/vm_size_limits
+{
+  "input": {
+    "payload": {
+      "type": "request.layers_assembled",
+      "fields": { "cpu_count": { "value": 4 }, ... }
+    },
+    "actor": { "uuid": "actor-001", "roles": ["developer"],
+               "tenant_uuid": "payments-uuid" },
+    "deployment": { "deployment_posture": "prod",
+                    "compliance_domains": ["hipaa"] },
+    "entity": null,
+    "provider": null
+  }
+}
+
+OPA response:
+{
+  "result": {
+    "allow": true,
+    "deny": [],
+    "field_locks": [],
+    "warnings": []
+  }
+}
+
+DCM Policy Engine reads result → allow → pipeline continues.
+```
+
+## 2.4 notification service — Relationship Graph Audience
+
+```
+Event: entity.decommissioning (VLAN-100 entering DECOMMISSIONING state)
+
+Notification Router:
+  1. Load relationship graph for VLAN-100:
+     VM-A (AppTeam, attached_to, stake_strength: required)
+     VM-B (DevTeam, attached_to, stake_strength: required)
+     VM-C (OpsTeam, attached_to, stake_strength: optional)
+
+  2. Resolve audiences:
+     VLAN-100 owner (NetworkOps): audience_role = owner
+     VM-A owner (AppTeam admin): audience_role = stakeholder
+       stakeholder_reason: { via_entity: "VM-A", via_relationship: "attached_to" }
+     VM-B owner (DevTeam admin): audience_role = stakeholder
+     VM-C owner (OpsTeam admin): audience_role = observer (optional stake)
+
+  3. Per-actor notification envelopes generated (4 total)
+
+POST https://slack-notif.corp.example.com/deliver
+{
+  "notification_uuid": "notif-001",
+  "event_type": "entity.decommissioning",
+  "urgency": "high",
+  "entity": { "uuid": "vlan-100-uuid", "display_name": "VLAN-100" },
+  "audience": {
+    "actor_uuid": "appteam-admin-uuid",
+    "audience_role": "stakeholder",
+    "stakeholder_reason": {
+      "via_entity_uuid": "vm-a-uuid",
+      "via_entity_display_name": "VM-A (payments-api-server-01)",
+      "via_relationship_type": "attached_to"
+    }
+  },
+  "context": { "change_summary": "VLAN-100 decommission initiated" },
+  "requires_action": false
+}
+
+Slack provider delivers:
+  "#payments-platform: ⚠️ VLAN-100 is being decommissioned.
+   Your VM 'payments-api-server-01' is attached to it.
+   Action required: migrate VM network attachment before decommission completes."
+```
+
+---
+
+# Section 3 — Consumer API Examples
+
+## 3.1 Complete Request Lifecycle (API Perspective)
+
+```
+# 1. Browse catalog
+GET /api/v1/catalog?category=Compute
+X-DCM-Tenant: payments-tenant-uuid
+Authorization: Bearer 
+
+Response: { "catalog_items": [
+  { "catalog_item_uuid": "vm-standard-uuid",
+    "resource_type": "Compute.VirtualMachine",
+    "display_name": "Standard Linux VM",
+    "estimated_cost": { "per_hour": 0.32, "currency": "USD" },
+    "accreditations": [{ "framework": "hipaa", "status": "active" }]
+  }
+] }
+
+# 2. Describe catalog item (see schema + constraints)
+GET /api/v1/catalog/vm-standard-uuid
+
+Response includes:
+  "schema.fields[cpu_count].constraint": { "type": "range", "min": 1, "max": 32 }
+  "schema.fields[monitoring_agent].constraint.visibility": "hidden"  # injected by policy
+
+# 3. Submit request
+POST /api/v1/requests
+{ "catalog_item_uuid": "vm-standard-uuid",
+  "fields": { "cpu_count": 4, "memory_gb": 8, "os_family": "rhel",
+              "name": "payments-api-server-01" } }
+
+Response 202: { "request_uuid": "req-001", "entity_uuid": "ent-001",
+                "status": "ACKNOWLEDGED",
+                "status_url": "/api/v1/requests/req-001/status" }
+
+# 4. Poll status (or use webhook)
+GET /api/v1/requests/req-001/status
+
+Sequence of responses:
+  { "status": "ASSEMBLING" }           # layer assembly running
+  { "status": "DISPATCHED" }           # sent to provider
+  { "status": "PROVISIONING" }         # provider executing
+  { "status": "COMPLETED",
+    "resource_url": "/api/v1/resources/ent-001" }
+
+# 5. Get realized resource
+GET /api/v1/resources/ent-001
+
+Response:
+{ "entity_uuid": "ent-001",
+  "lifecycle_state": "OPERATIONAL",
+  "drift_status": "clean",
+  "fields": {
+    "cpu_count": { "value": 4, "confidence": { "band": "very_high" } },
+    "assigned_ip_address": { "value": "10.1.45.23",
+                              "confidence": { "band": "very_high" } }
+  },
+  "estimated_cost_per_hour": 0.32 }
+```
+
+## 3.2 Provider Update Notification — Consumer Approval Flow
+
+```
+# Provider submits auto-scale notification (memory doubled)
+POST /api/v1/provider/entities/ent-001/update-notification
+Authorization: mTLS (provider cert)
+{ "provider_uuid": "eu-west-prod-1-uuid",
+  "notification_uuid": "notif-001",
+  "notification_type": "auto_scale",
+  "changed_fields": {
+    "memory_gb": { "previous_value": 8, "new_value": 16,
+                   "change_reason": "Auto-scale at 85% utilization" }
+  } }
+
+→ DCM evaluates: no pre-authorization policy for this tenant → REQUIRES_CONSUMER_APPROVAL
+→ Entity → PENDING_REVIEW
+→ Notification to owner: "Provider requests to update memory_gb: 8→16. Approve?"
+
+# Consumer reviews pending notification
+GET /api/v1/resources/ent-001/provider-notifications
+
+Response: { "notifications": [{
+  "notification_uuid": "notif-001",
+  "notification_type": "auto_scale",
+  "status": "pending_approval",
+  "change_summary": "memory_gb: 8 → 16",
+  "change_reason": "Auto-scale at 85% utilization"
+}] }
+
+# Consumer approves
+POST /api/v1/resources/ent-001/provider-notifications/notif-001:approve
+{ "decision": "approve", "reason": "Legitimate auto-scale event" }
+
+Response 202: { "decision": "approve", "realized_state_uuid": "real-002" }
+
+→ New Requested State created (source_type: provider_update)
+→ New Realized State snapshot written (memory_gb: 16)
+→ Audit: PROVIDER_UPDATE_APPLIED
+```
+
+---
+
+# Section 4 — Admin API Examples
+
+## 4.1 Review and Approve Provider Registration
+
+```
+# New provider submitted registration
+# Platform admin receives notification (urgency: medium)
+# "New provider registration pending review: eu-west-prod-1"
+
+# List pending registrations
+GET /api/v1/admin/registrations/pending
+Authorization: Bearer 
+
+Response: { "registrations": [{
+  "registration_uuid": "reg-001",
+  "provider_type_id": "service_provider",
+  "handle": "org/compute/eu-west-prod-1",
+  "submitted_at": "2026-03-15T09:00:00Z",
+  "validation_status": "passed",        # all 8 automated checks passed
+  "sovereignty_zone": "eu-west-sovereign",
+  "accreditations": [{ "framework": "hipaa", "type": "baa" }],
+  "health_check_status": "healthy",
+  "governance_matrix_pre_check": "ALLOW"
+}] }
+
+# Admin reviews and approves
+POST /api/v1/admin/registrations/reg-001:approve
+{ "review_notes": "Certificate verified against corp CA. BAA reviewed and valid." }
+
+Response: { "registration_uuid": "reg-001", "status": "ACTIVE" }
+
+→ Provider enters active registry
+→ Governance Matrix re-evaluated with this provider active
+→ Notification to provider operator: "Registration approved. Provider UUID: eu-west-prod-1-uuid"
+```
+
+## 4.2 Resolve Orphan Candidate
+
+```
+# Discovery found vm-legacy-0001 after a timeout-cancelled request
+
+GET /api/v1/admin/orphans
+Response: { "orphan_candidates": [{
+  "orphan_candidate_uuid": "orp-001",
+  "provider_uuid": "eu-west-prod-1-uuid",
+  "provider_entity_id": "vm-legacy-0001",
+  "suspected_request_uuid": "req-failed-001",
+  "resource_type": "Compute.VirtualMachine",
+  "discovered_at": "2026-03-15T10:30:00Z",
+  "status": "under_review"
+}] }
+
+# Admin investigates: vm-legacy-0001 matches the timed-out request
+# Decision: adopt into DCM lifecycle under the original requesting tenant
+
+POST /api/v1/admin/orphans/orp-001/resolve
+{ "resolution": "adopt_into_dcm",
+  "reason": "Confirmed match for timed-out request req-failed-001",
+  "target_tenant_uuid": "payments-tenant-uuid" }
+
+Response: { "resolution": "adopt_into_dcm",
+            "new_entity_uuid": "ent-001",    # original entity UUID preserved
+            "status": "OPERATIONAL" }
+
+→ Entity promoted from orphan candidate to full DCM lifecycle
+→ Realized State written
+→ drift detection activated
+→ original request_uuid marked COMPLETED (late completion)
+```
+
+---
+
+# Section 5 — Registration Flow Example
+
+## 5.1 Complete Provider Onboarding — Service Provider
+
+```
+# Step 1: Platform admin issues registration token
+POST /api/v1/admin/registration-tokens
+{ "provider_type_id": "service_provider",
+  "expires_in": "PT72H",
+  "scope": {
+    "provider_handle_pattern": "org/compute/eu-west-*",
+    "sovereignty_zone": "eu-west-sovereign",
+    "grants_auto_approval": false    # human review still required
+  },
+  "purpose": "EU-WEST production compute provider onboarding" }
+
+Response: { "token_uuid": "tok-001",
+            "token_value": "DCM_REG_abc123...",   # shown once only
+            "expires_at": "2026-03-18T09:00:00Z" }
+
+# Step 2: Provider operator submits registration
+POST /api/v1/provider/register
+X-DCM-Registration-Token: DCM_REG_abc123...
+Content-Type: application/json
+# (mTLS certificate presented at TLS layer)
+{
+  "provider_type_id": "service_provider",
+  "handle": "org/compute/eu-west-prod-1",
+  "display_name": "EU West Production Compute",
+  "version": "2.1.0",
+  "sovereignty_declaration": {
+    "operating_jurisdictions": ["DE", "FR", "NL"],
+    "data_residency_zones": ["eu-west-sovereign"]
+  },
+  "accreditations": [
+    { "accreditation_uuid": "acc-hipaa-001", "framework": "hipaa",
+      "accreditation_type": "baa", "status": "active" }
+  ],
+  "capabilities": {
+    "resource_types": [
+      { "fqn": "Compute.VirtualMachine", "spec_version": "2.1.0",
+        "catalog_item_uuid": "vm-standard-uuid" }
+    ],
+    "cancellation": { "supports_cancellation": true,
+                      "cancellation_supported_during": ["DISPATCHED", "PROVISIONING"] },
+    "discovery": { "supports_discovery": true, "discovery_method": "api_query" },
+    "cost_metadata": { "opex_per_unit_per_hour": 0.28, "currency": "USD" }
+  },
+  "health_endpoint": "https://eu-west-prod-1.corp.example.com/health",
+  "delivery_endpoint": "https://eu-west-prod-1.corp.example.com/dispatch"
+}
+
+Response 202: { "registration_uuid": "reg-001", "status": "VALIDATING",
+                "token_recognized": true, "auto_approval_eligible": false }
+
+# Step 3: Automated validation runs (8 checks)
+# V1: service_provider enabled in prod profile ✓
+# V2: Governance Matrix pre-check: ALLOW ✓
+# V3: Token valid, matches handle pattern ✓
+# V4: mTLS certificate valid, corp CA chain ✓
+# V5: Sovereignty declaration complete ✓
+# V6: Capability declaration internally consistent ✓
+# V7: Health endpoint reachable, returns { "status": "healthy" } ✓
+# V8: BAA accreditation present (prod requires accreditation submission) ✓
+→ Status → PENDING_APPROVAL
+
+# Step 4: Platform admin notified, reviews, approves (see Section 4.1)
+# Step 5: Status → ACTIVE
+# Provider enters registry, capacity monitoring begins
+```
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+# Section 6 — Provider Type Examples
+
+## 6.1 data store — State Store Write and Read
+
+A data store persists and streams DCM internal state. This example shows
+the full lifecycle: DCM writing a Requested State record to a PostgreSQL-backed
+data store, followed by the Request Orchestrator reading it back.
+
+**Provider registration excerpt:**
+
+```yaml
+(prescribed infrastructure)_registration:
+  provider_type: (prescribed infrastructure)
+  display_name: "Primary PostgreSQL State Store"
+  endpoint: https://pg-state.internal:5432
+  storage_sub_type: relational_state
+  stores_owned:
+    - store_type: requested_state
+    - store_type: realized_state
+    - store_type: intent_state
+  consistency_guarantee: strong          # synchronous write confirmation
+  replication:
+    strategy: synchronous_replica
+    replica_endpoints:
+      - https://pg-state-replica-1.internal:5432
+      - https://pg-state-replica-2.internal:5432
+  provenance_emission: true              # emits audit event on every write
+```
+
+**Write: DCM persists a Requested State record**
+
+```
+DCM Request Orchestrator
+  │
+  ▼ POST https://pg-state.internal/api/v1/records
+    Authorization: Bearer 
+    Content-Type: application/json
+    {
+      "record_type": "requested_state",
+      "entity_uuid": "a1b2c3d4-...",
+      "tenant_uuid": "t1t2t3t4-...",
+      "request_uuid": "r1r2r3r4-...",
+      "payload": { ... assembled request payload ... },
+      "written_at": "2026-03-31T10:00:00Z"
+    }
+
+Provider response:
+    {
+      "record_uuid": "s1s2s3s4-...",
+      "written_at": "2026-03-31T10:00:00.042Z",
+      "replicated": true
+    }
+
+Provider emits provenance event:
+    event_type: storage.record_written
+    record_type: requested_state
+    record_uuid: s1s2s3s4-...
+    entity_uuid: a1b2c3d4-...
+    store_provider_uuid: 
+```
+
+**Read: Drift scheduler retrieves Realized State for comparison**
+
+```
+DCM Drift Scheduler
+  │
+  ▼ GET https://pg-state.internal/api/v1/records
+    ?entity_uuid=a1b2c3d4-...
+    &record_type=realized_state
+    &version=latest
+    Authorization: Bearer 
+
+Provider response:
+    {
+      "record_uuid": "z9z8z7z6-...",
+      "entity_uuid": "a1b2c3d4-...",
+      "record_type": "realized_state",
+      "payload": { ... realized state snapshot ... },
+      "written_at": "2026-03-31T09:55:00Z",
+      "supersedes_uuid": "y8y7y6y5-..."
+    }
+```
+
+---
+
+## 6.2 Auth Provider — OIDC Cutover (GitHub OAuth → Corporate OIDC)
+
+DCM's auth configuration is a versioned artifact. Adding a new Auth Provider
+and cutting over is a standard GitOps PR workflow — no downtime.
+
+**Step 1: Register the corporate OIDC provider**
+
+```yaml
+# GitOps PR: auth-providers/corporate-oidc-v1.yaml
+auth_provider:
+  handle: "auth-providers/corporate-oidc"
+  version: "1.0.0"
+  status: developing               # shadow mode — not yet enforced
+  provider_type: oidc
+  display_name: "Corporate OIDC (Keycloak)"
+  endpoint: https://sso.corp.internal/realms/dcm
+  client_id: dcm-control-plane
+  client_secret_ref: credential://vault/dcm/oidc-client-secret
+  scopes: [openid, profile, email, groups]
+  group_claim: "dcm_groups"       # claim containing DCM group memberships
+  mfa_required: true
+  profile_overlay: standard       # overrides to fsi/sovereign possible
+```
+
+**Step 2: Shadow evaluation (parallel run)**
+
+```
+Platform Admin: PATCH /api/v1/admin/auth-providers/{uuid}
+  { "status": "proposed" }
+
+DCM response:
+  Both providers now evaluate all logins in parallel.
+  auth.session_created events include shadow_auth_result for comparison.
+  Divergences (user authenticated by one provider but not the other)
+  are surfaced via governance.auth_shadow_divergence events.
+
+After 72 hours of shadow evaluation:
+  Shadow report: 1,847 logins evaluated
+    Converged: 1,845 (99.9%)
+    Diverged: 2 (both: OIDC rejected due to missing group claim — fixed)
+```
+
+**Step 3: Activate and retire GitHub OAuth**
+
+```
+Platform Admin: PATCH /api/v1/admin/auth-providers/{oidc-uuid}
+  { "status": "active" }
+
+Platform Admin: PATCH /api/v1/admin/auth-providers/{github-uuid}
+  { "status": "deprecated" }
+
+DCM response:
+  Corporate OIDC: primary, enforced
+  GitHub OAuth: accepted for 30 days (configured sunset), then retired
+  All existing sessions: revoked (auth.session_revoked × N users)
+  Users: re-authenticate on next request
+```
+
+---
+
+## 6.3 credential management service — SSH Key Issuance After VM Realization
+
+After a VM is realized, the credential management service issues an SSH key pair to the
+requesting consumer — scoped to that specific entity.
+
+**Flow:**
+
+```
+1. Consumer: POST /api/v1/requests
+     catalog_item_uuid: 
+     fields: { cpu: 4, ram_gb: 16, os: "RHEL 9.4" }
+
+2. Policy evaluation — Transformation policy injects credential requirement:
+     fields.credential_requirements:
+       - credential_type: ssh_key
+         issued_to: requesting_actor
+         scope: [ssh_access]
+         ttl: P90D             # 90-day key lifetime
+
+3. VM realization completes → entity_uuid: vm-abc123
+
+4. DCM dispatches credential issuance sub-request:
+   POST https://vault.internal/api/v1/credentials
+     {
+       "credential_type": "ssh_key",
+       "entity_uuid": "vm-abc123",
+       "issued_to_actor_uuid": "actor-xyz",
+       "scope": ["ssh_access"],
+       "ttl": "P90D"
+     }
+
+5. credential management service (HashiCorp Vault) response:
+     {
+       "credential_uuid": "cred-456",
+       "public_key": "ssh-ed25519 AAAA...",
+       "private_key_ref": "vault://dcm/ssh-keys/cred-456/private",
+       "expires_at": "2026-06-30T00:00:00Z"
+     }
+
+6. Consumer retrieves credential:
+   GET /api/v1/resources/vm-abc123/credentials/cred-456/value
+     Authorization: Bearer 
+
+   Response:
+     {
+       "credential_uuid": "cred-456",
+       "credential_type": "ssh_key",
+       "public_key": "ssh-ed25519 AAAA...",
+       "private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n...",
+       "expires_at": "2026-06-30T00:00:00Z"
+     }
+   Note: private_key delivered once at retrieval; not stored in DCM.
+```
+
+**Rotation at P45D (50% of lifetime):**
+
+```
+credential management service fires: credential.rotation_due
+  credential_uuid: cred-456
+  entity_uuid: vm-abc123
+  days_until_expiry: 45
+
+DCM Policy: Transformation → auto-issue renewal credential
+  New credential_uuid: cred-789
+  expires_at: 2026-09-30 (new 90-day window)
+
+credential.rotated event → consumer notified via webhook
+Old credential (cred-456): expires_at unchanged, both active during overlap
+At expires_at: cred-456 → expired, credential.expired event
+```
+
+---
+
+## 6.4 compound service definition — Three-Tier Web Application Stack
+
+A compound service definition composes multiple atomic service providers into a single catalog
+item. The consumer requests one thing; DCM orchestrates the constituent parts.
+
+**compound service definition registration (compound service):**
+
+```yaml
+compound service_registration:
+  provider_type: compound service
+  display_name: "Three-Tier Web App Stack"
+  resource_types_composed:
+    - fqn: ApplicationStack.WebApp
+      version: "1.0.0"
+      constituents:
+        - component_id: db
+          resource_type: Compute.VirtualMachine
+          provided_by: external            # DCM selects a compute provider
+          depends_on: []
+          required_for_delivery: required
+
+        - component_id: app
+          resource_type: Compute.VirtualMachine
+          provided_by: external
+          depends_on: [db]                 # waits for db realization
+          inject_from:
+            - component: db
+              field: realized_fields.primary_ip
+              into: fields.db_host
+          required_for_delivery: required
+
+        - component_id: lb
+          resource_type: Network.LoadBalancer
+          provided_by: external
+          depends_on: [app]
+          inject_from:
+            - component: app
+              field: realized_fields.primary_ip
+              into: fields.backend_pool[0]
+          required_for_delivery: required
+
+        - component_id: dns
+          resource_type: Network.DNSRecord
+          provided_by: external
+          depends_on: [lb]
+          inject_from:
+            - component: lb
+              field: realized_fields.vip
+              into: fields.record_value
+          required_for_delivery: optional  # stack delivered without DNS if it fails
+```
+
+**Consumer request:**
+
+```
+POST /api/v1/requests
+  {
+    "catalog_item_uuid": "",
+    "fields": {
+      "app_name": "payments-api",
+      "environment": "prod",
+      "db_size": "large",
+      "app_replicas": 3
+    }
+  }
+```
+
+**What DCM orchestrates (transparent to consumer):**
+
+```
+Request 1 → db VM (dispatched immediately)
+  Realized: db.primary_ip = 10.0.1.5
+
+Request 2 → app VM (dispatched after db realized)
+  fields.db_host = 10.0.1.5 (injected from db realization)
+  Realized: app.primary_ip = 10.0.1.10
+
+Request 3 → load balancer (dispatched after app realized)
+  fields.backend_pool[0] = 10.0.1.10 (injected)
+  Realized: lb.vip = 203.0.113.42
+
+Request 4 → DNS record (dispatched after lb realized)
+  fields.record_value = 203.0.113.42 (injected)
+  Realized: dns.fqdn = payments-api.corp.example.com
+
+Consumer entity: ApplicationStack.WebApp
+  Status: OPERATIONAL
+  Constituent entities: [db-uuid, app-uuid, lb-uuid, dns-uuid]
+  Logical endpoint: payments-api.corp.example.com
+```
+
+---
+
+## 6.5 ITSM integration — ServiceNow Change Request Lifecycle
+
+An ITSM integration creates and manages change tickets in ServiceNow as part of
+DCM request lifecycle gates. The ITSM ticket becomes the approval gate.
+
+**Provider registration:**
+
+```yaml
+itsm_provider_registration:
+  provider_type: itsm_provider
+  display_name: "ServiceNow — Production ITSM"
+  endpoint: https://corp.service-now.com
+  itsm_system: servicenow
+  supported_actions:
+    - create_change_request
+    - update_change_request
+    - close_change_request
+    - get_approval_status
+  field_mapping_ref: "itsm-mappings/servicenow-prod-v1.yaml"
+  cmdb_ci_type_map:
+    Compute.VirtualMachine: cmdb_ci_server
+    Network.VLAN: cmdb_ci_network
+```
+
+**Lifecycle: DCM request requiring Change Approval:**
+
+```
+1. Consumer: POST /api/v1/requests
+     fields: { ... VM configuration ... }
+
+2. GateKeeper policy fires (prod tenant + restricted network):
+     action: require_itsm_approval
+     itsm_provider_uuid: 
+     change_type: standard
+     risk_level: medium
+
+3. DCM → ITSM integration: create_change_request
+     POST https://corp.service-now.com/api/dcm/v1/changes
+     {
+       "short_description": "DCM: Provision Compute.VirtualMachine",
+       "dcm_request_uuid": "req-abc123",
+       "change_type": "standard",
+       "risk": "medium",
+       "implementation_plan": "DCM automated provisioning",
+       "configuration_item": "vm-payments-prod-07",
+       "requested_by": "jane.smith@corp.example.com"
+     }
+
+   ServiceNow response:
+     { "change_number": "CHG0012345", "state": "Assess", "sys_id": "abc123" }
+
+4. DCM request status: AWAITING_EXTERNAL_APPROVAL
+   Consumer notified: "Change request CHG0012345 created — awaiting approval"
+
+5. ServiceNow CAB approves → webhook to DCM:
+   POST /api/v1/admin/itsm-events
+   {
+     "itsm_provider_uuid": "",
+     "change_number": "CHG0012345",
+     "dcm_request_uuid": "req-abc123",
+     "event_type": "approved",
+     "approved_by": "change.manager@corp.example.com",
+     "approved_at": "2026-03-31T14:00:00Z"
+   }
+
+6. DCM resumes request dispatch → VM provisioned
+   ITSM integration: update_change_request → state: Implement
+
+7. VM realized → ITSM integration: close_change_request
+     { "state": "Closed Complete", "close_notes": "DCM: entity vm-xyz OPERATIONAL" }
+```
+
+---
+
+## 6.6 event routing service — External System Event Bridge
+
+A event routing service bridges DCM events to external messaging infrastructure.
+This example shows DCM publishing entity lifecycle events to an Apache Kafka topic.
+
+**Provider registration:**
+
+```yaml
+(optional infrastructure)_registration:
+  provider_type: (optional infrastructure)
+  display_name: "Kafka Event Bridge — Infrastructure Events"
+  endpoint: https://kafka-bridge.internal:9092
+  protocol: kafka
+  subscribed_event_types:
+    - entity.lifecycle_changed
+    - drift.detected
+    - accreditation.status_changed
+    - provider.status_changed
+  topic_mapping:
+    entity.lifecycle_changed: dcm.infrastructure.lifecycle
+    drift.detected: dcm.infrastructure.drift
+    accreditation.status_changed: dcm.compliance.accreditation
+    provider.status_changed: dcm.infrastructure.providers
+  delivery_guarantee: at_least_once
+  dead_letter_topic: dcm.dlq
+```
+
+**Flow: VM reaches OPERATIONAL → Kafka message published:**
+
+```
+DCM internal: entity.lifecycle_changed event fires
+  entity_uuid: vm-abc123
+  resource_type: Compute.VirtualMachine
+  from_state: PROVISIONING
+  to_state: OPERATIONAL
+  tenant_uuid: t1t2...
+
+event routing service receives event, publishes to Kafka:
+  Topic: dcm.infrastructure.lifecycle
+  Key: vm-abc123
+  Value: {
+    "event_type": "entity.lifecycle_changed",
+    "entity_uuid": "vm-abc123",
+    "resource_type": "Compute.VirtualMachine",
+    "from_state": "PROVISIONING",
+    "to_state": "OPERATIONAL",
+    "tenant_uuid": "t1t2...",
+    "timestamp": "2026-03-31T10:05:33Z",
+    "dcm_instance_uuid": ""
+  }
+
+External consumer (monitoring pipeline) processes message:
+  → Updates CMDB record
+  → Triggers monitoring agent installation
+  → Notifies application team
+```
+
+**Dead letter handling:**
+
+```
+Kafka publish fails (broker unreachable):
+  event routing service: retry with exponential backoff (3 attempts)
+  After threshold: publish to dcm.dlq with failure metadata
+  Fire: message_bus.delivery_failed (urgency: medium) → Platform Admin
+  DCM: event stored in event routing service's local queue for replay
+```
+
+---
+
+# Section 7 — Policy Type Examples
+
+## 7.1 Transformation Policy — Automatic Data Enrichment
+
+Transformation policies modify the request payload before dispatch. They run
+after validation passes and before placement selection.
+
+**Use case:** Auto-inject the required OS image version based on the requested
+OS name and the current approved version from an Information Provider.
+
+```rego
+package dcm.policy.transform.os_image_injection
+
+import future.keywords.if
+
+# When consumer requests a VM with os_name but no os_image_uuid:
+transform if {
+    input.payload.resource_type == "Compute.VirtualMachine"
+    input.payload.fields.os_name != null
+    input.payload.fields.os_image_uuid == null
+}
+
+output := {
+    "output_type": "transformation",
+    "field_injections": [{
+        "field": "fields.os_image_uuid",
+        "value": data.information_providers.os_registry.current_approved[
+            input.payload.fields.os_name
+        ],
+        "source": "policy/transform/os-image-injection",
+        "immutable": true      # consumer cannot override after injection
+    }],
+    "audit_annotations": [{
+        "key": "os_image_injected_at",
+        "value": time.now_ns()
+    }]
+}
+```
+
+**In practice:**
+
+```
+Consumer submits:
+  { "os_name": "RHEL 9", "cpu": 4, "ram_gb": 16 }
+
+After transformation:
+  { "os_name": "RHEL 9", "cpu": 4, "ram_gb": 16,
+    "os_image_uuid": "img-rhel9-20260315",   ← injected
+    "_provenance": {
+      "os_image_uuid": {
+        "source": "policy/transform/os-image-injection",
+        "immutable": true,
+        "injected_at": 1743412800000
+      }
+    }
+  }
+
+If consumer tries to override os_image_uuid: Policy Engine rejects with:
+  { "code": "FIELD_IMMUTABLE",
+    "field": "fields.os_image_uuid",
+    "set_by": "policy/transform/os-image-injection" }
+```
+
+---
+
+## 7.2 Placement Policy — Provider Selection with Constraints
+
+Placement policies express where a resource should be dispatched. DCM's
+Placement Engine evaluates all registered providers against placement policies
+plus the Scoring Model.
+
+**Use case:** For a PHI-classified VM, require a provider with HIPAA BAA
+accreditation, in Zone A or Zone B, with at least 30% capacity remaining.
+
+```rego
+package dcm.policy.placement.phi_vm
+
+import future.keywords.if
+
+placement if {
+    input.payload.resource_type == "Compute.VirtualMachine"
+    input.payload.data_classification == "phi"
+}
+
+output := {
+    "output_type": "placement",
+    "require": {
+        "accreditations": ["hipaa_baa"],          # provider must hold active BAA
+        "sovereignty_zones": ["US-EAST", "US-WEST"], # data must stay in US
+        "availability_zones": ["zone-a", "zone-b"],
+        "minimum_capacity_pct": 30
+    },
+    "prefer": {
+        "accreditations": ["fedramp_moderate"],   # prefer FedRAMP if available
+        "availability_zones": ["zone-a"]          # prefer zone-a (lower latency)
+    },
+    "exclude": {
+        "provider_uuids": []                      # no explicit exclusions
+    }
+}
+```
+
+**Placement Engine evaluation:**
+
+```
+Candidate providers for Compute.VirtualMachine:
+  Provider A: hipaa_baa=✅  zone-a=✅  capacity=45%  fedramp=❌
+  Provider B: hipaa_baa=✅  zone-b=✅  capacity=72%  fedramp=✅
+  Provider C: hipaa_baa=❌  zone-a=✅  capacity=88%
+
+Filtering (require):
+  Provider C: eliminated (no hipaa_baa)
+
+Scoring (prefer + Scoring Model):
+  Provider B: higher score (fedramp preferred, higher capacity)
+  Provider A: lower score (no fedramp)
+
+Selected: Provider B
+  dispatch: CreateRequest → Provider B
+  placement_audit:
+    policy: placement/phi-vm
+    evaluated: [provider-a, provider-b, provider-c]
+    eliminated: [provider-c (missing: hipaa_baa)]
+    selected: provider-b
+    selection_reason: "highest scoring after require filter"
+```
+
+---
+
+## 7.3 Shadow Execution — Safe Policy Rollout
+
+Shadow execution lets a new policy run against real traffic without affecting
+outcomes. Divergences are surfaced for review before the policy goes active.
+
+**Use case:** Testing a new cost-cap gatekeeper before enforcement.
+
+```yaml
+# GitOps PR: policies/cost-cap-v1.yaml
+policy:
+  handle: "tenant/acme/cost-cap-1000"
+  version: "1.0.0"
+  status: proposed               # shadow mode — evaluates but does not enforce
+  type: gatekeeper
+  rules:
+    - condition: "cost_estimate.monthly_usd > 1000 AND tenant.uuid == 'acme-uuid'"
+      action: gate
+      message: "Estimated monthly cost exceeds $1,000 limit for this tenant"
+  shadow_target: "tenant/acme/cost-cap-500"   # compare against existing policy
+```
+
+**Shadow evaluation in practice:**
+
+```
+Request: VM with estimated cost $800/month (tenant: acme)
+
+existing policy (cost-cap-500, active):  GATE — $800 > $500 limit → request blocked
+new policy    (cost-cap-1000, shadow):   PASS — $800 < $1,000 limit
+
+Divergence detected:
+  policy.shadow_divergence event:
+    shadow_policy: cost-cap-1000
+    active_policy: cost-cap-500
+    divergence_type: active_gated_shadow_passed
+    request_uuid: req-abc123
+    estimated_cost: 800.00
+
+After 7 days of shadow evaluation:
+  Report: 234 requests evaluated
+    Converged (both gate):    89 (38%)
+    Converged (both pass):   118 (50%)
+    Diverged (active gates, shadow passes):  27 (12%)
+       ← these are requests that would be UNBLOCKED by the new policy
+
+Platform Admin reviews divergence report:
+  Decision: the 27 unblocked requests are legitimate — activate cost-cap-1000
+  PATCH /api/v1/admin/policies/{shadow-uuid} → { "status": "active" }
+  PATCH /api/v1/admin/policies/{old-uuid}    → { "status": "deprecated" }
+```
+
+---
+
+# Section 8 — Lifecycle and Model Examples
+
+## 8.1 Scheduled Request — Deferred Provisioning with Maintenance Window
+
+A new database VM is required, but the network team's policy requires all new
+network allocations to happen inside an approved maintenance window.
+
+```
+Consumer: POST /api/v1/requests
+  {
+    "catalog_item_uuid": "",
+    "fields": {
+      "db_engine": "postgresql",
+      "storage_gb": 500,
+      "environment": "prod"
+    },
+    "scheduled_at": null,           # not setting explicit time
+    "schedule": {
+      "dispatch": "window",
+      "window_id": "mw-network-weekly-saturday",   # declared maintenance window
+      "not_after": "2026-05-01T00:00:00Z"          # cancel if no window before May
+    }
+  }
+
+Response 200 — returns Operation:
+  {
+    "name": "/api/v1/operations/req-abc123",
+    "done": false,
+    "metadata": {
+      "stage": "SCHEDULED",
+      "resource_uuid": null,
+      "request_uuid": "req-abc123",
+      "scheduled_dispatch": "window",
+      "window_id": "mw-network-weekly-saturday",
+      "next_window_opens": "2026-04-05T02:00:00Z"
+    }
+  }
+
+At 2026-04-05T02:00:00Z (maintenance window opens):
+  DCM dispatches request to Database Service Provider
+  Stage advances: SCHEDULED → DISPATCHED → PROVISIONING → OPERATIONAL
+
+If window is missed and 2026-05-01 arrives without dispatch:
+  Request → CANCELLED
+  reason: "Scheduled dispatch deadline exceeded"
+  consumer notified via webhook
+```
+
+---
+
+## 8.2 Request Dependency Graph — Three-Tier App with Field Injection
+
+A consumer submits a three-tier application as a coordinated dependency group.
+DCM dispatches each tier in order, injecting realized values between tiers.
+
+```
+Consumer: POST /api/v1/request-groups
+  {
+    "group_handle": "payments-v2-deploy",
+    "requests": [
+      {
+        "request_uuid": "req-db-001",         # created beforehand or inline
+        "depends_on": [],
+        "catalog_item_uuid": "",
+        "fields": { "storage_gb": 500, "environment": "prod" }
+      },
+      {
+        "request_uuid": "req-app-001",
+        "depends_on": [
+          {
+            "request_uuid": "req-db-001",
+            "wait_for": "realized",
+            "inject_fields": [
+              {
+                "from_field": "realized_fields.primary_ip",
+                "to_field": "fields.db_host"
+              },
+              {
+                "from_field": "realized_fields.db_port",
+                "to_field": "fields.db_port"
+              }
+            ]
+          }
+        ],
+        "catalog_item_uuid": "",
+        "fields": { "app": "payments-api", "environment": "prod" }
+      },
+      {
+        "request_uuid": "req-lb-001",
+        "depends_on": [
+          {
+            "request_uuid": "req-app-001",
+            "wait_for": "realized",
+            "inject_fields": [
+              {
+                "from_field": "realized_fields.primary_ip",
+                "to_field": "fields.backend_pool[0]"
+              }
+            ]
+          }
+        ],
+        "catalog_item_uuid": "",
+        "fields": { "protocol": "HTTPS", "port": 443 }
+      }
+    ]
+  }
+
+Execution sequence:
+  T+0s:   req-db-001 dispatched (no dependencies)
+  T+45s:  req-db-001 REALIZED → db.primary_ip=10.0.1.5
+  T+45s:  req-app-001 dispatched (dependency met; db_host=10.0.1.5 injected)
+  T+90s:  req-app-001 REALIZED → app.primary_ip=10.0.1.10
+  T+90s:  req-lb-001 dispatched (dependency met; backend_pool[0]=10.0.1.10 injected)
+  T+105s: req-lb-001 REALIZED → lb.vip=203.0.113.42
+
+GET /api/v1/request-groups/payments-v2-deploy:
+  {
+    "group_status": "completed",
+    "requests": [
+      { "request_uuid": "req-db-001",  "status": "REALIZED", "entity_uuid": "vm-db-..." },
+      { "request_uuid": "req-app-001", "status": "REALIZED", "entity_uuid": "vm-app-..." },
+      { "request_uuid": "req-lb-001",  "status": "REALIZED", "entity_uuid": "lb-..." }
+    ]
+  }
+```
+
+---
+
+## 8.3 Authority Tier Routing — Tiered Approval for High-Impact Change
+
+DCM routes approval requests based on the authority tier required by matching
+policies. This example shows a sovereign-profile decommission routed through
+two sequential approval tiers.
+
+**Authority tier definition (organization-configured):**
+
+```yaml
+authority_tiers:
+  - name: operator
+    weight: 10
+    description: "Day-to-day platform operator"
+  - name: team_lead
+    weight: 30
+    description: "Technical team lead or senior engineer"
+  - name: platform_admin
+    weight: 60
+    description: "Platform administration team"
+  - name: ciso_office
+    weight: 100
+    description: "CISO office — for high-impact or compliance-relevant changes"
+```
+
+**GateKeeper policy requiring CISO approval:**
+
+```rego
+package dcm.policy.gate.sovereign_decommission
+
+gate if {
+    input.payload.lifecycle_action == "decommission"
+    input.payload.data_classification == "restricted"
+    input.profile == "sovereign"
+}
+
+output := {
+    "output_type": "gatekeeper",
+    "action": "require_approval",
+    "approval_tiers": ["platform_admin", "ciso_office"],   # sequential
+    "approval_mode": "sequential",
+    "approval_deadline": "P7D",
+    "gate_message": "Decommission of restricted-classified resource in sovereign profile requires Platform Admin and CISO approval"
+}
+```
+
+**Approval flow:**
+
+```
+Request: Decommission VM (restricted data, sovereign profile)
+
+GateKeeper fires:
+  Request → AWAITING_APPROVAL (tier: platform_admin)
+  Notification → Platform Admin audience (urgency: high)
+
+Platform Admin approves:
+  POST /api/v1/approvals/{approval-uuid}/approve
+  Request → AWAITING_APPROVAL (tier: ciso_office)
+  Notification → CISO audience (urgency: high)
+
+CISO approves:
+  POST /api/v1/approvals/{approval-uuid}/approve
+  Request → DISPATCHED → DECOMMISSIONED
+
+Audit trail:
+  gate_evaluation: platform_admin approved by actor-123 at T+2h
+  gate_evaluation: ciso_office approved by actor-456 at T+18h
+  total_gate_duration: 20 hours
+```
+
+---
+
+## 8.4 Rehydration (Intent Mode) — DR Failover to New Datacenter
+
+A business unit needs to redeploy their application in DC2 after DC1 becomes
+unavailable. Rehydration replays original intent through current policies —
+applying today's standards to the original request.
+
+**Contrast with Static Replace** (Section 1.9): Static Replace re-executes the
+Requested State verbatim. Rehydration (intent mode) re-runs the full pipeline
+from Intent State — layer assembly, policy evaluation, and placement selection
+all run fresh.
+
+```
+Consumer: POST /api/v1/resources/{entity_uuid}:rehydrate
+  {
+    "mode": "intent",
+    "reason": "DR failover — DC1 unavailable, deploying to DC2",
+    "placement_constraints": {
+      "require_zones": ["DC2-ZONE-A", "DC2-ZONE-B"],
+      "exclude_zones": ["DC1"]
+    },
+    "reuse_intent_version": null    # null = use original intent as-is
+  }
+
+DCM pipeline:
+  1. Retrieve Intent State for entity_uuid
+     (the consumer's original request, before any policy processing)
+
+  2. Layer assembly runs fresh against current layers
+     (DC2 data center layer, current security baseline, current network config)
+
+  3. Policy evaluation runs fresh
+     (current GateKeeper, Validation, Transformation, Placement policies)
+     Note: Placement constraint: exclude DC1, require DC2
+
+  4. Placement Engine selects DC2 provider
+     (new provider_uuid in the dispatch payload)
+
+  5. Dispatch → DC2 Service Provider
+     New entity realized in DC2
+
+  6. Original entity (DC1): status → INDETERMINATE_REALIZATION
+     (DC1 resources may still exist — drift reconciliation queued)
+
+  7. New entity (DC2): OPERATIONAL
+     entity_uuid: new (original entity retired)
+     intent_state links to original intent_uuid (provenance preserved)
+
+Result:
+  - New VM in DC2 with DC2 network addressing, current OS image, current policies
+  - Full provenance chain: original intent → DC1 realization → DC2 rehydration
+  - Audit record: rehydration_reason, original_entity_uuid, new_entity_uuid
+```
+
+---
+
+## 8.5 Session Revocation — Security Incident Response
+
+A security team detects that an actor's credentials may have been compromised.
+Emergency revocation immediately invalidates all active sessions.
+
+```
+Security Team: POST /api/v1/admin/actors/{actor_uuid}/sessions:revoke-all
+  {
+    "reason": "Suspected credential compromise — security incident INC-2026-042",
+    "revocation_scope": "all_sessions",
+    "urgency": "emergency"
+  }
+
+DCM processes:
+  1. All active sessions for actor_uuid → status: revoked
+     Sessions affected: 3 (web console, API client, CLI)
+     revoked_at: 2026-03-31T16:42:00Z
+     revocation_trigger: security_incident
+     revoked_by: actor-security-team
+
+  2. Session UUIDs added to Session Revocation Registry
+     (fast cache — all API components check this on every request)
+
+  3. Next API request from compromised actor:
+     GET /api/v1/resources (any request)
+     → 401 Unauthorized
+     { "code": "SESSION_REVOKED",
+       "message": "Session has been revoked. Please re-authenticate.",
+       "revoked_at": "2026-03-31T16:42:00Z" }
+
+  4. Events fired:
+     auth.session_revoked × 3 (one per session)
+     auth.emergency_revocation (urgency: critical)
+     → Security Team notified
+     → Audit records written (non-suppressable)
+
+  5. In-flight requests (if any):
+     Requests already dispatched to providers: allowed to complete
+     (provider callbacks authenticated separately via provider callback credential)
+     New requests from this actor: blocked immediately
+
+  6. Actor status → suspended (pending security review)
+     actor.suspended event → Platform Admin
+
+Recovery:
+  After investigation: actor cleared
+  POST /api/v1/admin/actors/{uuid}:unsuspend
+  Actor re-authenticates via Auth Provider → new session issued
+```
+
+---
+
+## 8.6 Workload Analysis — Brownfield VM Classification
+
+A platform admin runs brownfield ingestion on a newly discovered VM. The
+Workload Analysis pipeline classifies it and populates the WorkloadProfile.
+
+```
+Discovery Scheduler finds: vm-legacy-0007
+  provider_entity_id: vm-legacy-0007
+  no matching DCM entity in Realized State
+
+Ingestion record created (status: INGESTED):
+  entity_uuid: vm-new-abc123
+  tenant_uuid: __transitional__
+  resource_type: Compute.VirtualMachine (inferred from provider)
+
+Workload Analysis triggered (WLA-001):
+  WorkloadProfile entity created: wla-xyz789
+  linked to: vm-new-abc123
+
+Step 2 — Information Providers queried:
+  Port scan:
+    open ports: [443, 8443, 3000]
+  Process list:
+    [nginx, node, pm2, postgres-client]
+  OS metadata:
+    os: RHEL 8.6 / os_eol: 2029-05-31
+    mounts: / (50GB), /data (500GB — separate partition)
+  MTA assessment:
+    containerization_score: 7
+    blockers: []
+    suggested_target: Platform.KubernetesDeployment
+    archetype: web_server
+
+Step 3 — Classification (WLA-001 policy):
+  workload_archetype: web_server (confidence: high)
+  resource_type_match:
+    primary: Compute.VirtualMachine (confidence: high)
+    alternative: Platform.Container (confidence: medium — MTA score 7)
+  lifecycle_recommendation:
+    dcm_lifecycle_model: standard
+    rehydration_eligible: true      ← /data on separate partition
+    notes: "Containerization candidate per MTA score"
+
+WorkloadProfile → OPERATIONAL:
+  GET /api/v1/resources/vm-new-abc123/workload-profile
+  {
+    "workload_archetype": "web_server",
+    "resource_type_match": { "primary": "Compute.VirtualMachine", "confidence": "high" },
+    "migration_readiness": { "containerization_score": 7, "suggested_target": "Platform.KubernetesDeployment" },
+    "lifecycle_recommendation": { "rehydration_eligible": true }
+  }
+
+Ingestion advances:
+  INGESTED → ENRICHING (WorkloadProfile confidence: high → no manual review needed)
+  Tenant auto-assignment: web_server in /data subnet → Tenant: "payments-platform"
+  ENRICHING → PROMOTED → OPERATIONAL
+```
+
+---
+
+## 8.7 Scoring Model — Placement Tie-Breaking
+
+Two providers both satisfy all required placement constraints. The Scoring Model
+determines which is selected.
+
+**Scenario:** Four providers qualify for a FedRAMP High VM request.
+
+```
+Request:
+  resource_type: Compute.VirtualMachine
+  data_classification: restricted
+  required_accreditations: [fedramp_high]
+  tenant: payments-platform
+
+Providers qualifying after placement require-filter:
+  Provider A: fedramp_high=✅  zone=US-EAST-1a  capacity=60%
+  Provider B: fedramp_high=✅  zone=US-EAST-1b  capacity=82%
+  Provider C: fedramp_high=✅  zone=US-EAST-1a  capacity=35%
+  Provider D: fedramp_high=✅  zone=US-EAST-1b  capacity=91%
+
+Scoring Model evaluation:
+
+Signal 1 — Provider Health Score (weight: 0.30)
+  All four: status=healthy, 99.9% uptime
+  Scores: A=0.98, B=0.97, C=0.99, D=0.96
+
+Signal 2 — Capacity Headroom (weight: 0.25)
+  A=0.60, B=0.82, C=0.35, D=0.91
+  (normalized — higher headroom = lower risk)
+
+Signal 3 — Request Risk Score (weight: 0.20)
+  Cost estimate: $180/mo — medium risk
+  All providers: identical input → same score
+
+Signal 4 — Policy Preference Score (weight: 0.15)
+  Placement policy prefers US-EAST-1a:
+  A=1.0 (preferred zone), B=0.7, C=1.0, D=0.7
+
+Signal 5 — Accreditation Richness (weight: 0.10)
+  Provider B: fedramp_high + iso_27001 + soc2_type2 + verified_P1D → multiplier 1.0 → score: 0.82
+  Provider D: fedramp_high + soc2_type2 + verified_P7D → multiplier 0.9 → score: 0.71
+  Provider A: fedramp_high + verified_P1D → score: 0.52
+  Provider C: fedramp_high + stale_verification → multiplier 0.4 → score: 0.25
+
+Aggregate scores (weighted):
+  Provider A: 0.30×0.98 + 0.25×0.60 + 0.20×0.75 + 0.15×1.0 + 0.10×0.52 = 0.792
+  Provider B: 0.30×0.97 + 0.25×0.82 + 0.20×0.75 + 0.15×0.7 + 0.10×0.82 = 0.826
+  Provider C: 0.30×0.99 + 0.25×0.35 + 0.20×0.75 + 0.15×1.0 + 0.10×0.25 = 0.712
+  Provider D: 0.30×0.96 + 0.25×0.91 + 0.20×0.75 + 0.15×0.7 + 0.10×0.71 = 0.822
+
+Selected: Provider B (score: 0.826)
+  Note: Provider C's stale accreditation verification cost it the placement
+        despite having preferred zone.
+
+Placement audit record:
+  evaluated: [A, B, C, D]
+  scores: {A: 0.792, B: 0.826, C: 0.712, D: 0.822}
+  selected: B
+  selection_margin: 0.004 over Provider D
+  key_differentiator: "Signal 5 — Provider B richer accreditation portfolio"
+```
+
+---
+
+## 8.8 Accreditation Monitor — FedRAMP Status Change Detection
+
+The Accreditation Monitor polls the FedRAMP marketplace and detects that a
+provider's authorization has been downgraded mid-cycle.
+
+```
+Accreditation Monitor — daily poll cycle:
+
+  Provider: eu-west-prod-1 (Service Provider)
+  Accreditation: fedramp_high
+  external_registry_id: "FR2024-0042"
+  DCM status: active
+  last_verified_at: 2026-03-30T03:00:00Z
+
+  Query:
+  GET https://marketplace.fedramp.gov/api/products?id=FR2024-0042
+
+  Response:
+  {
+    "id": "FR2024-0042",
+    "status": "In Process",          ← was "Authorized"
+    "impact_level": "Moderate",      ← was "High"
+    "last_updated": "2026-03-31"
+  }
+
+  Mismatch detected:
+    DCM record:   status=authorized, impact_level=high
+    External:     status=in_process, impact_level=moderate
+
+  ACM-002 applies (not immediate revocation — status is not "Revoked"):
+    Accreditation: status → pending_review
+    last_verified_at: 2026-03-31T03:00:00Z
+    last_result: status_changed
+
+  Events fired:
+    accreditation.status_changed (urgency: high)
+      from_status: authorized / fedramp_high
+      to_status: in_process / fedramp_moderate
+      external_source: fedramp_marketplace
+      action_taken: pending_review
+
+  Notifications:
+    → Platform Admin (urgency: high): "FedRAMP authorization changed for eu-west-prod-1"
+    → Compliance Team
+
+  Governance Matrix impact:
+    Any new request placing fedramp_high workloads on eu-west-prod-1:
+    → Check 3 fails (required accreditation pending_review)
+    → New requests: BLOCKED until Platform Admin resolves
+
+  Active resources (already on eu-west-prod-1):
+    No immediate action (ACM-002 — not immediate revocation)
+    Drift reconciliation queued for governance review
+    Provider flagged: ACCREDITATION_PENDING_REVIEW
+
+Platform Admin investigation:
+  Confirms: FedRAMP PMO initiated annual re-authorization (routine, not security event)
+  Decision: retain accreditation — provider continues under monitoring
+
+  POST /api/v1/admin/accreditations/{uuid}:verify
+    { "override_reason": "Confirmed routine re-authorization — PMO contact: john@fedramp.gov" }
+
+  Accreditation: status → active (manual override with audit record)
+  new_requests: unblocked
+```
+
+
+# Section 9 — Resource Type and Data Layer Lifecycle (End-to-End)
+
+This section traces the complete lifecycle of two real resource types — a Virtual Machine
+and a Web Application as a Service — from the initial layer definitions authored by their
+owning authorities, through provider catalog item registration, layer assembly at request
+time, and finally through rehydration for both.
+
+The goal is to make concrete the abstract model: layers are data, resource types are built
+from layers, providers extend them with their own layers, and every field in every request
+payload knows exactly which layer set it and why.
+
+---
+
+## 9.1 Layer Definitions — Who Defines What, and Who Owns It
+
+Before any VM can be provisioned or any WebApp offered, the foundational data layers must
+exist. These are created by different authorities, each responsible for their domain.
+
+### Reference Data Layers (created by authority teams, stored in GitOps)
+
+**OS Image layer — owned by Platform Security Team:**
+
+```yaml
+# GitOps path: platform/reference-data/os-images/rhel-9-4-approved.yaml
+layer:
+  artifact_metadata:
+    uuid: "os-img-rhel-9-4"
+    handle: "platform/reference-data/os-images/rhel-9-4"
+    version: "1.0.0"
+    status: active
+    owned_by:
+      display_name: "Platform Security Team"
+      group_handle: "groups/platform-security"
+    created_via: pr
+    created_at: "2026-01-15T09:00:00Z"
+
+  layer_type: reference_data
+  reference_data_type: os_image
+  domain: platform
+
+  data:
+    image_name: "RHEL 9.4 — Approved Standard"
+    image_uuid: "img-rhel-9-4-20260315"
+    image_sha256: "a1b2c3d4e5f6..."
+    os_family: rhel
+    major_version: 9
+    minor_version: 4
+    release_date: "2026-03-15"
+    eol_date: "2032-05-31"
+    fips_compliant: true
+    cis_benchmark_ref: "CIS RHEL 9 Benchmark v1.0"
+    approved_for_classifications: [public, internal, confidential, restricted]
+    requires_subscription: true
+
+  concern_tags: [os-image, rhel, approved, fips-compliant, platform-standard]
+```
+
+**Location layer — owned by Data Center Operations:**
+
+```yaml
+# GitOps path: platform/locations/dc/fra-dc1.yaml
+layer:
+  artifact_metadata:
+    uuid: "loc-fra-dc1"
+    handle: "locations/dc/fra-dc1"
+    version: "2.1.0"
+    status: active
+    owned_by:
+      display_name: "Data Center Operations — Frankfurt"
+      group_handle: "groups/dc-operations-fra"
+    created_via: pr
+
+  layer_type: reference_data
+  reference_data_type: location.data_center
+  domain: platform
+  location_type: data_center
+
+  location_hierarchy:
+    parent_handle: "locations/az/eu-west-1a"
+    ancestors:
+      - { handle: "locations/region/eu-west", type: region }
+      - { handle: "locations/country/de",     type: country }
+
+  data:
+    dc_name: "DC1 — Frankfurt Alpha"
+    dc_code: "FRA-DC1"
+    tier_classification: tier_3
+    pue_rating: 1.35
+    redundancy_model: "2N"
+    jurisdiction: "EU/GDPR"
+    sovereignty_zone: "eu-west-sovereign"
+    max_data_classification: restricted
+    certifications:
+      - { standard: "ISO 27001", expires_at: "2027-06-30" }
+      - { standard: "SOC 2 Type II", expires_at: "2026-12-31" }
+    network_uplinks:
+      - { carrier: "DE-CIX", bandwidth_gbps: 100, redundant: true }
+
+  concern_tags: [location, data-center, frankfurt, eu-west, tier-3, iso27001]
+```
+
+**Network zone layer — owned by Network Operations:**
+
+```yaml
+# GitOps path: platform/reference-data/network-zones/prod-dmz-fra.yaml
+layer:
+  artifact_metadata:
+    uuid: "nz-prod-dmz-fra"
+    handle: "platform/reference-data/network-zones/prod-dmz-fra"
+    version: "1.1.0"
+    status: active
+    owned_by:
+      display_name: "Network Operations"
+      group_handle: "groups/network-ops"
+    created_via: pr
+
+  layer_type: reference_data
+  reference_data_type: network_zone
+  domain: platform
+
+  data:
+    zone_name: "Production DMZ — Frankfurt"
+    zone_code: "PROD-DMZ-FRA"
+    vlan_range: "100-199"
+    allowed_inbound_protocols: [HTTPS, SSH]
+    allowed_outbound_protocols: [HTTPS, DNS, NTP]
+    firewall_policy_ref: "policies/network/prod-dmz-baseline"
+    nat_enabled: true
+    internet_facing: true
+    approved_for_classifications: [public, internal]
+
+  concern_tags: [network-zone, dmz, production, frankfurt, internet-facing]
+```
+
+**Core location context layers — assembled automatically from hierarchy:**
+
+```yaml
+# Zone layer (parent of FRA-DC1) — owned by Data Center Operations
+layer:
+  artifact_metadata:
+    uuid: "loc-az-eu-west-1a"
+    handle: "locations/az/eu-west-1a"
+    version: "1.0.0"
+    status: active
+    owned_by: { display_name: "Data Center Operations", group_handle: "groups/dc-operations" }
+
+  layer_type: reference_data
+  reference_data_type: location.zone
+  domain: platform
+
+  data:
+    zone_name: "EU West Zone A"
+    zone_code: "eu-west-1a"
+    isolation_boundary: full
+    target_rpo_minutes: 15
+    target_rto_minutes: 60
+    ha_peer_zones: ["locations/az/eu-west-1b"]
+
+# Country layer — owned by Platform Governance
+layer:
+  artifact_metadata:
+    uuid: "loc-country-de"
+    handle: "locations/country/de"
+    version: "1.0.0"
+    status: active
+    owned_by: { display_name: "Platform Governance", group_handle: "groups/platform-governance" }
+
+  layer_type: reference_data
+  reference_data_type: location.country
+  domain: platform
+
+  data:
+    country_name: "Germany"
+    iso_3166_1_alpha2: "DE"
+    data_sovereignty_jurisdiction: "EU/GDPR"
+    regulatory_frameworks: [GDPR, NIS2, eIDAS]
+```
+
+---
+
+### 9.2 Resource Type Specification — Defined by the Resource Type Authority
+
+The Platform Team is the Resource Type Authority for `Compute.VirtualMachine`.
+They define the vendor-neutral contract all providers must implement.
+
+```yaml
+# GitOps path: registry/resource-types/compute/virtual-machine/v2-1-0.yaml
+resource_type_specification:
+  artifact_metadata:
+    uuid: "rt-compute-vm"
+    handle: "registry/compute/VirtualMachine"
+    fully_qualified_name: "Compute.VirtualMachine"
+    version: "2.1.0"
+    status: active
+    owned_by:
+      display_name: "Platform Team — Virtualization"
+      group_handle: "groups/platform-team"
+    tier: 1    # DCM Core — maintained by DCM Project
+
+  category: Compute
+  description: "A virtual machine instance. The foundational compute resource."
+
+  # Universal fields — all providers MUST implement these
+  universal_fields:
+
+    cpu_count:
+      type: integer
+      required: true
+      description: "Number of virtual CPUs"
+      portability: { classification: universal }
+      constraints:
+        - type: range
+          min: 1
+          max: 256
+          # Note: constraint is a range — provider judgment for what they support.
+          # No layer_reference here: CPU count is intrinsic to the resource type,
+          # not a governed organizational list.
+
+    memory_gb:
+      type: integer
+      required: true
+      description: "RAM in gigabytes"
+      portability: { classification: universal }
+      constraints:
+        - type: range
+          min: 1
+          max: 4096
+
+    storage_gb:
+      type: integer
+      required: true
+      description: "Primary disk size in gigabytes"
+      portability: { classification: universal }
+      constraints:
+        - type: range
+          min: 10
+          max: 65536
+
+    os_image:
+      type: string
+      format: layer-uuid
+      required: true
+      description: "Approved OS image. Must be a UUID of an active os_image reference data layer."
+      portability: { classification: universal }
+      constraints:
+        - type: layer_reference
+          layer_type: os_image
+          # Allowed values = active os_image layers.
+          # Adding a new approved OS = adding a new os_image layer.
+          # No spec change needed.
+
+    location:
+      type: string
+      format: layer-uuid
+      required: true
+      description: "Allocation location. Must be a UUID of an active location.data_center layer."
+      portability: { classification: universal }
+      constraints:
+        - type: layer_reference
+          layer_type: location.data_center
+          # Allowed values = active DC layers. Each carries jurisdiction,
+          # certifications, sovereignty zone, and capacity status.
+
+    network_zone:
+      type: string
+      format: layer-uuid
+      required: false
+      description: "Network zone. If omitted, placement policy selects default."
+      portability: { classification: universal }
+      constraints:
+        - type: layer_reference
+          layer_type: network_zone
+
+    hostname:
+      type: string
+      required: false
+      description: "VM hostname. If omitted, DCM generates one per naming policy."
+      portability: { classification: universal }
+      constraints:
+        - type: pattern
+          pattern: '^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$'
+
+    environment:
+      type: string
+      format: layer-uuid
+      required: false
+      description: "Deployment environment. Controls policy set and approval tier."
+      portability: { classification: universal }
+      constraints:
+        - type: layer_reference
+          layer_type: environment
+
+  # Conditional fields — declared by providers that support them
+  conditional_fields:
+
+    high_availability:
+      type: boolean
+      required: false
+      description: "Enable HA — live migration on host failure"
+      portability:
+        classification: conditional
+        portability_notes: "Supported by most hypervisor providers; not applicable for bare metal"
+
+    gpu_profile:
+      type: string
+      format: layer-uuid
+      required: false
+      description: "GPU configuration. Must be a UUID of an active gpu_profile reference data layer."
+      portability:
+        classification: conditional
+        portability_notes: "Only providers with GPU hardware support this field"
+      constraints:
+        - type: layer_reference
+          layer_type: gpu_profile
+
+    backup_policy:
+      type: string
+      required: false
+      description: "Backup schedule reference"
+      portability:
+        classification: conditional
+
+  # Extension point declaration — where providers MAY add fields
+  extension_points:
+    - name: provider_hypervisor_config
+      description: "Provider-specific hypervisor configuration"
+      portability_impact: provider_specific  # using this makes catalog item non-portable
+
+  lifecycle_operations: [create, read, update, delete, suspend, resume, rehydrate, drift_check]
+```
+
+**The WebApp Resource Type — defined by the Application Platform Team:**
+
+```yaml
+# GitOps path: registry/resource-types/application/web-app/v1-0-0.yaml
+resource_type_specification:
+  artifact_metadata:
+    uuid: "rt-app-webapp"
+    handle: "registry/application/WebApp"
+    fully_qualified_name: "Application.WebApp"
+    version: "1.0.0"
+    status: active
+    owned_by:
+      display_name: "Application Platform Team"
+      group_handle: "groups/app-platform"
+    tier: 3    # Organization tier
+
+  category: Application
+  description: >
+    A complete web application stack: load balancer, application VMs, and
+    database — provisioned and lifecycle-managed as a single compound resource.
+    Implemented by a compound service definition that orchestrates constituent atomic resources.
+
+  universal_fields:
+
+    app_name:
+      type: string
+      required: true
+      description: "Application name — used in DNS, naming, and tagging"
+      portability: { classification: universal }
+      constraints:
+        - type: pattern
+          pattern: '^[a-z0-9-]{3,48}$'
+
+    environment:
+      type: string
+      format: layer-uuid
+      required: true
+      description: "Deployment environment (controls redundancy, approval tier, TTL)"
+      portability: { classification: universal }
+      constraints:
+        - type: layer_reference
+          layer_type: environment
+
+    location:
+      type: string
+      format: layer-uuid
+      required: true
+      description: "Target zone or data center"
+      portability: { classification: universal }
+      constraints:
+        - type: layer_reference
+          layer_type: location.data_center
+
+    tier_level:
+      type: string
+      required: true
+      description: "Service tier governing redundancy, SLA, and approval"
+      portability: { classification: universal }
+      constraints:
+        - type: enum
+          allowed_values: [tier_1, tier_2, tier_3]
+          # Static enum — tier names are intrinsic to the resource type.
+          # Each tier carries policy implications enforced by GateKeeper policies.
+
+    web_replica_count:
+      type: integer
+      required: false
+      description: "Number of web tier VMs. Policy enforces minimums per tier."
+      portability: { classification: universal }
+      constraints:
+        - type: range
+          min: 1
+          max: 20
+
+    db_engine:
+      type: string
+      required: true
+      description: "Database engine"
+      portability: { classification: universal }
+      constraints:
+        - type: enum
+          allowed_values: [postgresql, mysql, mariadb]
+
+    db_storage_gb:
+      type: integer
+      required: true
+      constraints:
+        - type: range
+          min: 50
+          max: 10000
+
+  lifecycle_operations: [create, read, update, delete, suspend, resume, rehydrate, scale_out, drift_check]
+```
+
+---
+
+## 9.3 Provider Catalog Items — Implementing the Resource Type
+
+Two compute providers register catalog items implementing `Compute.VirtualMachine`.
+Both must cover all universal fields. Each adds provider-specific constraints and
+optionally extends with their own layers.
+
+**Provider A — Nutanix EU-WEST (portable catalog item):**
+
+```yaml
+# GitOps path: providers/nutanix-eu-west/catalog/vm-standard.yaml
+catalog_item:
+  uuid: "ci-nutanix-eu-west-vm-std"
+  name: "Nutanix EU-WEST — Standard VM"
+  version: "1.3.0"
+  status: active
+
+  implements:
+    resource_type_uuid: "rt-compute-vm"
+    resource_type_version: "2.1.0"
+    resource_type_fully_qualified_name: "Compute.VirtualMachine"
+
+  portability_warning: false        # no provider-specific extensions
+  portability_class: portable
+
+  # Universal fields — Nutanix's implementation of the spec
+  universal_fields:
+    cpu_count:
+      constraint: { type: enum, allowed_values: [2, 4, 8, 16, 32] }
+      # Nutanix narrows the spec's 1-256 range to their supported sizes.
+      # Still portable: another provider may offer overlapping values.
+
+    memory_gb:
+      constraint: { type: enum, allowed_values: [4, 8, 16, 32, 64, 128, 256] }
+
+    storage_gb:
+      constraint: { type: range, min: 40, max: 4096 }
+
+    os_image:
+      # Inherits layer_reference from spec — no override needed.
+      # Nutanix resolves the consumer's os_image layer UUID against their
+      # registered OS image inventory at dispatch time.
+
+    location:
+      # Inherits layer_reference from spec.
+      # Only location layers in Nutanix's registered availability_zones appear
+      # in allowed_values when this catalog item is selected.
+      filter:
+        availability_zones: ["eu-west-1a", "eu-west-1b"]
+
+  # Conditional fields this provider supports
+  conditional_fields_supported:
+    - high_availability    # Nutanix AOS live migration supported
+
+  # Nutanix contributes a Service Layer (domain: service) with defaults
+  # that apply when this catalog item is selected
+  service_layer_handle: "providers/nutanix-eu-west/layers/vm-platform-defaults"
+
+  # Cost metadata
+  cost_metadata:
+    pricing_model: per_hour
+    base_cost_per_vcpu_hour: 0.025
+    base_cost_per_gb_ram_hour: 0.008
+    currency: USD
+
+  sovereignty:
+    data_residency: EU
+    jurisdiction_codes: [DE, NL]
+    availability_zones: ["eu-west-1a", "eu-west-1b"]
+```
+
+**Nutanix Service Layer — injected for all Nutanix VM requests:**
+
+```yaml
+# GitOps path: providers/nutanix-eu-west/layers/vm-platform-defaults.yaml
+layer:
+  artifact_metadata:
+    uuid: "sl-nutanix-eu-west-vm"
+    handle: "providers/nutanix-eu-west/layers/vm-platform-defaults"
+    version: "2.0.0"
+    status: active
+    owned_by:
+      display_name: "Nutanix EU-WEST Operations"
+      group_handle: "providers/nutanix-eu-west/ops-team"
+    created_via: pr
+
+  layer_type: service
+  domain: service
+  type_scope:
+    resource_type_fqn: "Compute.VirtualMachine"
+    scope_inheritance: exact
+
+  # These fields are injected into every Nutanix VM request payload
+  data:
+    hypervisor: "AHV"                           # Nutanix Acropolis Hypervisor
+    cluster_uuid: "nutanix-cluster-fra-01"
+    storage_container: "default-container"
+    network_function_chain: "nfc-prod-default"
+    backup_enabled: true                        # Nutanix default backup policy
+    backup_schedule: "daily-7d-retention"
+    cvm_cores: 2                                # Controller VM allocation
+    monitoring_agent: "nutanix-era-agent"
+    support_tier: "standard"
+```
+
+**Provider B — VMware EU-WEST (provider-extended, non-portable):**
+
+```yaml
+# GitOps path: providers/vmware-eu-west/catalog/vm-enterprise.yaml
+catalog_item:
+  uuid: "ci-vmware-eu-west-vm-ent"
+  name: "VMware EU-WEST — Enterprise VM"
+  version: "1.0.0"
+  status: active
+
+  implements:
+    resource_type_uuid: "rt-compute-vm"
+    resource_type_version: "2.1.0"
+    resource_type_fully_qualified_name: "Compute.VirtualMachine"
+
+  portability_warning: true         # provider-specific extensions present
+  portability_class: provider-specific
+
+  universal_fields:
+    cpu_count:
+      constraint: { type: range, min: 1, max: 128 }
+    memory_gb:
+      constraint: { type: range, min: 2, max: 2048 }
+    storage_gb:
+      constraint: { type: range, min: 20, max: 8192 }
+
+  conditional_fields_supported:
+    - high_availability
+
+  # VMware extends the resource type with vSphere-specific fields
+  # via a provider extension layer (domain: provider)
+  provider_extension_layer_handles:
+    - "providers/vmware-eu-west/layers/vsphere-extensions-v1"
+
+  # These extension fields make the catalog item non-portable —
+  # if a consumer uses them, their request is VMware-specific
+```
+
+**VMware Provider Extension Layer — makes catalog item non-portable:**
+
+```yaml
+# GitOps path: providers/vmware-eu-west/layers/vsphere-extensions-v1.yaml
+layer:
+  artifact_metadata:
+    uuid: "pl-vmware-vsphere-ext"
+    handle: "providers/vmware-eu-west/layers/vsphere-extensions-v1"
+    version: "1.0.0"
+    status: active
+    owned_by:
+      display_name: "VMware EU-WEST Operations"
+      group_handle: "providers/vmware-eu-west/ops-team"
+
+  layer_type: provider_extension
+  domain: provider              # lowest authority — cannot override platform/tenant layers
+  type_scope:
+    resource_type_fqn: "Compute.VirtualMachine"
+
+  # VMware-specific fields exposed to consumers of this catalog item.
+  # These are NOT part of the Resource Type Specification.
+  # Using them makes the request non-portable (VMware-only).
+  extension_fields:
+    vsphere_resource_pool:
+      type: string
+      portability_breaking: true
+      description: "vSphere resource pool name"
+      constraint: { type: enum, allowed_values: ["prod-pool-a", "prod-pool-b", "dev-pool"] }
+
+    vsphere_datastore_cluster:
+      type: string
+      portability_breaking: true
+      description: "vSphere datastore cluster for VMDK placement"
+
+    vmware_tools_version:
+      type: string
+      portability_breaking: true
+      description: "Minimum VMware Tools version"
+      constraint: { type: pattern, pattern: '^\d+\.\d+\.\d+$' }
+
+  # These fields also inject defaults for the provider's own use
+  data:
+    hypervisor: "ESXi 8.0"
+    cluster_name: "vmware-cluster-fra-01"
+    distributed_switch: "dvs-prod-01"
+    admission_control: true
+```
+
+---
+
+## 9.4 Consumer Request — VM Provisioning
+
+The consumer browses the catalog, sees the two VM offerings, selects the Nutanix one,
+picks their location and OS image from the resolved `allowed_values` lists, and submits.
+
+**What the consumer sees (GET /api/v1/catalog/ci-nutanix-eu-west-vm-std):**
+
+```json
+{
+  "catalog_item_uuid": "ci-nutanix-eu-west-vm-std",
+  "display_name": "Nutanix EU-WEST — Standard VM",
+  "resource_type": "Compute.VirtualMachine",
+  "portability_class": "portable",
+  "portability_warning": false,
+
+  "schema": {
+    "fields": [
+      {
+        "field_name": "cpu_count",
+        "type": "integer",
+        "required": true,
+        "constraint": { "type": "enum", "allowed_values": [2, 4, 8, 16, 32] }
+      },
+      {
+        "field_name": "os_image",
+        "type": "string",
+        "required": true,
+        "constraint": {
+          "type": "layer_reference",
+          "layer_type": "os_image",
+          "allowed_values": [
+            {
+              "value": "os-img-rhel-9-4",
+              "display_name": "RHEL 9.4 — Approved Standard",
+              "os_family": "rhel",
+              "fips_compliant": true,
+              "eol_date": "2032-05-31"
+            },
+            {
+              "value": "os-img-ubuntu-24-04",
+              "display_name": "Ubuntu 24.04 LTS",
+              "os_family": "ubuntu",
+              "fips_compliant": false,
+              "eol_date": "2029-04-30"
+            }
+          ]
+        }
+      },
+      {
+        "field_name": "location",
+        "type": "string",
+        "required": true,
+        "constraint": {
+          "type": "layer_reference",
+          "layer_type": "location.data_center",
+          "allowed_values": [
+            {
+              "value": "loc-fra-dc1",
+              "display_name": "DC1 — Frankfurt Alpha",
+              "code": "FRA-DC1",
+              "zone": "eu-west-1a",
+              "sovereignty": "EU/GDPR",
+              "certifications": ["ISO 27001", "SOC 2 Type II"],
+              "capacity_status": "available"
+            },
+            {
+              "value": "loc-ams-dc2",
+              "display_name": "DC2 — Amsterdam Beta",
+              "code": "AMS-DC2",
+              "zone": "eu-west-1b",
+              "sovereignty": "EU/GDPR",
+              "certifications": ["ISO 27001"],
+              "capacity_status": "limited"
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
+```
+
+**Consumer submits request:**
+
+```json
+POST /api/v1/requests
+{
+  "catalog_item_uuid": "ci-nutanix-eu-west-vm-std",
+  "fields": {
+    "cpu_count": 8,
+    "memory_gb": 32,
+    "storage_gb": 120,
+    "os_image": "os-img-rhel-9-4",
+    "location": "loc-fra-dc1",
+    "hostname": "payments-api-01"
+  }
+}
+```
+
+---
+
+## 9.5 Request Processing Pipeline — VM
+
+Tracing every step from submission to realization, showing which layer contributes
+which field and why.
+
+```
+Step 1 — INTENT STATE CAPTURED
+──────────────────────────────
+Stored verbatim — the consumer's exact submission:
+  {
+    "catalog_item_uuid": "ci-nutanix-eu-west-vm-std",
+    "fields": {
+      "cpu_count": 8,
+      "memory_gb": 32,
+      "storage_gb": 120,
+      "os_image": "os-img-rhel-9-4",       // layer UUID
+      "location": "loc-fra-dc1",            // layer UUID
+      "hostname": "payments-api-01"
+    }
+  }
+Intent UUID: intent-vm-001
+Stored in: Intent Store (GitOps — immutable)
+Nothing modified. No policies run yet. This is the permanent record of consumer intent.
+
+Step 2 — LAYER REFERENCE RESOLUTION
+─────────────────────────────────────
+DCM resolves each layer UUID to its full artifact:
+  os_image → layer "os-img-rhel-9-4":
+    image_uuid: "img-rhel-9-4-20260315"
+    image_sha256: "a1b2c3..."
+    fips_compliant: true
+    eol_date: "2032-05-31"
+
+  location → layer "loc-fra-dc1" + ancestor chain:
+    Country layer (loc-country-de):
+      jurisdiction: EU/GDPR
+      regulatory_frameworks: [GDPR, NIS2]
+    Zone layer (loc-az-eu-west-1a):
+      zone_code: eu-west-1a
+      target_rpo_minutes: 15
+      isolation_boundary: full
+    Data Center layer (loc-fra-dc1):
+      dc_code: FRA-DC1
+      sovereignty_zone: eu-west-sovereign
+      max_data_classification: restricted
+      certifications: [ISO 27001, SOC 2 Type II]
+
+Step 3 — LAYER ASSEMBLY (precedence order, lowest first)
+──────────────────────────────────────────────────────────
+  1. Base Layer (platform/base/compute-vm-baseline):
+     → monitoring_agent: null (to be injected by policy)
+     → backup_enabled: false (default)
+     → Source: Base Layer
+
+  2. Core Layers assembled:
+     ┌─ Country layer (loc-country-de):
+     │   location.jurisdiction = EU/GDPR
+     │   location.regulatory_frameworks = [GDPR, NIS2]
+     ├─ Zone layer (loc-az-eu-west-1a):
+     │   location.zone_code = eu-west-1a
+     │   location.rpo_minutes = 15
+     └─ DC layer (loc-fra-dc1):
+         location.dc_code = FRA-DC1
+         location.sovereignty_zone = eu-west-sovereign
+         location.max_data_classification = restricted
+
+  3. Service Layer (providers/nutanix-eu-west/layers/vm-platform-defaults):
+     → hypervisor = AHV
+     → cluster_uuid = nutanix-cluster-fra-01
+     → storage_container = default-container
+     → backup_enabled = true        (overrides Base Layer default)
+     → backup_schedule = daily-7d-retention
+     → monitoring_agent = nutanix-era-agent
+
+  4. OS Image layer data injected (resolved from os-img-rhel-9-4):
+     → os.image_uuid = img-rhel-9-4-20260315
+     → os.image_sha256 = a1b2c3...
+     → os.fips_compliant = true
+     → os.eol_date = 2032-05-31
+
+  5. Request Layer (consumer's fields — highest data layer precedence):
+     → cpu_count = 8
+     → memory_gb = 32
+     → storage_gb = 120
+     → hostname = payments-api-01
+
+Assembled payload at this point (before policies):
+  cpu_count: 8            [source: Request Layer]
+  memory_gb: 32           [source: Request Layer]
+  storage_gb: 120         [source: Request Layer]
+  hostname: payments-api-01 [source: Request Layer]
+  os.image_uuid: img-rhel-9-4-20260315  [source: os_image reference layer]
+  hypervisor: AHV         [source: Service Layer / Nutanix]
+  location.dc_code: FRA-DC1             [source: Core Location Layer]
+  location.zone_code: eu-west-1a        [source: Core Location Layer]
+  location.jurisdiction: EU/GDPR        [source: Core Location Layer]
+  location.sovereignty_zone: eu-west-sovereign  [source: Core Location Layer]
+  backup_enabled: true    [source: Service Layer / Nutanix]
+  monitoring_agent: nutanix-era-agent   [source: Service Layer / Nutanix]
+
+Step 4 — POLICY EVALUATION
+────────────────────────────
+  GateKeeper — Sovereignty Check:
+    PASS: location.sovereignty_zone = eu-west-sovereign
+          tenant data classification ≤ restricted
+          No cross-border transfer
+
+  Validation — FIPS Requirement (FSI profile):
+    PASS: os.fips_compliant = true
+
+  Transformation — Monitoring Agent Injection:
+    ADD: monitoring_agent = nutanix-era-agent  (already present — no override)
+    ADD: monitoring_config.endpoint = monitoring.internal:9090
+    ADD: monitoring_config.scrape_interval = 30s
+    Provenance: { source: policy/transform/monitoring-inject, immutable: true }
+
+  Transformation — Naming Convention:
+    MODIFY: hostname = payments-api-01 → validated against pattern '^[a-z0-9-]{3,63}$' ✓
+    ADD: fqdn = payments-api-01.fra-dc1.eu-west.corp.example.com
+    Provenance: { source: policy/transform/naming-convention }
+
+  GateKeeper — Cost Gate (if estimate > threshold):
+    Estimated cost: $0.38/hour → $274/month
+    Tenant monthly budget: $5,000 remaining
+    PASS: within budget
+
+Step 5 — PLACEMENT SELECTION
+──────────────────────────────
+  Placement Engine evaluates against location.sovereignty_zone = eu-west-sovereign:
+    Candidate providers:
+      Nutanix EU-WEST (eu-west-1a) → confirmed capacity
+      VMware EU-WEST (eu-west-1a) → confirmed capacity
+    
+    Filtered to catalog item ci-nutanix-eu-west-vm-std → Nutanix EU-WEST selected
+    Reserve query confirmed: Nutanix holds capacity for this request (hold: PT5M)
+
+Step 6 — REQUESTED STATE WRITTEN
+──────────────────────────────────
+Full assembled payload stored in Requested Store.
+Every field carries provenance:
+  cpu_count: 8
+    _provenance: { source: request.layer, intent_uuid: intent-vm-001 }
+  hypervisor: AHV
+    _provenance: { source: service.layer/nutanix-vm-defaults, version: 2.0.0 }
+  location.dc_code: FRA-DC1
+    _provenance: { source: core.layer/loc-fra-dc1, version: 2.1.0 }
+  monitoring_agent: nutanix-era-agent
+    _provenance: { source: policy/transform/monitoring-inject, immutable: true }
+  os.image_uuid: img-rhel-9-4-20260315
+    _provenance: { source: reference.layer/os-img-rhel-9-4, version: 1.0.0 }
+
+Step 7 — DISPATCH TO PROVIDER
+───────────────────────────────
+CreateRequest dispatched to Nutanix EU-WEST:
+  {
+    "dcm_entity_uuid": "vm-abc123",
+    "request_uuid": "req-xyz789",
+    "resource_type_uuid": "rt-compute-vm",
+    "resource_type_name": "Compute.VirtualMachine",
+    "fields": {
+      "cpu_count": 8,
+      "memory_gb": 32,
+      "storage_gb": 120,
+      "hostname": "payments-api-01",
+      "fqdn": "payments-api-01.fra-dc1.eu-west.corp.example.com",
+      "os_image_uuid": "img-rhel-9-4-20260315",
+      "hypervisor": "AHV",
+      "cluster_uuid": "nutanix-cluster-fra-01",
+      "backup_enabled": true,
+      "monitoring_agent": "nutanix-era-agent"
+      // Full payload — Nutanix naturalizes to their API format
+    }
+  }
+
+Step 8 — REALIZED STATE WRITTEN
+─────────────────────────────────
+Nutanix provisions the VM, denaturalizes the result:
+  {
+    "dcm_entity_uuid": "vm-abc123",
+    "resource_id": "nutanix-vm-8f7e6d5c",   // Nutanix's internal ID
+    "lifecycle_state": "OPERATIONAL",
+    "realized_fields": {
+      "primary_ip": "10.100.1.42",
+      "mac_address": "00:50:56:8f:7e:6d",
+      "host_uuid": "nutanix-host-001",
+      "realized_at": "2026-03-31T10:05:33Z",
+      "nutanix_vm_uuid": "8f7e6d5c-..."    // provider-native ID stored for correlation
+    }
+  }
+Entity vm-abc123 → status: OPERATIONAL
+```
+
+---
+
+## 9.6 Consumer Request — WebApp as a Service
+
+The WebApp catalog item is backed by a compound service definition that orchestrates VM, LoadBalancer,
+and Database constituent resources — all provisioned as one consumer action.
+
+**Consumer submits:**
+
+```json
+POST /api/v1/requests
+{
+  "catalog_item_uuid": "ci-webapp-payments-stack",
+  "fields": {
+    "app_name": "payments-portal",
+    "environment": "env-layer-production",   // layer UUID — production environment
+    "location": "loc-fra-dc1",               // layer UUID — FRA-DC1
+    "tier_level": "tier_1",
+    "web_replica_count": 3,
+    "db_engine": "postgresql",
+    "db_storage_gb": 500
+  }
+}
+```
+
+**What the compound service definition orchestrates (transparent to consumer):**
+
+```
+compound service definition decomposes the request into constituent requests:
+
+  Constituent 1: Compute.VirtualMachine × 3 (web tier)
+    os_image: os-img-rhel-9-4          // from platform OS image reference layer
+    location: loc-fra-dc1              // same DC as parent request
+    cpu_count: 4                       // from environment layer defaults
+    memory_gb: 16                      // from environment layer defaults
+    hostname: payments-portal-web-{1,2,3}
+    network_zone: nz-prod-dmz-fra      // injected by Placement Policy (tier_1 + web)
+
+  Constituent 2: Network.LoadBalancer × 1
+    location: loc-fra-dc1
+    protocol: HTTPS
+    port: 443
+    health_check_path: /health
+    backend_pool: []   // filled by dependency injection after VM realization
+
+  Constituent 3: Storage.DatabaseInstance × 1
+    location: loc-fra-dc1
+    db_engine: postgresql
+    storage_gb: 500
+    high_availability: true            // enforced by tier_1 GateKeeper policy
+    backup_enabled: true               // injected by environment layer
+
+Environment layer injection (env-layer-production):
+  default_cpu_per_vm: 4
+  default_ram_per_vm: 16
+  backup_enabled: true
+  ttl: null                           // production: no TTL
+  approval_tier: team_lead            // changes require team lead approval
+  monitoring: mandatory
+  log_retention_days: 90
+
+Tier 1 GateKeeper policies fire:
+  → Minimum 3 web VMs enforced (3 requested ✓)
+  → HA required on database (high_availability: true injected)
+  → LTM required in front of web tier (LoadBalancer constituent ✓)
+  → Cross-zone redundancy check: 3 VMs placed across ≥2 zones ✓
+
+Dispatch sequence:
+  T+0s:   Database dispatched (no dependencies)
+  T+45s:  Database REALIZED → db_host=10.100.2.10
+  T+45s:  Web VMs dispatched (db_host injected from db realization)
+  T+90s:  Web VMs REALIZED → ips=[10.100.1.42, 10.100.1.43, 10.100.1.44]
+  T+90s:  LoadBalancer dispatched (backend_pool injected from VM realization)
+  T+105s: LoadBalancer REALIZED → vip=203.0.113.42
+
+Realized entity: Application.WebApp
+  entity_uuid: webapp-payments-portal
+  status: OPERATIONAL
+  constituents:
+    web_vms: [vm-web-001, vm-web-002, vm-web-003]
+    load_balancer: lb-001
+    database: db-001
+  endpoint: payments-portal.fra-dc1.eu-west.corp.example.com → 203.0.113.42
+```
+
+---
+
+## 9.7 Rehydration — VM (Intent Mode, DR Failover)
+
+The payments-api-01 VM is in DC1 which is unavailable. The consumer triggers
+rehydration — replaying the original intent through current policies and layers,
+placing the new VM in DC2.
+
+This is NOT Static Replace (which re-executes the Requested State verbatim).
+Rehydration re-runs the full assembly pipeline from Intent State — applying today's
+layers and policies, including the new location constraint.
+
+```
+Consumer: POST /api/v1/resources/vm-abc123:rehydrate
+  {
+    "mode": "intent",
+    "reason": "DC1 unavailable — DR failover to DC2",
+    "placement_constraints": {
+      "location": "loc-ams-dc2"    // consumer explicitly targets DC2 layer UUID
+    }
+  }
+
+Pipeline:
+
+Step 1 — RETRIEVE INTENT STATE (intent-vm-001)
+  Original consumer submission:
+    cpu_count: 8, memory_gb: 32, storage_gb: 120
+    os_image: os-img-rhel-9-4       // same layer UUID — still valid
+    location: loc-fra-dc1           // OVERRIDDEN by placement_constraints
+    hostname: payments-api-01
+
+Step 2 — LAYER REFERENCE RESOLUTION (fresh run)
+  os_image (os-img-rhel-9-4): same layer, still active
+    → resolves to current approved RHEL 9.4 image
+    → Note: if Platform Security had retired RHEL 9.4 and issued RHEL 9.5,
+      the new os_image layer UUID would need to be in the intent, OR a
+      Transformation policy could auto-upgrade to the latest approved image.
+
+  location OVERRIDE → loc-ams-dc2:
+    Country layer (loc-country-nl):
+      jurisdiction: EU/GDPR (same sovereignty zone — valid for this tenant)
+    Zone layer (loc-az-eu-west-1b):
+      zone_code: eu-west-1b
+    DC layer (loc-ams-dc2):
+      dc_code: AMS-DC2
+      sovereignty_zone: eu-west-sovereign    // same zone — rehydration permitted
+      certifications: [ISO 27001]            // SOC 2 not present here
+
+Step 3 — LAYER ASSEMBLY (fresh — current layers used, not original)
+  Service Layer: providers/nutanix-eu-west/layers/vm-platform-defaults
+    → cluster_uuid: nutanix-cluster-ams-01   // different cluster in DC2
+    → storage_container: dc2-default-container
+    (This is the key difference from Static Replace — current service layer
+     reflects DC2 infrastructure, not DC1)
+
+Step 4 — POLICY EVALUATION (fresh — current policies applied)
+  GateKeeper — Sovereignty Check:
+    PASS: loc-ams-dc2 in eu-west-sovereign zone
+          Same regulatory scope — GDPR/NIS2 still applies
+
+  Validation — Certification Check:
+    WARNING: loc-ams-dc2 has ISO 27001 but not SOC 2 Type II
+    Active policy: warn-only for standard profile
+    Provenance: { audit_warning: "SOC 2 Type II not available at AMS-DC2" }
+
+  Transformation — Hostname preservation:
+    hostname: payments-api-01 (preserved from intent — same logical identity)
+    fqdn: payments-api-01.ams-dc2.eu-west.corp.example.com  (DC2 FQDN)
+
+Step 5 — PLACEMENT
+  Nutanix EU-WEST operates in both zones — selected (same provider)
+  Reserve query: Nutanix AMS cluster confirms capacity
+
+Step 6 — NEW REQUESTED STATE WRITTEN
+  Linked to: intent-vm-001 (same original intent)
+  New requested state UUID: req-rehydrate-vm-abc123-002
+  Location fields now reflect AMS-DC2 chain
+  Full provenance chain preserved:
+    req-001 → [FRA-DC1 realization]
+    req-002 → [AMS-DC2 rehydration] ← current
+
+Step 7 — DISPATCH + REALIZATION
+  Original DC1 VM: status → DECOMMISSIONED (DC1 cleanup queued for when DC1 recovers)
+  New AMS-DC2 VM: OPERATIONAL
+  entity_uuid: vm-abc123 (PRESERVED — same entity, new location)
+  primary_ip: 10.200.1.55    // AMS-DC2 IP
+  DNS updated: payments-api-01.ams-dc2.eu-west.corp.example.com
+
+Provenance chain for payments-api-01:
+  Intent captured: 2026-03-01 (intent-vm-001)
+  Realized in DC1: 2026-03-01 (req-001) → realized-001
+  Rehydrated to DC2: 2026-03-31 (req-002) → realized-002 [linked to intent-vm-001]
+```
+
+---
+
+## 9.8 Rehydration — WebApp as a Service (Intent Mode, Standards Refresh)
+
+The payments-portal WebApp was provisioned 6 months ago. The Platform Security Team
+has published a new approved OS image (RHEL 9.5) and retired RHEL 9.4. The organization
+runs a quarterly rehydration cycle to bring all Tier 1 apps up to current standards.
+
+This is different from the VM failover rehydration — no DC change, no incident.
+The goal is standards refresh: replay intent through current layers to pick up the
+new OS image and any updated policy/layer defaults.
+
+```
+Platform Admin: POST /api/v1/resources/webapp-payments-portal:rehydrate
+  {
+    "mode": "intent",
+    "reason": "Q1 2026 standards refresh — RHEL 9.5 rollout, updated env layer defaults",
+    "reuse_intent_version": null     // use original intent as-is
+  }
+
+Intent State retrieved (for each constituent):
+
+  Web VMs (× 3):
+    app_name: payments-portal
+    os_image: os-img-rhel-9-4    // RETIRED — no longer active layer
+    location: loc-fra-dc1        // still valid
+    tier_level: tier_1
+
+  Database:
+    db_engine: postgresql
+    location: loc-fra-dc1
+    storage_gb: 500
+
+Layer Resolution — key changes since original provisioning:
+
+  os_image (os-img-rhel-9-4): STATUS = retired
+    → Transformation policy: "os_image_auto_upgrade" fires
+    → Finds current latest active os_image layer for os_family=rhel:
+      os-img-rhel-9-5 (RHEL 9.5, released 2026-09-01)
+    → REPLACES os_image in assembled payload
+    → Provenance: { auto_upgraded_from: "os-img-rhel-9-4", by: policy/transform/os-image-auto-upgrade }
+
+  environment layer (env-layer-production): VERSION bumped from 1.0 to 1.2
+    → New defaults: log_retention_days: 365 (was 90 — compliance requirement added)
+    → New: vulnerability_scan_enabled: true
+    → backup_schedule: weekly-30d-retention (was daily-7d)
+    → These new defaults inject into the assembled payload
+
+  location (loc-fra-dc1): VERSION 2.1.0 → 2.2.0
+    → New certification added: DORA (EU Digital Operational Resilience Act)
+    → Injected into payload and audit record
+
+Policy changes since original provisioning:
+
+  New GateKeeper: "vulnerability_scan_on_rehydrate" (added 2026-06-01)
+    → Requires: vulnerability_scan_schedule declared before realization
+    → Transformation: adds vulnerability_scan_enabled: true, schedule: weekly
+
+  Tier 1 minimum web replicas: increased from 3 to 4 (policy updated 2026-08-01)
+    → GateKeeper fires: current request has web_replica_count: 3
+    → Policy action: AUTO_ADJUST (adds one more VM to constituent requests)
+    → Consumer notified: "web_replica_count adjusted from 3 to 4 per updated Tier 1 policy"
+    → New VM constituent added to rehydration dispatch
+
+Rehydration Execution:
+
+  Rolling replacement strategy (Tier 1 — zero downtime):
+    Phase 1: Provision new VMs with RHEL 9.5 (alongside existing RHEL 9.4 VMs)
+      → 4 new VMs provisioned in FRA-DC1 (RHEL 9.5, updated env defaults)
+      → Load balancer backend pool updated: drain RHEL 9.4 VMs one at a time
+    Phase 2: Verify new VMs healthy (health check passes)
+    Phase 3: Remove old RHEL 9.4 VMs from pool → decommission
+    Phase 4: Database: snapshot + engine version check (no RHEL dependency — unchanged)
+
+Result:
+  payments-portal: OPERATIONAL
+  RHEL version: 9.4 → 9.5 (on all 4 web VMs)
+  Replica count: 3 → 4 (Tier 1 policy enforcement)
+  Log retention: 90 days → 365 days
+  Vulnerability scanning: enabled (new policy)
+  DC: FRA-DC1 (unchanged)
+
+Provenance chain for payments-portal:
+  Original intent: 2026-03-01 (env-layer-production v1.0, rhel-9-4, 3 replicas)
+  Q1 refresh: 2026-09-30 (env-layer-production v1.2, rhel-9-5, 4 replicas)
+  Both intent records preserved — can audit exactly what changed between cycles.
+
+Audit record highlights:
+  os_image: auto-upgraded from os-img-rhel-9-4 (retired) → os-img-rhel-9-5
+    by: policy/transform/os-image-auto-upgrade
+  web_replica_count: 3 → 4
+    by: policy/gatekeeper/tier1-minimum-replicas v2.0
+  log_retention_days: 90 → 365
+    by: environment layer env-layer-production v1.2
+  vulnerability_scan_enabled: false → true
+    by: policy/gatekeeper/vulnerability-scan-on-rehydrate v1.0
+```
+
+---
diff --git a/content/docs/architecture/specifications/dcm-flow-gui-spec.md b/content/docs/architecture/specifications/dcm-flow-gui-spec.md
new file mode 100644
index 0000000..c4d16b6
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-flow-gui-spec.md
@@ -0,0 +1,1007 @@
+# DCM Flow GUI Specification
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** User Interface Specification
+
+
+> **📋 Draft**
+>
+> This specification defines the DCM Flow GUI — the visual interface for platform engineers to compose, test, simulate, and manage DCM's policy-driven orchestration. All views, data contracts, API endpoints, and component structure are specified. Feedback and contributions welcome via [GitHub Issues](https://github.com/dcm-project/issues).
+
+**Version:** 0.1.0-draft
+**Status:** Draft — Ready for implementation feedback
+**Document Type:** Technical Specification
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [Control Plane Components](../data-model/25-control-plane-components.md) | [OPA Integration Specification](dcm-opa-integration-spec.md) | [Policy Profiles](../data-model/14-policy-profiles.md) | [Policy Contract](../data-model/B-policy-contract.md) | [Consumer API](consumer-api-spec.md) | [Admin API](dcm-admin-api-spec.md)
+
+---
+
+## Abstract
+
+> **AEP Alignment:** This specification follows [AEP](https://aep.dev) conventions.
+> Custom methods use colon syntax (e.g., `POST /flow/api/v1/shadow/{uuid}:promote`).
+> List endpoints use `page_size` and `page_token` parameters.
+> The Flow GUI API is a dedicated backend for the visual tooling — it is separate from the
+> main Consumer and Admin APIs. See `schemas/openapi/dcm-consumer-api.yaml` for the
+> normative consumer-facing API specification.
+
+
+The DCM Flow GUI is the visual interface for platform engineers to compose, test, and manage DCM's data-driven orchestration. Because policies ARE the orchestration in DCM, the Flow GUI is fundamentally a **visual policy composer** — it makes the active policy graph visible and editable without requiring direct YAML or Rego authoring.
+
+The Flow GUI is a **platform engineer tool**, not a consumer tool. It operates with platform admin or policy author role permissions. Consumers interact with DCM through the Consumer API and Web UI, not through the Flow GUI.
+
+---
+
+## 1. Architecture and Component Structure
+
+### 1.1 Component Diagram
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│                        Browser (SPA)                            │
+│   Flow GUI Application — React single-page application          │
+│   Authentication: Bearer token (same session as Consumer API)   │
+└──────────────────────────────┬──────────────────────────────────┘
+                               │ HTTPS REST
+                               ▼
+┌─────────────────────────────────────────────────────────────────┐
+│                   Flow GUI Service                               │
+│   Purpose: aggregate data for Flow GUI views                     │
+│   Deployed alongside DCM control plane                           │
+│   Authentication: validates Bearer token; requires policy_author │
+│                   or platform_admin role                         │
+│                                                                  │
+│   Reads from:                                                    │
+│     Policy Engine  — live graph, firing frequency                │
+│     GitOps stores  — policy artifacts, PR status                 │
+│     Observability  — event volumes, error rates                  │
+│     OPA sidecar    — test harness, shadow results                │
+│   Writes via:                                                    │
+│     Git API        — create PRs for policy changes               │
+│     Admin API      — shadow mode promotion, profile changes      │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 1.2 Authentication and Authorization
+
+The Flow GUI uses the same session token as the Consumer API. Required roles:
+
+| Role | Access |
+|------|--------|
+| `platform_admin` | Full read/write — all views, all authoring, profile management |
+| `policy_author` | Read all views; author policies in assigned domains; cannot manage profiles or promote shadow policies |
+| `platform_observer` | Read-only — all views; no authoring; no simulation write |
+
+### 1.3 Base URL
+
+```
+https://{dcm-instance}/flow/api/v1/
+```
+
+Distinct from the Consumer API base URL to make routing and access control clear.
+
+---
+
+## 2. The Execution Graph View
+
+### 2.1 What It Shows
+
+The primary view shows the live execution graph: which policies are active, which payload types they match, how they compose with each other, and their firing frequency. This is the "live map" of DCM's orchestration state.
+
+```
+[request.initiated] ──→ [IntentCapturePolicy] ──→ [request.intent_captured]
+                                                          │
+                                          ┌───────────────┼───────────────┐
+                                          ▼               ▼               ▼
+                                  [LayerAssembly]   [CostCheck]   [AuthzCheck]
+                                  (system/blue)     (tenant/yellow)(system/blue)
+                                          │
+                                          ▼
+                               [request.layers_assembled]
+                                          │
+                              ┌───────────┼───────────┐
+                              ▼           ▼            ▼
+                      [GateKeeper:      [Transform:   [GovMatrix:
+                       vm-size-limits]  inject-mon.]  phi-boundary]
+                              └───────────┼───────────┘
+                                          ▼
+                               [request.policies_evaluated]
+```
+
+**Visual conventions:**
+- **Node color by domain:** system=blue, platform=green, tenant=yellow, resource_type=purple
+- **Node shape by policy type:** GateKeeper=shield, Transformation=gear, Recovery=arrow, Governance Matrix=lock, Orchestration Flow=rectangle
+- **Edge thickness:** proportional to firing frequency (last 1h)
+- **Edge color:** green=allow path, red=deny path, amber=conditional
+- **Node badge:** shadow mode indicator (S), deprecated indicator (D)
+
+### 2.2 API — Fetch Execution Graph
+
+```
+GET /flow/api/v1/graph
+
+Query parameters:
+  payload_type=      filter to policies matching this payload type
+  resource_type=      filter to policies applicable to this resource type
+  domain=          filter by policy domain
+  policy_type=       filter by policy type
+  tenant_uuid=       include tenant-domain policies for this Tenant
+
+Response 200:
+{
+  "graph": {
+    "nodes": [
+      {
+        "node_id": "",                 # policy_uuid
+        "label": "vm-size-limits",
+        "policy_type": "gatekeeper",
+        "domain": "tenant",
+        "tenant_uuid": "",
+        "handle": "tenant/payments/gatekeeper/vm-size-limits",
+        "version": "1.2.0",
+        "status": "active",
+        "shadow_mode": false,
+        "match_payload_types": ["request.layers_assembled"],
+        "match_conditions_summary": "cpu_count > 32 OR memory_gb > 256",
+        "firing_frequency": {
+          "last_1h": 3,
+          "last_24h": 47,
+          "last_7d": 312
+        },
+        "deny_rate_24h": 0.06             # 6% of evaluations resulted in deny
+      }
+    ],
+    "edges": [
+      {
+        "from_payload_type": "request.layers_assembled",
+        "to_node_id": "",
+        "edge_type": "policy_fires_on",
+        "volume_24h": 47
+      },
+      {
+        "from_node_id": "",
+        "to_payload_type": "request.policies_evaluated",
+        "edge_type": "produces",
+        "condition": "on_allow"
+      }
+    ]
+  },
+  "payload_types": [
+    {
+      "payload_type": "request.layers_assembled",
+      "volume_24h": 789,
+      "active_policy_count": 4
+    }
+  ],
+  "last_updated": ""
+}
+```
+
+### 2.3 API — Get Policy Node Detail
+
+```
+GET /flow/api/v1/graph/nodes/{policy_uuid}
+
+Response 200:
+{
+  "policy_uuid": "",
+  "handle": "tenant/payments/gatekeeper/vm-size-limits",
+  "version": "1.2.0",
+  "policy_type": "gatekeeper",
+  "domain": "tenant",
+  "concern_type": "security",
+  "enforcement": "soft",
+  "status": "active",
+
+  "match_conditions": {
+    "payload_type": "request.layers_assembled",
+    "conditions": [
+      { "field": "payload.fields.cpu_count.value", "operator": "gt", "value": 32 }
+    ]
+  },
+
+  "output_schema": {
+    "decision": "deny",
+    "reason_template": "cpu_count {value} exceeds maximum 32"
+  },
+
+  "firing_history": [
+    { "timestamp": "", "result": "deny", "request_uuid": "" },
+    { "timestamp": "", "result": "allow", "request_uuid": "" }
+  ],
+
+  "git_path": "policy-store/tenant/payments/gatekeeper/vm-size-limits/v1.2.0.yaml",
+  "pr_url": null,             # null if no pending PR; URL if change in review
+
+  "compliance_basis": null,
+  "review_required_before": null,
+
+  "test_suite": {
+    "test_count": 3,
+    "last_run": "",
+    "result": "pass"
+  }
+}
+```
+
+---
+
+## 3. Policy Canvas — Static Flow Builder
+
+### 3.1 Interaction Model
+
+The Policy Canvas is a drag-and-drop interface for building named Orchestration Flow Policies (Level 1 orchestration — named workflow artifacts). The output is a valid DCM Orchestration Flow Policy YAML committed via a Git PR.
+
+**Key constraint:** The canvas never writes directly to the Policy Store. All saves generate a Git PR. The PR goes through the standard review process. Shadow mode activates automatically when the PR is created — the proposed workflow evaluates against real traffic in shadow mode until merged.
+
+### 3.2 Canvas Operations
+
+| Operation | Description | Backend action |
+|-----------|-------------|----------------|
+| Drag payload type node | Add a workflow step | Canvas state update (local) |
+| Connect nodes | Declare step sequence | Canvas state update (local) |
+| Set step conditions | Add conditions to a step | Canvas state update (local) |
+| Set failure behavior | halt / skip / escalate | Canvas state update (local) |
+| Preview YAML | Show generated policy YAML | `GET /flow/api/v1/canvas/preview` |
+| Save as PR | Create Git PR with policy YAML | `POST /flow/api/v1/canvas/save` |
+| Load existing | Load an existing flow policy | `GET /flow/api/v1/policies/{policy_uuid}/canvas` |
+
+### 3.3 API — Preview Canvas as YAML
+
+```
+POST /flow/api/v1/canvas/preview
+
+Request body:
+{
+  "handle": "org/orchestration/vm-provisioning-flow",
+  "concern_type": "orchestration_flow",
+  "ordered": true,
+  "steps": [
+    {
+      "step": 1,
+      "payload_type": "request.initiated",
+      "policy_handle": "system/orchestration/capture-intent",
+      "on_fail": "halt"
+    },
+    {
+      "step": 2,
+      "payload_type": "request.intent_captured",
+      "policy_handle": "system/orchestration/assemble-layers",
+      "on_fail": "halt"
+    },
+    {
+      "step": 3,
+      "payload_type": "request.layers_assembled",
+      "policy_handle": "system/orchestration/run-placement",
+      "on_fail": "halt",
+      "condition": "not payload.placement_complete"
+    }
+  ],
+  "applicable_resource_types": ["Compute.VirtualMachine"]
+}
+
+Response 200:
+{
+  "yaml": "# Generated by DCM Flow GUI\n# Handle: org/orchestration/vm-provisioning-flow\n...",
+  "rego": "package dcm.orchestration.vm_provisioning_flow\n...",
+  "validation": {
+    "valid": true,
+    "warnings": ["Step 3 condition references 'payload.placement_complete' which is not in the standard payload vocabulary"]
+  }
+}
+```
+
+### 3.4 API — Save Canvas as Git PR
+
+```
+POST /flow/api/v1/canvas/save
+
+Request body:
+{
+  "canvas_definition": { ... },  # same as preview request
+  "commit_message": "Add VM provisioning orchestration flow",
+  "pr_title": "feat(orchestration): VM provisioning named workflow",
+  "pr_description": "Defines explicit sequence for VM provisioning requests",
+  "target_branch": "main",
+  "shadow_mode": true            # proposed status — shadow evaluates before merge
+}
+
+Response 201 Created:
+{
+  "pr_uuid": "",
+  "pr_url": "https://git.corp.example.com/dcm-policies/pulls/142",
+  "pr_status": "open",
+  "shadow_mode_activated": true,
+  "policy_handle": "org/orchestration/vm-provisioning-flow",
+  "policy_status": "proposed"   # active in shadow mode; not yet enforced
+}
+```
+
+### 3.5 API — Load Existing Flow Policy into Canvas
+
+```
+GET /flow/api/v1/policies/{policy_uuid}/canvas
+
+Response 200:
+{
+  "canvas_definition": {
+    "handle": "...",
+    "ordered": true,
+    "steps": [...]
+  },
+  "yaml": "...",
+  "policy_uuid": "",
+  "version": "1.2.0",
+  "git_path": "..."
+}
+```
+
+---
+
+## 4. Policy Authoring Interface
+
+### 4.1 Visual Condition Builder
+
+For simple policies (field comparisons, role checks, quota checks), a visual condition builder generates valid Rego without requiring Rego knowledge.
+
+**Supported condition types:**
+
+| Field type | Operators | Example |
+|-----------|-----------|---------|
+| Numeric field | equals, not_equals, gt, gte, lt, lte, in_range | `cpu_count > 32` |
+| String field | equals, not_equals, in_list, matches_regex | `os_family in [rhel, ubuntu-lts]` |
+| List field | contains, does_not_contain | `actor.roles contains platform_admin` |
+| Boolean field | is_true, is_false | `payload.fields.production_workload = true` |
+| Existence | exists, does_not_exist | `payload.fields.cost_center exists` |
+
+### 4.2 API — Generate Policy from Visual Conditions
+
+```
+POST /flow/api/v1/policies/generate
+
+Request body:
+{
+  "policy_type": "gatekeeper",
+  "handle": "tenant/payments/gatekeeper/vm-size-limits",
+  "concern_type": "security",
+  "domain": "tenant",
+  "tenant_uuid": "",
+  "enforcement": "soft",
+  "match": {
+    "payload_type": "request.layers_assembled",
+    "resource_type": "Compute.VirtualMachine",
+    "conditions": [
+      { "field": "payload.fields.cpu_count.value", "operator": "gt", "value": 32 }
+    ],
+    "condition_logic": "any"
+  },
+  "output": {
+    "decision": "deny",
+    "reason_template": "cpu_count {payload.fields.cpu_count.value} exceeds maximum 32 for this Tenant"
+  },
+  "audit_on": ["DENY"],
+  "notification_on": ["DENY"]
+}
+
+Response 200:
+{
+  "yaml": "# DCM GateKeeper Policy\n...",
+  "rego": "package dcm.gatekeeper.vm_size_limits\n\ndeny contains reason if {\n    input.payload.type == \"request.layers_assembled\"\n    input.payload.fields.cpu_count.value > 32\n    reason := sprintf(\"cpu_count %d exceeds maximum 32\", [input.payload.fields.cpu_count.value])\n}\n",
+  "validation": {
+    "valid": true,
+    "warnings": []
+  }
+}
+```
+
+### 4.3 Rego Editor
+
+For complex policies requiring full Rego expressiveness, the GUI includes an embedded Rego editor with:
+
+- **Input schema autocomplete:** all valid `input.*` paths from the DCM input document schema
+- **DCM built-in reference:** sidebar showing available built-in functions and constants
+- **Real-time syntax validation:** calls OPA `/v1/compile` to validate without evaluation
+- **Test case runner:** executes the policy against saved test cases
+
+### 4.4 API — Validate Rego
+
+```
+POST /flow/api/v1/policies/validate-rego
+
+Request body:
+{
+  "rego": "package dcm.gatekeeper.example\n\ndeny contains reason if {\n    input.payload.fields.cpu_count.value > 32\n    reason := \"too many CPUs\"\n}\n",
+  "policy_type": "gatekeeper"
+}
+
+Response 200:
+{
+  "valid": true,
+  "warnings": [],
+  "errors": [],
+  "output_schema_match": true,   # output matches declared policy_type schema
+  "input_paths_used": [
+    "input.payload.fields.cpu_count.value"
+  ],
+  "input_paths_unknown": []      # paths that don't exist in the input document schema
+}
+
+Response 200 (with errors):
+{
+  "valid": false,
+  "errors": [
+    { "line": 4, "column": 5, "message": "undefined variable: reason_text" }
+  ]
+}
+```
+
+### 4.5 Test Case Management
+
+```
+# List test cases for a policy
+GET /flow/api/v1/policies/{policy_uuid}/tests
+
+Response 200:
+{
+  "test_cases": [
+    {
+      "test_uuid": "",
+      "name": "Reject oversized VM",
+      "input_payload": { "payload": { "type": "request.layers_assembled", "fields": { "cpu_count": { "value": 64 } } } },
+      "expected_output": { "deny": ["cpu_count 64 exceeds maximum 32"] },
+      "last_result": "pass",
+      "last_run": ""
+    }
+  ]
+}
+
+# Create test case from a real recent request
+POST /flow/api/v1/policies/{policy_uuid}/tests/from-request
+{
+  "request_uuid": "",      # saves that request's payload as a test case
+  "expected_output": { "deny": [] },
+  "test_name": "Normal VM request — should allow"
+}
+
+# Run all test cases
+POST /flow/api/v1/policies/{policy_uuid}/tests:run
+
+Response 200:
+{
+  "run_uuid": "",
+  "result": "pass",             # pass | fail | error
+  "test_results": [
+    {
+      "test_uuid": "",
+      "name": "Reject oversized VM",
+      "result": "pass",
+      "actual_output": { "deny": ["cpu_count 64 exceeds maximum 32"] },
+      "expected_output": { "deny": ["cpu_count 64 exceeds maximum 32"] }
+    }
+  ],
+  "duration_ms": 42
+}
+```
+
+---
+
+## 5. Flow Simulation
+
+### 5.1 Simulation Model
+
+Platform engineers simulate a synthetic request through the active policy engine without creating real state. The simulation runs against the live Policy Engine with a caller-constructed payload. No audit records are written. No Requested State is created.
+
+### 5.2 API — Simulate Request
+
+```
+POST /flow/api/v1/simulate
+
+Request body:
+{
+  "catalog_item_uuid": "",     # optional; used to seed field schema
+  "resource_type": "Compute.VirtualMachine",
+  "tenant_uuid": "",
+  "synthetic_fields": {
+    "cpu_count": 64,
+    "memory_gb": 128,
+    "os_family": "rhel"
+  },
+  "synthetic_actor": {
+    "roles": ["developer"],
+    "group_memberships": ["payments-team"]
+  },
+  "include_policy_types": ["gatekeeper", "transformation", "governance_matrix"]
+}
+
+Response 200:
+{
+  "simulation_uuid": "",
+  "result": "rejected",            # allowed | rejected | degraded
+  "terminal_reason": "GateKeeper policy rejected at step request.layers_assembled",
+
+  "execution_trace": [
+    {
+      "step": 1,
+      "payload_type": "request.initiated",
+      "policies_evaluated": [],
+      "result": "pass",
+      "duration_ms": 2
+    },
+    {
+      "step": 2,
+      "payload_type": "request.intent_captured",
+      "policies_evaluated": [],
+      "result": "pass",
+      "duration_ms": 1
+    },
+    {
+      "step": 3,
+      "payload_type": "request.layers_assembled",
+      "policies_evaluated": [
+        {
+          "policy_uuid": "",
+          "policy_handle": "tenant/payments/gatekeeper/vm-size-limits",
+          "policy_type": "gatekeeper",
+          "result": "deny",
+          "reason": "cpu_count 64 exceeds maximum 32",
+          "duration_ms": 8
+        },
+        {
+          "policy_uuid": "",
+          "policy_handle": "org/transformation/inject-monitoring",
+          "policy_type": "transformation",
+          "result": "applied",
+          "mutations": [
+            { "field": "fields.monitoring_endpoint", "operation": "set", "value": "https://metrics..." }
+          ],
+          "duration_ms": 3
+        }
+      ],
+      "result": "rejected",
+      "terminal": true
+    }
+  ],
+
+  "assembled_payload_snapshot": {
+    "fields": {
+      "cpu_count": { "value": 64, "provenance": { "origin": { "source_type": "consumer_request" } } },
+      "monitoring_endpoint": { "value": "https://metrics...", "provenance": { "origin": { "source_type": "policy" } } }
+    }
+  },
+
+  "cost_estimate": {
+    "total_per_hour": 1.28,
+    "currency": "USD",
+    "note": "Estimated assuming request would have been allowed"
+  }
+}
+```
+
+### 5.3 Simulation vs Shadow Mode
+
+| | Simulation | Shadow Mode |
+|-|-----------|------------|
+| Trigger | Manual, synthetic payload | Automatic on real traffic |
+| Audit record | Never written | Written to Validation Store |
+| Policy status | Evaluates active policies | Evaluates proposed policies |
+| Use case | "What if?" exploration | Pre-activation validation |
+| Real data | No | Yes |
+
+---
+
+## 6. Shadow Mode Dashboard
+
+### 6.1 What It Shows
+
+Shows all proposed policies currently in shadow mode and their evaluation results against real traffic.
+
+### 6.2 API — List Shadow Policies
+
+```
+GET /flow/api/v1/shadow
+
+Response 200:
+{
+  "shadow_policies": [
+    {
+      "policy_uuid": "",
+      "handle": "tenant/payments/gatekeeper/new-cost-check",
+      "policy_type": "gatekeeper",
+      "status": "proposed",
+      "shadow_since": "",
+      "pr_url": "https://git.corp.example.com/dcm-policies/pulls/143",
+      "pr_status": "open",
+
+      "shadow_results_24h": {
+        "total_evaluations": 156,
+        "would_have_denied": 4,
+        "would_have_allowed": 152,
+        "divergence_from_active": 4,
+        "divergence_rate": 0.026
+      }
+    }
+  ]
+}
+```
+
+### 6.3 API — Shadow Policy Detail with Divergence Cases
+
+```
+GET /flow/api/v1/shadow/{policy_uuid}
+
+Response 200:
+{
+  "policy_uuid": "",
+  "shadow_results_24h": {
+    "total_evaluations": 156,
+    "divergence_cases": [
+      {
+        "request_uuid": "",
+        "timestamp": "",
+        "active_result": "allow",
+        "shadow_result": "deny",
+        "shadow_reason": "Estimated cost $480/month exceeds budget ceiling $300/month",
+        "requester": "Bob Smith",
+        "resource_type": "Compute.VirtualMachine"
+      }
+    ]
+  }
+}
+```
+
+### 6.4 API — Promote Shadow Policy to Active
+
+```
+POST /flow/api/v1/shadow/{policy_uuid}:promote
+{
+  "reason": "Shadow results reviewed — divergence rate acceptable; promoting to active"
+}
+
+Response 202 Accepted:
+{
+  "policy_uuid": "",
+  "status": "active",
+  "pr_action": "approved_and_merged",
+  "promoted_at": ""
+}
+
+Response 403 Forbidden:
+{
+  "error": "insufficient_role",
+  "reason": "Policy promotion requires platform_admin role"
+}
+```
+
+---
+
+## 7. Profile and Governance Management
+
+### 7.1 Active Profile View
+
+```
+GET /flow/api/v1/profile
+
+Response 200:
+{
+  "deployment_posture": {
+    "name": "prod",
+    "description": "Production — full zero trust, dual approval for high-trust providers, human review for all registrations",
+    "active_policy_groups": 12,
+    "hard_constraints": [
+      "sovereign/classified data never crosses any boundary",
+      "All providers require at least self_declared accreditation"
+    ]
+  },
+  "compliance_domains": [
+    {
+      "domain": "hipaa",
+      "description": "HIPAA/HITECH compliance — PHI classification, BAA requirements, minimum necessary principle",
+      "active_policy_groups": 4,
+      "key_requirements": ["PHI requires BAA accreditation", "All PHI interactions audited", "No PHI export without regulatory cert"]
+    }
+  ],
+  "recovery_posture": "notify-and-wait",
+  "zero_trust_posture": "full",
+  "total_active_policies": 47
+}
+```
+
+### 7.2 Payload Type Browser
+
+```
+GET /flow/api/v1/payload-types
+
+Response 200:
+{
+  "payload_types": [
+    {
+      "payload_type": "request.layers_assembled",
+      "description": "Layer assembly complete — payload enriched with all layer fields",
+      "volume_24h": 789,
+      "active_policy_count": 4,
+      "sample_payload": {
+        "type": "request.layers_assembled",
+        "fields": {
+          "cpu_count": { "value": 4 },
+          "memory_gb": { "value": 8 }
+        }
+      },
+      "downstream_payload_types": ["request.policies_evaluated", "recovery.gatekeeper_denied"]
+    }
+  ]
+}
+```
+
+---
+
+### 7.3 API — Update Scoring Thresholds
+
+```
+PATCH /flow/api/v1/profile/scoring
+
+Authorization: Bearer 
+
+Request body:
+{
+  "thresholds": [
+    { "tier": "auto",       "max_score": 24 },
+    { "tier": "reviewed",   "max_score": 59 },
+    { "tier": "verified",   "max_score": 79 },
+    { "tier": "authorized", "max_score": 100 }
+  ],
+  "policy_overrides": [
+    {
+      "policy_uuid": "",
+      "enforcement_class_override": "compliance",
+      "reason": "Regulatory requirement"
+    }
+  ]
+}
+
+Response 200:
+{
+  "active_profile": "standard",
+  "thresholds": [ ... ],
+  "preview": {
+    "last_7d_auto_approve_pct": 61.2,
+    "last_7d_reviewed_pct": 31.5,
+    "last_7d_verified_pct": 7.3
+  }
+}
+```
+
+**Constraint:** `auto` tier `max_score` cannot exceed 50 (SMX-008 hard cap). The API
+returns `422 Unprocessable Entity` if this constraint is violated.
+
+### 7.4 API — Payload Type Browser
+
+```
+GET /flow/api/v1/payload-types
+
+Response 200:
+{
+  "payload_types": [
+    {
+      "type": "request.initiated",
+      "description": "A new service request has been received",
+      "fields": [
+        { "path": "catalog_item_uuid", "type": "uuid", "required": true },
+        { "path": "tenant_uuid",       "type": "uuid", "required": true },
+        { "path": "fields",            "type": "object", "required": true }
+      ],
+      "policy_types_applicable": ["gatekeeper", "validation", "transformation",
+                                   "recovery", "orchestration_flow"]
+    }
+  ]
+}
+```
+
+
+## 8. Notification Flow View
+
+### 8.1 API — Notification Flow for an Entity
+
+```
+GET /flow/api/v1/notifications/flow/{entity_uuid}
+
+Response 200:
+{
+  "entity_uuid": "",
+  "entity_display_name": "VLAN-100",
+  "relationship_graph_depth": 2,
+
+  "notification_audiences": [
+    {
+      "actor_uuid": "",
+      "display_name": "NetworkOps Team",
+      "audience_role": "owner",
+      "stakeholder_reason": null,
+      "service_providers": ["slack-corp", "pagerduty-prod"]
+    },
+    {
+      "actor_uuid": "",
+      "display_name": "AppTeam Admin",
+      "audience_role": "stakeholder",
+      "stakeholder_reason": {
+        "via_entity": "VM-A",
+        "via_relationship": "attached_to",
+        "stake_strength": "required"
+      },
+      "service_providers": ["slack-corp"]
+    }
+  ],
+
+  "active_service_providers": [
+    {
+      "provider_uuid": "",
+      "display_name": "slack-corp",
+      "status": "healthy",
+      "delivery_success_rate_24h": 0.998
+    }
+  ]
+}
+```
+
+---
+
+## 9. Error Model
+
+All Flow GUI API errors follow the standard DCM error format:
+
+```json
+{
+  "error": "",
+  "message": "",
+  "request_id": "",
+  "timestamp": ""
+}
+```
+
+| HTTP Status | Error Code | Meaning |
+|-------------|-----------|---------|
+| 403 | `insufficient_role` | Operation requires platform_admin or policy_author role |
+| 404 | `policy_not_found` | Policy UUID not found in active policy store |
+| 409 | `pr_already_open` | A PR already exists for this policy handle |
+| 422 | `invalid_canvas` | Canvas definition is invalid (disconnected steps, unknown payload types) |
+| 422 | `rego_invalid` | Rego syntax error or output schema mismatch |
+| 422 | `simulation_failed` | Simulation could not be executed (missing fields, invalid tenant) |
+| 503 | `policy_engine_unavailable` | Policy Engine unreachable — graph data may be stale |
+| 503 | `git_unavailable` | GitOps store unreachable — PR creation unavailable |
+
+---
+
+## 10. Conformance Levels
+
+| Level | Name | Capabilities | Intended Use |
+|-------|------|-------------|-------------|
+| 1 | Read-Only | Execution Graph View (read), Profile View, Payload Type Browser, Notification Flow View, Scoring Overlay | Dashboards, observability integrations, read-only monitoring tools |
+| 2 | Standard | All Level 1 + Flow Simulation, Shadow Mode Dashboard (view only), Policy Node Detail, Scoring Simulation | Platform engineer tooling, policy review workflows |
+| 3 | Full | All Level 2 + Policy Canvas (save as PR), Policy Authoring Interface, Test Case Management, Shadow Mode Promotion | Full policy lifecycle management, policy authoring tooling |
+
+### 10.1 Conformance Implementation Checklist
+
+**Level 1 — minimum required endpoints:**
+- `GET /flow/api/v1/graph`
+- `GET /flow/api/v1/graph/nodes/{policy_uuid}`
+- `GET /flow/api/v1/profile`
+- `GET /flow/api/v1/payload-types`
+- `GET /flow/api/v1/notifications/flow/{entity_uuid}`
+- `GET /flow/api/v1/graph/scoring-overlay`
+
+**Level 2 — adds:**
+- `POST /flow/api/v1/simulate`
+- `POST /flow/api/v1/simulate/score`
+- `GET /flow/api/v1/shadow`
+- `GET /flow/api/v1/shadow/{policy_uuid}`
+
+**Level 3 — adds:**
+- `GET /flow/api/v1/canvas/preview`
+- `POST /flow/api/v1/canvas/save`
+- `GET /flow/api/v1/policies/{policy_uuid}/canvas`
+- `POST /flow/api/v1/policies/generate`
+- `POST /flow/api/v1/policies/validate-rego`
+- `GET /flow/api/v1/policies/{policy_uuid}/tests`
+- `POST /flow/api/v1/policies/{policy_uuid}/tests/from-request`
+- `POST /flow/api/v1/policies/{policy_uuid}/tests:run`
+- `POST /flow/api/v1/shadow/{policy_uuid}:promote`
+- `PATCH /flow/api/v1/profile/scoring`
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+
+---
+
+## 11. Scoring Model Views
+
+### 11.1 Risk Score Overlay on Execution Graph
+
+The Execution Graph View has a **Score Mode** toggle that overlays risk scoring information:
+
+- Each operational-class GateKeeper node displays its `scoring_weight`
+- Node background color shifts from green (weight 1–20) through amber (21–50) to red (51–100)
+- A running score accumulator shows the current aggregate as the user traces a path through the graph
+- Compliance-class GateKeeper nodes display a lock icon — they are always boolean
+
+### 11.2 API — Get Score Configuration for Graph Overlay
+
+```
+GET /flow/api/v1/graph/scoring-overlay
+
+Response 200:
+{
+  "active_profile": "standard",
+  "thresholds": [
+    { "tier": "auto",       "max_score": 24 },
+    { "tier": "reviewed",   "max_score": 59 },
+    { "tier": "verified",   "max_score": 79 },
+    { "tier": "authorized", "max_score": 100 }
+  ],
+  "nodes": [
+    {
+      "node_id": "",
+      "enforcement_class": "operational",
+      "scoring_weight": 35,
+      "avg_contribution_24h": 28.5
+    }
+  ]
+}
+```
+
+### 11.3 Threshold Configuration UI (Profile Management)
+
+The Profile and Governance Management view (Section 7) is extended with a **Scoring Thresholds** panel:
+
+- Visual slider showing auto_approve / reviewed / verified / authorized bands on a 0–100 scale
+- Signal weight configuration (pie chart showing proportional contribution of each signal)
+- Policy enforcement override management (which policies are promoted/demoted in this profile)
+- Live preview: "At the current thresholds, X% of last week's requests would have been auto-approved"
+
+### 11.4 Score Breakdown in Simulation
+
+The Flow Simulation output (Section 5) is extended with a score breakdown panel:
+
+```
+Simulation result: risk_score=47, routing=reviewed
+
+Score breakdown:
+  Operational GateKeepers:  50 × 0.45 = 22.5
+    ├── cost-ceiling:        +35 ("Cost $620/month exceeds $500")
+    └── off-hours:           +15 ("Request outside business hours")
+  Completeness:             20 × 0.15 = 3.0
+    └── cost_center_absent:  +10
+  Actor risk history:       30 × 0.20 = 6.0
+    └── (2 recent events)
+  Quota pressure:           48 × 0.10 = 4.8
+    └── (87% utilized)
+  Provider risk:            15 × 0.10 = 1.5
+    └── (richness score: 85/100 → contribution: 1.5)
+  ─────────────────────────────────────────────
+  Total:                              37.8 → 47 (normalized)
+  Threshold (reviewed):           25
+  Routing decision:                   HUMAN_REVIEW ✓
+```
+
+### 11.5 API — Get Score Simulation
+
+```
+POST /flow/api/v1/simulate/score
+
+Request body:
+{
+  "synthetic_fields": { ... },
+  "synthetic_actor": { "roles": ["developer"], "risk_history_score_override": 30 },
+  "profile_override": "prod"   # optional — simulate with different profile thresholds
+}
+
+Response 200:
+{
+  "risk_score": 47,
+  "routing_decision": "reviewed",
+  "signal_breakdown": { ... },
+  "threshold_applied": 25,
+  "profile": "standard",
+  "advisory_warnings": [...]
+}
+```
+
diff --git a/content/docs/architecture/specifications/dcm-opa-integration-spec.md b/content/docs/architecture/specifications/dcm-opa-integration-spec.md
new file mode 100644
index 0000000..1113984
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-opa-integration-spec.md
@@ -0,0 +1,582 @@
+# DCM OPA Integration Specification
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Integration Specification
+
+
+> **AEP Alignment:** DCM API endpoints referenced in this spec follow [AEP](https://aep.dev) conventions. `resource_type` accepts FQN string or Registry UUID — DCM resolves internally. See `schemas/openapi/dcm-consumer-api.yaml` and `dcm-admin-api.yaml`.
+
+
+> **📋 Draft**
+>
+> This specification has been promoted from Work in Progress to Draft status. All questions resolved. All 7 policy types validated with working Rego examples. OPA/Rego confirmed as complete reference implementation. It is ready for implementation feedback but has not yet been formally reviewed for final release.
+>
+> This specification defines the OPA integration contract for DCM External Policy Evaluators. It is published to share design direction and invite feedback. Do not build production integrations against this specification until it reaches draft status.
+
+**Version:** 0.1.0-draft
+**Status:** Draft — Ready for implementation feedback
+**Document Type:** Technical Specification
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [Policy Profiles](../data-model/14-policy-profiles.md) | [Control Plane Components](../data-model/25-control-plane-components.md) | [DCM Operator Interface Specification](dcm-operator-interface-spec.md)
+
+---
+
+## Abstract
+
+This specification defines how Open Policy Agent (OPA) integrates with the DCM Policy Engine as the reference implementation for OPA-based policy evaluations. It defines the DCM payload schema as an OPA input document, the expected decision schema as OPA output, the built-in functions DCM provides to Rego policies, and the test harness contract for validating policies before activation.
+
+OPA is not required to implement DCM — any OPA-based policy evaluation can implement DCM's policy contract. However, OPA with Rego is the recommended reference implementation, and this specification enables implementors and integrators to build standards-compliant DCM policy engines.
+
+---
+
+## 1. Introduction
+
+### 1.1 The Policy Engine Contract
+
+DCM's Policy Engine evaluates policies at multiple points in the request lifecycle. The engine receives a payload, evaluates all active matching policies, and accumulates mutations. The OPA integration maps this contract to Rego evaluation.
+
+DCM policy types:
+- **GateKeeper** — approve or reject; output is a decision (allow/deny + reason)
+- **Validation** — verify correctness; output is a validation result (pass/fail + details)
+- **Transformation** — enrich or modify; output is a set of field mutations
+- **Recovery** — respond to failure/ambiguity; output is a recovery action
+- **Orchestration Flow** — coordinate pipeline steps; output is a flow directive
+
+All five types share the same OPA input schema. The output schema differs per type.
+
+### 1.2 OPA-based policy evaluation
+
+A OPA-based policy evaluation executes OPA Rego bundles. DCM dispatches the policy input document to the OPA instance and receives the decision document. The OPA instance may be:
+- Embedded within DCM (the reference implementation)
+- A sidecar OPA instance (co-located with DCM)
+- A remote OPA instance (requires network call; latency considerations apply)
+
+---
+
+## 2. Input Schema — DCM Payload as OPA Document
+
+Every OPA policy evaluation receives the following input document:
+
+```rego
+# input document structure
+input := {
+  # The current payload being evaluated
+  "payload": {
+    "type": "request.initiated",         # payload type from the vocabulary
+    "entity_uuid": "...",
+    "resource_type": "Compute.VirtualMachine",
+    "version": "2.1.0",
+    "fields": {
+      "cpu_count": {
+        "value": 4,
+        "provenance": { "origin": {...}, "modifications": [...] }
+      }
+      # ... all assembled fields with provenance
+    }
+  },
+
+  # The requesting actor context
+  "actor": {
+    "uuid": "...",
+    "type": "human",                     # human | service_account | system
+    "tenant_uuid": "...",
+    "roles": ["developer"],
+    "groups": ["payments-team", "eu-west-users"],
+    "mfa_verified": true,
+    "auth_level": "oidc_mfa"
+  },
+
+  # The active deployment governance
+  "deployment": {
+    "posture": "prod",
+    "compliance_domains": ["hipaa", "gdpr"],
+    "recovery_posture": "notify-and-wait",
+    "profile_uuid": "..."
+  },
+
+  # Entity context (null for new requests)
+  "entity": {
+    "uuid": "...",
+    "lifecycle_state": "OPERATIONAL",
+    "ownership_model": "whole_allocation",
+    "owned_by_tenant_uuid": "...",
+    "relationship_count": 3,
+    "drift_status": "clean"
+  },
+
+  # Provider context (null before placement)
+  "provider": {
+    "uuid": "...",
+    "sovereignty_declaration": {...},
+    "trust_score": 94,
+    "capacity_confidence": "high"
+  },
+
+  # DCM built-in data (resolved by DCM before OPA evaluation)
+  "dcm": {
+    "tenant": {
+      "uuid": "...",
+      "display_name": "Payments Platform",
+      "active_entity_count": { "Compute.VirtualMachine": 47 },
+      "compliance_overlays": ["hipaa"]
+    },
+    "cost_estimate": {
+      "per_hour": 0.32,
+      "confidence": "high"
+    }
+  }
+}
+```
+
+---
+
+## 3. Output Schema — OPA Decision Documents
+
+### 3.1 GateKeeper Output
+
+```rego
+package dcm.gatekeeper.vm_size_limits
+
+import future.keywords
+
+# Main decision
+allow if {
+  input.payload.fields.cpu_count.value <= max_cpu
+}
+
+deny contains reason if {
+  input.payload.fields.cpu_count.value > max_cpu
+  reason := sprintf("cpu_count %d exceeds maximum %d for tenant %s",
+    [input.payload.fields.cpu_count.value, max_cpu, input.actor.tenant_uuid])
+}
+
+# DCM reads the deny set; empty = allow
+max_cpu := 32
+```
+
+DCM output contract:
+```json
+{
+  "allow": true,
+  "deny": [],
+  "warnings": [],
+  "policy_uuid": "...",
+  "evaluated_at": "..."
+}
+```
+
+### 3.2 Transformation Output
+
+```rego
+package dcm.transformation.inject_monitoring
+
+mutations contains mutation if {
+  input.payload.type == "request.layers_assembled"
+  not input.payload.fields.monitoring_endpoint
+  mutation := {
+    "field": "monitoring_endpoint",
+    "value": concat(".", ["https://metrics.internal", input.deployment.posture, "example.com"]),
+    "source_type": "policy",
+    "operation_type": "enrichment",
+    "reason": "Standard monitoring endpoint injection"
+  }
+}
+```
+
+DCM output contract:
+```json
+{
+  "mutations": [
+    {
+      "field": "monitoring_endpoint",
+      "value": "https://metrics.internal.prod.example.com",
+      "source_type": "policy",
+      "operation_type": "enrichment",
+      "reason": "Standard monitoring endpoint injection"
+    }
+  ],
+  "policy_uuid": "..."
+}
+```
+
+### 3.3 Recovery Policy Output
+
+```rego
+package dcm.recovery.discard_on_timeout
+
+action := "DISCARD_AND_REQUEUE" if {
+  input.payload.type == "recovery.timeout_fired"
+  input.entity.lifecycle_state == "TIMEOUT_PENDING"
+}
+```
+
+DCM output contract:
+```json
+{
+  "action": "DISCARD_AND_REQUEUE",
+  "action_parameters": { "requeue_delay": "PT0S" },
+  "policy_uuid": "..."
+}
+```
+
+---
+
+## 4. DCM Built-in Functions for Rego
+
+DCM provides built-in functions callable from Rego policies:
+
+```rego
+# Entity relationship graph queries
+dcm.entity.relationships(entity_uuid)
+  # Returns: array of relationship records for the entity
+
+dcm.entity.has_relationship(entity_uuid, relationship_type)
+  # Returns: bool
+
+dcm.entity.stakeholder_count(entity_uuid, min_stake_strength)
+  # Returns: int
+
+# Information Provider data
+dcm.entity.field_confidence(entity_uuid, field_path)
+  # Returns: { band, score, authority_level }
+
+# Sovereignty checks
+dcm.sovereignty.compatible(entity_uuid, provider_uuid)
+  # Returns: bool
+
+dcm.sovereignty.violates(entity_uuid, data_residency_requirement)
+  # Returns: bool
+
+# Cost queries
+dcm.cost.estimate(catalog_item_uuid, fields)
+  # Returns: { per_hour, currency, confidence }
+
+# Tenant quota queries
+dcm.tenant.active_count(tenant_uuid, resource_type)
+  # Returns: int
+
+dcm.tenant.has_authorization(granting_tenant_uuid, consuming_tenant_uuid, resource_type)
+  # Returns: bool
+```
+
+---
+
+## 5. Policy Bundle Structure
+
+OPA policies for DCM are packaged as bundles:
+
+```
+dcm-policy-bundle/
+├── .manifest
+│   {
+│     "roots": ["dcm"],
+│     "metadata": {
+│       "dcm_policy_type": "gatekeeper",
+│       "resource_types": ["Compute.VirtualMachine"],
+│       "domain": "tenant",
+│       "handle": "org/policies/vm-size-limits",
+│       "version": "1.0.0"
+│     }
+│   }
+├── dcm/
+│   └── gatekeeper/
+│       └── vm_size_limits/
+│           └── policy.rego
+└── tests/
+    └── vm_size_limits_test.rego
+```
+
+---
+
+## 6. Test Harness
+
+DCM provides a test harness that policy authors use to validate policies against sample payloads before activation:
+
+```
+POST /api/v1/admin/policies/test
+
+{
+  "policy_bundle": "",
+  "test_cases": [
+    {
+      "description": "VM within CPU limit should be allowed",
+      "input": {
+        "payload": { "type": "request.initiated", "fields": { "cpu_count": { "value": 4 } } },
+        "actor": { "roles": ["developer"] },
+        "deployment": { "posture": "prod" }
+      },
+      "expected_output": { "allow": true, "deny": [] }
+    }
+  ]
+}
+```
+
+The test harness is also used during shadow mode — DCM runs the policy against real traffic and compares actual output to expected output before the policy activates.
+
+---
+
+## 7. Policy Shadow Mode with OPA
+
+When a policy is in `proposed` status, DCM evaluates it in shadow mode:
+
+1. Policy bundle loaded into a shadow OPA instance
+2. Every real request payload is evaluated by both active policies AND shadow policies
+3. Shadow outputs recorded in the Validation Store (not applied to requests)
+4. Policy authors review shadow results via the Admin API or Flow GUI
+5. On approval (no adverse results): policy status → `active`
+
+
+---
+
+## 8. Policy Model Validation — All Seven Types
+
+This section validates that OPA/Rego can express all seven DCM policy types and both levels of the orchestration model. Each type is shown with a working Rego example and an assessment.
+
+### 8.1 GateKeeper
+
+```rego
+package dcm.gatekeeper.vm_size_limits
+
+import future.keywords
+
+allow if {
+    input.payload.type == "request.layers_assembled"
+    input.payload.fields.cpu_count.value <= 32
+}
+
+deny contains reason if {
+    input.payload.type == "request.layers_assembled"
+    input.payload.fields.cpu_count.value > 32
+    reason := sprintf("cpu_count %d exceeds maximum 32",
+                      [input.payload.fields.cpu_count.value])
+}
+
+field_locks contains lock if {
+    input.deployment.compliance_domains[_] == "hipaa"
+    lock := {"field": "fields.patient_id", "lock_type": "immutable"}
+}
+```
+**Assessment:** Clean. Set-based deny with reasons, allow rules, field locks as set output.
+
+### 8.2 Validation
+
+```rego
+package dcm.validation.memory_alignment
+
+field_results contains result if {
+    input.payload.fields.memory_gb.value % 2 != 0
+    result := {
+        "field": "fields.memory_gb",
+        "result": "invalid",
+        "message": "memory_gb must be a power of 2"
+    }
+}
+
+result := "pass" if count(field_results) == 0
+result := "fail" if count(field_results) > 0
+```
+**Assessment:** Clean. Set comprehension for field results.
+
+### 8.3 Transformation
+
+```rego
+package dcm.transformation.inject_monitoring
+
+import future.keywords
+
+mutations contains mutation if {
+    input.payload.type == "request.layers_assembled"
+    not input.payload.fields.monitoring_endpoint
+    mutation := {
+        "field": "fields.monitoring_endpoint",
+        "operation": "set",
+        "value": concat(".", ["https://metrics.internal",
+                               input.deployment.deployment_posture, "example.com"]),
+        "reason": "Standard monitoring endpoint injection",
+        "source_type": "enrichment"
+    }
+}
+```
+**Assessment:** Clean. Multiple mutations as independent set members.
+
+### 8.4 Recovery
+
+```rego
+package dcm.recovery.timeout_response
+
+action := "NOTIFY_AND_WAIT" if {
+    input.payload.type == "recovery.timeout_fired"
+    input.deployment.deployment_posture in ["prod", "fsi", "sovereign"]
+}
+
+action := "DRIFT_RECONCILE" if {
+    input.payload.type == "recovery.timeout_fired"
+    input.deployment.deployment_posture in ["minimal", "dev", "standard"]
+}
+
+action_parameters := {"deadline": "PT4H", "on_deadline_exceeded": "ESCALATE"}
+    if action == "NOTIFY_AND_WAIT"
+```
+**Assessment:** Clean. Conditional action based on trigger + context.
+
+### 8.5 Orchestration Flow (Named Workflow)
+
+```rego
+package dcm.orchestration.request_lifecycle
+
+steps := [
+    {"step": 1, "payload_type": "request.initiated",
+     "policy_handle": "system/orchestration/capture-intent", "on_fail": "halt"},
+    {"step": 2, "payload_type": "request.intent_captured",
+     "policy_handle": "system/orchestration/assemble-layers", "on_fail": "halt"},
+    {"step": 3, "payload_type": "request.layers_assembled",
+     "policy_handle": "system/orchestration/run-placement", "on_fail": "halt"},
+    {"step": 4, "payload_type": "request.placement_complete",
+     "policy_handle": "system/orchestration/dispatch", "on_fail": "halt"}
+]
+
+ordered := true
+```
+**Assessment:** Clean. Step sequence as an array with `ordered: true` flag. GateKeeper and Transformation policies declared in separate packages fire on the same payload types independently — the Policy Engine coordinates both.
+
+### 8.6 Governance Matrix Rule
+
+```rego
+package dcm.governance_matrix.phi_federation
+
+import future.keywords
+
+decision := "DENY" if {
+    input.data.classification == "phi"
+    input.target.type == "dcm_peer"
+    not "hipaa" in input.target.accreditation_held
+}
+
+decision := "ALLOW_WITH_CONDITIONS" if {
+    input.data.classification == "phi"
+    input.target.type == "dcm_peer"
+    "hipaa" in input.target.accreditation_held
+    input.target.trust_posture == "verified"
+}
+
+field_permissions := {
+    "mode": "allowlist",
+    "paths": ["fields.resource_type", "fields.lifecycle_state"],
+    "on_blocked_field": "STRIP_FIELD"
+} if decision == "ALLOW_WITH_CONDITIONS"
+
+enforcement := "hard" if decision == "DENY"
+enforcement := "soft" if decision != "DENY"
+```
+**Assessment:** Clean. Four-axis input maps directly to OPA's input document. Decision + field permissions + enforcement as structured output.
+
+### 8.7 Lifecycle Policy
+
+```rego
+package dcm.lifecycle.required_dependency
+
+import future.keywords
+
+on_related_destroy := "cascade" if {
+    input.payload.type == "relationship.related_entity_destroying"
+    input.relationship.stake_strength == "required"
+}
+
+on_related_destroy := "notify" if {
+    input.payload.type == "relationship.related_entity_destroying"
+    input.relationship.stake_strength == "preferred"
+}
+
+propagation_depth := 1
+action_delay := "PT0S"
+```
+**Assessment:** Clean. Relationship event conditions; action output.
+
+---
+
+## 9. Three Things the Policy Engine Does That OPA Does Not
+
+OPA evaluates each package independently and returns results. The Policy Engine provides three coordination functions that OPA alone cannot:
+
+**1. Cross-policy ordered enforcement:** OPA produces the Orchestration Flow step sequence; the Policy Engine tracks which steps have fired and enforces ordering. Clean separation — OPA declares; Policy Engine enforces.
+
+**2. Hard enforcement composition:** OPA returns `enforcement: "hard"` as output metadata; the Policy Engine ensures hard DENY wins over all soft decisions. Clean — OPA produces the flag; Policy Engine applies the composition algorithm.
+
+**3. Domain precedence sequencing:** Multiple packages match the same payload type. The Policy Engine evaluates them in domain precedence order (system → platform → tenant → resource_type → entity) and composes results. Clean — each OPA package is stateless and independently evaluable; Policy Engine manages composition.
+
+**Conclusion:** OPA/Rego is a complete reference implementation for all seven DCM policy types and both levels of the orchestration model. No model gaps exist.
+
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
+
+
+---
+
+## Scoring Model — OPA/Rego Patterns
+
+### Operational GateKeeper Output Schema
+
+```rego
+package dcm.gatekeeper.operational.cost_ceiling
+
+# Operational-class GateKeeper produces risk_score_contribution, not deny
+# enforcement_class: operational is declared in policy YAML metadata
+
+risk_score_contribution[result] {
+    input.payload.cost_estimate.per_month > 500
+    result := {
+        "contribution": 35,
+        "label": "cost_ceiling_exceeded",
+        "reason": sprintf(
+            "Estimated monthly cost $%v exceeds Tenant ceiling $500",
+            [input.payload.cost_estimate.per_month]
+        )
+    }
+}
+
+# Operational GateKeepers can also produce hard deny for extreme values
+deny contains reason {
+    input.payload.cost_estimate.per_month > 10000
+    reason := "Cost exceeds absolute maximum — manual review required before submission"
+}
+```
+
+### Advisory Validation Output Schema
+
+```rego
+package dcm.validation.advisory.cost_center
+
+# Advisory-class Validation produces completeness_contribution + warning
+# output_class: advisory is declared in policy YAML metadata
+
+completeness_warnings[warning] {
+    not input.payload.fields.cost_center
+    warning := {
+        "contribution": 10,
+        "warning_code": "recommended_field_absent",
+        "warning_message": "cost_center not provided — cost attribution will use Tenant default",
+        "field": "fields.cost_center"
+    }
+}
+```
+
+### Validation — Structural vs Advisory in Same Package
+
+```rego
+package dcm.validation.vm_fields
+
+# Structural validation (output_class: structural)
+fail contains reason {
+    not input.payload.fields.cpu_count
+    reason := {
+        "field": "fields.cpu_count",
+        "code": "required_field_absent",
+        "message": "cpu_count is required"
+    }
+}
+
+# Advisory validation (output_class: advisory — separate policy)
+# Never mix structural and advisory in the same policy artifact
+```
+
diff --git a/content/docs/architecture/specifications/dcm-operator-api.yaml b/content/docs/architecture/specifications/dcm-operator-api.yaml
new file mode 100644
index 0000000..3831a36
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-operator-api.yaml
@@ -0,0 +1,621 @@
+openapi: "3.1.0"
+
+info:
+  title: DCM Operator Interface — Services API
+  version: "1.0.0"
+  description: |
+    The DCM Services API is the contract between the DCM control plane and Service Provider
+    operators. DCM calls this API to create, update, discover, and decommission resources.
+    The operator implements this API; DCM is the client.
+
+    **Conformance levels:** Operators implement one of five conformance levels. Higher levels
+    unlock additional DCM capabilities. See `dcm-operator-interface-spec.md` for the full
+    conformance model.
+
+    | Level | Minimum endpoints required |
+    |-------|---------------------------|
+    | Level 0 | Label-based passive discovery only — no API required |
+    | Level 1 | POST /create, GET /list, GET /{id}, DELETE /{id}, POST /health |
+    | Level 2 | Level 1 + PUT /{id} (update), POST /capacity, POST /discover |
+    | Level 3 | Level 2 + lifecycle callbacks, decommission_confirmation |
+    | Level 4 | Level 3 + streaming status, dependency graph, rehydration support |
+
+    **Authentication:** DCM uses mTLS for all operator interactions. Every request presents
+    a valid DCM-issued certificate. Operators must validate the certificate chain against the
+    DCM CA registered at provider registration time.
+
+    **Idempotency:** All mutating operations include a `request_id` (DCM request UUID). Operators
+    must implement idempotency: a second request with the same `request_id` must return the same
+    result without performing the operation again.
+
+    **Callback pattern:** For long-running operations (create, update, decommission), the operator
+    responds immediately with `PROVISIONING`/`UPDATING`/`DECOMMISSIONING` status and later
+    calls the DCM Callback API to report completion or failure. Level 1 operators may alternatively
+    block until completion.
+
+
+
+    **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions:
+    custom methods use colon syntax (`POST /resources/{name}:suspend`),
+    async operations return an `Operation` resource (AEP-136 LRO),
+    and list pagination uses `page_size`/`page_token` parameters.
+
+  contact:
+    name: DCM Project
+    url: https://github.com/dcm-project
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0
+
+servers:
+  - url: https://{operator-host}/api/v1/{service_type}
+    description: Operator endpoint (DCM is the client; operator implements this API)
+    variables:
+      operator-host:
+        description: Operator base URL declared at provider registration
+        default: operator.namespace.svc
+      service_type:
+        description: Resource type path segment (e.g., compute.virtualmachine)
+        default: compute.virtualmachine
+
+security:
+  - MutualTLS: []
+
+tags:
+  - name: resources
+    description: Resource CRUD operations (Level 1+)
+  - name: discovery
+    description: Active discovery of existing resources (Level 2+)
+  - name: capacity
+    description: Capacity reporting for placement engine (Level 2+)
+  - name: lifecycle
+    description: Lifecycle callback endpoints DCM calls for state transitions (Level 3+)
+  - name: health
+    description: Provider health endpoint (all levels)
+  - name: registration
+    description: Provider self-registration with DCM
+
+paths:
+
+  /health:
+    get:
+      tags: [health]
+      operationId: healthCheck
+      summary: Provider health check (all conformance levels)
+      description: |
+        DCM polls this endpoint on the configured interval. Operators must respond within
+        the `failure_threshold` timeout or DCM will record a failed health check.
+      security: []
+      servers:
+        - url: https://{operator-host}
+          variables:
+            operator-host:
+              default: operator.namespace.svc
+      responses:
+        "200":
+          description: Provider health status
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/HealthResponse" }
+
+  /:
+    post:
+      tags: [resources]
+      operationId: createResource
+      summary: Create a resource (Level 1+)
+      description: |
+        DCM dispatches a Requested State payload. The operator naturalizes it to the
+        provider-native format and initiates resource creation.
+
+        **Response contract:** Return immediately with `PROVISIONING` status (Level 1 may block
+        to REALIZED, but this is discouraged for resources taking >30s). Report completion via
+        the DCM Callback API or via the next discovery cycle.
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/CreateRequest" }
+      responses:
+        "202":
+          description: Request accepted; resource creation initiated
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/CreateResponse" }
+        "200":
+          description: Resource realized synchronously (Level 1 blocking response)
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/RealizedStatePayload" }
+        "400": { $ref: "#/components/responses/BadRequest" }
+        "403": { $ref: "#/components/responses/Forbidden" }
+        "409":
+          description: Resource already exists with this request_id (idempotency)
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/CreateResponse" }
+        "422":
+          description: Capacity insufficient or validation failure
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/DenialResponse" }
+
+    get:
+      tags: [resources]
+      operationId: listResources
+      summary: List all resources managed by this operator (Level 1+)
+      parameters:
+        - name: page_size
+          in: query
+          schema: { type: integer, default: 100, maximum: 1000 }
+        - name: page_token
+          in: query
+          schema: { type: string }
+        - name: lifecycle_state
+          in: query
+          schema: { type: string }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ResourceList" }
+
+  /{resource_id}:
+    get:
+      tags: [resources]
+      operationId: getResource
+      summary: Get current state of a specific resource (Level 1+)
+      parameters:
+        - { $ref: "#/components/parameters/resource_id" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/RealizedStatePayload" }
+        "404": { $ref: "#/components/responses/NotFound" }
+
+    put:
+      tags: [resources]
+      operationId: updateResource
+      summary: Apply a delta update to a resource (Level 2+)
+      description: |
+        DCM sends the delta (changed fields only). The operator applies the changes and
+        responds immediately with the current lifecycle state. Reports completion via callback.
+      parameters:
+        - { $ref: "#/components/parameters/resource_id" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/UpdateRequest" }
+      responses:
+        "202": { description: Update accepted and initiated }
+        "200":
+          description: Update applied synchronously
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/RealizedStatePayload" }
+        "409": { description: Idempotent response — update already applied }
+
+    delete:
+      tags: [resources]
+      operationId: decommissionResource
+      summary: Decommission and remove a resource (Level 1+)
+      description: |
+        DCM sends a decommission request. The operator initiates removal and responds
+        immediately with `DECOMMISSIONING` status. Reports completion via callback.
+
+        At Level 3+, DCM first sends a `decommission_confirmation` callback to allow
+        lifecycle policies (retain data, notify stakeholders) to run before deletion proceeds.
+      parameters:
+        - { $ref: "#/components/parameters/resource_id" }
+      requestBody:
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/DecommissionRequest" }
+      responses:
+        "200":
+          description: Operation initiated. Poll `operation.name` for completion.
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/Operation" }
+        "409": { description: Resource already decommissioned }
+
+  /discover:
+    post:
+      tags: [discovery]
+      operationId: discoverResources
+      summary: Trigger active discovery of all resources (Level 2+)
+      description: |
+        DCM calls this on the configured schedule to discover the current state of all
+        resources managed by this operator. The operator returns a complete snapshot of
+        all currently realized resources in DCM Unified Data Model format.
+
+        This is the mechanism for drift detection — DCM compares Discovered State
+        (returned here) against Realized State (stored by DCM after last realization).
+      servers:
+        - url: https://{operator-host}/api/v1/{service_type}
+          variables:
+            operator-host: { default: operator.namespace.svc }
+            service_type:  { default: compute.virtualmachine }
+      requestBody:
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/DiscoverRequest" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/DiscoverResponse" }
+        "202":
+          description: Discovery initiated asynchronously; results will be pushed to DCM Callback API
+
+  /capacity:
+    post:
+      tags: [capacity]
+      operationId: reportCapacity
+      summary: Report current capacity for placement engine queries (Level 2+)
+      description: |
+        DCM calls this when evaluating provider placement for a new request.
+        The operator reports current available, reserved, and committed capacity.
+        DCM uses this to select the best provider and to avoid over-committing.
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/CapacityQueryRequest" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/CapacityReport" }
+
+# ─── COMPONENTS ────────────────────────────────────────────────────────────────
+
+components:
+
+  securitySchemes:
+    MutualTLS:
+      type: mutualTLS
+      description: |
+        All DCM → Operator interactions use mTLS. DCM presents its certificate; the
+        operator must validate the certificate chain against the DCM CA registered at
+        provider registration time. Operators must also present a valid certificate
+        to DCM on the reverse check.
+
+  parameters:
+    resource_id:
+      name: resource_id
+      in: path
+      required: true
+      schema: { type: string }
+      description: Operator-assigned resource ID (stable identifier; different from DCM entity UUID)
+
+  responses:
+    BadRequest:
+      description: Malformed request
+      content:
+        application/json:
+          schema: { $ref: "#/components/schemas/OperatorError" }
+    Forbidden:
+      description: mTLS certificate not trusted or insufficient permissions
+      content:
+        application/json:
+          schema: { $ref: "#/components/schemas/OperatorError" }
+    NotFound:
+      description: Resource not found
+      content:
+        application/json:
+          schema: { $ref: "#/components/schemas/OperatorError" }
+
+  schemas:
+
+    HealthResponse:
+      type: object
+      required: [status, version]
+      additionalProperties: false
+      properties:
+        status:
+          type: string
+          enum: [healthy, degraded, unhealthy]
+        version: { type: string }
+        capabilities_available:
+          type: array
+          items: { type: string }
+        conformance_level:
+          type: integer
+          minimum: 1
+          maximum: 4
+        details:
+          type: object
+          additionalProperties: true
+          description: Provider-specific; DCM treats as opaque
+
+    CreateRequest:
+      type: object
+      required: [request_id, dcm_entity_uuid, tenant_uuid, resource_type, spec]
+      additionalProperties: false
+      properties:
+        request_id:
+          type: string
+          format: uuid
+          description: DCM request UUID — idempotency key; operators must deduplicate on this
+        dcm_entity_uuid:
+          type: string
+          format: uuid
+          description: DCM-assigned entity UUID; must be echoed back in all responses and callbacks
+        tenant_uuid:
+          type: string
+          format: uuid
+        resource_type_uuid:
+          type: string
+          format: uuid
+        resource_type_name:
+          type: string
+          description: FQN (e.g., Compute.VirtualMachine)
+        spec:
+          type: object
+          additionalProperties: true
+          description: |
+            DCM Unified Data Model fields for this resource. Field names match the
+            Resource Type Specification schema registered for this resource type.
+            All fields carry provenance metadata where applicable.
+        relationships:
+          type: array
+          items:
+            type: object
+            properties:
+              relationship_type: { type: string }
+              target_entity_uuid: { type: string, format: uuid }
+              target_resource_type: { type: string }
+        callback_url:
+          type: string
+          format: uri
+          description: DCM Callback API URL for reporting async completion
+        override_controls:
+          type: object
+          additionalProperties: true
+          description: Field-level override constraints (Level 3+)
+        scheduled_at:
+          type: string
+          format: date-time
+          description: For scheduled/deferred requests — when to start execution
+
+    CreateResponse:
+      type: object
+      required: [resource_id, dcm_request_id, lifecycle_state]
+      additionalProperties: false
+      properties:
+        resource_id:
+          type: string
+          description: Operator-assigned stable resource ID
+        dcm_request_id:
+          type: string
+          format: uuid
+          description: Echoed from request
+        lifecycle_state:
+          type: string
+          enum: [PROVISIONING, REALIZED, FAILED]
+        estimated_ready_at:
+          type: string
+          format: date-time
+        provider_reference:
+          type: object
+          description: Provider-native reference (e.g., Kubernetes namespace/name for CRs)
+          additionalProperties: true
+
+    UpdateRequest:
+      type: object
+      required: [request_id, delta_fields]
+      additionalProperties: false
+      properties:
+        request_id:
+          type: string
+          format: uuid
+        dcm_entity_uuid:
+          type: string
+          format: uuid
+        delta_fields:
+          type: object
+          additionalProperties: true
+          description: Changed fields only — not a full replacement
+        callback_url:
+          type: string
+          format: uri
+
+    DecommissionRequest:
+      type: object
+      additionalProperties: false
+      properties:
+        request_id:
+          type: string
+          format: uuid
+        dcm_entity_uuid:
+          type: string
+          format: uuid
+        reason:
+          type: string
+        retain_data:
+          type: boolean
+          default: false
+          description: If true, operator should retain underlying data (e.g., PVC retention)
+        callback_url:
+          type: string
+          format: uri
+
+    RealizedStatePayload:
+      type: object
+      required: [resource_id, dcm_entity_uuid, lifecycle_state, realized_at, spec]
+      additionalProperties: false
+      description: |
+        The Realized State of a resource in DCM Unified Data Model format.
+        This is what the operator sends back to DCM after realization, and what
+        DCM stores in the Realized State Store. All provider-native identifiers
+        should be included in `provider_metadata` — the `spec` must be in DCM format.
+      properties:
+        resource_id:
+          type: string
+        dcm_entity_uuid:
+          type: string
+          format: uuid
+        dcm_request_id:
+          type: string
+          format: uuid
+        lifecycle_state:
+          type: string
+          enum: [PROVISIONING, REALIZED, OPERATIONAL, DEGRADED, FAILED, DECOMMISSIONING, DECOMMISSIONED]
+        realized_at:
+          type: string
+          format: date-time
+        spec:
+          type: object
+          additionalProperties: true
+          description: Realized field values in DCM Unified Data Model format
+        provider_metadata:
+          type: object
+          additionalProperties: true
+          description: Provider-native metadata (opaque to DCM; stored for operator use)
+        failure_reason:
+          type: string
+          description: Present when lifecycle_state is FAILED
+
+    DenialResponse:
+      type: object
+      required: [request_id, denial_reason, denial_timestamp]
+      additionalProperties: false
+      properties:
+        request_id:
+          type: string
+          format: uuid
+        denial_reason:
+          type: string
+          enum: [INSUFFICIENT_RESOURCES, VALIDATION_FAILED, POLICY_REJECTED, UNSUPPORTED_CONFIGURATION]
+        denial_timestamp:
+          type: string
+          format: date-time
+        resource_type_uuid:
+          type: string
+          format: uuid
+        estimated_available_at:
+          type: string
+          format: date-time
+        details:
+          type: string
+
+    DiscoverRequest:
+      type: object
+      additionalProperties: false
+      properties:
+        scope:
+          type: string
+          enum: [full, delta]
+          default: full
+        since:
+          type: string
+          format: date-time
+          description: For delta discovery — only return resources changed since this time
+
+    DiscoverResponse:
+      type: object
+      required: [discovery_timestamp, resources]
+      additionalProperties: false
+      properties:
+        discovery_timestamp:
+          type: string
+          format: date-time
+        resources:
+          type: array
+          items: { $ref: "#/components/schemas/RealizedStatePayload" }
+        pagination:
+          type: object
+          properties:
+            page_token:     { type: string }
+            has_more:   { type: boolean }
+
+    CapacityQueryRequest:
+      type: object
+      additionalProperties: false
+      properties:
+        resource_type_name:   { type: string }
+        requested_spec:       { type: object, additionalProperties: true }
+        tenant_uuid:          { type: string, format: uuid }
+
+    CapacityReport:
+      type: object
+      required: [provider_id, report_timestamp, capacity]
+      additionalProperties: false
+      properties:
+        provider_id:
+          type: string
+          format: uuid
+        report_timestamp:
+          type: string
+          format: date-time
+        next_report_at:
+          type: string
+          format: date-time
+        capacity:
+          type: object
+          required: [available_units, reserved_units, committed_units]
+          additionalProperties: false
+          properties:
+            available_units:  { type: integer, minimum: 0 }
+            reserved_units:   { type: integer, minimum: 0 }
+            committed_units:  { type: integer, minimum: 0 }
+            unit_definition:  { type: string, description: "What one 'unit' means (e.g., '1 vCPU + 2GB RAM')" }
+            confidence:       { type: string, enum: [high, medium, low], default: high }
+        can_fulfill:
+          type: boolean
+          description: Whether the requested_spec (if provided) can be fulfilled
+
+    OperatorError:
+      type: object
+      required: [error]
+      additionalProperties: false
+      properties:
+        error:
+          type: object
+          required: [code, message]
+          properties:
+            code:    { type: string }
+            message: { type: string }
+
+    Operation:
+      type: object
+      description: |
+        AEP-136 Long-Running Operation returned by async create/update/decommission responses.
+        DCM polls this until done is true.
+      required: [name, done]
+      additionalProperties: false
+      properties:
+        name:
+          type: string
+          description: Stable operation resource path
+        done:
+          type: boolean
+          default: false
+        metadata:
+          type: object
+          properties:
+            resource_id:    { type: string }
+            resource_type:  { type: string }
+            operation_type: { type: string, enum: [create, update, decommission] }
+        response:
+          type: object
+          description: Present when done=true and successful. Contains RealizedStatePayload.
+        error:
+          $ref: "#/components/schemas/OperatorError"
+          description: Present when done=true and failed.
+
+    ResourceList:
+      type: object
+      description: List of discovered resources returned by the discover endpoint.
+      required: [resources]
+      additionalProperties: false
+      properties:
+        resources:
+          type: array
+          items:
+            $ref: "#/components/schemas/RealizedStatePayload"
+        total_count:
+          type: integer
+          description: Total number of resources discovered
diff --git a/content/docs/architecture/specifications/dcm-operator-interface-spec.md b/content/docs/architecture/specifications/dcm-operator-interface-spec.md
new file mode 100644
index 0000000..23c061d
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-operator-interface-spec.md
@@ -0,0 +1,1142 @@
+# DCM Operator Interface Specification
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Operator Interface Specification
+
+
+
+> ## 📋 Draft — Promoted from Work in Progress
+>
+> All questions resolved. Level 0–4 conformance levels defined. Cluster-scoped resource ownership clarified. CAPI integration specified.
+>
+> **This section is explicitly a work in progress and is less mature than the core DCM data model and architecture documentation.**
+>
+> The Kubernetes operator integration layer — including the Operator Interface Specification, Operator SDK API, and Kubernetes compatibility mappings — represents design intent that has not yet been validated against implementation. Specific interface contracts, API signatures, SDK method names, and CRD structures **will change** as implementation work begins.
+>
+> **Do not build against these specifications yet.** They are published to share design direction and invite feedback, not as stable contracts.
+>
+> Known gaps and open items for this section:
+> - Operator Interface Specification: reconciliation hook signatures are provisional
+> - Operator SDK API: Go module structure and dependency model not yet finalized
+> - Kubernetes Compatibility Mappings: some concept mappings remain under discussion
+> - SDK code examples are illustrative only — not yet tested against a real implementation
+>
+> Feedback and contributions welcome via [GitHub Issues](https://github.com/dcm-project/issues).
+
+
+
+**Version:** 0.1.0-draft  
+**Status:** Draft — Ready for implementation feedback
+**Document Type:** Technical Specification  
+**Maintainers:** Red Hat FlightPath Team  
+**GitHub:** https://github.com/dcm-project  
+**Last Updated:** 2026-03
+
+---
+
+## Abstract
+
+This specification defines the interface by which Kubernetes operators integrate with the DCM (Data Center Management) control plane as first-class Service Providers. An operator that conforms to this specification becomes a DCM Service Provider, enabling its managed resources to participate in DCM's unified lifecycle management, multi-tenancy, policy governance, cost analysis, drift detection, and service catalog.
+
+DCM is designed as a superset of Kubernetes — extending Kubernetes' declarative, controller-based model upward to provide unified management across multiple clusters, infrastructure types, and organizational boundaries. This specification is the technical contract that enables that extension without requiring operators to abandon their existing Kubernetes-native design.
+
+Operators conforming to this specification function as Service Providers within a single DCM instance. In federated deployments (Hub-Spoke or Peer topology), the operator registers with the appropriate Regional or local DCM instance — federation routing is handled by DCM, not by the operator.
+
+---
+
+## 1. Introduction
+
+> **OIS Versioning:** Providers declare the OIS version they implement in capability registration (`ois_version`). DCM maintains dispatch compatibility with all supported OIS versions. See [API Versioning Strategy](../data-model/34-api-versioning-strategy.md) Section 7.
+
+
+### 1.1 Motivation
+
+Kubernetes operators are the most mature pattern for managing complex, stateful resources declaratively on Kubernetes. However, operators operate within a single cluster and lack the cross-cluster lifecycle management, multi-tenancy, cost attribution, sovereignty governance, and policy enforcement that enterprise organizations require at scale.
+
+DCM provides these capabilities at the management plane level — above individual clusters. By conforming to this specification, an operator's managed resources become:
+
+- **Multi-tenant** — DCM Tenant ownership and isolation applied automatically
+- **Cost-attributed** — resource costs tracked and attributed across the full lifecycle
+- **Policy-governed** — organizational policies applied at request time via DCM's Policy Engine
+- **Cross-cluster** — the same resource type managed across multiple clusters through DCM
+- **Self-service** — automatically available in the DCM Service Catalog for consumer request
+- **Sovereignty-compliant** — placement and operational constraints enforced by DCM's GateKeeper policies
+- **Audit-complete** — full provenance chain from intent through realization
+
+### 1.2 Scope
+
+This specification defines:
+- The HTTP API an operator must expose to participate in DCM
+- The data format for all API payloads (DCM Unified Data Model)
+- The registration, health, capacity, status, and lifecycle event contracts
+- The field mapping specification for translating between DCM format and CRD format
+- Conformance levels and what each level unlocks in DCM
+
+This specification does not define:
+- How operators implement their internal reconciliation logic
+- Which specific Kubernetes distributions operators must support
+- The internal architecture of the DCM control plane
+- Provider-specific business logic or domain knowledge
+
+### 1.3 Relationship to the DCM Service Provider Contract
+
+This specification is a Kubernetes-specific instantiation of the DCM Service Provider Contract. All general Service Provider Contract requirements apply. This specification adds Kubernetes-specific requirements and guidance. Where this specification and the general Service Provider Contract conflict, this specification takes precedence for Kubernetes operator implementations.
+
+### 1.4 Terminology
+
+- **Operator** — a Kubernetes controller that manages custom resources via a Custom Resource Definition (CRD)
+- **DCM Control Plane** — the DCM management system that routes requests and manages lifecycle
+- **Adapter** — a component that sits between DCM and an operator, implementing this specification on the operator's behalf (used when the operator cannot be modified directly)
+- **Native implementation** — an operator that implements this specification directly, without an adapter
+- **CR** — Custom Resource — an instance of a CRD managed by the operator
+- **CRD** — Custom Resource Definition — the Kubernetes schema definition for a CR
+- **Reconciliation loop** — the operator's control loop that drives actual state toward desired state
+
+---
+
+## 2. Conformance Levels
+
+This specification defines three conformance levels. Higher levels unlock additional DCM capabilities. An operator may implement any level — DCM accepts operators at all levels, with capabilities gated by the declared conformance level.
+
+**Design principle:** Level 1 must be achievable in a single day of work for an existing operator. Level 3 is the target for operators that want full DCM integration. The SDK (see Section 9) handles all protocol concerns — operator developers only implement business logic.
+
+### 2.1 Level 1 — Basic
+
+**What it requires:**
+- Operator registration with DCM on startup
+- Health check endpoint (`GET /health`)
+- Basic status reporting to DCM when resource state changes
+
+**What it unlocks:**
+- Operator resources appear in the DCM Service Catalog
+- Basic lifecycle state tracking (PROVISIONING, OPERATIONAL, FAILED, DECOMMISSIONED)
+- Health monitoring via DCM Observability
+- Basic cost tracking (resource exists/does not exist)
+
+**Estimated implementation effort:** 1 day using the DCM Operator SDK
+
+### 2.2 Level 2 — Standard
+
+Level 2 conformance is required for providers that support auto-scaling, auto-healing, or provider-side maintenance operations. Level 2 includes all Level 1 requirements plus the Provider Update Notification API (Section 7a).
+
+
+**What it requires:** All Level 1 requirements, plus:
+- Capacity reporting to DCM (scheduled registration)
+- Full lifecycle event reporting (DEGRADED, MAINTENANCE, UNSANCTIONED_CHANGE, etc.)
+- Complete realized state payloads in DCM Unified Data Model format
+- Field mapping declaration (CRD fields mapped to DCM Resource Type fields)
+
+**What it unlocks:** All Level 1 capabilities, plus:
+- Intelligent placement — DCM can route requests based on real capacity data
+- Drift detection — DCM compares discovered state against realized state
+- Full cost attribution — granular resource cost tracking throughout lifecycle
+- Cross-cluster management — DCM can route the same resource type to multiple clusters
+- Dependency graph participation — operator resources participate in DCM entity relationships
+
+**Estimated implementation effort:** 2-3 days using the DCM Operator SDK
+
+### 2.3 Level 3 — Full
+
+**What it requires:** All Level 2 requirements, plus:
+- Sovereignty capability declaration
+- Field-level provenance in realized state payloads
+- Override control metadata support
+- Discovery endpoint (`POST /discover`) — operator can discover existing resources for brownfield ingestion
+- Decommission confirmation callback
+
+**What it unlocks:** All Level 2 capabilities, plus:
+- Sovereignty enforcement — DCM can enforce placement and operational constraints per regulatory requirements
+- Full audit chain — complete provenance from intent through realization
+- Brownfield ingestion — existing resources can be imported into DCM lifecycle management
+- Override control enforcement — policy-set field locks honored in operator requests
+
+**Estimated implementation effort:** 3-5 days using the DCM Operator SDK
+
+---
+
+## 3. Registration API
+
+### 3.1 Overview
+
+Operators register with DCM on startup. Registration informs DCM of the operator's endpoint, the resource types it manages, its capabilities, and its conformance level. Registration is idempotent — re-registering with the same name updates the existing registration rather than creating a duplicate.
+
+### 3.2 Registration Endpoint
+
+**DCM endpoint:** `POST /api/v1/providers`
+
+**Timing:** Called by the operator (or adapter) during startup, after the HTTP server is ready. Retried with exponential backoff on failure. Registration failure does not block operator startup — the operator functions normally for Kubernetes consumers even if DCM registration fails.
+
+### 3.3 Registration Payload
+
+```yaml
+# Registration request payload
+provider_registration:
+  name: 
+  display_name: 
+  conformance_level: <1|2|3>
+  endpoint: 
+  version: 
+
+  service_types:
+    - service_type: 
+      service_type_uuid: 
+      crd_reference:
+        group: 
+        version: 
+        kind: 
+      operations_supported: [CREATE, READ, UPDATE, DELETE, DISCOVER]
+      # DISCOVER only required for Level 3
+      field_mapping_ref: 
+
+  kubernetes:
+    cluster_id: 
+    cluster_endpoint: 
+    namespace_strategy: 
+    # per_tenant: one namespace per DCM Tenant
+    # shared: all DCM resources in one namespace, isolated by labels
+    # per_resource: one namespace per resource instance
+
+  metadata:
+    region: 
+    zone: 
+    cluster_type: 
+    cluster_version: 
+
+  # Level 2+ required
+  capacity:
+    update_mode: 
+    update_frequency_seconds: 
+
+  # Level 3 required
+  sovereignty_capabilities:
+    data_residency_regions: []
+    operational_sovereignty: 
+    hard_tenancy_supported: 
+    air_gapped_capable: 
+    compliance_frameworks: []
+```
+
+### 3.4 Registration Response
+
+```yaml
+# Success response
+provider_registration_response:
+  provider_uuid: 
+  name: 
+  status: 
+  conformance_level_accepted: <1|2|3>
+  capabilities_enabled:
+    - service_catalog
+    - health_monitoring
+    - cost_tracking
+    # Level 2+
+    - placement
+    - drift_detection
+    - cross_cluster_management
+    # Level 3
+    - sovereignty_enforcement
+    - brownfield_ingestion
+    - full_audit_chain
+```
+
+---
+
+## 4. Health Check API
+
+### 4.1 Overview
+
+DCM polls the operator's health endpoint every 10 seconds (configurable). A healthy operator is eligible to receive new resource requests. An unhealthy operator is excluded from placement decisions.
+
+### 4.2 Health Endpoint
+
+**Endpoint:** `GET /health`  
+**Authentication:** Unauthenticated (or internally secured — operator choice)  
+**Expected response:** HTTP 200 OK for healthy or warn status; any non-200 for unhealthy (fail)
+
+The health response body is **normative**. DCM uses the `status` field to determine provider health and trigger alerts. Providers that return a non-conforming or absent body are treated as `warn` until three consecutive failures, after which they are treated as `fail`.
+
+```http
+GET /health HTTP/1.1
+
+HTTP/1.1 200 OK
+Content-Type: application/health+json
+
+{
+  "status": "pass",              // REQUIRED: "pass" | "warn" | "fail"
+  "version": "",         // REQUIRED: provider software version
+  "dcm_registration_status": "registered",  // REQUIRED: "registered" | "unregistered" | "error"
+  "uptime_seconds": 86423,       // RECOMMENDED: seconds since last restart
+  "checks": {                    // RECOMMENDED: per-subsystem health
+    "provider_backend": {
+      "status": "pass",
+      "observed_at": ""
+    },
+    "service_provider_connectivity": {
+      "status": "pass",
+      "observed_at": ""
+    }
+  },
+  "details": {}                  // OPTIONAL: operator-specific additional detail
+}
+```
+
+**Status semantics:**
+
+| Status | HTTP code | Meaning | DCM behavior |
+|--------|-----------|---------|--------------|
+| `pass` | 200 | Fully operational | No action |
+| `warn` | 200 | Operational but degraded | Fires `provider.degraded` event; alert platform admin |
+| `fail` | any non-200 | Not operational | Fires `provider.unhealthy` event; triggers recovery policy |
+
+The health endpoint format follows [RFC 8615 / IANA health+json](https://www.iana.org/assignments/media-types/application/health+json).
+
+**DCM polling behavior:**
+- Polling interval: declared in provider capability registration (`health_check_interval`, default PT30S)
+- Consecutive `fail` threshold before `provider.unhealthy` event: 3 (profile-governed)
+- Recovery: first `pass` after `fail` fires `provider.healthy` event
+
+### 4.3 State Machine
+
+- **Ready** — HTTP 200 received. Operator eligible for new requests.
+- **NotReady** — Non-200 or timeout received 3 consecutive times (configurable threshold). Operator excluded from placement. Existing resources not affected.
+- **Recovery** — Single HTTP 200 transitions NotReady back to Ready immediately.
+
+---
+
+## 5. Capacity Reporting API
+
+*Required for Level 2 conformance.*
+
+### 5.1 Overview
+
+DCM maintains an internal capacity rating per operator, per service type, per location. Operators report capacity on a configurable schedule. DCM uses capacity data for intelligent placement decisions.
+
+### 5.2 Capacity Registration
+
+**DCM endpoint:** `POST /api/v1/providers/{provider_uuid}/capacity`
+
+```yaml
+capacity_report:
+  provider_id: 
+  report_timestamp: 
+  next_report_at: 
+  capacity_by_service_type:
+    - service_type_uuid: 
+      available_units: 
+      reserved_units: 
+      committed_units: 
+      unit_definition: 
+      kubernetes_resources:
+        available_cpu: 
+        available_memory: 
+        available_storage: 
+        node_count: 
+```
+
+### 5.3 Capacity Denial
+
+When DCM dispatches a request the operator cannot fulfill, the operator **must** reject it with `INSUFFICIENT_RESOURCES`. DCM receives the denial and retries with an alternative provider.
+
+```yaml
+# Denial response to a resource creation request
+denial_response:
+  request_id: 
+  denial_reason: INSUFFICIENT_RESOURCES
+  denial_timestamp: 
+  service_type_uuid: 
+  estimated_available_at: 
+  details: 
+```
+
+DCM updates its internal capacity rating for this operator immediately upon receiving a denial.
+
+---
+
+## 6. Resource Lifecycle API
+
+### 6.1 Overview
+
+DCM dispatches resource lifecycle operations to the operator via standard REST endpoints. The operator translates these into Kubernetes CR operations (Naturalization) and reports results back to DCM in DCM Unified Data Model format (Denaturalization).
+
+### 6.2 Standard Endpoints
+
+| Method | Endpoint | Description | Required Level |
+|--------|----------|-------------|---------------|
+| `POST` | `/api/v1/{service_type}` | Create a new resource | Level 1 |
+| `GET` | `/api/v1/{service_type}` | List all resources | Level 1 |
+| `GET` | `/api/v1/{service_type}/{resource_id}` | Get a specific resource | Level 1 |
+| `PUT` | `/api/v1/{service_type}/{resource_id}` | Update a resource | Level 2 |
+| `DELETE` | `/api/v1/{service_type}/{resource_id}` | Delete a resource | Level 1 |
+| `POST` | `/api/v1/{service_type}/discover` | Discover existing resources | Level 3 |
+
+### 6.3 Create Request
+
+DCM sends the Requested State payload to the operator. The operator naturalizes it to a Kubernetes CR and submits it. The operator responds immediately with a PROVISIONING status — not waiting for reconciliation to complete.
+
+```yaml
+# Create request from DCM — Requested State payload in DCM format
+create_request:
+  request_id: 
+  tenant_uuid: 
+  # Both resource_type_uuid and resource_type_name are always present — DCM resolves from consumer input
+  resource_type_uuid: 
+  resource_type_name: Storage.Database
+  spec:
+    
+  relationships:
+    
+  metadata:
+    override_control:
+      
+```
+
+```yaml
+# Create response — immediate acknowledgment
+create_response:
+  resource_id: 
+  dcm_request_id: 
+  lifecycle_state: PROVISIONING
+  kubernetes_reference:
+    namespace: 
+    name: 
+    uid: 
+```
+
+### 6.4 Realized State Payload
+
+When the operator's reconciliation loop completes provisioning, it pushes the realized state to DCM. This is the critical Denaturalization step — translating Kubernetes-native status into DCM Unified Data Model format.
+
+**DCM endpoint:** `PUT /api/v1/instances/{resource_id}/status`
+
+```yaml
+# Realized state payload — DCM Unified Data Model format
+realized_state:
+  resource_id: 
+  dcm_entity_uuid: 
+  lifecycle_state: 
+  realized_timestamp: 
+
+  spec:
+    
+    
+
+  # Level 3 — provenance for each field
+  field_provenance:
+    :
+      source_type: provider
+      source_uuid: 
+      timestamp: 
+
+  kubernetes_reference:
+    namespace: 
+    name: 
+    uid: 
+    resource_version: 
+
+  relationships:
+    
+```
+
+### 6.5 Delete and Decommission
+
+When DCM requests deletion, the operator deletes the CR and confirms decommission via the realized state endpoint with `lifecycle_state: DECOMMISSIONED`.
+
+For **Level 3**, the operator must wait for DCM confirmation before deleting — this allows DCM to apply lifecycle policies (retain, detach) before the operator acts.
+
+```yaml
+# Decommission confirmation callback (Level 3)
+# DCM calls this before the operator deletes
+decommission_confirmation:
+  resource_id: 
+  lifecycle_policies_applied:
+    - entity_uuid: 
+      policy_applied: retain
+      # storage was retained, not deleted with the parent
+    - entity_uuid: 
+      policy_applied: destroy
+  proceed_with_deletion: 
+```
+
+---
+
+
+---
+
+## 7a. Provider Update Notification API
+
+This section defines the Provider Update Notification endpoint — the formal mechanism by which Service Providers report authorized state changes to DCM. This is a **Level 2** conformance requirement for providers that support auto-scaling, auto-healing, or provider-side maintenance operations.
+
+### 7a.1 Overview
+
+The Provider Update Notification API enables providers to report authorized state changes so DCM can update its Realized State with a traceable Requested State record. This is distinct from drift — a provider submitting an update notification is asserting that the change was authorized (by a pre-existing policy or operational agreement). DCM evaluates the assertion and decides whether to accept or reject it.
+
+**Key principle:** Providers never write directly to DCM's Realized State. They submit a notification; DCM processes it through its governance pipeline; DCM writes the Realized State if approved.
+
+### 7a.2 Conformance Requirements
+
+| Conformance Level | Requirement |
+|------------------|-------------|
+| Level 1 — Basic | Not required. Providers at Level 1 report all state changes as lifecycle events; DCM handles them as drift. |
+| Level 2 — Standard | Required for providers that implement auto-scaling, auto-healing, or provider-side maintenance. |
+| Level 3 — Full | Required. All authorized provider-side state changes must use this API. |
+
+### 7a.3 Endpoint
+
+```
+POST /api/v1/provider/entities/{entity_uuid}/update-notification
+Host: {dcm-instance}
+Authorization: mTLS (provider certificate)
+Content-Type: application/json
+```
+
+**Note:** This endpoint is on the DCM API Gateway, not on the provider. Providers call DCM; DCM does not poll providers for updates.
+
+### 7a.4 Request Payload
+
+```json
+{
+  "provider_uuid": "",
+  "notification_uuid": "",
+  "notification_type": "authorized_change | maintenance_change | auto_scale | auto_heal",
+  "changed_fields": {
+    "": {
+      "previous_value": "",
+      "new_value": "",
+      "change_reason": "",
+      "authorizing_policy_ref": ""
+    }
+  },
+  "effective_at": "",
+  "provider_evidence_ref": ""
+}
+```
+
+**`notification_uuid`** is an idempotency key. If DCM receives the same `notification_uuid` twice, it acknowledges the second request without reprocessing.
+
+**`authorizing_policy_ref`** is the UUID of the DCM policy that pre-authorized this type of change. If null, DCM will evaluate whether a policy covers this change. If no policy covers it, the notification is rejected.
+
+### 7a.5 Response Codes
+
+| Response | Meaning |
+|----------|---------|
+| `202 Accepted` | Notification accepted. DCM is processing. Use `notification_status_url` to poll. |
+| `200 OK` (with `status: approved`) | Notification accepted and Realized State updated. |
+| `200 OK` (with `status: pending_approval`) | Notification queued pending consumer approval. Entity in PENDING_REVIEW. |
+| `200 OK` (with `status: rejected`) | Notification rejected. Realized State not updated. Discrepancy is now drift. |
+| `409 Conflict` | A notification for this entity is already being processed. Retry after the `retry_after` interval. |
+| `422 Unprocessable` | Notification payload malformed or entity UUID not found in this provider's scope. |
+
+```json
+{
+  "notification_uuid": "",
+  "status": "approved | pending_approval | rejected",
+  "realized_state_uuid": "",
+  "rejection_reason": "",
+  "retry_after": "",
+  "notification_status_url": "/api/v1/provider/notifications/{notification_uuid}"
+}
+```
+
+### 7a.6 Notification Status Polling
+
+```
+GET /api/v1/provider/notifications/{notification_uuid}
+
+Response:
+{
+  "notification_uuid": "",
+  "status": "processing | approved | pending_approval | rejected",
+  "entity_uuid": "",
+  "realized_state_uuid": "",
+  "consumer_approval_required": true | false,
+  "consumer_notified_at": "",
+  "resolved_at": ""
+}
+```
+
+### 7a.7 Idempotency
+
+Provider Update Notifications are idempotent by `notification_uuid`. If DCM crashes between receiving a notification and writing the Realized State, the provider can safely resend the same notification. DCM will not create duplicate Realized State records.
+
+### 7a.8 Pre-Authorization Declarations
+
+Providers may declare categories of updates they routinely make — enabling organizations to pre-authorize them in policy rather than reviewing each one:
+
+```json
+{
+  "provider_uuid": "",
+  "update_capabilities": [
+    {
+      "notification_type": "auto_scale",
+      "affected_fields": ["cpu_count", "memory_gb"],
+      "max_change_magnitude": "2x",
+      "typical_trigger": "Resource utilization threshold"
+    },
+    {
+      "notification_type": "auto_heal",
+      "affected_fields": ["storage_device_id", "network_interface_id"],
+      "max_change_magnitude": "replacement",
+      "typical_trigger": "Hardware failure"
+    }
+  ]
+}
+```
+
+This declaration is part of provider registration (Section 3.3) and is surfaced in the Service Catalog to help consumers understand what provider-side changes they can expect.
+
+
+
+---
+
+## 7b. Cancellation API
+
+This section defines the cancellation endpoint that Service Providers implement for Level 2+ conformance. Providers that declare `supports_cancellation: true` in their registration must implement this endpoint.
+
+### 7b.1 Cancellation Endpoint
+
+```
+POST /cancel (on the provider, called by DCM)
+Authorization: DCM mTLS certificate
+
+Body:
+{
+  "cancellation_uuid": "",
+  "entity_uuid": "",
+  "requested_state_uuid": "",
+  "reason": "consumer_requested | timeout | policy_triggered",
+  "requested_at": "",
+  "best_effort": true
+}
+```
+
+### 7b.2 Response
+
+| Code | Meaning |
+|------|---------|
+| `200 OK` (status: cancelled) | Cancellation clean; no resources provisioned |
+| `200 OK` (status: partial_rollback) | Cancellation attempted; some resources may remain |
+| `200 OK` (status: too_late) | Provider completed before cancellation arrived; late response forthcoming |
+| `409 Conflict` | Already cancelled or already completed |
+
+```json
+{
+  "cancellation_uuid": "",
+  "status": "cancelled | partial_rollback | too_late",
+  "resources_remaining": [],
+  "late_response_expected": false,
+  "notes": ""
+}
+```
+
+### 7b.3 Late Response After Cancellation
+
+If the provider returns `status: too_late`, it must still send the completed realization response via the standard realized-state callback. DCM's Late Response Pipeline handles this — the provider does not need to do anything different. The `LATE_RESPONSE_RECEIVED` Recovery Policy fires on the DCM side.
+
+### 7b.4 Capability Declaration
+
+```json
+{
+  "cancellation_capabilities": {
+    "supports_cancellation": true,
+    "cancellation_supported_during": ["DISPATCHED", "PROVISIONING"],
+    "partial_rollback_possible": true,
+    "cancellation_response_time_seconds": 30
+  }
+}
+```
+
+
+### 6.4 Interim Status Reporting
+
+For long-running operations (provisioning complex resources, compound service constituents), providers may send interim progress updates to DCM without waiting for terminal status. This gives DCM — and therefore consumers — live visibility into multi-step operations.
+
+**DCM endpoint for interim status:**
+
+```
+POST /api/v1/provider/entities/{entity_uuid}/status
+
+Authorization: Bearer 
+Content-Type: application/json
+
+{
+  "request_id": "",
+  "lifecycle_state": "PROVISIONING",   // current state — not yet terminal
+  "progress": {
+    "step_current": 3,
+    "step_total": 7,
+    "step_label": "Configuring network interfaces",
+    "step_started_at": "",
+    "estimated_completion": ""
+  },
+  "constituent_status": [              // for compound/compound service definition operations
+    { "ref": "vm",      "status": "REALIZED",     "completed_at": "" },
+    { "ref": "ip",      "status": "REALIZED",     "completed_at": "" },
+    { "ref": "dns",     "status": "PROVISIONING", "started_at": "" },
+    { "ref": "storage", "status": "PENDING",      "started_at": null }
+  ],
+  "notes": ""
+}
+
+Response 202 Accepted
+```
+
+DCM uses interim status to:
+1. Update `current_step` and progress fields in the request status response
+2. Publish `request.progress_updated` event (info urgency) to the Message Bus
+3. Deliver live status updates to consumers via SSE stream (see Consumer API Section 4.3)
+
+**Frequency:** Providers should not send interim status more frequently than once per 10 seconds. DCM rate-limits interim status calls per entity_uuid.
+
+**Terminal status** is still reported via the existing create/update response callback — interim status supplements, not replaces it.
+
+## 7. Field Mapping Specification
+
+*Required for Level 2 conformance.*
+
+### 7.1 Overview
+
+The field mapping declaration tells DCM how to translate between DCM Unified Data Model fields and the operator's CRD fields. This mapping enables DCM to:
+- Generate CRs from DCM Requested State payloads (Naturalization)
+- Extract DCM Realized State from CR status (Denaturalization)
+- Understand which DCM fields correspond to which CRD fields for drift detection
+
+### 7.2 Field Mapping Declaration Format
+
+```yaml
+field_mapping:
+  service_type: Storage.Database
+  service_type_uuid: 
+  crd_reference:
+    group: postgresql.cnpg.io
+    version: v1
+    kind: Cluster
+
+  # DCM Requested State → Kubernetes CR (Naturalization)
+  dcm_to_cr:
+    - dcm_path: resources.cpu
+      cr_path: spec.instances[0].resources.requests.cpu
+      transform: 
+      required: true
+
+    - dcm_path: resources.memory
+      cr_path: spec.instances[0].resources.requests.memory
+      transform: gigabytes_to_kubernetes_memory
+      required: true
+
+    - dcm_path: engine
+      cr_path: spec.imageName
+      transform: engine_version_to_image
+      # engine: postgresql, version: 15 → imageName: ghcr.io/cloudnative-pg/postgresql:15
+      required: true
+
+    - dcm_path: metadata.name
+      cr_path: metadata.name
+      required: true
+
+    - dcm_path: tenant_uuid
+      cr_path: metadata.labels.dcm-tenant-id
+      required: true
+
+    - dcm_path: dcm_entity_uuid
+      cr_path: metadata.labels.dcm-entity-id
+      required: true
+      # All DCM-managed CRs must be labeled with their DCM entity UUID
+      # This enables discovery and drift detection
+
+  # Kubernetes CR status → DCM Realized State (Denaturalization)
+  cr_status_to_dcm:
+    - cr_path: status.phase
+      dcm_path: lifecycle_state
+      transform: cr_phase_to_dcm_state
+      # Mapping defined in condition_mappings below
+
+    - cr_path: status.readyInstances
+      dcm_path: realized_data.ready_instances
+      transform: none
+
+    - cr_path: status.instancesStatus[0].ip
+      dcm_path: realized_data.connection.host
+      transform: none
+
+    - cr_path: status.certificates.serverCASecret
+      dcm_path: realized_data.tls.ca_secret_ref
+      transform: none
+
+  # Kubernetes conditions → DCM lifecycle states
+  condition_mappings:
+    - kubernetes_condition: "Ready=True"
+      dcm_lifecycle_state: OPERATIONAL
+
+    - kubernetes_condition: "Ready=False,Progressing=True"
+      dcm_lifecycle_state: PROVISIONING
+
+    - kubernetes_condition: "Ready=False,Progressing=False"
+      dcm_lifecycle_state: FAILED
+
+    - kubernetes_condition: "Degraded=True"
+      dcm_lifecycle_state: DEGRADED
+
+  # Kubernetes events → DCM lifecycle events
+  lifecycle_event_mappings:
+    - kubernetes_event: condition_change
+      condition: "Ready=False"
+      dcm_event: ENTITY_HEALTH_CHANGE
+      severity: WARNING
+
+    - kubernetes_event: condition_change
+      condition: "Degraded=True"
+      dcm_event: DEGRADATION
+      severity: CRITICAL
+
+    - kubernetes_event: spec_change_without_dcm_request
+      dcm_event: UNSANCTIONED_CHANGE
+      severity: WARNING
+      # Detected when CR spec changes without a corresponding DCM request ID
+      # Indicates drift — someone modified the CR directly in Kubernetes
+
+  # Namespace strategy implementation
+  namespace_strategy:
+    type: per_tenant
+    namespace_name_pattern: "dcm-{tenant_uuid_short}"
+    # {tenant_uuid_short} = first 8 chars of tenant UUID
+    labels_required:
+      dcm-managed: "true"
+      dcm-tenant-id: "{tenant_uuid}"
+      dcm-entity-id: "{entity_uuid}"
+```
+
+### 7.3 Mandatory CR Labels
+
+All CRs created by a DCM-conformant operator must carry these labels. These labels enable DCM's discovery and drift detection capabilities:
+
+| Label | Value | Purpose |
+|-------|-------|---------|
+| `dcm-managed` | `"true"` | Identifies this CR as DCM-managed |
+| `dcm-tenant-id` | DCM Tenant UUID | Tenant ownership |
+| `dcm-entity-id` | DCM Entity UUID | Links CR to DCM entity record |
+| `dcm-provider-id` | DCM Provider UUID | Which provider created this |
+| `dcm-request-id` | DCM Request UUID | Which request created this |
+
+Any CR change that does not have a corresponding DCM request ID in its update metadata is flagged as an UNSANCTIONED_CHANGE and reported to DCM.
+
+---
+
+## 8. Lifecycle Event API
+
+*Required for Level 2 conformance.*
+
+### 8.1 Overview
+
+Operators must notify DCM of any event that affects the operational status of a managed resource. DCM acts as the Tenant advocate — it receives events, evaluates them through the Policy Engine, and determines the appropriate response.
+
+### 8.2 Event Endpoint
+
+**DCM endpoint:** `POST /api/v1/instances/{resource_id}/events`
+
+### 8.3 Standard Event Types
+
+| Event Type | Trigger | Severity | Required Level |
+|------------|---------|----------|---------------|
+| `ENTITY_HEALTH_CHANGE` | CR condition changes | INFO/WARNING | Level 2 |
+| `DEGRADATION` | Resource is degraded but operational | WARNING | Level 2 |
+| `MAINTENANCE_SCHEDULED` | Planned maintenance window | INFO | Level 2 |
+| `MAINTENANCE_STARTED` | Maintenance has begun | INFO | Level 2 |
+| `MAINTENANCE_COMPLETED` | Maintenance completed | INFO | Level 2 |
+| `UNSANCTIONED_CHANGE` | CR modified without DCM request | WARNING | Level 2 |
+| `CAPACITY_CHANGE` | Available capacity changed significantly | INFO | Level 2 |
+| `DECOMMISSION_NOTICE` | Operator is shutting down | CRITICAL | Level 2 |
+| `PROVIDER_DEGRADATION` | Operator itself is degraded | CRITICAL | Level 2 |
+
+```yaml
+# Event payload
+lifecycle_event:
+  event_uuid: 
+  event_type: UNSANCTIONED_CHANGE
+  provider_id: 
+  resource_id: 
+  dcm_entity_uuid: 
+  event_timestamp: 
+  severity: WARNING
+  requires_immediate_action: true
+
+  details:
+    changed_fields:
+      - field_path: spec.instances[0].resources.requests.cpu
+        previous_value: "2000m"
+        current_value: "4000m"
+        changed_by: 
+        changed_at: 
+
+  kubernetes_reference:
+    namespace: 
+    name: 
+    resource_version: 
+```
+
+---
+
+## 9. DCM Operator SDK
+
+### 9.1 Overview
+
+The DCM Operator SDK is an open source Go library that handles all DCM protocol concerns for operator developers. Using the SDK, an operator developer only needs to:
+
+1. Import the SDK
+2. Configure field mappings (declarative YAML)
+3. Add SDK hooks at key points in the reconciliation loop
+
+The SDK handles registration, health check endpoint exposure, capacity reporting, status translation, lifecycle event emission, provenance generation, and label management.
+
+### 9.2 SDK Initialization
+
+```go
+import dcmsdk "github.com/dcm-project/operator-sdk"
+
+func main() {
+    // Load field mapping configuration
+    mappings, err := dcmsdk.LoadFieldMappings("dcm-mappings.yaml")
+
+    // Initialize DCM SDK
+    dcm, err := dcmsdk.New(dcmsdk.Config{
+        ProviderName:      "cloudnativepg-provider",
+        DisplayName:       "CloudNativePG Service Provider",
+        ConformanceLevel:  dcmsdk.Level2,
+        DCMEndpoint:       os.Getenv("DCM_ENDPOINT"),
+        OperatorEndpoint:  os.Getenv("OPERATOR_ENDPOINT"),
+        FieldMappings:     mappings,
+        CapacityReporter:  &PostgresCapacityReporter{},
+    })
+
+    // Start HTTP server with DCM endpoints automatically registered
+    dcm.StartServer(":8080")
+
+    // Register with DCM on startup
+    dcm.Register(context.Background())
+
+    // Start operator manager
+    mgr.Start(ctrl.SetupSignalHandler())
+}
+```
+
+### 9.3 Reconciliation Loop Integration
+
+```go
+func (r *ClusterReconciler) Reconcile(
+    ctx context.Context,
+    req ctrl.Request,
+) (ctrl.Result, error) {
+
+    cluster := &cnpgv1.Cluster{}
+    if err := r.Get(ctx, req.NamespacedName, cluster); err != nil {
+        return ctrl.Result{}, client.IgnoreNotFound(err)
+    }
+
+    // Check if this CR is DCM-managed
+    if !r.DCM.IsManagedResource(cluster) {
+        return ctrl.Result{}, nil
+        // Not a DCM resource — normal operator behavior
+    }
+
+    // Detect unsanctioned changes
+    if r.DCM.IsUnsanctionedChange(cluster) {
+        r.DCM.ReportEvent(ctx, cluster, dcmsdk.UnsanctionedChange{
+            ChangedFields: r.DCM.DetectChangedFields(cluster),
+        })
+    }
+
+    // ... existing reconciliation logic ...
+
+    // Report current state to DCM
+    realizedState, err := r.DCM.TranslateStatus(cluster)
+    if err != nil {
+        return ctrl.Result{}, err
+    }
+    r.DCM.ReportStatus(ctx, cluster, realizedState)
+
+    return ctrl.Result{}, nil
+}
+```
+
+### 9.4 SDK Responsibilities
+
+The SDK automatically handles:
+- Self-registration on startup with retry and exponential backoff
+- Health check HTTP endpoint (`GET /health`)
+- Capacity reporting on configurable schedule
+- CR label injection on creation (`dcm-managed`, `dcm-tenant-id`, etc.)
+- Unsanctioned change detection (spec change without DCM request ID)
+- Status translation using field mapping configuration
+- Lifecycle event formatting and delivery to DCM
+- Provenance metadata generation for realized state payloads (Level 3)
+
+---
+
+## 10. Kubernetes-to-DCM Concept Mappings
+
+Understanding how Kubernetes concepts map to DCM concepts is essential for implementing this specification correctly.
+
+| Kubernetes Concept | DCM Concept | Notes |
+|-------------------|-------------|-------|
+| Custom Resource Definition (CRD) | Resource Type Specification | CRD schema maps to DCM Resource Type fields |
+| Custom Resource (CR) | Requested State → Realized State | CR is the naturalized form of the DCM payload |
+| Operator reconciliation loop | Realization + Drift Detection | Reconciliation IS the realization process |
+| CR status subresource | Realized State payload | Status must be denaturalized to DCM format |
+| Kubernetes Namespace | DCM Tenant boundary | One namespace per Tenant (per_tenant strategy) |
+| ownerReference | Entity Relationship | ownerReferences map to `contains`/`contained_by` relationships |
+| Labels/Annotations | DCM Entity metadata | DCM-specific labels declared as mandatory |
+| Finalizers | Lifecycle policy enforcement | Finalizers implement `retain` lifecycle policies |
+| Kubernetes conditions | DCM lifecycle states | Mapped via condition_mappings declaration |
+| Watch events | DCM lifecycle events | Kubernetes watch → DCM event translation |
+| Kubernetes RBAC | DCM IDM/IAM + Policy Engine | Kubernetes RBAC is the runtime enforcement; DCM Policy Engine governs the request |
+| Kubernetes cluster | DCM Resource Type: Platform.KubernetesCluster | The cluster itself is a DCM-managed resource |
+
+---
+
+## 11. Conformance Testing
+
+### 11.1 Overview
+
+The DCM project provides a conformance test suite that validates an operator's implementation against this specification. Operators that pass the conformance test suite at their declared level can claim DCM conformance.
+
+### 11.2 Test Suite Structure
+
+```
+dcm-operator-conformance/
+├── level1/
+│   ├── registration_test.go
+│   ├── health_check_test.go
+│   └── basic_status_test.go
+├── level2/
+│   ├── capacity_test.go
+│   ├── lifecycle_events_test.go
+│   ├── realized_state_test.go
+│   └── field_mapping_test.go
+└── level3/
+    ├── sovereignty_test.go
+    ├── provenance_test.go
+    ├── discovery_test.go
+    └── decommission_confirmation_test.go
+```
+
+### 11.3 Running the Conformance Tests
+
+```bash
+# Run Level 1 conformance tests against a running operator
+dcm-conformance test \
+  --level 1 \
+  --operator-endpoint https://my-operator:8080 \
+  --dcm-endpoint https://dcm-control-plane:8080 \
+  --service-type Storage.Database
+
+# Run all levels
+dcm-conformance test --level 3 --operator-endpoint ...
+```
+
+### 11.4 Conformance Certification
+
+Operators that pass the conformance test suite may:
+- Use the "DCM Compatible — Level N" badge in their documentation
+- Be listed in the DCM Operator Registry
+- Receive inclusion in the DCM default Service Catalog for participating organizations
+
+---
+
+## 12. Security Considerations
+
+### 12.1 Authentication
+
+DCM authenticates outbound requests to operators using the trust model established during registration. Operators must validate that incoming requests originate from the DCM control plane. The specific authentication mechanism is declared in the provider registration:
+
+```yaml
+trust_declaration:
+  auth_method: 
+  auth_config: 
+```
+
+### 12.2 Namespace Isolation
+
+When using the `per_tenant` namespace strategy, operators must enforce that resources in one namespace cannot access resources in another namespace. This is the physical enforcement of DCM's hard tenancy model at the Kubernetes level.
+
+### 12.3 Unsanctioned Change Detection
+
+Operators must monitor for changes to DCM-managed CRs that did not originate from a DCM request. Any such change is an UNSANCTIONED_CHANGE event and must be reported to DCM immediately. DCM's Policy Engine determines the appropriate response (REVERT, UPDATE_DEFINITION, ALERT, etc.).
+
+---
+
+## 13. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should the specification be submitted to CNCF as a sandbox project or proposed as a Kubernetes SIG? | Community adoption strategy | ✅ Resolved |
+| 2 | Should conformance certification be self-certified (test suite passes) or require DCM project review? | Community trust | ✅ Resolved |
+| 3 | How should the specification handle operators that manage cluster-scoped (non-namespaced) resources? | Namespace strategy | ✅ Resolved — Two models: (A) Cluster-as-a-Service: Tenant owns the entire cluster entity including all cluster-scoped resources within it; (B) Shared cluster: cluster-scoped governance resources belong to __platform__ Tenant. Cluster-as-a-Service is the primary model. |
+| 4 | Should the SDK support non-Go operator frameworks (Java Operator SDK, Python kopf)? | Ecosystem breadth | ✅ Resolved |
+| 5 | How does the specification interact with Kubernetes Cluster API — can CAPI clusters be DCM-managed resources? | Scope | ✅ Resolved |
+| 6 | Should there be a Level 0 — a pure label-based passive mode requiring no operator changes? | Adoption friction | ✅ Resolved |
+
+---
+
+## Appendix A — Example Implementation Checklist
+
+### Level 1 Checklist
+- [ ] Operator registers with DCM on startup via `POST /api/v1/providers`
+- [ ] Registration retried with exponential backoff on failure
+- [ ] `GET /health` endpoint returns HTTP 200 when healthy
+- [ ] `GET /health` returns non-200 when operator cannot fulfill requests
+- [ ] Status reported to DCM when resource transitions to OPERATIONAL, FAILED, or DECOMMISSIONED
+- [ ] All DCM-managed CRs labeled with mandatory DCM labels
+- [ ] Create response returns PROVISIONING state immediately
+
+### Level 2 Checklist
+- [ ] All Level 1 items complete
+- [ ] Capacity reported to DCM on configurable schedule
+- [ ] Capacity denial returns `INSUFFICIENT_RESOURCES` with proper payload
+- [ ] Full realized state payload in DCM Unified Data Model format
+- [ ] Field mapping declaration complete and validated
+- [ ] All standard lifecycle event types implemented
+- [ ] Unsanctioned change detection active
+- [ ] CR condition changes translated to DCM lifecycle events
+
+### Level 3 Checklist
+- [ ] All Level 2 items complete
+- [ ] Sovereignty capabilities declared in registration
+- [ ] Field-level provenance included in realized state payloads
+- [ ] `POST /discover` endpoint implemented
+- [ ] Decommission confirmation callback handled
+- [ ] Override control metadata honored in CR creation
+
+---
+
+## Appendix B — Relationship to Other Specifications
+
+- **DCM Data Model** — defines the Unified Data Model format used in all API payloads
+- **DCM Service Provider Contract** — the general provider contract this specification extends
+- **DCM Resource Type Registry** — where DCM Resource Types are registered; operators must reference registry UUIDs
+- **AEP (API Enhancement Proposals)** — the DCM API follows AEP standards for REST API design
+- **OpenAPI 3.1.0** — all API schemas are defined in OpenAPI 3.1.0
+
+---
+
+*This specification is maintained by the DCM Project. For questions, contributions, or conformance certification see [GitHub](https://github.com/dcm-project).*
+
+
+## Resolution Notes
+
+**Q1:** Submit the Operator Interface Specification as a CNCF specification project (not a Sandbox project requiring a working implementation). SIG App Delivery and SIG Cluster Lifecycle engagement happens before submission. See cncf-strategy.md for the full submission strategy.
+
+**Q2:** Self-certified via automated test suite is the conformance gate — this is the low-friction path that enables broad adoption. An optional 'DCM Verified' badge is available via DCM project review for organizations wanting a higher-trust production claim. This mirrors Kubernetes conformance: automated test suite gates access; CNCF certification provides the badge.
+
+**Q3:** Two distinct models apply, and it is important to not conflate them:
+
+**Model A — Cluster as a catalog item (example Service Provider implementation):** A Kubernetes cluster can be offered as a catalog item that any authorized Tenant requests and owns — this is a natural use of DCM's Service Provider model, not a special architectural feature. From DCM's perspective, `Platform.KubernetesCluster` is simply a resource type whose Service Provider happens to provision Kubernetes clusters (e.g., via CAPI). The Tenant owns the resulting cluster entity, including all cluster-scoped resources within it, because the cluster is the resource boundary. This is an example of how DCM's architecture enables complex resources as services — DCM has no special knowledge of Kubernetes; it treats the cluster as any other resource entity.
+
+**Model B — Shared cluster infrastructure (the exception):** When multiple Tenants share a single cluster (the multi-tenant cluster model), cluster-scoped resources that govern the shared infrastructure itself (admission webhook configurations, cluster-level network policies, CRD registrations) cannot be owned by any single Tenant — they belong to the `__platform__` system Tenant. These are resources that, if modified by a Tenant, would affect all other Tenants on the cluster. The distinction: resources *inside* a Tenant-owned cluster are always Tenant-owned; resources that *govern shared cluster infrastructure* belong to `__platform__`.
+
+**The rule:** Cluster-scoped resources are owned by the Tenant that owns the cluster. If no single Tenant owns the cluster (shared infrastructure), cluster-scoped governance resources belong to `__platform__`. Operators managing cluster-scoped resources implement the standard base contract. The catalog item scope (`scope: cluster` vs `scope: namespaced`) determines which ownership model applies and what role is required to request it.
+
+**Q4:** The Operator Interface Specification is a REST/HTTP API specification and is language-agnostic by definition. The Go SDK is the reference implementation. Operators in any language implement the specification directly via HTTP — no language-specific adapter is required. Community SDKs for Java and Python are encouraged as community projects under the DCM umbrella; the DCM project does not maintain them in v1.
+
+**Q5:** CAPI clusters are `Platform.KubernetesCluster` resources in DCM. The CAPI operator registers as a Service Provider for this resource type. Once provisioned, a CAPI cluster can optionally register with DCM as a nested DCM deployment or as a Service Provider for workload resources (the compound service definition pattern). Sovereignty constraints are enforced at the CAPI provider selection level.
+
+**Q6:** Level 0 exists as a label-based passive discovery mode. Organizations apply DCM labels to existing operator-managed resources. DCM discovers and tracks these resources (they appear in inventory, drift detection runs against them) but DCM does not dispatch to or control them. No operator code changes are required for Level 0. This is the brownfield ingestion model applied to operators — the lowest possible adoption friction.
+
diff --git a/content/docs/architecture/specifications/dcm-operator-sdk-api.md b/content/docs/architecture/specifications/dcm-operator-sdk-api.md
new file mode 100644
index 0000000..74bfd06
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-operator-sdk-api.md
@@ -0,0 +1,728 @@
+# DCM Operator SDK — API Design
+
+**Document Status:** ✅ Complete
+**Document Type:** SDK Reference (Go)
+**Related Documents:** [Operator Interface Specification](dcm-operator-interface-spec.md) | [Kubernetes Compatibility](kubernetes-compatibility.md) | [Provider Callback Auth](../data-model/43-provider-callback-auth.md) | [Registration Specification](dcm-registration-spec.md)
+
+> **AEP Alignment:** DCM interaction uses colon-syntax custom methods (`:approve`, `:suspend`).
+> `operation_uuid == request_uuid` — Operations polling uses `GET /api/v1/operations/{uuid}`.
+> `resource_type` accepts both FQN string (`Compute.VirtualMachine`) and Registry UUID;
+> DCM resolves either form internally. See `schemas/openapi/dcm-operator-api.yaml`
+> for the normative operator-facing OpenAPI specification.
+
+
+
+> ## 📋 Draft — Promoted from Work in Progress
+>
+> All questions resolved. Local durable queue, mock test harness, Prometheus metrics, and dynamic field resolution all specified.
+>
+> **This section is explicitly a work in progress and is less mature than the core DCM data model and architecture documentation.**
+>
+> The Kubernetes operator integration layer — including the Operator Interface Specification, Operator SDK API, and Kubernetes compatibility mappings — represents design intent that has not yet been validated against implementation. Specific interface contracts, API signatures, SDK method names, and CRD structures **will change** as implementation work begins.
+>
+> **Do not build against these specifications yet.** They are published to share design direction and invite feedback, not as stable contracts.
+>
+> Known gaps and open items for this section:
+> - Operator Interface Specification: reconciliation hook signatures are provisional
+> - Operator SDK API: Go module structure and dependency model not yet finalized
+> - Kubernetes Compatibility Mappings: some concept mappings remain under discussion
+> - SDK code examples are illustrative only — not yet tested against a real implementation
+>
+> Feedback and contributions welcome via [GitHub Issues](https://github.com/dcm-project/issues).
+
+
+
+**Version:** 0.1.0-draft  
+**Status:** Draft — Ready for implementation feedback
+**Document Type:** Technical Design  
+**Language:** Go  
+**Repository:** https://github.com/dcm-project/operator-sdk  
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [DCM Operator Interface Specification](dcm-operator-interface-spec.md) | [Kubernetes Compatibility](kubernetes-compatibility.md)
+
+---
+
+## 1. Purpose
+
+This document defines the public API of the DCM Operator SDK — the Go library that enables Kubernetes operators to implement the DCM Operator Interface Specification with minimal code changes. The SDK handles all DCM protocol concerns so that operator developers only need to implement business logic — field mappings and reconciliation hooks.
+
+**Design principle:** The SDK must be adoptable in a single day. If implementing Level 1 takes more than a day, the API is too complex.
+
+---
+
+## 2. Package Structure
+
+```
+github.com/dcm-project/operator-sdk/
+├── pkg/
+│   ├── client/          # DCM control plane client
+│   ├── config/          # SDK configuration
+│   ├── mapping/         # Field mapping engine
+│   ├── reconciler/      # Reconciliation loop helpers
+│   ├── registration/    # Provider registration
+│   ├── server/          # HTTP server with DCM endpoints
+│   ├── status/          # Status translation and reporting
+│   ├── events/          # Lifecycle event types and emission
+│   ├── discovery/       # Brownfield discovery helpers (Level 3)
+│   └── provenance/      # Provenance metadata generation (Level 3)
+├── api/
+│   └── v1/              # DCM API type definitions
+└── examples/
+    ├── level1/          # Minimal Level 1 implementation example
+    ├── level2/          # Full Level 2 implementation example
+    └── level3/          # Complete Level 3 implementation example
+```
+
+---
+
+## 3. Core Types
+
+### 3.1 Config
+
+```go
+// Config is the primary SDK configuration structure.
+// All fields have sensible defaults — only DCMEndpoint,
+// OperatorEndpoint, and ProviderName are required.
+type Config struct {
+    // Required
+    ProviderName     string
+    DCMEndpoint      string
+    OperatorEndpoint string
+
+    // Required — at least one ServiceType must be declared
+    ServiceTypes []ServiceTypeConfig
+
+    // Optional — defaults to Level1 if not specified
+    ConformanceLevel ConformanceLevel
+
+    // Optional — defaults to "unknown" if not specified
+    DisplayName string
+    Version     string
+
+    // Optional — field mappings loaded from file if not inline
+    FieldMappings []FieldMapping
+    FieldMappingFiles []string
+
+    // Level 2+ — capacity reporter
+    // If nil and ConformanceLevel >= Level2, SDK returns error on init
+    CapacityReporter CapacityReporter
+
+    // Level 3 — sovereignty and provenance
+    SovereigntyCapabilities *SovereigntyCapabilities
+
+    // Optional — HTTP server configuration
+    ServerConfig ServerConfig
+
+    // Optional — registration retry configuration
+    RegistrationConfig RegistrationConfig
+
+    // Optional — health check configuration
+    HealthConfig HealthConfig
+
+    // Optional — logger (defaults to zap logger)
+    Logger logr.Logger
+}
+
+// ConformanceLevel declares the operator's DCM conformance level
+type ConformanceLevel int
+
+const (
+    Level1 ConformanceLevel = 1
+    Level2 ConformanceLevel = 2
+    Level3 ConformanceLevel = 3
+)
+
+// ServiceTypeConfig declares a DCM Resource Type this operator implements
+type ServiceTypeConfig struct {
+    // DCM Resource Type name — e.g., "Storage.Database"
+    ServiceTypeName string
+    // DCM Resource Type UUID from the registry
+    ServiceTypeUUID string
+    // Kubernetes CRD this service type maps to
+    CRDReference CRDReference
+    // Operations this operator supports for this type
+    OperationsSupported []Operation
+}
+
+type CRDReference struct {
+    Group   string
+    Version string
+    Kind    string
+}
+
+type Operation string
+
+const (
+    OperationCreate   Operation = "CREATE"
+    OperationRead     Operation = "READ"
+    OperationUpdate   Operation = "UPDATE"
+    OperationDelete   Operation = "DELETE"
+    OperationDiscover Operation = "DISCOVER" // Level 3 only
+)
+```
+
+### 3.2 Client — DCM Control Plane Interface
+
+```go
+// Client is the interface for communicating with the DCM control plane.
+// The SDK creates and manages this internally — operator developers
+// use it only through the higher-level SDK methods.
+type Client interface {
+    // Register sends the provider registration to DCM.
+    // Returns the DCM-assigned provider UUID on success.
+    Register(ctx context.Context, reg ProviderRegistration) (string, error)
+
+    // ReportStatus sends a realized state payload to DCM.
+    ReportStatus(ctx context.Context, resourceID string, status RealizedState) error
+
+    // ReportEvent sends a lifecycle event to DCM.
+    ReportEvent(ctx context.Context, resourceID string, event LifecycleEvent) error
+
+    // ReportCapacity sends a capacity update to DCM.
+    // Required for Level 2+.
+    ReportCapacity(ctx context.Context, capacity CapacityReport) error
+
+    // ConfirmDecommission acknowledges a decommission request from DCM.
+    // Required for Level 3.
+    ConfirmDecommission(ctx context.Context, resourceID string, confirmation DecommissionConfirmation) error
+}
+```
+
+### 3.3 SDK — Primary Interface
+
+```go
+// SDK is the primary interface for the DCM Operator SDK.
+// Operator developers interact with DCM through this interface.
+type SDK interface {
+    // --- Lifecycle ---
+
+    // Register sends the provider registration to DCM.
+    // Called during operator startup. Retries with exponential backoff.
+    // Does not block — runs in background goroutine.
+    Register(ctx context.Context)
+
+    // Shutdown gracefully deregisters the operator from DCM and
+    // stops background goroutines.
+    Shutdown(ctx context.Context) error
+
+    // --- HTTP Server ---
+
+    // StartServer starts the HTTP server with all DCM-required endpoints.
+    // Blocks until context is cancelled.
+    StartServer(ctx context.Context, addr string) error
+
+    // Handler returns an http.Handler for use with an existing HTTP server.
+    // Alternative to StartServer when the operator already has an HTTP server.
+    Handler() http.Handler
+
+    // --- Reconciliation Helpers ---
+
+    // IsManagedResource returns true if the Kubernetes object
+    // carries DCM management labels.
+    IsManagedResource(obj client.Object) bool
+
+    // IsUnsanctionedChange returns true if the object's spec has changed
+    // without a corresponding DCM request annotation.
+    // Used in reconciliation loops to detect drift.
+    IsUnsanctionedChange(obj client.Object) bool
+
+    // DetectChangedFields returns the list of fields that changed
+    // relative to the last known DCM request state.
+    DetectChangedFields(obj client.Object) []FieldChange
+
+    // InjectLabels adds DCM-required labels to a Kubernetes object
+    // before creation. Called before submitting a CR to Kubernetes.
+    InjectLabels(obj client.Object, req CreateRequest) client.Object
+
+    // AnnotateRequest adds the DCM request ID annotation to a
+    // Kubernetes object. Used to mark changes as DCM-sanctioned.
+    AnnotateRequest(obj client.Object, requestID string) client.Object
+
+    // --- Status Translation ---
+
+    // TranslateStatus translates a Kubernetes object's status
+    // to a DCM RealizedState using the configured field mappings.
+    TranslateStatus(obj client.Object) (RealizedState, error)
+
+    // ReportStatus translates and reports status to DCM in one call.
+    // Convenience wrapper for TranslateStatus + Client.ReportStatus.
+    ReportStatus(ctx context.Context, obj client.Object) error
+
+    // --- Event Emission ---
+
+    // ReportEvent sends a lifecycle event to DCM.
+    ReportEvent(ctx context.Context, obj client.Object, event LifecycleEventType, details EventDetails) error
+
+    // ReportUnsanctionedChange is a convenience method for reporting
+    // an unsanctioned change event with the detected changed fields.
+    ReportUnsanctionedChange(ctx context.Context, obj client.Object, changes []FieldChange) error
+
+    // ReportDegradation reports a DEGRADATION event to DCM.
+    ReportDegradation(ctx context.Context, obj client.Object, reason string) error
+
+    // ReportHealthChange reports an ENTITY_HEALTH_CHANGE event.
+    ReportHealthChange(ctx context.Context, obj client.Object, healthy bool, reason string) error
+
+    // --- Capacity ---
+
+    // StartCapacityReporting starts the background capacity reporting
+    // goroutine. Required for Level 2+. Called automatically by StartServer.
+    StartCapacityReporting(ctx context.Context)
+
+    // --- Discovery (Level 3) ---
+
+    // BuildDiscoveryResponse queries Kubernetes for existing resources
+    // and returns them in DCM Realized State format.
+    // Used to implement the POST /discover endpoint.
+    BuildDiscoveryResponse(ctx context.Context, k8sClient client.Client, opts DiscoveryOptions) ([]RealizedState, error)
+}
+```
+
+---
+
+## 4. Field Mapping API
+
+```go
+// FieldMapping declares how a DCM Resource Type maps to a Kubernetes CRD.
+// Can be loaded from a YAML file or declared inline in Go.
+type FieldMapping struct {
+    ServiceTypeName string
+    ServiceTypeUUID string
+    CRDReference    CRDReference
+
+    // DCM Requested State → Kubernetes CR spec (Naturalization)
+    DCMToCR []FieldMap
+
+    // Kubernetes CR status → DCM Realized State (Denaturalization)
+    CRStatusToDCM []FieldMap
+
+    // Kubernetes conditions → DCM lifecycle states
+    ConditionMappings []ConditionMapping
+
+    // Kubernetes events → DCM lifecycle event types
+    LifecycleEventMappings []LifecycleEventMapping
+
+    // Namespace strategy for this resource type
+    NamespaceStrategy NamespaceStrategy
+}
+
+// FieldMap declares a single field translation
+type FieldMap struct {
+    // Source field path — dot-notation, supports array indexing
+    // e.g., "resources.cpu" or "nodes.controlPlane[0].cpu"
+    SourcePath string
+
+    // Destination field path
+    DestPath string
+
+    // Transform function name — registered in the transform registry
+    // "none" for direct copy, or a named transform
+    Transform string
+
+    // Required — if true and source field is absent, returns error
+    Required bool
+
+    // Default — used when source field is absent and Required is false
+    Default interface{}
+}
+
+// ConditionMapping maps a Kubernetes condition to a DCM lifecycle state
+type ConditionMapping struct {
+    // Kubernetes condition expression — e.g., "Ready=True"
+    // Supports AND: "Ready=False,Progressing=True"
+    KubernetesCondition string
+
+    // DCM lifecycle state
+    DCMLifecycleState LifecycleState
+}
+
+// LifecycleEventMapping maps a Kubernetes event to a DCM event type
+type LifecycleEventMapping struct {
+    // "condition_change" | "spec_change_without_dcm_request" | "deletion"
+    KubernetesEvent string
+
+    // Condition that triggers this mapping (for condition_change events)
+    Condition string
+
+    // DCM event type
+    DCMEventType LifecycleEventType
+
+    // Severity
+    Severity EventSeverity
+}
+
+// Transform registry — operator developers register custom transforms
+type TransformRegistry interface {
+    // Register adds a named transform function
+    Register(name string, fn TransformFunc) error
+
+    // Get retrieves a transform function by name
+    Get(name string) (TransformFunc, error)
+}
+
+// TransformFunc transforms a value from source to destination format
+type TransformFunc func(value interface{}) (interface{}, error)
+```
+
+---
+
+## 5. Status and State Types
+
+```go
+// LifecycleState represents the DCM lifecycle state of a resource
+type LifecycleState string
+
+const (
+    LifecycleStateProvisioning   LifecycleState = "PROVISIONING"
+    LifecycleStateOperational    LifecycleState = "OPERATIONAL"
+    LifecycleStateDegraded       LifecycleState = "DEGRADED"
+    LifecycleStateSuspended      LifecycleState = "SUSPENDED"
+    LifecycleStateFailed         LifecycleState = "FAILED"
+    LifecycleStateDecommissioned LifecycleState = "DECOMMISSIONED"
+)
+
+// RealizedState is the DCM Unified Data Model representation of
+// a resource's realized state. This is what the operator sends
+// to DCM after successful provisioning or status change.
+type RealizedState struct {
+    // DCM resource ID (returned by DCM in the create request)
+    ResourceID string
+
+    // DCM entity UUID
+    DCMEntityUUID string
+
+    // Current lifecycle state
+    LifecycleState LifecycleState
+
+    // Timestamp of this realization
+    RealizedTimestamp time.Time
+
+    // All realized fields in DCM Unified Data Model format
+    Spec map[string]interface{}
+
+    // Level 3 — field-level provenance
+    FieldProvenance map[string]FieldProvenance
+
+    // Kubernetes reference for correlation
+    KubernetesReference KubernetesReference
+
+    // Relationships created during realization
+    Relationships []RelationshipRecord
+}
+
+// KubernetesReference carries Kubernetes-specific identity for correlation
+type KubernetesReference struct {
+    Namespace       string
+    Name            string
+    UID             types.UID
+    ResourceVersion string
+    Generation      int64
+}
+
+// FieldProvenance carries lineage for a single field (Level 3)
+type FieldProvenance struct {
+    SourceType    string    // "provider"
+    SourceUUID    string    // operator provider UUID
+    Timestamp     time.Time
+    Reason        string
+}
+```
+
+---
+
+## 6. Event Types
+
+```go
+// LifecycleEventType represents a DCM lifecycle event type
+type LifecycleEventType string
+
+const (
+    EventEntityHealthChange     LifecycleEventType = "ENTITY_HEALTH_CHANGE"
+    EventDegradation            LifecycleEventType = "DEGRADATION"
+    EventMaintenanceScheduled   LifecycleEventType = "MAINTENANCE_SCHEDULED"
+    EventMaintenanceStarted     LifecycleEventType = "MAINTENANCE_STARTED"
+    EventMaintenanceCompleted   LifecycleEventType = "MAINTENANCE_COMPLETED"
+    EventUnsanctionedChange     LifecycleEventType = "UNSANCTIONED_CHANGE"
+    EventCapacityChange         LifecycleEventType = "CAPACITY_CHANGE"
+    EventDecommissionNotice     LifecycleEventType = "DECOMMISSION_NOTICE"
+    EventProviderDegradation    LifecycleEventType = "PROVIDER_DEGRADATION"
+)
+
+// EventSeverity represents the severity of a lifecycle event
+type EventSeverity string
+
+const (
+    SeverityInfo     EventSeverity = "INFO"
+    SeverityWarning  EventSeverity = "WARNING"
+    SeverityCritical EventSeverity = "CRITICAL"
+)
+
+// LifecycleEvent is the payload sent to DCM for a lifecycle event
+type LifecycleEvent struct {
+    EventUUID             string
+    EventType             LifecycleEventType
+    ProviderID            string
+    ResourceID            string
+    DCMEntityUUID         string
+    EventTimestamp        time.Time
+    Severity              EventSeverity
+    RequiresImmediateAction bool
+    Details               EventDetails
+    KubernetesReference   KubernetesReference
+}
+
+// EventDetails carries event-specific detail data
+type EventDetails struct {
+    // For UNSANCTIONED_CHANGE events
+    ChangedFields []FieldChange
+
+    // For DEGRADATION events
+    DegradationReason string
+    AffectedComponents []string
+
+    // For MAINTENANCE events
+    MaintenanceWindow  *MaintenanceWindow
+    MaintenanceReason  string
+
+    // For CAPACITY_CHANGE events
+    PreviousCapacity   *CapacityReport
+    CurrentCapacity    *CapacityReport
+
+    // Human-readable message for any event type
+    Message string
+}
+
+// FieldChange describes a single field change in an unsanctioned change event
+type FieldChange struct {
+    FieldPath     string
+    PreviousValue interface{}
+    CurrentValue  interface{}
+    ChangedBy     string    // Kubernetes user or service account
+    ChangedAt     time.Time
+}
+```
+
+---
+
+## 7. Capacity Types
+
+```go
+// CapacityReporter is the interface operator developers implement
+// to report capacity data to DCM. The SDK calls this on schedule.
+type CapacityReporter interface {
+    // GetCapacity returns the current capacity for all service types.
+    // Called by the SDK on the configured reporting schedule.
+    GetCapacity(ctx context.Context) (CapacityReport, error)
+}
+
+// CapacityReport contains capacity data for all service types
+type CapacityReport struct {
+    ProviderID          string
+    ReportTimestamp     time.Time
+    NextReportAt        time.Time
+    CapacityByServiceType []ServiceTypeCapacity
+}
+
+// ServiceTypeCapacity contains capacity for a single service type
+type ServiceTypeCapacity struct {
+    ServiceTypeUUID  string
+    AvailableUnits   int
+    ReservedUnits    int
+    CommittedUnits   int
+    UnitDefinition   string
+    KubernetesResources KubernetesResourceCapacity
+}
+
+// KubernetesResourceCapacity contains raw Kubernetes resource availability
+type KubernetesResourceCapacity struct {
+    AvailableCPUMillicores int64
+    AvailableMemoryBytes   int64
+    AvailableStorageBytes  int64
+    NodeCount              int
+}
+```
+
+---
+
+## 8. Constructor and Initialization
+
+```go
+// New creates and initializes a new DCM SDK instance.
+// Returns an error if the configuration is invalid or
+// if required components for the declared conformance level
+// are missing.
+func New(config Config) (SDK, error)
+
+// NewWithClient creates a new SDK instance with a pre-configured
+// DCM client. Used primarily for testing.
+func NewWithClient(config Config, client Client) (SDK, error)
+
+// LoadFieldMappings loads field mapping declarations from YAML files.
+// Accepts one or more file paths or glob patterns.
+func LoadFieldMappings(paths ...string) ([]FieldMapping, error)
+
+// MustNew creates a new SDK instance and panics if initialization fails.
+// Convenience function for use in main() where error handling via
+// panic is acceptable.
+func MustNew(config Config) SDK
+```
+
+---
+
+## 9. Minimal Level 1 Example
+
+```go
+package main
+
+import (
+    "context"
+    "os"
+
+    dcmsdk "github.com/dcm-project/operator-sdk"
+    ctrl "sigs.k8s.io/controller-runtime"
+)
+
+func main() {
+    // Minimal Level 1 configuration
+    dcm, err := dcmsdk.New(dcmsdk.Config{
+        ProviderName:     "my-operator",
+        DisplayName:      "My Operator DCM Provider",
+        DCMEndpoint:      os.Getenv("DCM_ENDPOINT"),
+        OperatorEndpoint: os.Getenv("OPERATOR_ENDPOINT"),
+        ConformanceLevel: dcmsdk.Level1,
+        ServiceTypes: []dcmsdk.ServiceTypeConfig{
+            {
+                ServiceTypeName:     "Storage.Database",
+                ServiceTypeUUID:     "dcm-registry-uuid-for-storage-database",
+                CRDReference: dcmsdk.CRDReference{
+                    Group:   "postgresql.cnpg.io",
+                    Version: "v1",
+                    Kind:    "Cluster",
+                },
+                OperationsSupported: []dcmsdk.Operation{
+                    dcmsdk.OperationCreate,
+                    dcmsdk.OperationRead,
+                    dcmsdk.OperationDelete,
+                },
+            },
+        },
+        FieldMappingFiles: []string{"dcm-mappings.yaml"},
+    })
+    if err != nil {
+        panic(err)
+    }
+
+    ctx := ctrl.SetupSignalHandler()
+
+    // Register with DCM in background — does not block startup
+    dcm.Register(ctx)
+
+    // Start HTTP server with health + DCM endpoints
+    go dcm.StartServer(ctx, ":8080")
+
+    // Start operator manager (existing code unchanged)
+    mgr, _ := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{})
+    mgr.Start(ctx)
+}
+
+// In reconciliation loop — minimal Level 1 additions
+func (r *ClusterReconciler) Reconcile(
+    ctx context.Context,
+    req ctrl.Request,
+) (ctrl.Result, error) {
+
+    cluster := &cnpgv1.Cluster{}
+    if err := r.Get(ctx, req.NamespacedName, cluster); err != nil {
+        return ctrl.Result{}, client.IgnoreNotFound(err)
+    }
+
+    // Only process DCM-managed resources
+    if !r.DCM.IsManagedResource(cluster) {
+        return ctrl.Result{}, nil
+    }
+
+    // Existing reconciliation logic here...
+
+    // Report status to DCM (SDK handles translation via field mappings)
+    r.DCM.ReportStatus(ctx, cluster)
+
+    return ctrl.Result{}, nil
+}
+```
+
+---
+
+## 11. Callback Credential Management
+
+The SDK manages the provider callback credential lifecycle automatically.
+
+### 11.1 Credential Storage
+
+```go
+// CallbackCredential is managed internally by the SDK.
+// Operators do not need to handle credential rotation manually.
+type CallbackCredential struct {
+    Value      string    // Bearer token — never logged
+    ValidUntil time.Time // Pre-rotation begins at 50% of lifetime
+    UUID       string    // For audit correlation
+}
+```
+
+### 11.2 Automatic Rotation
+
+The SDK initiates credential rotation before expiry (at 50% of the credential lifetime). During the transition window, the SDK accepts both the old and new credentials simultaneously. Operators do not need to handle rotation — the SDK does it transparently.
+
+```go
+// The SDK emits a credential rotation event when rotation completes.
+// Operators can subscribe to be notified (e.g., to update secret stores).
+sdk.OnCredentialRotated(func(old, new CallbackCredential) {
+    // Optional: persist new credential to external secret store
+    log.Info("credential rotated", "new_uuid", new.UUID)
+})
+```
+
+### 11.3 entity_uuid and resource_id
+
+The SDK ensures `dcm_entity_uuid` is echoed in every response and callback. `resource_id` is the operator's own stable identifier. DCM uses `dcm_entity_uuid` for all internal routing — `resource_id` is stored by DCM as a correlation handle but never used for routing or identity.
+
+```go
+// The SDK automatically populates dcm_entity_uuid from the CreateRequest.
+// Operators set resource_id to their own stable identifier.
+resp := &CreateResponse{
+    ResourceID:   myInternalID,           // operator-assigned
+    DCMEntityUUID: req.DCMEntityUUID,     // echoed from CreateRequest — SDK validates this
+    LifecycleState: StatePROVISIONING,
+}
+```
+
+---
+
+## 10. Open Questions
+
+> All questions resolved. See Resolution Notes below.
+
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | Should the SDK support non-Go operator frameworks via a language-agnostic REST adapter? | Ecosystem breadth | ✅ Resolved |
+| 2 | How should the SDK handle DCM endpoint unavailability — queue events locally or drop? | Reliability | ✅ Resolved |
+| 3 | Should field mappings support dynamic resolution — a transform that queries external data? | Flexibility | ✅ Resolved |
+| 4 | Should the SDK provide a testing framework for unit testing operator-DCM integration? | Developer experience | ✅ Resolved |
+| 5 | Should the SDK expose metrics (Prometheus) for DCM registration status, event delivery success, etc.? | Observability | ✅ Resolved |
+
+---
+
+
+
+## Resolution Notes
+
+**Q1:** No language-agnostic REST adapter is needed in the Go SDK — the Operator Interface Specification is itself language-agnostic. Operators in any language implement the specification directly via HTTP. Community SDKs for Java/Python are encouraged as community projects. The Go SDK is the reference implementation only.
+
+**Q2:** Queue locally, always. The SDK maintains a local durable queue (SQLite — simple, no external dependencies) with configurable capacity and TTL. On DCM reconnection, queued events are replayed in order. If the local queue reaches capacity (DCM unavailable for an extended period), the SDK enters DEGRADED mode: new events are still accepted up to the hard capacity limit, then dropped with a QUEUE_OVERFLOW audit record and an alert via the operator's configured alerting channel. Dropping events silently is never acceptable — the system is designed to be the authoritative source of truth.
+
+**Q3:** Dynamic field resolution is implemented as an Information Provider reference in the field mapping declaration. The SDK declares 'this field resolves from Information Provider X with lookup key Y'. DCM resolves the value during layer assembly via the standard Information Provider query. This keeps transformation logic in DCM's Policy Engine where it belongs and is auditable via standard field provenance.
+
+**Q4:** A mock DCM test harness ships as a first-class component of the SDK. The harness implements the registration, dispatch, cancel, and discover endpoints with configurable behaviors: inject failures, inject delays, return specific payloads, simulate timeout scenarios. Operators use the test harness for unit and integration testing without a live DCM deployment. This is essential for adoption — operators must be able to test DCM integration in CI without a full environment.
+
+**Q5:** Prometheus metrics are mandatory, not optional. The SDK exposes: registration_status (gauge), event_delivery_total (counter, labels: status=success|failure), event_delivery_duration_seconds (histogram), local_queue_depth (gauge, only when local queuing active), dispatch_duration_seconds (histogram), discovery_cycle_duration_seconds (histogram). Metrics endpoint follows the standard DCM observability model and is required for Level 2 conformance.
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/specifications/dcm-provider-callback-api.yaml b/content/docs/architecture/specifications/dcm-provider-callback-api.yaml
new file mode 100644
index 0000000..237b906
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-provider-callback-api.yaml
@@ -0,0 +1,868 @@
+openapi: "3.1.0"
+
+info:
+  title: DCM Provider Callback API
+  version: "1.0.0"
+  description: |
+    The DCM Provider Callback API defines the endpoints that the DCM control plane exposes
+    for Service Provider operators to call. Operators use these endpoints to:
+
+    - Register with DCM and declare their capabilities
+    - Report capacity for placement engine decisions
+    - Push realized state after resource creation or update (the Denaturalization step)
+    - Report interim progress on long-running operations
+    - Notify DCM of authorized provider-side state changes
+    - Report lifecycle events (health changes, unsanctioned changes, maintenance windows)
+
+    **Authentication — Two-Layer Model** (see `43-provider-callback-auth.md` for full specification):
+
+    - **Layer 1 — mTLS:** Every connection requires the provider's registered certificate.
+      DCM validates the certificate chain against the CA registered at provider activation.
+      Proves transport-level identity.
+
+    - **Layer 2 — Provider Callback Credential:** Every call requires an `Authorization: Bearer`
+      header containing the provider's active callback credential. This credential is a
+      `dcm_interaction` type credential issued by DCM's Credential Provider at activation time.
+      It is scoped to the specific `provider_uuid` and a set of allowed operations. It is
+      short-lived (profile-governed: PT15M for fsi/sovereign, PT1H for standard) and must be
+      rotated before expiry. DCM initiates rotation automatically.
+
+    - **Entity-level authorization** is checked per call independent of credential validity:
+      DCM verifies that the calling provider is the provider that was dispatched to for the
+      specific entity. A valid credential does not grant access to entities hosted at other
+      providers or entities the provider was not dispatched to.
+
+    - **Registration calls** use a registration token (single-use, admin-issued) rather than
+      the callback credential — no callback credential exists until activation completes.
+
+    **Idempotency:** All mutating operations are idempotent. Use the same `request_id` or
+    `notification_uuid` to safely retry without side effects.
+
+    **Direction:** This is the reverse of the Operator Interface Services API. The Operator
+    Interface defines endpoints operators implement (DCM calls them). This document defines
+    endpoints DCM implements (operators call them).
+
+    **Conformance level requirements** are noted per endpoint. Level 1 operators only need
+    registration and realized state push. Level 2+ add capacity, events, and update notifications.
+
+
+
+    **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions:
+    custom methods use colon syntax (`POST /resources/{name}:suspend`),
+    async operations return an `Operation` resource (AEP-136 LRO),
+    and list pagination uses `page_size`/`page_token` parameters.
+
+  contact:
+    name: DCM Project
+    url: https://github.com/dcm-project
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0
+
+servers:
+  - url: https://{dcm-host}/
+    description: DCM Control Plane
+    variables:
+      dcm-host:
+        description: Hostname of the DCM control plane
+        default: dcm.example.com
+
+security:
+  - ProviderCredential: []
+
+tags:
+  - name: registration
+    description: Provider registration and capacity reporting (Level 1+)
+  - name: realized-state
+    description: Realized state push — the Denaturalization step (Level 1+)
+  - name: interim-status
+    description: Interim progress updates for long-running operations (Level 1+, optional)
+  - name: update-notifications
+    description: Provider-initiated authorized state change notifications (Level 2+)
+  - name: lifecycle-events
+    description: Resource lifecycle and health event reporting (Level 2+)
+
+paths:
+
+  # ─── REGISTRATION ─────────────────────────────────────────────────────────
+
+  /api/v1/providers:
+    post:
+      tags: [registration]
+      operationId: registerProvider
+      summary: Register or update a provider registration with DCM (Level 1+)
+      description: |
+        Called by the operator on startup to register with DCM. Registration is idempotent —
+        re-registering with the same `name` updates the existing registration rather than
+        creating a duplicate. Safe to call on every operator restart.
+
+        On first registration, DCM creates a new provider record in SUBMITTED status and
+        routes it through the approval pipeline. On re-registration with the same name,
+        DCM updates the version, capabilities, and endpoints without re-triggering approval
+        (unless the sovereignty declaration changes).
+
+        **Timing:** Call after the operator's HTTP server is ready to receive requests.
+        Retry with exponential backoff on failure. Registration failure does not block
+        operator startup — the operator functions normally for Kubernetes consumers.
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/ProviderRegistrationRequest" }
+      responses:
+        "200":
+          description: Registration updated (existing provider)
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderRegistrationResponse" }
+        "201":
+          description: New registration created; pending approval
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderRegistrationResponse" }
+        "400": { $ref: "#/components/responses/BadRequest" }
+        "409":
+          description: Registration conflict — name already registered with different sovereignty declaration
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderError" }
+
+  /api/v1/providers/{provider_id}/capacity:
+    post:
+      tags: [registration]
+      operationId: reportCapacity
+      summary: Push capacity report to DCM for placement engine decisions (Level 2+)
+      description: |
+        Operators push capacity reports on a scheduled interval (declared at registration).
+        DCM uses this data to make placement decisions and to avoid over-committing resources.
+
+        Reports are also accepted on-demand when capacity changes significantly (e.g., after
+        a node is added or removed). Providers should not push more than once per minute
+        unless responding to a significant capacity event.
+      parameters:
+        - { $ref: "#/components/parameters/provider_id" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/CapacityReport" }
+      responses:
+        "202":
+          description: Capacity report accepted
+        "400": { $ref: "#/components/responses/BadRequest" }
+        "403": { $ref: "#/components/responses/Forbidden" }
+        "429":
+          description: Rate limit exceeded — capacity reports accepted max once per 60 seconds
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderError" }
+
+  # ─── REALIZED STATE ───────────────────────────────────────────────────────
+
+  /api/v1/instances/{resource_id}/status:
+    put:
+      tags: [realized-state]
+      operationId: pushRealizedState
+      summary: Push realized or terminal state to DCM — the Denaturalization step (Level 1+)
+      description: |
+        Called by the operator's reconciliation loop when a resource reaches a terminal
+        lifecycle state (OPERATIONAL, FAILED, DEGRADED, DECOMMISSIONED) or when the
+        operator needs to report a provider-native state transition.
+
+        This is the **Denaturalization** step: the operator translates Kubernetes-native
+        CR status into DCM Unified Data Model format and pushes it here. DCM stores the
+        result as a Realized State record in the append-only Realized State Store.
+
+        **Idempotency:** Use the same `dcm_request_id` to safely retry. DCM will not
+        create duplicate Realized State records for the same `dcm_request_id` + terminal state.
+
+        **Required for decommission:** When a resource is fully removed, the operator must
+        push a payload with `lifecycle_state: DECOMMISSIONED`. This closes the entity's
+        lifecycle in DCM and releases any held allocations or relationships.
+
+        **Level 3 — provenance required:** Level 3 conformance requires `field_provenance`
+        to be populated for all realized fields. This enables full audit chain.
+      parameters:
+        - { $ref: "#/components/parameters/resource_id" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/RealizedStatePush" }
+      responses:
+        "200":
+          description: Realized state accepted and stored
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/RealizedStateAck" }
+        "400": { $ref: "#/components/responses/BadRequest" }
+        "403": { $ref: "#/components/responses/Forbidden" }
+        "404":
+          description: resource_id not recognized by DCM — provider may need to re-register
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderError" }
+        "409":
+          description: |
+            Idempotent response — this request_id + terminal state already recorded.
+            The response body contains the existing Realized State record UUID.
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/RealizedStateAck" }
+
+  # ─── INTERIM STATUS ───────────────────────────────────────────────────────
+
+  /api/v1/provider/entities/{entity_uuid}/status:
+    post:
+      tags: [interim-status]
+      operationId: pushInterimStatus
+      summary: Push interim progress update for a long-running operation (Level 1+, optional)
+      description: |
+        Operators may push interim status updates for long-running operations (provisioning
+        complex resources, compound service constituents) to give DCM — and therefore
+        consumers — live visibility into multi-step operations without waiting for terminal state.
+
+        DCM uses interim status to:
+        - Update `current_step` and progress fields in the request status response
+        - Publish `request.progress_updated` event (info urgency) to the Message Bus
+        - Deliver live updates to consumers via SSE stream
+
+        **Rate limit:** Maximum one update per 10 seconds per `entity_uuid`. DCM rate-limits
+        interim status calls and will return 429 if the limit is exceeded.
+
+        **Not a replacement for terminal state:** Interim status supplements the realized
+        state push. Operators must still call `PUT /api/v1/instances/{resource_id}/status`
+        to report terminal state.
+
+        **For compound/Meta Provider operations:** Include `constituent_status` to give
+        visibility into each constituent's progress independently.
+      parameters:
+        - { $ref: "#/components/parameters/entity_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/InterimStatusPush" }
+      responses:
+        "202":
+          description: Interim status accepted; consumers and SSE stream updated
+        "400": { $ref: "#/components/responses/BadRequest" }
+        "403": { $ref: "#/components/responses/Forbidden" }
+        "429":
+          description: Rate limit exceeded — max one interim status per 10 seconds per entity
+          headers:
+            Retry-After:
+              schema: { type: integer }
+              description: Seconds until the rate limit resets
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/ProviderError" }
+
+  # ─── UPDATE NOTIFICATIONS ─────────────────────────────────────────────────
+
+  /api/v1/provider/entities/{entity_uuid}/update-notification:
+    post:
+      tags: [update-notifications]
+      operationId: submitUpdateNotification
+      summary: Notify DCM of an authorized provider-initiated state change (Level 2+)
+      description: |
+        Called when the provider has made an authorized state change to a resource
+        outside of a DCM-initiated request (e.g., auto-scaling, auto-healing, maintenance).
+
+        **Key principle:** Providers never write directly to DCM's Realized State. They
+        submit a notification here; DCM processes it through the governance pipeline
+        (Policy Engine evaluation); DCM writes the Realized State only if approved.
+
+        **Distinct from drift:** A provider submitting an update notification is asserting
+        that the change was authorized by a pre-existing policy or operational agreement.
+        Drift is detected through discovery and represents an unauthorized or untracked change.
+
+        **Pre-authorization declarations:** Providers may declare categories of updates
+        they routinely make at registration time, enabling organizations to pre-authorize
+        them in policy rather than reviewing each one. Pre-authorized notification types
+        receive `AUTO_APPROVED` status immediately.
+
+        **Idempotency:** Use the same `notification_uuid` to safely retry. DCM will not
+        create duplicate Realized State records for the same `notification_uuid`.
+      parameters:
+        - { $ref: "#/components/parameters/entity_uuid" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/UpdateNotificationRequest" }
+      responses:
+        "202":
+          description: Notification accepted; processing begun
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/UpdateNotificationAccepted" }
+        "400": { $ref: "#/components/responses/BadRequest" }
+        "403": { $ref: "#/components/responses/Forbidden" }
+        "409":
+          description: Idempotent — notification_uuid already processed
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/UpdateNotificationAccepted" }
+
+  /api/v1/provider/notifications/{notification_uuid}:
+    get:
+      tags: [update-notifications]
+      operationId: getNotificationStatus
+      summary: Poll the processing status of a submitted update notification (Level 2+)
+      description: |
+        Allows providers to check whether a submitted update notification has been
+        approved, rejected, or is still awaiting consumer approval.
+
+        Providers should poll with backoff rather than polling at high frequency.
+        Alternatively, subscribe to the `provider_update.*` event domain via webhook
+        for push notification of decision outcomes.
+      parameters:
+        - { $ref: "#/components/parameters/notification_uuid" }
+      responses:
+        "200":
+          content:
+            application/json:
+              schema: { $ref: "#/components/schemas/UpdateNotificationStatus" }
+        "404": { $ref: "#/components/responses/NotFound" }
+
+  # ─── LIFECYCLE EVENTS ─────────────────────────────────────────────────────
+
+  /api/v1/instances/{resource_id}/events:
+    post:
+      tags: [lifecycle-events]
+      operationId: reportLifecycleEvent
+      summary: Report a resource lifecycle or health event to DCM (Level 2+)
+      description: |
+        Called by operators to notify DCM of events affecting the operational status of
+        a managed resource. DCM receives the event, evaluates it through the Policy Engine,
+        and determines the appropriate response (notify consumer, trigger recovery, escalate).
+
+        DCM acts as the Tenant advocate — it does not expose raw infrastructure events to
+        consumers directly. It applies policy, filters urgency, and routes notifications
+        to the appropriate audience based on the governance model.
+
+        **Idempotency:** Use the same `event_uuid` to safely retry. DCM will not create
+        duplicate records for the same `event_uuid`.
+
+        **UNSANCTIONED_CHANGE:** When the provider detects a CR was modified without a
+        corresponding DCM request ID (i.e., someone modified Kubernetes resources directly),
+        report this as `UNSANCTIONED_CHANGE`. DCM will flag this as drift, notify the Tenant,
+        and fire the appropriate Recovery Policy.
+      parameters:
+        - { $ref: "#/components/parameters/resource_id" }
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema: { $ref: "#/components/schemas/LifecycleEventReport" }
+      responses:
+        "202":
+          description: Event accepted; DCM policy evaluation initiated
+        "400": { $ref: "#/components/responses/BadRequest" }
+        "403": { $ref: "#/components/responses/Forbidden" }
+        "409":
+          description: Idempotent — event_uuid already recorded
+
+# ─── COMPONENTS ────────────────────────────────────────────────────────────────
+
+components:
+
+  securitySchemes:
+    ProviderCredential:
+      type: http
+      scheme: bearer
+      description: |
+        Short-lived provider interaction credential issued by DCM at registration time.
+        Scoped to the specific provider UUID. Must be rotated before expiry. Rotation
+        is managed via the Credential Provider integration declared at registration.
+
+  parameters:
+
+    provider_id:
+      name: provider_id
+      in: path
+      required: true
+      schema: { type: string, format: uuid }
+      description: DCM-assigned provider UUID
+
+    resource_id:
+      name: resource_id
+      in: path
+      required: true
+      schema: { type: string }
+      description: Operator-assigned resource ID (as returned in CreateResponse)
+
+    entity_uuid:
+      name: entity_uuid
+      in: path
+      required: true
+      schema: { type: string, format: uuid }
+      description: DCM entity UUID (provided by DCM in the CreateRequest)
+
+    notification_uuid:
+      name: notification_uuid
+      in: path
+      required: true
+      schema: { type: string, format: uuid }
+      description: Provider-assigned notification UUID (from UpdateNotificationRequest)
+
+  responses:
+
+    BadRequest:
+      description: Malformed request payload
+      content:
+        application/json:
+          schema: { $ref: "#/components/schemas/ProviderError" }
+
+    Forbidden:
+      description: Invalid or expired provider credential, or credential scoped to different provider
+      content:
+        application/json:
+          schema: { $ref: "#/components/schemas/ProviderError" }
+
+    NotFound:
+      description: Resource or notification not found
+      content:
+        application/json:
+          schema: { $ref: "#/components/schemas/ProviderError" }
+
+  schemas:
+
+    ProviderError:
+      type: object
+      required: [error]
+      additionalProperties: false
+      properties:
+        error:
+          type: object
+          required: [code, message, request_id]
+          additionalProperties: false
+          properties:
+            code:       { type: string }
+            message:    { type: string }
+            request_id: { type: string, format: uuid }
+
+    ProviderRegistrationRequest:
+      type: object
+      required: [name, display_name, conformance_level, endpoint, version, service_types]
+      additionalProperties: false
+      properties:
+        name:
+          type: string
+          description: Unique provider name — natural key for idempotent re-registration
+          pattern: "^[a-z0-9][a-z0-9-_/]*[a-z0-9]$"
+        display_name:    { type: string, maxLength: 256 }
+        conformance_level:
+          type: integer
+          minimum: 1
+          maximum: 4
+        endpoint:
+          type: string
+          format: uri
+          description: Base URL of the operator's DCM Services API
+        version:
+          type: string
+          pattern: "^\\d+\\.\\d+\\.\\d+$"
+        service_types:
+          type: array
+          minItems: 1
+          items:
+            type: object
+            required: [service_type, service_type_uuid, operations_supported]
+            additionalProperties: false
+            properties:
+              service_type:
+                type: string
+                description: DCM Resource Type FQN (e.g., Storage.Database)
+                pattern: "^[A-Z][a-zA-Z0-9]+\\.[A-Z][a-zA-Z0-9]+$"
+              service_type_uuid:
+                type: string
+                format: uuid
+              crd_reference:
+                type: object
+                description: Kubernetes CRD reference (Kubernetes operators only)
+                additionalProperties: false
+                properties:
+                  group:   { type: string }
+                  version: { type: string }
+                  kind:    { type: string }
+              operations_supported:
+                type: array
+                items:
+                  type: string
+                  enum: [CREATE, READ, UPDATE, DELETE, DISCOVER]
+              field_mapping_ref:
+                type: string
+                format: uri
+                description: URL or reference to the field mapping declaration (Level 2+)
+        kubernetes:
+          type: object
+          description: Kubernetes-specific registration metadata (Kubernetes operators only)
+          additionalProperties: false
+          properties:
+            cluster_id:         { type: string }
+            cluster_endpoint:   { type: string, format: uri }
+            namespace_strategy:
+              type: string
+              enum: [per_tenant, shared, per_resource]
+        sovereignty_declaration:
+          type: object
+          description: Where this provider operates and which jurisdictions it is subject to
+          additionalProperties: false
+          properties:
+            operating_jurisdictions:
+              type: array
+              items: { type: string, pattern: "^[A-Z]{2}$" }
+            data_residency_zones:
+              type: array
+              items: { type: string }
+            regulatory_frameworks:
+              type: array
+              items: { type: string }
+        update_capabilities:
+          type: array
+          description: Pre-authorization declarations for provider-initiated updates (Level 2+)
+          items:
+            type: object
+            required: [notification_type, affected_fields]
+            additionalProperties: false
+            properties:
+              notification_type:
+                type: string
+                enum: [authorized_change, maintenance_change, auto_scale, auto_heal]
+              affected_fields:
+                type: array
+                items: { type: string }
+              max_change_magnitude: { type: string }
+              typical_trigger:      { type: string }
+        cancellation_capabilities:
+          type: object
+          description: Request cancellation support declaration (Level 2+)
+          additionalProperties: false
+          properties:
+            supports_cancellation: { type: boolean }
+            cancellation_supported_during:
+              type: array
+              items: { type: string, enum: [DISPATCHED, PROVISIONING] }
+            partial_rollback_possible: { type: boolean }
+            cancellation_response_time_seconds: { type: integer }
+
+    ProviderRegistrationResponse:
+      type: object
+      required: [provider_id, name, status, conformance_level_accepted]
+      additionalProperties: false
+      properties:
+        provider_id:
+          type: string
+          format: uuid
+          description: DCM-assigned provider UUID — stable across re-registrations
+        name:                   { type: string }
+        status:
+          type: string
+          enum: [registered, updated, pending_approval]
+        conformance_level_accepted: { type: integer }
+        capabilities_enabled:
+          type: array
+          items: { type: string }
+        credential_ref:
+          type: string
+          description: Reference to the provider interaction credential (retrieve via Credential Provider)
+        credential_expires_at:
+          type: string
+          format: date-time
+
+    CapacityReport:
+      type: object
+      required: [provider_id, report_timestamp, capacity_by_service_type]
+      additionalProperties: false
+      properties:
+        provider_id:
+          type: string
+          format: uuid
+        report_timestamp:
+          type: string
+          format: date-time
+        next_report_at:
+          type: string
+          format: date-time
+          description: When the provider will send the next scheduled report
+        capacity_by_service_type:
+          type: array
+          minItems: 1
+          items:
+            type: object
+            required: [service_type_uuid, available_units, reserved_units, committed_units]
+            additionalProperties: false
+            properties:
+              service_type_uuid:  { type: string, format: uuid }
+              available_units:    { type: integer, minimum: 0 }
+              reserved_units:     { type: integer, minimum: 0 }
+              committed_units:    { type: integer, minimum: 0 }
+              unit_definition:
+                type: string
+                description: What one unit represents (e.g., "1 database cluster", "1 vCPU")
+              kubernetes_resources:
+                type: object
+                description: Raw Kubernetes resource availability (Kubernetes operators only)
+                additionalProperties: false
+                properties:
+                  available_cpu_millicores: { type: integer }
+                  available_memory_bytes:   { type: integer }
+                  available_storage_bytes:  { type: integer }
+                  node_count:               { type: integer }
+
+    RealizedStatePush:
+      type: object
+      required: [resource_id, dcm_entity_uuid, lifecycle_state, realized_at, spec]
+      additionalProperties: false
+      description: |
+        The Denaturalization payload — provider-native state translated into DCM Unified
+        Data Model format. DCM stores this in the append-only Realized State Store.
+      properties:
+        resource_id:
+          type: string
+          description: Operator-assigned resource ID
+        dcm_entity_uuid:
+          type: string
+          format: uuid
+          description: DCM entity UUID (provided by DCM in the CreateRequest)
+        dcm_request_id:
+          type: string
+          format: uuid
+          description: DCM request UUID (idempotency key — provide for all non-spontaneous state changes)
+        lifecycle_state:
+          type: string
+          enum: [PROVISIONING, REALIZED, OPERATIONAL, DEGRADED, FAILED, DECOMMISSIONING, DECOMMISSIONED]
+        realized_at:
+          type: string
+          format: date-time
+        spec:
+          type: object
+          additionalProperties: true
+          description: All realized field values in DCM Unified Data Model format
+        field_provenance:
+          type: object
+          additionalProperties:
+            type: object
+            properties:
+              source_type:  { type: string, enum: [provider, operator, kubernetes_status] }
+              source_uuid:  { type: string, format: uuid }
+              timestamp:    { type: string, format: date-time }
+          description: Per-field provenance. Required for Level 3 conformance.
+        provider_metadata:
+          type: object
+          additionalProperties: true
+          description: Provider-native metadata stored by DCM but treated as opaque
+        kubernetes_reference:
+          type: object
+          description: Kubernetes CR reference (Kubernetes operators only)
+          additionalProperties: false
+          properties:
+            namespace:        { type: string }
+            name:             { type: string }
+            uid:              { type: string }
+            resource_version: { type: string }
+        relationships:
+          type: array
+          description: Any relationships created or discovered during realization
+          items:
+            type: object
+            properties:
+              relationship_type:    { type: string }
+              target_entity_uuid:   { type: string, format: uuid }
+        failure_reason:
+          type: string
+          description: Required when lifecycle_state is FAILED
+
+    RealizedStateAck:
+      type: object
+      required: [realized_state_uuid, entity_uuid, lifecycle_state]
+      additionalProperties: false
+      properties:
+        realized_state_uuid:  { type: string, format: uuid }
+        entity_uuid:          { type: string, format: uuid }
+        lifecycle_state:      { type: string }
+        recorded_at:          { type: string, format: date-time }
+
+    InterimStatusPush:
+      type: object
+      required: [request_id, lifecycle_state]
+      additionalProperties: false
+      properties:
+        request_id:
+          type: string
+          format: uuid
+          description: DCM request UUID this status update corresponds to
+        lifecycle_state:
+          type: string
+          enum: [PROVISIONING, UPDATING, DECOMMISSIONING]
+          description: Current non-terminal state
+        progress:
+          type: object
+          additionalProperties: false
+          properties:
+            step_current:          { type: integer, minimum: 1 }
+            step_total:            { type: integer, minimum: 1 }
+            step_label:            { type: string }
+            step_started_at:       { type: string, format: date-time }
+            estimated_completion:  { type: string, format: date-time }
+        constituent_status:
+          type: array
+          description: Per-constituent status for compound/Meta Provider operations
+          items:
+            type: object
+            required: [ref, status]
+            additionalProperties: false
+            properties:
+              ref:          { type: string, description: "Constituent identifier (matches Meta Provider composition declaration)" }
+              status:       { type: string, enum: [PENDING, PROVISIONING, REALIZED, FAILED] }
+              completed_at: { type: string, format: date-time }
+              started_at:   { type: string, format: date-time }
+        notes:
+          type: string
+          maxLength: 512
+          description: Optional human-readable detail for consumer display
+
+    UpdateNotificationRequest:
+      type: object
+      required: [provider_uuid, notification_uuid, notification_type, changed_fields, effective_at]
+      additionalProperties: false
+      properties:
+        provider_uuid:
+          type: string
+          format: uuid
+        notification_uuid:
+          type: string
+          format: uuid
+          description: Provider-assigned idempotency key — must be unique per change event
+        notification_type:
+          type: string
+          enum: [authorized_change, maintenance_change, auto_scale, auto_heal]
+          description: |
+            authorized_change: explicitly authorized by policy.
+            maintenance_change: result of a maintenance window.
+            auto_scale: automatic scaling action.
+            auto_heal: automatic healing/recovery action.
+        changed_fields:
+          type: object
+          additionalProperties:
+            type: object
+            required: [previous_value, new_value]
+            additionalProperties: false
+            properties:
+              previous_value:       { description: "Value before the change" }
+              new_value:            { description: "Value after the change" }
+              change_reason:        { type: string }
+              authorizing_policy_ref:
+                oneOf: [{ type: string, format: uuid }, { type: "null" }]
+                description: UUID of the DCM policy that pre-authorizes this change type
+        effective_at:
+          type: string
+          format: date-time
+          description: When the change took effect on the provider side
+        provider_evidence_ref:
+          type: string
+          description: Provider-side reference (e.g., Kubernetes event UID, cloud audit log ID)
+
+    UpdateNotificationAccepted:
+      type: object
+      required: [notification_uuid, status, entity_uuid]
+      additionalProperties: false
+      properties:
+        notification_uuid:       { type: string, format: uuid }
+        entity_uuid:             { type: string, format: uuid }
+        status:
+          type: string
+          enum: [processing, auto_approved, pending_consumer_approval, pending_admin_approval]
+        realized_state_uuid:
+          oneOf: [{ type: string, format: uuid }, { type: "null" }]
+          description: Set immediately when status is auto_approved
+        notification_status_url:
+          type: string
+          format: uri
+          description: URL to poll for status updates
+
+    UpdateNotificationStatus:
+      type: object
+      required: [notification_uuid, status, entity_uuid]
+      additionalProperties: false
+      properties:
+        notification_uuid:
+          type: string
+          format: uuid
+        status:
+          type: string
+          enum: [processing, approved, auto_approved, pending_approval, rejected]
+        entity_uuid:           { type: string, format: uuid }
+        realized_state_uuid:
+          oneOf: [{ type: string, format: uuid }, { type: "null" }]
+        consumer_approval_required: { type: boolean }
+        consumer_notified_at:
+          oneOf: [{ type: string, format: date-time }, { type: "null" }]
+        resolved_at:
+          oneOf: [{ type: string, format: date-time }, { type: "null" }]
+        rejection_reason:
+          oneOf: [{ type: string }, { type: "null" }]
+
+    LifecycleEventReport:
+      type: object
+      required: [event_uuid, event_type, provider_id, resource_id, dcm_entity_uuid, event_timestamp, severity]
+      additionalProperties: false
+      properties:
+        event_uuid:
+          type: string
+          format: uuid
+          description: Provider-assigned idempotency key
+        event_type:
+          type: string
+          enum:
+            - ENTITY_HEALTH_CHANGE
+            - DEGRADATION
+            - MAINTENANCE_SCHEDULED
+            - MAINTENANCE_STARTED
+            - MAINTENANCE_COMPLETED
+            - UNSANCTIONED_CHANGE
+            - CAPACITY_CHANGE
+            - DECOMMISSION_NOTICE
+            - PROVIDER_DEGRADATION
+        provider_id:
+          type: string
+          format: uuid
+        resource_id:
+          type: string
+        dcm_entity_uuid:
+          type: string
+          format: uuid
+        event_timestamp:
+          type: string
+          format: date-time
+        severity:
+          type: string
+          enum: [INFO, WARNING, CRITICAL]
+        requires_immediate_action: { type: boolean, default: false }
+        details:
+          type: object
+          additionalProperties: true
+          description: |
+            Event-type-specific detail. Common patterns:
+
+            For UNSANCTIONED_CHANGE:
+              changed_fields: [{field_path, previous_value, new_value}]
+              change_source: "direct_kubernetes_edit" | "external_tool"
+
+            For DEGRADATION:
+              degraded_components: [{component, status, detail}]
+              impact_on_consumers: "none" | "reduced_performance" | "partial_outage"
+
+            For MAINTENANCE_SCHEDULED:
+              maintenance_window: {start_at, end_at, window_type}
+              expected_impact: "none" | "brief_unavailability" | "full_unavailability"
+
+            For CAPACITY_CHANGE:
+              previous_available_units: integer
+              new_available_units: integer
+              change_reason: string
+        related_request_id:
+          oneOf: [{ type: string, format: uuid }, { type: "null" }]
+          description: DCM request UUID if this event is related to an in-progress operation
diff --git a/content/docs/architecture/specifications/dcm-provider-gui-spec.md b/content/docs/architecture/specifications/dcm-provider-gui-spec.md
new file mode 100644
index 0000000..63d57db
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-provider-gui-spec.md
@@ -0,0 +1,327 @@
+# DCM Provider Management GUI Specification
+
+> **AEP Alignment:** Provider API endpoints referenced in this spec follow [AEP](https://aep.dev) conventions — custom methods use colon syntax. See `schemas/openapi/dcm-admin-api.yaml` for the normative specification.
+
+
+**Document Status:** 🔄 In Progress
+**Document Type:** Specification — Provider Management Interface
+**Related Documents:** [Unified Provider Contract](../data-model/A-provider-contract.md) | [OIS Specification](dcm-operator-interface-spec.md) | [Registration Specification](dcm-registration-spec.md) | [Admin GUI Specification](dcm-admin-gui-spec.md) | [credential management service Model](../data-model/31-credential-provider-model.md)
+
+> **Status:** Draft — Ready for implementation feedback
+>
+> The Provider Management GUI is the interface for teams responsible for operating and maintaining DCM providers. It surfaces in the DCM web application for actors holding provider owner roles. Each of the eleven DCM provider types has a common management shell plus type-specific extension panels.
+
+---
+
+## 1. Architecture
+
+### 1.1 Surface in Unified Shell
+
+Provider management is a **third surface in the unified DCM web application**. An actor who owns a provider (registered in a provider's `owner_team_uuid` or holds the `provider_owner` role scoped to a provider) sees a "Providers" section in their navigation alongside the Consumer Portal.
+
+```
+DCM Web Application
+├── Consumer Portal
+├── Admin Panel                    ← platform_admin role
+└── Provider Management            ← provider_owner role
+      ├── My Providers (list)
+      └── [Provider Type] → [type-specific management]
+```
+
+### 1.2 Provider Owner Identity
+
+Provider ownership is declared at registration time and may include:
+- A tenant UUID (the team that owns this provider)
+- A group UUID (the specific group within that tenant)
+- Named contacts (from Business Data)
+
+The `provider_owner` role is scoped to specific provider UUIDs — a team may own multiple providers and sees all of them. Platform Admins see all registered providers across all owners.
+
+### 1.3 Common Management Shell
+
+Every provider type (all eleven) renders within the same management shell. The shell provides:
+- Provider name, type badge, health status indicator
+- Registration status (pending / active / suspended / deregistered)
+- Navigation tabs that vary by provider type
+- Health check response viewer
+- Audit trail for all admin actions on this provider
+
+---
+
+## 2. Common Provider Tabs (All Types)
+
+### 2.1 Overview
+
+- Provider UUID, handle, type, registration date, owner team/group
+- Health status: current `pass | warn | fail` with last check timestamp
+- Health response detail (from OIS `/health` endpoint — live refresh every 30s)
+- Connection details: endpoint URL, OIS version declared
+- Registration token status (active / expiry date)
+
+### 2.2 Configuration
+
+- Provider capability declaration YAML viewer (read-only; changes require re-registration or update submission)
+- Editable fields: display name, description, owner contact, notification preferences
+- Profile compatibility indicator: which DCM profiles this provider is certified to operate under
+
+### 2.3 Health History
+
+- 30-day health check history: pass / warn / fail timeline
+- Degraded periods highlighted; duration of each incident
+- Correlation with entity realization failures during degraded periods
+
+### 2.4 Audit Trail
+
+- All admin actions on this provider: approval, suspension, config changes, capacity updates
+- Actor who performed each action, timestamp, comment
+- Filterable by action type
+
+### 2.5 Notifications
+
+- Notification endpoint configuration for this provider
+- Which DCM events this provider subscribes to (webhook subscriptions for provider.* events)
+- Test webhook delivery
+
+---
+
+## 3. Service Provider — Extended Tabs
+
+Service Providers (the most common type — realize infrastructure resources) have the richest management surface.
+
+### 3.1 Capacity Management
+
+**API:** `POST /api/v1/providers/{uuid}/capacity`
+
+- Current capacity report: available units per resource type per location
+- Historical capacity charts: capacity utilization over time
+- **Manual capacity update form**: override reported capacity for emergency situations
+- Capacity denial history: requests denied due to insufficient capacity
+- Alert configuration: notify when capacity below threshold
+
+### 3.2 Managed Entities
+
+- All DCM entities realized by this provider: type, tenant, state, TTL, drift status
+- Filterable by tenant (for Platform Admin), resource type, state
+- Entity count by state (pie chart)
+- Entities with open drift records: grouped by drift severity
+- Entities approaching TTL expiry in next P7D
+- Click-through to entity detail (read-only view for provider owner; editable for Platform Admin)
+
+### 3.3 Naturalization Mapping
+
+- Resource type → provider-native mapping declarations
+- View the Naturalization configuration for each supported resource type
+- Denaturalization mapping: what provider-native fields map back to DCM fields
+- **Test naturalization**: submit a DCM payload and see the naturalized version without dispatching
+
+### 3.4 Interim Status Configuration
+
+- Enable / disable interim status reporting per resource type
+- Reporting frequency configuration (minimum interval, max steps)
+- View recent interim status payloads for debugging
+
+### 3.5 Realization History
+
+- Recent realization requests: accepted / failed / in-progress
+- Mean realization time by resource type (last 30 days)
+- Failure analysis: top failure reasons with counts
+- In-flight realizations with live status
+
+---
+
+## 4. credential management service — Extended Tabs
+
+### 4.1 Credential Inventory
+
+- All credentials managed by this provider: type, entity scope, issued date, expiry, last retrieved
+- Never shows credential values — only metadata
+- Filter by credential type (api_key, x509_certificate, ssh_key, etc.)
+- Credentials approaching expiry: highlighted red within renewal trigger window
+
+### 4.2 Rotation Management
+
+- Credentials currently in rotation (transition window open): old UUID → new UUID pairs
+- Manual rotation trigger for specific credentials
+- Rotation history with trigger reason
+
+### 4.3 Revocation Registry
+
+- Summary: total revoked, still-in-TTL (in registry), post-TTL (pruned from registry)
+- Search by credential UUID to check revocation status
+- Emergency revocation form: revoke by credential UUID or entity UUID with required reason
+
+### 4.4 External CA Configuration (if ca_type: external)
+
+- CA protocol in use (ACME / EST / SCEP / CMP / Vault PKI / etc.)
+- CA endpoint connectivity status
+- Certificate chain view: root CA → intermediate → issued certs
+- Pending certificate requests
+- CRL / OCSP endpoint status
+
+### 4.5 Algorithm Compliance View
+
+- Algorithms in use across managed credentials
+- Forbidden algorithm violations (should be zero — highlighted red if any found)
+- Algorithm distribution chart: ECDSA P-384 vs P-256 vs RSA vs other
+- Upcoming algorithm deprecations from doc 40 forbidden list
+
+---
+
+## 5. Auth Provider — Extended Tabs
+
+### 5.1 Connection Status
+
+- Auth Provider endpoint connectivity: pass / warn / fail
+- Failover chain: current primary, configured fallbacks, activation status
+- Token validation latency (p50, p99) — last 1 hour
+
+### 5.2 Actor and Group Sync
+
+- SCIM sync status (if SCIM 2.0 enabled): last sync timestamp, records synced, errors
+- Group → DCM role mapping table (read-only; changes via configuration update)
+- Actor provisioning audit: recent SCIM-triggered creates, updates, deactivations
+
+### 5.3 Session Statistics
+
+- Active session count from this Auth Provider
+- Session distribution by auth method (OIDC / LDAP / API key / mTLS)
+- MFA verification rates: mfa_verified: true vs false breakdown
+- Step-up MFA events in last 24h
+
+### 5.4 Configuration
+
+- Auth Provider YAML viewer
+- Editable: display name, session TTL, concurrent session limit, role mapping (changes go through standard artifact lifecycle — proposed → reviewed → active)
+- Shadow mode toggle for configuration changes
+
+---
+
+## 6. External Policy Evaluator — Extended Tabs
+
+### 6.1 Policy Inventory
+
+- Policies managed by this provider: handle, type, enforcement class, status (active / shadow / deprecated)
+- Policy evaluation counts and outcomes (last 24h)
+- Shadow divergence alerts: policies with >5% divergence rate from expected
+
+### 6.2 Trust Level Management
+
+- Current trust level: local / community / verified / authoritative
+- Trust elevation request form (requires Platform Admin approval)
+- Trust history
+
+### 6.3 Policy Contribution Pipeline
+
+- Policies submitted for contribution to the DCM Policy Registry
+- Lifecycle status: shadow validation period, community review, approval
+- Withdraw pending contributions
+
+---
+
+## 7. Information Provider — Extended Tabs
+
+### 7.1 Data Source Status
+
+- Connection status to upstream data source
+- Last successful sync and record count
+- Data freshness indicator (stale threshold from provider registration)
+
+### 7.2 Confidence Score Management
+
+- Declared confidence scores per data field
+- Confidence override history (when DCM overrode provider confidence based on corroboration)
+
+### 7.3 Query Performance
+
+- Response time distribution for DCM queries to this provider
+- Cache hit rate (if DCM caches this provider's data)
+- Top queried fields
+
+---
+
+## 8. data store — Extended Tabs
+
+### 8.1 Store Health
+
+- Store-specific health: write latency, read latency, replication lag (if replicated), disk utilization
+- Consistency guarantee compliance: declared vs observed consistency level
+
+### 8.2 Capacity and Retention
+
+- Storage utilization by store type (Intent, Requested, Realized, Audit)
+- Retention policy status: records approaching retention deadline
+- Partition / shard status (for GitOps stores using partitioning strategies from doc 41)
+
+---
+
+## 9. Notification and event routing services — Extended Tabs
+
+### 9.1 notification service
+
+- Delivery channel status: email, Slack, PagerDuty, etc.
+- Delivery success rate (last 24h)
+- Failed deliveries with retry status
+- Audience routing test: send a test notification to a specific audience
+
+### 9.2 event routing service
+
+- Broker connectivity status
+- Topic / stream inventory: DCM topics and consumer group lag
+- Message throughput (messages/minute by topic)
+- Dead letter queue: messages that failed delivery after retry
+
+---
+
+## 10. compound service definition — Extended Tabs
+
+### 10.1 Compound Service Status
+
+- Active compound service instances: all constituent statuses
+- Constituents in PENDING_DEPENDENCY state across all active instances
+- Failed compensation attempts (COMPENSATION_FAILED state)
+
+### 10.2 Constituent Provider Health
+
+- Health of each provider this compound service definition depends on
+- Impact analysis: if Provider X degrades, which compound services are affected
+
+---
+
+## 11. Resource Type Registry and Peer DCM — Extended Tabs
+
+### 11.1 Resource Type Registry
+
+- Resource type registry sync status: last sync, version, record count
+- Type registration submissions pending review
+- Registry health: response time, availability
+
+### 11.2 Peer DCM (Federation)
+
+- Federation tunnel status: connected / degraded / disconnected
+- Peer DCM version and deployment profile
+- Cross-instance request routing: requests forwarded to / from this peer (last 24h)
+- Sovereignty boundary status: which data classifications are permitted across this tunnel
+
+---
+
+## 12. Provider-Scoped Security
+
+### 12.1 Provider Interaction Credentials
+
+- Interaction credentials issued to this provider: count, last issued, rotation status
+- Emergency credential revocation capability
+- Audit of all credential retrievals by this provider (CPX-005: first retrieval always audited)
+
+### 12.2 mTLS Certificate Status
+
+- Provider's mTLS certificate: issuer, expiry, OCSP status
+- Certificate renewal tracking (for external CA managed certs)
+
+### 12.3 Provider Audit Trail Contribution
+
+- Provenance records emitted by this provider: count, last emitted
+- Audit forwarding status: is the provider successfully forwarding audit events?
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/specifications/dcm-registration-spec.md b/content/docs/architecture/specifications/dcm-registration-spec.md
new file mode 100644
index 0000000..dd9001f
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-registration-spec.md
@@ -0,0 +1,906 @@
+# DCM Registration Specification
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Registration Specification
+
+
+> **AEP Alignment:** Registration API endpoints follow [AEP](https://aep.dev) conventions — custom methods use colon syntax (`POST /admin/registrations/{uuid}:approve`). `resource_type` in provider capabilities accepts FQN string or Registry UUID. See `schemas/openapi/dcm-admin-api.yaml` for the normative specification.
+
+
+> **📋 Draft**
+>
+> This specification has been promoted from Work in Progress to Draft status. All questions resolved. Complete registration pipeline for all 11 provider types with full capability declaration schemas and federation trust model. It is ready for implementation feedback but has not yet been formally reviewed for final release.
+>
+> This specification defines the unified registration flow for all DCM provider types. Published to share design direction and invite feedback.
+
+**Version:** 0.1.0-draft
+**Status:** Draft — Ready for implementation feedback
+**Document Type:** Technical Specification
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [Control Plane Components](../data-model/25-control-plane-components.md) | [Governance Matrix](../data-model/27-governance-matrix.md) | [Accreditation and Authorization Matrix](../data-model/26-accreditation-and-authorization-matrix.md) | [Policy Profiles](../data-model/14-policy-profiles.md) | [DCM Operator Interface Specification](dcm-operator-interface-spec.md)
+
+---
+
+## Abstract
+
+This specification defines the unified registration flow by which all DCM provider types establish a trusted, governed relationship with a DCM deployment. It covers: the Provider Type Registry, the registration token model, the approval method configuration, the step-by-step registration pipeline, trust establishment, the per-type capability declaration schemas, the ongoing lifecycle after activation, federated trust configuration, and profile-bound registration policy defaults.
+
+---
+
+## 1. Provider Type Registry
+
+The Provider Type Registry is the authoritative list of provider types that a DCM deployment will accept registrations for. It follows the same three-tier registry model as the Resource Type Registry.
+
+### 1.1 Registry Tiers
+
+| Tier | Maintained By | Examples |
+|------|--------------|---------|
+| **Core** | DCM Project | The eleven built-in provider types |
+| **Verified Community** | Named community maintainers | Domain-specific provider types |
+| **Organization** | Deploying organization | Custom/proprietary integrations |
+
+### 1.2 Provider Type Registry Entry
+
+```yaml
+provider_type_registry_entry:
+  artifact_metadata:
+    uuid: 
+    handle: "provider-types/service-provider"
+    version: "1.0.0"
+    status: active
+    tier: core
+
+  provider_type_id: service_provider
+  display_name: "Service Provider"
+  description: "Realizes infrastructure resources for DCM"
+
+  # What this provider type is permitted to do
+  permissions:
+    may_receive_assembled_payload: true
+    may_write_realized_state: true
+    may_write_discovered_state: true
+    may_receive_scoped_credentials: true
+    may_receive_phi_by_default: false          # requires HIPAA accreditation
+    may_receive_sovereign_data: false          # hard limit; never overridden
+
+  # Approval method defaults (profile may override — see Section 4)
+  default_approval_method: reviewed        # auto | reviewed | verified | authorized
+
+  # Minimum trust level granted after approval
+  default_trust_level: standard               # minimal | standard | elevated | high
+
+  # Which deployment profiles permit this provider type
+  enabled_in_profiles: [minimal, dev, standard, prod, fsi, sovereign]
+
+  # Capability declaration schema reference
+  capability_schema_ref: "schemas/service-provider-capabilities-v1.0.0"
+
+  # Health check requirements
+  health_check:
+    endpoint_required: true
+    minimum_check_interval: PT1M
+    failure_threshold: 3                       # failures before degraded status
+```
+
+### 1.3 The Eleven Core Provider Types
+
+| # | provider_type_id | Default Approval | Enabled In |
+|---|-----------------|-----------------|------------|
+| 1 | `service_provider` | reviewed | all profiles |
+| 2 | `information_provider` | reviewed | all profiles |
+| 3 | `compound service` | verified | standard+ |
+| 4 | `(prescribed infrastructure)` | verified | all profiles |
+| 5 | `(optional infrastructure)` | reviewed | dev+ (external endpoints: standard+) |
+| 6 | `external_policy_evaluation` (Mode 1-2) | reviewed | all profiles |
+| 7 | `external_policy_evaluation` (Internal and External) | verified | standard+ |
+| 8 | `service_provider` | verified | standard+ |
+| 9 | `auth_provider` | verified | all profiles |
+| 10 | `service_provider` | reviewed | all profiles |
+
+Note: Internal and External External Policy Evaluators are treated as a separate registry entry from Mode 1-2 due to the elevated trust requirements.
+
+---
+
+## 2. Registration Token Model
+
+Registration tokens are pre-issued by platform admins to authorize specific registrations without requiring full manual review at submission time.
+
+### 2.1 Token Structure
+
+```yaml
+registration_token:
+  token_uuid: 
+  token_value: 
+  issued_by: 
+  issued_at: 
+  expires_at:                      # short-lived; default PT72H
+  single_use: true                           # token invalidated after first use
+
+  scope:
+    provider_type_id: service_provider       # which provider type this authorizes
+    provider_handle_pattern: "eu-west-*"     # optional: restrict to matching handles
+    sovereignty_zone: eu-west-sovereign      # optional: restrict to this zone
+    grants_auto_approval: true               # whether token enables auto-approval
+    # grants_auto_approval: false = token still required but human review still needed
+    # (useful for tracking/auditing expected registrations without bypassing review)
+
+  max_trust_level_granted: standard          # token cannot grant higher than this
+```
+
+### 2.2 Token Issuance
+
+```
+POST /api/v1/admin/registration-tokens
+Role: platform_admin
+
+{
+  "provider_type_id": "service_provider",
+  "expires_in": "PT72H",
+  "scope": {
+    "provider_handle_pattern": "eu-west-*",
+    "sovereignty_zone": "eu-west-sovereign",
+    "grants_auto_approval": true
+  },
+  "purpose": "EU-WEST production compute provider onboarding"
+}
+
+Response 201 Created:
+{
+  "token_uuid": "",
+  "token_value": "",
+  "expires_at": "",
+  "scope": { ... }
+}
+```
+
+Token values are presented exactly once — at creation. They are never retrievable again (stored as a hash). Platform admins must transmit the token securely to the provider operator.
+
+---
+
+## 3. Approval Method Configuration
+
+> **Authority Tier Model:** Approval methods (`reviewed`, `verified`, `authorized`) are defined in the [Authority Tier Model](../data-model/32-authority-tier-model.md) as a named, ordered list. Organizations may insert custom tiers. The effective method resolution (Section 3.2) uses tier names; DCM resolves numeric weight from the ordered list at evaluation time (ATM-001).
+
+### 3.1 The Four Approval Methods
+
+| Method | Description | Approval path |
+|--------|-------------|--------------|
+| `auto` | DCM validates automatically; activates without human review | All validation checks pass → active |
+| `reviewed` | One platform admin must explicitly approve | Submitted → validated → pending_approval → one admin approves → active |
+| `verified` | Two platform admins must independently approve | Submitted → validated → pending_approval → two admins approve → active |
+| `authorized` | N members of a declared DCMGroup must record decisions via the Admin API; quorum tracked by DCM; deliberation process is the organization's responsibility | Submitted → validated → pending_approval → DCMGroup members record votes via Admin API (or external systems calling API) → quorum → active |
+
+### 3.2 Effective Approval Method Resolution
+
+The effective approval method for a specific registration is the most restrictive result of:
+
+```
+effective_method = most_restrictive(
+  provider_type_registry.default_approval_method,
+  active_profile.registration_policy.min_approval_method,
+  registration_token.grants_auto_approval ? relax_to_auto : no_change
+)
+```
+
+Resolution rules:
+- Profile minimum overrides provider type default (always upward; profiles can only tighten)
+- A valid registration token can relax the effective method to `auto` ONLY if the profile's `allow_token_auto_approval` is true
+- `authorized` cannot be relaxed by any token
+
+### 3.3 Profile Registration Policy Defaults
+
+```yaml
+profile_registration_policy:
+  minimal:
+    min_approval_method: reviewed
+    allow_token_auto_approval: true        # token can enable auto for any type
+    require_sovereignty_declaration: false
+    require_health_check_before_approval: false
+
+  dev:
+    min_approval_method: reviewed
+    allow_token_auto_approval: true
+    require_sovereignty_declaration: false
+    require_health_check_before_approval: true
+
+  standard:
+    min_approval_method: reviewed
+    allow_token_auto_approval: true        # tokens can auto-approve non-elevated types
+    token_auto_approval_max_trust: standard  # tokens cannot auto-approve elevated types
+    require_sovereignty_declaration: true
+    require_health_check_before_approval: true
+
+  prod:
+    min_approval_method: reviewed
+    high_trust_types_require: verified  # storage, auth, policy-mode3-4, credential
+    allow_token_auto_approval: false          # no auto-approval in prod
+    require_sovereignty_declaration: true
+    require_accreditation_submission: true   # must submit at least self_declared
+    require_health_check_before_approval: true
+    approval_timeout: P7D                    # auto-reject if not approved within 7 days
+
+  fsi:
+    min_approval_method: verified       # everything requires dual approval
+    allow_token_auto_approval: false
+    require_sovereignty_declaration: true
+    require_accreditation_submission: true
+    minimum_accreditation_type: third_party  # self_declared not accepted
+    require_health_check_before_approval: true
+    require_governance_matrix_check: true    # governance matrix evaluated at registration
+    approval_timeout: P14D
+
+  sovereign:
+    min_approval_method: authorized           # everything requires authorized approval
+    allow_token_auto_approval: false
+    require_sovereignty_declaration: true
+    require_accreditation_submission: true
+    minimum_accreditation_type: regulatory_certification
+    require_hardware_attestation: true
+    require_governance_matrix_check: true
+    authorized_group_handle: "platform/registration-authorized"
+    approval_timeout: P30D
+```
+
+---
+
+## 4. Registration Pipeline
+
+### 4.1 Lifecycle States
+
+```
+SUBMITTED → VALIDATING → PENDING_APPROVAL → ACTIVE
+                       ↘ REJECTED (validation failure)
+                                         ↘ REJECTED (approval denied)
+
+Additional states:
+ACTIVE → SUSPENDED (platform admin action or health failure)
+ACTIVE → DEREGISTERING → DEREGISTERED (graceful removal)
+ACTIVE → FORCED_DEREGISTERED (immediate removal)
+```
+
+### 4.2 Step 1 — Submission
+
+Provider submits registration payload to DCM:
+
+```
+POST /api/v1/provider/register
+Content-Type: application/json
+X-DCM-Registration-Token:     # optional; enables auto-approval if valid
+
+{
+  "provider_type_id": "service_provider",
+  "handle": "eu-west-prod-1",
+  "display_name": "EU West Production Compute Provider",
+  "version": "2.1.0",
+
+  # Mutual TLS certificate presented at connection level
+  # DCM extracts the certificate fingerprint from the TLS handshake
+
+  "sovereignty_declaration": { ... },
+  "accreditations": [ ... ],
+  "capabilities": { ... },              # per-type capability declaration
+  "health_endpoint": "https://provider.example.com/health",
+  "delivery_endpoint": "https://provider.example.com/dispatch"
+}
+
+Response 202 Accepted:
+{
+  "registration_uuid": "",
+  "status": "VALIDATING",
+  "token_recognized": true,
+  "auto_approval_eligible": true,
+  "estimated_activation": ""
+}
+```
+
+### 4.3 Step 2 — Validation (automated)
+
+DCM runs automated validation checks. All must pass before advancing to PENDING_APPROVAL:
+
+```
+Validation checks:
+  V1: Provider type permitted in active profile
+      → Check Provider Type Registry: enabled_in_profiles includes active posture
+      → FAIL: REJECTED with reason "provider_type_not_enabled_in_profile"
+
+  V2: Governance Matrix pre-check
+      → Evaluate matrix: is a provider of this type, in this zone, with these
+        accreditations, permitted to register?
+      → FAIL: REJECTED with reason "governance_matrix_denied" + rule_uuid
+
+  V3: Registration token validation (if provided)
+      → Token exists and not expired
+      → Token matches provider_type_id and handle pattern
+      → Token not already used
+      → FAIL: Token invalid; fall back to non-token approval method
+
+  V4: Certificate validation
+      → mTLS certificate presented and valid
+      → Certificate chain acceptable (registered CA or pinned self-signed)
+      → Certificate not in revocation list
+      → FAIL: REJECTED with reason "certificate_invalid"
+
+  V5: Sovereignty declaration completeness
+      → Required fields present (if profile requires declaration)
+      → Jurisdiction codes valid
+      → FAIL: REJECTED with reason "sovereignty_declaration_incomplete"
+
+  V6: Capability declaration consistency
+      → Declared capabilities consistent with provider type
+      → No contradictory declarations
+      → FAIL: REJECTED with reason "capability_declaration_invalid"
+
+  V7: Health endpoint reachability
+      → DCM contacts health_endpoint
+      → Provider responds with valid health payload
+      → FAIL: status → PENDING_APPROVAL with warning (profile may require passing)
+
+  V8: Accreditation submission check
+      → If profile requires accreditation submission: at least one accreditation present
+      → Accreditation type meets profile minimum
+      → FAIL: REJECTED with reason "accreditation_insufficient"
+```
+
+### 4.4 Step 3 — Approval
+
+Approval flow depends on effective_approval_method:
+
+**auto:** Registration immediately advances to ACTIVE after validation passes.
+
+**reviewed:**
+```
+Registration enters PENDING_APPROVAL
+Platform admin notification dispatched (urgency: medium)
+Platform admin reviews in Admin API or Flow GUI:
+  GET /api/v1/admin/registrations/pending
+  POST /api/v1/admin/registrations/{registration_uuid}:approve
+  POST /api/v1/admin/registrations/{registration_uuid}:reject
+On approval: → ACTIVE
+On rejection: → REJECTED with required reason field
+On timeout (approval_timeout): → REJECTED with reason "approval_timeout"
+```
+
+**verified:**
+```
+Registration enters PENDING_APPROVAL
+Two independent platform admins must approve
+First approval: recorded; notification sent to other admins for second approval
+Second approval by different actor: → ACTIVE
+Same actor cannot approve twice
+On timeout: → REJECTED
+```
+
+**authorized:**
+```
+Registration enters PENDING_APPROVAL
+Authority group notified (all members)
+Members vote via Admin API within declared quorum window
+Quorum reached: → ACTIVE
+Quorum not reached within approval_timeout: → REJECTED
+```
+
+### 4.5 Step 4 — Activation
+
+On ACTIVE status:
+- Provider enters the DCM provider registry
+- Governance matrix rules are re-evaluated with this provider now active
+- Capacity monitoring begins (if Service or Information Provider)
+- Health check polling begins
+- Certificate rotation schedule established
+- Activation audit record written: PROVIDER_ACTIVATED
+- Notification: platform admin + Tenant admins (if Tenant-scoped provider)
+
+---
+
+## 5. Per-Type Capability Declaration Schemas
+
+### 5.1 Service Provider Capabilities
+
+```yaml
+service_provider_capabilities:
+  resource_types:
+    - resource_type_fqn: Compute.VirtualMachine
+      resource_type_spec_version: "2.1.0"
+      catalog_item_uuid: 
+      availability_zones: [eu-west-1a, eu-west-1b]
+      max_instances: 1000
+
+  capacity_model:
+    reporting_method: reserve_query | static_declaration | both
+    reserve_query_endpoint: /reserve
+    reserve_query_timeout: PT10S
+    static_capacity:
+      Compute.VirtualMachine: 500
+
+  cancellation:
+    supports_cancellation: true
+    cancellation_supported_during: [DISPATCHED, PROVISIONING]
+    partial_rollback_possible: true
+
+  discovery:
+    supports_discovery: true
+    discovery_endpoint: /discover
+    discovery_method: api_query | passive_event | hybrid
+    supports_incremental_discovery: true
+
+  monitoring:
+    # Prometheus metrics endpoint — required for 1.0 readiness
+    metrics_endpoint: /metrics            # must return Prometheus text format
+    metrics_port: 8080                    # or same as operator endpoint
+    
+    # Required metric families (must be present at activation):
+    required_metrics:
+      - dcm_provider_dispatches_total      # {resource_type, outcome}
+      - dcm_provider_dispatch_duration_seconds  # {resource_type, quantile}
+      - dcm_provider_realizations_total    # {resource_type, status}
+      - dcm_provider_health_status         # 1=healthy, 0=unhealthy
+    
+    # Optional but recommended:
+    optional_metrics:
+      - dcm_provider_queue_depth           # pending dispatch requests
+      - dcm_provider_capacity_remaining    # {resource_type}
+    
+    # AEP.DEV linting — required for 1.0 readiness gate
+    aep_linting:
+      passes_aep_linting: true            # must pass aep.dev linter before activation
+      linting_report_ref:            # link to linting report
+    
+    # Tenant metadata endpoint — required for multi-tenant readiness
+    tenant_metadata_endpoint: /api/v1/tenants/{tenant_uuid}/metadata
+    # Returns: usage by tenant, quota consumed, active resources by type
+
+  naturalization:
+    target_format: openstack_nova | vmware_vsphere | custom
+    custom_schema_ref: 
+
+  cost_metadata:
+    capex_allocation_per_unit: 12.50
+    opex_per_unit_per_hour: 0.28
+    currency: USD
+    cost_data_dynamic_source: null | 
+
+  data_handling:
+    max_data_classification_accepted: restricted
+    phi_capable: false                   # true requires HIPAA BAA accreditation
+    pci_capable: false
+```
+
+### 5.2 Information Provider Capabilities
+
+```yaml
+information_provider_capabilities:
+  data_domains:
+    - domain: business_data
+      data_types: [business_unit, cost_center, product_owner]
+      authority_level: primary | secondary | supplementary
+      schema_version: "1.0.0"
+      query_endpoint: /query
+      write_back_supported: false
+
+  query_capacity:
+    max_queries_per_second: 100
+    rate_limit_window: 60s
+    burst_capacity: 200
+
+  confidence_model:
+    data_freshness_sla: PT1H
+    corroboration_sources: [cmdb, hr_system]
+
+  caching:
+    cacheable: true
+    cache_ttl: PT15M
+    cache_invalidation_webhook: /invalidate
+```
+
+### 5.3 data store Capabilities
+
+```yaml
+(prescribed infrastructure)_capabilities:
+  store_types_supported:
+    - store_type: gitops
+      branch_per_request: true
+      pr_semantics: true
+      search_index_companion: true
+    - store_type: write_once_snapshot
+      entity_uuid_keyed: true
+      hash_chain_integrity: true
+      point_in_time_query: true
+
+  consistency:
+    guarantee: strong | eventual | bounded_staleness
+    bounded_staleness_max: PT5M
+
+  replication:
+    geo_replicated: true
+    replication_regions: [eu-west, eu-north]
+    synchronous_replication: true
+
+  encryption:
+    at_rest: AES-256
+    hsm_backed: false
+    key_management: provider_managed | customer_managed | hsm
+
+  retention:
+    supports_retention_policy: true
+    minimum_retention: P1Y
+    maximum_retention: P10Y
+    tamper_evident: true
+```
+
+### 5.4 External Policy Evaluator Capabilities
+
+```yaml
+external_policy_evaluation_capabilities:
+  mode: 1 | 2 | 3 | 4
+  policy_types_supported:
+    - gatekeeper
+    - validation
+    - transformation
+    - recovery
+    - orchestration_flow
+
+  framework: opa | cedar | custom
+  rego_version: "1.0"                # for OPA providers
+
+  # Internal/External specific
+  remote_endpoint: https://policy.example.com/evaluate
+  endpoint_sovereignty_zone: eu-west-sovereign
+  evaluation_latency_p95: PT200MS
+  supports_bundle_push: true
+  supports_bundle_pull: true
+
+  shadow_mode_supported: true
+  test_harness_endpoint: /test
+```
+
+### 5.5 Auth Provider Capabilities
+
+```yaml
+auth_provider_capabilities:
+  authentication_modes:
+    - api_key
+    - ldap
+    - oidc
+    - oidc_mfa
+    - saml
+    - mtls
+    - hardware_token
+    - hardware_token_mfa
+
+  mfa_methods:
+    - totp
+    - push_notification
+    - hardware_token
+
+  rbac_model: flat | hierarchical | attribute_based
+  external_idp_integration: true
+  idp_protocols: [oidc, saml, ldap]
+
+  token_lifetime_config:
+    default_lifetime: PT1H
+    min_lifetime: PT5M
+    max_lifetime: PT8H
+    step_up_supported: true
+
+  builtin: false                       # true for DCM's built-in auth provider
+```
+
+### 5.6 notification service Capabilities
+
+```yaml
+service_provider_capabilities:
+  delivery_channels:
+    - channel_type: slack
+      supports_threading: true
+      supports_urgency_routing: true
+      config_schema_ref: 
+    - channel_type: pagerduty
+      supports_escalation: true
+      config_schema_ref: 
+    - channel_type: webhook
+      protocols: [https]
+      auth_modes: [hmac_sha256, mtls, bearer]
+      config_schema_ref: 
+    - channel_type: email
+      html_supported: true
+
+  delivery_guarantees:
+    at_least_once: true
+    idempotency_key: notification_uuid
+    max_delivery_latency_seconds: 30
+    retry_policy:
+      max_attempts: 7
+      backoff: exponential
+      on_exhaustion: dead_letter
+
+  sovereignty_aware_delivery: true    # checks endpoint jurisdiction before delivery
+```
+
+### 5.7 credential management service Capabilities
+
+```yaml
+service_provider_capabilities:
+  credential_types:
+    - api_key
+    - x509_certificate
+    - ssh_key
+    - service_account_token
+    - database_password
+    - hsm_backed_key
+
+  secret_engines:
+    - vault
+    - aws_secrets_manager
+    - azure_key_vault
+    - gcp_secret_manager
+
+  rotation_support: true
+  hsm_backed: false
+  fips_140_2_level: 1 | 2 | 3        # for sovereign deployments
+  dynamic_secrets: true               # generate credentials on demand
+```
+
+### 5.8 event routing service Capabilities
+
+```yaml
+(optional infrastructure)_capabilities:
+  protocols: [kafka, amqp, mqtt, grpc]
+  persistence: true
+  durability: at_least_once | exactly_once
+  max_throughput_msg_per_sec: 100000
+  retention:
+    message_retention: P7D
+    retention_configurable: true
+  external_endpoints: false           # true if messages can leave sovereignty boundary
+  encryption_in_transit: TLS-1.3
+  encryption_at_rest: AES-256
+```
+
+### 5.9 compound service definition Capabilities
+
+```yaml
+compound service_capabilities:
+  constituent_provider_types:
+    - service_provider
+    - information_provider
+
+  composition_model: sequential | parallel | conditional
+  partial_delivery_supported: true
+  compensation_supported: true
+
+  resource_types_composed:
+    - resource_type_fqn: ApplicationStack.WebApp
+      constituent_resource_types:
+        - Compute.VirtualMachine
+        - Network.IPAddress
+        - DNS.Record
+        - Network.LoadBalancer
+```
+
+---
+
+## 6. Federated Trust Configuration
+
+### 6.1 Federation Trust Postures
+
+| Posture | Description | Operations permitted |
+|---------|-------------|---------------------|
+| `verified` | Manually verified and approved by local platform admin | Full declared scope per tunnel authorization |
+| `vouched` | Introduced through a trusted Hub DCM | Vouching authority's declared scope; cannot exceed voucher's scope |
+| `provisional` | Cryptographically verified but not yet manually approved | catalog_query only (if profile permits) |
+
+### 6.2 Federation Trust Registration Flow
+
+```
+Remote DCM requests federation peering
+  │
+  ▼ Cryptographic verification (always):
+  │   mTLS certificate validation
+  │   Certificate not in revocation list
+  │   Certificate signed by acceptable CA
+
+  ▼ Governance matrix pre-check:
+  │   Is federation with this peer's jurisdiction/accreditation permitted?
+
+  ▼ Trust posture determination:
+  │   Prior record of this remote UUID? → verified or vouched (per prior record)
+  │   No prior record → provisional
+
+  ▼ Approval flow (per profile):
+  │   dev:      provisional auto-promoted to verified (if governance matrix permits)
+  │   standard: reviewed for verified promotion; provisional gets limited scope
+  │   prod:     verified for verified promotion; no provisional operations
+  │   fsi:      verified + accreditation check; no provisional
+  │   sovereign: authorized_approval + hardware attestation; no provisional
+
+  ▼ Scope assignment per trust posture
+
+  ▼ Tunnel established with governance matrix enforcement
+```
+
+### 6.3 Profile Federation Trust Policy
+
+```yaml
+profile_federation_policy:
+  minimal:
+    permitted_trust_postures: [verified, vouched, provisional]
+    auto_promote_provisional: true
+    cross_jurisdiction_permitted: true
+    accreditation_required_for_federation: false
+
+  dev:
+    permitted_trust_postures: [verified, vouched, provisional]
+    auto_promote_provisional: true
+    provisional_permitted_operations: [catalog_query, resource_query]
+    cross_jurisdiction_permitted: true
+
+  standard:
+    permitted_trust_postures: [verified, vouched]
+    approval_method_for_verified: reviewed
+    cross_jurisdiction_permitted: true
+    accreditation_required_for_federation: false
+
+  prod:
+    permitted_trust_postures: [verified]
+    approval_method_for_verified: verified
+    cross_jurisdiction_permitted: true
+    accreditation_required_for_federation: false
+
+  fsi:
+    permitted_trust_postures: [verified]
+    approval_method_for_verified: verified
+    cross_jurisdiction_permitted: false
+    accreditation_required_for_federation: true
+    minimum_peer_accreditation: third_party
+    re_verification_interval: PT8H
+
+  sovereign:
+    permitted_trust_postures: [verified]
+    approval_method_for_verified: authorized
+    cross_jurisdiction_permitted: false
+    accreditation_required_for_federation: true
+    minimum_peer_accreditation: sovereign_authorization
+    hardware_attestation_required: true
+    data_classification_boundary: internal
+    re_verification_interval: PT4H
+```
+
+---
+
+## 7. Ongoing Lifecycle After Activation
+
+### 7.1 Health Monitoring
+
+```
+DCM polls provider health endpoint every health_check_interval
+  │
+  ├── Response: healthy → no action; next poll scheduled
+  ├── Response: degraded → DCM updates capacity rating; reduces routing preference
+  ├── No response (1 failure) → warning; retry at shorter interval
+  ├── No response (failure_threshold reached) → provider status → DEGRADED
+  │   Notification: platform admin (urgency: high)
+  │   New requests no longer routed to this provider
+  └── No response (2× failure_threshold) → provider status → UNAVAILABLE
+      Active entities checked; drift detection triggered
+      Platform admin notification (urgency: critical)
+```
+
+### 7.2 Certificate Rotation
+
+```yaml
+certificate_rotation:
+  rotation_interval: P90D           # profile-governed default
+  transition_window: P7D            # old cert valid during transition
+  pre_rotation_warning: P14D        # warn provider P14D before expiry
+
+# Rotation flow:
+POST /api/v1/provider/certificates:rotate
+{
+  "new_certificate_pem": "",
+  "transition_window": "P7D"
+}
+# DCM accepts both old and new certificates during transition window
+# After transition window: old certificate rejected
+```
+
+### 7.3 Capability Updates
+
+Providers may update their capability declarations (new resource types, updated capacity models, new accreditations). Capability updates go through a simplified registration amendment flow:
+
+```
+POST /api/v1/provider/capabilities/update
+{
+  "amendment_type": "add_resource_type | remove_resource_type | update_capacity | add_accreditation",
+  "changes": { ... }
+}
+
+→ VALIDATING (automated checks only)
+→ PENDING_APPROVAL (if amendment_type is add_resource_type or sovereignty change)
+→ ACTIVE (capability declarations updated)
+```
+
+### 7.4 Deregistration
+
+**Graceful deregistration:**
+```
+Provider submits deregistration intent
+DCM checks: active entities hosted at this provider
+If active entities > 0:
+  Decision required: migrate_entities | decommission_entities | reject_deregistration
+Platform admin approves deregistration plan
+Provider enters DEREGISTERING state
+Entity migration or decommission completes
+Provider status → DEREGISTERED
+```
+
+**Forced deregistration:**
+```
+POST /api/v1/admin/providers/{provider_uuid}/force-deregister
+Role: platform_admin
+Requires: verified (fsi/sovereign: authorized)
+
+Immediate effect:
+  Provider status → FORCED_DEREGISTERED
+  All active entities → INDETERMINATE_REALIZATION
+  Governance matrix re-evaluated for all affected entities
+  Recovery policy fires: DRIFT_RECONCILE or NOTIFY_AND_WAIT per profile
+```
+
+---
+
+### 7.2 Provider 1.0 Readiness Gates
+
+Before a Service Provider can be activated in `standard`, `prod`, `fsi`, or `sovereign`
+profiles, the following readiness gates must pass. These align with the DCM roadmap's
+1.0 criteria for Service Provider deployment:
+
+| Gate | Requirement | Profiles Required |
+|------|------------|-------------------|
+| `GATE-SP-01` | Simple OpenAPI Spec — declared at registration, URL reachable | all |
+| `GATE-SP-02` | Healthy API — health endpoint returns `{"status": "healthy"}` at activation | all |
+| `GATE-SP-03` | State Management — implements realized_state_push callback | all |
+| `GATE-SP-04` | Tenant Metadata — endpoint declared or implemented | standard+ |
+| `GATE-SP-05` | Prometheus Metrics — required metric families present at declared endpoint | standard+ |
+| `GATE-SP-06` | AEP.DEV Linting — OpenAPI spec passes AEP linter with no errors | standard+ |
+| `GATE-SP-07` | Multi-Tenant Ready — accepts tenant_uuid in all dispatch payloads | standard+ |
+
+DCM evaluates readiness gates automatically during the approval pipeline. A provider
+that fails a gate is rejected with a `READINESS_GATE_FAILED` error listing which
+gates failed and what is needed to pass.
+
+**Required metric families (GATE-SP-05):**
+
+```
+dcm_provider_dispatches_total{resource_type, outcome}
+dcm_provider_dispatch_duration_seconds{resource_type, quantile}
+dcm_provider_realizations_total{resource_type, status}
+dcm_provider_health_status   # 1=healthy, 0=unhealthy/degraded
+```
+
+**AEP linting (GATE-SP-06):**
+Run the AEP linter against the provider's OpenAPI spec before registration.
+Common failures: slash-verb paths instead of colon syntax, missing page_size
+on list endpoints, 202 responses without Operation resource on async operations.
+The linting report URL should be included in the monitoring capability declaration.
+
+---
+
+
+## 8. Error Model
+
+| Error Code | Meaning |
+|-----------|---------|
+| `provider_type_not_enabled` | Provider type not permitted in active profile |
+| `governance_matrix_denied` | Governance matrix pre-check denied registration |
+| `certificate_invalid` | mTLS certificate invalid or not from acceptable CA |
+| `token_invalid` | Registration token expired, used, or type mismatch |
+| `token_insufficient_scope` | Token present but does not grant required approval level |
+| `sovereignty_declaration_incomplete` | Required sovereignty fields missing |
+| `accreditation_insufficient` | Active profile requires higher accreditation type |
+| `capability_declaration_invalid` | Capability declarations internally inconsistent |
+| `approval_timeout` | Registration not approved within approval_timeout period |
+| `health_check_failed` | Provider health endpoint unreachable during validation |
+| `duplicate_handle` | A provider with this handle already exists in active status |
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/specifications/dcm-rhdh-integration-spec.md b/content/docs/architecture/specifications/dcm-rhdh-integration-spec.md
new file mode 100644
index 0000000..708023b
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-rhdh-integration-spec.md
@@ -0,0 +1,623 @@
+# DCM Red Hat Developer Hub Integration Specification
+
+**Document Status:** 🔄 In Progress
+**Document Type:** Specification — RHDH / Backstage Integration Architecture
+**Related Documents:** [Consumer GUI Specification](dcm-consumer-gui-spec.md) | [Admin GUI Specification](dcm-admin-gui-spec.md) | [Provider GUI Specification](dcm-provider-gui-spec.md) | [Consumer API Specification](consumer-api-spec.md) | [Auth Providers](../data-model/19-auth-providers.md) | [Standards Catalog](../data-model/40-standards-catalog.md)
+
+> **Status:** Draft — Ready for implementation feedback
+>
+> This specification defines the complete integration between DCM and Red Hat Developer Hub (RHDH) or upstream Backstage. It covers plugin architecture, entity model, auth delegation, permission mapping, Software Template auto-generation, and deployment.
+
+---
+
+## 1. Integration Architecture Overview
+
+### 1.1 Layering Model
+
+DCM and RHDH are separate systems that integrate at well-defined boundaries. DCM remains authoritative for all infrastructure state; RHDH provides the developer experience layer.
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│                     RHDH / Backstage                            │
+│   Software Catalog  │  Scaffolder  │  TechDocs  │  Search      │
+│   ─────────────────────────────────────────────────────────────│
+│   @dcm/plugin suite (Dynamic Plugins)                           │
+│   ├── Entity Provider  ← pulls from DCM API                     │
+│   ├── Scaffolder Actions  → pushes to DCM API                   │
+│   ├── Frontend Plugin  ← reads DCM API via proxy                │
+│   ├── Permission Policy  ↔ DCM roles                            │
+│   └── Auth Bridge  ↔ DCM Auth Provider (OIDC token exchange)   │
+└──────────────────────────────┬──────────────────────────────────┘
+                               │ DCM Consumer API (HTTPS)
+                               │ X-DCM-Tenant from RHDH group context
+                               ▼
+┌─────────────────────────────────────────────────────────────────┐
+│                    DCM Control Plane                            │
+│   Consumer API  │  Policy Engine  │  Scoring  │  Providers     │
+│   Stores: Intent, Requested, Realized, Discovered, Audit       │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**DCM is authoritative for:** resource state, policy decisions, audit trail, realized data, cost, drift detection.
+
+**RHDH is authoritative for:** developer experience, documentation, search index, Software Templates, organization/group model.
+
+### 1.2 Plugin Packages
+
+The DCM RHDH integration is delivered as six npm packages, all loadable as RHDH Dynamic Plugins:
+
+| Package | Type | Purpose |
+|---------|------|---------|
+| `@dcm/backstage-plugin` | Frontend | Nav, pages, entity tabs, drawers |
+| `@dcm/backstage-plugin-backend` | Backend | API proxy, SSE relay, auth middleware |
+| `@dcm/backstage-plugin-catalog-backend` | Backend | Entity provider, catalog processor |
+| `@dcm/backstage-plugin-scaffolder-backend` | Backend | Custom scaffolder actions |
+| `@dcm/backstage-permission-policy` | Backend | DCM → Backstage permission bridge |
+| `@dcm/backstage-plugin-auth-backend` | Backend | RHDH as DCM Auth Provider (optional) |
+
+---
+
+## 2. Authentication and Token Flow
+
+### 2.1 RHDH as DCM Auth Provider
+
+The recommended pattern: configure RHDH (Keycloak/RHSSO) as the Auth Provider for both RHDH and DCM. DCM trusts OIDC tokens issued by the same IdP that RHDH uses.
+
+```
+User authenticates → RHDH (via Keycloak/RHSSO OIDC)
+  │
+  RHDH issues Backstage session token + OIDC access token
+  │
+  DCM plugin backend receives OIDC access token
+  │
+  DCM plugin backend presents OIDC token to DCM Consumer API
+  (/api/v1/auth/token with grant_type: oidc_token_exchange)
+  │
+  DCM issues its own session token (JWT with actor_uuid, roles, tenant_scope)
+  │
+  DCM session token cached in RHDH backend (keyed by Backstage user entity ref)
+  │
+  All subsequent DCM API calls use DCM session token
+```
+
+DCM is registered as an OIDC Auth Provider with the same issuer as RHDH's Keycloak:
+
+```yaml
+# DCM Auth Provider registration
+auth_provider_registration:
+  provider_type: auth_provider
+  auth_method: oidc
+  oidc_config:
+    issuer: https://keycloak.corp.com/realms/corporate
+    client_id: dcm-api
+    trust_level: authoritative
+  role_mapping:
+    group_role_map:
+      - external_group: dcm-consumers
+        dcm_role: consumer
+      - external_group: dcm-approvers
+        dcm_role: approver
+      - external_group: dcm-platform-admins
+        dcm_role: platform_admin
+```
+
+### 2.2 Token Lifetime and Refresh
+
+- RHDH session: governed by Keycloak session settings (typically PT8H)
+- DCM session token: PT30M (prod profile) — refreshed transparently by RHDH backend plugin
+- DCM plugin backend maintains a token cache: `backstage_user_ref → dcm_session_token`
+- Token refresh: triggered when DCM session token is within PT5M of expiry
+
+### 2.3 Service Account Token for Entity Provider
+
+The `@dcm/backstage-plugin-catalog-backend` entity provider runs as a background service, not on behalf of a user. It uses a DCM service account:
+
+```yaml
+# DCM service account for RHDH catalog entity provider
+service_account:
+  handle: rhdh-catalog-provider
+  roles: [catalog_reader]          # read-only: catalog items + realized entities
+  credential_type: api_key
+  rotation: P30D
+```
+
+The service account API key is stored as a Kubernetes Secret and mounted into the RHDH backend pod.
+
+### 2.4 Tenancy from RHDH Group Context
+
+The active RHDH namespace/group context maps to `X-DCM-Tenant`:
+
+```typescript
+// In @dcm/backstage-plugin-backend — DCM API proxy middleware
+const groupContext = request.headers['x-backstage-namespace'] || 
+                     userEntity.spec?.memberOf?.[0];
+const tenantUuid = await dcmTenantCache.resolveFromGroup(groupContext);
+proxyRequest.headers['X-DCM-Tenant'] = tenantUuid;
+```
+
+Tenant UUID resolution: `@dcm/backstage-plugin-catalog-backend` maintains a `RHDH Group ref → DCM Tenant UUID` mapping, populated during entity sync.
+
+---
+
+## 3. Entity Model
+
+### 3.1 Custom Entity Kinds
+
+DCM introduces two custom Backstage entity kinds:
+
+#### DCMService (catalog item)
+
+Represents a DCM service catalog item — something a user can request.
+
+```yaml
+apiVersion: dcm.io/v1alpha1
+kind: DCMService
+metadata:
+  name: compute-vm-standard
+  namespace: dcm-catalog           # shared namespace for all DCM catalog items
+  annotations:
+    dcm.io/catalog-item-uuid: ""
+    dcm.io/resource-type-fqn: "Compute.VirtualMachine"
+    backstage.io/techdocs-ref: url:/docs/compute-vm
+  tags: [compute, infrastructure, self-service]
+spec:
+  type: dcm-service
+  lifecycle: production
+  owner: group:platform-team
+  providedBy:
+    providerHandle: k8s-operator-prod
+    providerType: service_provider
+  fieldSchema:
+    # JSON Schema for the request form — auto-populated from DCM catalog item
+    $ref: "dcm-api://catalog//schema"
+  costEstimate:
+    currency: USD
+    estimatedMonthly: 240
+    billingDimensions: [cpu_count, memory_gb]
+  availability:
+    quotaRemaining: 4              # computed at sync time
+    sla: "99.9%"
+```
+
+#### DCMResource (realized entity)
+
+Represents a realized DCM resource — something that exists.
+
+```yaml
+apiVersion: dcm.io/v1alpha1
+kind: DCMResource
+metadata:
+  name: payments-api-server-01
+  namespace: payments-team         # namespace = DCM tenant
+  annotations:
+    dcm.io/entity-uuid: ""
+    dcm.io/resource-type-fqn: "Compute.VirtualMachine"
+    dcm.io/provider-uuid: ""
+    dcm.io/request-uuid: ""  # the request that created this
+    backstage.io/techdocs-ref: url:
+spec:
+  type: Compute.VirtualMachine
+  lifecycle: production
+  owner: group:payments-team
+  system: payments-platform
+  realizedFields:                  # key fields from Realized State
+    primary_ip: "10.42.0.105"
+    hostname: "payments-api-server-01.corp.internal"
+    cpu_count: 4
+    memory_gb: 16
+    os_family: rhel
+  lifecycleState: OPERATIONAL      # DCM lifecycle state
+  ttlExpiresAt: "2026-09-15"
+  driftStatus: none                # none | minor | moderate | significant | critical
+  providerHandle: k8s-operator-prod
+  dependsOn:
+    - dcmresource:payments-team/payments-db-01
+```
+
+### 3.2 Entity Provider
+
+`@dcm/backstage-plugin-catalog-backend` implements a `EntityProvider` that:
+
+1. On startup: fetches all DCM catalog items → emits `DCMService` entities
+2. On startup: fetches all realized entities for each tenant → emits `DCMResource` entities
+3. On schedule (default: every PT5M): polls for changes, emits delta mutations
+4. On `dcm:catalog:refresh` scaffolder action: triggers immediate refresh for specific entity
+
+```typescript
+class DcmEntityProvider implements EntityProvider {
+  async refresh(logger: Logger): Promise {
+    // Fetch catalog items
+    const catalogItems = await this.dcmApi.getCatalogItems();
+    const serviceEntities = catalogItems.map(toDCMServiceEntity);
+    
+    // Fetch realized resources per tenant
+    const tenants = await this.dcmApi.getTenants(); // admin service account
+    const resourceEntities = (await Promise.all(
+      tenants.map(t => this.dcmApi.getResources(t.uuid))
+    )).flat().map(toDCMResourceEntity);
+    
+    await this.connection.applyMutation({
+      type: 'full',
+      entities: [...serviceEntities, ...resourceEntities],
+    });
+  }
+}
+```
+
+### 3.3 Catalog Processor
+
+Handles entity validation, relationship resolution, and annotation enrichment for `DCMService` and `DCMResource` kinds.
+
+---
+
+## 4. Software Template Auto-Generation
+
+### 4.1 Generation Model
+
+`@dcm/backstage-plugin-catalog-backend` automatically generates Backstage Software Templates from DCM catalog items. No manual template authoring is needed when new resource types appear.
+
+Generation pipeline:
+```
+GET /api/v1/catalog → DCM catalog items
+  │
+  For each catalog item:
+  │  GET /api/v1/catalog/{uuid} → field schema
+  │
+  Transform:
+  │  field schema → Backstage template parameters (JSON Schema compatible)
+  │  catalog item metadata → template metadata
+  │  provider info → template tags
+  │
+  Emit as Backstage Template entity
+```
+
+### 4.2 Schema Transformation Rules
+
+| DCM field type | Backstage ui:widget | Notes |
+|---------------|---------------------|-------|
+| `enum` list | `select` | Options from DCM enum |
+| `string` with pattern | `text` + pattern validation | Pattern in JSON Schema |
+| `integer` range | `number` or `select` | Select if < 10 options |
+| `boolean` | `checkbox` | — |
+| `uuid` reference | `dcm:EntityPicker` | Custom picker component |
+| `duration` (ISO 8601) | `dcm:DurationPicker` | Custom picker |
+| `datetime` | `datetime` | Standard Backstage widget |
+| Injected field (read-only) | `readonly` | Shows source layer in tooltip |
+
+### 4.3 Multi-Step Template Structure
+
+All generated templates follow a consistent multi-step structure:
+
+```
+Step 1: "Configure [Service Name]"     ← DCM required fields
+Step 2: "Options"                      ← Optional DCM fields + scheduling
+Step 3: "Scheduling (Optional)"        ← dispatch: immediate/at/window/recurring
+Step 4: "Review"                       ← cost estimate + pre-flight check
+  ← Submit ←
+Step 5: "Provisioning..."              ← dcm:request:submit + dcm:request:wait (live log)
+Step 6: "Complete"                     ← link to entity in catalog + resource URL
+```
+
+---
+
+## 5. Scaffolder Actions Reference
+
+All actions in `@dcm/backstage-plugin-scaffolder-backend`:
+
+### `dcm:request:estimate`
+
+```typescript
+input:
+  catalogItemUuid: string        // DCM catalog item UUID
+  fields: object                 // field values from template parameters
+  tenantUuid?: string           // defaults to RHDH group context
+
+output:
+  estimatedMonthlyCost: number
+  currency: string
+  breakdown: Array<{dimension: string, cost: number}>
+  quotaCheck: {passes: boolean, remaining: number}
+  policyPreCheck: {passes: boolean, warnings: string[]}
+```
+
+**Purpose:** Called during the Review step. Provides cost estimate, quota check, and policy pre-flight. Does not submit the request.
+
+### `dcm:request:submit`
+
+```typescript
+input:
+  catalogItemUuid: string
+  fields: object
+  schedule?: {dispatch: 'immediate'|'at'|'window'|'recurring', notBefore?: string, notAfter?: string, windowId?: string}
+  dependsOn?: Array<{requestUuid: string, waitFor: string, injectFields?: ...}>
+
+output:
+  requestUuid: string
+  entityUuid: string             // UUID the resource will have when realized
+  status: string                 // typically ACKNOWLEDGED
+  requestUrl: string             // link to request in DCM consumer portal
+```
+
+### `dcm:request:wait`
+
+```typescript
+input:
+  requestUuid: string
+  timeoutMinutes?: number        // default: 30
+  pollIntervalSeconds?: number   // default: 5; uses SSE if available
+
+output:
+  status: 'REALIZED'|'FAILED'|'CANCELLED'
+  entityUuid: string
+  entityUrl: string              // link to entity in RHDH catalog
+  realizedFields: object         // key fields from provider (IP, hostname, etc.)
+  failureReason?: string
+```
+
+Streams status updates to the Scaffolder log panel:
+```
+[LOG] 09:01:05  Status: PROVISIONING — Step 3/7: Configuring network interfaces
+[LOG] 09:03:12  ✅ REALIZED — IP: 10.42.0.105, Hostname: payments-api-server-01.corp.internal
+```
+
+### `dcm:request:group`
+
+```typescript
+input:
+  groupHandle?: string
+  onFailure?: 'cancel_remaining'|'continue'
+  timeout?: string               // ISO 8601 duration
+  requests: Array<{
+    ref: string,                 // local reference within this submission
+    catalogItemUuid: string,
+    fields: object,
+    dependsOn?: Array<{ref: string, waitFor: string, injectFields?: ...}>
+  }>
+
+output:
+  groupUuid: string
+  requests: Array<{ref: string, requestUuid: string, entityUuid: string}>
+  groupUrl: string
+```
+
+### `dcm:catalog:refresh`
+
+```typescript
+input:
+  entityUuid: string             // DCM entity UUID to refresh in RHDH catalog
+
+output:
+  entityRef: string              // Backstage entity ref: dcmresource:/
+  entityUrl: string              // URL to entity page in RHDH
+```
+
+Triggers immediate re-poll of the entity provider for the specified entity. The entity appears in RHDH catalog within PT30S of REALIZED status.
+
+---
+
+## 6. Permission Framework Integration
+
+### 6.1 DCM Permissions in Backstage
+
+`@dcm/backstage-permission-policy` defines DCM permissions in Backstage permission framework terms:
+
+```typescript
+// DCM permission definitions
+export const dcmPermissions = {
+  // Resource permissions
+  resourceRead:      createPermission({name: 'dcm.resource.read', attributes: {action: 'read'}}),
+  resourceUpdate:    createPermission({name: 'dcm.resource.update', attributes: {action: 'update'}}),
+  resourceDelete:    createPermission({name: 'dcm.resource.delete', attributes: {action: 'delete'}}),
+  
+  // Catalog permissions  
+  catalogRequest:    createPermission({name: 'dcm.catalog.request', attributes: {action: 'create'}}),
+  
+  // Approval permissions
+  approvalVote:      createPermission({name: 'dcm.approval.vote', attributes: {action: 'update'}}),
+  
+  // Admin permissions
+  tenantManage:      createPermission({name: 'dcm.tenant.manage', attributes: {action: 'update'}}),
+  providerManage:    createPermission({name: 'dcm.provider.manage', attributes: {action: 'update'}}),
+};
+```
+
+### 6.2 Role Mapping
+
+The permission policy maps Backstage group membership to DCM permission grants:
+
+```typescript
+class DcmPermissionPolicy implements PermissionPolicy {
+  async handle(request: PolicyQuery, user?: BackstageIdentityResponse) {
+    const groups = user?.identity.ownershipEntityRefs ?? [];
+    
+    // Basic consumer permissions — all authenticated users
+    if (isAuthenticated(user)) {
+      if (DCM_READ_PERMISSIONS.includes(request.permission.name)) {
+        return { result: AuthorizeResult.ALLOW };
+      }
+    }
+    
+    // Role-based grants
+    if (groups.includes('group:dcm-approvers')) {
+      if (request.permission.name === 'dcm.approval.vote') {
+        return { result: AuthorizeResult.ALLOW };
+      }
+    }
+    
+    if (groups.includes('group:dcm-platform-admins')) {
+      return { result: AuthorizeResult.ALLOW }; // all permissions
+    }
+    
+    return { result: AuthorizeResult.DENY };
+  }
+}
+```
+
+### 6.3 RHDH RBAC Plugin Integration
+
+The RHDH RBAC plugin provides a no-code UI for managing role assignments. DCM roles are represented as RHDH group memberships:
+
+```
+RHDH RBAC UI:
+  Role: dcm-consumers       → Group: all-authenticated-users
+  Role: dcm-approvers       → Groups: [payments-leads, platform-approvers]
+  Role: dcm-platform-admins → Groups: [platform-team]
+  Role: dcm-contributors    → Groups: [policy-authors, power-users]
+```
+
+Changes to group membership propagate to DCM via SCIM 2.0 (if configured) or OIDC group claims on next login.
+
+---
+
+## 7. Deployment
+
+### 7.1 Dynamic Plugin Loading
+
+All DCM plugins are deployed as RHDH Dynamic Plugins — no RHDH image rebuild required:
+
+```yaml
+# RHDH app-config.yaml additions
+dynamicPlugins:
+  frontend:
+    dcm.backstage-plugin:
+      disabled: false
+  backend:
+    dcm.backstage-plugin-backend:
+      disabled: false
+    dcm.backstage-plugin-catalog-backend:
+      disabled: false
+    dcm.backstage-plugin-scaffolder-backend:
+      disabled: false
+    dcm.backstage-permission-policy:
+      disabled: false
+```
+
+Plugins loaded from OCI registry or npm. New plugin versions deployed by updating the tag — no RHDH pod rebuild.
+
+### 7.2 RHDH Configuration
+
+```yaml
+# app-config.yaml — DCM integration configuration
+dcm:
+  baseUrl: https://dcm.corp.internal
+  apiPath: /api/v1
+  
+  # Service account for catalog entity provider
+  serviceAccount:
+    apiKey:
+      $env: DCM_SERVICE_ACCOUNT_API_KEY
+  
+  # Catalog entity sync configuration
+  catalog:
+    syncIntervalSeconds: 300      # poll DCM API every 5 minutes
+    refreshOnScaffolderComplete: true
+    entityNamespace: dcm-catalog  # for DCMService entities
+  
+  # Tenant resolution
+  tenancy:
+    groupNamespacePrefix: "dcm-tenant-"  # RHDH group dcm-tenant-{uuid} → tenant uuid
+    fallbackTenantUuid: null             # null = require explicit group context
+  
+  # Auth delegation
+  auth:
+    oidcIssuer: https://keycloak.corp.com/realms/corporate
+    clientId: dcm-rhdh-bridge
+    clientSecret:
+      $env: DCM_OIDC_CLIENT_SECRET
+
+  # Feature flags
+  features:
+    liveStatusSse: true          # use SSE for request status (fallback to polling if false)
+    costEstimateInCatalog: true  # show cost estimate on catalog cards
+    quotaCheckOnBrowse: true     # show quota availability in catalog
+    autoGenerateTemplates: true  # auto-generate Scaffolder templates from catalog items
+```
+
+### 7.3 Kubernetes Deployment Pattern
+
+```yaml
+# RHDH configuration in OpenShift/Kubernetes
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: rhdh-app-config
+  namespace: rhdh
+data:
+  app-config.dcm.yaml: |
+    dcm:
+      baseUrl: https://dcm-api.dcm-system.svc.cluster.local
+      # ... (internal cluster DNS for in-cluster communication)
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: dcm-integration-secrets
+  namespace: rhdh
+stringData:
+  DCM_SERVICE_ACCOUNT_API_KEY: ""
+  DCM_OIDC_CLIENT_SECRET: ""
+```
+
+### 7.4 Zero-Trust in Cluster
+
+RHDH backend → DCM Consumer API communication:
+- Both running in same Kubernetes cluster (typically)
+- mTLS enforced by Istio service mesh (ICOM model applies to RHDH as a client)
+- RHDH is not a DCM internal component — it is an external client that uses the Consumer API
+- Auth: OIDC token exchange (Section 2.1) — RHDH backend presents OIDC access token; DCM issues session token
+
+---
+
+## 8. RHDH Pre-Built Capabilities Leveraged
+
+### 8.1 No-Build Integrations (Immediate Value)
+
+These work before writing any DCM-specific code:
+
+| RHDH Feature | DCM Benefit | Config needed |
+|-------------|-------------|---------------|
+| Keycloak/RHSSO auth | SSO into DCM portal — same login as everything else | Configure OIDC provider |
+| RBAC Plugin | No-code role management | Define DCM groups |
+| TechDocs | DCM docs rendered in-portal | Add `techdocs-ref` annotations |
+| Search | DCM entities searchable | Provided by catalog backend plugin |
+| Kubernetes plugin | See DCM pods alongside resources | Standard RHDH Kubernetes plugin config |
+| ArgoCD plugin | Layer store GitOps visibility | Standard RHDH ArgoCD plugin config |
+| Tekton plugin | DCM scaffolding pipeline visibility | Standard RHDH Tekton plugin config |
+
+### 8.2 Ansible Automation Platform Plugin
+
+RHDH ships an existing AAP (Ansible Automation Platform) plugin. DCM Service Providers that use Ansible Automation Platform can surface AAP job status directly in RHDH:
+
+```
+DCMResource entity page
+└── Additional tab contributed by AAP plugin:
+    "Automation"  ← shows AAP job runs for this resource's provisioning
+```
+
+This requires no DCM code — it emerges from RHDH's existing AAP plugin + `dcm.io/aap-job-id` annotation on DCMResource entities.
+
+### 8.3 OCM (Open Cluster Management) Plugin
+
+Organizations using OCM for cluster lifecycle management get cluster management alongside DCM service catalog in the same portal — genuinely one pane of glass for sovereign cloud operations.
+
+---
+
+## 9. Deployment Options
+
+DCM supports two frontend deployment modes that can be selected at initial deployment:
+
+**Standalone SPA** — DCM deploys its own React-based consumer portal. No RHDH dependency. Suitable for environments where RHDH is not present.
+
+**RHDH Mode** — DCM plugins are loaded into an existing RHDH instance. The RHDH Developer Hub becomes the consumer portal surface. Recommended for organizations already running RHDH.
+
+Both modes use the same DCM APIs and the same authentication model. The choice is a deployment configuration, not an architectural difference.
+
+```yaml
+# dcm-config.yaml
+frontend:
+  mode: standalone_spa | rhdh
+  rhdh_base_url: https://rhdh.internal  # only required for rhdh mode
+```
+
+---
diff --git a/content/docs/architecture/specifications/dcm-use-case-examples.md b/content/docs/architecture/specifications/dcm-use-case-examples.md
new file mode 100644
index 0000000..209dd91
--- /dev/null
+++ b/content/docs/architecture/specifications/dcm-use-case-examples.md
@@ -0,0 +1,1853 @@
+# DCM — Use Case Examples
+
+**Document Status:** 📋 Draft — Ready for Implementation Feedback
+**Document Type:** Use Case Reference
+**Related Documents:** [Examples and Use Cases](dcm-examples.md) | [Provider Contract](../data-model/A-provider-contract.md) | [Four States](../data-model/02-four-states.md) | [Layering and Versioning](../data-model/03-layering-and-versioning.md) | [Governance Matrix](../data-model/27-governance-matrix.md) | [Scoring Model](../data-model/29-scoring-model.md)
+
+> This document contains worked examples for every DCM provider type, data model, and specification
+> area not covered in [dcm-examples.md](dcm-examples.md). Examples use consistent fictitious actors,
+> tenants, and providers throughout for cross-reference coherence.
+
+---
+
+## Shared Context
+
+All examples reference these fictitious entities:
+
+```
+Tenants:
+  payments-bu         UUID: ten-pay-001   Business unit: card payment processing
+  web-platform-bu     UUID: ten-web-001   Business unit: customer-facing web
+  platform-team       UUID: ten-plt-001   Platform engineering (admin)
+
+Actors:
+  alice@corp.example  UUID: act-alice-001  Developer, payments-bu
+  bob@corp.example    UUID: act-bob-001    Platform engineer, platform-team
+  svc-pipeline@corp   UUID: act-svc-001    CI/CD service account
+
+Providers:
+  vmware-prod         UUID: pvd-vm-001     Service Provider — Compute.VirtualMachine
+  netbox-prod         UUID: pvd-net-001    Information Provider — Network.IPAddress
+  vault-prod          UUID: pvd-vlt-001    credential management service
+  freeipa-prod        UUID: pvd-ipa-001    Auth Provider — FreeIPA/LDAP
+  ceph-prod           UUID: pvd-cph-001    data store — Realized State snapshots
+  rabbitmq-prod       UUID: pvd-rmq-001    event routing service
+  servicenow-prod     UUID: pvd-sn-001     ITSM integration — ServiceNow
+  webapp-meta         UUID: pvd-wam-001    compound service definition — ApplicationStack.WebApp
+
+Data Centers / Zones:
+  dc-west-1 / zone-a    Primary production zone
+  dc-east-1 / zone-b    DR zone
+```
+
+---
+
+# Section 1 — Data Model Examples
+
+## 1.1 The Four States — VM Lifecycle from Request to Decommission
+
+This example traces a single `Compute.VirtualMachine` entity through all four states, from the consumer's original intent through decommission.
+
+**Step 1: Consumer submits a request — Intent State created**
+
+```
+POST /api/v1/requests
+Authorization: Bearer 
+
+{
+  "catalog_item_uuid": "cat-vm-standard",
+  "fields": {
+    "vm_name": "payments-api-03",
+    "cpu_cores": 8,
+    "ram_gb": 32,
+    "os": "RHEL 9",
+    "zone": "dc-west-1/zone-a",
+    "purpose": "payments API service node"
+  }
+}
+```
+
+DCM immediately writes the Intent State record — immutable from this moment forward:
+
+```yaml
+intent_state_record:
+  intent_uuid: int-pay-api-03
+  entity_uuid: ent-vm-pay-03      # assigned on first request
+  tenant_uuid: ten-pay-001
+  actor_uuid:  act-alice-001
+  recorded_at: 2026-03-31T09:00:00Z
+
+  # Verbatim consumer input — never altered by policy
+  raw_intent:
+    catalog_item_uuid: cat-vm-standard
+    fields:
+      vm_name: payments-api-03
+      cpu_cores: 8
+      ram_gb: 32
+      os: RHEL 9
+      zone: dc-west-1/zone-a
+      purpose: payments API service node
+```
+
+**Step 2: Layer assembly and policy evaluation — Requested State built**
+
+DCM's Request Payload Processor assembles layers in precedence order:
+
+```yaml
+# Layer 1 — Base (org-wide standards)
+dcm:
+  network:
+    dns_suffix: .internal.corp.example
+  security:
+    selinux: enforcing
+    firewall: enabled
+
+# Layer 2 — Data Center (dc-west-1)
+location:
+  datacenter: dc-west-1
+  rack_prefix: rack-w1
+  power_domain: ups-west-primary
+
+# Layer 3 — Payments BU Service Layer
+compliance:
+  pci_dss: true
+  network_segment: payments-dmz
+  encryption_at_rest: required
+  log_retention_days: 365
+
+# Layer 4 — Request Layer (consumer-supplied fields)
+vm:
+  name: payments-api-03
+  cpu_cores: 8
+  ram_gb: 32
+  os_image: rhel-9-latest-approved   # enriched from "RHEL 9" by Transformation policy
+  zone: dc-west-1/zone-a
+```
+
+GateKeeper policy fires: `payments-dmz` placement requires PCI DSS accreditation on the provider → `pvd-vm-001` has active PCI DSS accreditation → PASS.
+
+Transformation policy fires: `cpu_cores: 8` in payments zone → sets `cpu_pinning: true` per PCI DSS performance isolation requirement.
+
+Requested State written (the fully assembled, policy-approved dispatch payload):
+
+```yaml
+requested_state_record:
+  requested_state_uuid: req-pay-api-03
+  entity_uuid: ent-vm-pay-03
+  intent_uuid: int-pay-api-03
+  tenant_uuid: ten-pay-001
+  provider_uuid: pvd-vm-001        # placement resolved
+  assembled_at: 2026-03-31T09:00:05Z
+
+  payload:
+    vm_name: payments-api-03
+    cpu_cores: 8
+    cpu_pinning: true              # injected by Transformation policy
+    ram_gb: 32
+    os_image: rhel-9-latest-approved
+    network_segment: payments-dmz
+    encryption_at_rest: true
+    log_retention_days: 365
+    zone: dc-west-1/zone-a
+    selinux: enforcing
+```
+
+**Step 3: Provider realizes the request — Realized State written**
+
+Provider confirms provisioning and returns:
+
+```yaml
+realized_state_snapshot:
+  realized_state_uuid: rlz-pay-api-03-v1
+  entity_uuid: ent-vm-pay-03
+  source_requested_state_uuid: req-pay-api-03
+  realized_at: 2026-03-31T09:02:47Z
+  lifecycle_state: OPERATIONAL
+
+  # Provider-supplied realization details (added to assembled payload)
+  provider_data:
+    resource_id: vm-4821                  # provider's internal ID
+    hypervisor_host: esxi-w1-rack3-b04
+    mac_address: 00:50:56:ab:cd:ef
+    assigned_ip: 10.42.18.55
+    boot_time: 2026-03-31T09:02:31Z
+    bios_uuid: 4201abcd-0000-0000-0000-000000000001
+```
+
+**Step 4: Discovery confirms state — Discovered State updated**
+
+Discovery Scheduler polls `pvd-vm-001` 24 hours later:
+
+```yaml
+discovered_state_record:
+  entity_uuid: ent-vm-pay-03
+  discovered_at: 2026-04-01T09:05:00Z
+  provider_uuid: pvd-vm-001
+  resource_id: vm-4821
+
+  discovered_fields:
+    cpu_cores: 8
+    ram_gb: 32
+    os_image: rhel-9-latest-approved
+    cpu_pinning: true
+    power_state: running
+    uptime_hours: 24.1
+    disk_usage_pct: 12
+
+# Drift check: all discovered fields match Realized State → no drift
+# drift_record: null
+```
+
+**Step 5: Decommission**
+
+```
+DELETE /api/v1/resources/ent-vm-pay-03
+
+# Entity enters DECOMMISSIONING → provider deletes VM → DECOMMISSIONED
+# Intent, Requested, and Realized State records are retained for audit
+# Discovered State cleared
+```
+
+---
+
+## 1.2 Layer Assembly — Full Walkthrough
+
+A request for a web server in the payments zone demonstrates how six layers compose into a single dispatch payload.
+
+**Layers in precedence order (lowest → highest):**
+
+```yaml
+# Layer 0: Base entity (org-wide defaults)
+---
+handle: layers/base/compute/vm-standard
+version: "3.1.0"
+type: base_entity
+data:
+  selinux: enforcing
+  firewall: enabled
+  ntp_server: ntp.internal.corp.example
+  dns_suffix: .internal.corp.example
+  monitoring_agent: node_exporter
+  log_collector: filebeat
+  log_destination: logs.corp.example:5044
+```
+
+```yaml
+# Layer 1: Data Center — dc-west-1
+---
+handle: layers/dc/west-1
+version: "2.0.0"
+parent: layers/base/compute/vm-standard@3.1.0
+type: layer_entity
+data:
+  location:
+    datacenter: dc-west-1
+    region: us-west
+    rack_prefix: rack-w1
+    power_domain: ups-west-primary
+  network:
+    gateway: 10.42.0.1
+    dns: [10.42.0.53, 10.42.0.54]
+```
+
+```yaml
+# Layer 2: Zone — dc-west-1/zone-a (DMZ)
+---
+handle: layers/zone/west-1-zone-a-dmz
+version: "1.5.0"
+parent: layers/dc/west-1@2.0.0
+type: layer_entity
+data:
+  network:
+    segment: dmz-payments
+    vlan: 142
+    firewall_policy: payments-dmz-policy
+  compliance:
+    pci_dss: true
+    log_retention_days: 365
+```
+
+```yaml
+# Layer 3: Payments BU service layer
+---
+handle: layers/service/payments-bu/compute
+version: "1.2.0"
+parent: layers/zone/west-1-zone-a-dmz@1.5.0
+type: layer_entity
+data:
+  security:
+    cpu_pinning: true            # PCI isolation
+    encryption_at_rest: required
+  backup:
+    enabled: true
+    frequency: daily
+    retention: 30d
+```
+
+```yaml
+# Layer 4: Web server service layer
+---
+handle: layers/service/web-platform/nginx-config
+version: "2.0.0"
+parent: layers/service/payments-bu/compute@1.2.0
+type: layer_entity
+data:
+  software:
+    packages: [nginx, certbot]
+    nginx_config_ref: git://configs/nginx/standard.conf@v3
+  ports_open: [80, 443]
+```
+
+```yaml
+# Layer 5: Request layer (consumer-supplied)
+---
+type: request_layer
+data:
+  vm_name: payments-web-07
+  cpu_cores: 4
+  ram_gb: 16
+  os_image: rhel-9-latest-approved
+  zone: dc-west-1/zone-a
+```
+
+**Assembled payload (higher layers win on conflict):**
+
+```yaml
+# Assembled dispatch payload sent to pvd-vm-001
+assembled_payload:
+  vm_name: payments-web-07         # from request layer
+  cpu_cores: 4                     # from request layer
+  ram_gb: 16                       # from request layer
+  os_image: rhel-9-latest-approved # from request layer
+  selinux: enforcing               # from base (not overridden)
+  firewall: enabled                # from base (not overridden)
+  ntp_server: ntp.internal.corp.example
+  dns_suffix: .internal.corp.example
+  monitoring_agent: node_exporter  # from base
+  log_collector: filebeat          # from base
+  log_destination: logs.corp.example:5044
+  location:
+    datacenter: dc-west-1          # from dc layer
+    region: us-west
+    rack_prefix: rack-w1
+  network:
+    segment: dmz-payments          # from zone layer (overrides dc layer)
+    vlan: 142
+    gateway: 10.42.0.1
+    dns: [10.42.0.53, 10.42.0.54]
+    firewall_policy: payments-dmz-policy
+  compliance:
+    pci_dss: true                  # from zone layer
+    log_retention_days: 365
+  security:
+    cpu_pinning: true              # from payments BU layer
+    encryption_at_rest: required
+  backup:
+    enabled: true
+    frequency: daily
+    retention: 30d
+  software:
+    packages: [nginx, certbot]     # from web layer
+    nginx_config_ref: git://configs/nginx/standard.conf@v3
+  ports_open: [80, 443]
+
+provenance:
+  # Every field carries its source layer and the actor who set it
+  - field: cpu_pinning
+    value: true
+    source: layers/service/payments-bu/compute@1.2.0
+    set_by: bob@corp.example
+    reason: PCI DSS compute isolation requirement
+  - field: log_retention_days
+    value: 365
+    source: layers/zone/west-1-zone-a-dmz@1.5.0
+    set_by: compliance-team@corp.example
+    reason: PCI DSS requirement 10.5
+```
+
+---
+
+## 1.3 Governance Matrix — PHI Data Request Evaluation
+
+A developer in the `web-platform-bu` tenant requests a VM to host a new microservice that will process Protected Health Information (PHI). DCM evaluates the four-axis governance matrix.
+
+**The request:**
+
+```yaml
+subject: actor act-alice-001         # Axis 1: WHO
+  tenant: ten-web-001
+  mfa_verified: true
+  session_risk_score: 0.12           # low — recent login, known device
+
+data: payload.data_classifications   # Axis 2: WHAT
+  contains: [phi]                    # request fields marked phi by policy
+
+target: pvd-vm-001                   # Axis 3: WHERE
+  sovereignty_zone: us-commercial
+  accreditations: [iso_27001, soc2_type2]   # no hipaa_baa
+
+context:                             # Axis 4: UNDER WHAT CONDITIONS
+  profile: prod
+  time: 2026-03-31T14:00:00Z         # business hours
+  request_risk_score: 68             # elevated — PHI + no BAA
+```
+
+**Axis 1 (Subject):** Actor is authenticated, MFA verified, session risk low → PASS
+
+**Axis 2 (Data):** Payload contains PHI classification → triggers HIPAA rules
+
+**Axis 3 (Target):** `pvd-vm-001` is checked for HIPAA BAA accreditation → **no BAA registered** → FAIL
+
+**Axis 4 (Context):** `prod` profile requires accreditation completeness for PHI → DENY
+
+**Governance Matrix decision:**
+
+```yaml
+governance_decision:
+  outcome: DENY
+  rule_matched: "PHI data requires HIPAA BAA on target provider"
+  axis_failing: target
+  detail:
+    provider_uuid: pvd-vm-001
+    missing_accreditation: hipaa_baa
+    required_for: phi classification in payload
+  remediation:
+    option_1: "Submit BAA for pvd-vm-001 and await activation"
+    option_2: "Route to a provider with active HIPAA BAA accreditation"
+    option_3: "Remove PHI from this service's data scope"
+
+# Request is blocked before dispatch — no Requested State written
+# Consumer receives:
+{
+  "error": {
+    "code": "GOVERNANCE_DENIED",
+    "message": "PHI data cannot be placed on pvd-vm-001 — HIPAA BAA not present",
+    "remediation": "Contact your platform admin to add a BAA for this provider"
+  }
+}
+```
+
+---
+
+## 1.4 Scoring Model — Risk Score and Placement Decision
+
+Three providers are candidates for a Tier 1 VM request. DCM calculates a risk score and routes to the approval tier.
+
+**Signals for this request:**
+
+```yaml
+# Signal 1: Operational GateKeeper Score
+# No GateKeeper policies fired → score: 0 (lowest risk)
+signal_1: 0
+
+# Signal 2: Policy Completeness Score
+# All policies evaluated; no shadow-only policies for this resource type → score: 0
+signal_2: 0
+
+# Signal 3: Actor Risk History Score
+# alice@corp.example — no failed requests, no policy violations in 90 days → score: 5
+signal_3: 5
+
+# Signal 4: Tenant Quota Pressure Score
+# payments-bu is at 62% of VM quota → moderate pressure → score: 18
+signal_4: 18
+
+# Signal 5: Provider Accreditation Richness (for placement tie-breaking)
+# pvd-vm-001: iso_27001(20) + soc2_type2(20) + pci_dss(25) = 65 → external verified P1D → ×1.0
+# pvd-vm-002: iso_27001(20) + soc2_type2(20) = 40 → external verified P7D → ×0.9 = 36
+# pvd-vm-003: soc2_type2(20) = 20 → stale verification → ×0.4 = 8
+
+aggregate_risk_score:
+  formula: "(signal_1 × 0.35) + (signal_2 × 0.25) + (signal_3 × 0.20) + (signal_4 × 0.15) + (signal_5_inverse × 0.05)"
+  value: 21
+  # 21 → STANDARD tier (threshold: 0-39 = STANDARD, 40-69 = ELEVATED, 70+ = CRITICAL)
+```
+
+**Authority Tier routing:**
+
+```yaml
+# Risk score 21 → STANDARD tier → no additional approval required
+# Placement: pvd-vm-001 wins (highest accreditation richness score: 65)
+
+placement_decision:
+  provider_uuid: pvd-vm-001
+  approval_tier: STANDARD
+  auto_approved: true
+  rationale: "Risk score 21 < 40 threshold; pvd-vm-001 highest accreditation richness"
+```
+
+**Contrast — the same request from a high-risk actor:**
+
+```yaml
+# If signal_3 (actor risk) = 45 (recent policy violations):
+aggregate_risk_score: 52   # → ELEVATED tier
+
+placement_decision:
+  provider_uuid: pvd-vm-001
+  approval_tier: ELEVATED
+  auto_approved: false
+  requires_approval_from:
+    - role: tenant_admin            # payments-bu tenant admin
+  approval_deadline: PT4H
+```
+
+---
+
+## 1.5 Authority Tier Model — Multi-Tier Approval Routing
+
+A developer requests 200 VMs simultaneously (bulk deployment for a load test). Risk score crosses the CRITICAL threshold and requires multi-tier sign-off.
+
+```yaml
+# Bulk request: 200 × Compute.VirtualMachine for load-test-bu tenant
+# Signal 4 (quota pressure): 200 VMs = 95% of quota → score: 38
+# Signal 3 (actor history): svc-pipeline@corp — automated, clean history → 0
+# Aggregate risk score: 58 → ELEVATED
+
+# But: 200 instances triggers an additional policy:
+#   "bulk_request_over_100 → escalate to CRITICAL tier"
+# GateKeeper fires and elevates: score_override: CRITICAL
+
+authority_tier_routing:
+  risk_score_raw: 58
+  score_override: CRITICAL       # GateKeeper escalation
+  tier_applied: CRITICAL
+  approval_chain:
+    - step: 1
+      approver_role: tenant_admin
+      tenant_uuid: ten-web-001
+      deadline: PT2H
+      status: pending
+
+    - step: 2
+      approver_role: platform_admin
+      deadline: PT4H             # starts after step 1 approved
+      status: waiting
+
+    - step: 3
+      approver_role: ciso_delegate
+      deadline: PT8H             # starts after step 2 approved
+      status: waiting
+
+  # If any step times out → request enters APPROVAL_EXPIRED state
+  # Policy: NOTIFY_AND_WAIT → Compliance Team paged
+```
+
+**Tenant admin approves (step 1):**
+
+```
+POST /api/v1/approvals/apv-bulk-load-001
+Authorization: Bearer 
+
+{
+  "decision": "approve",
+  "rationale": "Authorized load test — signed off by VP Engineering"
+}
+
+# → step 2 notification fires to platform admin
+```
+
+---
+
+## 1.6 Entity Relationships — Composite Web Service
+
+A `Compute.VirtualMachine`, `Network.IPAddress`, and `Security.FirewallRule` are related as a composite web service entity.
+
+```yaml
+# Three entities with explicit relationships
+entities:
+  - entity_uuid: ent-vm-pay-03       # the VM
+    resource_type: Compute.VirtualMachine
+    tenant_uuid: ten-pay-001
+
+  - entity_uuid: ent-ip-pay-03       # the IP assigned to the VM
+    resource_type: Network.IPAddress
+    tenant_uuid: ten-pay-001
+
+  - entity_uuid: ent-fw-pay-03       # the firewall rule permitting traffic
+    resource_type: Security.FirewallRule
+    tenant_uuid: ten-pay-001
+
+relationships:
+  - relationship_uuid: rel-001
+    from_entity: ent-vm-pay-03
+    to_entity: ent-ip-pay-03
+    relationship_type: assigned_to
+    cardinality: one_to_one
+    required_for_delivery: true      # VM cannot be OPERATIONAL without an IP
+
+  - relationship_uuid: rel-002
+    from_entity: ent-vm-pay-03
+    to_entity: ent-fw-pay-03
+    relationship_type: protected_by
+    cardinality: one_to_many
+    required_for_delivery: false     # VM can be OPERATIONAL; rule is operational hygiene
+```
+
+**Impact of decommissioning the VM:**
+
+```yaml
+# Consumer: DELETE /api/v1/resources/ent-vm-pay-03
+# DCM evaluates relationship graph before dispatch:
+
+decommission_impact_analysis:
+  entity: ent-vm-pay-03
+  dependents:
+    - entity_uuid: ent-ip-pay-03
+      relationship: assigned_to
+      impact: IP address released → available for reassignment
+      action: decommission_with_parent
+
+    - entity_uuid: ent-fw-pay-03
+      relationship: protected_by
+      impact: Firewall rule becomes orphaned — no host to protect
+      action: notify_admin           # rule not auto-deleted; may apply to other VMs
+
+  consumer_presented:
+    "Decommissioning this VM will release IP ent-ip-pay-03. Firewall rule
+     ent-fw-pay-03 will become orphaned and require manual review."
+```
+
+---
+
+## 1.7 Universal Groups — Tenant, Resource Group, and Cross-Tenant Sharing
+
+**Setup: payments-bu tenant with a resource group and a shared database**
+
+```yaml
+# Tenant (group_class: tenant_boundary)
+dcm_group:
+  uuid: ten-pay-001
+  group_class: tenant_boundary
+  handle: tenants/payments-bu
+  display_name: Payments Business Unit
+  members:
+    - { type: actor, uuid: act-alice-001, role: member }
+    - { type: actor, uuid: act-pay-admin, role: tenant_admin }
+
+# Resource group within the tenant (group_class: resource_group)
+dcm_group:
+  uuid: rg-pay-api-servers
+  group_class: resource_group
+  handle: tenants/payments-bu/groups/api-servers
+  parent_tenant_uuid: ten-pay-001
+  display_name: Payments API Servers
+  members:
+    - { type: entity, uuid: ent-vm-pay-01, role: member }
+    - { type: entity, uuid: ent-vm-pay-02, role: member }
+    - { type: entity, uuid: ent-vm-pay-03, role: member }
+```
+
+**Cross-tenant sharing: payments-bu shares a read-only DB with web-platform-bu**
+
+```yaml
+# Authorization record (cross-tenant read access)
+cross_tenant_authorization:
+  uuid: xta-db-share-001
+  grantor_tenant: ten-pay-001
+  grantee_tenant: ten-web-001
+  scope:
+    entity_uuids: [ent-db-pay-analytics]
+    permissions: [read]             # not write or decommission
+  expires_at: 2026-12-31T23:59:59Z
+  approved_by: act-pay-admin
+  governance_matrix_check: ALLOW    # PHI not in this DB; cross-tenant read permitted
+
+# web-platform-bu can now query:
+GET /api/v1/resources/ent-db-pay-analytics
+# → 200 OK (authorized via cross-tenant grant)
+
+DELETE /api/v1/resources/ent-db-pay-analytics
+# → 403 Forbidden (write not in grant scope)
+```
+
+---
+
+## 1.8 Scheduled Requests and Maintenance Windows
+
+**Scenario: OS patch deployment during an approved maintenance window**
+
+**Step 1: Platform admin defines maintenance window**
+
+```
+POST /api/v1/admin/maintenance-windows
+
+{
+  "display_name": "Q2 OS Patching — West Zone",
+  "starts_at": "2026-04-06T02:00:00Z",
+  "ends_at":   "2026-04-06T06:00:00Z",
+  "scope": {
+    "tenant_uuids": ["ten-pay-001", "ten-web-001"],
+    "resource_types": ["Compute.VirtualMachine"]
+  },
+  "change_freeze": false
+}
+
+Response: { "window_uuid": "mw-q2-patch-001" }
+```
+
+**Step 2: CI/CD pipeline submits a deferred patching request**
+
+```
+POST /api/v1/requests
+
+{
+  "catalog_item_uuid": "cat-os-patch-rhel9",
+  "fields": {
+    "target_entity_uuid": "ent-vm-pay-03",
+    "patch_baseline": "rhel9-2026-q2",
+    "pre_patch_snapshot": true
+  },
+  "scheduled_at": "2026-04-06T02:15:00Z",
+  "maintenance_window_uuid": "mw-q2-patch-001"
+}
+
+Response:
+{
+  "name": "/api/v1/operations/req-patch-pay-03",
+  "done": false,
+  "metadata": {
+    "stage": "SCHEDULED",
+    "scheduled_at": "2026-04-06T02:15:00Z",
+    "resource_uuid": "ent-vm-pay-03"
+  }
+}
+```
+
+**Step 3: At 02:15Z — window opens, request executes**
+
+```yaml
+# Orchestrator fires at scheduled_at
+# Entity: OPERATIONAL → UPDATING
+# Process.OSPatch entity created, linked to VM entity
+# Provider receives:
+
+dispatch_payload:
+  entity_uuid: ent-vm-pay-03
+  operation: patch
+  patch_baseline: rhel9-2026-q2
+  pre_patch_snapshot: true
+  maintenance_window_uuid: mw-q2-patch-001
+
+# Provider: takes snapshot, applies patches, reboots, validates
+# Callback received: OPERATIONAL
+# New Realized State written with updated os_patch_level
+# Process.OSPatch entity: DECOMMISSIONED (process complete)
+```
+
+---
+
+## 1.9 Request Dependency Graph — Multi-Resource Compound Provisioning
+
+A CI/CD pipeline provisions three resources with strict ordering: DB first, then app server, then load balancer (which needs both IPs).
+
+**Step 1: Submit the dependency group**
+
+```
+POST /api/v1/request-groups
+
+{
+  "display_name": "payments-api-stack-v2 rollout",
+  "requests": [
+    {
+      "client_id": "db",
+      "catalog_item_uuid": "cat-postgresql-ha",
+      "fields": { "db_name": "payments_v2", "storage_gb": 500 }
+    },
+    {
+      "client_id": "app",
+      "catalog_item_uuid": "cat-vm-standard",
+      "fields": { "vm_name": "payments-api-04", "cpu_cores": 8 },
+      "depends_on": ["db"],
+      "wait_for": "OPERATIONAL",
+      "inject_from_dependency": {
+        "db": { "db_host": "$.realized.assigned_ip" }
+      }
+    },
+    {
+      "client_id": "lb",
+      "catalog_item_uuid": "cat-haproxy-config",
+      "fields": { "pool_name": "payments-api-pool" },
+      "depends_on": ["app"],
+      "wait_for": "OPERATIONAL",
+      "inject_from_dependency": {
+        "app": { "backend_ips": "$.realized.assigned_ip" }
+      }
+    }
+  ]
+}
+
+Response: { "group_uuid": "grp-pay-stack-v2" }
+```
+
+**Step 2: Execution sequence**
+
+```
+T+0s    DB request dispatched → pvd-pg-001
+T+90s   DB realized → assigned_ip: 10.42.18.100
+        DB field injection into app request:  db_host: 10.42.18.100
+T+90s   App request dispatched → pvd-vm-001 (with db_host injected)
+T+150s  App realized → assigned_ip: 10.42.18.55
+        App field injection into lb request: backend_ips: [10.42.18.55]
+T+150s  LB request dispatched → pvd-lb-001 (with backend_ips injected)
+T+165s  LB realized → all three OPERATIONAL
+        Group status: COMPLETE
+```
+
+**Query group status at any point:**
+
+```
+GET /api/v1/request-groups/grp-pay-stack-v2
+
+{
+  "group_uuid": "grp-pay-stack-v2",
+  "status": "IN_PROGRESS",
+  "requests": [
+    { "client_id": "db",  "status": "OPERATIONAL", "entity_uuid": "ent-db-pay-v2" },
+    { "client_id": "app", "status": "PROVISIONING", "entity_uuid": "ent-vm-pay-04" },
+    { "client_id": "lb",  "status": "PENDING_DEPENDENCY", "entity_uuid": null }
+  ]
+}
+```
+
+---
+
+## 1.10 Workload Analysis — Discovered VM Classified and Ingested
+
+A VM exists in the data center that was never provisioned through DCM. Discovery finds it; Workload Analysis classifies it; ingestion brings it under lifecycle management.
+
+**Step 1: Discovery Scheduler finds unknown VM**
+
+```yaml
+discovered_state_record:
+  entity_uuid: null                  # no DCM UUID yet — new entity
+  provider_uuid: pvd-vm-001
+  provider_entity_id: vm-9917        # provider's internal ID
+  discovered_at: 2026-03-31T08:00:00Z
+
+  discovered_fields:
+    ip_address: 10.42.22.77
+    hostname: legacy-payments-batch
+    os: RHEL 7.9
+    cpu_cores: 4
+    ram_gb: 8
+    running_processes: [java, cron, rsync]
+    open_ports: [8080, 22]
+    disk_gb: 200
+
+# DCM assigns a provisional entity_uuid and writes INGESTED state
+entity_uuid: ent-disc-9917
+lifecycle_state: INGESTED
+tenant_uuid: __transitional__
+```
+
+**Step 2: Workload Analysis fires automatically**
+
+```yaml
+# Analysis.WorkloadProfile entity created
+workload_profile_entity:
+  entity_uuid: ent-wla-9917
+  resource_type: Analysis.WorkloadProfile
+  subject_entity_uuid: ent-disc-9917
+  lifecycle_state: OPERATIONAL
+
+  classification:
+    resource_type_match:
+      primary: Compute.VirtualMachine
+      confidence: high
+
+    workload_archetype:
+      type: batch_processor
+      confidence: medium
+      signals:
+        - "cron present: scheduled job execution"
+        - "java process: JVM-based batch framework"
+        - "rsync present: data sync pattern"
+        - "port 8080: likely management API, not customer-facing"
+        - "RHEL 7.9: end-of-life — upgrade candidate"
+
+    migration_readiness:
+      containerization_score: 4      # low — stateful batch job, not container-friendly
+      blockers:
+        - "Stateful data in /data — requires persistent volume mapping"
+        - "RHEL 7.9 runtime — requires migration to RHEL 9 base image first"
+      suggested_target: Compute.VirtualMachine    # stay as VM, upgrade OS
+
+    lifecycle_recommendation:
+      dcm_lifecycle_model: standard
+      rehydration_eligible: true
+      notes: "Application on /opt, data on /data — static replace eligible after OS upgrade"
+```
+
+**Step 3: Platform admin enriches and promotes**
+
+```
+PATCH /api/v1/resources/ent-disc-9917
+{
+  "tenant_uuid": "ten-pay-001",
+  "display_name": "Payments Batch Processor (Legacy)",
+  "cost_center": "CC-PAY-OPS",
+  "owner": { "actor_uuid": "act-pay-admin" }
+}
+
+# Auto-assignment policy fires: batch_processor archetype → assign to 'batch-workloads' resource group
+# Entity promoted: INGESTED → ENRICHING → PROMOTED → OPERATIONAL
+# Entity now under full DCM lifecycle management
+```
+
+---
+
+## 1.11 Accreditation Monitor — FedRAMP Verification and Mid-Cycle Revocation
+
+**Setup: Provider with FedRAMP High accreditation**
+
+```yaml
+accreditation:
+  artifact_metadata:
+    uuid: acc-fr-high-001
+    handle: accreditations/providers/pvd-vm-fed-001/fedramp-high
+    version: "1.0.0"
+    status: active
+
+  subject_uuid: pvd-vm-fed-001
+  framework: fedramp_high
+  accreditation_type: regulatory_certification
+  accreditor:
+    name: FedRAMP PMO
+    type: government
+
+  issued_at: 2025-06-01T00:00:00Z
+  expires_at: 2026-06-01T00:00:00Z
+  renewal_warning_before: P90D
+
+  external_registry_id: FR2025-0088    # FedRAMP Marketplace ID
+
+  verification:
+    tier: external_registry
+    registry_api:
+      provider: fedramp
+      lookup_key: FR2025-0088
+      poll_interval: P1D
+      last_checked_at: 2026-03-30T09:00:00Z
+      last_result: confirmed_active
+    stale_after: P3D
+    stale_action: escalate           # sovereign profile — escalate not warn
+```
+
+**Day 1: Normal verification cycle**
+
+```yaml
+# Accreditation Monitor polls marketplace.fedramp.gov
+# GET /api/products?id=FR2025-0088
+# Response: { "status": "Authorized", "impact_level": "High" }
+
+# Monitor fires:
+event:
+  type: accreditation.verified
+  urgency: low
+  payload:
+    accreditation_uuid: acc-fr-high-001
+    framework: fedramp_high
+    registry: fedramp_marketplace
+    checked_at: 2026-03-31T09:00:00Z
+
+# DCM updates: last_verified_at, last_result: confirmed_active
+# Scoring Model Signal 5: weight 40 × multiplier 1.0 = 40 (full weight)
+```
+
+**Day 47: FedRAMP PMO revokes the authorization mid-cycle**
+
+```yaml
+# Monitor polls: GET /api/products?id=FR2025-0088
+# Response: { "status": "Revoked", "revocation_date": "2026-05-17" }
+
+# status = Revoked → immediate revocation (no admin confirmation required — ACM-002)
+event:
+  type: accreditation.status_changed
+  urgency: critical
+  payload:
+    accreditation_uuid: acc-fr-high-001
+    from_status: authorized
+    to_status: revoked
+    external_source: fedramp_marketplace
+    action_taken: immediate_revocation
+
+# DCM immediately:
+# 1. Sets accreditation status → revoked
+# 2. Fires Accreditation Gap for pvd-vm-fed-001
+# 3. All active requests targeting pvd-vm-fed-001 with sovereign/fedramp data → SUSPENDED
+# 4. Platform Admin + Compliance Team paged (urgency: critical, non-suppressable)
+# 5. Recovery Policy: NOTIFY_AND_WAIT (sovereign profile default)
+
+accreditation_gap_record:
+  provider_uuid: pvd-vm-fed-001
+  required_framework: fedramp_high
+  gap_type: revoked
+  severity: critical
+  affected_entity_uuids: [ent-vm-fed-01, ent-vm-fed-02, ent-vm-fed-07]
+  policy_response: NOTIFY_AND_WAIT
+```
+
+---
+
+## 1.12 Session Revocation — Security Event Response
+
+A developer's laptop is reported stolen at 14:30. The security team needs to immediately terminate all active DCM sessions for that actor.
+
+**Step 1: Security team triggers emergency revocation**
+
+```
+POST /api/v1/admin/actors/act-alice-001/sessions:revoke-all
+Authorization: Bearer 
+
+{
+  "revocation_reason": "SECURITY_EVENT",
+  "detail": "Laptop reported stolen — device UUID: dev-alice-macbook-001",
+  "audit_reference": "INC-2026-0847"
+}
+```
+
+**Step 2: DCM processes emergency revocation**
+
+```yaml
+# All active sessions for act-alice-001 identified: [sess-alice-001, sess-alice-002]
+# Session tokens added to Revocation Registry immediately
+# (Zero-latency: registry checked on every API call inbound)
+
+revocation_registry_entries:
+  - token_jti: jwt-alice-sess-001
+    revoked_at: 2026-03-31T14:30:07Z
+    reason: SECURITY_EVENT
+    actor_uuid: act-alice-001
+
+  - token_jti: jwt-alice-sess-002
+    revoked_at: 2026-03-31T14:30:07Z
+    reason: SECURITY_EVENT
+    actor_uuid: act-alice-001
+
+event:
+  type: security.session_revoked
+  urgency: critical
+  payload:
+    actor_uuid: act-alice-001
+    sessions_revoked: 2
+    reason: SECURITY_EVENT
+    incident_ref: INC-2026-0847
+```
+
+**Step 3: In-flight request intercepted**
+
+```yaml
+# Alice had just submitted a request at 14:30:05 — 2 seconds before revocation
+# Request was in POLICY_EVALUATION stage
+# Orchestrator checks Revocation Registry before dispatch:
+
+check_result: token jwt-alice-sess-001 in revocation registry
+action: ABORT_REQUEST
+request_status: CANCELLED
+reason: "Actor session revoked during request processing"
+# Resource NOT provisioned — safe state
+```
+
+**Step 4: Next login attempt fails cleanly**
+
+```
+GET /api/v1/resources
+Authorization: Bearer jwt-alice-sess-001
+
+→ 401 Unauthorized
+{
+  "error": "TOKEN_REVOKED",
+  "message": "Session has been administratively revoked. Contact your platform admin."
+}
+```
+
+---
+
+# Section 2 — Provider Interaction Examples
+
+## 2.5 Auth Provider — FreeIPA Integration
+
+**Registration:**
+
+```yaml
+auth_provider_registration:
+  artifact_metadata:
+    uuid: pvd-ipa-001
+    handle: providers/auth/corporate-freeipa
+    version: "1.0.0"
+    status: active
+    owned_by: { display_name: Platform Team }
+
+  name: Corporate FreeIPA
+  description: Primary enterprise directory — FreeIPA with Kerberos
+
+  capabilities:
+    authentication: true
+    mfa: false                       # Kerberos SSO, MFA handled by RHSSO layer
+    group_membership: true
+    role_mapping: true
+
+  protocol: ldap
+  endpoint: ldaps://ipa.corp.example:636
+  bind_credential_uuid: crd-ipa-bind-001   # stored in credential management service
+
+  group_mapping:
+    # FreeIPA groups → DCM roles
+    - ipa_group: dcm-platform-admins
+      dcm_role: platform_admin
+    - ipa_group: dcm-tenant-payments
+      dcm_role: tenant_member
+      tenant_uuid: ten-pay-001
+    - ipa_group: dcm-tenant-web
+      dcm_role: tenant_member
+      tenant_uuid: ten-web-001
+
+  health_check:
+    endpoint: /health
+    method: ldap_bind_check
+```
+
+**Authentication flow:**
+
+```
+1. alice@corp.example submits credentials to DCM Consumer API
+2. DCM forwards to pvd-ipa-001: LDAP bind as alice@corp.example
+3. FreeIPA validates credentials → success
+4. DCM queries: memberOf → [dcm-tenant-payments, dcm-team-api-devs]
+5. Group mapping applied → alice gets: tenant_member(ten-pay-001)
+6. DCM issues session token (JWT) with actor_uuid + group claims
+7. Session written to Session Store
+```
+
+---
+
+## 2.6 data store — Ceph Realized State Snapshots
+
+**Registration:**
+
+```yaml
+(prescribed infrastructure)_registration:
+  uuid: pvd-cph-001
+  name: ceph-prod
+  display_name: Ceph — Realized State realized data domain
+  store_type: write_once_snapshot
+  version: "1.0.0"
+  endpoint: https://ceph-rgw.corp.example:7480
+
+  capabilities:
+    write: true
+    read: true
+    delete: false            # write_once_snapshot — immutable
+    content_addressed: true  # SHA-256 keyed
+    encryption_at_rest: true
+    replication_factor: 3
+
+  bucket: dcm-realized-state
+  auth_credential_uuid: crd-ceph-s3-001
+```
+
+**DCM writes a Realized State snapshot:**
+
+```yaml
+# After provider confirms VM provisioning:
+# DCM stores the full realized_state_record as a content-addressed object
+
+storage_write_request:
+  store_type: write_once_snapshot
+  key: realized-state/ent-vm-pay-03/rlz-pay-api-03-v1
+  content_hash: sha256:a1b2c3d4...
+  payload: 
+
+# Ceph stores it; returns: { "stored": true, "etag": "sha256:a1b2c3d4..." }
+# DCM records: realized_state_uuid → storage key mapping
+```
+
+---
+
+## 2.7 event routing service — RabbitMQ Event Routing
+
+**Registration:**
+
+```yaml
+(optional infrastructure)_registration:
+  uuid: pvd-rmq-001
+  name: rabbitmq-prod
+  display_name: RabbitMQ — DCM Event Bus
+  version: "1.0.0"
+  endpoint: amqps://rabbitmq.corp.example:5671
+  auth_credential_uuid: crd-rmq-001
+
+  capabilities:
+    publish: true
+    subscribe: true
+    durable_queues: true
+    dead_letter: true
+
+  exchange_config:
+    name: dcm.events
+    type: topic
+    durable: true
+
+  routing_key_pattern: "{domain}.{event_type}.{urgency}"
+  # Examples:
+  #   request.completed.low
+  #   provider.health_changed.high
+  #   accreditation.status_changed.critical
+```
+
+**Event flows through the bus:**
+
+```yaml
+# 1. DCM publishes event (internal → bus)
+publish:
+  exchange: dcm.events
+  routing_key: accreditation.status_changed.critical
+  payload:
+    event_type: accreditation.status_changed
+    urgency: critical
+    accreditation_uuid: acc-fr-high-001
+    from_status: authorized
+    to_status: revoked
+
+# 2. notification service subscribes and routes to appropriate channels
+subscribe:
+  queue: dcm.notifications.critical
+  binding: "#.critical"            # all critical urgency events
+  handler: service_provider    # notification service consumes and routes
+
+# 3. ITSM integration subscribes to provider events
+subscribe:
+  queue: dcm.itsm.provider-events
+  binding: "provider.#"            # all provider domain events
+  handler: itsm_provider            # creates ServiceNow incident
+```
+
+---
+
+## 2.8 credential management service — Vault Secret Fetch at Dispatch Time
+
+**Registration:**
+
+```yaml
+service_provider_registration:
+  uuid: pvd-vlt-001
+  name: vault-prod
+  display_name: HashiCorp Vault — credential management service
+  version: "1.0.0"
+  endpoint: https://vault.corp.example:8200
+
+  capabilities:
+    fetch_secret: true
+    rotate_credential: true
+    dynamic_secret: true           # Vault dynamic credentials
+    ttl_management: true
+
+  auth_method: approle
+  approle_role_id: dcm-control-plane
+  approle_secret_uuid: crd-vault-approle-001   # bootstrap credential
+
+  secret_engines:
+    - path: secret/dcm/            # KV v2 for static credentials
+    - path: database/              # dynamic DB credentials
+    - path: pki/                   # certificate issuance
+```
+
+**Fetch flow at dispatch time:**
+
+```yaml
+# Request Payload Processor needs the FreeIPA bind password for the auth provider:
+
+credential_fetch_request:
+  credential_uuid: crd-ipa-bind-001
+  requesting_component: request_payload_processor
+  purpose: auth_provider_bind
+
+# DCM calls credential management service:
+# GET vault.corp.example:8200/v1/secret/data/dcm/providers/auth/freeipa-bind
+# Vault authenticates DCM via AppRole, returns:
+# { "data": { "password": "s3cr3t-b1nd-p4ss" } }
+
+# Credential returned to Payload Processor — never written to any store
+# Used ephemerally for the LDAP bind → discarded after use
+
+# For dynamic DB credentials (short-lived):
+dynamic_credential_fetch:
+  credential_uuid: crd-db-dynamic-001
+  vault_path: database/creds/payments-db-role
+  ttl: PT1H                        # expires after 1 hour
+  # Vault creates a temp DB user, returns: { "username": "v-dcm-pay-1a2b", "password": "..." }
+```
+
+**Consumer retrieves a resource credential:**
+
+```
+GET /api/v1/resources/ent-vm-pay-03/credentials
+
+Response:
+[
+  {
+    "credential_uuid": "crd-vm-pay-03-ssh",
+    "type": "ssh_key",
+    "display_name": "SSH Access Key",
+    "fetch_url": "/api/v1/credentials/crd-vm-pay-03-ssh/value"
+  }
+]
+
+GET /api/v1/credentials/crd-vm-pay-03-ssh/value
+Authorization: Bearer 
+
+Response:
+{
+  "type": "ssh_key",
+  "private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n...",
+  "username": "cloud-user",
+  "expires_at": "2026-04-30T00:00:00Z"
+}
+# Audit record written: act-alice-001 fetched crd-vm-pay-03-ssh at 2026-03-31T10:00:00Z
+```
+
+---
+
+## 2.9 compound service definition — Compound WebApp Provisioning
+
+`webapp-meta` is a compound service definition that composes a VM + IP + Firewall Rule + DNS Record into a single `ApplicationStack.WebApp` catalog item. The consumer requests one thing; DCM provisions four.
+
+**Compound service definition (registered by compound service definition):**
+
+```yaml
+compound service_registration:
+  uuid: pvd-wam-001
+  name: webapp-meta
+  display_name: Web Application Stack (compound service definition)
+
+  resource_types_composed:
+    - fqn: ApplicationStack.WebApp
+      version: "2.0.0"
+      constituents:
+        - component_id: vm
+          resource_type: Compute.VirtualMachine
+          provided_by: external       # DCM places with appropriate compute provider
+          required_for_delivery: required
+
+        - component_id: ip
+          resource_type: Network.IPAddress
+          provided_by: external
+          required_for_delivery: required
+          depends_on: []              # IP can provision in parallel with VM
+
+        - component_id: fw
+          resource_type: Security.FirewallRule
+          provided_by: external
+          required_for_delivery: optional
+          depends_on: [vm, ip]        # needs both IPs before rule can be written
+          inject_from:
+            vm: { source_ip: "$.realized.assigned_ip" }
+            ip: { dest_ip: "$.realized.ip_address" }
+
+        - component_id: dns
+          resource_type: Network.DNSRecord
+          provided_by: external
+          required_for_delivery: optional
+          depends_on: [ip]
+          inject_from:
+            ip: { a_record_value: "$.realized.ip_address" }
+```
+
+**Consumer request (one item, four resources provisioned):**
+
+```
+POST /api/v1/requests
+{
+  "catalog_item_uuid": "cat-webapp-standard",
+  "fields": {
+    "app_name": "payments-portal",
+    "cpu_cores": 4,
+    "ram_gb": 16,
+    "dns_hostname": "payments.corp.example",
+    "firewall_source": "0.0.0.0/0",
+    "firewall_dest_port": 443
+  }
+}
+
+# Composite entity created: ent-app-portal-001 (ApplicationStack.WebApp)
+# DCM decomposes into 4 constituent requests:
+#   ent-vm-portal  → pvd-vm-001    (PROVISIONING)
+#   ent-ip-portal  → pvd-net-001   (PROVISIONING, parallel)
+#   ent-fw-portal  → pvd-fw-001    (PENDING_DEPENDENCY on vm + ip)
+#   ent-dns-portal → pvd-dns-001   (PENDING_DEPENDENCY on ip)
+
+# Composite entity is OPERATIONAL when all required_for_delivery constituents are OPERATIONAL
+# Status visible as one entity to the consumer
+```
+
+---
+
+## 2.10 ITSM integration — ServiceNow Incident on Provider Health Change
+
+**Setup:** `servicenow-prod` is registered and configured to create incidents on provider health events.
+
+```yaml
+itsm_provider_registration:
+  provider_handle: servicenow-prod
+  itsm_system: servicenow
+  endpoint_url: https://corp.service-now.com
+  api_version: v2
+  auth_credential_uuid: crd-sn-api-001
+
+  supported_actions:
+    - create_incident
+    - update_incident
+    - resolve_incident
+    - create_change_request
+
+  field_mappings:
+    # DCM event fields → ServiceNow fields
+    incident:
+      short_description: "$.event.payload.detail"
+      urgency:
+        critical: 1     # ServiceNow urgency: 1=High
+        high: 2
+        medium: 3
+        low: 4
+      assignment_group: "DCM Platform Operations"
+      category: "Infrastructure"
+      subcategory: "Cloud Management"
+      cmdb_ci: "$.event.payload.provider_uuid"
+```
+
+**Event fires: provider goes unhealthy**
+
+```yaml
+# pvd-vm-001 health check fails 3 consecutive times
+event:
+  type: provider.health_changed
+  urgency: high
+  payload:
+    provider_uuid: pvd-vm-001
+    from_state: healthy
+    to_state: unhealthy
+    failure_count: 3
+    detail: "Health endpoint unreachable: connection timeout"
+
+# ITSM integration receives via Message Bus
+# Creates ServiceNow incident:
+
+servicenow_api_call:
+  method: POST
+  path: /api/now/table/incident
+  body:
+    short_description: "DCM Provider Unhealthy: pvd-vm-001 — connection timeout"
+    urgency: 2                        # high → ServiceNow urgency 2
+    assignment_group: DCM Platform Operations
+    category: Infrastructure
+    subcategory: Cloud Management
+    cmdb_ci: pvd-vm-001
+    description: |
+      DCM Provider pvd-vm-001 (VMware Prod) has failed 3 consecutive health checks.
+      Last error: connection timeout
+      Affected resource types: Compute.VirtualMachine
+      Routing: capacity reduced, new requests redirected to pvd-vm-002
+
+# ServiceNow responds: { "sys_id": "INC0087432" }
+# DCM stores: provider pvd-vm-001 → itsm_reference: INC0087432
+
+# When provider recovers:
+servicenow_api_call:
+  method: PATCH
+  path: /api/now/table/incident/INC0087432
+  body:
+    state: 6                          # ServiceNow: resolved
+    close_notes: "DCM Provider pvd-vm-001 returned to healthy state"
+    resolved_at: 2026-03-31T16:45:00Z
+```
+
+---
+
+# Section 3 — Registration Flow Examples
+
+## 3.1 Information Provider Onboarding — NetBox as Network IP Provider
+
+```yaml
+# Step 1: Platform admin issues registration token
+POST /api/v1/admin/registration-tokens
+{
+  "provider_type": "information_provider",
+  "handle_pattern": "providers/information/network/*",
+  "valid_for": "PT24H"
+}
+Response: { "token": "reg-tok-netbox-001", "expires_at": "2026-04-01T09:00:00Z" }
+
+# Step 2: NetBox provider submits registration
+POST /api/v1/admin/providers/register
+Authorization: Bearer reg-tok-netbox-001
+X-Client-Cert: 
+
+{
+  "provider_type": "information_provider",
+  "name": "NetBox — Network IP Registry",
+  "handle": "providers/information/network/netbox-prod",
+  "version": "1.0.0",
+  "endpoint": "https://netbox.corp.example",
+  "implements": [
+    {
+      "information_type_name": "Network.IPAddress",
+      "information_type_version": "1.2.0",
+      "lookup_methods_supported": ["primary_key", "cidr_query"],
+      "extended_fields_supported": true,
+      "extended_schema": {
+        "vrf_id": { "type": "integer" },
+        "site_slug": { "type": "string" },
+        "role": { "type": "string", "enum": ["loopback", "anycast", "secondary"] }
+      }
+    }
+  ],
+  "sovereignty_declaration": {
+    "data_residency": ["US"],
+    "crosses_jurisdiction": false
+  }
+}
+
+# Step 3: Validation (6 checks for information_provider)
+# V1: information_provider enabled in prod profile ✓
+# V2: Governance Matrix pre-check: ALLOW ✓
+# V3: Token valid and matches handle pattern ✓
+# V4: mTLS certificate valid, corp CA chain ✓
+# V5: Sovereignty declaration complete ✓
+# V6: Health endpoint reachable ✓
+
+# Step 4: Platform admin approves
+POST /api/v1/admin/registrations/reg-netbox-001/approve
+{ "rationale": "NetBox is our authoritative IP registry" }
+
+# Step 5: ACTIVE — NetBox now enriches assembly payloads
+# When a request for any resource in dc-west-1 is assembled:
+#   DCM queries NetBox: "give me next available IP in 10.42.18.0/24"
+#   NetBox returns: { "ip": "10.42.18.56", "vrf_id": 4, "site_slug": "dc-west-1" }
+#   IP injected into assembled payload
+```
+
+---
+
+## 3.2 Auth Provider Onboarding — Adding a Secondary OIDC Provider
+
+```yaml
+# Scenario: Adding Azure AD as a secondary auth source for contractors
+POST /api/v1/admin/providers/register
+Authorization: Bearer reg-tok-oidc-001
+X-Client-Cert: 
+
+{
+  "provider_type": "auth_provider",
+  "name": "Azure AD — Contractor Identity",
+  "handle": "providers/auth/azure-ad-contractors",
+  "version": "1.0.0",
+
+  "capabilities": {
+    "authentication": true,
+    "mfa": true,
+    "group_membership": true,
+    "role_mapping": true
+  },
+
+  "protocol": "oidc",
+  "oidc_config": {
+    "issuer": "https://login.microsoftonline.com/{tenant-id}/v2.0",
+    "client_id": "dcm-azure-ad-client",
+    "client_secret_uuid": "crd-azure-oidc-secret",
+    "scopes": ["openid", "profile", "email", "groups"],
+    "group_claim": "groups"
+  },
+
+  "group_mapping": [
+    {
+      "oidc_group_id": "aad-grp-dcm-contractors",
+      "dcm_role": "tenant_member",
+      "tenant_uuid": "ten-web-001",
+      "scope_restriction": {
+        "allowed_resource_types": ["Compute.VirtualMachine"],
+        "max_ttl": "P7D"             # contractor VMs expire after 7 days
+      }
+    }
+  ],
+
+  "precedence": 2                    # lower than FreeIPA (precedence 1)
+                                     # FreeIPA checked first; Azure AD is fallback
+}
+```
+
+---
+
+## 3.3 compound service definition Onboarding
+
+```yaml
+POST /api/v1/admin/providers/register
+Authorization: Bearer reg-tok-meta-001
+
+{
+  "provider_type": "compound service",
+  "name": "Web Application Stack",
+  "handle": "providers/meta/webapp-stack",
+  "version": "2.0.0",
+
+  "resource_types_composed": [
+    {
+      "fqn": "ApplicationStack.WebApp",
+      "version": "2.0.0",
+      "catalog_item_template_uuid": "cat-tmpl-webapp-001",
+      "constituents": [
+        {
+          "component_id": "vm",
+          "resource_type": "Compute.VirtualMachine",
+          "provided_by": "external",
+          "required_for_delivery": "required"
+        },
+        {
+          "component_id": "ip",
+          "resource_type": "Network.IPAddress",
+          "provided_by": "external",
+          "required_for_delivery": "required",
+          "depends_on": []
+        },
+        {
+          "component_id": "fw",
+          "resource_type": "Security.FirewallRule",
+          "provided_by": "external",
+          "required_for_delivery": "optional",
+          "depends_on": ["vm", "ip"]
+        }
+      ]
+    }
+  ],
+
+  "decomposition_policy_handle": "system/meta/webapp-decompose-v2"
+}
+
+# Validation:
+# V1: compound service enabled in profile ✓
+# V2: All constituent resource_types registered in Registry ✓
+# V3: No circular dependencies in constituent graph ✓
+# V4: Decomposition policy handle resolvable ✓
+
+# Once ACTIVE: "ApplicationStack.WebApp" appears in service catalog
+# Consumers request one item; DCM provisions all constituents automatically
+```
+
+---
+
+# Section 4 — OPA Policy Integration Examples
+
+## 4.1 OPA Policy Bundle Delivery and Shadow Mode
+
+**Scenario: New PCI DSS policy deployed in shadow mode before enforcement**
+
+```yaml
+# Policy author submits new policy artifact
+POST /api/v1/admin/policies/submit
+
+{
+  "handle": "compliance/pci/card-data-network-isolation",
+  "version": "1.0.0",
+  "type": "gatekeeper",
+  "enforcement_class": "hard_stop",
+  "status": "proposed",           # starts in shadow mode
+  "opa_bundle_ref": "git://policies/compliance/pci/card-data-isolation@v1.0.0",
+  "applies_to": {
+    "resource_types": ["Compute.VirtualMachine", "Network.VLAN"]
+  },
+  "description": "Card data VMs must be on isolated network segments — not shared with non-PCI workloads"
+}
+```
+
+**Shadow mode evaluation (next 30 days):**
+
+```yaml
+# Policy evaluates against every matching request but does NOT block
+# Results logged as shadow_divergence events
+
+shadow_divergence_event:
+  policy_handle: compliance/pci/card-data-network-isolation@1.0.0
+  request_uuid: req-vm-web-07
+  tenant_uuid: ten-web-001
+  actor_uuid: act-alice-001
+  shadow_result: WOULD_BLOCK
+  reason: "VM requested on shared VLAN 100 — card data isolation requires dedicated VLAN ≥200"
+  # Real decision: ALLOW (shadow mode — not enforced yet)
+
+# Platform admin reviews shadow dashboard after 2 weeks:
+GET /api/v1/admin/policies/compliance/pci/card-data-network-isolation/shadow-report
+
+{
+  "evaluation_period": "2026-03-01 to 2026-03-15",
+  "total_evaluations": 847,
+  "would_block_count": 12,
+  "would_block_pct": 1.4,
+  "top_blocking_reasons": [
+    { "reason": "VLAN < 200", "count": 9 },
+    { "reason": "mixed tenant segment", "count": 3 }
+  ],
+  "recommendation": "Safe to activate — 12 impacted requests in 2 weeks, all addressable"
+}
+
+# Admin activates:
+PATCH /api/v1/admin/policies/compliance/pci/card-data-network-isolation
+{ "status": "active" }
+# Policy now enforced — all future matching requests checked for real
+```
+
+---
+
+## 4.2 OPA Bundle Delivery to External Policy Evaluator
+
+```yaml
+# OPA sidecar (External Policy Evaluator) registered:
+external_policy_evaluation_registration:
+  uuid: pvd-opa-001
+  name: opa-compliance-sidecar
+  mode: sidecar                    # co-deployed with DCM control plane
+  bundle_sources:
+    - handle: compliance/pci/*
+      git_ref: git://policies/pci@main
+      pull_interval: PT5M          # pull fresh bundle every 5 minutes
+    - handle: compliance/hipaa/*
+      git_ref: git://policies/hipaa@main
+      pull_interval: PT5M
+
+# Bundle pull cycle:
+# 1. OPA sidecar polls git repo every 5 minutes
+# 2. New bundle detected (policy updated by compliance team via GitOps PR)
+# 3. OPA loads new bundle — hot reload, no downtime
+# 4. DCM notified: external_policy_evaluation.bundle_updated event
+
+# Policy evaluation call (from Request Orchestrator to OPA sidecar):
+POST /v1/data/dcm/policies/evaluate
+{
+  "input": {
+    "request": {
+      "resource_type": "Compute.VirtualMachine",
+      "tenant_uuid": "ten-pay-001",
+      "payload": { "network_segment": "dmz-payments", "vlan": 142 }
+    },
+    "actor": { "uuid": "act-alice-001", "roles": ["tenant_member"] },
+    "provider": { "uuid": "pvd-vm-001", "accreditations": ["pci_dss", "iso_27001"] }
+  }
+}
+
+Response:
+{
+  "result": {
+    "allow": true,
+    "policies_evaluated": 14,
+    "transformations": [
+      { "field": "cpu_pinning", "value": true, "reason": "PCI isolation" }
+    ],
+    "gatekeepers_fired": 0
+  }
+}
+```
+
+---
+
+# Section 5 — Admin and Consumer GUI Examples
+
+## 5.1 Consumer Portal — New Resource Request Flow
+
+```
+Step 1: Consumer logs in
+  → Redirected to FreeIPA SSO via OIDC
+  → Returns with session token (alice, payments-bu, tenant_member)
+
+Step 2: Service Catalog presented
+  → DCM filters by: alice's roles + payments-bu tenant + prod profile
+  → Shows: [Compute.VirtualMachine, Network.IPAddress, ApplicationStack.WebApp, ...]
+  → Cost estimate shown per catalog item (from Cost Analysis component)
+
+Step 3: Alice selects "Compute.VirtualMachine — Standard"
+  → Form generated from catalog item field_schema
+  → Pre-fill hints from previous requests (last used zone, OS)
+
+Step 4: Pre-request cost estimate
+  POST /api/v1/cost/estimate
+  { "catalog_item_uuid": "cat-vm-standard", "fields": { "cpu_cores": 8, "ram_gb": 32 } }
+  → { "monthly_estimate": "$142.40", "one_time": "$0", "currency": "USD" }
+
+Step 5: Alice submits request
+  → Operation returned: { "name": "/api/v1/operations/req-pay-api-03", "done": false }
+  → Portal polls operation.name every 2 seconds
+  → Progress bar: INITIATED → POLICY_EVALUATION → DISPATCHED → PROVISIONING → OPERATIONAL
+
+Step 6: Resource is OPERATIONAL
+  → Portal shows: IP address, SSH key download link, hostname
+  → Toast notification: "payments-api-03 is ready"
+```
+
+---
+
+## 5.2 Admin GUI — Policy Flow Visualization
+
+The Flow GUI shows the policy evaluation pipeline for a given request as an interactive diagram.
+
+```
+Admin selects: Request req-pay-api-03
+Flow GUI renders:
+
+[ Consumer Input ]
+       │
+       ▼
+[ Layer Assembly ]
+  ├── Base Layer (3.1.0) ✓
+  ├── DC West-1 Layer (2.0.0) ✓
+  ├── DMZ Zone Layer (1.5.0) ✓
+  ├── Payments BU Layer (1.2.0) ✓
+  └── Request Layer ✓
+       │
+       ▼
+[ Policy Evaluation — 14 policies ]
+  ├── GateKeeper: pci-network-isolation ✓ PASS
+  ├── GateKeeper: phi-provider-accreditation ✓ PASS (no PHI in request)
+  ├── Validation: vm-size-limits ✓ PASS
+  ├── Transformation: pci-cpu-pinning → cpu_pinning: true APPLIED
+  └── Transformation: approved-os-image → rhel-9-latest-approved APPLIED
+       │
+       ▼
+[ Placement Engine ]
+  ├── Candidates: [pvd-vm-001 (score:65), pvd-vm-002 (score:36)]
+  └── Selected: pvd-vm-001 (highest accreditation richness)
+       │
+       ▼
+[ Dispatch → pvd-vm-001 ]
+  └── Status: OPERATIONAL ✓
+
+# Clicking any node shows full input/output payload for that step
+# Shadow mode indicator (S) shown on any policy evaluated in shadow
+# Red path shown for any GateKeeper that fired and blocked
+```
+
+---
+
+## 5.3 Admin GUI — Drift Dashboard
+
+```
+Admin opens Drift Dashboard:
+
+Summary:
+  Open drift records: 7
+  ├── Critical: 1  (security config drift — out-of-hours change)
+  ├── High: 3      (resource sizing drift — manual changes)
+  └── Medium: 3    (metadata drift — tags removed)
+
+Critical drift: ent-vm-pay-03
+  Discovered: selinux: permissive
+  Realized:   selinux: enforcing
+  Changed at: 2026-03-30T03:15:00Z (2:00 AM — suspicious)
+  Provider:   pvd-vm-001
+  Policy response: NOTIFY_AND_WAIT → Compliance Team paged
+
+Admin actions available:
+  [Revert to Realized State]  → dispatches remediation to pvd-vm-001
+  [Accept Discovered State]   → updates Realized State record (requires reason)
+  [Investigate]               → opens audit trail for this entity since last drift-clean
+```
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/architecture/specifications/kubernetes-compatibility.md b/content/docs/architecture/specifications/kubernetes-compatibility.md
new file mode 100644
index 0000000..d7b9c00
--- /dev/null
+++ b/content/docs/architecture/specifications/kubernetes-compatibility.md
@@ -0,0 +1,443 @@
+# DCM — Kubernetes Compatibility and Concept Mappings
+
+
+> ## 📋 Draft — Promoted from Work in Progress
+>
+> All questions resolved. Cluster-as-a-Service model defined. Namespace-to-Tenant mapping, admission webhook model, and managed K8s integration all specified.
+>
+> **This section is explicitly a work in progress and is less mature than the core DCM data model and architecture documentation.**
+>
+> The Kubernetes operator integration layer — including the Operator Interface Specification, Operator SDK API, and Kubernetes compatibility mappings — represents design intent that has not yet been validated against implementation. Specific interface contracts, API signatures, SDK method names, and CRD structures **will change** as implementation work begins.
+>
+> **Do not build against these specifications yet.** They are published to share design direction and invite feedback, not as stable contracts.
+>
+> Known gaps and open items for this section:
+> - Operator Interface Specification: reconciliation hook signatures are provisional
+> - Operator SDK API: Go module structure and dependency model not yet finalized
+> - Kubernetes Compatibility Mappings: some concept mappings remain under discussion
+> - SDK code examples are illustrative only — not yet tested against a real implementation
+>
+> Feedback and contributions welcome via [GitHub Issues](https://github.com/dcm-project/issues).
+
+
+
+**Document Status:** ✅ Complete
+**Document Type:** Architecture Reference  
+**Related Documents:** [Foundational Abstractions](../data-model/00-foundations.md) | [Entity Relationships](../data-model/09-entity-relationships.md) | [Resource Type Hierarchy](../data-model/05-resource-type-hierarchy.md) | [Resource/Service Entities](../data-model/06-resource-service-entities.md) | [DCM Operator Interface Specification](dcm-operator-interface-spec.md)
+
+---
+
+## 1. Purpose
+
+> **AEP Alignment:** API endpoint references in this document follow [AEP](https://aep.dev) conventions
+> (custom methods use colon syntax). See `schemas/openapi/dcm-consumer-api.yaml` for the
+> normative OpenAPI specification.
+
+
+DCM is designed as a **superset of Kubernetes** — extending Kubernetes' declarative, controller-based model upward to provide unified management across multiple clusters, infrastructure types, and organizational boundaries that Kubernetes alone cannot address.
+
+This document serves three purposes:
+
+1. **Defines the formal mapping** between Kubernetes concepts and DCM concepts — enabling implementors to understand how the two models relate and where DCM extends beyond Kubernetes
+2. **Establishes DCM Resource Types** for standard Kubernetes resources — so that Kubernetes-managed resources participate in the DCM registry alongside non-Kubernetes resources
+3. **Documents the boundary** between what Kubernetes governs and what DCM governs — making clear that DCM extends Kubernetes rather than replacing it
+
+---
+
+## 2. The Superset Relationship
+
+DCM is a superset of Kubernetes in the sense that it provides all the capabilities Kubernetes provides — and more. An organization running Kubernetes exclusively is using a subset of what DCM can manage. DCM does not replace Kubernetes; it manages the lifecycle of Kubernetes clusters and the resources running on them.
+
+The superset relationship means DCM can manage Kubernetes-native resources (Deployments, Services, PersistentVolumes) through conformant operators, and it can manage the clusters themselves as catalog items. It also means DCM manages resources that have no Kubernetes equivalent — bare metal, VMs, VLANs, IP allocations, and organizational data entities.
+
+### 2.1 What Kubernetes Provides
+
+Kubernetes is a container orchestration platform that provides:
+- Declarative desired-state management within a single cluster
+- A controller/operator pattern for extending resource management
+- Namespace-based isolation within a cluster
+- RBAC for access control within a cluster
+- A rich ecosystem of operators for managing complex stateful resources
+
+### 2.2 What DCM Adds
+
+DCM extends Kubernetes upward by providing:
+
+| Capability | Kubernetes | DCM |
+|------------|-----------|-----|
+| Scope | Single cluster | Multi-cluster, multi-infrastructure |
+| Tenancy | Namespace isolation | First-class Tenant model with ownership |
+| Policy | RBAC + admission webhooks | Full Policy Engine with Validation/Transformation/GateKeeper |
+| Data lineage | Not provided | Field-level provenance on all data |
+| Cost attribution | Not provided | Full lifecycle cost analysis |
+| Drift detection | Basic — controller reconciles | Full four-state model with Intent/Requested/Realized/Discovered |
+| Service catalog | Not provided | Full self-service catalog with RBAC-governed presentation |
+| Sovereignty | Not provided | Sovereignty declarations, placement constraints, compliance evidence |
+| Information context | Labels/annotations | First-class Information Provider relationships |
+| Non-Kubernetes resources | Not provided | VMware, bare metal, OpenStack, etc. all managed through same model |
+
+### 2.3 What DCM Does Not Replace
+
+DCM does not replace Kubernetes at the runtime level. Kubernetes continues to:
+- Schedule and run containers
+- Manage Pod lifecycle within a cluster
+- Enforce network policies within a cluster
+- Provide the Kubernetes API for cluster-native tooling
+- Run operators that manage complex stateful resources
+
+DCM manages the management plane — the lifecycle of what gets requested, provisioned, owned, governed, and decommissioned. Kubernetes manages the execution plane — the runtime behavior of what is running.
+
+---
+
+## 3. Core Concept Mappings
+
+### 3.1 Resource Model
+
+| Kubernetes Concept | DCM Concept | Relationship | Notes |
+|-------------------|-------------|--------------|-------|
+| Custom Resource Definition (CRD) | Resource Type Specification | CRD schema → DCM Resource Type fields | DCM Resource Type is the portable, provider-agnostic equivalent. CRD is the Kubernetes-specific implementation schema. |
+| Custom Resource (CR) | Requested State payload → Realized State entity | CR is the naturalized form of the DCM payload | The operator translates DCM Requested State into a CR (Naturalization) and translates CR status back to DCM Realized State (Denaturalization). |
+| Built-in resource (Pod, Service, PV) | DCM Resource Type in Compute.*, Network.*, Storage.* | Kubernetes built-ins are valid DCM Resource Types | See Section 5 for standard Kubernetes resource type mappings. |
+| Kubernetes object | Resource/Service Entity | Every Kubernetes object managed by DCM has a corresponding DCM entity with UUID and provenance | |
+
+### 3.2 Control Loop
+
+| Kubernetes Concept | DCM Concept | Relationship | Notes |
+|-------------------|-------------|--------------|-------|
+| Operator reconciliation loop | Realization + Drift Detection combined | Reconciliation IS the realization process — the operator drives actual state toward desired state | DCM's Drift Detection compares Discovered State against Realized State. The operator's reconciliation loop is the mechanism that corrects drift. |
+| Desired state (CR spec) | Requested State | CR spec is the naturalized form of the DCM Requested State | DCM stores the Requested State in DCM format. The operator translates it to CR spec format. |
+| Actual state (CR status) | Realized State | CR status is the Kubernetes-native form of the DCM Realized State | The operator must denaturalize CR status back to DCM Realized State format and report it to DCM. |
+| Watch/Inform pattern | DCM Discovered State polling | Kubernetes watch events are the mechanism for keeping DCM Discovered State current | |
+
+### 3.3 Isolation and Multi-tenancy
+
+| Kubernetes Concept | DCM Concept | Relationship | Notes |
+|-------------------|-------------|--------------|-------|
+| Namespace | DCM Tenant boundary | One namespace per DCM Tenant (per_tenant strategy) | Kubernetes namespace provides the physical isolation enforcement. DCM Tenant provides the ownership and governance model. A single DCM Tenant maps to exactly one namespace per cluster. |
+| Namespace | DCM Resource Group | In shared namespace strategies, Resource Group labels replace namespace isolation | When multiple Tenants share a namespace, DCM Resource Group labels provide logical separation. |
+| Kubernetes RBAC | DCM IDM/IAM + Policy Engine | Kubernetes RBAC is the runtime enforcement mechanism. DCM Policy Engine governs who can request what via the service catalog. | DCM policies determine what a user can request. Kubernetes RBAC determines what a running workload can do. These are complementary, not duplicative. |
+| ServiceAccount | DCM Identity.ServiceAccount Information Type | Kubernetes ServiceAccounts that DCM provisions or references are modeled as DCM Information Type entities | |
+
+### 3.4 Relationships and Dependencies
+
+| Kubernetes Concept | DCM Concept | Relationship | Notes |
+|-------------------|-------------|--------------|-------|
+| ownerReference | Entity Relationship (`contains`/`contained_by`) | Kubernetes ownerReferences are a subset of DCM entity relationships — ownership only | DCM relationships are richer — supporting `requires`, `depends_on`, `references`, `peer`, `manages` in addition to ownership. During Denaturalization, ownerReferences are translated to DCM `contains` relationships. |
+| Finalizers | Lifecycle policy (`retain`, `detach`) | Kubernetes finalizers implement DCM lifecycle policies at the Kubernetes level | When DCM declares `on_parent_destroy: retain` for a storage entity, the operator implements this using Kubernetes finalizers to prevent deletion until DCM confirms the lifecycle policy has been applied. |
+| Label selectors | Resource Group membership | Kubernetes label selectors used for DCM Resource Group filtering | DCM mandatory labels (`dcm-tenant-id`, `dcm-entity-id`) are used as label selectors for Resource Group queries. |
+
+### 3.5 Data Model
+
+| Kubernetes Concept | DCM Concept | Relationship | Notes |
+|-------------------|-------------|--------------|-------|
+| Labels | DCM entity metadata + relationships | DCM-mandatory labels (`dcm-managed`, `dcm-tenant-id`, `dcm-entity-id`, etc.) carry core DCM identity data. Custom labels may map to DCM Information Type relationships. | |
+| Annotations | DCM field-level provenance + metadata | Annotations used by DCM to carry request correlation data during the request lifecycle | `dcm-request-id` annotation on a CR identifies the DCM request that created or last modified it — enabling unsanctioned change detection. |
+| Resource version | Entity version (Revision component) | Kubernetes resource versions map to DCM entity Revision increments | Major and Minor versions are managed by DCM based on breaking/non-breaking changes. Kubernetes resource version increments map to DCM Revision increments. |
+| Generation | Requested State version | CR generation increments correspond to new DCM Requested State records | Each new generation of a CR corresponds to a new intent/request cycle in DCM. |
+
+### 3.6 Lifecycle
+
+| Kubernetes Concept | DCM Concept | Relationship | Notes |
+|-------------------|-------------|--------------|-------|
+| Pod phases (Pending, Running, Succeeded, Failed, Unknown) | DCM lifecycle states | Pod phases map to DCM lifecycle states via condition_mappings declaration | |
+| CRD conditions | DCM lifecycle states and events | Standard conditions (Ready, Degraded, Progressing) map to DCM states and events via the field mapping specification | |
+| Kubernetes events | DCM lifecycle events | Kubernetes watch events trigger DCM lifecycle event reports | The operator translates Kubernetes events into DCM lifecycle event types (ENTITY_HEALTH_CHANGE, DEGRADATION, UNSANCTIONED_CHANGE, etc.) |
+| Cluster deletion | DCM decommission workflow | Cluster deletion triggers DCM's full decommission lifecycle — lifecycle policies applied to all related entities | |
+
+---
+
+
+## 3a. Cluster as a Service — The Primary Model
+
+A Kubernetes cluster is a first-class catalog item in DCM. Any authorized Tenant can request and own a cluster through the service catalog, the same way they request a VM or a network. This is not a special case — it is the expected primary consumption model for Kubernetes infrastructure in DCM.
+
+**How it works:**
+
+```yaml
+catalog_item: Platform.KubernetesCluster
+provider: CAPI-based Service Provider (or managed K8s Service Provider)
+tenant_uuid: 
+
+entity:
+  resource_type: Platform.KubernetesCluster
+  tenant_uuid:    # Tenant owns the cluster
+  lifecycle_state: OPERATIONAL
+  fields:
+    kubernetes_version: "1.29"
+    node_count: 3
+    api_endpoint: "https://cluster-01.eu-west.example.com"
+    kubeconfig_ref:   # via credential management service
+```
+
+**Ownership scope:** When a Tenant owns a `Platform.KubernetesCluster` entity, that Tenant owns everything within the cluster boundary — including cluster-scoped resources (ClusterRoles, StorageClasses, PersistentVolumes, CRDs registered for that cluster). The cluster entity is the ownership boundary. DCM treats the cluster as an opaque resource from a Tenant ownership perspective — the Tenant gets the cluster; what's inside it belongs to them.
+
+**The compound service definition pattern:** A Cluster-as-a-Service catalog item typically composes multiple constituent resources:
+```yaml
+Platform.KubernetesCluster → constituent providers:
+  - Compute resources (control plane + worker nodes)
+  - Network resources (load balancer, ingress)
+  - Storage resources (CSI driver + storage class)
+  - DNS records (cluster API endpoint)
+  - Credential issuance (kubeconfig via credential management service)
+```
+
+This is a compound service definition — the cluster catalog item orchestrates all constituents and presents a single entity to the Tenant.
+
+**Sovereignty and accreditation:** Cluster placement follows the standard Placement Engine model. Sovereignty constraints declared by the Tenant apply to cluster placement — a GDPR-scoped Tenant requesting a cluster gets a cluster placed in an EU sovereignty zone. The CAPI provider (or managed K8s Service Provider) must hold appropriate accreditations.
+
+**Post-provision:** Once the cluster is OPERATIONAL, it can optionally register with DCM as a nested Service Provider for workload resources. The Tenant can then request workload resources (Deployments, Services, PersistentVolumes) against their cluster through the same DCM service catalog. This creates the superset model: DCM provisions the cluster → cluster becomes a workload Service Provider → Tenant uses DCM to manage workloads on their cluster.
+
+
+## 4. Where DCM Extends Beyond Kubernetes
+
+These are capabilities that exist in DCM but have no Kubernetes equivalent. None of these require Kubernetes to be present — they operate across all provider types. For organizations running pure Kubernetes estates, these are the capabilities DCM brings that Kubernetes tooling alone cannot provide.
+
+**Summary of extensions:**
+
+| DCM Capability | Kubernetes Gap |
+|---------------|---------------|
+| Intent State | No concept of original consumer intent separate from desired state |
+| Field-Level Provenance | No field lineage — a field is a field |
+| Data Layers and Assembly | No layering model — manifests are flat declarations |
+| Policy Engine | Admission webhooks are cluster-scoped, admission-time only |
+| Cost Analysis | No native cost attribution in the request lifecycle |
+| Information Providers | No structured external organizational data relationships |
+| Cross-Cluster Lifecycle | Single-cluster scope — multi-cluster requires external tooling |
+
+These are concepts that exist in DCM but have no Kubernetes equivalent. They are the capabilities DCM adds that justify the superset positioning.
+
+### 4.1 Intent State
+
+Kubernetes has no concept of a consumer's original intent separate from the desired state. Once you apply a manifest, Kubernetes only knows the current desired state — not what the consumer originally asked for or why.
+
+DCM's Intent State is the immutable record of what the consumer asked for, stored before any policy processing or layer enrichment. This enables:
+- Rehydration — replaying the original intent through current policies to produce a new request
+- Intent portability — the same intent applied to a different provider
+- Audit — answering "what did the consumer originally ask for?" independently of what was realized
+
+### 4.2 Field-Level Provenance
+
+Kubernetes has no concept of where a field value came from or why it was set. A field in a CR spec is a field — there is no lineage.
+
+DCM's field-level provenance carries the full lineage of every field value through the entire lifecycle — which layer set it, which policy modified it, which provider realized it, and why each change was made. This enables complete audit trails and sovereignty evidence.
+
+### 4.3 Data Layers and Assembly
+
+Kubernetes has no equivalent to DCM's layering model. A Kubernetes manifest is a flat declaration — there is no concept of organizational standards, site-specific configuration, and service-specific configuration being separate layers that compose into a final manifest.
+
+DCM's layering model enables 36 layer definitions to govern 40,000 VMs without duplication — impossible in the Kubernetes model.
+
+### 4.4 Policy Engine
+
+Kubernetes admission webhooks provide some policy capability (validation, mutation) but are cluster-scoped, apply at admission time only, and have no concept of hierarchy (Global → Tenant → User policy levels) or field-level override control.
+
+DCM's Policy Engine operates at the management plane level, applies across all clusters and providers, enforces a three-level hierarchy with field-level override control (allow/constrained/immutable), and carries policy decisions as provenance metadata in the payload.
+
+### 4.5 Cost Analysis
+
+Kubernetes has no native cost attribution model. Tools like Kubecost exist but are add-ons with no integration into the request lifecycle.
+
+DCM's cost analysis is built into the lifecycle model — cost attribution is tracked from request time through realization, operation, and decommission for every entity.
+
+### 4.6 Information Providers
+
+Kubernetes has no concept of structured relationships to external organizational data (Business Units, Cost Centers, Product Owners). Labels and annotations are unstructured key-value pairs with no type safety, no external system integration, and no verification model.
+
+DCM's Information Provider model gives every entity structured, verified, versioned relationships to external organizational data with a stable external key model.
+
+### 4.7 Cross-Cluster Lifecycle
+
+Kubernetes manages resources within a single cluster. Multi-cluster management requires additional tools (ACM, Argo CD, Fleet) that are not part of the core Kubernetes model.
+
+DCM manages the lifecycle of resources across multiple clusters as a first-class capability — the same Resource Type can be instantiated on any cluster that has a conformant Service Provider registered.
+
+---
+
+## 5. Standard Kubernetes Resource Type Mappings
+
+These are the DCM Resource Type registry entries for standard Kubernetes resource types. Operators implementing these types should use these registry UUIDs and field definitions.
+
+### 5.1 Compute
+
+| DCM Resource Type | Kubernetes Equivalent | Notes |
+|------------------|----------------------|-------|
+| `Compute.Pod` | Pod | Lowest-level compute unit |
+| `Compute.Container` | Container (within a Pod) | Sub-entity of Pod — expanded via bundled declaration |
+| `Compute.Deployment` | Deployment | Managed set of Pods |
+| `Compute.StatefulSet` | StatefulSet | Stateful managed set of Pods |
+| `Compute.Job` | Job | One-time execution workload |
+| `Compute.CronJob` | CronJob | Scheduled execution workload |
+
+### 5.2 Network
+
+| DCM Resource Type | Kubernetes Equivalent | Notes |
+|------------------|----------------------|-------|
+| `Network.Service` | Service | In-cluster service discovery and load balancing |
+| `Network.Ingress` | Ingress | External HTTP/HTTPS routing |
+| `Network.NetworkPolicy` | NetworkPolicy | In-cluster network isolation |
+
+### 5.3 Storage
+
+| DCM Resource Type | Kubernetes Equivalent | Notes |
+|------------------|----------------------|-------|
+| `Storage.PersistentVolume` | PersistentVolume | Cluster-level storage resource |
+| `Storage.PersistentVolumeClaim` | PersistentVolumeClaim | Consumer's storage declaration — expanded into Storage.PersistentVolume relationship |
+| `Storage.StorageClass` | StorageClass | Storage type definition — maps to DCM Provider Catalog Item |
+| `Storage.ConfigMap` | ConfigMap | Configuration data storage |
+| `Storage.Secret` | Secret | Sensitive data storage |
+
+### 5.4 Platform
+
+| DCM Resource Type | Kubernetes Equivalent | Notes |
+|------------------|----------------------|-------|
+| `Platform.KubernetesCluster` | Kubernetes Cluster (via CAPI or managed service) | The cluster itself is a DCM-managed resource |
+| `Platform.Namespace` | Namespace | Maps to DCM Tenant boundary in per_tenant strategy |
+| `Platform.CustomResourceDefinition` | CRD | CRD registration maps to DCM Resource Type registration |
+
+### 5.5 Identity
+
+| DCM Resource Type | Kubernetes Equivalent | Notes |
+|------------------|----------------------|-------|
+| `Security.ServiceAccount` | ServiceAccount | Kubernetes identity for workloads |
+| `Security.Role` | Role / ClusterRole | Kubernetes RBAC role |
+| `Security.RoleBinding` | RoleBinding / ClusterRoleBinding | Kubernetes RBAC binding |
+
+---
+
+## 6. The Kubernetes Information Provider
+
+Kubernetes clusters function as both Service Providers (for provisioning resources) and Information Providers (for querying existing state). As an Information Provider, a Kubernetes cluster exposes its current resource state to DCM for:
+
+- **Brownfield ingestion** — discovering existing resources and bringing them under DCM lifecycle management
+- **Discovered State** — DCM's Discovered State for Kubernetes resources comes from querying the Kubernetes API
+- **Drift detection** — comparing DCM Realized State against what Kubernetes actually has
+
+### 6.1 Kubernetes as Information Provider Registration
+
+```yaml
+information_provider_registration:
+  name: kubernetes-cluster-01
+  implements:
+    - information_type: Platform.KubernetesCluster
+    - information_type: Compute.Pod
+    - information_type: Storage.PersistentVolume
+    # ... all resource types the cluster contains
+  endpoint: 
+  kubernetes_credentials:
+    auth_method: 
+  discovery_capabilities:
+    label_selector: "dcm-managed=true"
+    # Only returns DCM-managed resources by default
+    full_discovery: true
+    # Can also return all resources for brownfield ingestion
+```
+
+### 6.2 Discovered State from Kubernetes
+
+DCM queries the Kubernetes API using the Kubernetes Information Provider to populate Discovered State:
+
+```
+DCM Drift Detection
+  │
+  ▼
+Kubernetes Information Provider
+  │  GET /apis/{group}/{version}/namespaces/{ns}/{kind}
+  │  Filter: label dcm-entity-id = {entity_uuid}
+  ▼
+Discovered State payload (DCM format)
+  │  Kubernetes object denaturalized to DCM format
+  ▼
+Compare against Realized State
+  │  Field-by-field comparison
+  ▼
+UNSANCTIONED_CHANGE if differences found
+  │  Reported to Policy Engine for response determination
+```
+
+---
+
+## 7. Kubernetes-Native Patterns and DCM Equivalents
+
+### 7.1 GitOps
+
+Kubernetes GitOps (Argo CD, Flux) manages Kubernetes manifests in Git and synchronizes them to clusters. DCM's data model is also Git-based — all layers, Resource Type definitions, and policy definitions are stored in Git.
+
+The relationship: DCM manages the **request lifecycle** (what gets asked for, approved, and provisioned). GitOps manages the **deployment lifecycle** (what gets deployed to a cluster from a Git repository). These are complementary:
+
+- DCM governs the provisioning request — "is this consumer allowed to provision this resource?"
+- GitOps deploys application code to the provisioned resource
+- DCM and GitOps together form a complete lifecycle: DCM provisions the cluster, GitOps deploys applications to it
+
+### 7.2 Helm
+
+Helm charts are packages of Kubernetes manifests that can be parameterized. In DCM terms, a Helm chart is a form of Catalog Item — a curated, parameterized offering of a set of Kubernetes resources.
+
+DCM does not replace Helm — it can use Helm as a delivery mechanism inside a Service Provider. The Service Provider receives the DCM Requested State, translates it to Helm values, and uses Helm to deploy the resources. The operator pattern is preferred for Day 2 management (Helm has limited reconciliation), but Helm remains valid for initial provisioning.
+
+### 7.3 Cluster API (CAPI)
+
+CAPI is the Kubernetes sub-project for managing Kubernetes clusters themselves using the Kubernetes API and operator pattern. CAPI clusters are a natural fit for DCM's `Platform.KubernetesCluster` Resource Type — a CAPI-based operator would be the Service Provider for provisioning new Kubernetes clusters as DCM-managed resources.
+
+This is particularly significant: DCM managing the lifecycle of Kubernetes clusters through CAPI means DCM can provision the very infrastructure that operators run on. The superset relationship becomes concrete — DCM provisions the cluster, the cluster runs the operators, the operators provision the resources that DCM manages.
+
+---
+
+## 8. Incremental Adoption — Kubernetes-Native to DCM-Managed
+
+Organizations running Kubernetes can adopt DCM incrementally across these phases:
+
+### Phase 1 — Observation (no operator changes)
+Deploy DCM with the Kubernetes Information Provider. DCM observes existing resources via the Kubernetes API and builds a Discovered State inventory. No changes to existing operators or workloads.
+
+### Phase 2 — Brownfield Ingestion (no operator changes)
+DCM promotes Discovered State records to Realized State — assuming lifecycle management of existing resources. Resources get DCM UUIDs, Tenant assignments, and provenance records. Existing resources are now DCM-managed without any operator changes.
+
+### Phase 3 — Level 1 Conformance (minimal operator changes)
+Operators implement Level 1 of this specification via the DCM Operator SDK. New resources are provisioned through DCM's service catalog. Existing resources managed via brownfield ingestion continue as-is.
+
+### Phase 4 — Level 2 Conformance (moderate operator changes)
+Operators implement Level 2 — full field mappings, capacity reporting, lifecycle events. DCM gains placement intelligence, drift detection, and cross-cluster management capabilities.
+
+### Phase 5 — Level 3 Conformance (complete integration)
+Operators implement Level 3 — sovereignty declarations, provenance, discovery endpoint. Full DCM capabilities available.
+
+---
+
+## 9. Open Questions
+
+| # | Question | Impact | Status |
+|---|----------|--------|--------|
+| 1 | How does the Namespace-to-Tenant mapping work when a cluster has existing namespaces that predate DCM adoption? | Brownfield migration | ✅ Resolved |
+| 2 | Should `Platform.KubernetesCluster` be the boundary for a DCM deployment, or can DCM manage resources across clusters without treating the cluster as a DCM entity? | Architecture scope | ✅ Resolved |
+| 3 | How does DCM interact with Kubernetes admission webhooks — do they duplicate Policy Engine functions or complement them? | Policy model | ✅ Resolved |
+| 4 | Should the Kubernetes Information Provider be a built-in DCM component or a separately deployed provider? | Deployment architecture | ✅ Resolved |
+| 5 | How does the DCM superset model interact with managed Kubernetes services (EKS, GKE, AKS) where cluster management is outside the user's control? | Cloud provider integration | ✅ Resolved |
+
+---
+
+## 10. Related Concepts
+
+- **DCM Operator Interface Specification** — the technical contract for operators integrating with DCM
+- **DCM Operator SDK** — Go library implementing this specification for operator developers
+- **Entity Relationships** — DCM's universal relationship model, of which Kubernetes ownerReferences are a subset
+- **Resource Type Hierarchy** — the DCM registry where Kubernetes Resource Types are registered
+- **Information Providers** — the DCM model for the Kubernetes API as a discoverable information source
+- **Four States** — DCM's Intent/Requested/Realized/Discovered model, which extends Kubernetes' desired/actual model
+
+---
+
+
+
+## Resolution Notes
+
+**Q1:** Pre-existing namespaces are handled by the brownfield ingestion model. Each namespace maps to one DCM Tenant. Resources without clear ownership land in the `__transitional__` Tenant and are promoted by a platform admin. Same flow as brownfield VM ingestion — no special handling required.
+
+**Q2:** DCM manages resources across multiple clusters simultaneously. `Platform.KubernetesCluster` is a DCM-managed resource type — both something DCM provisions as a catalog item (Cluster as a Service) and something DCM tracks when externally provisioned. A Tenant can own a full cluster as a catalog item; the cluster is not the boundary of a DCM deployment. DCM's organizational boundary is the Tenant. A single DCM deployment routes requests to Service Providers across many clusters, and can provision new clusters as service catalog items.
+
+**Q3:** Admission webhooks and the DCM Policy Engine are complementary layers, not duplicates. Admission webhooks enforce cluster-native policy (security contexts, image policies, resource quotas). The DCM Policy Engine enforces DCM request policy (business rules, data governance, sovereignty). A DCM-managed workload resource is validated by both — DCM Policy Engine before dispatch, admission webhook at the cluster. This is defense in depth.
+
+**Q4:** The Kubernetes Information Provider is a separately deployed provider that registers with DCM as a standard Information Provider. It serves cluster state, namespace inventory, and workload status. There are no built-in Information Providers in DCM's architecture — all Information Providers follow the unified base contract and are independently deployable.
+
+**Q5:** Managed Kubernetes services (EKS, GKE, AKS) register as Service Providers of resource type `Platform.ManagedKubernetesCluster`. DCM manages workload resources within the cluster (Deployments, Services, PersistentVolumes) but explicitly does not manage the cluster control plane. Sovereignty enforcement applies at cluster selection — DCM places workloads on clusters satisfying sovereignty constraints. The cloud provider manages cluster infrastructure.
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/capabilities-matrix.md b/content/docs/capabilities-matrix.md
new file mode 100644
index 0000000..78e364c
--- /dev/null
+++ b/content/docs/capabilities-matrix.md
@@ -0,0 +1,715 @@
+# DCM — Foundational Capabilities Matrix
+
+> **Purpose:** This document defines the core operational capabilities required for DCM to perform lifecycle management as defined by the data model. Each capability maps to a consumer/service provider perspective and will be used to drive implementation work in Jira.
+>
+> **How to read this document:**
+> - **Capability Domain** — the architectural area the capability belongs to
+> - **Capability** — a discrete operational function; the smallest unit of independently implementable behavior
+> - **Consumer perspective** — what the end user / application team experiences
+> - **Service Provider perspective** — what the Service Provider or platform component must implement
+> - **Platform/Admin perspective** — what the platform engineer or SRE must configure or operate
+> - **Depends on** — other capabilities that must exist first
+
+---
+
+## 1. Identity and Access Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| IAM-001 | Actor Authentication | Authenticate to DCM via configured IdP | — | Register and configure Auth Providers; manage local user store | — |
+| IAM-002 | Session Token Management | Receive and use session tokens; token refresh | — | Configure session TTL, failover chain | IAM-001 |
+| IAM-003 | Role-Based Access Control | Receive role-appropriate service catalog and API responses | — | Declare role mappings; assign roles to actors | IAM-001 |
+| IAM-004 | Group Membership Resolution | Group memberships automatically applied from IdP | — | Map IdP groups to DCM groups; declare group-role relationships | IAM-001, IAM-003 |
+| IAM-005 | Multi-Factor Authentication | Satisfy per-session and step-up MFA challenges | — | Configure MFA methods; declare step-up operations | IAM-001 |
+| IAM-006 | SCIM Automated Provisioning | Actor created/updated/deprovisioned from IdP automatically | — | Configure SCIM endpoint and attribute mappings | IAM-001 |
+| IAM-007 | Tenant Scope Enforcement | Access restricted to authorized Tenants | — | Declare Tenant membership; configure cross-tenant policies | IAM-003, IAM-004 |
+| AUTH-002 | Multi-Auth-Provider Routing | — | — | Register multiple Auth Providers simultaneously; ingress routes by authentication signal | IAM-001 |
+| AUTH-003 | Auth Provider Trust Level Enforcement | Requests evaluated per provider trust level (authoritative / verified / advisory) | — | Configure trust level per registered Auth Provider | IAM-001 |
+| AUTH-004 | Auth Provider Artifact Versioning | — | — | Manage role/tenant mapping versioning through standard DCM artifact lifecycle; activate/deprecate mappings | IAM-003 |
+| AUTH-005 | Auth Provider Failover | Existing sessions remain valid on provider failure; new auth routes to failover chain | — | Configure failover chain; monitor provider health; manage session cache TTL | IAM-001 |
+| AUTH-006 | Auth Context in Audit Trail | — | — | Auth Provider identity and ingress context automatically recorded in all audit records | IAM-001 |
+| AUTH-007 | Auth Provider Credential Security | — | — | Enforce Auth Provider config credentials reference secrets management; no plaintext credentials | IAM-001 |
+| AUTH-008 | No Anonymous Access | — | — | Enforce authenticated access at all ingress surfaces across all profiles | IAM-001 |
+| AUTH-009 | Webhook and Message Bus Authentication | Authenticate webhook registrations | — | Enforce authentication on all inbound surfaces regardless of profile | IAM-001 |
+| AUTH-010 | Per-Actor Rate Limiting | Receive 429 responses when rate limit exceeded | — | Configure rate limits per actor; manage burst allowances | IAM-001 |
+| AUTH-011 | Git PR Identity Resolution | Git PR submissions resolve to same actor identity as API/UI login | — | Configure Auth Provider to trust Git server's identity assertion | IAM-001 |
+| AUTH-012 | SCIM Automated Provisioning | Actor created/updated/deprovisioned from IdP automatically via SCIM | — | Configure SCIM 2.0 endpoint; manage suspension-on-deprovision policy | IAM-001 |
+| AUTH-013 | In-Flight Request Continuity on Auth Failure | In-flight requests before auth failure are not interrupted | — | Configure session cache TTL; manage graceful degradation | IAM-001 |
+| AUTH-014 | Two-Tier MFA Enforcement | Satisfy per-session MFA at login and step-up MFA for high-risk operations | — | Configure per-session and step-up MFA; declare step-up trigger operations | IAM-005 |
+| AUTH-015 | Built-In Auth Provider Storage Backend | — | — | Configure built-in Auth Provider storage backend (SQLite for minimal/dev; PostgreSQL/MySQL for standard+) | IAM-001 |
+
+---
+
+## 2. Service Catalog
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| CAT-001 | Service Catalog Presentation | Browse available services filtered by RBAC | Declare catalog items for offered resource types | Activate catalog items; configure catalog visibility policies | IAM-003, IAM-007 |
+| CAT-002 | Service Schema Discovery | View field schemas, constraints, and edit constraints for a catalog item | Declare field schemas in Resource Type Spec | Configure constraint visibility level per profile | CAT-001 |
+| CAT-003 | Catalog Item Search and Filter | Search catalog by keyword, resource type, tag | — | Configure Search Index for catalog | CAT-001 |
+| CAT-004 | Catalog Item Versioning | Request a specific version of a catalog item | Publish new catalog item versions following semver | Manage version lifecycle; enforce deprecation timelines | CAT-001 |
+| CAT-005 | Cost Estimation | Receive estimated cost before submitting a request | Declare cost metadata on provider registration | Configure Cost Analysis component | CAT-001 |
+| CAT-006 | Dependency Visualization | See required dependencies for a catalog item before requesting | Declare dependency graph in Resource Type Spec | — | CAT-001 |
+| CAT-007 | Catalog Item Deprecation | Receive deprecation warnings on deprecated catalog items | Declare successor types in deprecation notice | Manage deprecation lifecycle; notify consumers | CAT-004 |
+
+---
+
+## 3. Request Lifecycle Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| REQ-001 | Submit Service Request | Submit a resource request via UI, API, or Git PR | — | Configure request ingress surfaces | IAM-007, CAT-001 |
+| REQ-002 | Intent State Capture | Request stored as versioned GitOps artifact before processing | — | Configure Intent Store; manage Git repository structure | REQ-001 |
+| REQ-003 | Layer Assembly | Request enriched with organizational defaults and context layers | Contribute Service Layers for resource types | Manage Core Layers; configure Layer Cache | REQ-002 |
+| REQ-004 | Policy Evaluation | Request validated, transformed, and gated by applicable policies | Contribute provider-specific policies | Manage Policy Engine; configure Policy Groups and Profiles | REQ-003 |
+| REQ-005 | Placement Engine Execution | Resource placed with the best available provider instance | Implement capacity reserve_query response | Configure placement constraints; manage provider priorities | REQ-004 |
+| REQ-006 | Requested State Persistence | Assembled payload stored as authoritative GitOps record | — | Configure Requested Store; manage storage redundancy | REQ-005 |
+| REQ-007 | Provider Dispatch | Request payload delivered to selected provider | Implement Services API to receive DCM payloads | Configure API Gateway and egress | REQ-006 |
+| REQ-008 | Request Status Tracking | Monitor request status from submitted through realized | Report realization status back to DCM | Configure observability for request tracking | REQ-007 |
+| REQ-009 | Request Cancellation | Cancel a pending request before realization | Handle cancellation payloads | Configure cancellation policies | REQ-002 |
+| REQ-010 | Git PR Request Ingress | Submit requests via Git Pull Request with policy dry-run feedback | — | Configure Git Request Watcher; manage repository structure | REQ-001, IAM-001 |
+
+---
+
+## 4. Provider Contract and Realization
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| PRV-001 | Provider Registration | — | Register provider with DCM: declare type, capabilities, sovereignty, cost metadata | Configure Provider Registry; validate sovereignty declarations | IAM-001 |
+| PRV-002 | Naturalization | — | Convert DCM unified payload to provider-native format | — | PRV-001, REQ-007 |
+| PRV-003 | Realization | — | Execute required actions to provision/configure/change resource | — | PRV-002 |
+| PRV-004 | Denaturalization | — | Convert provider-native result back to DCM unified format | — | PRV-003 |
+| PRV-005 | Realized State Reporting | — | Report realized payload and status to DCM API Gateway | Configure Realized State Store; manage Event Stream | PRV-004 |
+| PRV-006 | Capacity Reporting | — | Respond to reserve_query with current capacity and availability | Configure placement engine; manage capacity confidence | PRV-001 |
+| PRV-007 | Provider Health Reporting | — | Expose health check endpoint; report availability | Monitor provider health; configure trust score updates | PRV-001 |
+| PRV-008 | Sovereignty Declaration Maintenance | — | Notify DCM when sovereignty data changes within declared SLA | Monitor sovereignty changes; trigger re-evaluation | PRV-001 |
+| PRV-009 | Meta Provider Orchestration | — | Compose sub-providers to deliver higher-order services; manage composition visibility | Configure composite provider federation eligibility | PRV-001, PRV-003 |
+| PRR-001 | OpenAPI Spec Declaration (GATE-SP-01) | — | Declare OpenAPI spec URL at registration; spec must be machine-readable and reachable | Validate spec URL reachability during approval pipeline | PRV-001 |
+| PRR-002 | Healthy API at Activation (GATE-SP-02) | — | Health endpoint returns `{"status": "healthy"}` at activation time | Enforce health check as activation precondition | PRV-001, HLT-001 |
+| PRR-003 | State Management Callback (GATE-SP-03) | — | Implement realized_state_push callback at all conformance levels | Validate callback endpoint reachability during approval | PRV-001 |
+| PRR-004 | Tenant Metadata Endpoint (GATE-SP-04) | — | Implement GET /api/v1/tenants/{uuid}/metadata returning usage data | Require for standard+ profile activation; enforce quota integration | PRV-001 |
+| PRR-005 | Prometheus Metrics (GATE-SP-05) | — | Expose required metric families at declared metrics_endpoint | Validate metric presence during approval; gate standard+ activation | PRV-001, HLT-005 |
+| PRR-006 | AEP.DEV Linting (GATE-SP-06) | — | Pass AEP linter against OpenAPI spec with no errors before registration; include linting report URL | Gate standard+ activation on linting pass; block activation on errors | PRV-001 |
+
+| PRV-010 | Provider Sandbox/Test Mode | Submit test requests targeting sandbox providers via `_test_context.target_provider_uuid`; sandbox providers visible in registry with `status: sandbox` | Register with `sandbox_mode: true`; implement full OIS contract; graduate to production via standard approval | Manage sandbox provider registry; review graduation requests; sandbox providers excluded from production placement | PRV-001, GATE-SP-01 || PRR-007 | Multi-Tenant Dispatch (GATE-SP-07) | — | Accept tenant_uuid in all dispatch payloads; return tenant-scoped resources | Gate standard+ activation on multi-tenant compatibility test | PRV-001 |
+
+---
+
+## 5. Resource Lifecycle Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| LCM-001 | Resource State Transitions | Trigger lifecycle actions: suspend, resume, decommission | Handle state transition payloads | Configure lifecycle policies; manage state machine | REQ-008 |
+| LCM-002 | Post-Realization Field Updates | Update editable fields on realized resources (targeted delta) | Handle delta update payloads; apply partial changes | Configure editable field declarations; manage edit policies | PRV-005 |
+| LCM-003 | Resource TTL Management | Declare and extend resource TTLs; receive expiry notifications | Handle TTL-triggered decommission payloads | Configure Lifecycle Constraint Enforcer; manage expiry policies | LCM-001 |
+| LCM-004 | Ownership Transfer | Transfer resource ownership to a different Tenant | — | Authorize and execute ownership transfers; record transfer history | IAM-007, LCM-001 |
+| LCM-005 | Rehydration | Replay a resource's intent state to a new provider or context | Receive and execute rehydration payloads | Manage rehydration leases; configure auth level requirements | REQ-002, PRV-003 |
+| LCM-006 | Billing State Management | — | — | Configure billing state policies; integrate with Cost Analysis | LCM-001 |
+| LCM-007 | Resource Decommission | Decommission resources individually or as part of group decommission | Handle decommission payloads; release resources | Manage decommission workflows; coordinate dependency teardown | LCM-001 |
+
+---
+
+## 6. Drift Detection and Remediation
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| DRF-001 | Active Discovery | — | Expose discovery endpoint; respond to interrogation queries | Configure discovery schedules; manage Discovered Store | PRV-005 |
+| DRF-002 | Drift Comparison | Receive drift notifications for owned resources | — | Configure drift detection policies; manage comparison logic | DRF-001, PRV-005 |
+| DRF-003 | Drift Notification | Receive actionable drift alerts with field-level detail | — | Configure drift notification channels and escalation policies | DRF-002 |
+| DRF-004 | Drift Remediation | Approve or reject automatic drift remediation | Execute remediation payloads | Configure remediation policies (revert/update/alert/escalate) | DRF-002, LCM-002 |
+| DRF-005 | Unsanctioned Change Detection | Receive alerts on unauthorized resource modifications | Report all external state changes to DCM | Configure unsanctioned change policies | DRF-001 |
+
+---
+
+## 7. Policy Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| POL-001 | Policy Authoring | — | Contribute provider-specific policy rules | Author and manage policies via API or GitOps ingress | IAM-003 |
+| POL-002 | Policy Validation and Shadow Mode | View shadow evaluation results on own requests | — | Configure shadow mode; review shadow results | POL-001 |
+| POL-003 | Policy Activation and Review | — | — | Manage policy review periods; authorize policy activation | POL-001, POL-002 |
+| POL-004 | Policy Group Management | — | — | Compose Policy Groups; manage profile assignments | POL-003 |
+| POL-005 | Profile Management | — | — | Configure deployment profiles; manage compliance domain groups | POL-004 |
+| POL-006 | External Policy Evaluation | — | Register as external evaluation endpoint; implement BBQ-001–009 governance | Configure external evaluation trust levels; manage trust elevation | PRV-001, POL-001 |
+| POL-007 | Policy Override and Constraint Visibility | View constraint details for service catalog fields | Declare constraint schemas on Resource Type Specs | Configure constraint visibility levels per profile | CAT-002, POL-003 |
+| POL-008 | Constraint Type Registry | — | — | Register constraint types with OpenAPI v3 schemas; configure emittable_by/consumable_by; manage core and organization tiers | POL-001 |
+| POL-009 | Evaluation Context and Multi-Pass Convergence | — | — | Configure max evaluation passes; monitor convergence; manage escalation for unresolvable conflicts | POL-001, POL-008 |
+| POL-010 | Policy Templates | — | Contribute policy templates with parameterized Rego | Register templates (Gatekeeper ConstraintTemplate pattern); validate parameter schemas and constraint type references | POL-001, POL-008 |
+| POL-011 | DCM Constraint Types Library | — | — | Manage auto-generated Rego library (data.dcm.constraint_types); sync with Constraint Type Registry | POL-008, POL-010 |
+| POL-012 | Data-Driven Policy Matching | — | — | Configure match sources (request payload, operation context, evaluation context, entity metadata); validate match fields at activation | POL-001 |
+| POL-013 | Lifecycle-Scoped Policy Evaluation | — | — | Configure lifecycle_scope per policy (which operation types trigger it); enforce profile minimums (fsi/sovereign require sovereignty policies on all operations); configure changed_field_filter for update/scale operations | POL-001, POL-012 |
+| POL-014 | Override Policies | — | — | Author override policies targeting specific policies for defined scopes; enforce expiry and review dates; cannot target hard enforcement policies | POL-001, POL-003 |
+| POL-015 | Exception Grants | — | — | Create time-bounded, scope-limited waivers with compensating controls; track usage count; enforce dual-approval for hard policies; manage renewal limits | POL-001, AUD-008 |
+| POL-016 | Manual Override | Submit override request for blocked request with justification | — | Grant single-request overrides; enforce dual-approval for hard policies; manage override authority roles | POL-001, IAM-001 |
+| POL-017 | Dual-Approval Escalation | — | — | Configure role separation requirements; manage approval workflow for hard policy overrides; enforce fsi/sovereign dual-approval on all overrides | POL-001 |
+| POL-018 | Compensating Control Substitution | — | — | Define substitute control sets that satisfy policy intent through different mechanisms; require compliance officer validation | POL-001 |
+| POL-019 | Override Approval Flow | Submit override request for blocked request; view pending overrides awaiting approval | — | Configure override notification routing (internal, webhook); configure timeout per profile; configure escalation rules; manage override authority roles | POL-016, IAM-001 |
+| POL-020 | Override Notification Routing | — | — | Configure per-profile notification channels (LISTEN/NOTIFY, webhook to ServiceNow/Jira/Slack); configure routing by policy domain and enforcement level; configure escalation timeouts | POL-019 |
+| POL-021 | Policy Block Resolution | View blocking details, resolution guidance with compliant values; choose resolution action (modify, request override, cancel, escalate) | — | Configure block timeout per profile; configure resolution guidance generation; manage escalation routing | POL-001 |
+
+---
+
+## 8. Data Layer Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| LAY-001 | Core Layer Authoring | — | — | Author and manage Core and Organizational Layers in GitOps | IAM-003 |
+| LAY-002 | Service Layer Contribution | — | Contribute Service Layers for offered resource types | Manage layer compatibility declarations | PRV-001, LAY-001 |
+| LAY-003 | Layer Cache Management | — | — | Manage Layer Cache synchronization; handle cache invalidation | LAY-001, LAY-002 |
+| LAY-004 | Layer Exclusion | Declare layer exclusions on specific requests | — | Configure which layers may be excluded; manage non-excludable declarations | REQ-003 |
+| LAY-005 | Layer Versioning and Lifecycle | — | — | Manage layer versions; handle deprecation; enforce immutability | LAY-001 |
+
+---
+
+## 9. Information and Data Integration
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| INF-001 | Information Provider Registration | — | Register Information Provider; declare authority scope and schema | Configure Information Provider Registry; manage authority layers | IAM-001 |
+| INF-002 | Information Provider Push | — | Push field value updates to DCM; respond to conflict notifications | Configure ingestion pipeline; manage conflict resolution policies | INF-001 |
+| INF-003 | Information Provider Pull / Discovery | — | Expose data query endpoint for DCM pull operations | Configure pull schedules; manage cache TTLs | INF-001 |
+| INF-004 | Write-Back | — | Implement write-back endpoint to receive DCM-initiated updates | Configure write-back triggers via policy | INF-001, INF-002 |
+| INF-005 | Confidence Score Visibility | View confidence bands on entity field values; query confidence aggregation API | — | Configure confidence scoring formula; manage trust score thresholds | INF-001 |
+| INF-006 | Conflict Resolution Management | — | — | Review and resolve contested field values; manage conflict escalation | INF-002 |
+
+---
+
+## 10. Ingestion and Brownfield Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| ING-001 | Resource Discovery and Ingestion | — | Expose discovery endpoints for brownfield resources | Configure ingestion pipeline; manage __transitional__ Tenant | DRF-001 |
+| ING-002 | Ingested Entity Review | — | — | Review ingested entities; resolve conflicts; promote to active Tenants | ING-001 |
+| ING-003 | Bulk Promotion | — | — | Execute bulk entity promotions with preview and rollback | ING-002 |
+| ING-004 | Catalog Item Association | — | — | Associate ingested entities with Resource Type Specs; create catalog items | ING-002, CAT-001 |
+
+---
+
+## 11. Audit and Compliance
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| AUD-001 | Audit Trail Access | Query audit records for own resources | — | Configure Audit Store; manage retention policies | IAM-003 |
+| AUD-002 | Compliance Reporting | — | — | Generate compliance reports; manage report schedules | AUD-001 |
+| AUD-003 | Merkle Tree Verification | — | — | Run inclusion proofs, consistency proofs, request chain verification; manage integrity incidents | AUD-001 |
+| AUD-004 | Cross-DCM Audit Correlation | — | — | Correlate audit records across DCM instances via correlation_id; authorize cross-DCM pulls | AUD-001, DCM-001 |
+| AUD-005 | Audit Record Retention Management | — | — | Configure reference-based retention; manage post-lifecycle retention | AUD-001 |
+| AUD-006 | Audit Granularity Configuration | — | — | Configure granularity level per profile (stage, mutation, field); enforce minimum for fsi/sovereign | AUD-001 |
+| AUD-007 | Signed Tree Heads | — | — | Configure STH interval; manage audit signing keys; publish STH for external verification | AUD-001 |
+| AUD-008 | Payload Chain of Custody | View chain-of-custody proof for own requests | Verify dispatched payload matches DCM's signed output | Verify full pipeline integrity via request chain verification API | AUD-003 |
+| AUD-009 | Inter-Stage Verification | — | — | Configure verification mode per profile (synchronous, asynchronous, disabled) | AUD-001 |
+
+---
+
+## 12. Observability and Operations
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| OBS-001 | Operational Dashboard | View health and status of own resources | — | Configure and manage observability dashboard | — |
+| OBS-002 | Metrics and Telemetry Export | — | Expose resource-level metrics to DCM | Configure observability export; integrate enterprise observability platform | — |
+| OBS-003 | Curated Event Stream Subscription | Subscribe to observability event types via Message Bus | — | Configure event stream publication policies; manage subscriber roles | OBS-002 |
+| OBS-004 | Alert and Notification Management | Receive resource and policy alerts via declared channels | — | Configure alert routing; manage notification channels and escalation | OBS-001 |
+
+| OBS-006 | SLA/SLO Declaration | View SLO status for owned resources (`GET /resources/{uuid}/slo-status`) | Declare resource_type SLOs in Resource Type Specification; report realization timing via callbacks | Configure SLO targets per resource type; view aggregate SLO performance report (`GET /admin/slo/report`) | RLM-001, LCM-001 |
+| OBS-007 | SLO Breach Detection and Notification | Receive `slo.breach_approaching` and `slo.breach_detected` events | — | Configure SLO breach routing and escalation; review aggregate breach reports | OBS-006, EVT-001 || OBS-005 | Cost Analysis and Attribution | View cost estimates and actuals for owned resources | Provide cost metadata; report utilization | Configure Cost Analysis component; manage cost attribution policies | PRV-006 |
+
+---
+
+## 13. Storage and State Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| STO-001 | Data Store Management | — | — | Configure PostgreSQL data domains (intent, requested, realized, discovered); manage schema, RLS, and tenant isolation | — |
+| STO-002 | Realized State Management | — | — | Configure realized_entities table; manage version retention and is_current flag | PRV-005 |
+| STO-003 | Discovered State Management | — | — | Configure discovered_records table; manage retention policies per profile | DRF-001 |
+| STO-004 | Search and Query | Use entity and catalog search | — | Configure materialized views and indexes; manage cache refresh | STO-001 |
+| STO-005 | Backup and Recovery | — | — | Configure PostgreSQL backup (PITR); test recovery; verify audit hash chain integrity | STO-001 |
+| STO-006 | Provenance Model Configuration | — | — | Select and configure provenance model (full_inline / deduplicated / tiered); manage tier transitions | STO-001 |
+| STO-007 | Sovereignty Partitioning | — | Declare sovereignty constraints at registration | Configure separate PostgreSQL instances per sovereignty zone; manage cross-zone prohibition | STO-001, GOV-001 |
+| STO-008 | Tenant-Scoped Storage Isolation | Data is isolated by tenant via RLS | — | Configure RLS policies per table; enforce STI-001 through STI-004 | STO-001, IAM-001 |
+| STO-009 | Tenant-Scoped Encryption (fsi/sovereign) | — | — | Configure per-tenant AES-256-GCM encryption via secrets management; manage key rotation | STO-008, CPX-001 |
+| STO-010 | Internal Secrets Management | — | — | Configure envelope encryption (KEK source: env var, K8s secret, or HSM); manage secrets table; optional Vault external backend | STO-001 |
+| STO-011 | Pipeline Event Routing | — | — | Configure LISTEN/NOTIFY for pipeline events; optional Kafka for high-throughput; manage consumption tracking | STO-001 |
+| STO-012 | Internal Authentication | — | — | Configure local actor accounts (argon2id hashes, DCM-issued JWT); optional OIDC/SAML external auth_provider | STO-001, IAM-001 |
+
+---
+
+## 14. DCM Federation and Multi-Instance
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| FED-001 | DCM Provider Registration | Submit requests that are routed to peer DCMs | Register as DCM Provider in peer instances | Configure DCM Provider registrations; manage federation trust | PRV-001, IAM-001 |
+| FED-002 | Federation Routing | Requests automatically routed to appropriate Regional/Sovereign DCM | Respond to reserve queries from Hub DCM | Configure federation placement policies; manage sovereignty pre-filters | FED-001, REQ-005 |
+| FED-003 | Federation Trust Management | — | — | Manage mTLS certificates; monitor federation trust scores; handle cert rotation | FED-001 |
+| FED-004 | Cross-DCM Drift Detection | Receive drift alerts for federated resources | Publish Discovered State events to federation Message Bus | Configure federated drift detection; manage alert-and-hold policies | FED-001, DRF-002 |
+| FED-005 | DCM Export and Import | — | — | Export and import DCM state packages; verify import trust scores | STO-001, STO-002 |
+
+---
+
+## 15. Platform Governance and Administration
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| GOV-001 | Tenant Management | — | — | Create, configure, and decommission Tenants; manage compliance overlays | IAM-007 |
+| GOV-002 | Group Management | — | — | Create and manage DCM Groups; configure sovereignty rules; manage time-bounded memberships | IAM-003 |
+| GOV-003 | Registry Management | — | Register and maintain Resource Type Specifications in organization registry | Manage registry sync; configure registry policies; manage Tier 3 types | PRV-001 |
+| GOV-004 | Resource Type Lifecycle | — | Manage deprecation notices; declare successor types; maintain migration guidance | Enforce deprecation timelines; manage sunset periods | GOV-003 |
+| GOV-005 | Platform Configuration Management | — | — | Manage platform-wide layers; configure profiles; manage deployment manifest | LAY-001, POL-005 |
+| GOV-006 | Bootstrap and Self-Hosting | — | — | Manage DCM self-deployment; verify bootstrap manifest; handle repave scenarios | STO-001 |
+
+| GOV-008 | Tenant Onboarding Workflow | Trigger onboarding completion: receive `tenant.onboarding_complete` when first entity OPERATIONAL | — | Execute full provisioning sequence: tenant entity, default groups, quota, admin actor, GitOps namespace, audit stream; dispatch `tenant.created` and member invitation events | IAM-001, STO-008 || GOV-007 | Sovereign Deployment Management | — | — | Manage air-gapped DCM instances; configure signed bundle import; manage offline registry | FED-001, STO-001 |
+
+---
+
+## 16. Accreditation Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| ACC-001 | Accreditation Submission | — | Submit accreditation records (BAA, ISO 27001, FedRAMP, etc.) referencing external certificate evidence | Register accrediting bodies; configure minimum accreditation types per profile | PRV-001 |
+| ACC-002 | Accreditation Review and Approval | — | Receive approval/rejection notification | Review submitted accreditations; verify certificate references; approve or reject via Admin API | ACC-001 |
+| ACC-003 | Accreditation Lifecycle Monitoring | Receive notification when a provider's accreditation is nearing expiry or revoked | Renew accreditations before expiry; submit renewal documentation | Monitor expiry timelines; fire P90D renewal warnings; handle accreditation gaps | ACC-001 |
+| ACC-004 | Accreditation Gap Response | Receive notification when a provider enters accreditation gap affecting owned resources | — | Configure Recovery Policy for accreditation gap events; manage affected entity remediation | ACC-003, POL-005 |
+| ACC-005 | Data Classification Enforcement | Receive enforcement feedback when request payload contains data the selected provider cannot handle | Declare max_data_classification_accepted in capability registration | Configure classification immutability rules; manage phi/sovereign classification locks | ACC-001, PRV-001 |
+| ACC-006 | DCM Deployment Accreditation | — | — | Register DCM deployment-level accreditations; expose to federation peers for trust verification | PRV-001, FED-001 |
+
+---
+
+## 17. Zero Trust and Security Posture
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| ZTS-001 | Mutual TLS Enforcement | All interactions authenticated via mTLS at the client side | Present valid mTLS certificate on every interaction; rotate certificates on declared schedule | Configure trust anchors; manage CA chain; enforce mTLS at all interaction boundaries | IAM-001 |
+| ZTS-002 | Scoped Interaction Credentials | Receive scoped short-lived credentials for authorized operations | Validate credential scope before executing operations; reject out-of-scope credentials | Configure credential lifetime per profile; manage credential issuance via secrets management | IAM-001, PRV-001 |
+| ZTS-003 | Certificate Rotation Management | — | Implement certificate rotation before expiry; use transition window to avoid downtime | Monitor certificate expiry; fire P14D rotation warnings; manage P7D transition window | ZTS-001 |
+| ZTS-004 | Zero Trust Posture Configuration | — | — | Configure zero_trust_posture per profile (none/boundary/full/hardware_attested); manage posture overrides | POL-005 |
+| ZTS-005 | Hardware Attestation (Sovereign Profile) | — | Present hardware-attested identity (TPM/HSM) for sovereign profile interactions | Configure hardware attestation requirements; manage HSM integration; enforce for sovereign profile | ZTS-001, ZTS-002 |
+
+| ZTS-007 | Provider OpenAPI Spec Signing (SEC-001) | — | Sign OpenAPI spec with mTLS private key at registration; rejected at GATE-SP-01 if unsigned | Verify signature during registration approval pipeline | PRV-001, ZTS-001 |
+| ZTS-008 | GitOps Secrets Scanning (SEC-002) | Commits with detected secrets rejected with `SECRETS_DETECTED` audit record | Ensure service layer SCM does not contain plaintext secrets | Configure scanning ruleset; review and remediate detected secrets | GOV-001, AUD-001 |
+| ZTS-009 | Software Bill of Materials (SBOM) Declaration (SEC-003) | — | Declare SBOM reference at registration (mandatory for fsi/sovereign) | Enforce SBOM requirement during registration approval for fsi/sovereign profiles | PRV-001, ACR-001 || ZTS-006 | Five-Check Boundary Enforcement | — | Pass all five boundary checks on every interaction: identity → authorization → accreditation → matrix → sovereignty | Monitor boundary check audit records; respond to INTERACTION_DENIED events | ZTS-001, ACC-001, GMX-001 |
+
+---
+
+## 18. Unified Governance Matrix
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| GMX-001 | Governance Matrix Rule Authoring | — | — | Author governance matrix rules in GitOps; declare match conditions across four axes (subject/data/target/context); declare field permissions | POL-001 |
+| GMX-002 | Boundary Enforcement Evaluation | Receive DENY response with governing rule_uuid and human-readable reason when a request crosses a prohibited boundary | Receive field-stripped or redacted payloads when STRIP_FIELD/REDACT decisions apply | Monitor GMX evaluation audit records; respond to DENY events | GMX-001, ZTS-006 |
+| GMX-003 | Field-Level Data Control | Receive request feedback when specific payload fields are stripped or redacted by active matrix rules | Receive filtered payloads; handle missing optional fields gracefully | Configure allowlist/blocklist field permissions per rule; manage STRIP_FIELD vs REDACT vs DENY_REQUEST escalation | GMX-001 |
+| GMX-004 | Sovereignty Zone Management | — | Declare operating sovereignty zones in provider registration | Register sovereignty zones; declare jurisdictions, regulatory frameworks, inter-zone agreements | PRV-001, GMX-001 |
+| GMX-005 | Compliance Domain Matrix Activation | — | — | Activate compliance domain matrix rules (HIPAA, GDPR, etc.) by enabling compliance domain in profile; rules apply automatically | POL-005, GMX-001 |
+| GMX-006 | Tenant and Resource-Type Matrix Overrides | — | — | Declare Tenant-level and resource-type-level matrix rules that tighten (never relax) platform defaults | GMX-001, GOV-001 |
+| GMX-007 | Matrix Rule Lifecycle Management | — | — | Manage governance matrix rule lifecycle (developing → proposed → active); use shadow mode for safe validation before activation | GMX-001, POL-002 |
+
+---
+
+## 19. Drift Reconciliation
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| DRC-001 | Drift Record Production | Receive drift records with field-level detail: realized value, discovered value, field criticality, severity, unsanctioned flag | — | Configure Drift Reconciliation Component; manage comparison algorithm and severity thresholds | DRF-001, PRV-005 |
+| DRC-002 | Unsanctioned Change Classification | Receive elevated-severity alert when change has no corresponding Requested State record | — | Configure unsanctioned change detection; manage severity escalation rules | DRC-001 |
+| DRC-003 | Drift Severity Classification | Receive severity-classified drift records (minor/significant/critical) based on field criticality × change magnitude | — | Declare field criticality in Resource Type Specifications; configure magnitude thresholds per profile | DRC-001, GOV-003 |
+| DRC-004 | Drift Resolution Tracking | View drift record status (open/acknowledged/resolved/escalated); receive resolved notification when next discovery confirms clean state | — | Monitor drift resolution rates; configure escalation policies for aged-open drift records | DRC-001, DRF-004 |
+| DRC-005 | Governance Matrix Drift Integration | — | — | Configure governance matrix check in drift comparison pipeline: expected provider changes are not flagged as drift | DRC-001, GMX-001 |
+
+
+## 20. Federated Contribution Model
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| FCM-001 | Consumer Policy Authoring | Author and submit tenant-domain policies (GateKeeper, Transformation, Recovery, Lifecycle, Orchestration Flow, Governance Matrix rules) via API or Flow GUI; receive PR URL and shadow mode results | — | Configure consumer policy authoring permissions (policy_author role); manage review requirements per profile | POL-001, IAM-003, IAM-007 |
+| FCM-002 | Provider Resource Type Publication | — | Publish Resource Type Specifications and Catalog Items for offered resource types via provider contribution API; receive registry PR for platform admin review | Manage provider contribution registry; configure review requirements for provider specs; manage Organization-tier registry | PRV-001, GOV-003 |
+| FCM-003 | Provider Service Layer Contribution | — | Contribute Service Layers for offered resource types; layers applied during request assembly for all consumers requesting that resource type | Review and activate provider-contributed layers; manage layer compatibility | PRV-001, LAY-002 |
+| FCM-004 | Consumer Resource Group and Definition Contribution | Author and manage resource groups, notification subscriptions, webhook registrations, and cross-tenant authorization records within own Tenant | — | Configure contribution permissions per role; manage Tenant-scoped artifact lifecycle | IAM-007, GOV-002 |
+| FCM-005 | Federation Contribution (Peer DCM) | — | Peer DCM contributes registry entries, policy templates, and service layers via federation channels | Manage federation contribution trust posture (verified/vouched/provisional); configure review requirements per trust posture; manage cross-DCM artifact lifecycle | FED-001, GOV-003, POL-003 |
+| FCM-006 | Contribution Review and Lifecycle | View contribution status (proposed, pending_review, active, withdrawn); withdraw a pending contribution; receive notification when contribution is approved or rejected | Receive notification when provider contributions are reviewed | Review and approve/reject contributions via Admin API; manage shadow review periods; assign new owners to orphaned artifacts | POL-002, POL-003 |
+| FCM-007 | Contributor Scope Enforcement | Receive clear DENY response when attempting to contribute outside permitted domain scope | Receive DENY when contributing specs for resource types not offered | Monitor Governance Matrix enforcement at contribution time; configure scope violation audit and notification | GMX-001, GMX-002 |
+
+---
+
+
+## 21. Scoring Model
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| SMX-001 | Operational GateKeeper Scoring | Receive risk score and score_drivers with request acknowledgment; understand why score is at its level | Declare `enforcement_class: operational` and `scoring_weight` on contributed GateKeeper policies | Configure operational GateKeeper policies with appropriate weights; manage per-policy enforcement class | POL-001, REQ-004 |
+| SMX-002 | Advisory Validation and Completeness Score | Receive advisory_warnings list with request acknowledgment; understand what optional improvements exist | Declare `output_class: advisory` on advisory Validation policies | Configure advisory Validation policies; manage completeness score thresholds | POL-001, REQ-004 |
+| SMX-003 | Actor Risk History Tracking | View own risk history score and contributing events via Consumer API | — | Monitor actor risk history; reset scores for trusted automation accounts; configure decay parameters | AUD-001, IAM-001 |
+| SMX-004 | Quota Pressure Scoring | Receive quota_pressure as a score driver when approaching Tenant quota limits | — | Configure per-resource-type quota limits; manage free_threshold parameter | IAM-007, REQ-004 |
+| SMX-005 | Provider Accreditation Richness Scoring | — | Benefit from lower risk contribution by maintaining rich accreditation portfolio | Configure accreditation richness weights; manage portfolio scoring | ACC-001, PRV-001 |
+| SMX-006 | Profile Scoring Threshold Management | — | — | Configure approval routing thresholds per profile (auto/reviewed/verified/authorized + custom tiers via named-tier list); manage signal weights; enforce SMX-008 (max auto_approve_below: 50) | POL-005, REQ-004 |
+| SMX-007 | Policy Enforcement Class Override | — | Contribute policies with declared enforcement_class; receive notification when profile overrides enforcement class | Declare per-profile enforcement class overrides; manage regulatory_mandate flag to protect compliance-class policies from demotion | POL-004, POL-005 |
+| SMX-008 | Score Audit Trail | Query risk score and routing decision for own requests; view score_drivers and advisory_warnings | — | Query full Score Record detail including signal breakdown and actor risk history; manage score audit retention | AUD-001, REQ-004 |
+| SMX-009 | Scoring Weight Range Enforcement | — | Declare operational GateKeeper scoring_weight between 1 and 100 | Enforce weight range at policy activation; reject out-of-range weights | SMX-001 |
+| SMX-010 | Score Breakdown Audit Inclusion | View score breakdown in request audit record | — | Configure score breakdown storage in Audit Store for all scored requests | SMX-001, AUD-001 |
+
+---
+
+
+## 22. Meta Provider Composability
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| MPX-001 | Compound Service Request | Request a compound service as a single catalog item; receive composite entity UUID; track compound execution status via standard request status endpoint | Register as Meta Provider with constituent specification; implement compound dispatch endpoint | Configure Meta Provider registration; manage composite service catalog items | CAT-001, REQ-007, PRV-001 |
+| MPX-002 | Dependency-Ordered Constituent Execution | — | Execute constituents in declared dependency order; manage parallel rounds; respect depends_on declarations | Configure composition model; monitor execution round progress via status events | MPX-001, PRV-003 |
+| MPX-003 | Partial Delivery and DEGRADED State | Receive DEGRADED composite entity when partial delivery is accepted; choose to accept or reject degraded state | Declare partial_delivery_supported and required_for_delivery per constituent; return DEGRADED compound payload | Configure accept_degraded_delivery per profile; manage degraded notification urgency | MPX-001, PRV-005 |
+| MPX-004 | Compound Compensation | Receive notification and recovery decision when compound service fails; approve or reject compensation | Implement compensation in dependency-reverse order; guarantee idempotent decommission calls | Configure compensation timeout; manage PARTIALLY_COMPENSATED orphan detection | MPX-001, LCM-007, DRC-001 |
+| MPX-005 | Transparent Constituent Visibility | Query and manage DCM-visible constituent entities independently (when transparency mode); receive constituent-level drift alerts | Declare composition_visibility mode; register transparent constituents with deterministic UUIDs | Configure visibility mode per compound resource type; manage constituent entity lifecycle policies | MPX-001, DRF-001 |
+| MPX-006 | Compound Execution Status Tracking | Monitor compound execution round progress via request status; see component-level status during long-running compositions | Send intermediate status events to DCM during execution; declare status_reporting.interval | Monitor compound execution health; configure execution timeout alerts | MPX-001, REQ-008 |
+| MPX-007 | Nested Meta Provider Composition | Request high-order compound services composed of other compound services (max depth 3) | Implement as a Meta Provider that calls other Meta Providers as constituents; declare max_nesting_depth | Configure nesting depth limits; manage nested compensation chains | MPX-001, PRV-009 |
+| MPX-008 | Meta Provider Nesting Depth Enforcement | — | Declare nesting depth in Meta Provider registration | Enforce maximum nesting depth of 3 at placement time; reject deeper compositions | MPX-001 |
+
+---
+
+
+## 23. Credential Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| CPX-001 | Resource Credential Issuance | Receive credential metadata and retrieval URL with realized resource; retrieve credential value via authenticated endpoint | Declare credential requirements in Resource Type Spec; receive credential issuance confirmation | Register service_provider with Credential.* resource types; configure credential types and lifetimes per resource type; manage issuance policies | PRV-005, ZTS-002 |
+| CPX-002 | DCM Interaction Credential Issuance | — | Validate scoped interaction credential on every DCM dispatch; reject interactions without valid scoped credential (CPX-002) | Configure interaction credential lifetime per profile; manage secrets table for DCM-internal credentials | ZTS-002, PRV-001 |
+| CPX-003 | Credential Rotation | Receive rotation notification before old credential expires; retrieve new credential during transition window | Implement rotate endpoint; honor transition window; notify DCM when rotation is complete | Configure rotation schedules and transition windows per credential type; manage pre-expiry rotation warnings | CPX-001, IAM-001 |
+| CPX-004 | Emergency Rotation and Security Event Response | Receive immediate notification on emergency rotation; retrieve new credential via fastest channel | Implement immediate revocation with no transition window on security_event trigger | Configure security event triggers; manage emergency rotation audit trail; notify platform admin | CPX-003, OBS-004 |
+| CPX-005 | Credential Revocation | Receive revocation notification when credentials are revoked (actor deprovisioned, entity decommissioned); confirm transition to new credential | Implement revoke endpoint with declared SLA; invalidate value immediately on emergency revocation | Manage Credential Revocation Registry; configure revocation cache TTL per profile (PT1M standard, PT30S fsi/sovereign); enforce CPX-007 (decommission blocks on credential revocation) | CPX-001, LCM-007 |
+| CPX-006 | Revocation Propagation | — | Refresh revocation cache within profile-governed TTL; validate credential UUID against cache at use time (not only at receipt) | Configure revocation cache TTL; monitor revocation propagation latency; alert on SLA violations | CPX-005, IAM-001 |
+| CPX-007 | Audit Trail for Credential Lifecycle | View own credential record history (issue, rotate, revoke events); every value retrieval audited with retrieval_uuid | — | Query full credential audit trail including retrieval count; manage credential audit retention | CPX-001, AUD-001 |
+| CPX-008 | IP-Bound Credentials for fsi/sovereign | — | — | Enforce IP binding (bound_to_ip) on all credentials issued for fsi and sovereign profiles | CPX-001, ZTS-002 |
+| CPX-009 | Algorithm and Key Usage Declaration | — | Declare algorithm and key_usage on credential records at issuance | Enforce declaration at issuance; reject credentials without declared algorithm | CPX-001 |
+| CPX-010 | Idle Credential Detection | Receive notification when credential reaches idle threshold | — | Configure idle_detection_threshold per profile; enforce alert-only action | CPX-001 |
+| CPX-011 | Compliance Domain Additive Credential Requirements | — | — | Enforce additive credential requirements when compliance domains are active on a profile | CPX-001, POL-005 |
+| CPX-012 | Credential Value Store Isolation (All Profiles) | — | — | Enforce credential values never stored in DCM stores in ALL profiles including minimal | CPX-001 |
+
+---
+
+
+## 24. Authority Tier Model
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| ATM-001 | Authority Tier Registry | Reference approval decisions by tier name; tier weight resolved dynamically from ordered list | Implement approval workflows that reference tier names (not hardcoded weights) | Manage the ordered authority tier list; control tier positions and gravity values | POL-005 |
+| ATM-002 | Custom Tier Definition | — | — | Contribute custom tiers between existing tiers; declare decision_gravity and dcm_gate semantics; requires verified-tier approval | ATM-001, FCM-001 |
+| ATM-003 | Dynamic Threshold Configuration | View which tier an action routes to | — | Configure profile approval_routing as named-tier threshold list; adjust score ranges when new tiers inserted | ATM-001, SMX-001 |
+| ATM-004 | Tier Registry Change Impact Detection | Receive notification when tier changes affect owned resources or pending approvals | — | Propose tier registry changes; receive tier impact diff report; review SECURITY_DEGRADATION and BROKEN_REFERENCE items; accept degradations via Admin API | ATM-001, AUD-001 |
+| ATM-005 | Degradation Review Gate | — | — | Review and explicitly accept each SECURITY_DEGRADATION item before a registry change activates; provide compensating control rationale; must hold verified or authorized tier reviewer role | ATM-004, IAM-001 |
+| ATM-006 | Profile Gap Detection | — | — | Receive PROFILE_GAP warnings when tier registry changes leave profile threshold lists incomplete; update threshold lists or acknowledge gap within approval window | ATM-003, ATM-004 |
+| ATM-007 | Tier Registry Audit Trail | Query historical tier registry versions and impact reports | — | Access full audit trail of all tier registry changes: proposal, impact assessment, degradation acceptances, activation | ATM-004, AUD-001 |
+| ATM-008 | Approval Record Tier Weight Snapshot | — | — | Store tier name and resolved weight at approval record creation; historical records retain weight for audit comparison across regime changes | ATM-001 |
+| ATM-009 | Tier Registry Degradation Gate | Receive notification when tier change produces degradation affecting owned resources | — | Block tier registry activation on SECURITY_DEGRADATION items; require verified-tier acceptance per item | ATM-001 |
+| ATM-010 | Broken Reference Gate | — | — | Block tier registry activation when BROKEN_REFERENCE items exist (removed tier still referenced in active config) | ATM-001 |
+| ATM-011 | Tier Change Impact Report | — | — | Generate and store tier impact report in Audit Store at proposal and activation time | ATM-001, AUD-001 |
+| ATM-012 | Profile Gap Warning on Tier Insertion | — | — | Detect PROFILE_GAP when new tier inserted but profile threshold list not updated; emit non-blocking warning | ATM-001, POL-005 |
+
+---
+
+
+## 25. Event Catalog
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| EVT-001 | Event Subscription | Subscribe to DCM events via notification service_provider or Message Bus; filter by event type, entity type, urgency; idempotency via event_uuid | Publish standard events when provider actions occur; use reverse-DNS prefix for non-standard events | Configure notification service_provider channels and audience routing | IAM-001, OBS-001 |
+| EVT-002 | Request Pipeline Events | Receive real-time status of own requests (submitted → intent_captured → policies_evaluated → requires_approval → approved → dispatched → realized/failed) | — | Configure request event delivery per profile; manage urgency routing | REQ-001 |
+| EVT-003 | Entity Lifecycle Events | Receive entity lifecycle events (realized, state_changed, ttl_warning, decommissioning, etc.) for owned entities and entities with stakes | — | Configure entity event delivery; manage stakeholder audience routing | LCM-001 |
+| EVT-004 | Security and Critical Events | Receive critical security events (audit chain alerts, sovereignty violations, unsanctioned provider writes) regardless of subscription preferences | — | Configure non-suppressable event delivery; manage security team routing | AUD-001, ZTS-001 |
+| EVT-005 | Approval Pipeline Events | Receive approval events (requires_approval, decision_recorded, quorum_reached, window_expiring, expired) for own requests and approvals | — | Configure reviewer notification routing; manage approval window alerts | ATM-001, IAM-001 |
+| EVT-006 | Provider and Infrastructure Events | — | Publish provider health events (registered, healthy, unhealthy, degraded); publish provider_update events on entity changes | Monitor provider health events; configure provider degradation alerts | PRV-001 |
+| EVT-007 | Tier Registry and Governance Events | — | — | Receive tier_registry events (proposed, impact_assessed, degradation_detected, activated); configure governance event routing | ATM-004 |
+
+---
+
+
+## 26. API Versioning
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| VER-001 | Version Discovery | Discover available API versions and their status via `GET /.well-known/dcm-api-versions`; learn current, supported, and deprecated versions; get changelog and migration guide URLs | Declare supported OIS version in capability registration | Monitor version adoption; manage sunset schedules | — |
+| VER-002 | Breaking Change Governance | Receive at least the profile-governed deprecation notice period before a breaking change takes effect; continue using deprecated versions until sunset date | Receive OIS version deprecation notice; migrate to new OIS version before sunset | Declare new major versions; configure deprecation timeline per profile; ensure VER-002 (breaking change definition) is applied | VER-001 |
+| VER-003 | Deprecation Headers | Receive `Deprecation`, `Sunset`, and `Link` headers on all responses from deprecated API versions (RFC 8594/RFC 9745); use these to drive migration priority | — | Configure header injection for deprecated versions; ensure headers are accurate | VER-002 |
+| VER-004 | Migration Guide | Access machine-readable migration guide at `GET /api/v{N}/migration-guide`; understand all breaking changes from previous version with migration instructions | Access OIS migration guide at `GET /provider/api/v{N}/migration-guide` | Maintain migration guides for all new major versions (required by VER-008) | VER-002 |
+| VER-005 | Preview Endpoints | Access preview endpoints at `/api/v{N}/preview/`; understand stability commitment is none; provide feedback before graduation | — | Mark endpoints as preview; graduate to stable in new major version | VER-001 |
+| VER-006 | Latest Alias Production Warning | — | — | Support `latest` version alias; emit response header discouraging production use | VER-001 |
+| VER-007 | Preview Endpoint Instability Declaration | — | — | Mark preview endpoints explicitly; may change or be removed without major version bump | VER-001 |
+| VER-009 | Provider Dispatch Compatibility | — | Maintain backward compatibility with DCM dispatch payloads from supported prior versions | Manage provider dispatch versioning; maintain supported version matrix | VER-001 |
+
+---
+
+
+## 27. Session Revocation
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| SES-001 | Session Lifecycle Management | View own active sessions; logout single session (DELETE /api/v1/auth/session); logout all sessions; revoke specific session by UUID | — | Force revoke sessions for any actor; view session store health | IAM-001, AUTH-001 |
+| SES-002 | Actor Deprovisioning Session Revocation | — | — | Parallel session + credential revocation on actor deprovisioning; deprovisioning not acknowledged until both complete (AUTH-016) | SES-001, CPX-005 |
+| SES-003 | Emergency Session Revocation | Receive critical notification on security-event session revocation | — | Trigger emergency revocation (security_event); revocation propagates within profile SLA (PT5S sovereign to PT30S standard) | SES-001, EVT-001 |
+| SES-004 | Token Introspection | — | Call POST /api/v1/auth/introspect to validate bearer tokens without maintaining own revocation cache | Configure introspection endpoint access; manage introspection scope grants | SES-001, IAM-001 |
+| SES-005 | Concurrent Session Enforcement | Oldest session auto-revoked when new session exceeds concurrent limit; receive notification via notification service_provider | — | Configure concurrent_sessions limit per profile; monitor session counts | SES-001 |
+| AUTH-017 | Session Revocation Propagation SLA | — | — | Propagate revocation to Session Revocation Registry within profile SLA (PT5S sovereign → PT30S standard) | SES-001 |
+| AUTH-018 | Per-Request Revocation Registry Check | — | Check Session Revocation Registry on each request bearing a bearer token | Configure revocation registry query path; manage registry availability | SES-001 |
+| AUTH-019 | Emergency Revocation No-Grace Period | Receive critical notification on emergency revocation | — | Emergency session revocation fires immediately with no grace period | SES-003 |
+| AUTH-020 | Introspection Endpoint Authentication | — | Authenticate introspection calls using provider interaction credential | Configure introspection scope grants; manage endpoint access | SES-004 |
+| AUTH-021 | Oldest Session Revocation on Limit | Receive notification when oldest session is auto-revoked | — | Configure concurrent_sessions limit; enforce oldest-first revocation order | SES-005 |
+| AUTH-022 | Refresh Token Invalidation on Session Revoke | — | — | Invalidate refresh token when parent session is revoked; return REVOKED_SESSION error on use | SES-001 |
+
+---
+
+## 28. Internal Component Authentication
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| ICOM-001 | Component Identity and mTLS | — | — | Manage Internal CA; issue and rotate component certificates; all inter-component calls use mTLS (ICOM-001) | ZTS-001, CPX-001 |
+| ICOM-002 | Component Bootstrap | — | — | Generate one-time bootstrap tokens (PT1H max lifetime); components acquire first certificate via bootstrap token; token invalidated after single use (ICOM-007) | ICOM-001 |
+| ICOM-003 | Internal Call Authorization | — | — | Declare allowed_sources per internal endpoint; declare allowed_targets per component; unauthorized source calls rejected with ICOM_UNAUTHORIZED_SOURCE audit record (urgency: high) | ICOM-001, AUD-001 |
+| ICOM-004 | Internal Interaction Credentials | — | — | Every internal call presents a scoped ZTS-002 interaction credential in addition to mTLS; credential scoped to specific operation and target component | ICOM-001, CPX-002, ZTS-002 |
+| ICOM-005 | Component Certificate Revocation | — | — | Compromised component certificates added to Internal CA CRL immediately; CRL cache refresh within profile SLA (PT15S sovereign to PT1M standard); ICOM_CERT_COMPROMISED audit record (urgency: critical) | ICOM-001, AUD-001 |
+| ICOM-006 | Component Certificate Maximum Validity | — | — | Issue internal component certificates with maximum validity P90D; enforce expiry and rotation | ICOM-001 |
+| ICOM-008 | Compromised Certificate Immediate CRL | — | — | Add compromised internal component certificates to Internal CA CRL immediately; propagate within PT60S | ICOM-001 |
+| ICOM-009 | Trust Anchor Registration | — | — | Register root or intermediate CA as trust anchor for internal mTLS; reject certificates not chaining to registered trust anchor | ICOM-001 |
+
+---
+
+
+## 29. Scheduled and Deferred Requests
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| SCH-001 | Request Scheduling | Submit requests with schedule.dispatch: at/window/recurring; SCHEDULED requests visible in GET /api/v1/requests; cancellable before dispatch; receive request.scheduled event | — | Manage Maintenance Windows; configure Request Scheduler; monitor scheduled queue depth | REQ-001 |
+| SCH-002 | Maintenance Windows | Reference maintenance windows in scheduled requests; view available windows at GET /api/v1/maintenance-windows | — | Create/manage/suspend maintenance windows; approve window schedules; configure platform-wide windows | SCH-001, GOV-001 |
+| SCH-003 | Dual Policy Evaluation | — | — | Understand that scheduled requests run GateKeeper at declaration AND at dispatch; dispatch-time failure → FAILED with schedule_policy_rejection (SCH-003) | SCH-001, POL-001 |
+| SCH-004 | Deadline Enforcement | Set not_after on scheduled requests; receive request.failed(schedule_deadline_missed) if deadline passes without dispatch | — | Monitor deadline miss rates; configure alerting on deadline misses | SCH-001, EVT-001 |
+| SCH-005 | Not-After Expiry Failure | Receive FAILED status when scheduled request expires before dispatch | — | Configure not_after enforcement; manage SCHEDULE_EXPIRED recovery policy | SCH-001 |
+| SCH-006 | Maintenance Window Platform Authorization | — | — | Require platform_admin or tenant_admin authority to create/modify Maintenance Windows | SCH-004 |
+
+---
+
+## 30. Request Dependency Graph
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| RDG-001 | Dependency Group Submission | Submit POST /api/v1/request-groups with requests and depends_on declarations; local refs within submission; receive group_uuid and per-request entity_uuids | — | Monitor group queue depth; configure max group size | REQ-001 |
+| RDG-002 | Field Injection | Declare inject_fields to pass realized output fields (e.g. IP address) from dependency into dependent request fields automatically at dispatch time | — | — | RDG-001, REQ-001 |
+| RDG-003 | PENDING_DEPENDENCY Status | Track dependent requests in PENDING_DEPENDENCY status; cancel pending requests individually or cancel whole group; receive request.pending_dependency and request.dependency_met events | — | Monitor PENDING_DEPENDENCY queue depth; detect stalled groups | RDG-001, EVT-001 |
+| RDG-004 | Group Failure Handling | Configure on_failure: cancel_remaining or continue; group-level timeout; group status via GET /api/v1/request-groups/{uuid} | — | Monitor group failure rates | RDG-001 |
+| RDG-005 | Group-Level Timeout Enforcement | Receive TIMEOUT failure when group-level timeout elapses | — | Configure group_timeout independent of individual request timeouts | RDG-001 |
+| RDG-006 | Single Group Membership Enforcement | — | — | Reject attempts to add a request to more than one dependency group | RDG-001 |
+
+---
+
+## 31. DCM Self-Health
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| HLT-001 | Liveness Probe | — | — | GET /livez: fast liveness check (PT5S max, no external calls); Kubernetes restarts pod on failure; unauthenticated | — |
+| HLT-002 | Readiness Probe | — | — | GET /readyz: checks Session Store, Audit Store, Policy Engine, Message Bus, Auth Provider connectivity; Kubernetes removes from LB on failure; startup sequence observable via readyz | — |
+| HLT-003 | Component Health Detail | — | — | GET /api/v1/admin/health: per-component status (pass/warn/fail), metrics, queue depths, provider/auth summary; admin auth required | IAM-001 |
+| HLT-004 | Prometheus Metrics | — | — | GET /metrics: Prometheus scrape endpoint; request pipeline, policy, session, drift, provider, internal CA metrics | OBS-001 |
+| HLT-005 | Prometheus Metrics Endpoint | — | — | Expose Prometheus-compatible metrics at GET /metrics including request throughput, store latency, policy eval time, provider health counters | HLT-001 |
+| HLT-006 | Startup Readiness via /readyz | — | — | Report PASSING on /readyz only after all required stores available and bootstrap complete | HLT-001 |
+
+---
+
+
+## 32. Operational Reference
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| OPS-001 | Data Store Partitioning | — | — | Declare partitioning strategy in deployment manifest; execute tenant-shard, per-tenant, or time-based archiving migration; configure shard routing and mirror lag monitoring | STO-001 |
+| OPS-002 | Store Migration | — | — | Execute dual-write migration between store implementations; maintain audit chain continuity across cutover; enforce profile-governed burn-in before source decommission | STO-001, AUD-001 |
+| OPS-003 | Disaster Recovery | — | — | Execute scenario-specific recovery procedures (component/store/full-CP/repave); meet profile-governed RTOs (PT1M–PT15M component, PT5M–PT2H store, PT5M–PT30M full-CP); complete post-recovery validation checklist | HLT-001, AUD-001 |
+| OPS-004 | Backup Management | — | — | Configure PostgreSQL backup schedules (PITR for all data domains); enforce P365D minimum audit retention | STO-001 |
+
+---
+
+
+## 33. Web Interfaces
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| GUI-001 | Consumer Portal — Catalog, Requests, Resources | Browse catalog; live cost estimate; submit requests with scheduling and dependency groups; live SSE status with constituent tracking; cross-resource drift report; consumer-scoped audit trail with correlation ID trace | — | — | CAT-001, REQ-001, SCH-001, RDG-001, EVT-002 |
+| GUI-002 | Consumer Portal — Live Request Status | Real-time status via SSE stream (status_change, progress_updated, approval events, heartbeat); constituent status for compound requests; approval flow inline; fallback to polling | — | — | REQ-001, EVT-002 |
+| GUI-003 | Consumer Portal — Resource Management | View/filter owned resources by state and type; lifecycle state badges; drift indicator; resource detail with Overview/Drift/Audit/Cost/Credentials/Relationships/Groups tabs; state-sensitive action buttons; bulk operations | — | — | LCM-001, DRF-001, AUD-001 |
+| GUI-004 | Consumer Portal — Session and Security | View active sessions; revoke individual or all other sessions; step-up MFA prompt for gated operations; tenant context selector; role-gated navigation (hide not disable) | — | — | SES-001, IAM-001 |
+| GUI-005 | Admin Panel — Platform Dashboard | Control plane component health grid; provider health summary; pending approvals count; open drift records by severity; request throughput; all driven by GET /api/v1/admin/health | — | Platform Admins, SREs configure dashboard widgets; role-gated sections | HLT-003 |
+| GUI-006 | Admin Panel — Governance and Approvals | Approval queue (all tenants); approval detail with risk score breakdown; authority tier registry editor (drag-and-drop reordering, impact report visualization, degradation acceptance flow); scoring threshold editor (auto_approve_below ≤ 50 hard-stop) | — | Policy Owners and Platform Admins | ATM-004, SMX-001 |
+| GUI-007 | Admin Panel — Audit and Compliance | Platform-wide cross-tenant audit trail; pre-built compliance reports (SOC 2, FedRAMP, HIPAA); audit chain integrity status; correlation ID trace; session and security event feed | — | Auditors, Security team, Platform Admins | AUD-001, SES-003 |
+| GUI-008 | Provider Management — Common Shell | Overview, configuration, health history, audit trail, and notification tabs for all 11 provider types; provider owner role gates access; Platform Admins see all providers | — | Provider owners manage own providers; Platform Admins manage all | PRV-001, IAM-001 |
+| GUI-009 | Provider Management — Type Extensions | Service Provider: capacity, managed entities, naturalization mapping, realization history; secrets management: inventory, rotation, revocation, external CA config, algorithm compliance; Auth Provider: session stats, SCIM sync, connection status; external policy evaluation: trust level, contribution pipeline | — | Provider owners access type-specific tabs for their provider type | GUI-008, PRV-001 |
+| GUI-011 | RHDH Plugin Suite | Use DCM capabilities within Red Hat Developer Hub (RHDH) or Backstage via Dynamic Plugins (@dcm/backstage-plugin-*); no RHDH rebuild required for updates | — | Configure RHDH app-config.yaml with DCM connection; configure Dynamic Plugin loading |
+| GUI-012 | Scaffolder Template Auto-Generation | DCM catalog items automatically generate Backstage Software Templates; new resource types appear as templates without UI code; field schema → JSON Schema → Scaffolder form | — | Configure @dcm/backstage-plugin-catalog-backend; template generation is automatic |
+| GUI-013 | DCM Entity Provider | DCMService (catalog items) and DCMResource (realized entities) appear in RHDH Software Catalog; entities sync every PT5M; search-indexed; tenancy enforced via namespace | — | Service account credential configuration; sync interval configuration |
+| GUI-014 | ITSM Integration Bridge | View ITSM references (ServiceNow, Jira) on resource entity Overview tab; link change records to DCM requests; see ITSM-sourced approval votes in request status; CMDB reference on entity pages | ITSM systems receive DCM lifecycle events via notification service_provider and call Admin API to record approval votes; CMDB sync via webhook subscription | Configure ITSM notification service_provider; map DCM event → ITSM action; configure CMDB field mapping | EVT-001, GUI-002 |
+| GUI-010 | Unified Shell | Single DCM web application with role-gated surfaces: Consumer Portal (all actors), Admin Panel (platform roles), Provider Management (provider_owner role), Flow GUI link (policy_owner/sre); one login, one session; no separate applications | — | Platform Admins configure which surfaces are available | IAM-001, SES-001 |
+
+---
+
+
+## 34. ITSM Integration
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| ITSM-001 | ITSM Provider Registration | — | Register as ITSM Provider with declared capabilities (supported_actions, itsm_system, field_mapping_ref, cmdb_ci_type_map); implement standard OIS health check | Register ITSM Providers; review and approve ITSM Provider registrations; configure inbound webhook authentication | PRV-001, CPX-001 |
+| ITSM-002 | Outbound ITSM Record Creation | View ITSM references on resource entities (change request, incident, CMDB CI links with deep links to ITSM system) | Receive action requests from DCM; create/update records in ITSM system; return record ID for storage on entity | Configure ITSM Policies (create_change_request, create_incident, update_cmdb_ci); configure block_until_created for compliance gates | ITSM-001, POL-001 |
+| ITSM-003 | Inbound ITSM Approval Routing | — | Verify HMAC signature on inbound webhook; forward ITSM approval decisions to DCM Admin API approval vote endpoint | Configure inbound webhook secret (secrets management); monitor approval routing from ITSM systems (ServiceNow CAB, Jira workflow) | ITSM-001, CPX-001, ATM-001 |
+| ITSM-004 | ITSM Policy Authoring | — | — | Author ITSM Action policies with template expressions; configure shadow validation; configure on_failure behavior; use block_until_created for pipeline gates (with mandatory timeout per ITSM-005) | ITSM-001, POL-001 |
+| ITSM-005 | CMDB Synchronization | View CMDB CI reference on resource entities; CI auto-created on realization, auto-retired on decommission | Receive create_cmdb_ci and retire_cmdb_ci actions; maintain dcm_entity_uuid correlation on CMDB CI records | Configure CMDB CI type mapping per resource type; monitor CMDB sync failures | ITSM-001, ITSM-002 |
+| ITSM-006 | ITSM Field Mapping Declaration | — | — | Declare field mappings between DCM entity fields and ITSM CI types in ITSM Provider config; validate against Resource Type Specs | ITSM-001 |
+| ITSM-007 | ITSM Policy Template Expression Validation | — | — | Validate template expressions in ITSM Policy action_payload at policy activation; reject unresolvable expressions | ITSM-001, POL-003 |
+
+---
+
+
+## 35. Provider Callback Authentication
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|
+| PCA-001 | Two-Layer Provider Callback Authentication | — | Present valid mTLS certificate (Layer 1) and provider callback credential (Layer 2) on every call to DCM callback endpoints | Configure DCM CA trust anchor; issue provider callback credentials at activation; enforce both layers | PRV-001, ZTS-001 |
+| PCA-002 | Provider Callback Credential Scope Enforcement | — | Use callback credential scoped to own provider_uuid only; cannot act on other providers | Enforce credential scope at validation; reject cross-provider credential use | PRV-001 |
+| PCA-003 | Entity-Level Callback Authorization | — | Receive 403 ENTITY_NOT_OWNED_BY_PROVIDER when pushing state for entities not dispatched to this provider | Enforce per-call entity ownership check independent of credential validity | PRV-001, REQ-007 |
+| PCA-004 | Scope Violation Auto-Suspension | Receive critical notification when owned provider is suspended due to scope violations | — | Auto-suspend provider and notify platform admin after 5 consecutive scope violations within PT1H | PRV-001, ZTS-001 |
+| PCA-005 | Callback Credential Issued by secrets management | — | Retrieve callback credential via secrets management at activation; not directly from API Gateway | Issue callback credentials exclusively through secrets management; reject direct credential issuance requests | PRV-001, CPX-001 |
+| PCA-006 | Registration Token Single-Use Enforcement | — | Use registration token for initial registration only; obtain callback credential after activation | Invalidate registration token after first successful use regardless of expiry timestamp | PRV-001 |
+| PCA-007 | Sovereignty Change Re-Registration | — | Submit new registration with new registration token when sovereignty declaration changes | Require new registration token and approval pipeline for sovereignty declaration changes | PRV-001, GMX-004 |
+| PCA-008 | Callback Credential Pre-Expiry Rotation | — | Implement credential refresh; receive new credential before old expires during transition window | Initiate rotation before expiry; maintain transition window (50% of credential lifetime) | PRV-001, CPX-001 |
+| PCA-009 | IP-Bound Callback Credentials for fsi/sovereign | — | Present callback calls from declared bound_to_ip address for fsi/sovereign profiles | Enforce IP binding on callback credentials for fsi and sovereign profiles | PCA-001, CPX-008 |
+| PCA-010 | All Inbound Provider Calls Produce Audit Records | — | — | Write audit record for every inbound provider call including rejected calls; no silent failures | PCA-001, AUD-001 |
+
+---
+
+
+## 36. Workload Analysis
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|| WLA-001 | Automated Workload Classification | View workload profile for owned resources; see archetype, resource type match, confidence | Report resource metadata via discovery for classification input | Configure classification ruleset version; review low-confidence classifications manually | DRC-001, INF-001 |
+| WLA-002 | Migration Readiness Scoring | View containerization score and migration blockers for owned resources | Report workload characteristics that inform migration scoring | Configure migration readiness thresholds; integrate MTA Information Provider | WLA-001 |
+| WLA-003 | MTA Information Provider Integration | — | Implement workload_analysis information type OR delegate to MTA | Register MTA as Information Provider; configure analysis trigger policies | WLA-001, INF-001 |
+| WLA-004 | On-Demand Re-Analysis | Request re-analysis when resource role changes (`POST /resources/{uuid}/workload-profile:analyze`) | — | Trigger re-analysis for any resource; override archetype manually with reason | WLA-001 |
+| WLA-005 | WorkloadProfile Audit Chain | View analysis history for owned resources | — | Query full analysis history including superseded profiles | WLA-001, AUD-001 |
+| WLA-006 | Low-Confidence Manual Review Gate | Receive notification when owned resource requires manual classification | — | Review and resolve low-confidence classifications; unblock ingestion | WLA-001, LCM-001 |
+
+---
+
+
+## 37. Accreditation Monitoring
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|| ACM-001 | Tier 1 External Registry Verification | — | Declare `external_registry_id` at registration for FedRAMP, CMMC, ISO 27001 | Configure registry poll intervals; review status change alerts; manage `external_registry_id` accuracy | PRV-001, ACR-001 |
+| ACM-002 | Tier 2 Document Currency Verification | — | Maintain current `certificate_ref` and `audit_report_ref` URLs pointing to valid, accessible documents | Configure `max_age` per framework; review document expiry alerts; upload new reports when notified | PRV-001, ACR-001 |
+| ACM-003 | Tier 3 Contract Webhook Integration | — | Configure contract management webhook for BAA and DoD IL accreditations | Register contract system; configure inbound webhook credential; receive BAA/contract lifecycle events | PRV-001, ACR-001 |
+| ACM-004 | Verification Staleness Enforcement | — | Ensure monitoring infrastructure can reach DCM to deliver verification events | Configure stale_after thresholds and stale_action per profile; enforce sovereign/fsi minimum tier requirements | ACM-001, ACR-001 |
+| ACM-005 | Immediate Revocation on External Revoke | Receive notification when provider accreditation is revoked; understand service impact | — | Review `accreditation.status_changed` events; confirm immediate revocations; trigger recovery policy | ACM-001, ACR-001 |
+| ACM-006 | Verification Currency in Scoring | — | Maintain verification currency to maximize accreditation richness score | Monitor verification multiplier impact on provider placement; prioritize externally verified providers | ACM-001, SMX-001 |
+| ACM-007 | Manual Override in Air-Gapped Mode | — | — | Manually update `last_verified_at` with justification in air-gapped deployments; maintain audit trail of manual verifications | ACR-001, AUD-001 |
+
+---
+
+
+## 38. Location Topology Management
+
+| ID | Capability | Consumer | Service Provider | Platform/Admin | Depends On |
+|----|-----------|---------|---------|---------------|-----------|| LOC-001 | Location Type Registry | Browse available location types (standard and custom) | Declare supported locations in provider registration capability declaration | Register custom location types; manage standard type definitions; deprecate types | PRV-001 |
+| LOC-002 | Location Node Management | Browse available locations via `GET /api/v1/locations`; filter by resource type, data classification, sovereignty zone | Declare which location nodes (DC, Zone, etc.) the provider serves at registration | Create, version, and retire location layer instances via GitOps; update mutable capacity fields | LOC-001 |
+| LOC-003 | Location Selection at Request Time | Submit `location_uuid` or `location_handle` with service request; select at any level (Country through Rack); DCM refines to specific DC at placement | — | Configure default location selection rules; enforce location-based placement policies | LOC-001, LOC-002 |
+| LOC-004 | Location Layer Assembly | Transparent — full location context injected into payload automatically | Receive full location context in dispatch payload (location.country_code, location.zone_code, location.dc_code, etc.) | Configure layer assembly order; define location-level field overrides | LOC-002, DLM-001 |
+| LOC-005 | Location-Based Sovereignty Enforcement | See sovereignty zone and data residency on each location node | Declare sovereignty capabilities per served location | Configure `max_data_classification` per location; enforce cross-border policies at location layer | LOC-002, GOV-001 |
+| LOC-006 | Location Hierarchy Navigation | Browse parent/child location relationships; query ancestors of a selected node | — | Manage location hierarchy; validate acyclicity on layer submission | LOC-001, LOC-002 |
+| LOC-007 | Custom Location Types | Use custom location types in selection (e.g., Fleet/Ship in Navy context) | Declare support for resources at custom location types | Register and manage custom types; define level insertion point in hierarchy | LOC-001 |
+| LOC-008 | Location Capacity Visibility | See `capacity_status` (available/limited/full) and `providers_available` count per location | Report capacity scoped to location during reserve query | Update mutable capacity fields (e.g., rack_units_available) without a new layer version | LOC-002, PRV-001 |
+
+---
+
+
+
+
+## 39. Subscription Management (SUB)
+
+| ID | Capability | Consumer | Provider | Platform/Admin | Dependencies |
+|----|-----------|----------|----------|----------------|-------------|
+| SUB-001 | Create subscription | Submit subscription request with tier and terms | Acknowledge subscription, provision initial entities | Approve subscription creation | CAT-001, REQ-001, POL-001 |
+| SUB-002 | Manage subscription lifecycle | Suspend, resume, cancel own subscriptions | Report managed entity status | Force-cancel, view all subscriptions | LCM-001, AUD-001 |
+| SUB-003 | Change subscription tier | Request tier upgrade/downgrade | Adjust capacity for managed entities | Approve tier changes requiring admin | POL-001, POL-003 |
+| SUB-004 | Subscription renewal | Approve/decline renewal; view renewal status | Honor renewed terms | Configure renewal policies | SCH-001, POL-001 |
+| SUB-005 | Provider-originated updates | Approve/reject non-auto updates | Submit updates via standard callback | View update audit trail | PRV-003, AUD-001 |
+| SUB-006 | Update channel management | Configure auto_apply per channel | Declare available update channels | Set organization-wide channel policies | POL-001, POL-003 |
+| SUB-007 | Subscription cost attribution | View subscription cost | Declare cost model per tier | Configure cost allocation rules | — |
+| SUB-008 | Entitlement enforcement | View remaining capacity | Stay within entitlement bounds | Override entitlements in exceptional cases | POL-002 |
+| SUB-009 | Grace period management | View grace period; request extension | Continue health reporting during grace | Configure grace period defaults | LCM-001 |
+| SUB-010 | Subscription audit trail | View own subscription audit history | — | View all subscription audit records | AUD-001, AUD-002 |
+
+## Capability Count Summary
+
+| Domain | Capabilities |
+|--------|-------------|
+| Identity and Access Management | 21 |
+| Service Catalog | 7 |
+| Request Lifecycle Management | 10 |
+| Provider Contract and Realization | 16 |
+| Resource Lifecycle Management | 7 |
+| Drift Detection and Remediation | 5 |
+| Policy Management | 7 |
+| Data Layer Management | 5 |
+| Information and Data Integration | 6 |
+| Ingestion and Brownfield Management | 4 |
+| Audit and Compliance | 5 |
+| Observability and Operations | 6 |
+| Storage and State Management | 8 |
+| DCM Federation and Multi-Instance | 5 |
+| Platform Governance and Administration | 7 |
+| Accreditation Management | 6 |
+| Zero Trust and Security Posture | 8 |
+| Unified Governance Matrix | 7 |
+| Drift Reconciliation | 5 |
+| Federated Contribution Model | 7 |
+| Scoring Model | 10 |
+| Meta Provider Composability | 8 |
+| secrets management Model | 12 |
+| Authority Tier Model | 12 |
+| Event Catalog | 7 |
+| API Versioning | 8 |
+| Session Revocation | 11 |
+| Internal Component Authentication | 8 |
+| Scheduled and Deferred Requests | 6 |
+| Request Dependency Graph | 6 |
+| DCM Self-Health | 6 |
+| Operational Reference | 4 |
+| Web Interfaces | 14 |
+| ITSM Integration | 7 |
+| Provider Callback Authentication | 10 |
+| Workload Analysis | 5 |
+| Accreditation Monitoring | 6 |
+| Location Topology Management | 7 |
+| Subscription Management | 10 |
+| **Total** | **309** |
+---
+
+## Dependency Map — Critical Path Capabilities
+
+These capabilities block the most downstream work and should be implemented first:
+
+```
+IAM-001 (Actor Authentication)
+  └── IAM-002 (Session Tokens) → IAM-003 (RBAC) → IAM-007 (Tenant Scope)
+        └── CAT-001 (Service Catalog)
+              └── REQ-001 (Submit Request)
+                    └── REQ-002 (Intent State) → REQ-003 (Layer Assembly)
+                          └── REQ-004 (Policy Evaluation) → REQ-005 (Placement)
+                                └── REQ-007 (Provider Dispatch)
+                                      └── PRV-003 (Realization) → PRV-005 (Realized State)
+                                            └── LCM-001 (State Transitions)
+                                                  └── DRF-001 (Discovery) → DRF-002 (Drift)
+
+PRV-001 (Provider Registration) — parallel critical path
+  └── PRV-002 (Naturalization) → PRV-003 (Realization)
+  └── PRV-006 (Capacity Reporting) → REQ-005 (Placement)
+```
+
+**Minimum viable DCM capability set (to demonstrate end-to-end lifecycle):**
+
+IAM-001 → IAM-002 → IAM-003 → IAM-007 → CAT-001 → REQ-001 → REQ-002 → REQ-003 → REQ-004 → REQ-005 → REQ-006 → REQ-007 → PRV-001 → PRV-002 → PRV-003 → PRV-004 → PRV-005 → LCM-001 → DRF-001 → DRF-002 → AUD-001
+
+**21 capabilities for a functional end-to-end demonstration.**
+
+**Note:** FCM-001 through FCM-007 (Federated Contribution Model) are not on the critical path — they extend DCM's multi-user capabilities but are not required for the initial end-to-end lifecycle demonstration.
+
+---
+
+*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).*
diff --git a/content/docs/capabilities.md b/content/docs/capabilities.md
new file mode 100644
index 0000000..4bfe0d4
--- /dev/null
+++ b/content/docs/capabilities.md
@@ -0,0 +1,67 @@
+---
+title: "Capabilities Map"
+type: docs
+weight: 8
+---
+
+The DCM Capabilities Map is an interactive reference of the 95 foundational capabilities required for DCM to perform lifecycle management. Each capability maps to three perspectives: **Consumer**, **Service Provider**, and **Platform/Admin**.
+
+## Using the Map
+
+- **Domain cards** — click any of the 15 domain cards to filter to that capability area; click again to clear
+- **Search** — matches against capability IDs, names, descriptions, and dependencies; search `IAM-001` to find all capabilities that depend on authentication
+- **Perspective filter** — extract the implementation checklist for a specific role
+- **Table / Cards** toggle — dense comparison view or individual capability cards
+- **MVP badges** — the 21 capabilities on the minimum viable critical path
+- **Dependency tags** — clickable in table view; jumps to that capability
+- **CSV download** — built into the map; also available as a [direct download](/capabilities/DCM-Capabilities-Matrix.csv)
+
+## Capability Domains
+
+| Prefix | Domain | Capabilities |
+|--------|--------|-------------|
+| IAM | Identity and Access Management | 7 |
+| CAT | Service Catalog | 7 |
+| REQ | Request Lifecycle Management | 10 |
+| PRV | Provider Contract and Realization | 9 |
+| LCM | Resource Lifecycle Management | 7 |
+| DRF | Drift Detection and Remediation | 5 |
+| POL | Policy Management | 7 |
+| LAY | Data Layer Management | 5 |
+| INF | Information and Data Integration | 6 |
+| ING | Ingestion and Brownfield Management | 4 |
+| AUD | Audit and Compliance | 5 |
+| OBS | Observability and Operations | 5 |
+| STO | Storage and State Management | 6 |
+| FED | DCM Federation and Multi-Instance | 5 |
+| GOV | Platform Governance and Administration | 7 |
+
+## MVP Critical Path — 21 Capabilities
+
+The minimum viable set for an end-to-end DCM lifecycle demonstration:
+
+```
+IAM-001 → IAM-002 → IAM-003 → IAM-007 → CAT-001
+  → REQ-001 → REQ-002 → REQ-003 → REQ-004 → REQ-005 → REQ-006 → REQ-007
+    → PRV-001 → PRV-002 → PRV-003 → PRV-004 → PRV-005
+      → LCM-001 → DRF-001 → DRF-002 → AUD-001
+```
+
+## Downloads
+
+| Format | Use |
+|--------|-----|
+| [CSV](/capabilities/DCM-Capabilities-Matrix.csv) | Import into Jira, Confluence, Notion, Airtable |
+| [Standalone Map](/capabilities/map.html) | Full-page interactive map |
+| [Markdown Reference](/docs/capabilities-matrix) | Full matrix with dependency map |
+
+## Interactive Map
+
+
+ +
diff --git a/content/docs/dcm-documentation-review-strategy.md b/content/docs/dcm-documentation-review-strategy.md new file mode 100644 index 0000000..f50e605 --- /dev/null +++ b/content/docs/dcm-documentation-review-strategy.md @@ -0,0 +1,191 @@ +# DCM Architecture Documentation — Review Strategy + +**Author:** Chris Roadfeldt, Principal Architect +**Date:** April 2026 +**Audience:** DCM Engineering Team + +--- + +## The Problem + +The DCM architecture now comprises 58 data model documents, 15 specifications, 4 OpenAPI schemas, and a capabilities matrix with 331 entries — roughly 30,000 lines of technical documentation. This body of work defines the complete control plane, data model, provider contract, policy engine, audit system, and governance model. + +This documentation needs engineering review, but the experience with PRs #7 and #8 demonstrated the challenge: even GitHub's Sourcery bot hit its 20,000-line review limit. Nobody is going to read 30,000 lines linearly, and asking the team to do so would be neither productive nor respectful of their time. + +This document proposes a strategy to make the architecture documentation consumable, reviewable, and useful for the engineering team. + +--- + +## Principles + +1. **Nobody needs to read everything.** Each team member needs the 3-5 documents relevant to their area, not all 58. +2. **Decisions are reviewable. Reference material is not.** The team should review *what we decided and why* — not the full specification text that implements those decisions. +3. **Concrete examples beat abstract specifications.** A worked example tracing one request through the full pipeline teaches more than 10 documents of structural definitions. +4. **The documentation should be queryable, not just readable.** The AI prompt (5,768 lines, 125 sections) enables conversational exploration of any architectural topic. + +--- + +## Proposal 1: Role-Based Reading Guide + +**Effort:** Low (1 page) +**Impact:** Immediate + +Create a single-page reading guide that tells each person where to start based on what they work on. Example: + +| If you work on... | Start with these documents | Then reference... | +|---|---|---| +| **Provider development** (VM, ACM, network) | Doc A (Provider Contract), Doc 06 (Resource Entities), Provider Dev Guide | Docs 10, 22, 30 (provider types) | +| **Policy and placement** | Doc B (Policy Contract §1-7), Doc 14 (Profiles) | Doc B §8-9 (Constraint Registry, Templates), §18 (Overrides) | +| **Request pipeline** (orchestrator, processor) | Doc 02 (Four States), Doc 03 (Layering), Doc 04 (Examples) | Doc 25 (Control Plane), Consumer API Spec | +| **Catalog and API gateway** | Doc 06 (Resource Entities), Consumer API Spec, Admin API Spec | Doc 36 (API Versioning), AEP conventions | +| **Audit, security, compliance** | Doc 16 (Universal Audit §1-8), Doc 31 (Credentials), Doc 43 (Callback Auth) | Doc 26 (Governance Matrix), Doc 14 §8-10 | +| **Database and storage** | Doc 11 (Data Store Contracts), Doc 02 §4 (Data Domains), SQL Schema | Doc 17 (Deployment) | + +This cuts the perceived scope by 80% for any individual reviewer. Each path is 3-5 documents, not 58. + +--- + +## Proposal 2: Architecture Decision Records (ADRs) + +**Effort:** Medium (1 page per major decision) +**Impact:** High — this is what the team actually needs to review + +The team does not need to review the full 1,344-line Policy Contract to understand the override model. They need a 1-page summary that says: + +> **Decision:** 5 override mechanisms layered by severity. +> **Context:** Requests can be blocked by hard enforcement policies. The team needs a way to handle legitimate exceptions without undermining policy governance. +> **Options considered:** Override policies only, manual override only, exception grants, compensating controls. +> **Decision:** All five, layered: Override Policy (planned) → Exception Grant (pre-authorized) → Manual Override (immediate) → Compensating Control (structural) → Dual-Approval (modifier for hard policies). +> **Consequences:** `override_requests` SQL table added. 5 new events. Consumer sees POLICY_BLOCKED with resolution options (modify, override, cancel, escalate). Admin API gets 4 new endpoints. + +The major decisions that need ADRs (roughly 15): + +1. One required infrastructure (PostgreSQL only) +2. Internal auth/secrets/events with optional external delegation +3. 5 provider types (why these 5, what was consolidated) +4. 2 policy evaluation modes (Internal/External) +5. Evaluation Context with multi-pass convergence +6. Constraint Type Registry and Policy Templates +7. Lifecycle-scoped policy evaluation (10 operation types) +8. Policy Override Model (5 mechanisms) +9. Policy Block Resolution (consumer options, not auto-override) +10. Merkle tree audit with configurable granularity +11. Data-driven policy matching (4 sources) +12. Stage signing and payload chain of custody +13. AEP API conventions adoption +14. Traefik over KrakenD (API Gateway decision) +15. PostgreSQL consolidation (stores model) + +ADRs are individually reviewable. The full specification documents become reference material that people consult when implementing — not review material that blocks progress. + +--- + +## Proposal 3: Domain-Split PRs + +**Effort:** Medium (splitting existing content into themed PRs) +**Impact:** High — makes the merge process manageable + +Instead of one PR with the entire architecture, submit 6-8 PRs by domain. Each is reviewable in one sitting and can be assigned to the most relevant reviewer: + +| PR | Content | Primary Reviewer | Size | +|---|---|---|---| +| Core Data Model | Docs 00-04, 11, SQL schema | machacekondra | ~3,000 lines | +| Provider Contract | Doc A, docs 06, 10, 22, 30, provider callback | pkliczewski, ygalblum | ~3,500 lines | +| Policy Contract | Doc B, doc 14 | gabriel-farache | ~4,000 lines | +| Audit and Security | Docs 16, 31, 43, 26, 27 | jenniferubah | ~2,500 lines | +| Request Pipeline and Lifecycle | Docs 25, 50, 36, consumer/admin API specs | machacekondra | ~3,000 lines | +| Capabilities and Examples | Capabilities Matrix, doc 04, doc 52, taxonomy | All (light review) | ~2,500 lines | +| Infrastructure and Deployment | Doc 51, doc 17, doc 41, OpenAPI schemas | ygalblum | ~2,000 lines | +| Federation and Advanced | Docs 20, 44, 48, DISCUSSION-TOPICS | pkliczewski | ~2,000 lines | + +Each reviewer focuses on their area of expertise. Cross-cutting concerns (like "does the policy model work with the provider contract?") are handled in a final integration review after the domain PRs merge. + +--- + +## Proposal 4: End-to-End Walkthrough Document + +**Effort:** Medium (1 document, ~500 lines) +**Impact:** Very High — the single most useful onboarding artifact + +The team responded well to concrete examples. machacekondra specifically requested the three-tier app example. A dedicated walkthrough document that traces one request through the *entire* pipeline — with actual YAML payloads at each stage — would be worth more than 10 specification documents. + +The walkthrough would cover: + +1. Consumer submits a VM request (show the API call and intent payload) +2. Layer assembly (show which layers merge and the assembled payload) +3. Policy evaluation (show which policies fire, what constraints emit, how conflicts resolve) +4. A policy blocks the request (show the POLICY_BLOCKED response with resolution guidance) +5. Consumer modifies the request (show the modified payload) +6. Placement (show candidate scoring and selection) +7. Dispatch to provider (show the naturalized payload) +8. Provider callback (show the realized state) +9. Audit trail (show the Merkle tree leaves at mutation granularity) + +Every stage shows real data structures, not abstract descriptions. The walkthrough references the specification documents for detail but stands alone as a readable narrative. + +--- + +## Proposal 5: Interactive Architecture Map on the Website + +**Effort:** Higher (requires frontend work) +**Impact:** High for onboarding and navigation + +The Hugo website could host a visual diagram (Mermaid or D3) showing the 9 control plane services, the pipeline flow, and provider interactions. Each node links to the relevant documentation. People orient visually before drilling into text. + +This is lower priority than proposals 1-4 but would be a strong differentiator for the project's public presence and for onboarding new contributors. + +--- + +## Proposal 6: AI Prompt as Team Onboarding Tool + +**Effort:** Zero (already built) +**Impact:** Immediate for anyone willing to use it + +The AI prompt (DCM-AI-PROMPT.md) is the most comprehensive single document in the project: 5,768 lines covering every architectural decision, every capability, every data structure, and every cross-reference. When loaded into Claude (or any capable LLM), it enables conversational exploration: + +- "How does the override model work?" → 2-paragraph answer with doc references +- "What happens when a sovereignty policy blocks a request?" → full flow with YAML examples +- "What's the difference between Internal and External policy evaluation?" → comparison table + +This is faster than reading documentation for exploratory understanding. The team should be encouraged to use it as a first stop before reading specification documents. + +--- + +## Proposal 7: Session Changelogs + +**Effort:** Low (produced as part of each work session) +**Impact:** Keeps the team current without re-reading everything + +After each significant architecture session, produce a short changelog: + +> **Session: April 4-7, 2026** +> +> **Added:** Policy Override Model (doc B §18) — 5 mechanisms layered by severity. Policy Block Resolution (doc B §18.8) — consumer gets compliant value suggestions, not just a deny. Merkle tree audit (doc 16 §8) — configurable granularity (stage/mutation/field). Lifecycle-scoped policy evaluation (doc B §2.2-2.3) — 10 operation types, changed_field_filter. Test Framework Specification (doc 52) — 60 invariants, machine-readable YAML summary. OpenStack Nova example provider. +> +> **Changed:** SQL schema now 18 tables (+override_requests, signed_tree_heads, merkle_tree_nodes). Capabilities matrix 309→331. Admin API 57→61 paths. Events 101→109. +> +> **Why it matters for you:** If you're working on the policy engine, read doc B §18 (override model) and §2.2 (lifecycle scope). If you're working on audit, read doc 16 §8 (Merkle tree). If you're working on providers, the OpenStack Nova example in dcm-examples shows the complete naturalization/denaturalization pattern. + +The team reads the changelog (~1 page). They drill into specific documents only when something affects their work. + +--- + +## Recommended Implementation Order + +| Priority | Proposal | Effort | Timeline | +|---|---|---|---| +| **1** | Reading Guide | 1 day | This week | +| **2** | Session Changelogs | Ongoing | Start immediately | +| **3** | Domain-Split PRs | 2-3 days | Before next review cycle | +| **4** | ADRs for top 15 decisions | 3-5 days | Next two weeks | +| **5** | End-to-End Walkthrough | 2-3 days | Before summit demo prep | +| **6** | AI Prompt onboarding | 0 days | Announce to team | +| **7** | Interactive Architecture Map | 1-2 weeks | Future | + +--- + +## Summary + +The architecture is comprehensive and consistent. The problem is not the documentation quality — it's the volume. The team needs navigational aids, decision summaries, and concrete examples to make 30,000 lines of specification consumable. The proposals above provide a layered approach: immediate wins (reading guide, changelogs, AI prompt), near-term improvements (domain-split PRs, ADRs), and longer-term investments (walkthrough, interactive map). + +The goal is that no team member ever needs to read more than 3,000 lines to understand their area, and that every architectural decision is reviewable in a 1-page ADR — not buried in a 1,344-line specification document. diff --git a/content/docs/dcm-pattern-catalog-overlay.md b/content/docs/dcm-pattern-catalog-overlay.md new file mode 100644 index 0000000..9b78350 --- /dev/null +++ b/content/docs/dcm-pattern-catalog-overlay.md @@ -0,0 +1,408 @@ +# Deployment Pattern Catalog — How It Overlays on DCM + +**Date:** April 2026 +**Status:** Architecture Overlay — For Team Discussion + +--- + +## What a Deployment Pattern Is + +A deployment pattern is a **reusable, provider-agnostic blueprint** that defines a collection of resources, their dependencies, their runtime wiring, and their operational policies — together delivering a service that no single provider offers. + +Examples: + +| Pattern | Constituents | What it delivers | +|---------|-------------|-----------------| +| **Standard Web Application** | 2 app server VMs, 1 PostgreSQL DB, 1 load balancer, 1 network segment, 3 DNS records | A production web app with HA, monitoring, and DNS | +| **Secure Data Pipeline** | 1 Kafka cluster, 2 worker VMs, 1 S3-compatible store, 1 network policy, 1 encryption key | An encrypted ingest pipeline with data residency controls | +| **Developer Sandbox** | 1 VM, 1 namespace, 1 ephemeral DB, 1 port forward | A disposable dev environment with TTL auto-cleanup | +| **Regulated Database Service** | 1 PostgreSQL cluster (HA), 1 backup schedule, 1 encryption key, 1 audit log sink, 2 DNS records | A database meeting FSI data handling requirements | +| **Edge Compute Node** | 1 bare metal host, 1 MicroShift cluster, 1 VPN tunnel, 1 monitoring agent, 1 cert | A self-contained edge node with central management | + +The key property: **no single provider owns the pattern.** The PostgreSQL DB might come from one provider, the VMs from another, the load balancer from a third, and the DNS records from a fourth. The pattern defines *what* is needed and *how the pieces connect* — DCM figures out *who* provides each piece. + +--- + +## The Layering Model + +``` +┌─────────────────────────────────────────────────────────┐ +│ PATTERN CATALOG │ +│ Curated library of reusable deployment blueprints │ +│ "Standard Web App" · "Data Pipeline" · "Dev Sandbox" │ +│ │ +│ Authored by: Platform Engineers │ +│ Consumed by: Consumer Developers │ +│ Stored in: Resource Type Registry (compound types) │ +├─────────────────────────────────────────────────────────┤ +│ SERVICE CATALOG │ +│ Provider-specific offerings of patterns + atomic items │ +│ "EU-WEST Web App — Standard" · "APAC VM — Large" │ +│ │ +│ Populated by: Providers (atomic) + Patterns (compound) │ +│ Consumed by: Consumer Developers │ +├─────────────────────────────────────────────────────────┤ +│ DCM CONTROL PLANE │ +│ Decompose → Policy → Placement → Dispatch → Audit │ +│ │ +│ Processes: Both atomic requests and pattern requests │ +│ Each constituent → full pipeline independently │ +├─────────────────────────────────────────────────────────┤ +│ SERVICE PROVIDERS │ +│ VM · Network · Database · DNS · Storage · Container │ +│ │ +│ Fulfill: Individual constituents of a pattern │ +│ Report back: Realized state per constituent │ +└─────────────────────────────────────────────────────────┘ +``` + +**The Pattern Catalog is not a new architectural component.** It is a curated view of the Resource Type Registry filtered to compound resource types. DCM already has all the machinery to execute patterns — the compound service model, dependency graphs, binding fields, and constituent dispatch. What the Pattern Catalog adds is the **curation and consumer experience layer** on top of that machinery. + +--- + +## How a Pattern Maps to DCM Constructs + +A single deployment pattern maps to these existing DCM concepts: + +| Pattern concept | DCM construct | Where it lives | +|----------------|--------------|----------------| +| The pattern itself | Compound Resource Type Specification | Resource Type Registry | +| The constituents | Resource Type references with dependency declarations | `constituents[]` in the compound spec | +| How pieces connect | Binding fields — runtime values from one constituent injected into another | `binding_fields[]` on dependent constituents | +| What the consumer fills in | Parameterized fields exposed at the pattern level | `fields_from_parent[]` mapping pattern params → constituent fields | +| Who provides each piece | `provided_by: external` (DCM places) or `provided_by: self` (compound service handles) | Per-constituent declaration | +| What happens on failure | Lifecycle policy on the compound spec | `on_constituent_failure: rollback_all | continue_degraded | notify` | +| Operational policies | Standard DCM policies scoped to the pattern's resource type | Policy match on `resource_type = ApplicationStack.WebApp` | + +--- + +## Concrete Example: "Standard Web Application" Pattern + +### Pattern Definition (authored by Platform Engineer) + +```yaml +# Registered in Resource Type Registry as a compound resource type +resource_type: ApplicationStack.WebApp +version: "1.0.0" +entity_type: composite_resource +description: "Production web application with database, app servers, load balancer, and DNS" + +# What the consumer fills in when requesting this pattern +parameters: + app_name: + type: string + required: true + description: "Application name — used for hostnames, DNS, and resource tagging" + environment: + type: string + required: true + constraint: { layer_reference: "environment" } # values governed by environment layers + db_engine: + type: string + required: true + default: postgresql + constraint: { enum: [postgresql, mysql, mariadb] } + db_storage_gb: + type: integer + required: true + default: 50 + constraint: { min: 10, max: 1000 } + app_replicas: + type: integer + required: true + default: 2 + constraint: { min: 1, max: 10 } + expose_public: + type: boolean + default: false + description: "Whether to create a public DNS record and public LB listener" + +# The constituents and how they connect +constituents: + - name: network_segment + resource_type: Network.Segment + provided_by: external + depends_on: [] + required_for_delivery: required + fields_from_parent: + - source: "environment" + target: "environment" + - source: "app_name" + target: "segment_name_prefix" + + - name: database + resource_type: Database.Managed + provided_by: external + depends_on: [network_segment] + required_for_delivery: required + binding_fields: + - source: "network_segment.subnet_cidr" + target: "database.network_cidr" + - source: "network_segment.security_group_id" + target: "database.security_group_id" + fields_from_parent: + - source: "db_engine" + target: "engine" + - source: "db_storage_gb" + target: "storage_gb" + - source: "environment" + target: "environment" + + - name: app_server + resource_type: Compute.VirtualMachine + provided_by: external + depends_on: [database, network_segment] + required_for_delivery: required + binding_fields: + - source: "database.ip_address" + target: "app_server.config.db_host" + - source: "database.port" + target: "app_server.config.db_port" + - source: "database.credentials_ref" + target: "app_server.config.db_credentials_ref" + - source: "network_segment.subnet_cidr" + target: "app_server.network_cidr" + fields_from_parent: + - source: "app_name" + target: "hostname_prefix" + - source: "app_replicas" + target: "replicas" + - source: "environment" + target: "environment" + + - name: load_balancer + resource_type: Network.LoadBalancer + provided_by: external + depends_on: [app_server] + required_for_delivery: required + binding_fields: + - source: "app_server.ip_addresses" + target: "load_balancer.backend_pool" + - source: "app_server.port" + target: "load_balancer.backend_port" + fields_from_parent: + - source: "app_name" + target: "lb_name" + - source: "expose_public" + target: "public_listener" + + - name: dns_internal + resource_type: DNS.Record + provided_by: external + depends_on: [load_balancer] + required_for_delivery: partial + binding_fields: + - source: "load_balancer.vip_address" + target: "dns_internal.target_address" + fields_from_parent: + - source: "app_name" + target: "hostname" + + - name: dns_public + resource_type: DNS.Record + provided_by: external + depends_on: [load_balancer] + required_for_delivery: optional + condition: "parent.expose_public == true" # only created if consumer wants public access + binding_fields: + - source: "load_balancer.public_vip_address" + target: "dns_public.target_address" + fields_from_parent: + - source: "app_name" + target: "hostname" + +lifecycle_policy: + on_constituent_failure: rollback_all + decommission_order: reverse_dependency # DNS first, then LB, then app, then DB, then network +``` + +### What the Consumer Sees + +The consumer browses the service catalog, finds "Standard Web Application," and submits: + +```json +POST /api/v1/requests +{ + "catalog_item_uuid": "webapp-standard-uuid", + "fields": { + "app_name": "pet-clinic", + "environment": "production", + "db_engine": "postgresql", + "db_storage_gb": 100, + "app_replicas": 3, + "expose_public": true + } +} +``` + +Six fields. The consumer has no idea that this will produce 6 resources across potentially 4 different providers. + +### What DCM Does + +``` +1. Intent captured — consumer's 6 fields stored + +2. Pattern decomposed — DCM reads the compound resource type spec + → 6 constituents identified + → Dependency graph resolved: + Network Segment (no deps) + ├── Database (needs network) + │ └── App Server (needs DB + network) + │ └── Load Balancer (needs app server) + │ ├── DNS Internal (needs LB) + │ └── DNS Public (needs LB, conditional on expose_public=true) + +3. Each constituent gets its own full pipeline: + → Layer assembly (datacenter, environment, tenant, compliance layers merge in) + → Policy evaluation (sovereignty, sizing, naming, monitoring — per constituent) + → Placement (each constituent placed independently, all honoring sovereignty) + +4. Execution in dependency order: + + Round 1: Network Segment + → Placed with EU-WEST network provider + → Realized: subnet_cidr=10.5.0.0/24, security_group_id=sg-abc123 + + Round 2: Database (binding fields inject network values) + → config.network_cidr = 10.5.0.0/24 (from network_segment) + → config.security_group_id = sg-abc123 (from network_segment) + → Placed with EU-WEST database provider + → Realized: ip_address=10.5.0.50, port=5432, credentials_ref=vault:secret/pet-clinic-db + + Round 3: App Server (binding fields inject DB + network values) + → config.db_host = 10.5.0.50 (from database) + → config.db_port = 5432 (from database) + → config.db_credentials_ref = vault:secret/pet-clinic-db (from database) + → config.network_cidr = 10.5.0.0/24 (from network_segment) + → Placed with EU-WEST compute provider, 3 replicas + → Realized: ip_addresses=[10.5.0.10, 10.5.0.11, 10.5.0.12], port=8080 + + Round 4: Load Balancer (binding fields inject app server IPs) + → config.backend_pool = [10.5.0.10, 10.5.0.11, 10.5.0.12] (from app_server) + → config.backend_port = 8080 (from app_server) + → Placed with EU-WEST network provider + → Realized: vip_address=10.5.0.100, public_vip_address=203.0.113.50 + + Round 5: DNS records (binding fields inject LB addresses) + → DNS Internal: pet-clinic.internal → 10.5.0.100 + → DNS Public: pet-clinic.example.com → 203.0.113.50 (created because expose_public=true) + +5. All constituents realized → composite entity status: OPERATIONAL + → Consumer receives: entity_uuid, status, and connection details + → 6 entities, each with independent audit trail, drift detection, lifecycle +``` + +### What Policies See + +Policies don't need special awareness of patterns. Each constituent is a standard DCM request with a standard resource type. Existing policies apply naturally: + +| Policy | Fires on which constituent | What it does | +|--------|---------------------------|-------------| +| Sovereignty GateKeeper | All 6 | Ensures all constituents land in EU-WEST | +| VM sizing limits | App Server only | Validates replica count and VM size within tenant tier | +| DB storage limits | Database only | Validates db_storage_gb within allowed range | +| Network naming | Network Segment, DNS | Enforces naming conventions | +| Monitoring injection | App Server, Database | Injects monitoring agent config | +| Backup policy | Database | Injects backup schedule based on environment | + +**No new policy types are needed.** The pattern decomposes into standard resource types, and standard policies match on those types. + +--- + +## How the Pattern Catalog Surfaces in DCM + +### In the Resource Type Registry + +Patterns are compound Resource Type Specifications with `entity_type: composite_resource`. They live alongside atomic resource types in the same registry: + +``` +Resource Type Registry +├── Compute.VirtualMachine (atomic) +├── Network.Segment (atomic) +├── Database.Managed (atomic) +├── DNS.Record (atomic) +├── Network.LoadBalancer (atomic) +├── ApplicationStack.WebApp (compound ← this is a pattern) +├── ApplicationStack.DataPipeline (compound ← this is a pattern) +├── Environment.DevSandbox (compound ← this is a pattern) +└── Platform.EdgeNode (compound ← this is a pattern) +``` + +### In the Service Catalog + +Provider catalog items can reference either atomic or compound resource types. For patterns, the catalog item represents the pattern itself — the consumer requests the pattern, not the individual constituents: + +``` +Service Catalog +├── "EU-WEST VM — Standard" → Compute.VirtualMachine (atomic, provider-specific) +├── "EU-WEST VM — Large" → Compute.VirtualMachine (atomic, provider-specific) +├── "Standard Web Application" → ApplicationStack.WebApp (compound, multi-provider) +├── "Secure Data Pipeline" → ApplicationStack.DataPipeline (compound, multi-provider) +└── "Developer Sandbox" → Environment.DevSandbox (compound, multi-provider) +``` + +### In the Consumer API + +No API changes. The consumer requests a catalog item. Whether it's atomic or compound is transparent — the same `POST /api/v1/requests` endpoint handles both. The response includes constituent status for compound requests. + +### In RHDH + +The RHDH catalog page shows patterns alongside atomic offerings. Patterns have a "Components" view showing the constituent resources, their dependency graph, and (after realization) the binding field values. Platform engineers use RHDH scaffolding templates to create new patterns. + +--- + +## Who Authors Patterns vs Who Consumes Them + +| Role | What they do with patterns | +|------|--------------------------| +| **Platform Engineer** | Authors pattern definitions (compound resource type specs). Defines constituents, dependencies, binding fields, parameters, lifecycle policies. Registers patterns in the Resource Type Registry. Creates service catalog items for patterns. | +| **Policy/Compliance Owner** | Writes policies that apply to pattern constituents. Does not need to know about patterns specifically — policies match on resource types, which patterns decompose into. May also write pattern-level policies (e.g., "all ApplicationStack.* types require monitoring on every constituent"). | +| **Consumer Developer** | Browses the catalog, selects a pattern, fills in parameters, submits. Sees aggregate status. Can drill into constituent detail. Does not need to understand the decomposition. | +| **Infrastructure Operator** | Provides the atomic services that patterns compose. Registers providers for Compute, Network, Database, DNS — not for the pattern itself. | + +--- + +## Pattern Lifecycle + +Patterns follow the standard DCM artifact lifecycle: + +``` +developing → proposed → active → deprecated → retired +``` + +**Versioning:** Patterns are versioned (`ApplicationStack.WebApp v1.0.0`). A new version can add optional constituents, change defaults, or add new binding fields without breaking existing deployments. Removing a required constituent is a major version bump. + +**Deprecation:** When a pattern version is deprecated, existing realized instances continue operating. New requests are redirected to the successor version. Consumers are notified of the deprecation timeline. + +**Pattern evolution:** Adding an optional constituent (e.g., adding a cache layer to the web app pattern) is a minor version bump. Existing deployments don't gain the new constituent automatically — but new requests do. Consumers with existing deployments can opt in via an update request. + +--- + +## Interaction with Other DCM Features + +| Feature | How it interacts with patterns | +|---------|-------------------------------| +| **Drift detection** | Each constituent is independently discoverable. Drift on any constituent is detected and attributed to that constituent — not to the pattern as a whole. | +| **Decommission** | Pattern decommission triggers reverse-dependency-order teardown of all constituents. Consumer can also decommission individual constituents (e.g., remove the public DNS record) without tearing down the pattern. | +| **Rehydration** | Pattern rehydration rebuilds all constituents in dependency order with current policies. Binding fields resolve against newly realized values. | +| **Sovereignty** | Every constituent is independently sovereignty-checked. A pattern cannot span sovereignty zones unless every constituent passes its own sovereignty policy. | +| **Cost estimation** | Pattern cost is the sum of constituent costs. Each constituent's cost comes from its provider catalog item. | +| **Audit** | Each constituent has its own Merkle audit trail. The pattern entity has a composite audit record linking all constituent entity_uuids. | +| **Override** | A policy block on any constituent blocks the entire pattern. The consumer resolves the block for that specific constituent — modify, override, cancel, or escalate to the responsible policy domain owner. | +| **Federation** | Pattern constituents can be placed across DCM instances. The network segment might be local while the database is federated to a remote DCM with a specialized database provider. | + +--- + +## New Use Case for Requirements Document + +### UC-100: Deploy a Resource Pattern from the Pattern Catalog + +A consumer browses the Pattern Catalog section of the service catalog and selects "Standard Web Application." The catalog shows the pattern's components (network, database, app servers, load balancer, DNS), the parameters the consumer needs to provide, the dependency graph, and estimated cost. + +The consumer fills in 6 parameters (app_name, environment, db_engine, db_storage_gb, app_replicas, expose_public) and submits. DCM decomposes the pattern into 6 constituent resources, resolves the dependency graph, and processes each constituent through the full pipeline — layer assembly, policy evaluation, placement, dispatch — independently. Binding fields inject runtime values (IP addresses, connection strings, credentials references) from realized constituents into dependent ones. + +The consumer monitors aggregate progress ("3 of 6 constituents realized") and can drill into individual constituent status. On completion, the consumer has a fully wired application environment: database with data, app servers connected to the database, load balancer distributing traffic, and DNS records resolving. + +If any required constituent fails (e.g., the database provider reports an error), the pattern's lifecycle policy determines the response — rollback all realized constituents, continue in degraded mode, or notify and hold for manual intervention. + +**Success criteria:** Single request produces a complete, wired application environment. Runtime values flow correctly between constituents via binding fields. Each constituent is independently managed (own entity_uuid, audit trail, drift detection, lifecycle). Decommission reverses dependency order. Sovereignty enforced per-constituent. No new control plane services, policy types, or API endpoints required — patterns use existing DCM machinery. diff --git a/content/docs/dcm-platform-requirements.md b/content/docs/dcm-platform-requirements.md new file mode 100644 index 0000000..1648a0c --- /dev/null +++ b/content/docs/dcm-platform-requirements.md @@ -0,0 +1,700 @@ +# DCM PLATFORM REQUIREMENTS DOCUMENT + +**Data Center Management — Sovereign Cloud Framework** + +*Requirements for the DCM control plane, data model, and provider ecosystem enabling enterprise organizations to manage infrastructure lifecycle with policy-as-code governance, tamper-evident audit, and multi-provider abstraction.* + +**Date:** April 2026 +**Author:** Chris Roadfeldt, Principal Architect +**Project:** Red Hat FlightPath — github.com/dcm-project + +--- + +# Engagement Context + +DCM addresses a fundamental gap in enterprise infrastructure management: on-premises data centers lack the unified control plane that public clouds provide as table stakes. Organizations operating large-scale, multi-platform infrastructure spend disproportionate engineering effort stitching together disparate automation tools, enforcing governance manually, and reconciling inventory that diverges silently between intended and actual state. + +**Target Organizations** + +- Enterprise data center operators running heterogeneous infrastructure (VM, container, network, storage) across multiple platforms +- Organizations with regulatory requirements (financial services, government, healthcare) requiring provable audit trails and data residency enforcement +- Platform engineering teams seeking to offer self-service infrastructure to development teams with built-in governance + +**DCM's Value Proposition** + +DCM is the management plane that sits above provisioning tools (Ansible, Terraform, Kubernetes operators) and governs what gets requested, approved, built, owned, and decommissioned. It provides: a unified data model and API across all infrastructure platforms; policy-as-code enforcement on every request before provisioning; full lifecycle management from request through decommission with tamper-evident audit; and a provider abstraction that makes any infrastructure platform consumable through the same interface. + +**Delivery Context** + +DCM is an open-source project under the Red Hat FlightPath initiative, licensed under Apache 2.0. The engineering team spans Red Hat and community contributors. The initial milestone is a summit demonstration showing end-to-end provisioning with policy enforcement and audit verification. + +--- + +# Executive Summary + +DCM requires a control plane that enables: declarative lifecycle management of infrastructure resources across heterogeneous providers; policy-as-code governance enforced on every request with configurable override mechanisms; a provider abstraction that makes any infrastructure platform — VM, container, network, storage — consumable through the same interface and data model; tamper-evident audit with mathematical provability (Merkle tree) at configurable granularity; sovereignty and data residency enforcement as first-class concepts, not afterthoughts; and a consumer experience that abstracts infrastructure complexity behind a service catalog. + +The platform is designed as a management plane — it orchestrates lifecycle, enforces governance, and maintains state. It does not provision infrastructure directly. Provisioning is delegated to service providers (OpenStack, KubeVirt, ACM, Ansible, Terraform) that implement DCM's provider contract. + +--- + +# Personas + +## Consumer Developer + +A developer or application owner who requests and manages infrastructure resources through the service catalog. Interacts with DCM via API, Web UI (RHDH), or Git PR. Does not need to understand which provider, datacenter, or network configuration fulfills their request — DCM handles that. + +- **Key activities:** Browse service catalog, submit resource requests, track request status, handle policy blocks, manage running resources, view cost estimates, trigger decommission + +## Platform Engineer + +Defines the organizational infrastructure standards that DCM enforces. Authors resource type specifications, data layers (datacenter configs, environment defaults, tenant overrides), policies, and compound resource type specifications (three-tier applications, data pipelines). + +- **Key activities:** Define resource types and catalog items, author data layers, write and test policies (shadow mode), create compound service definitions, manage the resource type registry + +## Infrastructure Operator + +Manages the physical and virtual infrastructure that DCM's providers abstract. Implements and operates service providers, manages provider health and capacity, handles accreditation and sovereignty declarations, and responds to drift remediation alerts. + +- **Key activities:** Implement and register service providers, manage provider health and capacity reporting, handle naturalization/denaturalization, respond to discovery and drift events + +## Policy and Compliance Owner + +Defines and manages the governance rules that DCM enforces. Authors GateKeeper policies (allow/deny), validation policies, sovereignty constraints, and override approval rules. Reviews audit trails and compliance reports. + +- **Key activities:** Author and activate policies, configure policy profiles (minimal through sovereign), review override requests (dual-approval), verify audit integrity, manage compliance rescans + +## Platform Administrator + +Deploys and operates the DCM control plane itself. Manages the 9 control plane services, database, tenant configuration, auth provider setup, and platform health monitoring. + +- **Key activities:** Deploy and upgrade DCM, onboard tenants, configure auth providers, manage profiles and deployment configuration, monitor platform health, rotate credentials + +--- + +# Use Cases + +The following use cases capture DCM's core requirements, organized by lifecycle phase and prioritized for the summit demonstration and subsequent delivery phases. + +| **ID** | **Use Case** | **Priority** | **Personas** | +|--------|-------------|-------------|-------------| +| **UC-001** | Deploy DCM Control Plane | **P0** | Administrator | +| **UC-002** | Register Service Providers | **P0** | Operator, Administrator | +| **UC-003** | Populate Service Catalog | **P0** | Platform Engineer | +| **UC-004** | Configure Organizational Policies | **P0** | Policy Owner, Platform Engineer | +| **UC-005** | Configure Deployment Profiles | **P1** | Administrator | +| **UC-010** | Provision a Virtual Machine | **P0** | Consumer | +| **UC-011** | Provision a Three-Tier Application | **P0** | Consumer | +| **UC-012** | Handle a Policy-Blocked Request | **P0** | Consumer | +| **UC-013** | View Cost Before Committing | **P1** | Consumer | +| **UC-014** | Track Request Progress | **P0** | Consumer | +| **UC-020** | Update a Running Resource | **P0** | Consumer | +| **UC-021** | Scale Resource Capacity | **P1** | Consumer | +| **UC-022** | Detect and Remediate Drift | **P0** | Operator, Consumer | +| **UC-023** | Transfer Resource Ownership | **P2** | Consumer, Administrator | +| **UC-024** | Manage Subscription Lifecycle | **P2** | Consumer, Administrator | +| **UC-030** | Rebuild from Stored State (DR) | **P1** | Operator, Administrator | +| **UC-031** | Migrate Resources to New Provider | **P2** | Operator, Administrator | +| **UC-040** | Decommission a Resource | **P0** | Consumer | +| **UC-050** | Enforce Sovereignty and Data Residency | **P0** | Policy Owner | +| **UC-051** | Rescan Existing Resources Against New Policy | **P1** | Policy Owner | +| **UC-052** | Prove Pipeline Integrity to Auditor | **P0** | Policy Owner | +| **UC-053** | Prove Override Authorization | **P1** | Policy Owner | +| **UC-060** | Cross-Instance Placement (Federation) | **P2** | Administrator | +| **UC-070** | Monitor DCM Platform Health | **P1** | Administrator | +| **UC-071** | Onboard a New Tenant | **P0** | Administrator | +| **UC-072** | Validate Provider Accreditation | **P2** | Administrator, Policy Owner | +| **UC-080** | Scoped Interaction Credentials (Zero Trust) | **P1** | Administrator | +| **UC-081** | Session Management and Auth Failover | **P0** | Consumer, Administrator | +| **UC-090** | ITSM Integration (ServiceNow) | **P2** | Administrator, Operator | +| **UC-091** | Git-Based Request Ingress | **P1** | Consumer, Platform Engineer | +| **UC-100** | Deploy a Resource Pattern from Pattern Catalog | **P0** | Consumer, Platform Engineer | + +--- + +## Day 0 — Platform Setup + +### UC-001: Deploy DCM Control Plane + +An administrator deploys the DCM control plane to an OpenShift cluster. PostgreSQL is the only required infrastructure dependency. The deployment includes 9 control plane services (API Gateway, Catalog Manager, Request Processor, Policy Engine, Placement Engine, Request Orchestrator, Audit Service, Discovery Service, Provider Manager), the PostgreSQL database with 18 tables and RLS tenant isolation, and initial configuration (system admin account, default profile, built-in auth provider). After deployment, the administrator verifies all services are healthy and the API gateway is reachable. + +**Success criteria:** All 9 services healthy. API gateway responds to requests. Admin can authenticate and access the admin API. No external dependencies required beyond PostgreSQL. + +### UC-002: Register Service Providers + +An infrastructure operator registers a service provider (e.g., OpenStack Nova compute) with DCM. The registration includes: provider type (service, information, meta, auth, peer_dcm, or process), capability declaration (resource types supported, lifecycle operations), health endpoint URL, sovereignty zone declarations (which jurisdictions the provider operates in), accreditation declarations (compliance certifications), and callback authentication configuration. DCM validates the registration, probes the health endpoint, and activates the provider. The provider is now available for placement consideration. + +**Success criteria:** Provider registered and active. Health check passes. Resource types appear in the registry. Provider is eligible for placement queries. + +### UC-003: Populate Service Catalog + +A platform engineer defines resource types and catalog items. A resource type specification (e.g., `Compute.VirtualMachine v1.0.0`) declares the vendor-neutral field schema (cpu_count, memory_gb, os_family), constraints (min/max values, allowed enums), type-level dependencies (every VM requires a Network.IPAddress), and lifecycle rules. A provider catalog item (e.g., "EU-WEST OpenStack VM — Standard") ties a resource type spec to a specific provider with pricing, SLAs, and availability. The platform engineer also creates data layers: datacenter layers (location, network ranges), environment layers (production vs dev defaults), tenant layers (team-specific overrides), and compliance layers (EU data residency requirements). + +**Success criteria:** Consumers can browse the catalog filtered by RBAC. Resource type schemas are queryable. Dependency graphs are visible. Data layers resolve correctly during assembly. + +### UC-004: Configure Organizational Policies + +A policy and compliance owner authors the baseline policy set: a sovereignty GateKeeper policy that blocks resources from deploying outside designated zones (hard enforcement); a sizing validation policy that enforces CPU and memory limits per tenant tier; a naming transformation policy that auto-generates standardized hostnames; a monitoring transformation policy that injects the organization's monitoring agent into every production resource; and a cost GateKeeper policy that blocks requests exceeding budget thresholds. Each policy is first deployed in **shadow mode** — it evaluates against real traffic and logs results without blocking requests. After validation, the policy is promoted to active. Policies use the Gatekeeper ConstraintTemplate pattern: reusable Rego logic with parameterized instances. + +**Success criteria:** Policies evaluate correctly in shadow mode. No false positives on legitimate requests. Activation enforces the policy on all matching requests. Audit records produced for every evaluation. + +### UC-005: Configure Deployment Profiles + +An administrator configures deployment profiles that govern operational behavior per environment: + +| Profile | Audit Granularity | Override Timeout | Policy Minimums | Use | +|---------|-------------------|-----------------|----------------|-----| +| minimal | stage | 24h | none | Homelab, evaluation | +| dev | stage | 4h | naming, tagging | Development | +| standard | mutation | 8h | all core policies | Production | +| fsi | field | 48h | sovereignty + all core | Financial services | +| sovereign | field (synchronous) | 72h | all policies on all ops | Government, classified | + +**Success criteria:** Profile assignment per tenant governs audit depth, override windows, and policy enforcement minimums. Profile changes take effect on next request. + +--- + +## Day 1 — Consumer Operations + +### UC-010: Provision a Virtual Machine + +A consumer developer browses the service catalog, selects "Virtual Machine — Standard," and submits a request with 6 fields: cpu_count, memory_gb, storage_gb, os_family, environment, and name. The consumer does not specify a provider, datacenter, or network configuration. + +**DCM processes the request through the full pipeline:** +1. **Intent captured** — Consumer's raw declaration stored with entity_uuid assigned +2. **Layer assembly** — 5 data layers merge organizational context (datacenter, environment, compliance, tenant, provider defaults) into the consumer's 6 fields, producing 10+ fields with full provenance +3. **Dependency resolution** — Resource type spec declares VM requires Network.IPAddress; DCM creates an IP sub-request automatically +4. **IP policy evaluation** — Sovereignty, subnet isolation, and pool selection policies evaluate against the IP sub-request; IPAM provider selected and IP allocated +5. **VM policy evaluation** — GateKeeper (sizing, sovereignty, approved OS images), Validation (field constraints), and Transformation (monitoring injection) policies evaluate +6. **Placement** — Sovereignty pre-filter eliminates non-compliant providers; remaining providers scored by capacity and confidence; best provider selected +7. **Dispatch** — Request Orchestrator sends the assembled payload (including the dependency-injected IP address) to the selected provider; the provider naturalizes DCM's unified payload into its native API +8. **Realization** — Provider provisions the VM, denaturalizes the result back to DCM's format, and callbacks with realized state +9. **Audit** — 17 Merkle tree leaves recorded across the VM entity and IP dependency + +**Success criteria:** Consumer receives a running VM with the pre-allocated IP address. Full provenance chain traceable from every field value back to its origin. Audit trail is tamper-evident and verifiable. Consumer tracked progress through pipeline stages via status API. + +### UC-011: Provision a Three-Tier Application + +A consumer requests a "Web Application — Standard" catalog item. This is a compound service backed by a compound resource type specification that decomposes into four constituent resources: network port, database VM, application server VM, and load balancer. + +The compound resource type spec declares the dependency graph and binding fields: +- Network port has no dependencies — provisioned first +- Database depends on network port — IP address injected from port +- Application server depends on database — connection string and credentials injected +- Load balancer depends on application server — backend pool configured from app server IPs + +DCM processes each constituent through the full pipeline (policy evaluation, placement, dispatch) independently, respecting dependency order. Realized outputs from each constituent flow into dependent constituents via binding fields. If any required constituent fails, a compensation policy triggers reverse-order teardown of already-realized constituents. + +**Success criteria:** Single catalog request produces 4 running, interconnected resources. Runtime values (IPs, connection strings) flow correctly between constituents. Each constituent is independently managed in DCM (its own entity_uuid, audit trail, drift detection). Decommission reverses dependency order. + +### UC-012: Handle a Policy-Blocked Request + +A consumer requests a VM with `environment: production` in a zone that violates their tenant's EU data residency policy. The request enters `POLICY_BLOCKED` state. + +The consumer receives a structured response containing: the blocking policy name, type, and enforcement level; the specific field that violated (sovereignty_zone); compliant value suggestions (e.g., "zone must be one of: eu-west-1, eu-west-2"); and four resolution paths. + +| Resolution | What happens | +|-----------|-------------| +| **Modify request** | Consumer changes the zone to eu-west-1 and resubmits. Request proceeds. | +| **Request override** | Consumer provides justification. For hard policies, dual-approval required (two approvers, different roles). | +| **Cancel** | Consumer abandons the request. Audit trail preserved. | +| **Escalate** | Request routed to the role responsible for the blocking policy domain (e.g., sovereignty admin for data residency violations, security admin for compliance blocks, cost admin for budget overruns). The responsible role reviews and may register an Exception Grant for future similar requests. Routing is configurable per policy domain and profile. | + +**Success criteria:** Consumer receives actionable guidance, not just "denied." Override requests are auditable. Block timeout auto-cancels abandoned requests. Override frequency is tracked for policy review. + +### UC-013: View Cost Before Committing + +A consumer sees an estimated cost for their resource request before submitting. Cost data comes from provider catalog item metadata (declared at registration). The placement engine considers cost alongside sovereignty, tier, and capacity constraints — not as the sole factor. + +**Success criteria:** Cost estimate displayed before submission. Cost attribution tracks to tenant and business unit. + +### UC-014: Track Request Progress + +After submitting a request, the consumer monitors progress through pipeline stages: SUBMITTED → ASSEMBLING → POLICY_EVALUATION → PLACEMENT → DISPATCHED → REALIZING → OPERATIONAL. For compound services, each constituent's status is tracked independently. + +**Success criteria:** Real-time status updates via Server-Sent Events (SSE) or polling. Constituent-level tracking for compound service requests. Failed stages show clear error with remediation guidance. + +--- + +## Day 2 — Ongoing Management + +### UC-020: Update a Running Resource + +A consumer modifies a running VM's memory from 8 GB to 16 GB. DCM creates a new request with operation_type `update`. Only lifecycle-relevant policies fire: the sizing validation policy re-evaluates (lifecycle_scope includes `update`, changed_field_filter includes `memory_gb`). The sovereignty policy does not re-evaluate (no zone change). Placement does not re-run (no provider change). The provider receives a delta payload and applies the change. + +**Success criteria:** Only relevant policies fire. Unchanged fields are not re-evaluated. Audit records the specific mutation. Realized state updated to reflect new memory. + +### UC-021: Scale Resource Capacity + +A consumer scales application server replicas from 2 to 4. If the existing provider/zone has capacity, the scale occurs in place. If capacity is insufficient, placement re-evaluates to find a zone with capacity — subject to the same sovereignty and policy constraints as initial provisioning. + +**Success criteria:** Scale-in-place when capacity exists. Automatic placement re-evaluation when it doesn't. Sovereignty constraints honored throughout. + +### UC-022: Detect and Remediate Drift + +The Discovery service polls providers on a configurable interval (default: 5 minutes). It compares discovered state to realized state. If a VM was manually modified outside DCM (e.g., memory changed from 8 GB to 16 GB via the hypervisor console), drift is detected. The configured drift policy determines the response: notify (alert the consumer and operator), auto-remediate (revert to realized state), or log (record the drift for manual review). + +**Success criteria:** Drift detected within one polling interval. Notification sent to appropriate parties. Auto-remediation restores realized state when configured. Both VM entity and its IP dependency are independently discoverable. + +### UC-030: Rebuild from Stored State (Disaster Recovery) + +A datacenter failure renders an entire availability zone unavailable. The infrastructure operator initiates rehydration for all affected resources. DCM reads the original **Intent State** for each affected entity — the consumer's raw declaration, preserved immutably since submission. + +Each entity re-enters the full pipeline as a new request with operation_type `rehydration`: + +1. **Layer assembly re-runs** — layers may have changed since original provisioning (new compliance requirements, updated monitoring agents). The resource gets current organizational context, not stale data. +2. **All policies re-evaluate** — current sovereignty policies, sizing limits, and security requirements apply. A resource that was compliant when originally provisioned may now violate a newer policy. If so, it enters POLICY_BLOCKED and the operator must resolve before rehydration proceeds. +3. **Placement re-evaluates** — the original provider/zone is unavailable. The placement engine scores surviving providers, subject to the same sovereignty pre-filter. A resource originally in EU-WEST-Prod-1 may rehydrate to EU-WEST-Prod-2. +4. **Dependencies rehydrate in order** — for compound services (three-tier apps), DCM reads the dependency graph and rehydrates constituents in dependency order: database first, then backend (with new DB IP injected), then frontend (with new backend IP injected). Binding fields resolve against newly realized values, not cached originals. +5. **Entity UUID is preserved** — the resource keeps its original entity_uuid across rehydration. Audit trail links the original lifecycle to the rehydrated one. + +**What does NOT happen:** The realized state from the lost zone is not replayed. The intent is re-processed from scratch with current layers, current policies, and current provider availability. This is a design choice — rehydration produces resources that comply with today's rules, not yesterday's. + +**Success criteria:** All affected resources rebuilt on surviving infrastructure. Current policies enforced (not original-time policies). Dependencies resolve correctly with new runtime values. Entity UUIDs preserved. Complete audit trail links original and rehydrated lifecycles. Sovereignty constraints honored — resources cannot rehydrate into non-compliant zones. + +### UC-040: Decommission a Resource + +A consumer or TTL trigger initiates decommission. DCM checks for dependencies: if other resources depend on this one (e.g., a VM using an IP address), decommission is blocked with guidance. If no blockers, the request enters the pipeline with operation_type `decommission`. The provider tears down the resource. Credentials are revoked. The IP address is released back to its pool. Audit trail is preserved permanently. + +**Success criteria:** Dependency checks prevent premature teardown. Provider confirms teardown. Dependent resources (IPs, credentials) are cleaned up. Audit trail survives decommission. + +--- + +## Governance + +### UC-050: Enforce Sovereignty and Data Residency + +All resources handling restricted, PHI, or PCI data are placed exclusively in designated sovereignty zones. The sovereignty GateKeeper policy fires on **every lifecycle operation** (initial provisioning, update, scale, rehydration, ownership transfer) — not just initial provisioning. A resource in EU-WEST stays in EU-WEST for its entire lifecycle. + +Override requires dual-approval: two approvers from different roles, with written justification and compensating controls. Every override produces a Merkle audit leaf at field granularity. + +**Success criteria:** No resource is ever realized in a non-compliant zone without an audited, dual-approved override. Sovereignty is enforced end-to-end, not just at creation. + +### UC-052: Prove Pipeline Integrity to Auditor + +An external auditor requests proof that a specific provisioning request was processed correctly. DCM returns: +- **Inclusion proof:** Mathematical proof that the specific audit record exists in the Merkle tree +- **Consistency proof:** Mathematical proof that the tree has only grown since the last signed tree head (no deletions) +- **Request chain:** The complete hash chain from intent through realization with Ed25519 signatures from each service +- **Signed tree head:** The current root hash signed by DCM's identity + +The auditor can independently verify these proofs without trusting DCM. + +**Success criteria:** Auditor verifies integrity using only DCM's public key and the proof data. No trust relationship with DCM required. Verification completes in seconds regardless of tree size. + +--- + +## Platform Operations + +### UC-071: Onboard a New Tenant + +An administrator creates a new tenant with profile assignment (e.g., "standard" for a production team, "dev" for a sandbox). RLS isolation is enforced immediately — the new tenant cannot see other tenants' data. RBAC is configured: which roles exist, which actors belong to which roles. The catalog is filtered: the tenant sees only catalog items they're authorized to request. The administrator submits a test request to verify full pipeline isolation. + +**Success criteria:** Tenant data is isolated by RLS from first query. RBAC filters the catalog correctly. Test request flows through the complete pipeline with no cross-tenant data leakage. + +### UC-081: Session Management and Auth Failover + +An actor authenticates via the configured auth provider (built-in, Keycloak, LDAP, OIDC). Session tokens are issued with configurable TTL. Concurrent session limits are enforced. If the primary auth provider fails, existing sessions remain valid (cached) and new authentication routes to the failover chain. Session revocation propagates immediately. + +**Success criteria:** Authentication works through all configured providers. Failover is transparent. Session revocation takes effect immediately. Concurrent session limits enforced. + +### UC-091: Git-Based Request Ingress + +A developer submits an infrastructure request via Git Pull Request. The PR identity is resolved to the same DCM actor as API/UI login. The request enters the standard pipeline. Policy dry-run feedback is posted as PR comments. Approval via PR review maps to DCM approval. On merge, the request is dispatched. + +**Success criteria:** Git PR identity resolves to DCM actor. Policy feedback appears as PR comments. Merge triggers dispatch. Full audit trail links PR to DCM request. + +--- + +## Federation + +### UC-060: Cross-Instance Placement (Federation) + +An organization runs two DCM instances: DCM-EMEA (EU datacenters) and DCM-APAC (Asia-Pacific datacenters). A consumer on DCM-EMEA requests a resource that, due to latency requirements for an APAC-facing application, should be placed in an APAC zone. The consumer does not need to know which DCM instance will fulfill the request. + +**How federation works:** + +1. **Peer DCM registration** — DCM-APAC is registered as a `peer_dcm` provider on DCM-EMEA. The registration includes sovereignty declarations, federation eligibility scope (which resource types and operations are permitted), and an mTLS certificate for the federation tunnel. + +2. **Request enters normal pipeline on DCM-EMEA** — Layer assembly, policy evaluation, and placement all run locally. The consumer's sovereignty policy allows APAC placement for this resource type. + +3. **Placement considers remote providers** — The placement engine treats DCM-APAC's providers as candidates alongside local providers. Cross-DCM confidence scoring applies: `cross_dcm_confidence = source_confidence × (tunnel_trust_score / 100)`. Remote providers are scored lower by the trust factor, but may win if local providers lack capacity or sovereignty eligibility. + +4. **DCM-EMEA dispatches to DCM-APAC via federation tunnel** — The request payload is sent over the mTLS tunnel. DCM-APAC receives it, runs its own local policy evaluation (local policies govern — remote policies cannot override), and dispatches to its selected provider. + +5. **Dual audit** — Audit records are written in both DCM instances with a shared `correlation_id`. DCM-EMEA records the outbound federation dispatch. DCM-APAC records the local pipeline execution and realization. Either instance can produce a complete audit trail for its portion. + +6. **Realized state flows back** — DCM-APAC's provider callback flows back through the federation tunnel to DCM-EMEA. The consumer on DCM-EMEA sees the resource as OPERATIONAL with the APAC provider's realized fields. + +**Federation constraints:** +- Sovereignty is verified before tunnel establishment — a classified-zone provider cannot participate in federation +- Storage providers default to `federation_eligibility: none` — data sovereignty prohibits storage federation unless explicitly authorized +- Remote DCMs cannot decommission local resources through a tunnel — decommission is always local +- Certificate rotation uses a 30-day overlap period so peers can update trust stores without coordinated downtime + +**Success criteria:** Consumer requests a resource on DCM-EMEA and receives it from DCM-APAC without knowing the routing. Audit trail in both instances with shared correlation_id. Sovereignty enforced at both ends. Federation tunnel is mTLS-only. + +--- + +## Integration + +### UC-090: ITSM Integration (ServiceNow Change Management) + +An organization requires that all infrastructure provisioning creates change records in ServiceNow, updates are tracked through CMDB configuration items, and decommission retires the CI. DCM integrates with ServiceNow via a **process_provider** — a bidirectional integration that enriches DCM entities with ITSM metadata without making ServiceNow a required dependency. + +**Design principle:** DCM replaces the infrastructure ticket as the provisioning mechanism. ITSM integration is additive — it enriches, it does not gate (unless explicitly configured to do so). + +**Outbound flow (DCM → ServiceNow):** + +1. **ITSM Policy evaluates** — An ITSM Action policy fires on DCM lifecycle events. It is a side-effect policy: it triggers ITSM actions but does not block the pipeline by default. + +2. **On `request.dispatched`** — The ITSM integration creates a ServiceNow change request (CHG record) containing the request details, requesting actor, tenant, resource type, and placement decision. The CHG number is stored on the DCM entity as `business_data.itsm_references[].external_id`. + +3. **On `entity.realized`** — The ITSM integration updates the change request to "Implemented" and creates or updates a CMDB Configuration Item (CI) with the realized resource's details (IP address, provider, datacenter, ownership). + +4. **On `entity.updated`** — The ITSM integration updates the CMDB CI and creates a new change task linked to the parent CHG. + +5. **On `entity.decommissioned`** — The ITSM integration closes the change request, retires the CMDB CI, and updates the CI's lifecycle status. + +**Inbound flow (ServiceNow → DCM):** + +6. **Change Advisory Board (CAB) approval** — For organizations that require CAB approval before provisioning, the ITSM Policy can be configured as `block_until_approved`. The DCM pipeline pauses after the change request is created in ServiceNow. When the CAB approves, ServiceNow calls DCM's approval API (`POST /api/v1/admin/approvals/{uuid}:vote`) with the decision. The pipeline resumes. + +7. **ITSM-initiated requests** — A ServiceNow workflow can create a DCM request via the Admin API, enabling "request infrastructure from ServiceNow" patterns for organizations transitioning from ticket-based provisioning. + +**Safety guardrail:** `block_until_created` (wait for CHG creation before proceeding) requires a `block_timeout` — the pipeline never permanently stalls waiting for an ITSM system. If the timeout fires, the pipeline proceeds with a warning and the ITSM record is created asynchronously when the system recovers. + +**Supported ITSM systems:** ServiceNow, Jira Service Management, BMC Remedy/Helix, Freshservice, PagerDuty, Opsgenie, ManageEngine, Cherwell, TOPdesk, and a generic REST adapter for others. + +**Success criteria:** Every provisioning request has a corresponding ServiceNow change record. CMDB CIs are created on realization and retired on decommission. CAB approval gates work when configured. ITSM system unavailability does not block DCM operations (non-blocking default with configurable blocking mode). Bidirectional links between DCM entity_uuid and ServiceNow CHG/CI numbers. + +--- + +## Pattern Catalog + +### UC-100: Deploy a Resource Pattern from the Pattern Catalog + +A consumer browses the Pattern Catalog section of the service catalog and selects "Standard Web Application." The catalog shows the pattern's components (network segment, database, app servers, load balancer, DNS), the parameters the consumer needs to provide, the dependency graph, and estimated cost. + +The consumer fills in 6 parameters (app_name, environment, db_engine, db_storage_gb, app_replicas, expose_public) and submits. DCM decomposes the pattern into 6 constituent resources, resolves the dependency graph, and processes each constituent through the full pipeline — layer assembly, policy evaluation, placement, dispatch — independently. Each constituent may be placed with a different provider: the database with a managed database provider, the VMs with a compute provider, the load balancer with a network provider. + +**Dependency resolution and binding fields in action:** + +1. Network Segment provisioned first (no dependencies) → produces subnet_cidr, security_group_id +2. Database provisioned next → receives network values via binding fields → produces ip_address, port, credentials_ref +3. App Servers provisioned next → receive DB connection string and network config via binding fields → produce ip_addresses, port +4. Load Balancer provisioned next → receives app server IPs as backend pool via binding fields → produces vip_address +5. DNS records provisioned last → receive LB address via binding fields → public DNS conditional on consumer parameter + +The consumer monitors aggregate progress ("4 of 6 constituents realized") and can drill into individual constituent status. On completion, the consumer has a fully wired application environment. + +**Policy interaction:** No new policy types are needed. Each constituent is a standard resource type, and existing policies match naturally — sovereignty policies check every constituent, sizing policies check VMs, storage policies check the database, naming policies check DNS records. A policy block on any constituent blocks the entire pattern until the consumer resolves it. + +**Failure handling:** If any required constituent fails, the pattern's lifecycle policy determines the response: rollback all realized constituents (default), continue in degraded mode, or notify and hold for manual intervention. + +**Pattern authoring:** Platform engineers create patterns as compound Resource Type Specifications in the Resource Type Registry. Patterns define constituents, dependencies, binding fields, exposed parameters, and lifecycle policies. Consumers see them as catalog items indistinguishable from atomic offerings — the decomposition is invisible. + +**Success criteria:** Single request produces a complete, wired application environment. Runtime values flow correctly between constituents. Each constituent is independently managed (own entity_uuid, audit trail, drift detection, lifecycle). Decommission reverses dependency order. No new control plane services, policy types, or API endpoints required — patterns use existing DCM machinery. + +### Pattern Catalog — Architectural Overlay + +A deployment pattern is a reusable, provider-agnostic blueprint that defines a collection of resources, their dependencies, their runtime wiring, and their operational policies — together delivering a service that no single provider offers. + +**Example patterns:** + +| Pattern | Constituents | What it delivers | +|---------|-------------|-----------------| +| Standard Web Application | 2 app VMs, 1 DB, 1 LB, 1 network, 3 DNS | Production web app with HA, monitoring, DNS | +| Secure Data Pipeline | 1 Kafka cluster, 2 workers, 1 object store, 1 network policy, 1 key | Encrypted ingest pipeline with data residency | +| Developer Sandbox | 1 VM, 1 namespace, 1 ephemeral DB, 1 port forward | Disposable dev environment with TTL auto-cleanup | +| Regulated Database Service | 1 PostgreSQL (HA), 1 backup, 1 key, 1 audit sink, 2 DNS | Database meeting FSI data handling requirements | +| Edge Compute Node | 1 bare metal, 1 MicroShift, 1 VPN, 1 monitor agent, 1 cert | Self-contained edge node with central management | + +**The key property:** No single provider owns the pattern. The database might come from one provider, the VMs from another, the load balancer from a third. The pattern defines *what* is needed and *how the pieces connect* — DCM figures out *who* provides each piece. + +### How Patterns Layer on DCM + +``` +┌──────────────────────────────────────────────────────┐ +│ PATTERN CATALOG │ +│ Curated library of reusable deployment blueprints │ +│ Authored by: Platform Engineers │ +│ Consumed by: Consumer Developers │ +│ Stored in: Resource Type Registry (compound types) │ +├──────────────────────────────────────────────────────┤ +│ SERVICE CATALOG │ +│ Provider-specific offerings + pattern offerings │ +│ Populated by: Providers (atomic) + Patterns (compound)│ +├──────────────────────────────────────────────────────┤ +│ DCM CONTROL PLANE │ +│ Decompose → Policy → Placement → Dispatch → Audit │ +│ Each constituent → full pipeline independently │ +├──────────────────────────────────────────────────────┤ +│ SERVICE PROVIDERS │ +│ VM · Network · Database · DNS · Storage · Container │ +│ Fulfill individual constituents of a pattern │ +└──────────────────────────────────────────────────────┘ +``` + +**The Pattern Catalog is not a new architectural component.** It is a curated view of the Resource Type Registry filtered to compound resource types. DCM already has all the machinery — the compound service definition model, dependency graphs, binding fields, and constituent dispatch. The Pattern Catalog adds the curation and consumer experience layer. + +### How a Pattern Maps to DCM Constructs + +| Pattern concept | DCM construct | Where it lives | +|----------------|--------------|----------------| +| The pattern itself | Compound Resource Type Specification | Resource Type Registry | +| The constituents | Resource Type references with dependency declarations | `constituents[]` in the compound spec | +| How pieces connect | Binding fields — runtime values from one constituent injected into another | `binding_fields[]` on dependent constituents | +| What the consumer fills in | Parameterized fields exposed at the pattern level | `fields_from_parent[]` mapping pattern params to constituent fields | +| Who provides each piece | `provided_by: external` (DCM places) or `provided_by: self` (compound service definition handles) | Per-constituent declaration | +| What happens on failure | Lifecycle policy on the compound spec | `on_constituent_failure: rollback_all` or `continue_degraded` or `notify` | +| Operational policies | Standard DCM policies scoped to constituent resource types | Policy match on resource_type per constituent | + +### Pattern Definition Example — Standard Web Application + +```yaml +resource_type: ApplicationStack.WebApp +version: "1.0.0" +entity_type: composite_resource + +# Consumer-facing parameters +parameters: + app_name: { type: string, required: true } + environment: { type: string, required: true, constraint: { layer_reference: "environment" } } + db_engine: { type: string, default: postgresql, constraint: { enum: [postgresql, mysql] } } + db_storage_gb: { type: integer, default: 50, constraint: { min: 10, max: 1000 } } + app_replicas: { type: integer, default: 2, constraint: { min: 1, max: 10 } } + expose_public: { type: boolean, default: false } + +constituents: + - name: network_segment + resource_type: Network.Segment + provided_by: external + depends_on: [] + required_for_delivery: required + fields_from_parent: + - { source: "environment", target: "environment" } + - { source: "app_name", target: "segment_name_prefix" } + + - name: database + resource_type: Database.Managed + provided_by: external + depends_on: [network_segment] + required_for_delivery: required + binding_fields: + - { source: "network_segment.subnet_cidr", target: "database.network_cidr" } + - { source: "network_segment.security_group_id", target: "database.security_group_id" } + fields_from_parent: + - { source: "db_engine", target: "engine" } + - { source: "db_storage_gb", target: "storage_gb" } + + - name: app_server + resource_type: Compute.VirtualMachine + provided_by: external + depends_on: [database, network_segment] + required_for_delivery: required + binding_fields: + - { source: "database.ip_address", target: "app_server.config.db_host" } + - { source: "database.port", target: "app_server.config.db_port" } + - { source: "database.credentials_ref", target: "app_server.config.db_credentials_ref" } + - { source: "network_segment.subnet_cidr", target: "app_server.network_cidr" } + fields_from_parent: + - { source: "app_name", target: "hostname_prefix" } + - { source: "app_replicas", target: "replicas" } + + - name: load_balancer + resource_type: Network.LoadBalancer + provided_by: external + depends_on: [app_server] + required_for_delivery: required + binding_fields: + - { source: "app_server.ip_addresses", target: "load_balancer.backend_pool" } + - { source: "app_server.port", target: "load_balancer.backend_port" } + fields_from_parent: + - { source: "expose_public", target: "public_listener" } + + - name: dns_internal + resource_type: DNS.Record + provided_by: external + depends_on: [load_balancer] + required_for_delivery: partial + binding_fields: + - { source: "load_balancer.vip_address", target: "dns_internal.target_address" } + fields_from_parent: + - { source: "app_name", target: "hostname" } + + - name: dns_public + resource_type: DNS.Record + provided_by: external + depends_on: [load_balancer] + required_for_delivery: optional + condition: "parent.expose_public == true" + binding_fields: + - { source: "load_balancer.public_vip_address", target: "dns_public.target_address" } + fields_from_parent: + - { source: "app_name", target: "hostname" } + +lifecycle_policy: + on_constituent_failure: rollback_all + decommission_order: reverse_dependency +``` + +### What the Consumer Submits + +```json +POST /api/v1/requests +{ + "catalog_item_uuid": "webapp-standard-uuid", + "fields": { + "app_name": "pet-clinic", + "environment": "production", + "db_engine": "postgresql", + "db_storage_gb": 100, + "app_replicas": 3, + "expose_public": true + } +} +``` + +Six fields. The consumer has no idea this produces 6 resources across 4 different providers. + +### What DCM Executes + +``` +Round 1: Network Segment (no deps) + → Placed with EU-WEST network provider + → Realized: subnet_cidr=10.5.0.0/24, security_group_id=sg-abc123 + +Round 2: Database (binding: network values injected) + → config.network_cidr = 10.5.0.0/24, security_group_id = sg-abc123 + → Placed with EU-WEST database provider + → Realized: ip_address=10.5.0.50, port=5432, credentials_ref=vault:secret/pet-clinic-db + +Round 3: App Server (binding: DB + network values injected) + → config.db_host=10.5.0.50, db_port=5432, db_credentials_ref=vault:secret/pet-clinic-db + → Placed with EU-WEST compute provider, 3 replicas + → Realized: ip_addresses=[10.5.0.10, 10.5.0.11, 10.5.0.12], port=8080 + +Round 4: Load Balancer (binding: app server IPs injected) + → config.backend_pool=[10.5.0.10, 10.5.0.11, 10.5.0.12], backend_port=8080 + → Placed with EU-WEST network provider + → Realized: vip_address=10.5.0.100, public_vip_address=203.0.113.50 + +Round 5: DNS records (binding: LB addresses injected) + → Internal: pet-clinic.internal → 10.5.0.100 + → Public: pet-clinic.example.com → 203.0.113.50 (expose_public=true) + +All 6 constituents realized → composite status: OPERATIONAL +``` + +### Policy Interaction with Patterns + +No new policy types are needed. Each constituent is a standard resource type, and existing policies match naturally: + +| Policy | Fires on | What it does | +|--------|---------|-------------| +| Sovereignty GateKeeper | All 6 constituents | Ensures everything lands in EU-WEST | +| VM sizing limits | App Server | Validates replicas and VM size within tenant tier | +| DB storage limits | Database | Validates db_storage_gb within allowed range | +| Network naming | Network Segment, DNS | Enforces naming conventions | +| Monitoring injection | App Server, Database | Injects monitoring agent config | +| Backup policy injection | Database | Injects backup schedule based on environment | + +### Who Authors Patterns vs Who Consumes Them + +| Role | Responsibility | +|------|---------------| +| **Platform Engineer** | Authors pattern definitions as compound Resource Type Specs. Defines constituents, dependencies, binding fields, exposed parameters, lifecycle policies. Registers in Resource Type Registry. Creates service catalog items. | +| **Policy/Compliance Owner** | Writes policies that apply to pattern constituents. Does not need pattern-specific awareness — policies match on resource types, which patterns decompose into. May write pattern-level policies (e.g., "all ApplicationStack types require monitoring on every constituent"). | +| **Consumer Developer** | Browses catalog, selects a pattern, fills in parameters, submits. Sees aggregate status. Can drill into constituent detail. Does not need to understand the decomposition. | +| **Infrastructure Operator** | Provides the atomic services that patterns compose. Registers providers for Compute, Network, Database, DNS — not for the pattern itself. | + +### Pattern Interaction with Other DCM Features + +| Feature | How it works with patterns | +|---------|--------------------------| +| **Drift detection** | Each constituent independently discoverable. Drift attributed per-constituent. | +| **Decommission** | Reverse-dependency-order teardown. Individual constituents can also be removed independently. | +| **Rehydration** | All constituents rebuilt in dependency order with current policies. Binding fields resolve against newly realized values. | +| **Sovereignty** | Every constituent independently sovereignty-checked. A pattern cannot span zones unless every constituent passes. | +| **Cost estimation** | Pattern cost = sum of constituent costs from provider catalog items. | +| **Audit** | Each constituent has its own Merkle audit trail. Composite entity links all constituent entity_uuids. | +| **Override** | Block on any constituent blocks the pattern. Consumer resolves per-constituent. Escalation routes to the responsible policy domain owner. | +| **Federation** | Constituents can be placed across DCM instances. Database local, app servers federated — if sovereignty permits. | + +### Pattern Lifecycle + +Patterns follow the standard DCM artifact lifecycle: `developing → proposed → active → deprecated → retired`. Adding an optional constituent (e.g., a cache layer) is a minor version bump — existing deployments unaffected, new requests get the new constituent. Removing a required constituent is a major version bump. + +--- + +# Architecture Principles + +- **Management plane, not provisioning tool.** DCM orchestrates lifecycle and enforces governance. Provisioning is delegated to service providers that implement the provider contract. +- **Three abstractions.** Everything in DCM is Data, Provider, or Policy. No exceptions. If a new concept doesn't map to one of these three, the abstraction model needs revision. +- **Provider-agnostic.** Any infrastructure platform is consumable through the same interface via naturalization/denaturalization. No lock-in to any platform. +- **Policy-mandatory.** Every request is policy-evaluated. This is not optional. Governance is the value proposition, not a feature toggle. +- **Tamper-evident audit.** Every mutation is recorded in a Merkle tree with Ed25519 signatures. Auditors can verify integrity without trusting DCM. +- **Sovereignty first-class.** Data residency is enforced at request time on every lifecycle operation, not discovered after deployment. +- **Minimal infrastructure.** PostgreSQL is the only required dependency. Everything else (Kafka, Vault, Keycloak) is optional and follows the Internal/External delegation pattern. +- **Declarative and idempotent.** Consumers declare desired state. DCM converges toward it. Resubmitting the same request produces the same result. +- **API-first.** All capabilities are accessible via API. The Web UI (RHDH) is a consumer of the same API. AEP conventions (snake_case, JSON, flat REST). + +--- + +# Success Outcomes + +Each capability area maps to a measurable outcome: + +| **Outcome** | **Enabling Use Cases** | +|-----------|----------------------| +| Developers provision infrastructure without knowing which platform fulfills it | UC-010, UC-011, UC-014 | +| Compound applications deploy as a single catalog request with dependency resolution | UC-011, UC-100 | +| Policy violations are caught at request time with actionable guidance | UC-004, UC-012 | +| Sovereignty and data residency enforced on every lifecycle operation | UC-050 | +| Audit integrity is mathematically provable to external auditors | UC-052, UC-053 | +| Drift between intended and actual state is detected and remediated | UC-022 | +| New infrastructure platforms are addable without changing DCM core | UC-002 | +| Bootstrap requires only PostgreSQL — no middleware stack | UC-001 | +| Override governance provides flexibility without undermining compliance | UC-012, UC-053 | +| Full lifecycle management from request through decommission | UC-010, UC-020, UC-040 | +| Disaster recovery rebuilds from stored intent with current policy evaluation | UC-030 | +| Multi-region placement without consumer awareness of DCM instance topology | UC-060 | +| ITSM records created automatically without gating provisioning by default | UC-090 | +| Self-service reduces platform engineering ticket volume | UC-010, UC-011, UC-014 | + +--- + +# Priority Summary + +| **Priority** | **Count** | **Use Cases** | +|------------|---------|-------------| +| **P0 (Must Have)** | 16 | UC-001 through UC-004, UC-010 through UC-012, UC-014, UC-020, UC-022, UC-040, UC-050, UC-052, UC-071, UC-081, UC-100 | +| **P1 (Should Have)** | 9 | UC-005, UC-013, UC-021, UC-030, UC-051, UC-053, UC-070, UC-080, UC-091 | +| **P2 (Future)** | 6 | UC-023, UC-024, UC-031, UC-060, UC-072, UC-090 | + +--- + +# Open Design Questions + +| **ID** | **Question** | **Status** | +|--------|-------------|-----------| +| **DQ-1** | Application definition language — How should consumers define multi-resource applications? YAML manifests, API composition, external DSL, or catalog-only? | Open — See [ADR-016](architecture/adr/016-application-definition-language.md) | +| **DQ-2** | RHDH integration depth — Is RHDH the sole frontend, or should DCM expose its own lightweight UI for environments without RHDH? | Open | +| **DQ-3** | Spectral AEP linter — Should OpenAPI specs be linted in CI? Requires 3.1 compatibility verification. | Open — See [DISCUSSION-TOPICS item 7](architecture/DISCUSSION-TOPICS.md) | +| **DQ-4** | Kessel integration — What, if any, integration with Project Kessel for authorization? | Discussion only — See doc 44 | + +--- + +# Appendix: Key Terms + +- **Naturalization:** Translation of DCM's unified payload into a provider's native API format. +- **Denaturalization:** Translation of a provider's native response back into DCM's unified format. +- **Evaluation Context:** The complete payload, provenance chain, constraint accumulator, and governance scope passed to the policy engine for each evaluation. +- **Binding Fields:** Declarations in compound service definitions that connect realized outputs of one resource (e.g., an IP address) to inputs of a dependent resource (e.g., a VM's network config). +- **Merkle Tree:** A binary hash tree where modifying any leaf changes the root hash. Enables inclusion proofs (a record exists) and consistency proofs (the tree has only grown). RFC 9162. +- **RLS (Row-Level Security):** PostgreSQL feature that automatically scopes every query to the actor's tenant — application code cannot leak cross-tenant data. +- **Sovereignty Zone:** A geopolitical or regulatory boundary declared by providers and enforced by policy. Resources placed in a zone are governed by that zone's data residency rules. +- **Shadow Mode:** A policy lifecycle stage where the policy evaluates against real traffic and logs results without blocking requests. Used for safe validation before activation. +- **RHDH (Red Hat Developer Hub):** Backstage-based developer portal used as DCM's primary Web UI frontend. +- **AEP (API Enhancement Proposals):** Open-source API design guidelines adopted by DCM for consistent API conventions. diff --git a/content/docs/enhancements/_index.md b/content/docs/enhancements/_index.md index 3bce142..862962f 100644 --- a/content/docs/enhancements/_index.md +++ b/content/docs/enhancements/_index.md @@ -1,12 +1,16 @@ --- title: Enhancements type: docs +weight: 2 sidebar: open: true --- # DCM Enhancements -Enhancement proposals for the DCM project. Each enhancement documents new features, significant changes, or architectural decisions. +Enhancement proposals for the DCM project. Each enhancement documents new features, +significant changes, or architectural decisions. -View all enhancements in the [DCM Enhancements Repository](https://github.com/dcm-project/enhancements). \ No newline at end of file +View all enhancements in the [DCM Enhancements Repository](https://github.com/dcm-project/enhancements). + +Enhancement documents are maintained in that repository as their source of truth. They are not duplicated here to avoid content drifting out of sync. The architecture documents in the [Data Model](/docs/architecture/data-model/) section incorporate the decisions from enhancements into the current specification. Enhancements are design history; the architecture is the current state. diff --git a/content/docs/enhancements/catalog-item-schema.md b/content/docs/enhancements/catalog-item-schema.md deleted file mode 100644 index b0c19f4..0000000 --- a/content/docs/enhancements/catalog-item-schema.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Catalog Item Schema -type: docs -weight: 7 ---- - -{{< remoteMD "https://raw.githubusercontent.com/dcm-project/enhancements/main/enhancements/catalog-item-schema/catalog-item-schema.md" >}} \ No newline at end of file diff --git a/content/docs/enhancements/kubevirt-sp.md b/content/docs/enhancements/kubevirt-sp.md deleted file mode 100644 index b001416..0000000 --- a/content/docs/enhancements/kubevirt-sp.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: KubeVirt SP -type: docs -weight: 15 ---- - -{{< remoteMD "https://raw.githubusercontent.com/dcm-project/enhancements/main/enhancements/kubevirt-sp/kubevirt-sp.md" >}} \ No newline at end of file diff --git a/content/docs/enhancements/policy-engine.md b/content/docs/enhancements/policy-engine.md deleted file mode 100644 index 252f492..0000000 --- a/content/docs/enhancements/policy-engine.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Policy Engine -type: docs -weight: 12 ---- - -{{< remoteMD "https://raw.githubusercontent.com/dcm-project/enhancements/main/enhancements/policy-engine/policy-engine.md" >}} \ No newline at end of file diff --git a/content/docs/enhancements/service-provider-health-check.md b/content/docs/enhancements/service-provider-health-check.md deleted file mode 100644 index e4e6e0d..0000000 --- a/content/docs/enhancements/service-provider-health-check.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Service Provider Health Check -type: docs -weight: 9 ---- - -{{< remoteMD "https://raw.githubusercontent.com/dcm-project/enhancements/main/enhancements/service-provider-health-check/service-provider-health-check.md" >}} \ No newline at end of file diff --git a/content/docs/enhancements/service-type-definitions.md b/content/docs/enhancements/service-type-definitions.md deleted file mode 100644 index 4988dea..0000000 --- a/content/docs/enhancements/service-type-definitions.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Service Type Definitions -type: docs -weight: 5 ---- - -{{< remoteMD "https://raw.githubusercontent.com/dcm-project/enhancements/main/enhancements/service-type-definitions/service-type-definitions.md" >}} \ No newline at end of file diff --git a/content/docs/enhancements/sp-registration-flow.md b/content/docs/enhancements/sp-registration-flow.md deleted file mode 100644 index d0671d1..0000000 --- a/content/docs/enhancements/sp-registration-flow.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: SP Registration Flow -type: docs -weight: 1 ---- - -{{< remoteMD "https://raw.githubusercontent.com/dcm-project/enhancements/main/enhancements/sp-registration-flow/sp-registration-flow.md" >}} \ No newline at end of file diff --git a/content/docs/implementations/_index.md b/content/docs/implementations/_index.md new file mode 100644 index 0000000..e3a546c --- /dev/null +++ b/content/docs/implementations/_index.md @@ -0,0 +1,15 @@ +--- +title: Implementations +type: docs +weight: 3 +sidebar: + open: true +--- + +# DCM Implementations + +Reference implementations demonstrating how to deploy and extend DCM. + +The implementation source code, deployment manifests, and Ansible roles are maintained in the [dcm-examples](https://github.com/dcm-project/dcm-examples) repository. + +The documentation below describes the architecture mapping, deployment guides, and demo scripts for the reference implementation. diff --git a/content/docs/implementations/example-01-summit-demo/_index.md b/content/docs/implementations/example-01-summit-demo/_index.md new file mode 100644 index 0000000..a1bffe4 --- /dev/null +++ b/content/docs/implementations/example-01-summit-demo/_index.md @@ -0,0 +1,112 @@ +--- +title: "_Index" +type: docs +--- + +# DCM Example Implementation #1 — Summit Demo + +> **This is an example implementation** provided to demonstrate the DCM architecture +> in a working deployment. It is intended as a reference and portability exercise — +> service providers built here may be replaced by production implementations. +> All architectural decisions reference the DCM specification at +> [github.com/dcm-project](https://github.com/dcm-project). + +--- + +## Purpose + +This implementation validates the DCM control plane architecture and data model +against three Summit 2026 demonstration use cases: + +1. **Intelligent Placement** — policy-governed workload placement using OPA Rego rules +2. **Datacenter Rehydration** — full environment reconstruction from declared state +3. **Application as a Service** — Meta Provider composing VM + Network + OCP Cluster + +--- + +## Technology Stack + +| Component | Technology | Rationale | +|-----------|-----------|-----------| +| Control Plane | Go services | Lightweight, strong concurrency, cloud-native | +| API Gateway | Go (custom) | Full control over mTLS termination and rate limiting | +| Policy Engine | OPA (CNCF) | Red Hat sanctioned; used in ACM, OpenShift; Rego shown in slides | +| Git Server | GitLab CE | Decision: intent/requested state, policy GitOps store | +| Database | PostgreSQL (CrunchyData PGO) | Decision: Snapshot Store with RLS, OpenShift native | +| Event Bus | AMQ Streams (Kafka) | Red Hat product; reliable event streaming | +| Search | OpenSearch | Red Hat supported OSS | +| Auth | Keycloak (Red Hat SSO) | Red Hat product; OIDC + group membership | +| Secrets | HashiCorp Vault | Credential Provider; widely used in Red Hat ecosystem | +| Service Mesh | OpenShift Service Mesh (Istio) | mTLS between all control plane components | +| Monitoring | Prometheus + Grafana | Standard OpenShift observability stack | +| Frontend | Red Hat Developer Hub (RHDH) | Decision: consumer-facing interface | +| Container Platform | OpenShift | All components run as OpenShift workloads | + +--- + +## Service Providers (Summit Demo) + +| Provider | Wraps | Purpose | +|----------|-------|---------| +| `dcm-provider-vm` | Ansible Automation Platform | VM provisioning on target infrastructure | +| `dcm-provider-ocp-cluster` | OpenShift/CAPI | OCP cluster provisioning | +| `dcm-provider-network` | AAP/Ansible | Network port and VLAN provisioning | +| `dcm-provider-acm-shim` | Red Hat ACM API | ACM as standard DCM provider (shim) | +| `dcm-provider-webapp` | Meta Provider | Composes VM + Network + OCP Cluster | + +--- + +## Prerequisites + +- OpenShift 4.14+ cluster with cluster-admin access +- Ansible 2.14+ with `kubernetes.core` collection +- OpenShift CLI (`oc`) configured against target cluster +- The following OpenShift Operators available (installed by playbook): + - Red Hat OpenShift Service Mesh + - AMQ Streams + - Crunchy Data PGO (PostgreSQL Operator) + - Red Hat SSO (Keycloak) + +--- + +## Quick Deploy + +```bash +# 1. Configure your cluster connection +export KUBECONFIG=/path/to/kubeconfig + +# 2. Set required variables +cp ansible/inventory/hosts.yml.example ansible/inventory/hosts.yml +# Edit hosts.yml with your cluster details + +# 3. Run the full deployment +cd ansible +ansible-playbook site.yml + +# 4. Access the demo +# RHDH URL will be printed at end of playbook +``` + +Full step-by-step instructions: [docs/deployment-guide.md](docs/deployment-guide.md) + +--- + +## Architecture Mapping + +How this implementation maps to the DCM specification: +[docs/architecture-mapping.md](docs/architecture-mapping.md) + +## Demo Script + +Summit demo walkthrough scripts: +[docs/demo-script.md](docs/demo-script.md) + +## Building a New Provider + +[docs/provider-development-guide.md](docs/provider-development-guide.md) + +--- + +*This is Example Implementation #1 for the DCM project. See the +[DCM architecture documentation](https://github.com/dcm-project) for the +full specification this implementation is based on.* diff --git a/content/docs/implementations/example-01-summit-demo/architecture-mapping.md b/content/docs/implementations/example-01-summit-demo/architecture-mapping.md new file mode 100644 index 0000000..263e0e3 --- /dev/null +++ b/content/docs/implementations/example-01-summit-demo/architecture-mapping.md @@ -0,0 +1,127 @@ +--- +title: "Architecture Mapping" +type: docs +--- + +# Architecture Mapping — Example Implementation #1 + +This document maps each component of this example implementation to the +corresponding DCM specification document. Every design decision references +the spec; this is where to look when the implementation and spec diverge. + +--- + +## Component → Spec Mapping + +| Implementation Component | DCM Spec Reference | Notes | +|--------------------------|-------------------|-------| +| `dcm-api-gateway` (Go) | doc 25 §2, doc 49 §2 (rate limiting), doc 49 §7.1 (operation_uuid) | Custom Go; mTLS via Istio; token bucket rate limit | +| `dcm-request-orchestrator` (Go) | doc 25 §2, doc 33 (Event Catalog) | Pipeline event routing via PostgreSQL `LISTEN/NOTIFY` (standard) or Kafka (high-throughput enhancement) | +| `dcm-policy-engine` (Go + OPA) | doc B (Policy Contract), dcm-opa-integration-spec | OPA sidecar per pod; Rego policies in ConfigMap bundle; Internal evaluation mode | +| `dcm-placement-engine` (Go) | doc 29 (Scoring Model) | 5-signal weights; OPA placement-weights.rego | +| `dcm-request-processor` (Go) | doc 03 (Layering), doc 25 §2 | Reads layers from database; assembles payload | +| `dcm-audit` (Go) | doc 16 (Universal Audit), doc 49 §3 | SHA-256 hash chain; writes to PostgreSQL `audit_records` | +| `dcm-catalog` (Go) | doc 06, doc 10 | Reads from PostgreSQL `catalog_items`; RBAC via Keycloak groups | +| `dcm-discovery` (Go) | doc 13 (Ingestion), doc 06 §6 | CronJob every 15 min; polling providers via OIS discovery endpoint | +| PostgreSQL (CrunchyData PGO) | doc 51 §2 (Four Data Domains) | All four data domains (Intent, Requested, Realized, Discovered) + audit + pipeline events. RLS enforces tenant isolation. | +| Keycloak (Red Hat SSO) | doc 19 (Auth Providers) | OIDC; group membership maps to DCM roles; registered as `auth_provider` | +| HashiCorp Vault | doc 51 §4.1 (Prescribed Infrastructure) | Vault-compatible secrets API; mTLS certs, provider credentials, encryption keys | +| OpenShift Service Mesh | doc 36 (Internal Component Auth) | STRICT mTLS mode; Boundary 4 threat model mitigation | +| OPA (sidecar) | dcm-opa-integration-spec, doc B | Internal evaluation mode; Rego policies for GateKeeper + Validation | +| RHDH | dcm-rhdh-integration-spec | Application Domain interface; bearer token passthrough to API Gateway | +| AMQ Streams (Kafka) | doc 51 §4.2 (Optional) | **Optional** — deployed in this example for high-throughput event streaming. Standard deployments use PostgreSQL `LISTEN/NOTIFY`. | +| GitLab CE | doc 51 §6 (Git as Ingress Adapter) | **Optional** — deployed in this example as a Git ingress adapter. Not a state store. | + +--- + +## Data Flow: Intelligent Placement Demo + +The Summit demo Intelligent Placement use case flows through the implementation as follows: + +``` +1. alice@corp submits VM request in RHDH + → RHDH calls POST /api/v1/requests (Bearer token from Keycloak) + → dcm-api-gateway validates token, assigns operation_uuid, writes to PostgreSQL operations table + → Writes request.initiated event to pipeline_events table (LISTEN/NOTIFY triggers orchestrator) + +2. dcm-request-orchestrator receives event + → Dispatches to dcm-request-processor + +3. dcm-request-processor assembles payload + → Reads tier/zone from request fields + → Fetches core layers and service layers from database + → Merges: consumer fields + core layer + service layer + provenance map + → Writes assembled payload event → dcm-policy-engine + +4. dcm-policy-engine evaluates (Internal mode) + → Calls OPA sidecar at localhost:8181 + → Evaluates: vm-sizing.rego (Validation — structural) + → Evaluates: tier-region.rego (GateKeeper — compliance) + → tier-region.rego checks: request zones match tier's allowed zones + → If DENY: operation status = FAILED, reason written to audit + → If APPROVE: payload passed to dcm-placement-engine + +5. dcm-placement-engine selects provider + → Queries all active Service Providers for capacity data + → Calls OPA: placement-weights.rego for each candidate + → Aggregates 5-signal scores (capacity 35% + affinity 10% + cost 20% + perf 20% + risk 15%) + → Selects dcm-provider-vm (highest aggregate score) + → aggregate_score >= 40 (dev profile threshold) → auto_approve + → Writes Requested State to PostgreSQL requested_records (append-only) + +6. dcm-request-orchestrator dispatches to dcm-provider-vm + → POST /api/v1/resources (Operator Interface) + → dcm-provider-vm receives DCM VirtualMachine payload + +7. dcm-provider-vm naturalizes and executes + → Translates DCM VirtualMachine → automation platform parameters + → This example uses AAP: POST /api/v2/job_templates/{id}/launch + → DCM is automation-platform agnostic — providers can use AAP, Tekton, + Argo Workflows, direct API calls, or any execution mechanism + → Provisions VM + +8. dcm-provider-vm denaturalizes and callbacks + → Automation completes → provider collects result + → Translates result → DCM VirtualMachine realized state + → POST /api/v1/provider/entities/{entity_uuid}/status to dcm-api-gateway + (single canonical callback path — all providers use this same endpoint) + → DCM writes Realized State to PostgreSQL realized_entities + → DCM writes Intent State to PostgreSQL intent_records (append-only, immutable) + → Updates operation status: OPERATIONAL + → Writes audit record (SHA-256 hash chain) + +9. alice sees result in RHDH + → GET /api/v1/operations/{operation_uuid} shows OPERATIONAL + → RHDH displays provisioned VM details +``` + +--- + +## Portability Considerations + +This implementation is designed as a portability exercise. Providers can be replaced: + +- **dcm-provider-vm**: Replace AAP/Ansible with Terraform, CloudForms, or any automation tool + that can accept a JSON/YAML payload and return a realized state payload. +- **dcm-provider-ocp-cluster**: Replace CAPI+RHOCP with Rancher, Tanzu, or any K8s provisioner. +- **dcm-provider-network**: Replace AAP/Ansible with NetBox automation, NSO, or vendor APIs. +- **dcm-provider-acm-shim**: This is explicitly a shim — replace with a proper ACM provider + that implements the full OIS Level 3 contract when ready. + +The DCM control plane components are not aware of which specific automation tool any provider uses. +They communicate exclusively through the Operator Interface specification. + +--- + +## Technology Stack Decision Notes + +| Decision | Chosen | Alternatives | Required? | +|----------|--------|-------------|-----------| +| Database | PostgreSQL (PGO) | CockroachDB, YugabyteDB | **Required** (all four data domains) | +| Auth | Keycloak (Red Hat SSO) | Dex, Authentik, Okta (OIDC) | **Required** (registered as auth_provider) | +| Secrets | HashiCorp Vault | OpenBao, CyberArk | **Required** (Vault-compatible API) | +| Policy runtime | OPA sidecar | OPA embedded, Styra DAS | Recommended | +| Service mesh | OpenShift Service Mesh | Cilium, Linkerd | Recommended (production) | +| Event bus | AMQ Streams (Kafka) | PostgreSQL `LISTEN/NOTIFY` (default) | **Optional** — enhancement for high-throughput | +| Git server | GitLab CE | Gitea, GitHub Enterprise | **Optional** — ingress adapter only | +| API Gateway | Traefik | Envoy, Kong | Recommended | diff --git a/content/docs/implementations/example-01-summit-demo/demo-script.md b/content/docs/implementations/example-01-summit-demo/demo-script.md new file mode 100644 index 0000000..2f0eec6 --- /dev/null +++ b/content/docs/implementations/example-01-summit-demo/demo-script.md @@ -0,0 +1,177 @@ +--- +title: "Demo Script" +type: docs +--- + +# Summit Demo Script — DCM Example Implementation #1 + +Three demonstration scenarios for Red Hat Summit 2026. +Each shows a different DCM architectural capability. + +--- + +## Demo 1: Intelligent Placement (March 1 MVP target) + +**Persona:** CTO +**Story:** Application team requests a VM. DCM automatically enforces zone placement policy +using OPA/Rego. The consumer specifies a tier; DCM ensures zones match. + +**What it shows:** Policy-driven pipeline, OPA Rego enforcement, automated placement scoring. + +### Setup +```bash +# Ensure VM provider and policy engine are running +oc get pods -n dcm-summit-demo | grep -E 'provider-vm|policy-engine|placement' + +# Confirm tier-region policy is active (not shadow) +curl -H "Authorization: Bearer $TOKEN" $DCM_URL/api/v1/admin/policies | jq '.items[] | select(.name=="tier-region-policy")' +``` + +### Demo Flow + +**Step 1 — Browse catalog in RHDH** +- Open RHDH URL in browser +- Login as `alice / demo-password` +- Navigate to: DCM Service Catalog +- Show: "Standard Virtual Machine" catalog item with cost estimate + +**Step 2 — Submit valid request (t1 tier, us-east-1 zones)** +```bash +curl -X POST $DCM_URL/api/v1/requests \ + -H "Authorization: Bearer $ALICE_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "catalog_item_uuid": "00000000-0000-0000-0003-000000000001", + "fields": { + "name": "demo-vm-01", + "cpu": 8, + "ram_gb": 16, + "environment": "prod", + "tier": "t1" + } + }' +# → Returns: {"operation_uuid": "...", "done": false} +``` + +**Step 3 — Watch the pipeline** +```bash +# Poll operation status (or show in RHDH) +curl -H "Authorization: Bearer $ALICE_TOKEN" $DCM_URL/api/v1/operations/$OP_UUID +# Progresses: INITIATED → ASSEMBLING → POLICY_EVALUATION → PLACEMENT → DISPATCHED → OPERATIONAL +``` + +**Step 4 — Show the policy enforcing (submit invalid tier)** +```bash +# t2 tier requests us-west-* zones — sending t1 request to t2 zone should fail +curl -X POST $DCM_URL/api/v1/requests \ + -H "Authorization: Bearer $ALICE_TOKEN" \ + -d '{ + "catalog_item_uuid": "00000000-0000-0000-0003-000000000001", + "fields": { + "name": "demo-vm-bad", + "cpu": 4, + "ram_gb": 8, + "environment": "dev", + "tier": "t1", + "network_zone": "us-west-1" + } + }' +# → Denied: tier-region-policy returns DENY +# Show: audit record with denial reason +``` + +**Talking points:** +- The policy (tier-region.rego) is a Git artifact — versioned, testable, auditable +- Shadow mode lets new policies run against real traffic before enforcing +- The consumer specified only `tier`; the zones are enforced automatically +- Every decision is in the audit trail with the exact policy UUID and reason + +--- + +## Demo 2: Datacenter Rehydration (April 1 target) + +**Persona:** CIO +**Story:** Simulate a DC loss scenario. Trigger rehydration of all resources from declared state. + +**What it shows:** Four states model (Intent/Requested/Realized/Discovered), rehydration trigger, +Meta Provider orchestrating multiple child providers. + +### Setup +```bash +# Ensure webapp meta provider is running +oc get pods -n dcm-summit-demo | grep webapp-meta + +# Pre-provision a "webapp" to demonstrate rehydrating +curl -X POST $DCM_URL/api/v1/requests \ + -H "Authorization: Bearer $ALICE_TOKEN" \ + -d '{ + "catalog_item_uuid": "00000000-0000-0000-0003-000000000003", + "fields": {"app_name": "payments-app", "environment": "prod", "tier": "t1"} + }' +# Wait for OPERATIONAL, then simulate "loss" +``` + +### Demo Flow + +**Step 1 — Show existing realized state** +```bash +curl -H "Authorization: Bearer $ALICE_TOKEN" $DCM_URL/api/v1/resources | jq '.items[] | {name: .fields.name, state: .lifecycle_state}' +``` + +**Step 2 — Simulate DC loss (decommission resources)** +```bash +# Mark resources as FAILED (simulate discovery reporting loss) +curl -X POST $DCM_URL/api/v1/resources/$ENTITY_UUID:mark-failed \ + -H "Authorization: Bearer $TOKEN" \ + -d '{"reason": "simulated_dc_loss"}' +``` + +**Step 3 — Trigger rehydration from Intent State** +```bash +curl -X POST $DCM_URL/api/v1/resources/$ENTITY_UUID:rehydrate \ + -H "Authorization: Bearer $TOKEN" \ + -d '{"mode": "intent", "reason": "dc_loss_recovery"}' +# → DCM replays original Intent through current policies +# → Meta Provider re-provisions: Network Port → VM → OCP Cluster (in sequence) +``` + +**Talking points:** +- Intent State is immutable and stored in DCM's database (append-only, never modified) — survives the DC loss +- Rehydration replays the original intent through *current* policies (not old ones) +- The Meta Provider handles the sequencing automatically +- Full audit trail shows before/after states + +--- + +## Demo 3: Application as a Service (April 1 target) + +**Persona:** Application Owner +**Story:** Developer requests a full web application environment. One catalog item → three providers coordinated automatically. + +### Demo Flow + +```bash +# Single request → Meta Provider orchestrates VM + Network + OCP Cluster +curl -X POST $DCM_URL/api/v1/requests \ + -H "Authorization: Bearer $ALICE_TOKEN" \ + -d '{ + "catalog_item_uuid": "00000000-0000-0000-0003-000000000003", + "fields": { + "app_name": "summit-demo-app", + "environment": "dev", + "tier": "demo", + "vm_size": "medium", + "ocp_worker_count": 3 + } + }' +``` + +Watch in RHDH as the Meta Provider: +1. Allocates network port (network-provider) +2. Provisions app server VM (vm-provider) — depends on step 1 +3. Provisions OCP cluster (ocp-cluster-provider) — depends on step 1 + +**Talking points:** +- Consumer specified 4 fields; DCM assembled ~40 fields from layers + policies +- Rollback: if OCP cluster fails, VM and network port are automatically decommissioned +- The Meta Provider is itself a standard DCM provider — composability is architectural diff --git a/content/docs/implementations/example-01-summit-demo/deployment-guide.md b/content/docs/implementations/example-01-summit-demo/deployment-guide.md new file mode 100644 index 0000000..e3d8f76 --- /dev/null +++ b/content/docs/implementations/example-01-summit-demo/deployment-guide.md @@ -0,0 +1,220 @@ +--- +title: "Deployment Guide" +type: docs +--- + +# Deployment Guide — DCM Example Implementation #1 + +Step-by-step guide to deploy the DCM Summit Demo on an OpenShift cluster. + +--- + +## Prerequisites + +**Cluster requirements:** +- OpenShift 4.14+ +- Cluster-admin access +- Minimum 16 vCPUs / 32GB RAM available for DCM workloads +- Default StorageClass configured for dynamic PVC provisioning + +**Local tooling:** +```bash +# Verify oc CLI +oc version + +# Verify Ansible +ansible --version # 2.14+ +ansible-galaxy collection install kubernetes.core + +# Verify Helm (used for some chart installs) +helm version + +# Verify KUBECONFIG is set +oc whoami +``` + +**External dependencies:** +- Ansible Automation Platform (AAP) instance accessible from the cluster — for VM + Network providers +- Red Hat ACM hub cluster — for ACM shim provider (or disable if not available) + +--- + +## Step 1: Configure Inventory + +```bash +cd ansible +cp inventory/hosts.yml.example inventory/hosts.yml +# Edit hosts.yml and set cluster_domain to your OpenShift apps domain +# Example: cluster_domain: "apps.ocp.example.com" +``` + +--- + +## Step 2: Install Required Operators + +This step installs all OperatorHub operators. Requires cluster-admin. + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags operators +``` + +Wait for operators to reach `Succeeded` phase (~5-10 min): +```bash +oc get csv -n openshift-operators +``` + +--- + +## Step 3: Deploy Storage Stack + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags storage +``` + +This deploys: +- PostgreSQL cluster (CrunchyData PGO) — ~3 min to be ready +- AMQ Streams Kafka cluster — ~5 min to be ready +- GitLab CE — ~5 min to be ready (first start is slow) +- OpenSearch — ~2 min + +Verify: +```bash +oc get postgrescluster -n dcm-summit-demo +oc get kafka -n dcm-summit-demo +``` + +--- + +## Step 4: Deploy Auth and Vault + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags auth,vault +``` + +This: +1. Deploys Keycloak and imports the DCM realm +2. Deploys Vault and runs the bootstrap sequence (doc 49 §6) +3. Creates per-provider Vault policies and Kubernetes auth roles + +**Important:** The bootstrap admin password is `CHANGE_ME_BOOT002`. +Per BOOT-002, this MUST be rotated on first login: +```bash +# Get Keycloak URL +oc get route -n dcm-summit-demo | grep keycloak +# Login as platform-admin / CHANGE_ME_BOOT002 +# Change password immediately +``` + +--- + +## Step 5: Configure Service Mesh + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags service-mesh +``` + +This installs OpenShift Service Mesh and configures STRICT mTLS across all +DCM components. After this step, all inter-component traffic is encrypted +and authenticated using mutual TLS certificates issued by the mesh CA. + +--- + +## Step 6: Deploy Control Plane + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags control-plane +``` + +Verify all control plane pods are running: +```bash +oc get pods -n dcm-summit-demo | grep -E 'api-gateway|orchestrator|policy|placement|processor|audit|catalog|discovery' +``` + +--- + +## Step 7: Deploy Service Providers + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags providers +``` + +Each provider registers itself with the DCM API Gateway on startup via: +``` +POST /api/v1/admin/providers +``` + +Verify provider registration: +```bash +TOKEN=$(oc get secret dcm-admin-token -n dcm-summit-demo -o jsonpath='{.data.token}' | base64 -d) +DCM_URL=$(oc get route dcm-api-gateway -n dcm-summit-demo -o jsonpath='{.spec.host}') +curl -H "Authorization: Bearer $TOKEN" https://$DCM_URL/api/v1/admin/providers +``` + +--- + +## Step 8: Deploy Frontend (RHDH) + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags rhdh +``` + +--- + +## Step 9: Load Seed Data + +```bash +ansible-playbook site.yml -i inventory/hosts.yml --tags seed-data +``` + +This loads: +- Core Layers (US East 1, US West 2) +- Catalog items (VM Standard, OCP Cluster, Web App Meta) +- OPA policies (in shadow mode — review before activating) + +--- + +## Step 10: Activate Policies + +Policies are loaded in shadow mode (doc 14 — FCM-004). Review shadow evaluation +results before activating: + +```bash +# Check shadow evaluation results in the Policy Engine +curl -H "Authorization: Bearer $TOKEN" https://$DCM_URL/api/v1/admin/policies?status=shadow + +# Activate when ready +curl -X POST -H "Authorization: Bearer $TOKEN" \ + https://$DCM_URL/api/v1/admin/policies/{uuid}:activate +``` + +--- + +## Full Deployment (Single Command) + +```bash +cd ansible +ansible-playbook site.yml -i inventory/hosts.yml +``` + +Deployment takes approximately 20-30 minutes end-to-end. + +--- + +## Verification + +```bash +# Health check +curl https://$(oc get route dcm-api-gateway -n dcm-summit-demo -o jsonpath='{.spec.host}')/livez + +# List catalog items (authenticated) +curl -H "Authorization: Bearer $TOKEN" \ + https://$(oc get route dcm-api-gateway -n dcm-summit-demo -o jsonpath='{.spec.host}')/api/v1/catalog +``` + +--- + +## Teardown + +```bash +oc delete namespace dcm-summit-demo +``` diff --git a/content/docs/implementations/example-01-summit-demo/provider-development-guide.md b/content/docs/implementations/example-01-summit-demo/provider-development-guide.md new file mode 100644 index 0000000..4441e5a --- /dev/null +++ b/content/docs/implementations/example-01-summit-demo/provider-development-guide.md @@ -0,0 +1,178 @@ +--- +title: "Provider Development Guide" +type: docs +--- + +# Provider Development Guide + +How to build a new DCM Service Provider compatible with this implementation. + +--- + +## Overview + +A DCM Service Provider is a containerized Go (or any language) service that: +1. Implements the DCM Operator Interface Specification (OIS) +2. Registers with the DCM API Gateway on startup +3. Receives `CreateRequest` payloads and executes them against a target system +4. Sends status callbacks to the DCM API Gateway + +This guide follows the DCM spec for provider development. +Spec references: doc A (Provider Contract), dcm-operator-interface-spec.md, dcm-operator-sdk-api.md + +--- + +## OIS Conformance Levels + +| Level | Required Endpoints | Unlocks | +|-------|-------------------|---------| +| **Level 1** | POST /api/v1/resources, GET /health, GET /capabilities, callback on completion | Basic provisioning, decommission, capability introspection | +| **Level 2** | + GET /discover, GET /capacity, PATCH /api/v1/resources/{id} | Discovery, drift detection, updates | +| **Level 3** | + PUT /api/v1/resources/{id}:bulk-import | Brownfield ingestion | + +Start with Level 1, add Level 2 for drift detection capability. + +**`GET /capabilities`** returns the provider's available options — networks, storage classes, images, regions, or any domain-specific resources the consumer can reference in requests. The Policy Engine's Transformation policies query this endpoint during assembly to validate and enrich consumer input. Without it, policies would need to hardcode provider-specific values. + +--- + +## Provider Registration + +On startup, your provider must register with DCM: + +```go +// Registration payload (doc A §3.1) +registration := map[string]interface{}{ + "display_name": "My Custom Provider", + "provider_type": "service_provider", + "endpoint": "http://my-provider-service:8080", + "public_key_pem": loadMTLSCert(), // Your pod's service mesh certificate + "capabilities": map[string]interface{}{ + "supported_resource_types": []string{"Compute.VirtualMachine"}, + "ois_conformance_level": 1, + "discovery_supported": false, + "rehydration_supported": true, + }, + "sovereignty_declarations": []string{"us-east-1", "us-east-2"}, +} + +resp, err := http.Post( + os.Getenv("DCM_API_GATEWAY_URL") + "/api/v1/admin/providers", + "application/json", + jsonBody(registration), +) +// Store the returned provider_uuid +``` + +--- + +## Receiving a CreateRequest + +DCM calls your provider when a request is dispatched: + +```go +// POST /api/v1/resources +// Spec ref: dcm-operator-interface-spec.md §3.1 +type CreateRequest struct { + RequestUUID string `json:"request_uuid"` // == operation_uuid + EntityUUID string `json:"entity_uuid"` // Stable entity identifier + ResourceType string `json:"resource_type"` // e.g. "Compute.VirtualMachine" + Fields map[string]interface{} `json:"fields"` // Fully assembled payload + Provenance map[string]interface{} `json:"provenance"` // Field lineage + TenantUUID string `json:"tenant_uuid"` + RequestedAt string `json:"requested_at"` +} + +// Your handler: +func (p *Provider) HandleCreateRequest(w http.ResponseWriter, r *http.Request) { + var req CreateRequest + json.NewDecoder(r.Body).Decode(&req) + + // 1. Return 200 immediately (LRO pattern — do not block) + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(map[string]string{ + "status": "PROVISIONING", + "message": "Request accepted", + }) + + // 2. Process asynchronously + go p.provision(req) +} +``` + +--- + +## Sending Status Callbacks + +When your provisioning completes, callback to DCM: + +```go +// Spec ref: dcm-provider-callback-api.yaml, doc 43 (Provider Callback Authentication) +func (p *Provider) sendCallback(entityUUID string, success bool, result map[string]interface{}) { + payload := map[string]interface{}{ + "dcm_entity_uuid": entityUUID, + "provider_entity_id": result["native_id"], // Your system's ID + "lifecycle_state": "OPERATIONAL", // or "FAILED" + "realized_fields": result, + "realized_at": time.Now().UTC().Format(time.RFC3339), + } + + req, _ := http.NewRequest("POST", + os.Getenv("DCM_API_GATEWAY_URL") + "/api/v1/provider/entities/" + entityUUID + "/status", + jsonBody(payload)) + + // Authenticate callback with your provider callback credential + // This credential was issued by DCM Vault at registration (doc 43, doc 49 §7.1) + req.Header.Set("Authorization", "Bearer " + os.Getenv("DCM_CALLBACK_TOKEN")) + req.Header.Set("Content-Type", "application/json") + + http.DefaultClient.Do(req) +} +``` + +--- + +## Naturalization and Denaturalization + +Your provider translates between DCM's unified model and your native system: + +```go +// NATURALIZATION: DCM VirtualMachine → AAP Job parameters +func naturalizeToAAP(fields map[string]interface{}) map[string]interface{} { + return map[string]interface{}{ + "extra_vars": map[string]interface{}{ + "vm_name": fields["name"], + "vm_cpu": fields["cpu"], + "vm_memory_mb": fields["ram_gb"].(int) * 1024, + "vm_disk_gb": fields["storage_gb"], + "vcenter_url": fields["vcenter_url"], // Injected by Core Layer + "datastore": fields["datastore"], // Injected by Core Layer + "template": fields["os_image_path"], // Injected by OS Image Layer + }, + } +} + +// DENATURALIZATION: AAP result → DCM VirtualMachine realized state +func denaturalizeFromAAP(aapResult map[string]interface{}) map[string]interface{} { + artifacts := aapResult["artifacts"].(map[string]interface{}) + return map[string]interface{}{ + "ip_address": artifacts["vm_ip"], + "hostname": artifacts["vm_hostname"], + "vcenter_vm_id": artifacts["vm_moref"], + "power_state": "on", + "os_version": artifacts["os_version"], + } +} +``` + +--- + +## OpenShift Deployment Pattern + +Use the `dcm-provider-vm` deployment as your template: +1. Copy `openshift/providers/vm-provider/deployment.yaml` +2. Update: `name`, `dcm.io/resource-type`, `provider_uuid`, `image` +3. Add Vault init container if your provider needs external credentials +4. Register resource type in `capabilities.supported_resource_types` + +The service mesh handles mTLS automatically — no TLS code needed in your provider. diff --git a/content/docs/project-overview.md b/content/docs/project-overview.md new file mode 100644 index 0000000..0da803c --- /dev/null +++ b/content/docs/project-overview.md @@ -0,0 +1,324 @@ +# DCM — Project Overview + +**GitHub:** https://github.com/dcm-project +**License:** Apache 2.0 + +--- + +## What DCM Is + +Data Center Management (DCM) is an open-source framework that gives enterprise IT organizations a hyperscaler-like cloud experience on infrastructure they own and control. It is the governing control plane that sits above provisioning tools, automation platforms, and infrastructure systems — making them coherent, governed, and self-service. + +DCM is not a deployment tool, a configuration manager, or an automation platform. It is the management plane that connects them: a unified control plane with a declarative data model, a policy engine that enforces organizational standards automatically, and a service provider interface that integrates with the automation tools organizations already have. + +### Architecture in One Sentence + +DCM is built on three foundational abstractions — **Data**, **Provider**, and **Policy** — connected by a policy-driven event loop. Every concept in the architecture maps to one of these three. + +``` +Event (Data state change) + → Policy Engine evaluates all matching Policies + → Policies produce decisions / mutations / actions + → Actions invoke Providers or produce new Data + → New Data triggers new Events + → Repeat +``` + +--- + +## The Problem DCM Solves + +Enterprise on-premises infrastructure is managed by dozens of disconnected tools, teams, and manual processes. A single virtual machine might require five teams, three ticketing systems, and two weeks to provision. No one has a trustworthy, real-time answer to: *what exists, what was requested, what was actually provisioned, and does the current state match what was intended?* + +The gap between intended state and actual state is where compliance risk, operational toil, and security incidents live. + +Specifically: + +**Fragmented operations.** Organizations spend engineering capacity stitching together disparate automation efforts rather than delivering services. Functionality is trapped in monoliths or siloed teams. There is no unified service catalog or API interface — capabilities that public clouds provide as table stakes. + +**Long time-to-market.** The lifecycle of a single infrastructure asset is managed by many teams, with approvals, provisioning, and auditing taking weeks. Operations teams face high toil in day-to-day management. + +**The private cloud gap.** True private cloud requires networking, storage, identity, catalogs, FinOps, observability, auditing, and risk management — not just on-premises compute. Most organizations have the compute but not the surrounding governance layer. + +**Unreliable data.** There is typically no reconciliation between discovered inventory (what actually exists) and intended inventory (what was requested). Without a unified data model, it is impossible to know whether current state is aligned with deployed state, and intended state is often not stored at all. + +**Compliance overhead.** In regulated environments (financial services, government, defense, healthcare), compliance evidence must be produced manually from systems that were not designed to provide it. DCM makes compliance evidence a structural property of every operation — built into the data, not reconstructed after the fact. + +--- + +## What DCM Does + +DCM manages the complete lifecycle of infrastructure resources — from bare metal hardware and network gear at the physical layer through virtual machines, containers, Kubernetes clusters, and application platforms. The lifecycle includes Day 0 (standing up new infrastructure), Day 1 (provisioning resources), Day 2 (operating and managing), and Day N (decommissioning and releasing). + +### The Three Foundational Abstractions + +**Data** is everything that exists in DCM — resource entities, policy definitions, data layers, audit records, accreditations, group memberships. Every artifact has a UUID, a lifecycle state, a schema type, and complete field-level provenance recording where every value came from and why it changed. Data flows through the system in four states: + +| State | What It Represents | +|-------|-------------------| +| **Intent** | What the consumer originally asked for — immutable after submission | +| **Requested** | The fully assembled, policy-approved dispatch payload — what was sent to the provider | +| **Realized** | What the provider actually provisioned — the authoritative record of what exists | +| **Discovered** | What currently exists in the environment — used to detect drift | + +Comparing these four states continuously is how DCM detects drift, enforces governance, and enables rehydration (replaying original intent through current policies to reproduce a resource in a new location or after failure). + +**Policy** is every rule that governs DCM behavior — expressed as code, stored in Git, versioned, tested in shadow mode before activation, and enforced deterministically. Eight typed policy schemas cover every governance need: + +| Policy Type | What It Does | +|-------------|-------------| +| **GateKeeper** | Halts requests or contributes weighted risk scores for approval routing | +| **Validation** | Checks structural correctness; halts on failure or accumulates advisory warnings | +| **Transformation** | Automatically enriches request payloads (injects values, enforces field locks) | +| **Orchestration Flow** | Defines named workflows as explicit, ordered pipeline steps | +| **Recovery** | Governs what DCM does when things go wrong (timeout, failure, drift) | +| **Governance Matrix Rule** | Enforces data classification and sovereignty boundaries at every cross-boundary interaction | +| **Lifecycle** | Triggers actions on entity state changes (TTL expiry, scheduled operations) | +| **ITSM Action** | Creates and updates records in connected ITSM systems as a side-effect of pipeline events | + +Every business rule, every compliance constraint, every operational standard is a Policy — never hard-coded, always auditable, always testable in shadow mode before enforcement. + +**Providers** are everything external that DCM integrates with. Six provider types share a common base contract (registration, health, mTLS, sovereignty declaration, accreditation, governance matrix enforcement) and add typed capability extensions: + +| Provider Type | Capability | +|--------------|-----------| +| **Service Provider** | Realizes infrastructure resources (VMs, networks, storage, containers, bare metal). Also covers credentials (internal or Vault), notifications (email/Slack), and ITSM integration via resource type declarations. | +| **Information Provider** | Serves authoritative external data (CMDB, HR, finance, identity systems) | +| **Meta Provider** | Composes multiple providers into compound catalog items (a "three-tier web app" as a single catalog entry) | +| **Auth Provider** | Authenticates identities; resolves role and group memberships. Multiple auth providers enable tenant-routed authentication. | +| **Peer DCM** | Another DCM instance participating in federated deployment | +| **Process Provider** | Executes ephemeral workflows (software install, backup, migration, compliance scan) via automation platforms (AAP, Tekton) | + +### What This Enables + +A consumer can browse a Service Catalog showing only the resources they are entitled to request, configure their requirements, submit a request, and receive a provisioned resource — with full cost attribution, audit trail, drift monitoring, and decommissioning capability — without ever interacting with any underlying automation tool directly. + +Organizational standards, security requirements, placement constraints, and data sovereignty rules are enforced automatically by the Policy Engine before anything is dispatched to a provider. There are no runbooks to follow correctly. The correct behavior is structural. + +--- + +## Who Benefits + +### Consumers — Application Teams and Developers + +Self-service infrastructure that matches the experience of public cloud, on infrastructure the organization controls. Browse a catalog, configure requirements, submit a request, receive a provisioned resource. No tickets to raise, no teams to coordinate with, no runbooks to follow. + +### Platform Engineers and Infrastructure Operations + +A single control plane rather than a collection of disconnected tools. Define the service catalog, write policies that enforce organizational standards, manage the provider ecosystem. Provisioning consistency is structural. Drift is detected automatically. Day 2 toil is governed by policy, not enforced by individual operators following runbooks. + +### Security and Compliance Teams + +Policy-as-code tested before activation (shadow mode), enforced at every pipeline stage, producing a tamper-evident audit trail at every state transition. Data classification (PHI, restricted, sovereign) is a first-class concept enforced at the Governance Matrix layer — always boolean, never scoreable around. Accreditation monitoring automatically verifies provider certifications against external registries (FedRAMP marketplace, CMMC AB, ISO IAF CertSearch) so compliance posture is continuously verified rather than point-in-time attested. + +### SRE and Operations Teams + +Complete observability into what was requested, what was provisioned, whether it matches, and what changed. Drift detection compares Realized State against Discovered State and triggers remediation per policy. Four states give a complete picture of the gap between intent and reality at any moment. + +### Auditors + +A complete, chronological, tamper-evident record of every change to every artifact — who made it, when, through what authorization chain, and what the values were before and after. The audit hash chain means any deleted or modified record is detectable. Field-level provenance means every value in every record can be traced to its origin. + +### FinOps and Business Leadership + +Cost attribution built into the provisioning pipeline. Every resource has a cost estimate before provisioning and ongoing cost attribution to the owning Tenant and business unit throughout its lifecycle. Pre-request cost estimates, placement tie-breaking by cost, and consumer-visible cost views are all standard capabilities. + +--- + +## Where DCM Operates + +DCM operates in the control plane layer of an enterprise data center estate. It is infrastructure-agnostic — it does not care whether the underlying infrastructure is bare metal servers, VMware clusters, OpenStack clouds, Kubernetes clusters, or network equipment. Service Providers abstract those differences. DCM manages all of them through the same interface and data model. + +### Deployment Topology + +| Mode | Description | +|------|-------------| +| **Single-region** | One DCM instance governs one data center or regional infrastructure estate | +| **Federated multi-instance** | Multiple DCM instances with declared trust relationships route requests across regional and organizational boundaries | +| **Hub/Regional** | Hub DCM holds the authoritative registry and global policy hierarchy; Regional instances handle local provider routing and operate independently during hub unavailability | +| **Sovereign/Air-gapped** | Full air-gapped operation with HSM-backed key storage, local credential management, and no external network dependencies | + +### Data Sovereignty + +DCM enforces data sovereignty as a structural property, not a configuration option. Every entity carries sovereignty zone declarations that constrain which Storage Providers may hold copies and which Service Providers may handle the data. The Governance Matrix enforces these boundaries at every cross-boundary interaction — and it is always boolean. No scoring, no exceptions, no policy can route around a declared sovereignty constraint. + +### Target Environments + +DCM specifically targets organizations with on-premises infrastructure that must behave like a cloud — particularly in regulated industries: + +- **Financial services** — where public cloud is constrained by regulatory requirements, data residency, and audit obligations +- **Government and defense** — where FedRAMP, CMMC, DoD Impact Level authorization, and air-gapped operation are operational requirements +- **Healthcare** — where HIPAA BAA requirements and PHI data classification impose specific placement and access constraints +- **Critical infrastructure** — where sovereignty over the infrastructure itself is as important as sovereignty over the data + +These are also the environments where the gap between what on-premises infrastructure currently provides and what organizations need is widest — and where DCM's compliance framework integrations (FedRAMP, CMMC, HIPAA, SOC 2, ISO 27001, PCI DSS, DoD Impact Levels) are built directly into the accreditation model rather than added as an afterthought. + +--- + +## How DCM Works + +### The Event Loop + +DCM's runtime is a policy-driven event loop. There is no hard-coded pipeline — the pipeline is the sum of active policies responding to data state changes. + +``` +Event (Data state change — e.g., request submitted) + → Policy Engine evaluates all matching Policies + → Policies produce typed outputs: + GateKeeper: approve / halt / risk score + Validation: pass / fail / warning + Transformation: inject fields / lock values / annotate provenance + Placement: constraints + preferences → Provider selected + Orchestration Flow: ordered step sequence + Recovery: action when things go wrong + Governance Matrix: ALLOW / DENY / STRIP / REDACT + → Outputs invoke Providers or produce new Data + → New Data changes trigger new Events + → Repeat until terminal state +``` + +Every step is audited. Every field mutation carries provenance. Every decision is deterministic — the same data, evaluated by the same policies, produces the same outcome regardless of who submitted it or when. + +### The Request Lifecycle + +When a consumer submits a service request, DCM executes a governed assembly pipeline: + +``` +1. Consumer submits request (intent declared) + → Intent State written (immutable snapshot of original ask) + +2. Layer assembly + → Core Layers applied (datacenter, rack, network zone, location) + → Service Layers applied (resource-type-specific configuration) + → Consumer fields merged + → Transformation Policies inject and lock required fields + +3. Policy evaluation + → Validation Policies check structural correctness + → GateKeeper Policies assess risk and enforce business rules + → Placement Engine selects provider (constraints + scoring) + → Requested State written (the approved, assembled dispatch payload) + +4. Dispatch + → CreateRequest sent to selected Service Provider + → Provider naturalizes (translates DCM model to native format) + → Provider executes + → Provider denaturalizes (translates result back to DCM model) + → Realized State written (what was actually provisioned) + +5. Ongoing lifecycle + → Discovery Scheduler polls for drift between Realized and Discovered State + → Drift triggers Recovery Policy evaluation + → TTL, scheduled operations, and rehydration operate on Realized State + → Decommission mirrors provisioning in reverse +``` + +### How Policy Replaces Hard-Coded Logic + +Every business rule in DCM is a Policy artifact — stored in Git, versioned, tested in shadow mode before activation, and enforced deterministically. There are no approval workflows embedded in code, no hard-coded placement rules, no statically defined pipeline stages. + +This means: +- Adding a new approval step = writing a GateKeeper policy +- Changing where a resource is placed = updating a Placement policy +- Auto-injecting a required field = writing a Transformation policy +- Defining what happens on failure = writing a Recovery policy +- Building a named multi-step workflow = writing an Orchestration Flow policy + +Policy authoring is not an engineering task requiring code deployment. Policies are GitOps artifacts. They go through PR review, shadow evaluation (running against real traffic without enforcing), and staged activation. A new policy can go from idea to enforced governance without a code release. + +### How Providers Integrate + +Every provider implements one base contract: registration, health check, mTLS identity, sovereignty declaration, and governance matrix enforcement. What varies between provider types is the capability extension — the typed set of additional endpoints and behaviors that define what the provider can do. + +This means DCM does not need to know in advance what automation tools an organization uses. A VMware vSphere cluster, a bare metal Redfish endpoint, a Kubernetes cluster via CAPI, and an OpenStack cloud all register as Service Providers. DCM speaks the same language to all of them. The provider handles translation to and from the native tool format — Naturalization (DCM → native) and Denaturalization (native → DCM). + +Organizations do not replace their existing automation. They wrap it in the Provider interface. The investment in Ansible playbooks, Terraform modules, and vendor APIs is preserved — DCM adds the governance layer above it. + +### How Data Sovereignty Is Enforced + +Data sovereignty is not a configuration flag — it is a structural property evaluated at every boundary crossing. Every entity carries `sovereignty_zone` declarations. The Governance Matrix evaluates these at every provider dispatch, storage write, and federation routing decision. The result is always boolean: ALLOW or DENY. There is no scoring that can route around a sovereignty constraint. + +For regulated environments: PHI data cannot be dispatched to a provider without a HIPAA BAA accreditation. Restricted data cannot leave a declared sovereignty zone. Classified data cannot be held by a provider without the declared authorization level. These are not policies an administrator can override — they are matrix rules with hard enforcement. + +--- + +## Ethos + +DCM is built on a specific set of values that drive every design decision. These are not marketing statements — they are the decision framework applied when priorities conflict. + +### Security Is the Baseline, Not a Feature + +Security properties are architecturally present in every profile, including the minimal development profile. What profiles control is enforcement strictness and operational overhead — not whether the property applies. + +The `minimal` profile is "security with minimal operational overhead" — not "minimal security." It rotates credentials, audits first access, maintains a revocation registry, and runs shadow mode on all contributed policies. The intervals are longer, the triggers are more permissive, and manual steps replace automated ones — but the security model is present and correct. + +When security and convenience conflict, security wins. But the design obligation is not just to enforce security — it is to make the secure path the easy path. A security model that is routinely bypassed because it is too burdensome has failed at both security and usability. The profile system exists precisely to make secure behavior automatic rather than effortful. + +### The Governed Path Must Also Be the Easy Path + +Self-service is not a nice-to-have — it is the mechanism through which governance is achieved at scale. If consuming resources through DCM is harder than raising a ticket or writing a one-off script, application teams will find the path of least resistance and all of DCM's governance benefits evaporate. + +This principle shapes every interface decision: request submission should be a single API call; ordinary requests should auto-approve; policy authoring should not require Rego expertise for common cases; cost estimates should appear before a consumer commits. The score-driven auto-approval system exists specifically so that clean, standard requests are not gated on human review that adds no value. + +Ease of use is not in tension with governance — it is the delivery mechanism for governance. Organizations that find DCM's governed path easier than ungoverned alternatives will use it. Those that do not will route around it. + +### Compliance Is Constructed, Not Audited + +Compliance evidence in DCM is a structural product of every operation, not something reconstructed after the fact. The audit trail is written at every state transition. Provenance is embedded in every field. The Governance Matrix enforces data classification boundaries at every crossing. Accreditation status is continuously monitored against external registries. + +The implication: when an auditor asks "show me every person who touched this data between these dates," DCM can answer that question directly from the audit store without any manual evidence gathering. When a regulator asks "prove this data never left the EU," the Governance Matrix enforcement log answers the question with cryptographic tamper evidence. + +This is different from compliance tooling that inspects systems and produces reports. DCM does not inspect systems and report findings — it governs operations and makes compliance a property of the operations themselves. + +### The Architecture Should Be Easy to Implement and Extend + +DCM is designed so that new capabilities fit within the existing three-abstraction model without modifying the core. A new provider type is a new implementation of the base contract with a new capability extension — no core changes. A new policy type is a new output schema — no core changes. A new data entity type is a new schema registration — no core changes. + +This is the test: if a new capability can be expressed as Data, Provider, or Policy, it belongs in DCM and requires no architectural changes. If it cannot be expressed within these three abstractions, it is either a runtime implementation detail or a signal that the abstractions need to be reconsidered. + +The same principle applies to organizations deploying DCM. They should not need to modify DCM source code to adapt it to their environment. New compliance requirements become policy additions. New infrastructure types become new provider implementations. New approval processes become custom authority tier definitions. DCM provides the framework; organizations provide the domain knowledge. + +### No Silent Behavior + +Every operation in DCM produces an observable artifact. No change is silent. No failure disappears into a log that no one reads. Every state transition produces an audit record. Every policy decision produces a typed output with a score driver. Every drift detection produces a record that links current state to intended state. + +This is not just an audit requirement — it is an architectural property that makes debugging, compliance, and operational understanding possible. When something goes wrong in a governed system, the question "what happened and why" should always be answerable from the data the system produced as part of its normal operation. + +--- + + +## Key Facts + +| | | +|-|-| +| **Architecture** | Three abstractions (Data, Provider, Policy) · Policy-driven event loop | +| **Provider types** | 12 (unified base contract + typed capability extensions) | +| **Policy types** | 8 (typed output schemas with deterministic evaluation) | +| **Entity lifecycle states** | 4 (Intent · Requested · Realized · Discovered) | +| **Capabilities** | 331 across 39 domains | +| **Data model documents** | 58 | +| **Specifications** | 15 | +| **OpenAPI paths** | 63 consumer · 57 admin · 5 operator · 7 provider callback | +| **Compliance frameworks** | FedRAMP · CMMC · HIPAA · SOC 2 · ISO 27001 · PCI DSS · DoD IL2–IL6 | +| **Deployment profiles** | minimal · dev · standard · prod · fsi · sovereign | +| **License** | Apache 2.0 | +| **Reference Implementation** | | **Reference Implementation** | [dcm-project/dcm-examples](https://github.com/dcm-project/dcm-examples) — Example #1: Summit Demo (OpenShift + AAP + ACM + RHDH) | — OpenShift + AAP + ACM + RHDH | +| **GitHub** | https://github.com/dcm-project | + +--- + +## Core Design Principles + +1. **Declarative** — data describes desired state, not procedures +2. **API-First** — every capability available via standard AEP-aligned API +3. **Policy-Governed** — all business logic through the Policy Engine, never hard-coded +4. **Idempotent** — applying the same data multiple times produces the same result +5. **Immutable if Versioned** — published versions never change; changes produce new versions +6. **Provider-Agnostic** — DCM defines contracts, not implementations +7. **GitOps-Native** — intent and policy artifacts are Git-native; versioned, reviewable, auditable +8. **Federated** — all authorized actor types contribute within permitted scope; no single administrator bottleneck +9. **Compliance by Construction** — audit trail, provenance, and sovereignty enforcement are structural, not added post-hoc + +--- + +*DCM is open-source. Contributions, feedback, and discussion welcome at https://github.com/dcm-project* diff --git a/content/docs/schemas/001-initial.sql b/content/docs/schemas/001-initial.sql new file mode 100644 index 0000000..4485fbf --- /dev/null +++ b/content/docs/schemas/001-initial.sql @@ -0,0 +1,544 @@ +-- DCM PostgreSQL Schema — Initial +-- Spec ref: DCM data model doc 49, doc 51 (Infrastructure Optimization) +-- Implements: STI-001 (mandatory tenant_uuid predicate), STI-002 (RLS) +-- +-- This schema implements ALL FOUR DCM data domains in a single database: +-- Intent Domain — append-only consumer declarations +-- Requested Domain — append-only assembled/validated payloads +-- Realized Domain — versioned provider-confirmed state +-- Discovered Domain — ephemeral discovery snapshots +-- Plus: audit records (hash chain), operations (LRO), pipeline events, subscriptions + +\connect dcm + +-- ─── Extensions ────────────────────────────────────────────────────────────── + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "pgcrypto"; + +-- ─── Tenants ────────────────────────────────────────────────────────────────── + +CREATE TABLE tenants ( + tenant_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + handle VARCHAR(64) UNIQUE NOT NULL, + display_name VARCHAR(256) NOT NULL, + status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' + CHECK (status IN ('ACTIVE', 'SUSPENDED', 'DECOMMISSIONED')), + profile VARCHAR(32) NOT NULL DEFAULT 'dev' + CHECK (profile IN ('minimal', 'dev', 'standard', 'prod', 'fsi', 'sovereign')), + data_classifications_permitted JSONB NOT NULL DEFAULT '["internal"]', + sovereignty_zones JSONB NOT NULL DEFAULT '[]', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- ─── Actors (users and service accounts) ────────────────────────────────────── + +CREATE TABLE actors ( + actor_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tenant_uuid UUID NOT NULL REFERENCES tenants(tenant_uuid), + actor_type VARCHAR(32) NOT NULL + CHECK (actor_type IN ('human', 'service_account', 'system_component', 'provider')), + handle VARCHAR(256) NOT NULL, + display_name VARCHAR(256) NOT NULL, + auth_method VARCHAR(32) NOT NULL DEFAULT 'internal' + CHECK (auth_method IN ('internal', 'external')), + password_hash VARCHAR(256), -- argon2id hash (internal auth only) + totp_secret_ref VARCHAR(256), -- reference to TOTP secret in secrets table (optional MFA) + external_id VARCHAR(512), -- IdP subject claim (external auth only) + auth_provider_uuid UUID, -- which auth_provider (external auth only) + roles JSONB NOT NULL DEFAULT '[]', -- direct role assignments (internal); merged with IdP claims (external) + status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + last_login_at TIMESTAMPTZ, + UNIQUE(tenant_uuid, handle) +); + +-- ─── Entities (Realized State) ──────────────────────────────────────────────── +-- Spec ref: DCM data model doc 01 (Entity Types), doc 02 (Four States) +-- Realized domain — one row per realized entity per version. + +CREATE TABLE realized_entities ( + realized_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + entity_uuid UUID NOT NULL, -- Stable identifier across versions + tenant_uuid UUID NOT NULL REFERENCES tenants(tenant_uuid), + resource_type VARCHAR(256) NOT NULL, -- FQN e.g. Compute.VirtualMachine + resource_type_uuid UUID, + entity_type VARCHAR(64) NOT NULL + CHECK (entity_type IN ('infrastructure_resource', 'composite_resource', + 'process_resource', 'shared_resource', 'allocatable_pool')), + lifecycle_state VARCHAR(32) NOT NULL + CHECK (lifecycle_state IN ('PROVISIONING', 'OPERATIONAL', 'DEGRADED', + 'SUSPENDED', 'FAILED', 'DECOMMISSIONED', + 'INGESTED', 'INGESTION_PENDING')), + request_uuid UUID, -- The request that created/updated this + provider_uuid UUID, -- Which provider owns this entity + realized_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + realized_by UUID REFERENCES actors(actor_uuid), + fields JSONB NOT NULL DEFAULT '{}', -- Full realized field set + provenance JSONB NOT NULL DEFAULT '{}', -- Field-level provenance map + provider_metadata JSONB NOT NULL DEFAULT '{}', -- Provider-supplied metadata + sovereignty_zones JSONB NOT NULL DEFAULT '[]', + tags JSONB NOT NULL DEFAULT '{}', + version_major INTEGER NOT NULL DEFAULT 1, + version_minor INTEGER NOT NULL DEFAULT 0, + version_revision INTEGER NOT NULL DEFAULT 0, + is_current BOOLEAN NOT NULL DEFAULT TRUE -- Only one current per entity_uuid +); + +CREATE INDEX idx_realized_tenant ON realized_entities(tenant_uuid); +CREATE INDEX idx_realized_entity ON realized_entities(entity_uuid); +CREATE INDEX idx_realized_lifecycle ON realized_entities(tenant_uuid, lifecycle_state); +CREATE INDEX idx_realized_resource_type ON realized_entities(tenant_uuid, resource_type); +CREATE INDEX idx_realized_current ON realized_entities(entity_uuid, is_current) WHERE is_current = TRUE; + +-- ─── Operations (LRO tracking) ──────────────────────────────────────────────── +-- Spec ref: doc 25 §2 (Request Orchestrator), doc 49 §7.1 (operation_uuid issuer) +-- operation_uuid == request_uuid, issued by API Gateway at ingress. + +CREATE TABLE operations ( + operation_uuid UUID PRIMARY KEY, -- == request_uuid, issued by API Gateway + tenant_uuid UUID NOT NULL REFERENCES tenants(tenant_uuid), + resource_uuid UUID, -- AEP convention: the resource this operation acts on + operation_type VARCHAR(64) NOT NULL, -- e.g. create_request, rehydration, discovery + status VARCHAR(32) NOT NULL DEFAULT 'INITIATED' + CHECK (status IN ('INITIATED', 'ASSEMBLING', 'POLICY_EVALUATION', + 'PLACEMENT', 'DISPATCHED', 'PROVISIONING', + 'OPERATIONAL', 'FAILED', 'CANCELLED')), + actor_uuid UUID REFERENCES actors(actor_uuid), + catalog_item_uuid UUID, + resource_type VARCHAR(256), + metadata JSONB NOT NULL DEFAULT '{}', + score NUMERIC(5,2), + selected_provider_uuid UUID, + error_code VARCHAR(128), + error_message TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + completed_at TIMESTAMPTZ +); + +CREATE INDEX idx_operations_tenant ON operations(tenant_uuid); +CREATE INDEX idx_operations_status ON operations(tenant_uuid, status); +CREATE INDEX idx_operations_resource ON operations(resource_uuid); + +-- ─── Audit Records ──────────────────────────────────────────────────────────── +-- Spec ref: DCM data model doc 16 (Universal Audit), doc 49 §3 (Hash Chain) +-- Implements: Tamper-evident hash chain with SHA-256 + +CREATE TABLE audit_records ( + record_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + record_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(), + entity_uuid UUID, -- Subject entity (if applicable) + entity_type VARCHAR(64), + tenant_uuid UUID REFERENCES tenants(tenant_uuid), + action VARCHAR(128) NOT NULL, -- Closed vocabulary + -- WHO + immediate_actor_uuid UUID, + immediate_actor_type VARCHAR(32), + authorized_by_uuid UUID, + session_uuid UUID, + signer_uuid UUID NOT NULL, -- Service or actor that signed this leaf + signer_type VARCHAR(16) NOT NULL CHECK (signer_type IN ('service', 'actor', 'provider')), + -- WHAT + subject_handle VARCHAR(512), + stage VARCHAR(64), -- Pipeline stage (intent_submitted, layer_applied, etc.) + source VARCHAR(256), -- Which layer/policy/service + source_type VARCHAR(64), -- layer_merge, policy_gatekeeper, policy_transformation, etc. + decision VARCHAR(32), -- allow, deny, applied, resolved, etc. + -- CHAIN OF CUSTODY — payload integrity + input_payload_hash VARCHAR(64), -- SHA-256 of payload BEFORE this mutation + output_payload_hash VARCHAR(64), -- SHA-256 of payload AFTER this mutation + context_hash VARCHAR(64), -- Evaluation context hash (policy stages only) + -- FIELD-LEVEL DETAIL (mutation/field granularity only) + fields_changed JSONB, -- Array of field paths changed (mutation+) + field_mutations JSONB, -- Per-field old/new hashes (field granularity only) + -- MERKLE TREE (RFC 9162 pattern) + leaf_index BIGINT NOT NULL, -- Position in global Merkle tree + record_hash VARCHAR(64) NOT NULL, -- SHA-256 of record content + previous_leaf_hash VARCHAR(64), -- Hash of previous leaf for this request + signature TEXT NOT NULL, -- Ed25519/ECDSA-P256 over all fields + -- METADATA + dcm_version VARCHAR(32), + request_uuid UUID, + policy_uuid UUID, + provider_uuid UUID +); + +CREATE INDEX idx_audit_entity ON audit_records(entity_uuid, leaf_index); +CREATE INDEX idx_audit_tenant ON audit_records(tenant_uuid, record_timestamp); +CREATE INDEX idx_audit_action ON audit_records(action, record_timestamp); +CREATE INDEX idx_audit_request ON audit_records(request_uuid, leaf_index); +CREATE INDEX idx_audit_leaf ON audit_records(leaf_index); + +-- Signed Tree Heads (RFC 9162 pattern) +CREATE TABLE signed_tree_heads ( + sth_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tree_size BIGINT NOT NULL, -- Number of leaves when STH was computed + timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(), + sha256_root_hash VARCHAR(64) NOT NULL, -- Merkle root hash + signature TEXT NOT NULL, -- Signed by DCM audit signing key + signing_key_id VARCHAR(128) NOT NULL -- Identifies which key signed this STH +); + +CREATE INDEX idx_sth_tree_size ON signed_tree_heads(tree_size); + +-- Merkle tree intermediate nodes (materialized mode — optional) +CREATE TABLE merkle_tree_nodes ( + level INT NOT NULL, -- Tree level (0 = leaves) + position BIGINT NOT NULL, -- Position at this level + hash VARCHAR(64) NOT NULL, -- SHA-256 hash of this node + PRIMARY KEY (level, position) +); + +-- Audit table is append-only — enforce via policy +REVOKE UPDATE, DELETE ON audit_records FROM dcm_app; +GRANT INSERT, SELECT ON audit_records TO dcm_app; +GRANT INSERT, SELECT ON audit_records TO dcm_audit; + +-- ─── Providers ──────────────────────────────────────────────────────────────── + +CREATE TABLE providers ( + provider_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + handle VARCHAR(128) UNIQUE NOT NULL, + display_name VARCHAR(256) NOT NULL, + provider_type VARCHAR(64) NOT NULL + CHECK (provider_type IN ('service_provider', 'information_provider', + 'meta_provider', 'auth_provider', + 'peer_dcm', 'process_provider')), + status VARCHAR(32) NOT NULL DEFAULT 'PENDING' + CHECK (status IN ('PENDING', 'ACTIVE', 'SUSPENDED', 'DEREGISTERED', 'SANDBOX')), + endpoint VARCHAR(512) NOT NULL, -- mTLS endpoint URL + public_key_pem TEXT, + capabilities JSONB NOT NULL DEFAULT '{}', + supported_resource_types JSONB NOT NULL DEFAULT '[]', + sovereignty_declarations JSONB NOT NULL DEFAULT '[]', + registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + last_health_check TIMESTAMPTZ, + health_status VARCHAR(32) DEFAULT 'UNKNOWN' +); + +CREATE INDEX idx_providers_status ON providers(status); +CREATE INDEX idx_providers_type ON providers(provider_type, status); + +-- ─── Service Catalog ────────────────────────────────────────────────────────── + +CREATE TABLE catalog_items ( + catalog_item_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + handle VARCHAR(128) UNIQUE NOT NULL, + display_name VARCHAR(256) NOT NULL, + description TEXT, + resource_type VARCHAR(256) NOT NULL, + provider_uuid UUID REFERENCES providers(provider_uuid), + field_schema JSONB NOT NULL DEFAULT '{}', -- JSON Schema for request fields + cost_estimate JSONB, + visibility_policy JSONB NOT NULL DEFAULT '{}', -- RBAC / group visibility rules + status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', + version_major INTEGER NOT NULL DEFAULT 1, + version_minor INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_catalog_status ON catalog_items(status); +CREATE INDEX idx_catalog_resource_type ON catalog_items(resource_type); + +-- ─── Row-Level Security (STI-001, STI-002) ──────────────────────────────────── +-- Enforce tenant isolation at storage layer. +-- dcm_app cannot query across tenant boundaries. +-- dcm_admin bypasses RLS for platform admin operations (separately audited). + +ALTER TABLE realized_entities ENABLE ROW LEVEL SECURITY; +ALTER TABLE operations ENABLE ROW LEVEL SECURITY; +ALTER TABLE audit_records ENABLE ROW LEVEL SECURITY; + +CREATE POLICY tenant_isolation_realized + ON realized_entities + FOR ALL + TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); + +CREATE POLICY tenant_isolation_operations + ON operations + FOR ALL + TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); + +CREATE POLICY tenant_isolation_audit + ON audit_records + FOR SELECT + TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); + +-- dcm_admin bypasses RLS (explicit grant) +ALTER TABLE realized_entities FORCE ROW LEVEL SECURITY; +ALTER TABLE operations FORCE ROW LEVEL SECURITY; +GRANT ALL ON ALL TABLES IN SCHEMA public TO dcm_admin; +ALTER ROLE dcm_admin BYPASSRLS; + +-- ─── Triggers ──────────────────────────────────────────────────────────────── + +-- Auto-set updated_at +CREATE OR REPLACE FUNCTION update_updated_at() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER operations_updated_at + BEFORE UPDATE ON operations + FOR EACH ROW EXECUTE FUNCTION update_updated_at(); + +-- Enforce append-only on audit (belt-and-suspenders beyond REVOKE) +CREATE OR REPLACE FUNCTION prevent_audit_modification() +RETURNS TRIGGER AS $$ +BEGIN + RAISE EXCEPTION 'Audit records are immutable. Record UUID: %', OLD.record_uuid; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER audit_immutable + BEFORE UPDATE OR DELETE ON audit_records + FOR EACH ROW EXECUTE FUNCTION prevent_audit_modification(); + +-- ─── Intent Domain (doc 51 §2.3) ──────────────────────────────────────────── +-- Append-only. Raw consumer declarations. Never modified after write. + +CREATE TABLE intent_records ( + intent_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + entity_uuid UUID NOT NULL, + tenant_uuid UUID NOT NULL REFERENCES tenants(tenant_uuid), + catalog_item_uuid UUID NOT NULL, + submitted_by UUID NOT NULL, + submitted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + submitted_via VARCHAR(32) NOT NULL + CHECK (submitted_via IN ('api', 'gitops', 'cli', 'message_bus')), + intent_version INTEGER NOT NULL DEFAULT 1, + fields JSONB NOT NULL DEFAULT '{}', + provenance JSONB NOT NULL DEFAULT '{}' +); + +CREATE INDEX idx_intent_entity ON intent_records(entity_uuid, intent_version); +CREATE INDEX idx_intent_tenant ON intent_records(tenant_uuid, submitted_at); +REVOKE UPDATE, DELETE ON intent_records FROM dcm_app; + +-- ─── Requested Domain (doc 51 §2.3) ───────────────────────────────────────── +-- Append-only. Assembled, policy-evaluated, placed payloads. + +CREATE TABLE requested_records ( + requested_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + entity_uuid UUID NOT NULL, + tenant_uuid UUID NOT NULL REFERENCES tenants(tenant_uuid), + operation_uuid UUID NOT NULL REFERENCES operations(operation_uuid), + intent_uuid UUID NOT NULL REFERENCES intent_records(intent_uuid), + resource_type VARCHAR(256) NOT NULL, + provider_uuid UUID NOT NULL, + assembled_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + assembled_payload JSONB NOT NULL DEFAULT '{}', + layer_sources JSONB NOT NULL DEFAULT '[]', + policy_results JSONB NOT NULL DEFAULT '{}', + placement_result JSONB NOT NULL DEFAULT '{}', + provenance JSONB NOT NULL DEFAULT '{}' +); + +CREATE INDEX idx_requested_entity ON requested_records(entity_uuid); +CREATE INDEX idx_requested_tenant ON requested_records(tenant_uuid); +CREATE INDEX idx_requested_operation ON requested_records(operation_uuid); +REVOKE UPDATE, DELETE ON requested_records FROM dcm_app; + +-- ─── Discovered Domain (doc 51 §2.3) ──────────────────────────────────────── +-- Ephemeral snapshots from provider discovery runs. + +CREATE TABLE discovered_records ( + discovery_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + entity_uuid UUID, + tenant_uuid UUID REFERENCES tenants(tenant_uuid), + provider_uuid UUID NOT NULL, + resource_type VARCHAR(256) NOT NULL, + discovered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + discovery_run_uuid UUID NOT NULL, + discovered_fields JSONB NOT NULL DEFAULT '{}', + provider_native_id VARCHAR(512), + match_confidence VARCHAR(16) DEFAULT 'exact' + CHECK (match_confidence IN ('exact', 'high', 'low', 'unmatched')) +); + +CREATE INDEX idx_discovered_entity ON discovered_records(entity_uuid, discovered_at); +CREATE INDEX idx_discovered_run ON discovered_records(discovery_run_uuid); +CREATE INDEX idx_discovered_orphans ON discovered_records(entity_uuid) WHERE entity_uuid IS NULL; + +-- ─── Pipeline Events (doc 51 §2.3) ────────────────────────────────────────── +-- Append-only event log. LISTEN/NOTIFY for real-time pipeline routing. + +CREATE TABLE pipeline_events ( + event_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + event_type VARCHAR(128) NOT NULL, + entity_uuid UUID, + request_uuid UUID, + tenant_uuid UUID, + actor_uuid UUID, + payload JSONB NOT NULL DEFAULT '{}', + published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + consumed_by JSONB NOT NULL DEFAULT '[]', + consumed_at TIMESTAMPTZ +); + +CREATE INDEX idx_events_type ON pipeline_events(event_type, published_at); +CREATE INDEX idx_events_entity ON pipeline_events(entity_uuid, published_at); +CREATE INDEX idx_events_unconsumed ON pipeline_events(event_type, published_at) + WHERE consumed_at IS NULL; +REVOKE UPDATE, DELETE ON pipeline_events FROM dcm_app; + +-- Notify trigger for real-time pipeline routing +CREATE OR REPLACE FUNCTION notify_pipeline_event() RETURNS TRIGGER AS $$ +BEGIN + PERFORM pg_notify('dcm_pipeline', json_build_object( + 'event_uuid', NEW.event_uuid, + 'event_type', NEW.event_type, + 'entity_uuid', NEW.entity_uuid + )::text); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER pipeline_event_notify + AFTER INSERT ON pipeline_events + FOR EACH ROW EXECUTE FUNCTION notify_pipeline_event(); + +-- ─── RLS on new tables ────────────────────────────────────────────────────── + +ALTER TABLE intent_records ENABLE ROW LEVEL SECURITY; +ALTER TABLE requested_records ENABLE ROW LEVEL SECURITY; +ALTER TABLE discovered_records ENABLE ROW LEVEL SECURITY; +ALTER TABLE pipeline_events ENABLE ROW LEVEL SECURITY; + +CREATE POLICY tenant_isolation_intent ON intent_records + FOR ALL TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); +CREATE POLICY tenant_isolation_requested ON requested_records + FOR ALL TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); +CREATE POLICY tenant_isolation_discovered ON discovered_records + FOR ALL TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); +CREATE POLICY tenant_isolation_events ON pipeline_events + FOR SELECT TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); + +-- ─── Secrets (doc 31, doc 51 §4.2) ─────────────────────────────────────────── +-- Internal secrets management using envelope encryption. +-- Each secret value is AES-256-GCM encrypted with a per-secret DEK. +-- DEKs are encrypted with the master KEK from the deployment environment. +-- External mode (Vault) bypasses this table entirely. + +CREATE TABLE secrets ( + secret_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tenant_uuid UUID REFERENCES tenants(tenant_uuid), -- null for system secrets + secret_path VARCHAR(512) NOT NULL, -- hierarchical path (e.g., providers/{uuid}/credentials) + secret_type VARCHAR(64) NOT NULL + CHECK (secret_type IN ('credential', 'encryption_key', + 'signing_key', 'certificate', 'generic')), + encrypted_value BYTEA NOT NULL, -- AES-256-GCM encrypted + encrypted_dek BYTEA NOT NULL, -- DEK encrypted with KEK + encryption_algorithm VARCHAR(32) NOT NULL DEFAULT 'AES-256-GCM', + kek_id VARCHAR(128) NOT NULL, -- identifies which KEK encrypted the DEK + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + rotated_at TIMESTAMPTZ, + expires_at TIMESTAMPTZ, + lifecycle_state VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' + CHECK (lifecycle_state IN ('ACTIVE', 'ROTATING', 'REVOKED', 'EXPIRED')), + UNIQUE(secret_path) +); + +CREATE INDEX idx_secrets_tenant ON secrets(tenant_uuid); +CREATE INDEX idx_secrets_path ON secrets(secret_path); +CREATE INDEX idx_secrets_expiry ON secrets(expires_at) WHERE lifecycle_state = 'ACTIVE'; + +-- Secrets table: no UPDATE on encrypted_value (rotation creates new row, revokes old) +-- SELECT restricted to dcm_app via RLS +ALTER TABLE secrets ENABLE ROW LEVEL SECURITY; +CREATE POLICY tenant_isolation_secrets ON secrets + FOR ALL TO dcm_app + USING (tenant_uuid IS NULL OR tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); + +-- ─── Subscriptions (doc 50) ────────────────────────────────────────────────── + +CREATE TABLE subscriptions ( + subscription_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tenant_uuid UUID NOT NULL REFERENCES tenants(tenant_uuid), + handle VARCHAR(256) NOT NULL, + display_name VARCHAR(256) NOT NULL, + catalog_item_uuid UUID NOT NULL, + resource_type VARCHAR(256) NOT NULL, + provider_uuid UUID NOT NULL, + lifecycle_state VARCHAR(32) NOT NULL DEFAULT 'PENDING' + CHECK (lifecycle_state IN ( + 'PENDING', 'PROVISIONING', 'ACTIVE', + 'SUSPENDED', 'RENEWAL_PENDING', 'TIER_CHANGE_PENDING', + 'EXPIRED', 'CANCELLED', 'DECOMMISSIONING', 'DECOMMISSIONED' + )), + terms JSONB NOT NULL DEFAULT '{}', + entitlements JSONB NOT NULL DEFAULT '{}', + update_channels JSONB NOT NULL DEFAULT '[]', + terms_version VARCHAR(32) NOT NULL DEFAULT '1.0.0', + started_at TIMESTAMPTZ, + expires_at TIMESTAMPTZ, + grace_period INTERVAL NOT NULL DEFAULT '30 days', + auto_renew BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + UNIQUE(tenant_uuid, handle) +); + +CREATE INDEX idx_subscriptions_tenant ON subscriptions(tenant_uuid, lifecycle_state); +CREATE INDEX idx_subscriptions_provider ON subscriptions(provider_uuid); +CREATE INDEX idx_subscriptions_expiry ON subscriptions(expires_at) WHERE lifecycle_state = 'ACTIVE'; + +CREATE TABLE subscription_entities ( + subscription_uuid UUID NOT NULL REFERENCES subscriptions(subscription_uuid), + entity_uuid UUID NOT NULL, + role VARCHAR(64) NOT NULL DEFAULT 'managed', + bound_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (subscription_uuid, entity_uuid) +); + +CREATE TABLE subscription_updates ( + update_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + subscription_uuid UUID NOT NULL REFERENCES subscriptions(subscription_uuid), + entity_uuid UUID NOT NULL, + provider_uuid UUID NOT NULL, + channel VARCHAR(64) NOT NULL, + status VARCHAR(32) NOT NULL DEFAULT 'PENDING' + CHECK (status IN ('PENDING', 'APPROVED', 'REJECTED', + 'APPLIED', 'FAILED', 'EXPIRED')), + update_payload JSONB NOT NULL DEFAULT '{}', + submitted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + decided_at TIMESTAMPTZ, + decided_by UUID, + applied_at TIMESTAMPTZ, + auto_applied BOOLEAN NOT NULL DEFAULT false +); + +CREATE INDEX idx_sub_updates_subscription ON subscription_updates(subscription_uuid, status); +CREATE INDEX idx_sub_updates_pending ON subscription_updates(status, submitted_at) WHERE status = 'PENDING'; + +ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY; +ALTER TABLE subscription_entities ENABLE ROW LEVEL SECURITY; +ALTER TABLE subscription_updates ENABLE ROW LEVEL SECURITY; + +CREATE POLICY tenant_isolation_subscriptions ON subscriptions + FOR ALL TO dcm_app + USING (tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid); +CREATE POLICY tenant_isolation_sub_entities ON subscription_entities + FOR ALL TO dcm_app + USING (subscription_uuid IN ( + SELECT subscription_uuid FROM subscriptions + WHERE tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid + )); +CREATE POLICY tenant_isolation_sub_updates ON subscription_updates + FOR ALL TO dcm_app + USING (subscription_uuid IN ( + SELECT subscription_uuid FROM subscriptions + WHERE tenant_uuid = current_setting('dcm.current_tenant_uuid')::uuid + )); diff --git a/content/docs/schemas/README.md b/content/docs/schemas/README.md new file mode 100644 index 0000000..d0af7e9 --- /dev/null +++ b/content/docs/schemas/README.md @@ -0,0 +1,176 @@ +# DCM Normative Data Specifications + +**Status:** Draft — Ready for implementation feedback +**Version:** 1.0.0 +**GitHub:** https://github.com/dcm-project + +These are the machine-readable normative specifications for the DCM data model. They are the +code-generation layer — SDK authors, server stub generators, and validation middleware should +use these files, not the narrative documentation in `data-model/`. + +The narrative documentation remains authoritative for intent and rationale. These schemas are +authoritative for field names, types, constraints, and API shapes. + +--- + +## Files + +### JSON Schema (entity, policy, provider, and event data shapes) + +| File | Describes | Types | Source docs | +|------|-----------|-------|-------------| +| `dcm-common.json` | Shared primitive types reused across all schemas | 17 | `00-foundations.md`, `16-universal-audit.md` | +| `entities/dcm-entities.json` | All three entity types (Infrastructure Resource, Composite Resource, Process Resource) | 18 | `01-entity-types.md` | +| `policies/dcm-policies.json` | All seven policy types and their output schemas | 31 | `B-policy-contract.md`, `14-policy-profiles.md` | +| `providers/dcm-providers.json` | Provider base contract and all 11 capability extensions | 19 | `A-provider-contract.md`, `dcm-registration-spec.md` | +| `events/dcm-events.json` | Base event envelope + all 82 event-specific payload schemas | 75 | `33-event-catalog.md` | +| `resource-types/resource-type-spec-template.json` | Resource Type Spec schema + authoring guide + Compute.VirtualMachine example | 3 | `05-resource-type-hierarchy.md`, `20-registry-governance.md` | + +### OpenAPI 3.1 (API shapes) + +| File | Describes | Paths | Schemas | Source docs | +|------|-----------|-------|---------|-------------| +| `openapi/dcm-consumer-api.yaml` | Consumer API — application teams and Tenant owners | 59 | 31 | `consumer-api-spec.md` | +| `openapi/dcm-admin-api.yaml` | Admin API — platform engineers and SREs | 41 | 19 | `dcm-admin-api-spec.md` | +| `openapi/dcm-operator-api.yaml` | Operator Interface Services API — provider-facing (DCM is client) | 5 | 12 | `dcm-operator-interface-spec.md` | +| `openapi/dcm-provider-callback-api.yaml` | Provider Callback API — endpoints DCM exposes for operators to call (registration, realized state push, interim status, update notifications, lifecycle events) | 7 | 11 | `dcm-operator-interface-spec.md` | + +## AEP Alignment + +The OpenAPI specifications follow [AEP (API Enhancement Proposals)](https://aep.dev) conventions +in three areas: + +**1. Custom methods (AEP-136)** — Actions on resources use colon syntax rather than slash-separated +verb paths. For example: `POST /resources/{name}:suspend` rather than `POST /resources/{name}/suspend`. +This applies to all state-transition and action operations across the Consumer and Admin APIs. + +**2. Long-Running Operations (AEP-151)** — Async operations that produce a trackable result return +an `Operation` resource with a stable `name` field (poll URL), a `done` boolean, and either a +`response` or `error` field when complete. Operations that are genuinely fire-and-forget (capacity +reports, interim status, lifecycle events) retain `202 Accepted` without an Operation body. + +**3. Pagination (AEP-158)** — List endpoints use `page_size` and `page_token` query parameters. +Responses include a `next_page_token` field (empty string when no further pages exist). + +**What was deliberately not aligned:** + +- **Resource names** — DCM retains UUIDs as the primary identifier. AEP's hierarchical + `name` strings (`tenants/{t}/resources/{r}`) are not used because DCM's UUID immutability + guarantee across ownership transfers and provider migrations is more architecturally + significant than AEP naming convention compliance. +- **Timestamp field names** — `created_at`/`updated_at` are retained in API responses rather + than `create_time`/`update_time`. Renaming these would require a data model change (the fields + exist in the entity schemas and Realized State records) with no functional benefit. + + +--- + +## Schema Design Principles + +**1. `$ref` over duplication.** Types defined in `dcm-common.json` are referenced by `$ref` from +all other schemas. The common types — uuid, handle, semver, iso8601_datetime, artifact_metadata, +sovereignty_declaration — are defined once. + +**2. Discriminated unions via `entity_type` / `policy_type`.** The `dcm_entity` and `dcm_policy` +union types use the `entity_type` and `policy_type` fields as discriminators. Validation tools +that support OpenAPI 3.1 discriminators can route to the correct sub-schema automatically. + +**3. `additionalProperties: false` on all closed shapes.** Entities and provider registrations +use `additionalProperties: false` where the shape is fully known. Resource-type-specific fields +(which vary by resource type) use `additionalProperties: true` on the `fields` object only. + +**4. Field-level provenance as a pattern.** The `field_provenance` type in `dcm-common.json` +documents the provenance metadata that any data field may carry as a sibling `_provenance` key. +This is a pattern, not enforced by schema (because JSON Schema cannot express "every field may +have a sibling `_fieldname_provenance` key" without enumeration). + +**5. ISO 8601 throughout.** All datetime fields use `format: date-time`. All duration fields use +`format: iso8601_duration` with the pattern `P...`. All country codes use `pattern: [A-Z]{2}`. + +**6. Closed vocabularies as enums.** All closed vocabularies from the narrative documentation +are expressed as `enum` arrays. The closed vocabulary for payload_type (policy match conditions), +lifecycle states, ownership models, provider types, and credential types are all enumerated. + +--- + +## Usage + +### Validation (Python) + +```python +import json +import jsonschema +from pathlib import Path + +schema_dir = Path("dcm-docs/schemas") + +# Load schemas +common = json.loads((schema_dir / "dcm-common.json").read_text()) +entities = json.loads((schema_dir / "entities/dcm-entities.json").read_text()) + +# Build a resolver that handles $ref across files +store = { + "https://dcm-project.io/schemas/common/v1": common, + "https://dcm-project.io/schemas/entities/v1": entities, +} +resolver = jsonschema.RefResolver.from_schema(entities, store=store) + +# Validate an entity +entity = { ... } +jsonschema.validate(entity, entities, resolver=resolver) +``` + +### Validation (Go) + +```go +import ( + "github.com/santhosh-tekuri/jsonschema/v5" + _ "github.com/santhosh-tekuri/jsonschema/v5/httploader" +) + +compiler := jsonschema.NewCompiler() +compiler.AddResource("dcm-common.json", openFile("schemas/dcm-common.json")) +compiler.AddResource("entities/dcm-entities.json", openFile("schemas/entities/dcm-entities.json")) + +schema, err := compiler.Compile("entities/dcm-entities.json") +if err != nil { panic(err) } + +var entity interface{} +json.Unmarshal(data, &entity) +if err := schema.Validate(entity); err != nil { + fmt.Println(err) +} +``` + +### OpenAPI Code Generation + +```bash +# Generate Go server stubs from Consumer API +oapi-codegen -package api -generate server,types \ + schemas/openapi/dcm-consumer-api.yaml > pkg/api/consumer.gen.go + +# Generate Python client from Admin API +openapi-python-client generate \ + --path schemas/openapi/dcm-admin-api.yaml \ + --output dcm-admin-client/ + +# Generate TypeScript types +openapi-typescript schemas/openapi/dcm-consumer-api.yaml \ + --output src/types/dcm-consumer-api.ts +``` + +--- + +## What Is Not Yet Here + +### Resource Type Field Schemas (per resource type) + +The `resource-types/resource-type-spec-template.json` defines the schema for the Resource Type Spec envelope and provides a worked `Compute.VirtualMachine` example. Individual resource type field schemas are owned by Service Providers and published to the Resource Type Registry — DCM does not define them centrally. Service Providers should use the template and authoring guide to produce their own schemas. + +### Resource Type Field Schemas (per resource type) + +The `resource-types/resource-type-spec-template.json` defines the schema for the Resource Type Spec envelope and includes a complete `Compute.VirtualMachine` worked example. Individual resource type field schemas are owned by Service Providers and published to the Resource Type Registry per the template authoring guide. + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* diff --git a/content/docs/schemas/dcm-admin-api.yaml b/content/docs/schemas/dcm-admin-api.yaml new file mode 100644 index 0000000..37b2c72 --- /dev/null +++ b/content/docs/schemas/dcm-admin-api.yaml @@ -0,0 +1,2094 @@ +openapi: "3.1.0" + +info: + title: DCM Admin API + version: "1.0.0" + description: | + The DCM Admin API is used by platform engineers, SREs, and system administrators to manage + the DCM control plane itself. All endpoints require platform admin or higher authority. + + **Key principles:** + - Requires `verified` or `authorized` tier authority for most operations + - Mutating operations against the governance model (tier registry, profiles) require + `authorized` tier with a declared DCMGroup quorum + - All actions produce audit records + - Destructive or degrading operations have explicit confirmation steps + + + + **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions: + custom methods use colon syntax (`POST /resources/{name}:suspend`), + async operations return an `Operation` resource (AEP-136 LRO), + and list pagination uses `page_size`/`page_token` parameters. + + contact: + name: DCM Project + url: https://github.com/dcm-project + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + +servers: + - url: https://{dcm-host}/ + description: DCM Control Plane + variables: + dcm-host: + default: dcm.example.com + +security: + - BearerAuth: [] + +tags: + - name: health + description: DCM control plane health and readiness + - name: tenants + description: Tenant lifecycle management + - name: actors + description: Actor session and risk management + - name: providers + description: Provider registration approval and management + - name: accreditations + description: Provider accreditation review + - name: discovery + description: Brownfield discovery scheduling and monitoring + - name: drift + description: Drift orphan management and recovery decisions + - name: quotas + description: Tenant quota management + - name: search + description: Search index operations + - name: bootstrap + description: Bootstrap credential management + - name: scoring + description: Risk scoring model configuration + - name: approvals + description: Platform-level approval management + - name: tier-registry + description: Authority tier registry management + +paths: + + # ─── HEALTH ─────────────────────────────────────────────────────────────── + + /livez: + get: + tags: [health] + operationId: liveness + summary: Kubernetes-style liveness probe + security: [] + responses: + "200": { description: Control plane process is alive } + "503": { description: Control plane unhealthy } + + /readyz: + get: + tags: [health] + operationId: readiness + summary: Kubernetes-style readiness probe + security: [] + responses: + "200": { description: Control plane ready to serve requests } + "503": { description: Control plane not ready (stores unavailable, bootstrap incomplete, etc.) } + + /metrics: + get: + tags: [health] + operationId: metrics + summary: Prometheus metrics endpoint + security: [] + responses: + "200": + description: Prometheus text format metrics + content: + text/plain: + schema: { type: string } + + /api/v1/admin/health: + get: + tags: [health] + operationId: getAdminHealth + summary: Detailed control plane health including component and provider status + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/AdminHealthResponse" } + + # ─── TENANTS ────────────────────────────────────────────────────────────── + + /api/v1/admin/tenants: + get: + tags: [tenants] + operationId: listTenants + summary: List all Tenants + parameters: + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + - name: status + in: query + schema: { type: string, enum: [active, suspended, decommissioned] } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/TenantList" } + post: + tags: [tenants] + operationId: createTenant + summary: Create a new Tenant + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/TenantCreate" } + responses: + "201": + content: + application/json: + schema: { $ref: "#/components/schemas/Tenant" } + "409": { description: Tenant handle already exists } + + /api/v1/admin/tenants/{tenant_uuid}:suspend: + post: + tags: [tenants] + operationId: suspendTenant + summary: Suspend a Tenant (blocks all new requests; active resources remain) + parameters: + - { $ref: "#/components/parameters/tenant_uuid" } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + responses: + "200": { description: Tenant suspended } + "409": { description: Tenant already suspended or decommissioned } + + /api/v1/admin/tenants/{tenant_uuid}:reinstate: + post: + tags: [tenants] + operationId: reinstateTenant + summary: Reinstate a suspended Tenant + parameters: + - { $ref: "#/components/parameters/tenant_uuid" } + responses: + "200": { description: Tenant reinstated } + + /api/v1/admin/tenants/{tenant_uuid}: + delete: + tags: [tenants] + operationId: decommissionTenant + summary: Decommission a Tenant (must have zero active resources) + parameters: + - { $ref: "#/components/parameters/tenant_uuid" } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason, confirmation] + properties: + reason: { type: string } + confirmation: { type: string, const: "DECOMMISSION", description: "Must be the string 'DECOMMISSION'" } + responses: + "200": + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: { $ref: "#/components/schemas/Operation" } + "409": { description: Tenant has active resources — cannot decommission } + + /api/v1/admin/tenants/{tenant_uuid}/quotas: + get: + tags: [quotas] + operationId: getTenantQuotas + summary: Get quota configuration for a Tenant + parameters: + - { $ref: "#/components/parameters/tenant_uuid" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/TenantQuotas" } + + /api/v1/admin/tenants/{tenant_uuid}/quotas/{resource_type}: + put: + tags: [quotas] + operationId: setTenantQuota + summary: Set or update quota for a specific resource type on a Tenant + parameters: + - { $ref: "#/components/parameters/tenant_uuid" } + - name: resource_type + in: path + required: true + schema: { type: string } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/QuotaUpdate" } + responses: + "200": { description: Quota updated } + + # ─── ACTORS ─────────────────────────────────────────────────────────────── + + /api/v1/admin/actors/{actor_uuid}:revoke-sessions: + post: + tags: [actors] + operationId: revokeActorSessions + summary: Revoke all active sessions for an actor (emergency session revocation) + parameters: + - { $ref: "#/components/parameters/actor_uuid" } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + responses: + "200": + content: + application/json: + schema: + type: object + properties: + sessions_revoked: { type: integer } + + /api/v1/admin/actors/{actor_uuid}/sessions: + get: + tags: [actors] + operationId: getActorSessions + summary: List all active sessions for an actor + parameters: + - { $ref: "#/components/parameters/actor_uuid" } + responses: + "200": + content: + application/json: + schema: + type: object + properties: + sessions: { type: array, items: { type: object } } + + /api/v1/admin/actors/{actor_uuid}/risk-history: + get: + tags: [actors, scoring] + operationId: getActorRiskHistory + summary: Get risk score history for an actor (full detail for admin) + parameters: + - { $ref: "#/components/parameters/actor_uuid" } + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/RiskHistory" } + + /api/v1/admin/actors/{actor_uuid}/risk-history:reset: + post: + tags: [actors] + operationId: resetActorRiskHistory + summary: Reset risk score history for an actor (requires verified tier) + parameters: + - { $ref: "#/components/parameters/actor_uuid" } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + responses: + "200": { description: Risk history reset } + + # ─── PROVIDERS ──────────────────────────────────────────────────────────── + + /api/v1/admin/location-types: + get: + tags: [locations] + operationId: listLocationTypes + summary: List registered location types (standard and custom) + security: + - bearerAuth: [] + responses: + "200": + description: All registered location types + content: + application/json: + schema: + type: object + properties: + location_types: + type: array + items: { type: object, additionalProperties: true } + post: + tags: [locations] + operationId: registerCustomLocationType + summary: Register a custom location type + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: { type: object, additionalProperties: true } + responses: + "200": + description: Operation initiated + content: + application/json: + schema: { $ref: "#/components/schemas/Operation" } + + /api/v1/admin/locations: + get: + tags: [locations] + operationId: adminListLocations + summary: List all location nodes (admin — no entitlement filter) + parameters: + - { name: level, in: query, schema: { type: string } } + - { name: page_size, in: query, schema: { type: integer, default: 100 } } + - { name: page_token, in: query, schema: { type: string } } + security: + - bearerAuth: [] + responses: + "200": + description: All location nodes + content: + application/json: + schema: { type: object, additionalProperties: true } + + /api/v1/admin/locations/{location_uuid}: + patch: + tags: [locations] + operationId: updateLocationCapacity + summary: Update mutable location fields (e.g., rack_units_available) + parameters: + - { name: location_uuid, in: path, required: true, schema: { type: string, format: uuid } } + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: { type: object, additionalProperties: true } + responses: + "200": + description: Location updated + content: + application/json: + schema: { type: object, additionalProperties: true } + "404": { $ref: "#/components/responses/NotFound" } + + + /api/v1/admin/providers: + get: + tags: [providers] + operationId: listProviders + summary: List all registered providers + parameters: + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + - name: provider_type + in: query + schema: { type: string } + - name: status + in: query + schema: { type: string } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderList" } + + /api/v1/admin/providers/pending: + get: + tags: [providers] + operationId: listPendingProviders + summary: List providers awaiting approval + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderList" } + + /api/v1/admin/providers/{provider_uuid}:approve: + post: + tags: [providers] + operationId: approveProvider + summary: Approve a provider registration + parameters: + - { $ref: "#/components/parameters/provider_uuid" } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + conditions: { type: string, description: "Any conditions attached to approval" } + external_reference: { type: string } + responses: + "200": { description: Provider approved and activated } + "409": { description: Provider not in PENDING_APPROVAL state } + + /api/v1/admin/providers/{provider_uuid}:reject: + post: + tags: [providers] + operationId: rejectProvider + summary: Reject a provider registration + parameters: + - { $ref: "#/components/parameters/provider_uuid" } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + responses: + "200": { description: Provider rejected } + + /api/v1/admin/providers/{provider_uuid}:suspend: + post: + tags: [providers] + operationId: suspendProvider + summary: Suspend a provider (no new requests routed; existing resources unaffected) + parameters: + - { $ref: "#/components/parameters/provider_uuid" } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + duration: { type: string, description: "ISO 8601 duration; null = indefinite" } + responses: + "200": { description: Provider suspended } + + # ─── ACCREDITATIONS ─────────────────────────────────────────────────────── + + /api/v1/admin/accreditations: + get: + tags: [accreditations] + operationId: listAccreditations + summary: List provider accreditations (pending, active, expiring soon) + parameters: + - name: status + in: query + schema: { type: string, enum: [pending, active, expired, expiring_soon] } + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/AccreditationList" } + + /api/v1/admin/accreditations/{accreditation_uuid}:approve: + post: + tags: [accreditations] + operationId: approveAccreditation + summary: Approve a submitted accreditation + parameters: + - { name: accreditation_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + external_reference: { type: string } + responses: + "200": { description: Accreditation approved } + + /api/v1/admin/accreditations/{accreditation_uuid}: + delete: + tags: [accreditations] + operationId: revokeAccreditation + summary: Revoke an active accreditation + parameters: + - { name: accreditation_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + responses: + "200": { description: Accreditation revoked; affected entities notified } + + # ─── DISCOVERY ──────────────────────────────────────────────────────────── + + /api/v1/admin/discovery:trigger: + post: + tags: [discovery] + operationId: triggerDiscovery + summary: Trigger an immediate discovery cycle for one or all providers + requestBody: + content: + application/json: + schema: + type: object + properties: + provider_uuid: { type: string, format: uuid, description: "Omit to trigger all active providers" } + scope: { type: string, enum: [full, targeted], default: full } + responses: + "202": + content: + application/json: + schema: + type: object + properties: + discovery_job_uuid: { type: string, format: uuid } + + /api/v1/admin/discovery/jobs/{discovery_job_uuid}: + get: + tags: [discovery] + operationId: getDiscoveryJob + summary: Get status of a discovery job + parameters: + - { name: discovery_job_uuid, in: path, required: true, schema: { type: string, format: uuid } } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/DiscoveryJobStatus" } + + # ─── DRIFT / ORPHANS ────────────────────────────────────────────────────── + + /api/v1/admin/orphans: + get: + tags: [drift] + operationId: listOrphans + summary: List discovered entities with no matching Requested State record + parameters: + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/OrphanList" } + + /api/v1/admin/orphans/{orphan_candidate_uuid}/resolve: + post: + tags: [drift] + operationId: resolveOrphan + summary: Resolve an orphan candidate (ingest, ignore, or decommission) + parameters: + - { name: orphan_candidate_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [resolution] + properties: + resolution: { type: string, enum: [ingest, ignore, decommission] } + target_tenant_uuid: { type: string, format: uuid, description: "Required for 'ingest'" } + reason: { type: string } + responses: + "200": { description: Orphan resolved } + + /api/v1/admin/recovery-decisions/pending: + get: + tags: [drift] + operationId: listPendingRecoveryDecisions + summary: List platform-level recovery decisions awaiting admin resolution + responses: + "200": + content: + application/json: + schema: + type: object + properties: + decisions: { type: array, items: { type: object } } + + /api/v1/admin/recovery-decisions/{recovery_decision_uuid}: + post: + tags: [drift] + operationId: resolveRecoveryDecision + summary: Resolve a platform-level recovery decision + parameters: + - { name: recovery_decision_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [decision, reason] + properties: + decision: { type: string, enum: [approve, reject, escalate] } + reason: { type: string } + responses: + "200": { description: Decision recorded } + + # ─── SEARCH INDEX ───────────────────────────────────────────────────────── + + /api/v1/admin/search-index:rebuild: + post: + tags: [search] + operationId: rebuildSearchIndex + summary: Trigger a full search index rebuild + responses: + "202": + content: + application/json: + schema: + type: object + properties: + job_uuid: { type: string, format: uuid } + + /api/v1/admin/search-index/status: + get: + tags: [search] + operationId: getSearchIndexStatus + summary: Get current search index status and last rebuild time + responses: + "200": + content: + application/json: + schema: + type: object + properties: + status: { type: string } + entity_count: { type: integer } + last_rebuilt: { type: string, format: date-time } + + # ─── BOOTSTRAP ──────────────────────────────────────────────────────────── + + /api/v1/admin/bootstrap:rotate-credential: + post: + tags: [bootstrap] + operationId: rotateBootstrapCredential + summary: Rotate the bootstrap credential (zero-day trust credential rotation) + description: | + Rotates the bootstrap credential used for initial DCM trust establishment. + Requires the current credential to be presented and records the rotation in + the Audit Store. New credential is returned once; cannot be retrieved again. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [current_credential_ref, reason] + properties: + current_credential_ref: { type: string } + reason: { type: string } + responses: + "200": + content: + application/json: + schema: + type: object + properties: + new_credential: { type: string, description: "New bootstrap credential. Shown once." } + rotated_at: { type: string, format: date-time } + + # ─── SCORING ────────────────────────────────────────────────────────────── + + /api/v1/admin/profiles/{profile_name}/scoring: + get: + tags: [scoring] + operationId: getProfileScoring + summary: Get scoring model configuration for a profile + parameters: + - { $ref: "#/components/parameters/profile_name" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/ScoringConfiguration" } + + patch: + tags: [scoring] + operationId: updateProfileScoring + summary: Update scoring configuration for a profile (threshold adjustments) + parameters: + - { $ref: "#/components/parameters/profile_name" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/ScoringConfigurationUpdate" } + responses: + "200": { description: Scoring configuration updated } + "422": { description: Configuration violates SMX-008 hard constraint (auto max_score ≤ 50) } + + /api/v1/admin/profiles/{profile_name}/scoring/overrides: + post: + tags: [scoring] + operationId: addScoringOverride + summary: Add a per-policy enforcement class override for a profile + parameters: + - { $ref: "#/components/parameters/profile_name" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/ScoringOverride" } + responses: + "201": { description: Override added } + + /api/v1/admin/scoring/audit: + get: + tags: [scoring] + operationId: getScoringAudit + summary: Get scoring evaluation audit records for review + parameters: + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + - name: actor_uuid + in: query + schema: { type: string, format: uuid } + responses: + "200": + content: + application/json: + schema: + type: object + properties: + records: { type: array, items: { type: object } } + + # ─── APPROVALS ──────────────────────────────────────────────────────────── + + /api/v1/admin/approvals/pending: + get: + tags: [approvals] + operationId: listPlatformApprovals + summary: List all platform-level approvals pending admin action + parameters: + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + - name: tier + in: query + schema: { type: string } + responses: + "200": + content: + application/json: + schema: + type: object + properties: + approvals: { type: array, items: { $ref: "#/components/schemas/ApprovalRecord" } } + + /api/v1/admin/approvals/{approval_uuid}: + get: + tags: [approvals] + operationId: getApproval + summary: Get full detail for an approval record including all decisions + parameters: + - { name: approval_uuid, in: path, required: true, schema: { type: string, format: uuid } } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/ApprovalDetail" } + + /api/v1/admin/approvals/{approval_uuid}:vote: + post: + tags: [approvals] + operationId: recordAdminApprovalVote + summary: Record a platform admin approval vote + parameters: + - { name: approval_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [decision, reason] + properties: + decision: { type: string, enum: [approve, reject, abstain] } + reason: { type: string } + external_reference: { type: string } + responses: + "200": { description: Vote recorded; approval gate re-evaluated } + + # ─── AUTHORITY TIER REGISTRY ────────────────────────────────────────────── + + /api/v1/admin/tier-registry/changes: + post: + tags: [tier-registry] + operationId: proposeTierRegistryChange + summary: Propose a change to the authority tier registry + description: | + Submits a proposed updated tier list. DCM computes the tier impact diff immediately — + comparing the proposed ordered list to the current one and classifying every changed + tier as SECURITY_DEGRADATION, SECURITY_UPGRADE, BROKEN_REFERENCE, PROFILE_GAP, or + STALE_WEIGHT. Returns the impact report UUID for inspection. + The change cannot activate until all SECURITY_DEGRADATION and BROKEN_REFERENCE items + are explicitly accepted. See 32-authority-tier-model.md Section 7. + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/TierRegistryChangeProposal" } + responses: + "202": + content: + application/json: + schema: + type: object + properties: + change_uuid: { type: string, format: uuid } + impact_report_uuid: { type: string, format: uuid } + blocking_items: { type: integer, description: "Number of SECURITY_DEGRADATION or BROKEN_REFERENCE items" } + status: { type: string, enum: [pending_review, ready_to_activate] } + + get: + tags: [tier-registry] + operationId: listTierRegistryChanges + summary: List tier registry change proposals + parameters: + - name: status + in: query + schema: { type: string, enum: [pending_review, ready_to_activate, activated, rejected] } + - { $ref: "#/components/parameters/page_size" } + - { $ref: "#/components/parameters/page_token" } + responses: + "200": + content: + application/json: + schema: + type: object + properties: + changes: { type: array, items: { type: object } } + + /api/v1/admin/tier-registry/changes/{change_uuid}/impact: + get: + tags: [tier-registry] + operationId: getTierRegistryImpact + summary: Get the full impact report for a proposed tier registry change + parameters: + - { name: change_uuid, in: path, required: true, schema: { type: string, format: uuid } } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/TierImpactReport" } + + /api/v1/admin/tier-registry/changes/{change_uuid}:accept-degradation: + post: + tags: [tier-registry] + operationId: acceptTierDegradation + summary: Accept a specific SECURITY_DEGRADATION item (requires verified tier) + description: | + Accepts a single SECURITY_DEGRADATION item identified in the impact report. + The accepting actor must be at `verified` tier or above and must provide a reason + describing what compensating controls justify the degradation. + The change cannot activate until ALL SECURITY_DEGRADATION items are accepted. + parameters: + - { name: change_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [degradation_item_uuid, reason, compensating_controls] + properties: + degradation_item_uuid: { type: string, format: uuid } + reason: { type: string, minLength: 20, description: "Why this degradation is acceptable" } + compensating_controls: { type: string, minLength: 20, description: "What compensating controls are in place" } + responses: + "200": { description: Degradation accepted; impact report updated } + "403": { description: Actor does not meet required tier (verified or above)" } + "409": { description: Item already accepted or change not in pending_review state } + + /api/v1/admin/tier-registry/changes/{change_uuid}:activate: + post: + tags: [tier-registry] + operationId: activateTierRegistryChange + summary: Activate a proposed tier registry change + description: | + Activates the proposed tier list as the new authoritative authority tier registry. + Returns 409 if any SECURITY_DEGRADATION or BROKEN_REFERENCE items remain unaccepted. + Impact report is stored in the Audit Store at activation time. + Requires `authorized` tier. + parameters: + - { name: change_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: { type: string } + responses: + "200": { description: Tier registry updated; new ordered list now active } + "403": { description: Actor does not meet required tier (authorized)" } + "409": + description: Blocking items remain unaccepted + content: + application/json: + schema: + type: object + properties: + blocking_items: { type: array, items: { type: object } } + +# ─── COMPONENTS ──────────────────────────────────────────────────────────────── + + # ── Workload Analysis (Admin — aggregate view) ──────────────────────────────── + /api/v1/admin/workload-analysis: + get: + tags: [workload-analysis] + operationId: listWorkloadProfiles + summary: List all workload profiles across all tenants (admin aggregate view) + description: | + Returns workload profiles platform-wide. Useful for capacity planning, + workload-type distribution reporting, and migration readiness assessment. + Filtered by archetype, confidence, or resource type. + parameters: + - {name: archetype, in: query, schema: {type: string, enum: [web_server, database, batch_processor, message_broker, api_gateway, cache, storage, monitoring, unknown]}} + - {name: confidence, in: query, schema: {type: string, enum: [high, medium, low, undetermined]}} + - {name: resource_type, in: query, schema: {type: string}, description: "FQN e.g. Compute.VirtualMachine"} + - {name: tenant_uuid, in: query, schema: {type: string, format: uuid}} + - {name: containerization_score_min, in: query, schema: {type: integer, minimum: 1, maximum: 10}} + - {name: page_size, in: query, schema: {type: integer, default: 50}} + - {name: page_token, in: query, schema: {type: string}} + security: [{bearerAuth: []}] + responses: + "200": + description: Workload profile list + content: + application/json: + schema: + type: object + properties: + items: + type: array + items: + type: object + properties: + entity_uuid: {type: string, format: uuid} + tenant_uuid: {type: string, format: uuid} + resource_type: {type: string} + workload_archetype: {type: string} + confidence: {type: string} + containerization_score: {type: integer} + analyzed_at: {type: string, format: date-time} + total_count: {type: integer} + archetype_distribution: + type: object + description: Count per archetype across all profiles in result set + additionalProperties: {type: integer} + next_page_token: {type: string} + "401": {$ref: "#/components/responses/Unauthorized"} + "403": {$ref: "#/components/responses/Forbidden"} + + # ── Accreditation Monitor ───────────────────────────────────────────────────── + /api/v1/admin/accreditations/{accreditation_uuid}:verify: + post: + tags: [accreditation] + operationId: triggerAccreditationVerification + summary: Trigger immediate external verification of an accreditation + parameters: + - {name: accreditation_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + requestBody: + content: + application/json: + schema: + type: object + properties: + override_reason: {type: string, description: "Required when manually overriding last_verified_at in air-gapped mode"} + responses: + "200": + description: Operation initiated + content: + application/json: + schema: {$ref: "#/components/schemas/Operation"} + "404": {$ref: "#/components/responses/NotFound"} + + /api/v1/admin/accreditations/{accreditation_uuid}:configure-webhook: + post: + tags: [accreditation] + operationId: configureAccreditationWebhook + summary: Configure a contract management webhook for Tier 3 accreditation verification + description: | + Used for BAA and DoD IL accreditations. Registers the contract management + system (DocuSign, Ironclad, etc.) to send lifecycle events to DCM when + the underlying contract is signed, amended, or terminated. + parameters: + - {name: accreditation_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [contract_system, contract_id] + properties: + contract_system: {type: string, enum: [docusign, ironclad, agiloft, custom]} + contract_id: {type: string, description: ID in the contract management system} + webhook_secret: {type: string, description: HMAC secret for webhook authentication} + responses: + "200": + description: Webhook configured; webhook_url returned for registration in contract system + content: + application/json: + schema: + type: object + properties: + accreditation_uuid: {type: string, format: uuid} + webhook_url: {type: string, format: uri, description: "Register this URL in your contract management system"} + webhook_secret_set: {type: boolean} + + /api/v1/admin/accreditations/{accreditation_uuid}/contract-event: + post: + tags: [accreditation] + operationId: receiveAccreditationContractEvent + summary: Inbound webhook — receive contract lifecycle event from contract management system + description: | + Called by contract management systems (DocuSign, Ironclad, etc.) when + the underlying BAA or authorization contract changes state. + Authenticated via HMAC signature using the webhook_secret. + parameters: + - {name: accreditation_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{webhookHmac: []}] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [contract_event_type, contract_id, effective_date] + properties: + contract_event_type: {type: string, enum: [signed, amended, terminated, renewal_due, renewed]} + contract_id: {type: string} + effective_date: {type: string, format: date-time} + details: {type: object, additionalProperties: true} + responses: + "200": + description: Event received and processed + content: + application/json: + schema: + type: object + properties: + accreditation_uuid: {type: string, format: uuid} + dcm_action_taken: {type: string, enum: [activated, pending_review, revoked, none]} + "401": {$ref: "#/components/responses/Unauthorized"} + "404": {$ref: "#/components/responses/NotFound"} + + # ── Maintenance Windows ─────────────────────────────────────────────────────── + /api/v1/admin/maintenance-windows: + get: + tags: [scheduling] + operationId: listMaintenanceWindows + summary: List declared maintenance windows + parameters: + - {name: status, in: query, schema: {type: string, enum: [active, upcoming, expired]}} + - {name: page_size, in: query, schema: {type: integer, default: 50}} + - {name: page_token, in: query, schema: {type: string}} + security: [{bearerAuth: []}] + responses: + "200": + description: Maintenance window list + content: + application/json: + schema: + type: object + properties: + items: {type: array, items: {$ref: "#/components/schemas/MaintenanceWindow"}} + next_page_token: {type: string} + post: + tags: [scheduling] + operationId: createMaintenanceWindow + summary: Declare a new maintenance window + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: {$ref: "#/components/schemas/MaintenanceWindowCreate"} + responses: + "200": + description: Maintenance window created + content: + application/json: + schema: {$ref: "#/components/schemas/MaintenanceWindow"} + "422": {$ref: "#/components/responses/UnprocessableEntity"} + + /api/v1/admin/maintenance-windows/{window_uuid}: + get: + tags: [scheduling] + operationId: getMaintenanceWindow + summary: Get maintenance window details including scheduled requests in queue + parameters: + - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + responses: + "200": + description: Maintenance window detail + content: + application/json: + schema: {$ref: "#/components/schemas/MaintenanceWindow"} + "404": {$ref: "#/components/responses/NotFound"} + patch: + tags: [scheduling] + operationId: updateMaintenanceWindow + summary: Update maintenance window schedule or description + parameters: + - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: {$ref: "#/components/schemas/MaintenanceWindowPatch"} + responses: + "200": + description: Maintenance window updated + content: + application/json: + schema: {$ref: "#/components/schemas/MaintenanceWindow"} + delete: + tags: [scheduling] + operationId: deleteMaintenanceWindow + summary: Delete a maintenance window (cancels queued requests if policy dictates) + parameters: + - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + responses: + "200": + description: Deleted; queued_request_disposition indicates what happened to waiting requests + content: + application/json: + schema: + type: object + properties: + window_uuid: {type: string, format: uuid} + queued_requests_affected: {type: integer} + queued_request_disposition: {type: string, enum: [cancelled, reassigned, held]} + + /api/v1/admin/maintenance-windows/{window_uuid}/scheduled-requests: + get: + tags: [scheduling] + operationId: listWindowScheduledRequests + summary: List requests queued for this maintenance window + parameters: + - {name: window_uuid, in: path, required: true, schema: {type: string, format: uuid}} + - {name: page_size, in: query, schema: {type: integer, default: 50}} + - {name: page_token, in: query, schema: {type: string}} + security: [{bearerAuth: []}] + responses: + "200": + description: Queued requests + content: + application/json: + schema: + type: object + properties: + items: {type: array, items: {type: object, additionalProperties: true}} + next_page_token: {type: string} + + # ── Federation Management ──────────────────────────────────────────────────── + /api/v1/admin/federation/peers: + get: + tags: [federation] + operationId: listFederationPeers + summary: List all registered federation peer DCM instances + parameters: + - {name: trust_posture, in: query, schema: {type: string, enum: [verified, vouched, untrusted]}} + - {name: status, in: query, schema: {type: string, enum: [active, suspended, pending]}} + - {name: page_size, in: query, schema: {type: integer, default: 50}} + - {name: page_token, in: query, schema: {type: string}} + security: [{bearerAuth: []}] + responses: + "200": + description: List of federation peer registrations + content: + application/json: + schema: + type: object + properties: + items: {type: array, items: {$ref: "#/components/schemas/FederationPeer"}} + next_page_token: {type: string} + "401": {$ref: "#/components/responses/Unauthorized"} + "403": {$ref: "#/components/responses/Forbidden"} + post: + tags: [federation] + operationId: registerFederationPeer + summary: Register a new federation peer DCM instance + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: {$ref: "#/components/schemas/FederationPeerRegistration"} + responses: + "200": + description: Operation initiated — peer registration pending trust verification + content: + application/json: + schema: {$ref: "#/components/schemas/Operation"} + "422": {$ref: "#/components/responses/UnprocessableEntity"} + + /api/v1/admin/federation/peers/{peer_uuid}: + get: + tags: [federation] + operationId: getFederationPeer + summary: Get federation peer details and current trust status + parameters: + - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + responses: + "200": + description: Federation peer record + content: + application/json: + schema: {$ref: "#/components/schemas/FederationPeer"} + "404": {$ref: "#/components/responses/NotFound"} + delete: + tags: [federation] + operationId: deregisterFederationPeer + summary: Deregister a federation peer (graceful tunnel teardown) + parameters: + - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + responses: + "200": + description: Operation initiated + content: + application/json: + schema: {$ref: "#/components/schemas/Operation"} + + /api/v1/admin/federation/peers/{peer_uuid}:set-trust-posture: + post: + tags: [federation] + operationId: setFederationPeerTrustPosture + summary: Set the trust posture for a federation peer + parameters: + - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [trust_posture, reason] + properties: + trust_posture: {type: string, enum: [verified, vouched, untrusted]} + reason: {type: string, description: Justification for trust posture change} + responses: + "200": + description: Trust posture updated + content: + application/json: + schema: {$ref: "#/components/schemas/FederationPeer"} + + /api/v1/admin/federation/peers/{peer_uuid}:suspend: + post: + tags: [federation] + operationId: suspendFederationPeer + summary: Suspend federation tunnel (stop routing; preserve registration) + parameters: + - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: {type: string} + responses: + "200": + description: Peer suspended + content: + application/json: + schema: {$ref: "#/components/schemas/FederationPeer"} + + /api/v1/admin/federation/peers/{peer_uuid}/routed-requests: + get: + tags: [federation] + operationId: listFederationRoutedRequests + summary: List requests routed through this federation peer + parameters: + - {name: peer_uuid, in: path, required: true, schema: {type: string, format: uuid}} + - {name: status, in: query, schema: {type: string}} + - {name: page_size, in: query, schema: {type: integer, default: 50}} + - {name: page_token, in: query, schema: {type: string}} + security: [{bearerAuth: []}] + responses: + "200": + description: Federated request list + content: + application/json: + schema: + type: object + properties: + items: {type: array, items: {type: object, additionalProperties: true}} + next_page_token: {type: string} + + /api/v1/admin/federation/config: + get: + tags: [federation] + operationId: getFederationConfig + summary: Get this DCM instance's federation configuration (identity, capabilities, trust policy) + security: [{bearerAuth: []}] + responses: + "200": + description: Federation configuration + content: + application/json: + schema: {$ref: "#/components/schemas/FederationConfig"} + patch: + tags: [federation] + operationId: updateFederationConfig + summary: Update federation configuration (enable/disable federation, set scope) + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: {$ref: "#/components/schemas/FederationConfigPatch"} + responses: + "200": + description: Configuration updated + content: + application/json: + schema: {$ref: "#/components/schemas/FederationConfig"} + + # ── Subscription Administration (doc 50) ──────────────────────────── + + /api/v1/admin/subscriptions: + get: + operationId: adminListSubscriptions + summary: List all subscriptions across tenants + tags: [Subscription Administration] + parameters: + - {name: tenant_uuid, in: query, schema: {type: string, format: uuid}} + - {name: lifecycle_state, in: query, schema: {type: string}} + - {name: provider_uuid, in: query, schema: {type: string, format: uuid}} + - {name: page_size, in: query, schema: {type: integer, default: 100}} + - {name: page_token, in: query, schema: {type: string}} + responses: + "200": + description: Paginated subscription list across tenants + content: + application/json: + schema: {$ref: "#/components/schemas/ResourceList"} + + /api/v1/admin/subscriptions/expiring: + get: + operationId: adminListExpiringSubscriptions + summary: List subscriptions approaching expiry + tags: [Subscription Administration] + parameters: + - {name: within_days, in: query, schema: {type: integer, default: 30}} + - {name: page_size, in: query, schema: {type: integer, default: 100}} + - {name: page_token, in: query, schema: {type: string}} + responses: + "200": + description: Expiring subscriptions + content: + application/json: + schema: {$ref: "#/components/schemas/ResourceList"} + + /api/v1/admin/subscriptions/{subscription_uuid}:force-cancel: + post: + operationId: adminForceCancelSubscription + summary: Admin force cancellation — bypasses consumer approval + tags: [Subscription Administration] + parameters: + - {name: subscription_uuid, in: path, required: true, schema: {type: string, format: uuid}} + requestBody: + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: {type: string} + skip_grace_period: {type: boolean, default: false} + responses: + "200": + description: Force cancellation initiated + content: + application/json: + schema: {$ref: "#/components/schemas/Operation"} + + + /api/v1/admin/overrides: + get: + summary: List pending override requests + operationId: listOverrideRequests + parameters: + - name: status + in: query + schema: + type: string + enum: [pending, approved, rejected, expired] + - name: role + in: query + schema: + type: string + responses: + '200': + description: Override requests + /api/v1/admin/overrides/{request_uuid}: + get: + summary: Get override request details + operationId: getOverrideRequest + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Override request details + /api/v1/admin/overrides/{request_uuid}/approve: + post: + summary: Approve override request + operationId: approveOverride + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [justification, role] + properties: + justification: + type: string + role: + type: string + compensating_controls: + type: array + items: + type: string + responses: + '200': + description: Override approved + /api/v1/admin/overrides/{request_uuid}/reject: + post: + summary: Reject override request + operationId: rejectOverride + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: + type: string + responses: + '200': + description: Override rejected + +components: + + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + parameters: + + tenant_uuid: + name: tenant_uuid + in: path + required: true + schema: { type: string, format: uuid } + + provider_uuid: + name: provider_uuid + in: path + required: true + schema: { type: string, format: uuid } + + actor_uuid: + name: actor_uuid + in: path + required: true + schema: { type: string, format: uuid } + + profile_name: + name: profile_name + in: path + required: true + schema: { type: string, enum: [minimal, dev, standard, prod, fsi, sovereign] } + + limit: + name: page_size + in: query + schema: { type: integer, minimum: 1, maximum: 1000, default: 50 } + + cursor: + name: page_token + in: query + schema: { type: string } + + schemas: + + + + MaintenanceWindow: + type: object + properties: + window_uuid: {type: string, format: uuid} + handle: {type: string, description: "Stable reference used in schedule.window_id"} + display_name: {type: string} + description: {type: string} + schedule: + type: object + description: Cron expression or recurrence rule for this window + properties: + rrule: {type: string, description: "RFC 5545 RRULE e.g. FREQ=WEEKLY;BYDAY=SA;BYHOUR=2"} + duration: {type: string, description: "ISO 8601 duration e.g. PT4H"} + timezone: {type: string, description: "IANA timezone e.g. UTC, America/New_York"} + status: {type: string, enum: [active, suspended, expired]} + next_opens_at: {type: string, format: date-time} + next_closes_at: {type: string, format: date-time} + queued_request_count: {type: integer} + created_at: {type: string, format: date-time} + owned_by_actor_uuid: {type: string, format: uuid} + + MaintenanceWindowCreate: + type: object + required: [handle, display_name, schedule] + properties: + handle: {type: string, pattern: "^[a-z][a-z0-9-]{2,63}$"} + display_name: {type: string} + description: {type: string} + schedule: + type: object + required: [rrule, duration, timezone] + properties: + rrule: {type: string} + duration: {type: string} + timezone: {type: string} + + MaintenanceWindowPatch: + type: object + properties: + display_name: {type: string} + description: {type: string} + status: {type: string, enum: [active, suspended]} + schedule: + type: object + properties: + rrule: {type: string} + duration: {type: string} + timezone: {type: string} + + FederationPeer: + type: object + properties: + peer_uuid: {type: string, format: uuid} + display_name: {type: string} + dcm_instance_uuid: {type: string, format: uuid} + endpoint: {type: string, format: uri} + trust_posture: {type: string, enum: [verified, vouched, untrusted]} + status: {type: string, enum: [active, suspended, pending, deregistered]} + sovereignty_declarations: {type: array, items: {type: string}} + registered_at: {type: string, format: date-time} + last_heartbeat_at: {type: string, format: date-time} + routed_request_count: {type: integer} + + FederationPeerRegistration: + type: object + required: [display_name, endpoint, public_key_pem] + properties: + display_name: {type: string} + endpoint: {type: string, format: uri, description: mTLS endpoint of the peer DCM instance} + public_key_pem: {type: string, description: Public key for mTLS identity verification} + initial_trust_posture: {type: string, enum: [verified, vouched, untrusted], default: untrusted} + scope_declaration: + type: object + properties: + resource_types_accessible: {type: array, items: {type: string}} + sovereignty_constraints: {type: array, items: {type: string}} + + FederationConfig: + type: object + properties: + federation_enabled: {type: boolean} + this_instance_uuid: {type: string, format: uuid} + this_instance_display_name: {type: string} + this_instance_endpoint: {type: string, format: uri} + default_trust_posture: {type: string, enum: [verified, vouched, untrusted]} + peer_count: {type: integer} + active_peer_count: {type: integer} + + FederationConfigPatch: + type: object + properties: + federation_enabled: {type: boolean} + default_trust_posture: {type: string, enum: [verified, vouched, untrusted]} + this_instance_display_name: {type: string} + + Operation: + type: object + description: | + AEP-136 Long-Running Operation. Returned by async operations instead of 202 Accepted. + Poll GET {operation.name} until done is true. + The operation.name is a stable resource path: /api/v1/operations/{uuid} + required: [name, done] + additionalProperties: false + properties: + name: + type: string + description: "Stable resource path for this operation. Poll this URL for status." + example: /api/v1/operations/a1b2c3d4-e5f6-7890-abcd-ef1234567890 + done: + type: boolean + description: True when the operation has reached a terminal state (success or error) + default: false + metadata: + type: object + description: Operation-specific progress metadata + additionalProperties: false + properties: + stage: + type: string + description: Current pipeline stage + progress_pct: + type: integer + minimum: 0 + maximum: 100 + resource_uuid: + type: string + format: uuid + description: UUID of the resource being created/modified (set as soon as assigned) + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + response: + type: object + description: Present when done is true and the operation succeeded. Contains the result resource. + additionalProperties: true + error: + type: object + description: Present when done is true and the operation failed. + additionalProperties: false + properties: + code: { type: string } + message: { type: string } + details: { type: array, items: { type: object } } + + + Error: + type: object + required: [error] + properties: + error: + type: object + required: [code, message, request_id] + properties: + code: { type: string } + message: { type: string } + request_id: { type: string, format: uuid } + rule_uuid: { type: string, format: uuid } + fields: { type: array, items: { type: object } } + + AdminHealthResponse: + type: object + properties: + status: { type: string, enum: [healthy, degraded, unhealthy] } + version: { type: string } + uptime: { type: string } + + /api/v1/admin/overrides: + get: + summary: List pending override requests + operationId: listOverrideRequests + parameters: + - name: status + in: query + schema: + type: string + enum: [pending, approved, rejected, expired] + - name: role + in: query + schema: + type: string + responses: + '200': + description: Override requests + /api/v1/admin/overrides/{request_uuid}: + get: + summary: Get override request details + operationId: getOverrideRequest + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Override request details + /api/v1/admin/overrides/{request_uuid}/approve: + post: + summary: Approve override request + operationId: approveOverride + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [justification, role] + properties: + justification: + type: string + role: + type: string + compensating_controls: + type: array + items: + type: string + responses: + '200': + description: Override approved + /api/v1/admin/overrides/{request_uuid}/reject: + post: + summary: Reject override request + operationId: rejectOverride + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: + type: string + responses: + '200': + description: Override rejected + +components: + type: array + items: + type: object + properties: + name: { type: string } + status: { type: string } + detail: { type: string } + providers: + type: object + properties: + total: { type: integer } + healthy: { type: integer } + degraded: { type: integer } + unavailable: { type: integer } + + Tenant: + type: object + properties: + uuid: { type: string, format: uuid } + handle: { type: string } + display_name: { type: string } + status: { type: string } + created_at: { type: string, format: date-time } + updated_at: { type: string, format: date-time } + resource_count: { type: integer } + compliance_domains: { type: array, items: { type: string } } + + TenantCreate: + type: object + required: [handle, display_name] + properties: + handle: { type: string } + display_name: { type: string } + compliance_domains: { type: array, items: { type: string } } + + TenantList: + type: object + properties: + items: { type: array, items: { $ref: "#/components/schemas/Tenant" } } + pagination: { type: object } + + TenantQuotas: + type: object + properties: + tenant_uuid: { type: string, format: uuid } + quotas: + type: array + items: + type: object + properties: + resource_type: { type: string } + page_size: { type: integer } + used: { type: integer } + remaining: { type: integer } + + QuotaUpdate: + type: object + required: [limit] + properties: + page_size: { type: integer, minimum: 0 } + reason: { type: string } + + ProviderList: + type: object + properties: + items: + type: array + items: + type: object + properties: + uuid: { type: string, format: uuid } + display_name: { type: string } + provider_type_id: { type: string } + status: { type: string } + trust_score: { type: number } + health_status: { type: string } + accreditations: { type: array, items: { type: object } } + pagination: { type: object } + + AccreditationList: + type: object + properties: + items: + type: array + items: + type: object + properties: + accreditation_uuid: { type: string, format: uuid } + provider_uuid: { type: string, format: uuid } + framework: { type: string } + status: { type: string } + expires_at: { type: string, format: date-time } + pagination: { type: object } + + DiscoveryJobStatus: + type: object + properties: + discovery_job_uuid: { type: string, format: uuid } + status: { type: string, enum: [running, completed, failed] } + started_at: { type: string, format: date-time } + completed_at: { type: string, format: date-time } + providers_queried: { type: integer } + entities_discovered: { type: integer } + drift_records_created: { type: integer } + orphans_detected: { type: integer } + + OrphanList: + type: object + properties: + items: + type: array + items: + type: object + properties: + orphan_candidate_uuid: { type: string, format: uuid } + provider_uuid: { type: string, format: uuid } + resource_type: { type: string } + discovered_at: { type: string, format: date-time } + provider_entity_id: { type: string } + pagination: { type: object } + + ScoringConfiguration: + type: object + properties: + profile: { type: string } + approval_routing: + type: array + items: + type: object + properties: + tier: { type: string } + max_score: { type: integer } + smx008_auto_cap: + type: integer + description: "Hard cap on auto-approve max_score (≤ 50 always enforced)" + + ScoringConfigurationUpdate: + type: object + properties: + approval_routing: + type: array + items: + type: object + required: [tier, max_score] + properties: + tier: { type: string } + max_score: { type: integer, minimum: 0, maximum: 100 } + + ScoringOverride: + type: object + required: [policy_uuid, enforcement_class_override] + properties: + policy_uuid: { type: string, format: uuid } + enforcement_class_override: { type: string, enum: [compliance, operational] } + reason: { type: string } + + RiskHistory: + type: object + properties: + actor_uuid: { type: string, format: uuid } + records: + type: array + items: + type: object + properties: + score: { type: integer } + routing_tier: { type: string } + evaluated_at: { type: string, format: date-time } + request_uuid: { type: string, format: uuid } + stored_tier_weight: { type: integer } + + ApprovalRecord: + type: object + properties: + approval_uuid: { type: string, format: uuid } + subject_type: { type: string } + subject_uuid: { type: string, format: uuid } + required_tier: { type: string } + required_quorum: { type: integer } + decisions: + type: array + items: + type: object + properties: + actor_uuid: { type: string, format: uuid } + decision: { type: string } + reason: { type: string } + recorded_at: { type: string, format: date-time } + expires_at: { type: string, format: date-time } + created_at: { type: string, format: date-time } + status: { type: string, enum: [open, approved, rejected, expired] } + + ApprovalDetail: + allOf: + - { $ref: "#/components/schemas/ApprovalRecord" } + - type: object + properties: + subject_detail: { type: object } + stored_tier_weights: + type: object + description: "Tier weights recorded at approval creation time (ATM-008)" + additionalProperties: { type: integer } + + TierRegistryChangeProposal: + type: object + required: [proposed_tier_list, reason] + properties: + proposed_tier_list: + type: array + minItems: 1 + items: + type: object + required: [name, decision_gravity] + properties: + name: { type: string, description: "Stable tier name (e.g., auto, reviewed, verified, authorized)" } + decision_gravity: { type: string, enum: [none, routine, elevated, critical] } + description: { type: string } + dcm_gate: { type: string } + organization_provides: { type: string } + dcmgroup_required: { type: boolean } + dcmgroup_uuid: { type: string, format: uuid } + quorum_threshold: { type: integer, minimum: 1 } + reason: { type: string } + + TierImpactReport: + type: object + properties: + impact_report_uuid: { type: string, format: uuid } + change_uuid: { type: string, format: uuid } + computed_at: { type: string, format: date-time } + blocking_items: { type: integer } + items: + type: array + items: + type: object + properties: + item_uuid: { type: string, format: uuid } + classification: + type: string + enum: [SECURITY_DEGRADATION, SECURITY_UPGRADE, BROKEN_REFERENCE, PROFILE_GAP, STALE_WEIGHT] + tier_name: { type: string } + old_position: { type: integer } + new_position: { type: integer } + old_gravity: { type: string } + new_gravity: { type: string } + affected_items: + type: array + items: + type: object + properties: + item_type: { type: string } + item_uuid: { type: string, format: uuid } + description: { type: string } + accepted: { type: boolean } + accepted_by: { type: string, format: uuid } + accepted_reason: { type: string } diff --git a/content/docs/schemas/dcm-consumer-api.yaml b/content/docs/schemas/dcm-consumer-api.yaml new file mode 100644 index 0000000..0565b6e --- /dev/null +++ b/content/docs/schemas/dcm-consumer-api.yaml @@ -0,0 +1,2663 @@ +openapi: 3.1.0 +info: + title: DCM Consumer API + version: 1.0.0 + description: 'The DCM Consumer API provides the interface used by application teams, + Tenant owners, + + and automated tooling to interact with the DCM control plane. All interactions + are + + authenticated, Tenant-scoped, and governed by the Policy Engine. + + + **Key principles:** + + - All requests require a valid session token (Bearer) obtained via `/api/v1/auth/token` + + - All responses are Tenant-scoped — actors only see entities they are authorized + to see + + - Policy denials return 403 with a `rule_uuid` identifying the governing rule + + - All mutating operations produce an audit record + + - List endpoints support cursor-based pagination via `page_size` and `page_token` + query parameters + + + **API Versioning:** DCM uses URL path versioning (`/api/v1/`). Version discovery + is available + + at `/.well-known/dcm-api-versions`. See `34-api-versioning-strategy.md`. + + + + + **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions: + + custom methods use colon syntax (`POST /resources/{name}:suspend`), + + async operations return an `Operation` resource (AEP-136 LRO), + + and list pagination uses `page_size`/`page_token` parameters. + + ' + contact: + name: DCM Project + url: https://github.com/dcm-project + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 +servers: +- url: https://{dcm-host}/ + description: DCM Control Plane + variables: + dcm-host: + description: Hostname of the DCM control plane deployment + default: dcm.example.com +security: +- BearerAuth: [] +tags: +- name: discovery + description: API version discovery and migration guides +- name: authentication + description: Session management, token issuance, and introspection +- name: catalog + description: Service catalog browsing and search +- name: requests + description: Service request submission and lifecycle +- name: resources + description: Resource entity management and lifecycle operations +- name: drift + description: Drift detection, acknowledgement, and remediation +- name: groups + description: Resource group management +- name: approvals + description: Approval workflow for pending decisions +- name: cost + description: Cost estimation and attribution +- name: notifications + description: Notification inbox management +- name: webhooks + description: Outbound webhook subscription management +- name: search + description: Cross-entity search +- name: audit + description: Audit trail access +- name: contributions + description: Federated contribution submission (policies, resource groups) +- name: credentials + description: Credential retrieval and rotation +paths: + /.well-known/dcm-api-versions: + get: + tags: + - discovery + operationId: getApiVersions + summary: List supported API versions + security: [] + responses: + '200': + description: Supported API versions and deprecation status + content: + application/json: + schema: + type: object + properties: + versions: + type: array + items: + type: object + properties: + version: + type: string + status: + type: string + enum: + - current + - supported + - deprecated + - sunset + sunset_date: + type: string + format: date + /api/v1/migration-guide: + get: + tags: + - discovery + operationId: getMigrationGuide + summary: Get migration guide for API version transitions + security: [] + parameters: + - name: from + in: query + required: true + schema: + type: string + example: '1' + - name: to + in: query + required: true + schema: + type: string + example: '2' + responses: + '200': + description: Migration guide document + content: + application/json: + schema: + type: object + properties: + from_version: + type: string + to_version: + type: string + breaking_changes: + type: array + items: + type: object + guidance: + type: string + /api/v1/auth/token: + post: + tags: + - authentication + operationId: createSession + summary: Authenticate and obtain a session token + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - grant_type + properties: + grant_type: + type: string + enum: + - password + - client_credentials + - oidc_code + username: + type: string + password: + type: string + format: password + client_id: + type: string + client_secret: + type: string + format: password + code: + type: string + redirect_uri: + type: string + format: uri + responses: + '200': + description: Session token issued + content: + application/json: + schema: + $ref: '#/components/schemas/SessionTokenResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /api/v1/auth/sessions: + get: + tags: + - authentication + operationId: listSessions + summary: List active sessions for the authenticated actor + responses: + '200': + content: + application/json: + schema: + type: object + properties: + sessions: + type: array + items: + $ref: '#/components/schemas/SessionSummary' + description: List active sessions + delete: + tags: + - authentication + operationId: revokeAllSessions + summary: Revoke all sessions for the authenticated actor (except the current + session) + responses: + '204': + description: All other sessions revoked + /api/v1/auth/sessions/{session_uuid}: + delete: + tags: + - authentication + operationId: revokeSession + summary: Revoke a specific session + parameters: + - $ref: '#/components/parameters/session_uuid' + responses: + '204': + description: Session revoked + '404': + $ref: '#/components/responses/NotFound' + /api/v1/auth:introspect: + post: + tags: + - authentication + operationId: introspectToken + summary: Introspect a token and return actor identity and roles + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - token + properties: + token: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/TokenIntrospection' + '401': + $ref: '#/components/responses/Unauthorized' + /api/v1/auth/session: + delete: + tags: + - auth + operationId: logoutCurrentSession + summary: Logout current session + description: Terminates the calling actor's current session and invalidates + the bearer token. + security: + - bearerAuth: [] + responses: + '204': + description: Session terminated + '401': + $ref: '#/components/responses/Unauthorized' + /api/v1/catalog: + get: + tags: + - catalog + operationId: listCatalogItems + summary: List catalog items available to the authenticated actor (RBAC-filtered) + parameters: + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + - name: category + in: query + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CatalogItemList' + /api/v1/catalog/{catalog_item_uuid}: + get: + tags: + - catalog + operationId: getCatalogItem + summary: Get full schema and details for a catalog item including field constraints + parameters: + - $ref: '#/components/parameters/catalog_item_uuid' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CatalogItem' + '404': + $ref: '#/components/responses/NotFound' + /api/v1/catalog/search: + get: + tags: + - catalog + operationId: searchCatalog + summary: Search catalog by keyword, resource type, or tag + parameters: + - name: q + in: query + required: true + schema: + type: string + minLength: 1 + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CatalogItemList' + /api/v1/requests: + post: + tags: + - requests + operationId: submitRequest + summary: Submit a service request + description: 'Submits a resource request. The request is stored as an Intent + State artifact, processed + + through layer assembly and policy evaluation, placed with a provider, and + dispatched. + + Returns immediately with a request_uuid for status polling or SSE streaming. + + ' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceRequest' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + '400': + $ref: '#/components/responses/BadRequest' + '403': + $ref: '#/components/responses/Forbidden' + '422': + $ref: '#/components/responses/UnprocessableEntity' + get: + tags: + - requests + operationId: listRequests + summary: List requests submitted by the authenticated actor + parameters: + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + - name: status + in: query + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/RequestList' + /api/v1/requests/{request_uuid}/status: + get: + tags: + - requests + operationId: getRequestStatus + summary: Poll request status and pipeline stage + parameters: + - $ref: '#/components/parameters/request_uuid' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/RequestStatus' + '404': + $ref: '#/components/responses/NotFound' + /api/v1/requests/{request_uuid}/stream: + get: + tags: + - requests + operationId: streamRequestStatus + summary: Stream real-time request status updates (Server-Sent Events) + parameters: + - $ref: '#/components/parameters/request_uuid' + responses: + '200': + description: SSE stream of RequestStatus events + content: + text/event-stream: + schema: + type: string + /api/v1/requests/{request_uuid}: + delete: + tags: + - requests + operationId: cancelRequest + summary: Cancel a pending request (before provider dispatch) + parameters: + - $ref: '#/components/parameters/request_uuid' + responses: + '204': + description: Request cancelled + '409': + description: Request already dispatched — cannot cancel + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v1/request-groups: + post: + tags: + - requests + operationId: createRequestGroup + summary: Submit a group of related requests (bulk or dependency-ordered) + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RequestGroup' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + /api/v1/request-groups/{group_uuid}: + get: + tags: + - requests + operationId: getRequestGroup + summary: Get status of all requests in a group + parameters: + - $ref: '#/components/parameters/group_uuid' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/RequestGroupStatus' + /api/v1/resources: + get: + tags: + - resources + operationId: listResources + summary: List resources owned by the authenticated actor's Tenant + parameters: + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + - name: resource_type + in: query + schema: + type: string + - name: lifecycle_state + in: query + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceList' + /api/v1/resources/{entity_uuid}: + get: + tags: + - resources + operationId: getResource + summary: Get full entity detail for a resource + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceEntity' + '404': + $ref: '#/components/responses/NotFound' + patch: + tags: + - resources + operationId: updateResource + summary: Update editable fields on a realized resource (delta only) + parameters: + - $ref: '#/components/parameters/entity_uuid' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceUpdate' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + '400': + $ref: '#/components/responses/BadRequest' + '403': + $ref: '#/components/responses/Forbidden' + delete: + tags: + - resources + operationId: decommissionResource + summary: Initiate resource decommission + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + '409': + description: Resource has active dependents — cannot decommission + /api/v1/resources/{entity_uuid}:suspend: + post: + tags: + - resources + operationId: suspendResource + summary: Suspend an OPERATIONAL resource + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + '409': + description: Resource not in OPERATIONAL state + /api/v1/resources/{entity_uuid}:resume: + post: + tags: + - resources + operationId: resumeResource + summary: Resume a SUSPENDED resource + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + '409': + description: Resource not in SUSPENDED state + /api/v1/resources/{entity_uuid}:rehydrate: + post: + tags: + - resources + operationId: rehydrateResource + summary: Rehydrate a resource to a new provider or context + parameters: + - $ref: '#/components/parameters/entity_uuid' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RehydrateRequest' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + '403': + $ref: '#/components/responses/Forbidden' + /api/v1/resources/{entity_uuid}:extend-ttl: + post: + tags: + - resources + operationId: extendTtl + summary: Extend or modify the TTL of a resource + parameters: + - $ref: '#/components/parameters/entity_uuid' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - new_ttl + properties: + new_ttl: + type: string + description: ISO 8601 duration or datetime + responses: + '200': + description: TTL updated + /api/v1/resources/expiring: + get: + tags: + - resources + operationId: listExpiringResources + summary: List resources expiring within a time window + parameters: + - name: within + in: query + schema: + type: string + description: ISO 8601 duration (e.g., P30D) + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceList' + /api/v1/resources/{entity_uuid}:transfer: + post: + tags: + - resources + operationId: initiateOwnershipTransfer + summary: Initiate ownership transfer to another Tenant + parameters: + - $ref: '#/components/parameters/entity_uuid' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - target_tenant_uuid + - reason + properties: + target_tenant_uuid: + type: string + format: uuid + reason: + type: string + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + /api/v1/resources/transfers/{transfer_uuid}:accept: + post: + tags: + - resources + operationId: acceptOwnershipTransfer + summary: Accept an incoming ownership transfer + parameters: + - name: transfer_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Transfer accepted; ownership updated + /api/v1/resources/transfers/{transfer_uuid}:reject: + post: + tags: + - resources + operationId: rejectOwnershipTransfer + summary: Reject an incoming ownership transfer + parameters: + - name: transfer_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Transfer rejected + /api/v1/resources:bulk-decommission: + post: + tags: + - resources + operationId: bulkDecommission + summary: Decommission multiple resources in dependency-safe order + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - entity_uuids + properties: + entity_uuids: + type: array + items: + type: string + format: uuid + minItems: 1 + dry_run: + type: boolean + default: false + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + /api/v1/resources/{entity_uuid}/relationships: + get: + tags: + - resources + operationId: getResourceRelationships + summary: Get all relationships for a resource entity + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + relationships: + type: array + items: + type: object + /api/v1/resources/{entity_uuid}/provider-notifications: + get: + tags: + - resources + operationId: getProviderNotifications + summary: List pending provider-initiated state change notifications requiring + approval + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + notifications: + type: array + items: + type: object + /api/v1/resources/{entity_uuid}/provider-notifications/{notification_uuid}:approve: + post: + tags: + - resources + operationId: approveProviderNotification + summary: Approve a provider-initiated state change + parameters: + - $ref: '#/components/parameters/entity_uuid' + - name: notification_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Provider notification approved + /api/v1/resources/{entity_uuid}/recovery-decisions: + get: + tags: + - resources + operationId: getRecoveryDecisions + summary: List pending recovery decisions for a resource (for notify_and_wait + policies) + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + decisions: + type: array + items: + type: object + /api/v1/resources/{entity_uuid}/recovery-decisions/{recovery_decision_uuid}: + post: + tags: + - resources + operationId: resolveRecoveryDecision + summary: Resolve a pending recovery decision + parameters: + - $ref: '#/components/parameters/entity_uuid' + - name: recovery_decision_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - decision + properties: + decision: + type: string + enum: + - approve + - reject + - escalate + reason: + type: string + responses: + '200': + description: Decision recorded + /api/v1/resources/{entity_uuid}/audit: + get: + tags: + - audit + operationId: getResourceAudit + summary: Get audit trail for a resource entity + parameters: + - $ref: '#/components/parameters/entity_uuid' + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AuditList' + /api/v1/resources/{entity_uuid}/drift: + get: + tags: + - drift + operationId: getResourceDrift + summary: Get current drift records for a resource + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/DriftRecordList' + /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:acknowledge: + post: + tags: + - drift + operationId: acknowledgeDrift + summary: Acknowledge a drift record (suppress notification without resolving) + parameters: + - $ref: '#/components/parameters/entity_uuid' + - name: drift_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Drift acknowledged + /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:accept: + post: + tags: + - drift + operationId: acceptDrift + summary: Accept the drifted state as the new intended state (update Requested + State) + parameters: + - $ref: '#/components/parameters/entity_uuid' + - name: drift_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Drifted state accepted; Requested State updated + /api/v1/resources/{entity_uuid}/drift/{drift_uuid}:revert: + post: + tags: + - drift + operationId: revertDrift + summary: Revert the resource to its Requested State (dispatch remediation) + parameters: + - $ref: '#/components/parameters/entity_uuid' + - name: drift_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + /api/v1/groups: + get: + tags: + - groups + operationId: listGroups + summary: List groups the authenticated actor's Tenant has access to + responses: + '200': + content: + application/json: + schema: + type: object + properties: + groups: + type: array + items: + type: object + /api/v1/groups/{group_uuid}: + get: + tags: + - groups + operationId: getGroup + summary: Get group detail and member list + parameters: + - name: group_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + content: + application/json: + schema: + type: object + /api/v1/groups/{group_uuid}/members: + post: + tags: + - groups + operationId: addGroupMember + summary: Add an entity to a group + parameters: + - name: group_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - entity_uuid + properties: + entity_uuid: + type: string + format: uuid + responses: + '201': + description: Entity added to group + /api/v1/groups/{group_uuid}/members/{entity_uuid}: + delete: + tags: + - groups + operationId: removeGroupMember + summary: Remove an entity from a group + parameters: + - name: group_uuid + in: path + required: true + schema: + type: string + format: uuid + - $ref: '#/components/parameters/entity_uuid' + responses: + '204': + description: Entity removed from group + /api/v1/approvals/pending: + get: + tags: + - approvals + operationId: listPendingApprovals + summary: List approval decisions pending the authenticated actor's action + parameters: + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + approvals: + type: array + items: + $ref: '#/components/schemas/ApprovalRecord' + /api/v1/approvals/{approval_uuid}: + post: + tags: + - approvals + operationId: recordApprovalDecision + summary: Record an approval decision (approve/reject/abstain) + description: 'Designed to be called by both humans via the UI and by external + systems (ITSM, Slack bots, + + CI/CD pipelines) that have been authorized to record decisions on behalf of + the organization. + + DCM enforces the gate and records the audit trail; the deliberation process + is the + + organization''s responsibility. + + ' + parameters: + - name: approval_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - decision + - reason + properties: + decision: + type: string + enum: + - approve + - reject + - abstain + reason: + type: string + minLength: 1 + external_reference: + type: string + description: ITSM ticket ID, Jira issue key, etc. + responses: + '200': + description: Decision recorded; approval gate re-evaluated + '403': + $ref: '#/components/responses/Forbidden' + '409': + description: Approval already in terminal state + /api/v1/cost/estimate: + post: + tags: + - cost + operationId: estimateCost + summary: Get cost estimate before submitting a request + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceRequest' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CostEstimate' + /api/v1/resources/{entity_uuid}/cost: + get: + tags: + - cost + operationId: getResourceCost + summary: Get cost actuals and attribution for a resource + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CostActuals' + /api/v1/operations/{operation_uuid}: + get: + tags: + - operations + operationId: getOperation + summary: Poll a Long-Running Operation for status (AEP-136) + description: 'Returns the current state of an async operation. Poll until `done` + is `true`. + + + **Note:** `operation_uuid == request_uuid`. All async operations initiated + via + + POST /api/v1/requests, PATCH /api/v1/resources/{uuid}, etc., return an Operation + + whose UUID is the same as the request UUID. + + + Two polling views are available: + + - This endpoint: AEP-standard (done, metadata, response/error) + + - GET /api/v1/requests/{uuid}/status: DCM-native rich view (pipeline_stage, + full history) + + + Both reflect the same underlying operation state. + + ' + parameters: + - name: operation_uuid + in: path + required: true + description: The operation UUID (same as the request UUID) + schema: + type: string + format: uuid + security: + - bearerAuth: [] + responses: + '200': + description: Operation status (check `done` field for completion) + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + '404': + $ref: '#/components/responses/NotFound' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + /api/v1/quota: + get: + tags: + - cost + operationId: getQuota + summary: Get quota status for the authenticated actor's Tenant + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/QuotaStatus' + /api/v1/notifications: + get: + tags: + - notifications + operationId: listNotifications + summary: List notifications in the authenticated actor's inbox + parameters: + - name: unread_only + in: query + schema: + type: boolean + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + notifications: + type: array + items: + type: object + /api/v1/notifications/{notification_uuid}/read: + post: + tags: + - notifications + operationId: markNotificationRead + summary: Mark a notification as read + parameters: + - name: notification_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Marked as read + /api/v1/notifications:read-all: + post: + tags: + - notifications + operationId: markAllNotificationsRead + summary: Mark all notifications as read + responses: + '204': + description: All notifications marked as read + /api/v1/webhooks: + get: + tags: + - webhooks + operationId: listWebhooks + summary: List outbound webhook subscriptions for the authenticated actor + responses: + '200': + content: + application/json: + schema: + type: object + properties: + webhooks: + type: array + items: + $ref: '#/components/schemas/WebhookSubscription' + post: + tags: + - webhooks + operationId: createWebhook + summary: Create an outbound webhook subscription + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookSubscription' + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookSubscription' + /api/v1/webhooks/{webhook_uuid}: + delete: + tags: + - webhooks + operationId: deleteWebhook + summary: Delete a webhook subscription + parameters: + - name: webhook_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: Webhook deleted + # ── Workload Analysis ───────────────────────────────────────────────────────── + /api/v1/resources/{entity_uuid}/workload-profile: + get: + tags: [workload-analysis] + operationId: getWorkloadProfile + summary: Get the Workload Analysis profile for a resource + description: | + Returns the most recent WorkloadProfile entity for the given resource. + WorkloadProfiles are created automatically during brownfield ingestion + and can be refreshed on demand via the :analyze custom method. + parameters: + - {name: entity_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + responses: + "200": + description: Workload profile + content: + application/json: + schema: {$ref: "#/components/schemas/WorkloadProfile"} + "404": {$ref: "#/components/responses/NotFound"} + "403": {$ref: "#/components/responses/Forbidden"} + + /api/v1/resources/{entity_uuid}/workload-profile:analyze: + post: + tags: [workload-analysis] + operationId: analyzeWorkload + summary: Trigger re-analysis of a resource's workload profile + description: | + Initiates a new Workload Analysis pass for the resource. The existing + WorkloadProfile is superseded when the new analysis completes. Useful + when a resource's role has changed since initial classification. + parameters: + - {name: entity_uuid, in: path, required: true, schema: {type: string, format: uuid}} + security: [{bearerAuth: []}] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [reason] + properties: + reason: {type: string, description: Why re-analysis is needed} + include_mta: {type: boolean, default: true, description: Include MTA containerization assessment} + responses: + "200": + description: Operation initiated — poll for completion + content: + application/json: + schema: {$ref: "#/components/schemas/Operation"} + "404": {$ref: "#/components/responses/NotFound"} + + /api/v1/search: + get: + tags: + - search + operationId: search + summary: Cross-entity search (resources, groups, catalog items) + parameters: + - name: q + in: query + required: true + schema: + type: string + minLength: 1 + - name: types + in: query + schema: + type: array + items: + type: string + style: form + explode: false + - $ref: '#/components/parameters/page_size' + - $ref: '#/components/parameters/page_token' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/SearchResults' + /api/v1/audit/correlation/{correlation_id}: + get: + tags: + - audit + operationId: getAuditByCorrelation + summary: Get all audit records for a correlation ID (cross-resource trace) + parameters: + - name: correlation_id + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AuditList' + /api/v1/contribute/policy: + post: + tags: + - contributions + operationId: contributePolicy + summary: Submit a policy contribution via the federated contribution pipeline + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PolicyContribution' + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + /api/v1/contribute/resource-group: + post: + tags: + - contributions + operationId: contributeResourceGroup + summary: Submit a resource group contribution + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + /api/v1/contribute: + get: + tags: + - contributions + operationId: listContributions + summary: List contributions submitted by the authenticated actor + responses: + '200': + content: + application/json: + schema: + type: object + properties: + contributions: + type: array + items: + type: object + /api/v1/contribute/{contribution_uuid}: + delete: + tags: + - contributions + operationId: withdrawContribution + summary: Withdraw a pending contribution + parameters: + - name: contribution_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: Contribution withdrawn + '409': + description: Contribution already activated — cannot withdraw + /api/v1/resources/{entity_uuid}/credentials: + get: + tags: + - credentials + operationId: listResourceCredentials + summary: List credentials associated with a resource (metadata only — no values) + parameters: + - $ref: '#/components/parameters/entity_uuid' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + credentials: + type: array + items: + $ref: '#/components/schemas/CredentialSummary' + /api/v1/credentials/{credential_uuid}/value: + get: + tags: + - credentials + operationId: getCredentialValue + summary: Retrieve the current credential value (audited; step-up auth may be + required) + parameters: + - name: credential_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialValue' + '403': + $ref: '#/components/responses/Forbidden' + /api/v1/credentials/{credential_uuid}:rotate: + post: + tags: + - credentials + operationId: rotateCredential + summary: Initiate credential rotation + parameters: + - name: credential_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + # ── Subscription Management (doc 50) ────────────────────────────────── + + /api/v1/subscriptions: + get: + operationId: listSubscriptions + summary: List tenant subscriptions (paginated) + tags: [Subscription Management] + parameters: + - { name: page_size, in: query, schema: { type: integer, default: 100 } } + - { name: page_token, in: query, schema: { type: string } } + - { name: lifecycle_state, in: query, schema: { type: string } } + responses: + '200': + description: Paginated subscription list + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionList' + post: + operationId: createSubscription + summary: Create a subscription (enters request pipeline) + tags: [Subscription Management] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionCreate' + responses: + '200': + description: Subscription creation initiated. Poll operation for status. + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + /api/v1/subscriptions/{subscription_uuid}: + get: + operationId: getSubscription + summary: Get subscription details + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + responses: + '200': + description: Subscription details + content: + application/json: + schema: + $ref: '#/components/schemas/Subscription' + patch: + operationId: updateSubscription + summary: Update subscription (tier change, auto_renew toggle) + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionUpdate' + responses: + '200': + description: Update initiated + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + /api/v1/subscriptions/{subscription_uuid}:cancel: + post: + operationId: cancelSubscription + summary: Cancel subscription (starts grace period) + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + content: + application/json: + schema: + type: object + properties: + reason: { type: string } + responses: + '200': + description: Cancellation initiated + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + /api/v1/subscriptions/{subscription_uuid}:renew: + post: + operationId: renewSubscription + summary: Manually renew subscription + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + responses: + '200': + description: Renewal initiated + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + /api/v1/subscriptions/{subscription_uuid}:suspend: + post: + operationId: suspendSubscription + summary: Suspend subscription + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + content: + application/json: + schema: + type: object + properties: + reason: { type: string } + responses: + '200': + description: Suspension initiated + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + /api/v1/subscriptions/{subscription_uuid}:resume: + post: + operationId: resumeSubscription + summary: Resume suspended subscription + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + responses: + '200': + description: Resume initiated + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + /api/v1/subscriptions/{subscription_uuid}/entities: + get: + operationId: listSubscriptionEntities + summary: List managed entities under this subscription + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + - { name: page_size, in: query, schema: { type: integer, default: 100 } } + - { name: page_token, in: query, schema: { type: string } } + responses: + '200': + description: Paginated entity list + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceList' + + /api/v1/subscriptions/{subscription_uuid}/updates: + get: + operationId: listSubscriptionUpdates + summary: List pending and applied updates + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + - { name: status, in: query, schema: { type: string, enum: [PENDING, APPROVED, REJECTED, APPLIED, FAILED] } } + - { name: page_size, in: query, schema: { type: integer, default: 100 } } + - { name: page_token, in: query, schema: { type: string } } + responses: + '200': + description: Paginated update list + content: + application/json: + schema: + $ref: '#/components/schemas/SubscriptionUpdateList' + + /api/v1/subscriptions/{subscription_uuid}/updates/{update_uuid}:approve: + post: + operationId: approveSubscriptionUpdate + summary: Approve a pending provider-originated update + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + - { name: update_uuid, in: path, required: true, schema: { type: string, format: uuid } } + responses: + '200': + description: Update approved + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + /api/v1/subscriptions/{subscription_uuid}/updates/{update_uuid}:reject: + post: + operationId: rejectSubscriptionUpdate + summary: Reject a pending provider-originated update + tags: [Subscription Management] + parameters: + - { name: subscription_uuid, in: path, required: true, schema: { type: string, format: uuid } } + - { name: update_uuid, in: path, required: true, schema: { type: string, format: uuid } } + requestBody: + content: + application/json: + schema: + type: object + properties: + reason: { type: string } + responses: + '200': + description: Update rejected + content: + application/json: + schema: + $ref: '#/components/schemas/Operation' + + + /api/v1/requests/{request_uuid}/resolution: + get: + summary: Get policy block details and resolution options + operationId: getResolutionOptions + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Blocking details and resolution options + /api/v1/requests/{request_uuid}:resolve: + post: + summary: Resolve a policy-blocked request + operationId: resolveBlockedRequest + parameters: + - name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [action] + properties: + action: + type: string + enum: [modify, request_override, cancel, escalate] + modifications: + type: object + justification: + type: string + compensating_controls: + type: array + items: + type: string + context: + type: string + responses: + '200': + description: Resolution action accepted + +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: Session token obtained from POST /api/v1/auth/token + parameters: + entity_uuid: + name: entity_uuid + in: path + required: true + schema: + type: string + format: uuid + description: UUID of the resource entity + request_uuid: + name: request_uuid + in: path + required: true + schema: + type: string + format: uuid + catalog_item_uuid: + name: catalog_item_uuid + in: path + required: true + schema: + type: string + format: uuid + group_uuid: + name: group_uuid + in: path + required: true + schema: + type: string + format: uuid + session_uuid: + name: session_uuid + in: path + required: true + schema: + type: string + format: uuid + limit: + name: page_size + in: query + schema: + type: integer + minimum: 1 + maximum: 1000 + default: 50 + cursor: + name: page_token + in: query + schema: + type: string + description: Opaque page token from previous response (use next_page_token from + response) + responses: + BadRequest: + description: Invalid request syntax + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: Missing or invalid authentication token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Forbidden: + description: Authenticated but not authorized. rule_uuid identifies the governing + policy. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: Resource not found or not visible to authenticated actor + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + UnprocessableEntity: + description: Request syntax valid but semantically invalid (e.g., field validation + failure) + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + schemas: + Operation: + type: object + description: "AEP-136 Long-Running Operation. Returned by async operations instead\ + \ of 202 Accepted.\nPoll GET {operation.name} until done is true.\nThe operation.name\ + \ is a stable resource path: /api/v1/operations/{uuid}\nNote: operation_uuid\ + \ == request_uuid. Two polling endpoints are available:\n - GET /api/v1/operations/{uuid}\ + \ — AEP-standard thin view (done, metadata, response/error)\n - GET /api/v1/requests/{uuid}/status\ + \ — DCM-native rich view (pipeline_stage, full status history)\nBoth endpoints\ + \ reflect the same underlying operation state.\n" + required: + - name + - done + additionalProperties: false + properties: + name: + type: string + description: Stable resource path for this operation. Poll this URL for + status. + example: /api/v1/operations/a1b2c3d4-e5f6-7890-abcd-ef1234567890 + done: + type: boolean + description: True when the operation has reached a terminal state (success + or error) + default: false + metadata: + type: object + description: Operation-specific progress metadata + additionalProperties: false + properties: + stage: + type: string + description: Current pipeline stage + progress_pct: + type: integer + minimum: 0 + maximum: 100 + resource_uuid: + type: string + format: uuid + description: UUID of the resource being created/modified (set as soon + as assigned) + request_uuid: + type: string + format: uuid + description: The DCM request UUID. operation_uuid == request_uuid. Use + GET /api/v1/requests/{request_uuid}/status for the full DCM-native + pipeline view. + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + response: + type: object + description: Present when done is true and the operation succeeded. Contains + the result resource. + additionalProperties: true + error: + type: object + description: Present when done is true and the operation failed. + additionalProperties: false + properties: + code: + type: string + message: + type: string + details: + type: array + items: + type: object + Error: + type: object + required: + - error + properties: + error: + type: object + required: + - code + - message + - request_id + properties: + code: + type: string + message: + type: string + request_id: + type: string + format: uuid + rule_uuid: + type: string + format: uuid + description: Present on POLICY_DENIED and GOVERNANCE_DENIED + fields: + type: array + items: + type: object + properties: + field: + type: string + issue: + type: string + SessionTokenResponse: + type: object + required: + - access_token + - token_type + - expires_in + - session_uuid + properties: + access_token: + type: string + token_type: + type: string + const: bearer + expires_in: + type: integer + description: Seconds until expiry + session_uuid: + type: string + format: uuid + refresh_token: + type: string + SessionSummary: + type: object + properties: + session_uuid: + type: string + format: uuid + created_at: + type: string + format: date-time + expires_at: + type: string + format: date-time + ip_address: + type: string + user_agent: + type: string + current: + type: boolean + TokenIntrospection: + type: object + properties: + active: + type: boolean + actor_uuid: + type: string + format: uuid + actor_type: + type: string + display_name: + type: string + tenant_uuids: + type: array + items: + type: string + format: uuid + roles: + type: array + items: + type: string + groups: + type: array + items: + type: string + expires_at: + type: string + format: date-time + CatalogItem: + type: object + properties: + uuid: + type: string + format: uuid + resource_type: + description: Resource type — FQN string (e.g., 'Compute.VirtualMachine') + or Registry UUID. DCM resolves either form internally. + oneOf: + - type: string + pattern: ^[A-Z][a-zA-Z0-9]+\.[A-Z][a-zA-Z0-9]+$ + description: FQN form (recommended) + - type: string + format: uuid + description: UUID form + display_name: + type: string + description: + type: string + version: + type: string + status: + type: string + provider_uuid: + type: string + format: uuid + field_schema: + type: object + description: 'JSON Schema for the request body fields. Each field includes + a constraint block with type: range|enum|pattern|layer_reference|layer_reference_list. + For layer_reference constraints, allowed_values is resolved at render + time from active Reference Data Layer instances of the declared layer_type.' + cost_estimate: + type: object + dependencies: + type: array + items: + type: object + CatalogItemList: + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/CatalogItem' + pagination: + type: object + ServiceRequest: + type: object + required: + - catalog_item_uuid + - fields + properties: + catalog_item_uuid: + type: string + format: uuid + fields: + type: object + description: Resource-type-specific field values + additionalProperties: true + group_uuid: + type: string + format: uuid + description: Assign to a resource group on creation + scheduled_at: + type: string + format: date-time + description: Defer request execution to this time + depends_on: + type: array + items: + type: string + format: uuid + description: Request UUIDs that must complete first + dry_run: + type: boolean + default: false + description: Evaluate policy and placement without submitting + RequestAccepted: + type: object + properties: + request_uuid: + type: string + format: uuid + status: + type: string + stream_url: + type: string + format: uri + RequestStatus: + type: object + properties: + request_uuid: + type: string + format: uuid + status: + type: string + enum: + - ACKNOWLEDGED + - ASSEMBLING + - AWAITING_APPROVAL + - APPROVED + - DISPATCHED + - PROVISIONING + - COMPLETED + - FAILED + - CANCELLED + - SCHEDULED + - PENDING_DEPENDENCY + description: "Consumer request lifecycle status. COMPLETED/FAILED/CANCELLED are terminal." + pipeline_stage: + type: string + entity_uuid: + type: string + format: uuid + description: Set once realization begins + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + error: + $ref: '#/components/schemas/Error' + RequestList: + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/RequestStatus' + pagination: + type: object + RequestGroup: + type: object + required: + - requests + properties: + requests: + type: array + items: + $ref: '#/components/schemas/ServiceRequest' + minItems: 1 + ordered: + type: boolean + default: false + description: Execute in declared order with dependency waiting + group_name: + type: string + RequestGroupAccepted: + type: object + properties: + group_uuid: + type: string + format: uuid + request_uuids: + type: array + items: + type: string + format: uuid + RequestGroupStatus: + type: object + properties: + group_uuid: + type: string + format: uuid + status: + type: string + requests: + type: array + items: + $ref: '#/components/schemas/RequestStatus' + ResourceEntity: + type: object + description: Resource entity as returned by the Consumer API (may be field-filtered + by governance matrix) + properties: + uuid: + type: string + format: uuid + entity_type: + type: string + resource_type: + type: string + lifecycle_state: + type: string + owned_by_tenant_uuid: + type: string + format: uuid + provider_uuid: + type: string + format: uuid + drift_status: + type: string + billing_state: + type: string + ttl_expires_at: + type: string + format: date-time + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + fields: + type: object + additionalProperties: true + description: Resource-type-specific realized fields + relationships: + type: array + items: + type: object + ResourceList: + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/ResourceEntity' + pagination: + type: object + ResourceUpdate: + type: object + required: + - fields + properties: + fields: + type: object + additionalProperties: true + description: Editable fields and their new values (delta only) + reason: + type: string + RehydrateRequest: + type: object + properties: + target_provider_uuid: + type: string + format: uuid + reason: + type: string + DriftRecord: + type: object + properties: + drift_uuid: + type: string + format: uuid + entity_uuid: + type: string + format: uuid + detected_at: + type: string + format: date-time + severity: + type: string + enum: + - minor + - significant + - critical + unsanctioned: + type: boolean + status: + type: string + enum: + - open + - acknowledged + - resolved + - escalated + field_differences: + type: array + items: + type: object + DriftRecordList: + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/DriftRecord' + pagination: + type: object + ApprovalRecord: + type: object + properties: + approval_uuid: + type: string + format: uuid + subject_type: + type: string + subject_uuid: + type: string + format: uuid + required_tier: + type: string + required_quorum: + type: integer + decisions_so_far: + type: integer + expires_at: + type: string + format: date-time + created_at: + type: string + format: date-time + CostEstimate: + type: object + properties: + estimated_monthly_cost: + type: number + currency: + type: string + breakdown: + type: array + items: + type: object + confidence: + type: string + enum: + - high + - medium + - low + CostActuals: + type: object + properties: + entity_uuid: + type: string + format: uuid + period_start: + type: string + format: date-time + period_end: + type: string + format: date-time + total_cost: + type: number + currency: + type: string + breakdown: + type: array + items: + type: object + QuotaStatus: + type: object + properties: + tenant_uuid: + type: string + format: uuid + quotas: + type: array + items: + type: object + WebhookSubscription: + type: object + required: + - endpoint_url + - events + properties: + webhook_uuid: + type: string + format: uuid + readOnly: true + endpoint_url: + type: string + format: uri + events: + type: array + items: + type: string + description: Event types from the DCM Event Catalog + secret: + type: string + description: HMAC-SHA256 signing secret for signature verification + active: + type: boolean + default: true + created_at: + type: string + format: date-time + readOnly: true + SearchResults: + type: object + properties: + results: + type: array + items: + type: object + pagination: + type: object + AuditRecord: + type: object + properties: + audit_uuid: + type: string + format: uuid + entity_uuid: + type: string + format: uuid + event_type: + type: string + actor: + type: object + recorded_at: + type: string + format: date-time + correlation_id: + type: string + format: uuid + payload: + type: object + AuditList: + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/AuditRecord' + pagination: + type: object + PolicyContribution: + type: object + required: + - policy_artifact + properties: + policy_artifact: + type: object + description: DCM policy artifact per B-policy-contract schema + rationale: + type: string + shadow_first: + type: boolean + default: true + ContributionAccepted: + type: object + properties: + contribution_uuid: + type: string + format: uuid + status: + type: string + shadow_mode: + type: boolean + CredentialSummary: + type: object + properties: + credential_uuid: + type: string + format: uuid + credential_type: + type: string + entity_uuid: + type: string + format: uuid + expires_at: + type: string + format: date-time + rotated_at: + type: string + format: date-time + CredentialValue: + type: object + properties: + credential_uuid: + type: string + format: uuid + credential_type: + type: string + value: + type: object + description: Credential-type-specific value (e.g., kubeconfig, token, certificate) + expires_at: + type: string + format: date-time + + WorkloadProfile: + type: object + description: "Workload analysis classification for a DCM resource entity (doc 46)" + properties: + workload_profile_uuid: {type: string, format: uuid} + subject_entity_uuid: {type: string, format: uuid} + analyzed_at: {type: string, format: date-time} + analysis_version: {type: string} + classification: + type: object + properties: + resource_type_match: + type: object + properties: + primary: {type: string} + confidence: {type: string, enum: [high, medium, low, undetermined]} + workload_archetype: + type: object + properties: + type: {type: string, enum: [web_server, database, batch_processor, message_broker, api_gateway, cache, storage, monitoring, unknown]} + confidence: {type: string, enum: [high, medium, low, undetermined]} + migration_readiness: + type: object + properties: + containerization_score: {type: integer, minimum: 1, maximum: 10} + blockers: {type: array, items: {type: string}} + suggested_target: {type: string} + lifecycle_recommendation: + type: object + properties: + dcm_lifecycle_model: {type: string} + rehydration_eligible: {type: boolean} + notes: {type: string} + + # ── Subscription Schemas (doc 50) ──────────────────────────────── + + Subscription: + type: object + required: [subscription_uuid, tenant_uuid, catalog_item_uuid, lifecycle_state] + properties: + subscription_uuid: {type: string, format: uuid} + handle: {type: string} + display_name: {type: string} + tenant_uuid: {type: string, format: uuid} + catalog_item_uuid: {type: string, format: uuid} + resource_type: {type: string} + provider_uuid: {type: string, format: uuid} + lifecycle_state: + type: string + enum: [PENDING, PROVISIONING, ACTIVE, SUSPENDED, RENEWAL_PENDING, TIER_CHANGE_PENDING, EXPIRED, CANCELLED, DECOMMISSIONING, DECOMMISSIONED] + terms: + type: object + properties: + tier: {type: string} + consumption_model: {type: string, enum: [on_demand, reserved, subscription]} + billing_period: {type: string} + auto_renew: {type: boolean} + renewal_advance_notice: {type: string, description: 'ISO 8601 duration'} + started_at: {type: string, format: date-time} + expires_at: {type: string, format: date-time} + terms_version: {type: string} + entitlements: + type: object + properties: + max_instances: {type: integer} + resource_limits: {type: object, additionalProperties: true} + capabilities: {type: array, items: {type: string}} + update_channels: + type: array + items: + type: object + properties: + channel: {type: string} + auto_apply: {type: boolean} + managed_entity_count: {type: integer} + version: {type: string} + created_at: {type: string, format: date-time} + + SubscriptionCreate: + type: object + required: [catalog_item_uuid, consumption_model] + properties: + catalog_item_uuid: {type: string, format: uuid} + consumption_model: {type: string, enum: [on_demand, reserved, subscription]} + subscription_tier: {type: string} + auto_renew: {type: boolean, default: true} + display_name: {type: string} + fields: {type: object, additionalProperties: true} + + SubscriptionUpdate: + type: object + properties: + tier: {type: string} + auto_renew: {type: boolean} + update_channels: + type: array + items: + type: object + properties: + channel: {type: string} + auto_apply: {type: boolean} + + SubscriptionList: + type: object + properties: + subscriptions: {type: array, items: {$ref: '#/components/schemas/Subscription'}} + next_page_token: {type: string} + + SubscriptionUpdateRecord: + type: object + properties: + update_uuid: {type: string, format: uuid} + subscription_uuid: {type: string, format: uuid} + entity_uuid: {type: string, format: uuid} + channel: {type: string} + status: {type: string, enum: [PENDING, APPROVED, REJECTED, APPLIED, FAILED, EXPIRED]} + update_payload: {type: object, additionalProperties: true} + submitted_at: {type: string, format: date-time} + decided_at: {type: string, format: date-time} + auto_applied: {type: boolean} + + SubscriptionUpdateList: + type: object + properties: + updates: {type: array, items: {$ref: '#/components/schemas/SubscriptionUpdateRecord'}} + next_page_token: {type: string} diff --git a/content/docs/schemas/dcm-operator-api.yaml b/content/docs/schemas/dcm-operator-api.yaml new file mode 100644 index 0000000..3831a36 --- /dev/null +++ b/content/docs/schemas/dcm-operator-api.yaml @@ -0,0 +1,621 @@ +openapi: "3.1.0" + +info: + title: DCM Operator Interface — Services API + version: "1.0.0" + description: | + The DCM Services API is the contract between the DCM control plane and Service Provider + operators. DCM calls this API to create, update, discover, and decommission resources. + The operator implements this API; DCM is the client. + + **Conformance levels:** Operators implement one of five conformance levels. Higher levels + unlock additional DCM capabilities. See `dcm-operator-interface-spec.md` for the full + conformance model. + + | Level | Minimum endpoints required | + |-------|---------------------------| + | Level 0 | Label-based passive discovery only — no API required | + | Level 1 | POST /create, GET /list, GET /{id}, DELETE /{id}, POST /health | + | Level 2 | Level 1 + PUT /{id} (update), POST /capacity, POST /discover | + | Level 3 | Level 2 + lifecycle callbacks, decommission_confirmation | + | Level 4 | Level 3 + streaming status, dependency graph, rehydration support | + + **Authentication:** DCM uses mTLS for all operator interactions. Every request presents + a valid DCM-issued certificate. Operators must validate the certificate chain against the + DCM CA registered at provider registration time. + + **Idempotency:** All mutating operations include a `request_id` (DCM request UUID). Operators + must implement idempotency: a second request with the same `request_id` must return the same + result without performing the operation again. + + **Callback pattern:** For long-running operations (create, update, decommission), the operator + responds immediately with `PROVISIONING`/`UPDATING`/`DECOMMISSIONING` status and later + calls the DCM Callback API to report completion or failure. Level 1 operators may alternatively + block until completion. + + + + **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions: + custom methods use colon syntax (`POST /resources/{name}:suspend`), + async operations return an `Operation` resource (AEP-136 LRO), + and list pagination uses `page_size`/`page_token` parameters. + + contact: + name: DCM Project + url: https://github.com/dcm-project + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + +servers: + - url: https://{operator-host}/api/v1/{service_type} + description: Operator endpoint (DCM is the client; operator implements this API) + variables: + operator-host: + description: Operator base URL declared at provider registration + default: operator.namespace.svc + service_type: + description: Resource type path segment (e.g., compute.virtualmachine) + default: compute.virtualmachine + +security: + - MutualTLS: [] + +tags: + - name: resources + description: Resource CRUD operations (Level 1+) + - name: discovery + description: Active discovery of existing resources (Level 2+) + - name: capacity + description: Capacity reporting for placement engine (Level 2+) + - name: lifecycle + description: Lifecycle callback endpoints DCM calls for state transitions (Level 3+) + - name: health + description: Provider health endpoint (all levels) + - name: registration + description: Provider self-registration with DCM + +paths: + + /health: + get: + tags: [health] + operationId: healthCheck + summary: Provider health check (all conformance levels) + description: | + DCM polls this endpoint on the configured interval. Operators must respond within + the `failure_threshold` timeout or DCM will record a failed health check. + security: [] + servers: + - url: https://{operator-host} + variables: + operator-host: + default: operator.namespace.svc + responses: + "200": + description: Provider health status + content: + application/json: + schema: { $ref: "#/components/schemas/HealthResponse" } + + /: + post: + tags: [resources] + operationId: createResource + summary: Create a resource (Level 1+) + description: | + DCM dispatches a Requested State payload. The operator naturalizes it to the + provider-native format and initiates resource creation. + + **Response contract:** Return immediately with `PROVISIONING` status (Level 1 may block + to REALIZED, but this is discouraged for resources taking >30s). Report completion via + the DCM Callback API or via the next discovery cycle. + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/CreateRequest" } + responses: + "202": + description: Request accepted; resource creation initiated + content: + application/json: + schema: { $ref: "#/components/schemas/CreateResponse" } + "200": + description: Resource realized synchronously (Level 1 blocking response) + content: + application/json: + schema: { $ref: "#/components/schemas/RealizedStatePayload" } + "400": { $ref: "#/components/responses/BadRequest" } + "403": { $ref: "#/components/responses/Forbidden" } + "409": + description: Resource already exists with this request_id (idempotency) + content: + application/json: + schema: { $ref: "#/components/schemas/CreateResponse" } + "422": + description: Capacity insufficient or validation failure + content: + application/json: + schema: { $ref: "#/components/schemas/DenialResponse" } + + get: + tags: [resources] + operationId: listResources + summary: List all resources managed by this operator (Level 1+) + parameters: + - name: page_size + in: query + schema: { type: integer, default: 100, maximum: 1000 } + - name: page_token + in: query + schema: { type: string } + - name: lifecycle_state + in: query + schema: { type: string } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/ResourceList" } + + /{resource_id}: + get: + tags: [resources] + operationId: getResource + summary: Get current state of a specific resource (Level 1+) + parameters: + - { $ref: "#/components/parameters/resource_id" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/RealizedStatePayload" } + "404": { $ref: "#/components/responses/NotFound" } + + put: + tags: [resources] + operationId: updateResource + summary: Apply a delta update to a resource (Level 2+) + description: | + DCM sends the delta (changed fields only). The operator applies the changes and + responds immediately with the current lifecycle state. Reports completion via callback. + parameters: + - { $ref: "#/components/parameters/resource_id" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/UpdateRequest" } + responses: + "202": { description: Update accepted and initiated } + "200": + description: Update applied synchronously + content: + application/json: + schema: { $ref: "#/components/schemas/RealizedStatePayload" } + "409": { description: Idempotent response — update already applied } + + delete: + tags: [resources] + operationId: decommissionResource + summary: Decommission and remove a resource (Level 1+) + description: | + DCM sends a decommission request. The operator initiates removal and responds + immediately with `DECOMMISSIONING` status. Reports completion via callback. + + At Level 3+, DCM first sends a `decommission_confirmation` callback to allow + lifecycle policies (retain data, notify stakeholders) to run before deletion proceeds. + parameters: + - { $ref: "#/components/parameters/resource_id" } + requestBody: + content: + application/json: + schema: { $ref: "#/components/schemas/DecommissionRequest" } + responses: + "200": + description: Operation initiated. Poll `operation.name` for completion. + content: + application/json: + schema: { $ref: "#/components/schemas/Operation" } + "409": { description: Resource already decommissioned } + + /discover: + post: + tags: [discovery] + operationId: discoverResources + summary: Trigger active discovery of all resources (Level 2+) + description: | + DCM calls this on the configured schedule to discover the current state of all + resources managed by this operator. The operator returns a complete snapshot of + all currently realized resources in DCM Unified Data Model format. + + This is the mechanism for drift detection — DCM compares Discovered State + (returned here) against Realized State (stored by DCM after last realization). + servers: + - url: https://{operator-host}/api/v1/{service_type} + variables: + operator-host: { default: operator.namespace.svc } + service_type: { default: compute.virtualmachine } + requestBody: + content: + application/json: + schema: { $ref: "#/components/schemas/DiscoverRequest" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/DiscoverResponse" } + "202": + description: Discovery initiated asynchronously; results will be pushed to DCM Callback API + + /capacity: + post: + tags: [capacity] + operationId: reportCapacity + summary: Report current capacity for placement engine queries (Level 2+) + description: | + DCM calls this when evaluating provider placement for a new request. + The operator reports current available, reserved, and committed capacity. + DCM uses this to select the best provider and to avoid over-committing. + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/CapacityQueryRequest" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/CapacityReport" } + +# ─── COMPONENTS ──────────────────────────────────────────────────────────────── + +components: + + securitySchemes: + MutualTLS: + type: mutualTLS + description: | + All DCM → Operator interactions use mTLS. DCM presents its certificate; the + operator must validate the certificate chain against the DCM CA registered at + provider registration time. Operators must also present a valid certificate + to DCM on the reverse check. + + parameters: + resource_id: + name: resource_id + in: path + required: true + schema: { type: string } + description: Operator-assigned resource ID (stable identifier; different from DCM entity UUID) + + responses: + BadRequest: + description: Malformed request + content: + application/json: + schema: { $ref: "#/components/schemas/OperatorError" } + Forbidden: + description: mTLS certificate not trusted or insufficient permissions + content: + application/json: + schema: { $ref: "#/components/schemas/OperatorError" } + NotFound: + description: Resource not found + content: + application/json: + schema: { $ref: "#/components/schemas/OperatorError" } + + schemas: + + HealthResponse: + type: object + required: [status, version] + additionalProperties: false + properties: + status: + type: string + enum: [healthy, degraded, unhealthy] + version: { type: string } + capabilities_available: + type: array + items: { type: string } + conformance_level: + type: integer + minimum: 1 + maximum: 4 + details: + type: object + additionalProperties: true + description: Provider-specific; DCM treats as opaque + + CreateRequest: + type: object + required: [request_id, dcm_entity_uuid, tenant_uuid, resource_type, spec] + additionalProperties: false + properties: + request_id: + type: string + format: uuid + description: DCM request UUID — idempotency key; operators must deduplicate on this + dcm_entity_uuid: + type: string + format: uuid + description: DCM-assigned entity UUID; must be echoed back in all responses and callbacks + tenant_uuid: + type: string + format: uuid + resource_type_uuid: + type: string + format: uuid + resource_type_name: + type: string + description: FQN (e.g., Compute.VirtualMachine) + spec: + type: object + additionalProperties: true + description: | + DCM Unified Data Model fields for this resource. Field names match the + Resource Type Specification schema registered for this resource type. + All fields carry provenance metadata where applicable. + relationships: + type: array + items: + type: object + properties: + relationship_type: { type: string } + target_entity_uuid: { type: string, format: uuid } + target_resource_type: { type: string } + callback_url: + type: string + format: uri + description: DCM Callback API URL for reporting async completion + override_controls: + type: object + additionalProperties: true + description: Field-level override constraints (Level 3+) + scheduled_at: + type: string + format: date-time + description: For scheduled/deferred requests — when to start execution + + CreateResponse: + type: object + required: [resource_id, dcm_request_id, lifecycle_state] + additionalProperties: false + properties: + resource_id: + type: string + description: Operator-assigned stable resource ID + dcm_request_id: + type: string + format: uuid + description: Echoed from request + lifecycle_state: + type: string + enum: [PROVISIONING, REALIZED, FAILED] + estimated_ready_at: + type: string + format: date-time + provider_reference: + type: object + description: Provider-native reference (e.g., Kubernetes namespace/name for CRs) + additionalProperties: true + + UpdateRequest: + type: object + required: [request_id, delta_fields] + additionalProperties: false + properties: + request_id: + type: string + format: uuid + dcm_entity_uuid: + type: string + format: uuid + delta_fields: + type: object + additionalProperties: true + description: Changed fields only — not a full replacement + callback_url: + type: string + format: uri + + DecommissionRequest: + type: object + additionalProperties: false + properties: + request_id: + type: string + format: uuid + dcm_entity_uuid: + type: string + format: uuid + reason: + type: string + retain_data: + type: boolean + default: false + description: If true, operator should retain underlying data (e.g., PVC retention) + callback_url: + type: string + format: uri + + RealizedStatePayload: + type: object + required: [resource_id, dcm_entity_uuid, lifecycle_state, realized_at, spec] + additionalProperties: false + description: | + The Realized State of a resource in DCM Unified Data Model format. + This is what the operator sends back to DCM after realization, and what + DCM stores in the Realized State Store. All provider-native identifiers + should be included in `provider_metadata` — the `spec` must be in DCM format. + properties: + resource_id: + type: string + dcm_entity_uuid: + type: string + format: uuid + dcm_request_id: + type: string + format: uuid + lifecycle_state: + type: string + enum: [PROVISIONING, REALIZED, OPERATIONAL, DEGRADED, FAILED, DECOMMISSIONING, DECOMMISSIONED] + realized_at: + type: string + format: date-time + spec: + type: object + additionalProperties: true + description: Realized field values in DCM Unified Data Model format + provider_metadata: + type: object + additionalProperties: true + description: Provider-native metadata (opaque to DCM; stored for operator use) + failure_reason: + type: string + description: Present when lifecycle_state is FAILED + + DenialResponse: + type: object + required: [request_id, denial_reason, denial_timestamp] + additionalProperties: false + properties: + request_id: + type: string + format: uuid + denial_reason: + type: string + enum: [INSUFFICIENT_RESOURCES, VALIDATION_FAILED, POLICY_REJECTED, UNSUPPORTED_CONFIGURATION] + denial_timestamp: + type: string + format: date-time + resource_type_uuid: + type: string + format: uuid + estimated_available_at: + type: string + format: date-time + details: + type: string + + DiscoverRequest: + type: object + additionalProperties: false + properties: + scope: + type: string + enum: [full, delta] + default: full + since: + type: string + format: date-time + description: For delta discovery — only return resources changed since this time + + DiscoverResponse: + type: object + required: [discovery_timestamp, resources] + additionalProperties: false + properties: + discovery_timestamp: + type: string + format: date-time + resources: + type: array + items: { $ref: "#/components/schemas/RealizedStatePayload" } + pagination: + type: object + properties: + page_token: { type: string } + has_more: { type: boolean } + + CapacityQueryRequest: + type: object + additionalProperties: false + properties: + resource_type_name: { type: string } + requested_spec: { type: object, additionalProperties: true } + tenant_uuid: { type: string, format: uuid } + + CapacityReport: + type: object + required: [provider_id, report_timestamp, capacity] + additionalProperties: false + properties: + provider_id: + type: string + format: uuid + report_timestamp: + type: string + format: date-time + next_report_at: + type: string + format: date-time + capacity: + type: object + required: [available_units, reserved_units, committed_units] + additionalProperties: false + properties: + available_units: { type: integer, minimum: 0 } + reserved_units: { type: integer, minimum: 0 } + committed_units: { type: integer, minimum: 0 } + unit_definition: { type: string, description: "What one 'unit' means (e.g., '1 vCPU + 2GB RAM')" } + confidence: { type: string, enum: [high, medium, low], default: high } + can_fulfill: + type: boolean + description: Whether the requested_spec (if provided) can be fulfilled + + OperatorError: + type: object + required: [error] + additionalProperties: false + properties: + error: + type: object + required: [code, message] + properties: + code: { type: string } + message: { type: string } + + Operation: + type: object + description: | + AEP-136 Long-Running Operation returned by async create/update/decommission responses. + DCM polls this until done is true. + required: [name, done] + additionalProperties: false + properties: + name: + type: string + description: Stable operation resource path + done: + type: boolean + default: false + metadata: + type: object + properties: + resource_id: { type: string } + resource_type: { type: string } + operation_type: { type: string, enum: [create, update, decommission] } + response: + type: object + description: Present when done=true and successful. Contains RealizedStatePayload. + error: + $ref: "#/components/schemas/OperatorError" + description: Present when done=true and failed. + + ResourceList: + type: object + description: List of discovered resources returned by the discover endpoint. + required: [resources] + additionalProperties: false + properties: + resources: + type: array + items: + $ref: "#/components/schemas/RealizedStatePayload" + total_count: + type: integer + description: Total number of resources discovered diff --git a/content/docs/schemas/dcm-provider-callback-api.yaml b/content/docs/schemas/dcm-provider-callback-api.yaml new file mode 100644 index 0000000..e6a3ac0 --- /dev/null +++ b/content/docs/schemas/dcm-provider-callback-api.yaml @@ -0,0 +1,868 @@ +openapi: "3.1.0" + +info: + title: DCM Provider Callback API + version: "1.0.0" + description: | + The DCM Provider Callback API defines the endpoints that the DCM control plane exposes + for Service Provider operators to call. Operators use these endpoints to: + + - Register with DCM and declare their capabilities + - Report capacity for placement engine decisions + - Push realized state after resource creation or update (the Denaturalization step) + - Report interim progress on long-running operations + - Notify DCM of authorized provider-side state changes + - Report lifecycle events (health changes, unsanctioned changes, maintenance windows) + + **Authentication — Two-Layer Model** (see `43-provider-callback-auth.md` for full specification): + + - **Layer 1 — mTLS:** Every connection requires the provider's registered certificate. + DCM validates the certificate chain against the CA registered at provider activation. + Proves transport-level identity. + + - **Layer 2 — Provider Callback Credential:** Every call requires an `Authorization: Bearer` + header containing the provider's active callback credential. This credential is a + `dcm_interaction` type credential issued by DCM's credential management system at activation time. + It is scoped to the specific `provider_uuid` and a set of allowed operations. It is + short-lived (profile-governed: PT15M for fsi/sovereign, PT1H for standard) and must be + rotated before expiry. DCM initiates rotation automatically. + + - **Entity-level authorization** is checked per call independent of credential validity: + DCM verifies that the calling provider is the provider that was dispatched to for the + specific entity. A valid credential does not grant access to entities hosted at other + providers or entities the provider was not dispatched to. + + - **Registration calls** use a registration token (single-use, admin-issued) rather than + the callback credential — no callback credential exists until activation completes. + + **Idempotency:** All mutating operations are idempotent. Use the same `request_id` or + `notification_uuid` to safely retry without side effects. + + **Direction:** This is the reverse of the Operator Interface Services API. The Operator + Interface defines endpoints operators implement (DCM calls them). This document defines + endpoints DCM implements (operators call them). + + **Conformance level requirements** are noted per endpoint. Level 1 operators only need + registration and realized state push. Level 2+ add capacity, events, and update notifications. + + + + **AEP Alignment:** This API follows [AEP](https://aep.dev) conventions: + custom methods use colon syntax (`POST /resources/{name}:suspend`), + async operations return an `Operation` resource (AEP-136 LRO), + and list pagination uses `page_size`/`page_token` parameters. + + contact: + name: DCM Project + url: https://github.com/dcm-project + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + +servers: + - url: https://{dcm-host}/ + description: DCM Control Plane + variables: + dcm-host: + description: Hostname of the DCM control plane + default: dcm.example.com + +security: + - ProviderCredential: [] + +tags: + - name: registration + description: Provider registration and capacity reporting (Level 1+) + - name: realized-state + description: Realized state push — the Denaturalization step (Level 1+) + - name: interim-status + description: Interim progress updates for long-running operations (Level 1+, optional) + - name: update-notifications + description: Provider-initiated authorized state change notifications (Level 2+) + - name: lifecycle-events + description: Resource lifecycle and health event reporting (Level 2+) + +paths: + + # ─── REGISTRATION ───────────────────────────────────────────────────────── + + /api/v1/providers: + post: + tags: [registration] + operationId: registerProvider + summary: Register or update a provider registration with DCM (Level 1+) + description: | + Called by the operator on startup to register with DCM. Registration is idempotent — + re-registering with the same `name` updates the existing registration rather than + creating a duplicate. Safe to call on every operator restart. + + On first registration, DCM creates a new provider record in SUBMITTED status and + routes it through the approval pipeline. On re-registration with the same name, + DCM updates the version, capabilities, and endpoints without re-triggering approval + (unless the sovereignty declaration changes). + + **Timing:** Call after the operator's HTTP server is ready to receive requests. + Retry with exponential backoff on failure. Registration failure does not block + operator startup — the operator functions normally for Kubernetes consumers. + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderRegistrationRequest" } + responses: + "200": + description: Registration updated (existing provider) + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderRegistrationResponse" } + "201": + description: New registration created; pending approval + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderRegistrationResponse" } + "400": { $ref: "#/components/responses/BadRequest" } + "409": + description: Registration conflict — name already registered with different sovereignty declaration + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderError" } + + /api/v1/providers/{provider_id}/capacity: + post: + tags: [registration] + operationId: reportCapacity + summary: Push capacity report to DCM for placement engine decisions (Level 2+) + description: | + Operators push capacity reports on a scheduled interval (declared at registration). + DCM uses this data to make placement decisions and to avoid over-committing resources. + + Reports are also accepted on-demand when capacity changes significantly (e.g., after + a node is added or removed). Providers should not push more than once per minute + unless responding to a significant capacity event. + parameters: + - { $ref: "#/components/parameters/provider_id" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/CapacityReport" } + responses: + "202": + description: Capacity report accepted + "400": { $ref: "#/components/responses/BadRequest" } + "403": { $ref: "#/components/responses/Forbidden" } + "429": + description: Rate limit exceeded — capacity reports accepted max once per 60 seconds + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderError" } + + # ─── REALIZED STATE ─────────────────────────────────────────────────────── + + /api/v1/instances/{resource_id}/status: + put: + tags: [realized-state] + operationId: pushRealizedState + summary: Push realized or terminal state to DCM — the Denaturalization step (Level 1+) + description: | + Called by the operator's reconciliation loop when a resource reaches a terminal + lifecycle state (OPERATIONAL, FAILED, DEGRADED, DECOMMISSIONED) or when the + operator needs to report a provider-native state transition. + + This is the **Denaturalization** step: the operator translates Kubernetes-native + CR status into DCM Unified Data Model format and pushes it here. DCM stores the + result as a Realized State record in the append-only Realized State Store. + + **Idempotency:** Use the same `dcm_request_id` to safely retry. DCM will not + create duplicate Realized State records for the same `dcm_request_id` + terminal state. + + **Required for decommission:** When a resource is fully removed, the operator must + push a payload with `lifecycle_state: DECOMMISSIONED`. This closes the entity's + lifecycle in DCM and releases any held allocations or relationships. + + **Level 3 — provenance required:** Level 3 conformance requires `field_provenance` + to be populated for all realized fields. This enables full audit chain. + parameters: + - { $ref: "#/components/parameters/resource_id" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/RealizedStatePush" } + responses: + "200": + description: Realized state accepted and stored + content: + application/json: + schema: { $ref: "#/components/schemas/RealizedStateAck" } + "400": { $ref: "#/components/responses/BadRequest" } + "403": { $ref: "#/components/responses/Forbidden" } + "404": + description: resource_id not recognized by DCM — provider may need to re-register + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderError" } + "409": + description: | + Idempotent response — this request_id + terminal state already recorded. + The response body contains the existing Realized State record UUID. + content: + application/json: + schema: { $ref: "#/components/schemas/RealizedStateAck" } + + # ─── INTERIM STATUS ─────────────────────────────────────────────────────── + + /api/v1/provider/entities/{entity_uuid}/status: + post: + tags: [interim-status] + operationId: pushInterimStatus + summary: Push interim progress update for a long-running operation (Level 1+, optional) + description: | + Operators may push interim status updates for long-running operations (provisioning + complex resources, compound service constituents) to give DCM — and therefore + consumers — live visibility into multi-step operations without waiting for terminal state. + + DCM uses interim status to: + - Update `current_step` and progress fields in the request status response + - Publish `request.progress_updated` event (info urgency) to the Message Bus + - Deliver live updates to consumers via SSE stream + + **Rate limit:** Maximum one update per 10 seconds per `entity_uuid`. DCM rate-limits + interim status calls and will return 429 if the limit is exceeded. + + **Not a replacement for terminal state:** Interim status supplements the realized + state push. Operators must still call `PUT /api/v1/instances/{resource_id}/status` + to report terminal state. + + **For compound/Meta Provider operations:** Include `constituent_status` to give + visibility into each constituent's progress independently. + parameters: + - { $ref: "#/components/parameters/entity_uuid" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/InterimStatusPush" } + responses: + "202": + description: Interim status accepted; consumers and SSE stream updated + "400": { $ref: "#/components/responses/BadRequest" } + "403": { $ref: "#/components/responses/Forbidden" } + "429": + description: Rate limit exceeded — max one interim status per 10 seconds per entity + headers: + Retry-After: + schema: { type: integer } + description: Seconds until the rate limit resets + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderError" } + + # ─── UPDATE NOTIFICATIONS ───────────────────────────────────────────────── + + /api/v1/provider/entities/{entity_uuid}/update-notification: + post: + tags: [update-notifications] + operationId: submitUpdateNotification + summary: Notify DCM of an authorized provider-initiated state change (Level 2+) + description: | + Called when the provider has made an authorized state change to a resource + outside of a DCM-initiated request (e.g., auto-scaling, auto-healing, maintenance). + + **Key principle:** Providers never write directly to DCM's Realized State. They + submit a notification here; DCM processes it through the governance pipeline + (Policy Engine evaluation); DCM writes the Realized State only if approved. + + **Distinct from drift:** A provider submitting an update notification is asserting + that the change was authorized by a pre-existing policy or operational agreement. + Drift is detected through discovery and represents an unauthorized or untracked change. + + **Pre-authorization declarations:** Providers may declare categories of updates + they routinely make at registration time, enabling organizations to pre-authorize + them in policy rather than reviewing each one. Pre-authorized notification types + receive `AUTO_APPROVED` status immediately. + + **Idempotency:** Use the same `notification_uuid` to safely retry. DCM will not + create duplicate Realized State records for the same `notification_uuid`. + parameters: + - { $ref: "#/components/parameters/entity_uuid" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/UpdateNotificationRequest" } + responses: + "202": + description: Notification accepted; processing begun + content: + application/json: + schema: { $ref: "#/components/schemas/UpdateNotificationAccepted" } + "400": { $ref: "#/components/responses/BadRequest" } + "403": { $ref: "#/components/responses/Forbidden" } + "409": + description: Idempotent — notification_uuid already processed + content: + application/json: + schema: { $ref: "#/components/schemas/UpdateNotificationAccepted" } + + /api/v1/provider/notifications/{notification_uuid}: + get: + tags: [update-notifications] + operationId: getNotificationStatus + summary: Poll the processing status of a submitted update notification (Level 2+) + description: | + Allows providers to check whether a submitted update notification has been + approved, rejected, or is still awaiting consumer approval. + + Providers should poll with backoff rather than polling at high frequency. + Alternatively, subscribe to the `provider_update.*` event domain via webhook + for push notification of decision outcomes. + parameters: + - { $ref: "#/components/parameters/notification_uuid" } + responses: + "200": + content: + application/json: + schema: { $ref: "#/components/schemas/UpdateNotificationStatus" } + "404": { $ref: "#/components/responses/NotFound" } + + # ─── LIFECYCLE EVENTS ───────────────────────────────────────────────────── + + /api/v1/instances/{resource_id}/events: + post: + tags: [lifecycle-events] + operationId: reportLifecycleEvent + summary: Report a resource lifecycle or health event to DCM (Level 2+) + description: | + Called by operators to notify DCM of events affecting the operational status of + a managed resource. DCM receives the event, evaluates it through the Policy Engine, + and determines the appropriate response (notify consumer, trigger recovery, escalate). + + DCM acts as the Tenant advocate — it does not expose raw infrastructure events to + consumers directly. It applies policy, filters urgency, and routes notifications + to the appropriate audience based on the governance model. + + **Idempotency:** Use the same `event_uuid` to safely retry. DCM will not create + duplicate records for the same `event_uuid`. + + **UNSANCTIONED_CHANGE:** When the provider detects a CR was modified without a + corresponding DCM request ID (i.e., someone modified Kubernetes resources directly), + report this as `UNSANCTIONED_CHANGE`. DCM will flag this as drift, notify the Tenant, + and fire the appropriate Recovery Policy. + parameters: + - { $ref: "#/components/parameters/resource_id" } + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/LifecycleEventReport" } + responses: + "202": + description: Event accepted; DCM policy evaluation initiated + "400": { $ref: "#/components/responses/BadRequest" } + "403": { $ref: "#/components/responses/Forbidden" } + "409": + description: Idempotent — event_uuid already recorded + +# ─── COMPONENTS ──────────────────────────────────────────────────────────────── + +components: + + securitySchemes: + ProviderCredential: + type: http + scheme: bearer + description: | + Short-lived provider interaction credential issued by DCM at registration time. + Scoped to the specific provider UUID. Must be rotated before expiry. Rotation + is managed via the credential management system integration declared at registration. + + parameters: + + provider_id: + name: provider_id + in: path + required: true + schema: { type: string, format: uuid } + description: DCM-assigned provider UUID + + resource_id: + name: resource_id + in: path + required: true + schema: { type: string } + description: Operator-assigned resource ID (as returned in CreateResponse) + + entity_uuid: + name: entity_uuid + in: path + required: true + schema: { type: string, format: uuid } + description: DCM entity UUID (provided by DCM in the CreateRequest) + + notification_uuid: + name: notification_uuid + in: path + required: true + schema: { type: string, format: uuid } + description: Provider-assigned notification UUID (from UpdateNotificationRequest) + + responses: + + BadRequest: + description: Malformed request payload + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderError" } + + Forbidden: + description: Invalid or expired provider credential, or credential scoped to different provider + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderError" } + + NotFound: + description: Resource or notification not found + content: + application/json: + schema: { $ref: "#/components/schemas/ProviderError" } + + schemas: + + ProviderError: + type: object + required: [error] + additionalProperties: false + properties: + error: + type: object + required: [code, message, request_id] + additionalProperties: false + properties: + code: { type: string } + message: { type: string } + request_id: { type: string, format: uuid } + + ProviderRegistrationRequest: + type: object + required: [name, display_name, conformance_level, endpoint, version, service_types] + additionalProperties: false + properties: + name: + type: string + description: Unique provider name — natural key for idempotent re-registration + pattern: "^[a-z0-9][a-z0-9-_/]*[a-z0-9]$" + display_name: { type: string, maxLength: 256 } + conformance_level: + type: integer + minimum: 1 + maximum: 4 + endpoint: + type: string + format: uri + description: Base URL of the operator's DCM Services API + version: + type: string + pattern: "^\\d+\\.\\d+\\.\\d+$" + service_types: + type: array + minItems: 1 + items: + type: object + required: [service_type, service_type_uuid, operations_supported] + additionalProperties: false + properties: + service_type: + type: string + description: DCM Resource Type FQN (e.g., Storage.Database) + pattern: "^[A-Z][a-zA-Z0-9]+\\.[A-Z][a-zA-Z0-9]+$" + service_type_uuid: + type: string + format: uuid + crd_reference: + type: object + description: Kubernetes CRD reference (Kubernetes operators only) + additionalProperties: false + properties: + group: { type: string } + version: { type: string } + kind: { type: string } + operations_supported: + type: array + items: + type: string + enum: [CREATE, READ, UPDATE, DELETE, DISCOVER] + field_mapping_ref: + type: string + format: uri + description: URL or reference to the field mapping declaration (Level 2+) + kubernetes: + type: object + description: Kubernetes-specific registration metadata (Kubernetes operators only) + additionalProperties: false + properties: + cluster_id: { type: string } + cluster_endpoint: { type: string, format: uri } + namespace_strategy: + type: string + enum: [per_tenant, shared, per_resource] + sovereignty_declaration: + type: object + description: Where this provider operates and which jurisdictions it is subject to + additionalProperties: false + properties: + operating_jurisdictions: + type: array + items: { type: string, pattern: "^[A-Z]{2}$" } + data_residency_zones: + type: array + items: { type: string } + regulatory_frameworks: + type: array + items: { type: string } + update_capabilities: + type: array + description: Pre-authorization declarations for provider-initiated updates (Level 2+) + items: + type: object + required: [notification_type, affected_fields] + additionalProperties: false + properties: + notification_type: + type: string + enum: [authorized_change, maintenance_change, auto_scale, auto_heal] + affected_fields: + type: array + items: { type: string } + max_change_magnitude: { type: string } + typical_trigger: { type: string } + cancellation_capabilities: + type: object + description: Request cancellation support declaration (Level 2+) + additionalProperties: false + properties: + supports_cancellation: { type: boolean } + cancellation_supported_during: + type: array + items: { type: string, enum: [DISPATCHED, PROVISIONING] } + partial_rollback_possible: { type: boolean } + cancellation_response_time_seconds: { type: integer } + + ProviderRegistrationResponse: + type: object + required: [provider_id, name, status, conformance_level_accepted] + additionalProperties: false + properties: + provider_id: + type: string + format: uuid + description: DCM-assigned provider UUID — stable across re-registrations + name: { type: string } + status: + type: string + enum: [registered, updated, pending_approval] + conformance_level_accepted: { type: integer } + capabilities_enabled: + type: array + items: { type: string } + credential_ref: + type: string + description: Reference to the provider interaction credential (retrieve via credential management system) + credential_expires_at: + type: string + format: date-time + + CapacityReport: + type: object + required: [provider_id, report_timestamp, capacity_by_service_type] + additionalProperties: false + properties: + provider_id: + type: string + format: uuid + report_timestamp: + type: string + format: date-time + next_report_at: + type: string + format: date-time + description: When the provider will send the next scheduled report + capacity_by_service_type: + type: array + minItems: 1 + items: + type: object + required: [service_type_uuid, available_units, reserved_units, committed_units] + additionalProperties: false + properties: + service_type_uuid: { type: string, format: uuid } + available_units: { type: integer, minimum: 0 } + reserved_units: { type: integer, minimum: 0 } + committed_units: { type: integer, minimum: 0 } + unit_definition: + type: string + description: What one unit represents (e.g., "1 database cluster", "1 vCPU") + kubernetes_resources: + type: object + description: Raw Kubernetes resource availability (Kubernetes operators only) + additionalProperties: false + properties: + available_cpu_millicores: { type: integer } + available_memory_bytes: { type: integer } + available_storage_bytes: { type: integer } + node_count: { type: integer } + + RealizedStatePush: + type: object + required: [resource_id, dcm_entity_uuid, lifecycle_state, realized_at, spec] + additionalProperties: false + description: | + The Denaturalization payload — provider-native state translated into DCM Unified + Data Model format. DCM stores this in the append-only Realized State Store. + properties: + resource_id: + type: string + description: Operator-assigned resource ID + dcm_entity_uuid: + type: string + format: uuid + description: DCM entity UUID (provided by DCM in the CreateRequest) + dcm_request_id: + type: string + format: uuid + description: DCM request UUID (idempotency key — provide for all non-spontaneous state changes) + lifecycle_state: + type: string + enum: [PROVISIONING, REALIZED, OPERATIONAL, DEGRADED, FAILED, DECOMMISSIONING, DECOMMISSIONED] + realized_at: + type: string + format: date-time + spec: + type: object + additionalProperties: true + description: All realized field values in DCM Unified Data Model format + field_provenance: + type: object + additionalProperties: + type: object + properties: + source_type: { type: string, enum: [provider, operator, kubernetes_status] } + source_uuid: { type: string, format: uuid } + timestamp: { type: string, format: date-time } + description: Per-field provenance. Required for Level 3 conformance. + provider_metadata: + type: object + additionalProperties: true + description: Provider-native metadata stored by DCM but treated as opaque + kubernetes_reference: + type: object + description: Kubernetes CR reference (Kubernetes operators only) + additionalProperties: false + properties: + namespace: { type: string } + name: { type: string } + uid: { type: string } + resource_version: { type: string } + relationships: + type: array + description: Any relationships created or discovered during realization + items: + type: object + properties: + relationship_type: { type: string } + target_entity_uuid: { type: string, format: uuid } + failure_reason: + type: string + description: Required when lifecycle_state is FAILED + + RealizedStateAck: + type: object + required: [realized_state_uuid, entity_uuid, lifecycle_state] + additionalProperties: false + properties: + realized_state_uuid: { type: string, format: uuid } + entity_uuid: { type: string, format: uuid } + lifecycle_state: { type: string } + recorded_at: { type: string, format: date-time } + + InterimStatusPush: + type: object + required: [request_id, lifecycle_state] + additionalProperties: false + properties: + request_id: + type: string + format: uuid + description: DCM request UUID this status update corresponds to + lifecycle_state: + type: string + enum: [PROVISIONING, UPDATING, DECOMMISSIONING] + description: Current non-terminal state + progress: + type: object + additionalProperties: false + properties: + step_current: { type: integer, minimum: 1 } + step_total: { type: integer, minimum: 1 } + step_label: { type: string } + step_started_at: { type: string, format: date-time } + estimated_completion: { type: string, format: date-time } + constituent_status: + type: array + description: Per-constituent status for compound/Meta Provider operations + items: + type: object + required: [ref, status] + additionalProperties: false + properties: + ref: { type: string, description: "Constituent identifier (matches Meta Provider composition declaration)" } + status: { type: string, enum: [PENDING, PROVISIONING, REALIZED, FAILED] } + completed_at: { type: string, format: date-time } + started_at: { type: string, format: date-time } + notes: + type: string + maxLength: 512 + description: Optional human-readable detail for consumer display + + UpdateNotificationRequest: + type: object + required: [provider_uuid, notification_uuid, notification_type, changed_fields, effective_at] + additionalProperties: false + properties: + provider_uuid: + type: string + format: uuid + notification_uuid: + type: string + format: uuid + description: Provider-assigned idempotency key — must be unique per change event + notification_type: + type: string + enum: [authorized_change, maintenance_change, auto_scale, auto_heal] + description: | + authorized_change: explicitly authorized by policy. + maintenance_change: result of a maintenance window. + auto_scale: automatic scaling action. + auto_heal: automatic healing/recovery action. + changed_fields: + type: object + additionalProperties: + type: object + required: [previous_value, new_value] + additionalProperties: false + properties: + previous_value: { description: "Value before the change" } + new_value: { description: "Value after the change" } + change_reason: { type: string } + authorizing_policy_ref: + oneOf: [{ type: string, format: uuid }, { type: "null" }] + description: UUID of the DCM policy that pre-authorizes this change type + effective_at: + type: string + format: date-time + description: When the change took effect on the provider side + provider_evidence_ref: + type: string + description: Provider-side reference (e.g., Kubernetes event UID, cloud audit log ID) + + UpdateNotificationAccepted: + type: object + required: [notification_uuid, status, entity_uuid] + additionalProperties: false + properties: + notification_uuid: { type: string, format: uuid } + entity_uuid: { type: string, format: uuid } + status: + type: string + enum: [processing, auto_approved, pending_consumer_approval, pending_admin_approval] + realized_state_uuid: + oneOf: [{ type: string, format: uuid }, { type: "null" }] + description: Set immediately when status is auto_approved + notification_status_url: + type: string + format: uri + description: URL to poll for status updates + + UpdateNotificationStatus: + type: object + required: [notification_uuid, status, entity_uuid] + additionalProperties: false + properties: + notification_uuid: + type: string + format: uuid + status: + type: string + enum: [processing, approved, auto_approved, pending_approval, rejected] + entity_uuid: { type: string, format: uuid } + realized_state_uuid: + oneOf: [{ type: string, format: uuid }, { type: "null" }] + consumer_approval_required: { type: boolean } + consumer_notified_at: + oneOf: [{ type: string, format: date-time }, { type: "null" }] + resolved_at: + oneOf: [{ type: string, format: date-time }, { type: "null" }] + rejection_reason: + oneOf: [{ type: string }, { type: "null" }] + + LifecycleEventReport: + type: object + required: [event_uuid, event_type, provider_id, resource_id, dcm_entity_uuid, event_timestamp, severity] + additionalProperties: false + properties: + event_uuid: + type: string + format: uuid + description: Provider-assigned idempotency key + event_type: + type: string + enum: + - ENTITY_HEALTH_CHANGE + - DEGRADATION + - MAINTENANCE_SCHEDULED + - MAINTENANCE_STARTED + - MAINTENANCE_COMPLETED + - UNSANCTIONED_CHANGE + - CAPACITY_CHANGE + - DECOMMISSION_NOTICE + - PROVIDER_DEGRADATION + provider_id: + type: string + format: uuid + resource_id: + type: string + dcm_entity_uuid: + type: string + format: uuid + event_timestamp: + type: string + format: date-time + severity: + type: string + enum: [INFO, WARNING, CRITICAL] + requires_immediate_action: { type: boolean, default: false } + details: + type: object + additionalProperties: true + description: | + Event-type-specific detail. Common patterns: + + For UNSANCTIONED_CHANGE: + changed_fields: [{field_path, previous_value, new_value}] + change_source: "direct_kubernetes_edit" | "external_tool" + + For DEGRADATION: + degraded_components: [{component, status, detail}] + impact_on_consumers: "none" | "reduced_performance" | "partial_outage" + + For MAINTENANCE_SCHEDULED: + maintenance_window: {start_at, end_at, window_type} + expected_impact: "none" | "brief_unavailability" | "full_unavailability" + + For CAPACITY_CHANGE: + previous_available_units: integer + new_available_units: integer + change_reason: string + related_request_id: + oneOf: [{ type: string, format: uuid }, { type: "null" }] + description: DCM request UUID if this event is related to an in-progress operation diff --git a/content/docs/taxonomy.md b/content/docs/taxonomy.md new file mode 100644 index 0000000..5ec1156 --- /dev/null +++ b/content/docs/taxonomy.md @@ -0,0 +1,415 @@ +# DCM Taxonomy + +The DCM taxonomy defines the precise vocabulary used throughout the architecture. Every term used in the data model, specifications, and implementation should conform to these definitions. + +**Purpose:** Eliminate ambiguity. When two people use the same word to mean different things, architecture breaks down. This taxonomy prevents that. + +--- + +## Part 1 — Core Vocabulary + +### The Three Foundational Abstractions + +| Term | Definition | +|------|-----------| +| **Data** | Any structured artifact in DCM with a type, UUID, lifecycle state, fields, data classification, and provenance. Everything that exists, is stored, and has a lifecycle. Entities, layers, policies, accreditations, audit records, groups, relationships — all Data. | +| **Provider** | Any external component DCM calls or that calls DCM. Implements the unified Provider base contract (registration, health, sovereignty, accreditation, governance matrix enforcement, zero trust). What varies between provider types is the capability extension. | +| **Policy** | A rule artifact that fires when Data matches declared conditions, produces a typed output (decision, mutation, action, or directive), and is enforced at a declared level. Policies govern every transition, transformation, and constraint in DCM. | + +### Provider Types (11) + +| Term | Definition | +|------|-----------| +| **Service Provider** | Typed Provider. Capability: realize infrastructure resources. Implements naturalization, realization, denaturalization, and discovery. | +| **Information Provider** | Typed Provider. Capability: serve authoritative external data (CMDB, HR, Finance, identity). | +| **Storage Provider** | Typed Provider. Capability: persist DCM state. Sub-types: GitOps, write-once snapshot, event stream, search index, audit. | +| **Meta Provider** | Typed Provider. Capability: compose multiple child providers into a compound service delivered as a single catalog item. | +| **Policy Provider** | Typed Provider. Capability: evaluate policies externally. Modes 1–4; Mode 3–4 for OPA/Rego sidecar and black-box query enrichment. | +| **Credential Provider** | Typed Provider. Capability: issue, rotate, and revoke secrets and credentials. | +| **Auth Provider** | Typed Provider. Capability: authenticate actor identities and resolve role/group memberships. | +| **Notification Provider** | Typed Provider. Capability: deliver notification envelopes to configured channels (Slack, PagerDuty, email, webhook). | +| **Message Bus Provider** | Typed Provider. Capability: async event streaming between DCM and external systems. | +| **Registry Provider** | Typed Provider. Capability: serve the Resource Type Registry (core, community, organization tiers). | +| **Peer DCM** | Typed Provider. Another DCM instance participating in federation. Federation is the Provider abstraction applied across DCM instances. | + +### Policy Types (7) + +| Term | Definition | +|------|-----------| +| **GateKeeper Policy** | Typed Policy. Declares `enforcement_class: compliance` (boolean deny — halts request) or `operational` (contributes `risk_score_contribution` to request risk score). Compliance-class is the default and fail-safe. See [Scoring Model](data-model/scoring-model/). | +| **Validation Policy** | Typed Policy. Declares `output_class: structural` (boolean pass/fail — halts on fail) or `advisory` (contributes completeness score + warning list without blocking). Structural is the default and fail-safe. See [Scoring Model](data-model/scoring-model/). | +| **Transformation Policy** | Typed Policy. Output: mutations[] — field additions, changes, locks. Fires on request payload. All mutations collected and applied with provenance. | +| **Recovery Policy** | Typed Policy. Output: action + parameters. Fires on failure/timeout trigger conditions. Governs what DCM does when things go wrong. | +| **Orchestration Flow Policy** | Typed Policy. Output: ordered step sequence. Fires on pipeline payload type events. Named workflow artifacts — the explicit, visible pipeline skeleton. | +| **Governance Matrix Rule** | Typed Policy. Output: ALLOW / DENY / ALLOW_WITH_CONDITIONS / STRIP_FIELD / REDACT / AUDIT_ONLY. Fires at every cross-boundary interaction. The single enforcement point for all data/capability boundary decisions. | +| **Lifecycle Policy** | Typed Policy. Output: action on related entity (cascade, protect, detach, notify). Fires on relationship events. | + +### Data Model Terms + +| Term | Definition | +|------|-----------| +| **Entity** | A Resource Entity, Process Entity, or Composite Entity — the primary managed thing in DCM. Has a UUID that is stable across all four lifecycle states. | +| **Four States** | Intent (consumer declaration), Requested (assembled/policy-validated), Realized (provider-confirmed), Discovered (independently observed). Same entity at four lifecycle stages in four specialized stores. | +| **Data Layer** | A versioned data artifact that contributes fields to request payload assembly. Types: Base, Core, Intermediate, Service, Request. Each has a declared contributor type. | +| **Resource Type Specification** | The schema definition for a resource type. Declares fields, constraints, portability class, dependency graph, and field criticality. | +| **Provider Catalog Item** | A provider-specific instantiation of a Resource Type Specification. What consumers actually request from the service catalog. | +| **Artifact Metadata** | Standard metadata block on every DCM artifact: uuid, handle, version, status, owned_by, created_by, contributed_by. | +| **Provenance** | Field-level lineage metadata embedded in every payload field, recording origin and all modifications. | +| **Data Classification** | Field-level metadata: public / internal / confidential / restricted / phi / pci / sovereign / classified. Phi, sovereign, and classified are immutable once set. | +| **Sovereignty Zone** | A registered DCM artifact declaring a geopolitical/regulatory boundary. Rules reference zones by ID, not raw country codes. | +| **Accreditation** | A formal, versioned, time-bounded attestation that a DCM component satisfies a specific compliance framework. First-class Data artifact with its own lifecycle. | +| **DCMGroup** | Universal grouping artifact with typed group_class: tenant_boundary, resource_grouping, policy_collection, policy_profile, composite, federation. | +| **Drift Record** | A Data artifact produced by the Drift Reconciliation Component recording field-by-field comparison of Realized vs Discovered state with severity classification. | +| **Governance Matrix Rule** | A Data artifact (also a Policy type) — a rule artifact governing cross-boundary interactions using four-axis match conditions. | + +### Operational Terms + +| Term | Definition | +|------|-----------| +| **Request Orchestrator** | The event bus. Routes lifecycle events to the Policy Engine. Contains no pipeline logic — Policies define all behavior. | +| **Policy Engine** | Evaluates all policy types using the same algorithm. The single policy evaluator — no component bypasses it. | +| **Placement Engine** | Six-step provider selection: sovereignty filter → accreditation filter → capability filter → parallel reserve queries → tie-breaking (policy/priority/affinity/cost/load/hash) → confirm. | +| **Drift Reconciliation** | Control plane component. Compares Discovered State vs Realized State. Produces drift records and events. Never writes to the Realized Store. | +| **Shadow Mode** | A Policy in `proposed` status evaluates against real traffic; output is captured but never applied. The primary mechanism for safe policy change management. | +| **Naturalization** | Service Provider converts a DCM unified payload to provider-native format before execution. | +| **Denaturalization** | Service Provider converts provider-native result back to DCM unified format after execution. | +| **Rehydration** | Replaying a resource's intent state to a new provider or context. Produces a new Requested State from the existing Intent State. | +| **Contributor** | An actor type that authored a Data artifact. Recorded in artifact_metadata.contributed_by. Types: platform_admin, consumer, service_provider, peer_dcm. Determines review requirements. | +| **Two-Level Orchestration** | Level 1: Named Workflow Artifacts (Orchestration Flow Policy, ordered: true) — explicit sequence skeleton. Level 2: Dynamic Policies (GateKeeper, Transformation, Recovery) — fire conditionally on same events without being declared in the workflow. | +| **Reserve Query** | A parallel capacity query sent to all eligible provider candidates. Providers confirm capacity and hold it for PT5M. The Placement Engine selects the winner and releases other holds. | + + + + +### Credential Provider Terms + +| Term | Definition | +|------|-----------| +| **Credential Record** | DCM Data artifact storing credential metadata (UUID, type, scope, expiry, status). Never contains the credential value — values are held by the Credential Provider. | +| **DCM Interaction Credential** | Short-lived (PT15M–PT1H profile-governed), scoped credential issued before every provider dispatch. Implements ZTS-002. Never stored beyond the interaction window. | +| **Credential Revocation Registry** | Fast-queryable store of revoked credential UUIDs. All components that receive interaction credentials must check this registry at each use. Cache TTL: PT1M standard; PT30S fsi/sovereign. | +| **Transition Window** | Period during rotation when both the old and new credential are valid. Prevents downtime. P1D for consumer credentials; PT5M for dcm_interaction; P7D for x509. | +| **Emergency Rotation** | Rotation triggered by a security event. No transition window — old credential revoked immediately. Fastest-channel notification delivery. | +| **CPX-001–CPX-012** | Credential Provider system policies. Key: CPX-001 (values never in DCM stores — every profile), CPX-002 (every dispatch must present scoped interaction credential), CPX-009 (algorithm and key_usage declared at issuance; validated at use), CPX-012 (CPX-001 applies in ALL profiles — no exceptions). | +| **credential_profile** | Profile-governed credential configuration block controlling: permitted credential types, max lifetime per type, rotation requirements, retrieval auth level (bearer/step-up-mfa/mtls), FIPS level enforcement, approved algorithms, revocation SLA, idle detection threshold, IP binding requirement. | +| **AAL (Authenticator Assurance Level)** | NIST 800-63B vocabulary: AAL1 (minimal/dev — single factor), AAL2 (standard/prod — MFA required for sensitive credentials), AAL2+ (fsi — hardware MFA, FIPS L2), AAL3 (sovereign — hardware-bound, FIPS L3, tamper evidence). | +| **Idle Credential** | A credential issued but not retrieved within the profile-governed threshold. Triggers notification but not automatic revocation. Auto-revocation after 2× threshold is profile-configurable. | +| **key_usage** | Declared purpose of a credential: authentication, signing, or encryption. Non-overlapping — a credential issued for authentication cannot be used for signing even if the algorithm supports both. Validated at use time by Credential Provider. | + + +### Meta Provider Composability Terms + +| Term | Definition | +|------|-----------| +| **Composite Entity** | A DCM entity produced by a Meta Provider. Exists across all four states as a single entity aggregating constituent Resource Entities. Has one entity UUID that links it through all states. | +| **Constituent** | A sub-resource within a compound service that a Meta Provider provisions. Declared with a `component_id`, `resource_type`, `depends_on`, and `required_for_delivery` classification. | +| **required_for_delivery** | Constituent delivery classification: `required` (failure halts the compound service and triggers compensation), `partial` (failure produces DEGRADED but not FAILED), `optional` (failure is noted but ignored). | +| **Composite Status** | Top-level outcome of a compound service execution: `REALIZED` (all required constituents succeeded), `DEGRADED` (required succeeded; partial(s) failed; accepted if profile permits), `FAILED` (required constituent(s) failed; triggers compensation). | +| **Compensation** | Ordered teardown of successfully realized constituents when a compound service cannot be delivered. Runs in dependency-reverse order. Best-effort; failures produce `PARTIALLY_COMPENSATED` with orphan detection. | +| **Composition Visibility** | How a Meta Provider exposes its internal structure to DCM: `opaque` (top-level only), `transparent` (all constituents as DCM entities), `selective` (declared sub-set as DCM entities). | +| **Dependency Round** | A batch of constituents that can execute in parallel because all their `depends_on` constituents are complete. Multiple rounds execute sequentially; constituents within a round execute in parallel. | +| **MPX-001–MPX-008** | Meta Provider system policies. Key: MPX-001 (compensation required if partial delivery supported), MPX-002 (dependency-reverse decommission), MPX-006 (DEGRADED is a valid terminal state when accepted), MPX-008 (compound payload fully assembled by DCM before dispatch). | + + + + + + + + + +### External CA and Live Update Terms + +| Term | Definition | +|------|-----------| +| **External CA Credential Provider** | A Credential Provider backend that issues x509 certificates using standard protocols (ACME/RFC 8555, EST/RFC 7030, SCEP, CMP, or native API like HashiCorp Vault PKI). Recommended for fsi and sovereign profiles to maintain enterprise PKI chain. Registered trust anchor root cert must be installed in all component trust stores. | +| **Trust Anchor** | The root or intermediate CA certificate installed in all DCM component trust stores. May be the built-in Internal CA or an external CA registered as a Credential Provider. ICOM-009: components only accept certificates from registered trust anchors. | +| **Server-Sent Events (SSE)** | W3C standard HTTP/1.1 unidirectional event stream. DCM exposes `GET /api/v1/requests/{uuid}/stream` as an SSE endpoint for live request status updates without polling. Stream closes on terminal status. | +| **Interim Status** | Provider-sent progress update during a long-running operation, via `POST /api/v1/provider/entities/{uuid}/status`. Includes step_current/step_total, step_label, and constituent_status array for compound operations. Triggers `request.progress_updated` event. | +| **constituent_status** | Array of named component statuses in a compound/Meta Provider request (e.g. `[{ref: "vm", status: "REALIZED"}, {ref: "dns", status: "PROVISIONING"}]`). Surfaced in SSE stream and polling response so consumers can track multi-part operations. | + + + + + +### RHDH and Backstage Integration Terms + +| Term | Definition | +|------|-----------| +| **Red Hat Developer Hub (RHDH)** | Red Hat's enterprise distribution of Backstage. Primary deployment target for DCM consumer GUI. Provides pre-built auth (RHSSO/Keycloak), RBAC plugin, Dynamic Plugins, ArgoCD/Tekton/AAP integrations, and the PatternFly design system. | +| **Dynamic Plugin** | An RHDH/Backstage plugin loaded at runtime without rebuilding the RHDH image. DCM ships all its plugins as Dynamic Plugins — new versions deploy by updating a tag. | +| **DCMService** | Backstage custom entity kind representing a DCM service catalog item. Auto-generated by `@dcm/backstage-plugin-catalog-backend`. Appears in RHDH Software Catalog and drives Software Template generation. | +| **DCMResource** | Backstage custom entity kind representing a realized DCM resource (entity in REALIZED or later lifecycle state). Auto-synced from DCM Realized State every PT5M. Namespace = DCM tenant UUID. | +| **Software Template** | Backstage Scaffolder construct that provides a wizard-based form for creating resources. DCM auto-generates one Software Template per catalog item from the item's field schema. The Scaffolder IS the DCM request form in RHDH mode. | +| **Scaffolder Action** | A Backstage backend function executable from a Software Template. DCM provides: `dcm:request:submit`, `dcm:request:wait` (with live log streaming), `dcm:request:group`, `dcm:request:estimate`, `dcm:catalog:refresh`. | +| **Entity Provider** | A Backstage catalog backend component that emits entity mutations. `@dcm/backstage-plugin-catalog-backend` implements an entity provider that syncs DCM catalog items and realized resources to the RHDH catalog. | +| **PatternFly** | Red Hat's open-source design system and React component library. Used for all DCM GUI surfaces. Key components: Nav (sidebar), NavGroup (non-clickable headers), NavItem with NotificationBadge (approvals count), Table, Toolbar, Gallery (catalog cards), Drawer (live status), Modal (step-up MFA). | +| **GUI-011–GUI-013** | RHDH-specific capabilities: Dynamic Plugin loading (GUI-011), Scaffolder Template auto-generation (GUI-012), DCM Entity Provider for catalog sync (GUI-013). | + + + +### ITSM Integration Terms + +| Term | Definition | +|------|-----------| +| **ITSM Bridge** | DCM's bidirectional integration with ITSM systems (ServiceNow, Jira Service Management, Remedy). DCM is the system of record; ITSM is a consumer of DCM events and a source of approval votes. No ITSM dependency for DCM core operation. | +| **ITSM Reference** | Optional metadata on a DCM entity linking it to one or more ITSM records (Change Request, Incident, CMDB CI). Stored as business data fields; visible on resource entity Overview tab; included in audit records. | +| **CMDB Sync** | One-way sync from DCM to CMDB. DCM entities are the system of record; CMDB CI state is updated via Notification Provider subscription to `entity.*` events. Field mapping is provider-configured, not hardcoded. | +| **GUI-014** | ITSM Integration Bridge capability — ITSM references on entity pages, change records linked to requests, ITSM-sourced approval votes in request status, CMDB sync via Notification Provider. | + + + +### ITSM Integration Terms + +| Term | Definition | +|------|-----------| +| **ITSM Provider** | The 12th DCM Provider type. Provides bidirectional integration with IT Service Management systems (ServiceNow, Jira Service Management, BMC Remedy/Helix, Freshservice, PagerDuty, etc.). Outbound: DCM lifecycle events → ITSM records. Inbound: ITSM approvals → DCM approval votes. Implements full base Provider contract. | +| **ITSM Action Policy** | The 8th DCM Policy output type. Side-effect policy that triggers an action in a connected ITSM system when a DCM event matches. Non-blocking by default (`on_failure: log_and_continue`). May gate the pipeline via `block_until_created: true` with mandatory timeout (ITSM-005). | +| **ITSM Reference** | Metadata stored on a DCM entity linking it to an ITSM record: system, record_type (change_request/incident/cmdb_ci), record_id, record_url, status, last_synced_at. Preserved through entity lifecycle; included in audit records. | +| **block_until_created** | Optional ITSM Policy flag. When `true`, DCM waits for ITSM record creation confirmation before dispatching to Service Provider. Requires `block_timeout`. Timeout expiry triggers `on_failure` behavior — the pipeline never permanently stalls due to ITSM unavailability (ITSM-005). | +| **CMDB CI Mapping** | ITSM Provider configuration mapping DCM resource type FQNs to ITSM CI class names (e.g. `Compute.VirtualMachine → cmdb_ci_server` in ServiceNow). Used for `create_cmdb_ci`, `update_cmdb_ci`, and `retire_cmdb_ci` actions. | +| **recorded_via** | Field on DCM approval vote records identifying the system that submitted the vote (dcm_admin_ui / servicenow / jira / slack_bot / api_direct / other). Used by ITSM Provider inbound approval routing and in audit records for compliance traceability. | +| **ITSM-001–007** | ITSM Provider system policies. Key: ITSM-002 (DCM never requires ITSM — non-blocking default), ITSM-003 (inbound webhooks must be authenticated), ITSM-005 (block_until_created must have timeout — pipeline never permanently stalled). | +| **ITSM-POL-001–004** | ITSM Policy system policies. Key: ITSM-POL-002 (ITSM Policies are side-effect only — not GateKeeper substitutes), ITSM-POL-003 (full audit record per evaluation), ITSM-POL-004 (multiple ITSM Policies on same event fire independently). | + + +### Web Interface Terms + +| Term | Definition | +|------|-----------| +| **Consumer Portal** | The self-service web interface for application developers, owners, and tenant admins. Wraps the Consumer API completely. Bounded by tenancy (X-DCM-Tenant context). Features: catalog browse, request submission with scheduling and dependency groups, live SSE status stream with constituent tracking, resource management, approvals, cost and quota, notifications, sessions. | +| **Admin Panel** | The platform operations console for Platform Admins, SREs, Policy Owners, Security teams, and Auditors. Wraps the Admin API. Features: platform health dashboard, tenant management, provider registration approval, accreditation, quota, scoring configuration, approval queue, tier registry editor, audit and compliance, session management. | +| **Provider Management GUI** | The management interface for provider owner teams. One common shell (overview, health, config, audit) for all 11 provider types, with type-specific extension tabs. Service Provider extends with capacity, managed entities, naturalization; Credential Provider with inventory, rotation, revocation, external CA config; Auth Provider with session stats and SCIM sync; etc. | +| **Unified Shell** | A single DCM web application with role-gated surfaces: Consumer Portal (all actors), Admin Panel (platform-level roles), Provider Management (provider_owner role), Flow GUI (policy_owner/sre). One login, one session. Navigation adapts to actor's highest privilege level. | +| **GUI-001–GUI-010** | Web Interface capabilities. Key: GUI-002 (SSE live status stream for consumer — status_change, progress_updated, approval events), GUI-006 (tier registry drag-and-drop with hard-stop at auto_approve_below ≤ 50), GUI-010 (unified shell — one application, role-gated surfaces). | + + +### Operational Reference Terms + +| Term | Definition | +|------|-----------| +| **GitOps Store Partitioning** | Splitting a DCM GitOps repository into multiple repositories to manage scale. Three strategies: tenant-shard (hash of tenant_uuid), per-tenant (one repo per tenant), and time-based archiving (active vs cold). Declared in deployment manifest; changes require dual-write migration. | +| **Dual-Write Mode** | Migration technique where DCM writes to both source and target store simultaneously. Required before store cutover. Duration is profile-governed (P1D minimal → P60D sovereign). | +| **Burn-In Period** | Post-cutover period during which the source store remains accessible in read-only mode for rollback. Profile-governed: P7D minimal → P90D fsi/sovereign. Source must NOT be decommissioned before burn-in completes. | +| **Repave** | The complete recovery scenario: all DCM infrastructure lost but Git remotes intact. DCM bootstraps from Git, restores operational stores from backup, and rehydrates managed resources. OPS-005 requires post-recovery validation checklist completion. | +| **RTO (Recovery Time Objective)** | Maximum acceptable time to restore DCM service after failure. Profile-governed: PT1M–PT5M component failure (sovereign/standard), PT5M–PT30M full control plane loss. | +| **RPO (Recovery Point Objective)** | Maximum acceptable data loss window. GitOps stores: 0 (Git remote is source of truth). Realized Store: PT1M (fsi/sovereign) to PT5M (standard). Audit Store: PT1M (fsi/sovereign) to PT15M (standard). | +| **OPS-001–007** | Operational Reference system policies. Key: OPS-002 (audit chain continuity across migration), OPS-003 (source read-only during burn-in; do not decommission early), OPS-006 (Audit Store retention minimum P365D all profiles). | + + +### Scheduling and Dependency Terms + +| Term | Definition | +|------|-----------| +| **Scheduled Request** | A DCM request with an explicit dispatch schedule (at a specific time, during a maintenance window, or recurring). Goes through the same pipeline as immediate requests; policy evaluates at declaration AND at dispatch time. | +| **PENDING_DEPENDENCY** | Intent State status for a request in a dependency group waiting for its declared dependency to reach the required wait_for state before dispatch. | +| **Request Dependency Group** | A consumer-declared set of requests with ordering constraints (depends_on) between them. Distinct from type-level dependencies (doc 07) and Meta Provider composition (doc 30). | +| **Field Injection** | Mechanism for passing realized output fields from a dependency automatically into a dependent request's fields at dispatch time. Subject to Transformation policies. | +| **Maintenance Window** | A reusable, named recurrence artifact declaring approved change windows. Consumers reference window_uuid in scheduled requests to slot into the next matching window. | +| **SCH-001–SCH-006** | Scheduled requests system policies. Key: SCH-001 (dual policy evaluation: declaration + dispatch), SCH-003 (dispatch-time policy rejection → FAILED), SCH-005 (not_after deadline miss → FAILED, no retry). | +| **RDG-001–RDG-006** | Request dependency graph policies. Key: RDG-001 (circular deps rejected at submission), RDG-002 (max 50 requests per group), RDG-004 (PENDING_DEPENDENCY requests count against quota), RDG-006 (request may belong to one group only). | + +### Self-Health Terms + +| Term | Definition | +|------|-----------| +| **Liveness (/livez)** | Fast DCM health check (PT5S max, no external calls). Failure → Kubernetes restarts the pod. Unauthenticated. | +| **Readiness (/readyz)** | DCM readiness check — validates Session Store, Audit Store, Policy Engine, Message Bus, Auth Provider. Failure → removed from load balancer. Used for startup probes. | +| **HLT-001–HLT-006** | Self-health system policies. Key: HLT-001 (livez and readyz required, unauthenticated), HLT-002 (livez PT5S max, no external calls), HLT-003 (readyz fails if core dependencies unreachable), HLT-005 (Prometheus metrics required). | + + +### Session Revocation Terms + +| Term | Definition | +|------|-----------| +| **Session Record** | DCM Data artifact tracking an active actor session: session_uuid, actor_uuid, auth_provider_uuid, created_at, expires_at, status (active/refreshing/revoked/expired), revocation metadata. | +| **Session Revocation Registry** | Fast-queryable store of revoked-but-not-yet-expired session UUIDs. All components that accept bearer tokens must check this on every request. Cache age is profile-governed (PT5M minimal → no cache sovereign). | +| **Session Store** | Operational store for active sessions (not GitOps-backed). Separate from Realized State Store. Backed by Redis or Postgres (standard+) or in-memory (minimal/dev). | +| **Token Introspection** | RFC 7662 endpoint (`POST /api/v1/auth/introspect`) for validating bearer tokens. Returns active/inactive plus session metadata. Requires `introspection` scope. | +| **AUTH-016–AUTH-022** | Session revocation system policies. Key: AUTH-016 (deprovisioning fires session + credential revocation in parallel), AUTH-017 (revocation SLA), AUTH-018 (all components check revocation registry), AUTH-019 (emergency revocation: critical urgency, non-suppressable). | + +### Internal Component Auth Terms + +| Term | Definition | +|------|-----------| +| **Internal CA** | The Certificate Authority operated by each DCM deployment for issuing mTLS certificates to internal components. Not exposed externally. Root cert installed in all component trust stores at deployment time. | +| **Component Identity** | Each DCM control plane component has a stable UUID, an mTLS certificate from the Internal CA, and a service account with declared allowed_sources and allowed_targets. | +| **Bootstrap Token** | A one-time-use credential (max PT1H lifetime) that enables a new component to acquire its first mTLS certificate from the Internal CA. Invalidated immediately after use. | +| **Component Communication Graph** | The declared graph of which components may call which others. Components may only call `allowed_targets`; endpoints only accept calls from `allowed_sources`. Violations are rejected and audited (ICOM-003, ICOM-004). | +| **ICOM-001–ICOM-009** | Internal Component Auth system policies. Key: ICOM-001 (mTLS required for all internal calls), ICOM-002 (interaction credential required in addition to mTLS), ICOM-007 (bootstrap tokens one-time-use, PT1H max), ICOM-008 (compromised certs → CRL immediately). | + + +### API Versioning Terms + +| Term | Definition | +|------|-----------| +| **Breaking Change** | Any change that requires an existing client to modify code or configuration to continue working. Removing fields, changing types, removing endpoints, tightening validation, changing HTTP status semantics. See [34-api-versioning-strategy.md] Section 2 for the complete definition. | +| **Deprecation Period** | The window between when a version is announced as deprecated and when it reaches its sunset date. Deprecated versions continue to function; responses include `Deprecation` and `Sunset` headers. Profile-governed: prod=365 days notice, 2 years support; sovereign=2 years notice, 4 years support. | +| **Sunset Date** | The date after which a deprecated API version returns `410 Gone`. Clients must migrate before this date. | +| **Preview Endpoint** | An endpoint at `/api/v{N}/preview/` path with no stability commitment. May change or be removed without a major version increment. Not for production use. | +| **VER-001–VER-009** | API Versioning system policies. Key: VER-002 (breaking change definition — when in doubt, treat as breaking), VER-003 (deprecation headers required on all deprecated version responses), VER-005 (support windows are profile-governed), VER-008 (machine-readable migration guide required for each new major version). | + + +### Event Catalog Terms + +| Term | Definition | +|------|-----------| +| **Event Catalog** | The authoritative source for all DCM event types, their payload schemas, urgency levels, and trigger conditions. See [33-event-catalog.md]. 82 event types across 20 domains. | +| **Event Envelope** | The common wrapper all DCM events share: event_uuid (idempotency key), event_type, event_schema_version, timestamp (from Commit Log), dcm_version, dcm_instance_uuid, subject, urgency, payload, links. | +| **event_uuid** | Stable idempotency key assigned to each event. Consumers must treat duplicate event_uuid values as already-processed — DCM delivers at-least-once. | +| **event_schema_version** | Increments on breaking payload schema changes. Adding optional fields is not breaking. Removing fields, changing types, or changing semantics are breaking. | +| **EVT-001–EVT-007** | Event Catalog system policies. Key: EVT-001 (all events must include base envelope), EVT-002 (event_uuid is idempotency key), EVT-005 (critical urgency events delivered via push regardless of subscriptions), EVT-006 (non-standard events use reverse-DNS prefix), EVT-007 (audit.* critical events are non-suppressable). | + + +### Authority Tier Model Terms + +| Term | Definition | +|------|-----------| +| **Authority Tier Registry** | The ordered list of authority tiers that governs approval routing across all DCM pipelines. Stored as a versioned registry entry. Changes require impact detection before activation. | +| **Tier Impact Diff** | Computed before any tier registry change activates. Compares proposed ordered list to current list; classifies each changed tier as SECURITY_DEGRADATION, BROKEN_REFERENCE, PROFILE_GAP, SECURITY_UPGRADE, or NEW. | +| **SECURITY_DEGRADATION** | Impact classification for a tier whose gravity or position decreased after a registry change. Blocks registry activation until explicitly accepted by a verified-tier or above reviewer (ATM-009). | +| **BROKEN_REFERENCE** | Impact classification when a tier name referenced in active configuration no longer exists in the registry. Blocks activation until resolved (ATM-010). | +| **PROFILE_GAP** | Impact classification when a profile's threshold list is incomplete after new tier insertion. Warning only — does not block activation (ATM-012). | Stored as a versioned registry entry. Custom tiers are inserted into the list by position; existing tier names remain stable. | +| **decision_gravity** | Stable, position-independent severity classification on each tier: `none` (auto), `routine` (reviewed), `elevated` (verified), `critical` (authorized). Used by the scoring model and profile system to reason about tier severity independently of tier names. | +| **Tier Weight** | Numeric value derived from a tier's position in the ordered list. Never hardcoded — resolved at evaluation time. Stored in approval records for point-in-time audit (ATM-008). | +| **Custom Tier** | An organization-defined tier inserted between existing tiers. Must declare `decision_gravity` consistent with position. Requires `verified` tier approval to contribute (ATM-004). | +| **ATM-001–ATM-008** | Authority Tier Model system policies. Key: ATM-001 (tiers identified by name; weight derived from position), ATM-002 (auto tier max_score ≤ 50), ATM-003 (custom tier gravity must be consistent with position), ATM-008 (approval records store weight at creation for point-in-time audit). | + + +### Authority Tier Terms + +| Term | Definition | +|------|-----------| +| **Authority Tier** | The required organizational authority level for a decision, expressed as a named position in an ordered list. DCM defines four tiers; organizations define what constitutes sufficient authority at each level. | +| **`auto`** | No human judgment required. System confidence (scoring, validation) is sufficient to proceed. | +| **`reviewed`** | Standard authority required. One qualified reviewer in the relevant domain must record a decision via the DCM Admin API. Who constitutes a qualified reviewer is the organization's definition. | +| **`verified`** | Elevated authority required. Two independent, distinct reviewers must each record a decision. The same actor cannot satisfy both. Enforces separation of duties. | +| **`authorized`** | Highest authority level required. Most consequential decisions — policy governance changes, regulated actions, high-risk provider registrations. N members of a declared DCMGroup must record decisions via the Admin API. The authority group composition (CTO, CISO, security board, one person with delegated authority) is entirely the organization's definition. | +| **DCMGroup (authority context)** | A declared set of actors who constitute the required authority for `authorized` tier decisions. Platform admin declares membership; quorum threshold (N of M) is profile-governed. | +| **`recorded_via`** | Audit field on approval decisions capturing which system submitted the decision (dcm_admin_ui, servicenow, jira, slack_bot, api_direct). Informational for audit; not enforced. | + + +### Design Priority Terms + +| Term | Definition | +|------|-----------| +| **Design Priority Order** | The four-priority hierarchy governing all DCM design decisions: (1) Security — industry best practices are the baseline; (2) Ease of use — secure path must be easy path; (3) Extensibility — adaptable through configuration not code; (4) Fit for purpose — always required. | +| **DPO-001–006** | Design Priority system policies. Key: DPO-001 (security properties present in all profiles), DPO-002 (every security requirement needs ease-of-use mechanism), DPO-005 (`minimal` profile = minimal overhead not minimal security), DPO-006 (when security and ease conflict, redesign ease-of-use not security). | +| **Priority 1 (Security)** | Security properties are architecturally present in ALL profiles. Profiles control enforcement strictness and automation level — never whether the property exists. Non-negotiable across all profiles: CPX-001, SMX-004, SMX-008, CPX-003, CPX-005 first retrieval audit, forbidden algorithm baseline. | +| **Priority 2 (Ease of use)** | The secure path must also be the easy path. Every security requirement must be accompanied by an ease-of-use mechanism. The scoring model auto-approval threshold, profile defaults, and Flow GUI visual condition builder are all Priority 2 implementations. | + +### Scoring Model Terms + +| Term | Definition | +|------|-----------| +| **enforcement_class** | Required property of GateKeeper policies. `compliance`: boolean deny gate — always halts on fire. `operational`: contributes `risk_score_contribution` to the request risk score. | +| **output_class** | Required property of Validation policies. `structural`: boolean pass/fail. `advisory`: contributes completeness score and warnings without blocking. | +| **request_risk_score** | Aggregate score (0–100) assembled from five weighted signals: operational GateKeeper contributions, completeness, actor risk history, quota pressure, provider accreditation richness. Drives approval routing. | +| **risk_score_contribution** | The weighted score a fired operational-class GateKeeper contributes to the request risk score. Declared as `scoring_weight` (1–100) in the policy. | +| **completeness_score** | Aggregate of advisory Validation contributions. Represents how incomplete or unusual the request is — higher = more warnings. Does not block requests. | +| **actor_risk_history_score** | Decay-weighted (λ=0.1, half-life ≈7 days) history of an actor's previous request outcomes. Contributes to request risk score. Not exposed to other consumers. | +| **quota_pressure_score** | Continuous score representing how close a Tenant is to quota limits for the requested resource type. Zero below 75% utilization; 100 at full quota. | +| **accreditation_richness_score** | Weighted sum of a provider's accreditation portfolio normalized to 0–100. Influences placement preference and inversely contributes to provider risk signal. | +| **scoring_threshold** | Profile-governed boundary on the request risk score that maps to an approval routing tier. Four tiers: auto_approve, reviewed, verified, authorized. `auto_approve_below` may not exceed 50 (SMX-008). | +| **Risk Score Aggregator** | Sub-function of the Policy Engine. Assembles five scoring signals into the request risk score after all compliance-class and Governance Matrix evaluations complete. | +| **regulatory_mandate** | Policy metadata flag. When `true`, the policy's `enforcement_class: compliance` cannot be demoted to operational by any profile (SMX-003). Set by platform admins, audited. | +| **score_drivers** | Human-readable list of the top contributing factors to a request risk score. Exposed to consumers (top 3 only). Full breakdown in Score Record for platform admins. | +| **Score Record** | Immutable audit artifact recording the full signal breakdown, weights, routing decision, and threshold applied for a scored request evaluation. Written to Audit Store for every scored request. | + + +### Federation Topology + +| Term | Definition | +|------|-----------| +| **Peer DCM** | A federated DCM instance. Treated as a typed Provider. Trust postures: verified (manually approved), vouched (Hub-introduced), provisional (crypto-verified only). | +| **Hub DCM** | A DCM instance that coordinates Regional DCMs in hub-spoke topology. Policy distribution source. Cannot force-activate policies on Regional DCMs. | +| **Regional DCM** | A DCM instance in a specific sovereignty region, managed by a Hub DCM. | +| **Federation Tunnel** | Mutually authenticated, encrypted, scoped channel between DCM instances. Establishes secure transport — not implicit trust. | +| **Federated Contribution** | A Peer DCM contributing registry entries, policy templates, or service layers to a receiving DCM, scoped by the peer's federation trust posture. | + +--- + +## Part 2 — Anti-Vocabulary + +Terms to avoid because they introduce ambiguity. Use the precise alternatives instead. + +| Avoid | Because | Use Instead | +|-------|---------|-------------| +| **Widget** | Vague — what thing, exactly? | Name the specific resource type (VirtualMachine, IPAddress, VLAN, etc.) | +| **Realize** (standalone) | Ambiguous — "realize" can mean understand, achieve, or provision | **Provision** a VM, **fulfill** a request, **execute** a process. "Realized State" is accepted vocabulary. | +| **Data Center** (as architectural term) | A building — not architecturally meaningful | **Region** (large geographically distinct area) or **Zone** / **Availability Zone** (isolated group within a Region) | +| **Orchestrator** (as a standalone component) | Suggests a single sequencer; DCM orchestration is policy-driven, not procedural | **Request Orchestrator** (the event bus) + **Orchestration Flow Policy** (named workflow) + **Policy Engine** (evaluator) | +| **Tangible / Intangible** | Nothing in DCM architecture is intangible — these words add no precision | Describe what the thing actually is | +| **Workflow** (without qualification) | Ambiguous between Level 1 (named Orchestration Flow Policy) and general process | **Named Workflow** (Orchestration Flow Policy with `ordered: true`) or **dynamic policy** (conditional policy) | +| **Producer** | DCM uses "Provider" terminology with typed contracts | **Service Provider** (provisions resources) or the specific provider type | +| **Shore / Ship / Enclave** | Legacy terminology from defense IT contexts; replaced in DCM | **Hub DCM** (central/global) / **Regional DCM** (distributed regional) / **Sovereign DCM** (air-gapped/compliance-isolated) | +| **User** (generic) | Ambiguous across domains — humans "use" DCM in every domain | **Consumer**, **Developer**, **Application Owner**, **Platform Engineer**, **SRE**, **Tenant Admin**, **Policy Author** | +| **Service** (unqualified) | Means different things in different contexts — provision target, catalog entry, or provider | **Catalog Item** (what consumers browse), **Resource Type** (abstract classification), **Service Provider** (who provisions) | +| **Manage** (unqualified) | Too broad — what action specifically? | **Provision**, **Configure**, **Monitor**, **Decommission**, **Govern** — name the lifecycle operation | + +--- + +## Part 3 — Roles and Personas + +| Role | Scope | API surface | +|------|-------|-------------| +| **Consumer** | Requests services from the catalog; manages owned resources | Consumer API | +| **Tenant Admin** | Manages a Tenant; can author tenant-domain policies and groups | Consumer API + contribution endpoints | +| **Policy Author** | Authors policies within assigned domain scope | Consumer API contribution endpoints, Flow GUI | +| **Platform Admin** | Manages the DCM deployment; all artifact types; all domains | Admin API, Flow GUI (full) | +| **Platform Observer** | Read-only view across all platform operations | Flow GUI (read-only), Admin API (read) | +| **Service Provider Operator** | Manages a registered Service Provider | Operator Interface (provider side), Admin API (registration) | +| **Policy Reviewer** | Reviews and approves/rejects contributed policies | Admin API, Flow GUI | +| **Auditor** | Read-only access to audit records and compliance reports | Consumer API (audit), Admin API (audit) | + +--- + +## Part 4 — Capability Domain Prefixes + +| Prefix | Domain | +|--------|--------| +| IAM | Identity and Access Management | +| CAT | Service Catalog | +| REQ | Request Lifecycle Management | +| PRV | Provider Contract and Realization | +| LCM | Resource Lifecycle Management | +| DRF | Drift Detection and Remediation | +| POL | Policy Management | +| LAY | Data Layer Management | +| INF | Information and Data Integration | +| ING | Ingestion and Brownfield Management | +| AUD | Audit and Compliance | +| OBS | Observability and Operations | +| STO | Storage and State Management | +| FED | DCM Federation and Multi-Instance | +| GOV | Platform Governance and Administration | +| ACC | Accreditation Management | +| ZTS | Zero Trust and Security Posture | +| GMX | Unified Governance Matrix | +| DRC | Drift Reconciliation | +| FCM | Federated Contribution Model | +| SMX | Scoring Model | +| MPX | Meta Provider Composability | +| CPX | Credential Provider Model | +| DPO | Design Priority Order | +| ATM | Authority Tier Model | +| EVT | Event Catalog | +| VER | API Versioning | +| SES | Session Revocation | +| ICOM | Internal Component Auth | +| SCH | Scheduled Requests | +| RDG | Request Dependency Graph | +| HLT | DCM Self-Health | +| OPS | Operational Reference | +| GUI | Web Interfaces | +| ITSM | ITSM Integration | +| AUTH | Auth Provider Capabilities | +| PCA | Provider Callback Authentication | +| PRR | Provider Readiness | +| WLA | Workload Analysis | +| ACM | Accreditation Monitoring | +| LOC | Location Topology Management | + +--- + +*Document maintained by the DCM Project. For questions or contributions see [GitHub](https://github.com/dcm-project).* diff --git a/static/capabilities/DCM-Capabilities-Matrix.csv b/static/capabilities/DCM-Capabilities-Matrix.csv new file mode 100644 index 0000000..e81e1f2 --- /dev/null +++ b/static/capabilities/DCM-Capabilities-Matrix.csv @@ -0,0 +1,96 @@ +Capability ID,Domain,Capability,Consumer Perspective,Service Provider Perspective,Platform/Admin Perspective,Depends On +IAM-001,Identity and Access Management,Actor Authentication,Authenticate to DCM via configured IdP,—,Register and configure Auth Providers; manage local user store,— +IAM-002,Identity and Access Management,Session Token Management,Receive and use session tokens; token refresh,—,"Configure session TTL, failover chain",IAM-001 +IAM-003,Identity and Access Management,Role-Based Access Control,Receive role-appropriate service catalog and API responses,—,Declare role mappings; assign roles to actors,IAM-001 +IAM-004,Identity and Access Management,Group Membership Resolution,Group memberships automatically applied from IdP,—,Map IdP groups to DCM groups; declare group-role relationships,"IAM-001, IAM-003" +IAM-005,Identity and Access Management,Multi-Factor Authentication,Satisfy per-session and step-up MFA challenges,—,Configure MFA methods; declare step-up operations,IAM-001 +IAM-006,Identity and Access Management,SCIM Automated Provisioning,Actor created/updated/deprovisioned from IdP automatically,—,Configure SCIM endpoint and attribute mappings,IAM-001 +IAM-007,Identity and Access Management,Tenant Scope Enforcement,Access restricted to authorized Tenants,—,Declare Tenant membership; configure cross-tenant policies,"IAM-003, IAM-004" +CAT-001,Service Catalog,Service Catalog Presentation,Browse available services filtered by RBAC,Declare catalog items for offered resource types,Activate catalog items; configure catalog visibility policies,"IAM-003, IAM-007" +CAT-002,Service Catalog,Service Schema Discovery,"View field schemas, constraints, and edit constraints for a catalog item",Declare field schemas in Resource Type Spec,Configure constraint visibility level per profile,CAT-001 +CAT-003,Service Catalog,Catalog Item Search and Filter,"Search catalog by keyword, resource type, tag",—,Configure Search Index for catalog,CAT-001 +CAT-004,Service Catalog,Catalog Item Versioning,Request a specific version of a catalog item,Publish new catalog item versions following semver,Manage version lifecycle; enforce deprecation timelines,CAT-001 +CAT-005,Service Catalog,Cost Estimation,Receive estimated cost before submitting a request,Declare cost metadata on provider registration,Configure Cost Analysis component,CAT-001 +CAT-006,Service Catalog,Dependency Visualization,See required dependencies for a catalog item before requesting,Declare dependency graph in Resource Type Spec,—,CAT-001 +CAT-007,Service Catalog,Catalog Item Deprecation,Receive deprecation warnings on deprecated catalog items,Declare successor types in deprecation notice,Manage deprecation lifecycle; notify consumers,CAT-004 +REQ-001,Request Lifecycle Management,Submit Service Request,"Submit a resource request via UI, API, or Git PR",—,Configure request ingress surfaces,"IAM-007, CAT-001" +REQ-002,Request Lifecycle Management,Intent State Capture,Request stored as versioned GitOps artifact before processing,—,Configure Intent Store; manage Git repository structure,REQ-001 +REQ-003,Request Lifecycle Management,Layer Assembly,Request enriched with organizational defaults and context layers,Contribute Service Layers for resource types,Manage Core Layers; configure Layer Cache,REQ-002 +REQ-004,Request Lifecycle Management,Policy Evaluation,"Request validated, transformed, and gated by applicable policies",Contribute provider-specific policies,Manage Policy Engine; configure Policy Groups and Profiles,REQ-003 +REQ-005,Request Lifecycle Management,Placement Engine Execution,Resource placed with the best available provider instance,Implement capacity reserve_query response,Configure placement constraints; manage provider priorities,REQ-004 +REQ-006,Request Lifecycle Management,Requested State Persistence,Assembled payload stored as authoritative GitOps record,—,Configure Requested Store; manage storage redundancy,REQ-005 +REQ-007,Request Lifecycle Management,Provider Dispatch,Request payload delivered to selected provider,Implement Services API to receive DCM payloads,Configure API Gateway and egress,REQ-006 +REQ-008,Request Lifecycle Management,Request Status Tracking,Monitor request status from submitted through realized,Report realization status back to DCM,Configure observability for request tracking,REQ-007 +REQ-009,Request Lifecycle Management,Request Cancellation,Cancel a pending request before realization,Handle cancellation payloads,Configure cancellation policies,REQ-002 +REQ-010,Request Lifecycle Management,Git PR Request Ingress,Submit requests via Git Pull Request with policy dry-run feedback,—,Configure Git Request Watcher; manage repository structure,"REQ-001, IAM-001" +PRV-001,Provider Contract and Realization,Provider Registration,—,"Register provider with DCM: declare type, capabilities, sovereignty, cost metadata",Configure Provider Registry; validate sovereignty declarations,IAM-001 +PRV-002,Provider Contract and Realization,Naturalization,—,Convert DCM unified payload to provider-native format,—,"PRV-001, REQ-007" +PRV-003,Provider Contract and Realization,Realization,—,Execute required actions to provision/configure/change resource,—,PRV-002 +PRV-004,Provider Contract and Realization,Denaturalization,—,Convert provider-native result back to DCM unified format,—,PRV-003 +PRV-005,Provider Contract and Realization,Realized State Reporting,—,Report realized payload and status to DCM API Gateway,Configure Realized State Store; manage Event Stream,PRV-004 +PRV-006,Provider Contract and Realization,Capacity Reporting,—,Respond to reserve_query with current capacity and availability,Configure placement engine; manage capacity confidence,PRV-001 +PRV-007,Provider Contract and Realization,Provider Health Reporting,—,Expose health check endpoint; report availability,Monitor provider health; configure trust score updates,PRV-001 +PRV-008,Provider Contract and Realization,Sovereignty Declaration Maintenance,—,Notify DCM when sovereignty data changes within declared SLA,Monitor sovereignty changes; trigger re-evaluation,PRV-001 +PRV-009,Provider Contract and Realization,Meta Provider Orchestration,—,Compose sub-providers to deliver higher-order services; manage composition visibility,Configure composite provider federation eligibility,"PRV-001, PRV-003" +LCM-001,Resource Lifecycle Management,Resource State Transitions,"Trigger lifecycle actions: suspend, resume, decommission",Handle state transition payloads,Configure lifecycle policies; manage state machine,REQ-008 +LCM-002,Resource Lifecycle Management,Post-Realization Field Updates,Update editable fields on realized resources (targeted delta),Handle delta update payloads; apply partial changes,Configure editable field declarations; manage edit policies,PRV-005 +LCM-003,Resource Lifecycle Management,Resource TTL Management,Declare and extend resource TTLs; receive expiry notifications,Handle TTL-triggered decommission payloads,Configure Lifecycle Constraint Enforcer; manage expiry policies,LCM-001 +LCM-004,Resource Lifecycle Management,Ownership Transfer,Transfer resource ownership to a different Tenant,—,Authorize and execute ownership transfers; record transfer history,"IAM-007, LCM-001" +LCM-005,Resource Lifecycle Management,Rehydration,Replay a resource's intent state to a new provider or context,Receive and execute rehydration payloads,Manage rehydration leases; configure auth level requirements,"REQ-002, PRV-003" +LCM-006,Resource Lifecycle Management,Billing State Management,—,—,Configure billing state policies; integrate with Cost Analysis,LCM-001 +LCM-007,Resource Lifecycle Management,Resource Decommission,Decommission resources individually or as part of group decommission,Handle decommission payloads; release resources,Manage decommission workflows; coordinate dependency teardown,LCM-001 +DRF-001,Drift Detection and Remediation,Active Discovery,—,Expose discovery endpoint; respond to interrogation queries,Configure discovery schedules; manage Discovered Store,PRV-005 +DRF-002,Drift Detection and Remediation,Drift Comparison,Receive drift notifications for owned resources,—,Configure drift detection policies; manage comparison logic,"DRF-001, PRV-005" +DRF-003,Drift Detection and Remediation,Drift Notification,Receive actionable drift alerts with field-level detail,—,Configure drift notification channels and escalation policies,DRF-002 +DRF-004,Drift Detection and Remediation,Drift Remediation,Approve or reject automatic drift remediation,Execute remediation payloads,Configure remediation policies (revert/update/alert/escalate),"DRF-002, LCM-002" +DRF-005,Drift Detection and Remediation,Unsanctioned Change Detection,Receive alerts on unauthorized resource modifications,Report all external state changes to DCM,Configure unsanctioned change policies,DRF-001 +POL-001,Policy Management,Policy Authoring,—,Contribute provider-specific policy rules,Author and manage policies in GitOps store,IAM-003 +POL-002,Policy Management,Policy Validation and Shadow Mode,View shadow evaluation results on own requests,—,Configure shadow mode; review shadow results in Validation Store,POL-001 +POL-003,Policy Management,Policy Activation and Review,—,—,Manage policy review periods; authorize policy activation,"POL-001, POL-002" +POL-004,Policy Management,Policy Group Management,—,—,Compose Policy Groups; manage profile assignments,POL-003 +POL-005,Policy Management,Profile Management,—,—,Configure deployment profiles; manage compliance domain groups,POL-004 +POL-006,Policy Management,Policy Provider Registration,—,Register Policy Providers; maintain provider in declared mode,Configure Policy Provider trust levels; manage trust elevation workflow,"PRV-001, POL-001" +POL-007,Policy Management,Policy Override and Constraint Visibility,View constraint details for service catalog fields,Declare constraint schemas on Resource Type Specs,Configure constraint visibility levels per profile,"CAT-002, POL-003" +LAY-001,Data Layer Management,Core Layer Authoring,—,—,Author and manage Core and Organizational Layers in GitOps,IAM-003 +LAY-002,Data Layer Management,Service Layer Contribution,—,Contribute Service Layers for offered resource types,Manage layer compatibility declarations,"PRV-001, LAY-001" +LAY-003,Data Layer Management,Layer Cache Management,—,—,Manage Layer Cache synchronization; handle cache invalidation,"LAY-001, LAY-002" +LAY-004,Data Layer Management,Layer Exclusion,Declare layer exclusions on specific requests,—,Configure which layers may be excluded; manage non-excludable declarations,REQ-003 +LAY-005,Data Layer Management,Layer Versioning and Lifecycle,—,—,Manage layer versions; handle deprecation; enforce immutability,LAY-001 +INF-001,Information and Data Integration,Information Provider Registration,—,Register Information Provider; declare authority scope and schema,Configure Information Provider Registry; manage authority layers,IAM-001 +INF-002,Information and Data Integration,Information Provider Push,—,Push field value updates to DCM; respond to conflict notifications,Configure ingestion pipeline; manage conflict resolution policies,INF-001 +INF-003,Information and Data Integration,Information Provider Pull / Discovery,—,Expose data query endpoint for DCM pull operations,Configure pull schedules; manage cache TTLs,INF-001 +INF-004,Information and Data Integration,Write-Back,—,Implement write-back endpoint to receive DCM-initiated updates,Configure write-back triggers via policy,"INF-001, INF-002" +INF-005,Information and Data Integration,Confidence Score Visibility,View confidence bands on entity field values; query confidence aggregation API,—,Configure confidence scoring formula; manage trust score thresholds,INF-001 +INF-006,Information and Data Integration,Conflict Resolution Management,—,—,Review and resolve contested field values; manage conflict escalation,INF-002 +ING-001,Ingestion and Brownfield Management,Resource Discovery and Ingestion,—,Expose discovery endpoints for brownfield resources,Configure ingestion pipeline; manage transitional Tenant,DRF-001 +ING-002,Ingestion and Brownfield Management,Ingested Entity Review,—,—,Review ingested entities; resolve conflicts; promote to active Tenants,ING-001 +ING-003,Ingestion and Brownfield Management,Bulk Promotion,—,—,Execute bulk entity promotions with preview and rollback,ING-002 +ING-004,Ingestion and Brownfield Management,Catalog Item Association,—,—,Associate ingested entities with Resource Type Specs; create catalog items,"ING-002, CAT-001" +AUD-001,Audit and Compliance,Audit Trail Access,Query audit records for own resources,—,Configure Audit Store; manage retention policies,IAM-003 +AUD-002,Audit and Compliance,Compliance Reporting,—,—,Generate compliance reports; manage report schedules,AUD-001 +AUD-003,Audit and Compliance,Hash Chain Verification,—,—,Run scheduled and on-demand hash chain verification; manage integrity incidents,AUD-001 +AUD-004,Audit and Compliance,Cross-DCM Audit Correlation,—,—,Correlate audit records across DCM instances via correlation_id; authorize cross-DCM pulls,"AUD-001, DCM-001" +AUD-005,Audit and Compliance,Audit Record Retention Management,—,—,Configure reference-based retention; manage post-lifecycle retention,AUD-001 +OBS-001,Observability and Operations,Operational Dashboard,View health and status of own resources,—,Configure and manage observability dashboard,— +OBS-002,Observability and Operations,Metrics and Telemetry Export,—,Expose resource-level metrics to DCM,Configure observability export; integrate enterprise observability platform,— +OBS-003,Observability and Operations,Curated Event Stream Subscription,Subscribe to observability event types via Message Bus,—,Configure event stream publication policies; manage subscriber roles,OBS-002 +OBS-004,Observability and Operations,Alert and Notification Management,Receive resource and policy alerts via declared channels,—,Configure alert routing; manage notification channels and escalation,OBS-001 +OBS-005,Observability and Operations,Cost Analysis and Attribution,View cost estimates and actuals for owned resources,Provide cost metadata; report utilization,Configure Cost Analysis component; manage cost attribution policies,PRV-006 +STO-001,Storage and State Management,GitOps Store Management,—,—,Configure and manage Intent and Requested Stores; manage Git repository structure,— +STO-002,Storage and State Management,Realized State Store Management,—,—,Configure Event Stream and Realized Store; manage retention,PRV-005 +STO-003,Storage and State Management,Discovered State Store Management,—,—,Configure Discovered Store; manage retention policies per profile,DRF-001 +STO-004,Storage and State Management,Search Index Management,Use entity and catalog search,—,Configure Search Index; manage rebuild on failure,STO-001 +STO-005,Storage and State Management,Backup and Recovery,—,—,Configure backup schedules; test recovery procedures,"STO-001, STO-002" +STO-006,Storage and State Management,Provenance Model Configuration,—,—,Select and configure provenance model (full_inline/deduplicated/tiered); manage tier transitions,STO-001 +FED-001,DCM Federation and Multi-Instance,DCM Provider Registration,Submit requests that are routed to peer DCMs,Register as DCM Provider in peer instances,Configure DCM Provider registrations; manage federation trust,"PRV-001, IAM-001" +FED-002,DCM Federation and Multi-Instance,Federation Routing,Requests automatically routed to appropriate Regional/Sovereign DCM,Respond to reserve queries from Hub DCM,Configure federation placement policies; manage sovereignty pre-filters,"FED-001, REQ-005" +FED-003,DCM Federation and Multi-Instance,Federation Trust Management,—,—,Manage mTLS certificates; monitor federation trust scores; handle cert rotation,FED-001 +FED-004,DCM Federation and Multi-Instance,Cross-DCM Drift Detection,Receive drift alerts for federated resources,Publish Discovered State events to federation Message Bus,Configure federated drift detection; manage alert-and-hold policies,"FED-001, DRF-002" +FED-005,DCM Federation and Multi-Instance,DCM Export and Import,—,—,Export and import DCM state packages; verify import trust scores,"STO-001, STO-002" +GOV-001,Platform Governance and Administration,Tenant Management,—,—,"Create, configure, and decommission Tenants; manage compliance overlays",IAM-007 +GOV-002,Platform Governance and Administration,Group Management,—,—,Create and manage DCM Groups; configure sovereignty rules; manage time-bounded memberships,IAM-003 +GOV-003,Platform Governance and Administration,Registry Management,—,Register and maintain Resource Type Specifications in organization registry,Manage registry sync; configure registry policies; manage Tier 3 types,PRV-001 +GOV-004,Platform Governance and Administration,Resource Type Lifecycle,—,Manage deprecation notices; declare successor types; maintain migration guidance,Enforce deprecation timelines; manage sunset periods,GOV-003 +GOV-005,Platform Governance and Administration,Platform Configuration Management,—,—,Manage platform-wide layers; configure profiles; manage deployment manifest,"LAY-001, POL-005" +GOV-006,Platform Governance and Administration,Bootstrap and Self-Hosting,—,—,Manage DCM self-deployment; verify bootstrap manifest; handle repave scenarios,STO-001 +GOV-007,Platform Governance and Administration,Sovereign Deployment Management,—,—,Manage air-gapped DCM instances; configure signed bundle import; manage offline registry,"FED-001, STO-001" diff --git a/static/capabilities/map.html b/static/capabilities/map.html new file mode 100644 index 0000000..7dc4c62 --- /dev/null +++ b/static/capabilities/map.html @@ -0,0 +1,824 @@ + + + + + +DCM Capabilities Matrix + + + +
+ +
+
DCM Project · Red Hat FlightPath
+

Foundational Capabilities Matrix

+

100 core operational capabilities required for DCM to perform lifecycle management. Each capability is mapped across consumer, service provider, and platform/admin perspectives.

+
+
95
Capabilities
+
15
Domains
+
21
MVP Critical Path
+
3
Perspectives
+
+
+ + +
+ + +
+
+ + +
+ +
+ + +
+
+ +
Showing 95 of 95 capabilities
+ + +
+
+ + + + + + + + + + + + +
IDCapabilityConsumerService ProviderPlatform / AdminDepends On
+
+
+ + + + + + +
+ + + +