# Resource Registry — API Management (Contexts & Types)

## Context Management — base path `/contexts`

### Authorization & Token Selection

| Action | HTTP | Path | Query Parameters | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|--------|------|------|------------------|:--------------:|:----------------:|:---------:|:----------:|:---------:|
| `List` | GET | `/contexts` | `limit`, `offset`, `lifecycle`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute JSON Query` | QUERY | `/contexts` | `limit`, `offset`, `lifecycle`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read` | GET | `/contexts/{context-uuid}` | `depth`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read Current` | GET | `/contexts/@current` | `depth`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Create` | PUT | `/contexts/{context-uuid}` | `metadataPolicy` | ✅ | ⚠️ | ❌ | ❌ | ❌ |
| `Tracked Update` | PATCH | `/contexts/{context-uuid}` | `metadataPolicy` | ✅ | ⚠️ | ❌ | ❌ | ❌ |
| `Read History` | GET | `/contexts/{context-uuid}/history` | *None* | ✅ | ✅ | ❌ | ❌ | ❌ |
| `Delete` | DELETE | `/contexts/{context-uuid}` | *None* | ✅ | ⚠️ | ❌ | ❌ | ❌ |

**Token Scope Rule:**

| Operation Group | Token must be issued for |
|-----------------|--------------------------|
| `List`, `Execute JSON Query` | No specific target Context is required; authorization is evaluated from the token role |
| `Read`, `Read History` | **The Context being read itself** (self). Administrative roles (`IS-M`, `Infra-M`, `IS-Admin`, `Ctx-M`) may alternatively present a token for **any ancestor** of the target Context, at any depth up the chain — this deliberately mirrors what `depth` already discloses from that same ancestor, so it adds no new exposure. Non-administrative roles never get this extension; for them the token must match the Context being read exactly. *(Contrast with `Create`/`Tracked Update`/`Delete` below, which stay strictly limited to the immediate parent — a deliberate one-hop governance boundary for write operations that this read-side extension does not relax.)* For `Read History`, this rule combines with the stricter Level-1 role gate (`IS-M`/`Infra-M`/`IS-Admin`/`Ctx-M` only — non-administrative roles cannot call it at all). |
| `Read Current` | The current token Context (token-bound endpoint semantics) |
| `Create`, `Tracked Update`, `Delete` | **Parent Context** of the target Context. *(Exception: operations on a Root Context require a token issued for the Root Context itself)* |

### HTTP Contract

| Action | Additional Request Headers | Response Headers | Success HTTP Code | Error HTTP Code |
|--------|----------------------------|------------------|-------------------|-----------------|
| `List` | `Accept` | `Content-Type`, `Link`, `X-Warning` | `200 OK` | `403 Forbidden` |
| `Execute JSON Query` | `Content-Type`, `Accept` | `Content-Type`, `Link`, `X-Warning` | `200 OK` | `400 Bad Request`, `403 Forbidden` |
| `Read` | `Accept` | `Content-Type`, `Link`, `X-Warning`, conditional `X-Result-Scope` / `X-Resolved-Context-Path` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found` |
| `Read Current` | `Accept` | `Content-Type`, `Link`, `X-Warning`, conditional `X-Result-Scope` / `X-Resolved-Context-Path` | `200 OK` | `403 Forbidden`, `404 Not Found` |
| `Create` | `Content-Type`, `Accept` | `Content-Type`, `Link`, `X-Warning`, conditional `X-Result-Scope` / `X-Resolved-Context-Path` | `201 Created` / `200 OK` | `400 Bad Request`, `403 Forbidden`, `409 Conflict` |
| `Tracked Update` | `Content-Type: application/merge-patch+json`, `Accept` | `Content-Type`, `Link`, `X-Warning`, conditional `X-Result-Scope` / `X-Resolved-Context-Path` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `Read History` | `Accept` | `Content-Type`, `Link`, `X-Warning`, conditional `X-Result-Scope` / `X-Resolved-Context-Path` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found` |
| `Delete` | *None* | `X-Warning`, conditional `X-Result-Scope` / `X-Resolved-Context-Path` | `204 No Content` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |

### Operational Notes

- **Token path resolution:** Every operation that derives an operational Context from the caller's token first matches the token path against current `path` values and then, only when no current path matches, against `previousPaths`. A fallback match has the same authorization meaning as a direct match and emits `X-Result-Scope: previous-path` plus `X-Resolved-Context-Path` containing the current path. A direct match emits neither header. This shared behavior applies to Context reads and parent-scope mutations as well as instance operations.
- **Read:** `GET /contexts/{context-uuid}` resolves a Context explicitly by UUID. The distinction between `403` and `404` is intentional and operationally significant:
  - `404 Not Found`: no Context with the specified UUID exists — neither among live contexts nor in the cemetery.
  - `403 Forbidden`: a Context with the specified UUID exists but is inaccessible to the caller. This occurs when the Context is in the **DELETED** state (cemetery) and the caller does not hold a manager role (`IS-M` / `Infra-M`), or when the Context exists among live contexts but the caller's token is neither for that exact Context nor — for administrative roles only — for one of its ancestors (see the Token Scope Rule above).
- **Read Current:** `GET /contexts/@current` applies the same `403` vs `404` distinction as `Read`, but with a key difference: the target Context is not specified explicitly by the caller — it is **resolved implicitly from the authorization token itself**.
  - `404 Not Found`: the token's path cannot be resolved to any known Context — neither among live contexts nor in the cemetery. The known root causes are:
    - **IAM / IS bootstrap race:** the IAM has registered a Context path and issued a token for it, but the corresponding Context record has not yet been created in the IS (or the creation failed). This is inherently transient: in a correctly sequenced deployment the IS Context is provisioned before the IAM path is activated. If this 404 appears in production it signals an integration error.
    - **Token carries an unknown path:** the path embedded in the token matches neither a current `path` nor any retained `previousPaths` entry.
  - `403 Forbidden`: the token's path resolves to a Context in the **DELETED** state (cemetery) and the caller does not hold a manager role. This is inherently a transient edge case — it can only arise in the window between Context deletion and the expiration or IAM-side revocation of the caller's outstanding token. In a correctly integrated deployment, token revocation is triggered on Context deletion, making this state short-lived.
- **IS-Admin / Ctx-M permissions:** For `Create`, `Tracked Update`, and `Delete`, the token must be of the parent Context; IS-Admin / Ctx-M receives filtered metadata.
- **The Root Context Bootstrap Exception:** The authorization rule dictates that creating a Context requires a token for its parent. This creates a logical paradox for a Root Context (which has `parent: null`). To resolve this without introducing insecure anti-patterns or requiring a token without a resource, the API permits an `IS-M / Infra-M` to create a Root Context using a token explicitly issued for that exact Root Context being created.
- **Creation via `PUT` vs `POST`:** The API uses `PUT` instead of `POST` for Context creation because the target Context UUID must be explicitly provided in the URL path by the client (e.g., `/contexts/{context-uuid}`). Accepting an internal UUID exclusively in the body of a `POST` request to the base collection endpoint would be an architectural anti-pattern. Note that the UUID must also be present in the JSON body representation; the server performs a strict double-check between the URL path and the body representation to ensure consistency.
- **`PUT /contexts/{context-uuid}` semantics (idempotent creation):** `PUT` is strictly for Context creation. It requires defining `id`, `name`, and `parent`. The `state` field is system-managed upon creation (forced to `CREATED`); supplied `state` and `previousPaths` fields are ignored and reported through `X-Warning`. Historical paths can be introduced only through authenticated `PATCH`. To strictly adhere to HTTP `PUT` idempotency, sending the exact same creation payload multiple times will return `201 Created` the first time and `200 OK` (no-op) on subsequent identical requests. However, if a client attempts to execute a `PUT` on an existing Context with a payload that is incongruent with the existing representation, the system will reject it with `409 Conflict`. Creating a Context whose current path matches another Context's retained previous path removes that retained value in the same transaction and records the collision in the affected Context's History. All legitimate state modifications must be performed exclusively via the `Tracked Update` (`PATCH`) endpoint.
- **`PATCH /contexts/{context-uuid}` semantics (Tracked Update):** Uses the `application/merge-patch+json` media type ([RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396)) to partially update the Context. The body must be a JSON object. It processes changes only to `state`, `name`, `parent`, and `previousPaths`; every other top-level field is ignored and reported through an `X-Warning` header. `state`, when present, must be the string `active` or `suspended` (`created` and `deleted` are system-managed). `name`, when present, must be a valid non-null Context name. `parent`, when present, must be either JSON null (move to the Root level) or an object containing a textual UUID `id`; the resulting hierarchy must remain acyclic. `previousPaths`, when present, must be null or an array of absolute path strings and replaces the complete retained ordered set; null and `[]` clear it, duplicate values are removed, and administrators may add historical paths for remediation. When one patch also renames or reparents the Context, the explicit replacement runs first and the outgoing current path is then appended. Every descendant whose path changes receives its own outgoing path atomically. The backend writes history and modification metadata only for effective tracked-field changes, so an empty patch, a same-value patch, or a patch containing only ignored fields is a successful `200 OK` no-op.
- **Reparenting and Renaming Constraints:** Changing the `parent` Context or the `name` of a Context shifts the structural addressing and IAM resolution of an entire hierarchical branch. To ensure security consistency and prevent accidental "live exposure", reparenting or renaming is strictly allowed only when the Context's **local `state`** is non-`ACTIVE` (e.g., `SUSPENDED` or `CREATED`). Attempting to move or rename a Context while its local `state` is `ACTIVE` will result in a `409 Conflict`. 
  - *Architectural Rationale:* Why check the local `state` instead of the `effectiveState`? Consider a child Context whose local state is `ACTIVE`, but its parent is `SUSPENDED`. Due to inheritance, the child's `effectiveState` is naturally `SUSPENDED`. If the system permitted the move based solely on the `effectiveState`, an administrator could move this child under a new, `ACTIVE` parent. Because its local state was already `ACTIVE`, the child Context would instantly "wake up" and become fully operational immediately upon transfer, potentially exposing the entire subtree to live network traffic without warning. Forcing the local state to be manually set to `SUSPENDED` before the move guarantees that the Context remains safely offline during and after the migration, requiring a deliberate secondary action to reactivate it.
- **Read History:** `GET /contexts/{context-uuid}/history` is a pure read-only endpoint that returns a flat JSON array of **`Event`** objects (sorted in reverse-chronological order, newest first). The path identifier must be a valid UUID; malformed values return `400 Bad Request`. Query parameters are unsupported and ignored with an `X-Warning` header; in particular, `lifecycle` cannot select cemetery routing. Lifecycle visibility is derived exclusively from role: `IS-M` / `Infra-M` can read live or deleted Context history, while `IS-Admin` / `Ctx-M` can read live Context history only. This provides a complete audit trail of the Context's lifecycle. Each `Event` explicitly captures the transition details:
  - `who`: The principal who triggered the change.
  - `what`: The transition value (e.g., `CREATED -> ACTIVE`).
  - `where`: The specific field modified (e.g., `state` or `name`).
  - `when`: The timestamp of the occurrence.
  - `how`: The triggering mechanism or API method (e.g., `PATCH`).

  Changes to `previousPaths` are recorded independently: direct and cascaded path changes add the outgoing path, explicit replacements record each addition and removal, and creation-time path reuse records the reclaimed path and new Context UUID. These events use `where: previousPaths` and do not replace the separate `name`, `parent`, or `path` events.
  
  *Crucial Auditing Feature:* This history actively records changes to the `effectiveState`. If an ancestor Context is suspended, the backend cascades the suspension down the tree, writing an `Event` into the history of every descendant Context (e.g., `where: effectiveState`, `how: Cascade from parent /contexts/{parent-uuid}`). This guarantees that administrators can instantly understand why a specific Context went offline without manually traversing the parent hierarchy.
- **Context Deletion (The Cemetery):** `DELETE` permanently removes the Context from active listings and transitions it to a `DELETED` state in the historical "cemetery" (from which it cannot be resurrected, though a new Context with the same path but a different UUID can be created). The path identifier must be a valid UUID; malformed values return `400 Bad Request`. The target must exist in the live topology, so deleting an unknown or already deleted UUID returns `404 Not Found`. The operation requires an administrative token for the target's current immediate parent, or for the target itself when deleting a Root Context. Unsupported query parameters are ignored and reported through `X-Warning`. Deletion fails with `409 Conflict` if the Context still contains any child Contexts or graph instances. Collection reads (`GET /contexts`, `QUERY /contexts`) support explicit cemetery visibility via the `lifecycle` parameter: `live` (default), `deleted`, `all`. Access to cemetery-inclusive modes (`deleted`, `all`) is restricted to `IS-M / Infra-M`; for all other roles, requesting these modes returns `403 Forbidden`.
- **Execute JSON Query (QBE):** `QUERY /contexts` exposes the powerful JSON Query-By-Example engine specifically for the Contexts collection. Instead of polluting the `GET /contexts` endpoint with hardcoded query parameters, clients can submit a JSON payload matching the desired Context state. This allows advanced filtering across the entire Context representation (e.g., querying `{"effectiveState": "SUSPENDED"}` to find all offline Contexts, or searching by `metadata.createdBy`). The endpoint shares the exact same semantics, operators, and `_emit` projection capabilities as the instance query engine.
  - *Lifecycle visibility contract:* `lifecycle=live` returns only active-topology contexts (default); `lifecycle=deleted` returns only cemetery contexts; `lifecycle=all` returns the union. The server applies role checks before query evaluation.
- **Non-`ACTIVE` Context behavior:** When a Context is **non-`ACTIVE`** (e.g., `CREATED` or `SUSPENDED`), performing instance operations within it is restricted based on the caller's role. `IS-M / Infra-M` can operate freely regardless of the Context state. An `IS-Admin / Ctx-M` can perform instance operations inside a **non-`ACTIVE`** Context provided their credentials explicitly address that specific Context. This intentional design allows an administrator to safely "prepare" a newly `CREATED` Context by pre-populating instances without external interference, before finally flipping the state to `ACTIVE`. Roles with lesser administrative scope (`IS-Editor`, `IS-Contrib`, and `IS-Member`) are strictly blocked from performing any instance operations (both read and write) within **non-`ACTIVE`** Contexts and will receive a `403 Forbidden`.

### Context Lifecycle State Machine

A Context transitions through a strict lifecycle governed by the API operations. The state machine ensures that environments can be safely prepared (`CREATED`), paused (`SUSPENDED`), actively used (`ACTIVE`), and securely destroyed (`DELETED`).

> **Architectural Metaphor (Founder, CEO and Division Manager):**
> To understand the authorization split between parent and child Contexts during lifecycle operations, consider a corporate metaphor. 
> - **The Root Context Bootstrap:** An entrepreneur has a business idea. They found a company, and by the physical act of founding it, they become its CEO. This perfectly explains the Bootstrap Exception: an `IS-M` creating the **Root** Context uses a token for that exact new boundary.
> - **The Child Contexts:** The CEO (the admin of the **Root** Context) decides to create a new company division (`PUT /contexts/child`). The CEO creates it (which starts in `CREATED`), eventually activates it (`ACTIVE`), temporarily suspends it (`SUSPENDED`), or permanently closes it (`DELETED`). However, the CEO assigns a specific manager (the admin of the *child* Context) to run the division. This local manager is the one who actually populates the division with employees and rules (creating/updating instances inside the `CREATED` Context). Once the local manager has prepared everything, the CEO officially declares the division open (`PATCH /contexts/child` to `ACTIVE`). This perfectly illustrates why creating/activating a Context requires parent-level authority, while populating it requires local authority.

```mermaid
---
config:
  theme: neutral
  flowchart:
    edgeLabelBackground: "#ffffff"
---
stateDiagram-v2
    [*] --> CREATED : Create (PUT)
    CREATED --> ACTIVE : Tracked Update (PATCH)
    
    ACTIVE --> SUSPENDED : Tracked Update (PATCH)
    SUSPENDED --> ACTIVE : Tracked Update (PATCH)
    
    CREATED --> DELETED : Delete (DELETE)
    ACTIVE --> DELETED : Delete (DELETE)
    SUSPENDED --> DELETED : Delete (DELETE)
    
    DELETED --> [*] : Terminal State (Cemetery)
    
    note right of DELETED
        Deletion is permanently irreversible.
        Requires empty Context 
        (0 instances, 0 children).
    end note
```

**State Transition Matrix**
All transitions require the caller to possess a valid token for the **parent Context** and hold an authorized managerial role (`IS-M / Infra-M` or `IS-Admin / Ctx-M`).

| Current State | API Operation | Next State | System Behavior & Validation |
|---------------|---------------|------------|------------------------------|
| (Does not exist) | `PUT` | **`CREATED`** | The system provisions the Context. The state is forced to `CREATED`. |
| `CREATED` | `PATCH` | **`ACTIVE`** | Opens the Context to operational users (`IS-Editor`, `IS-Contrib`, `IS-Member`). |
| `ACTIVE` | `PATCH` | **`SUSPENDED`** | Temporarily blocks `IS-Editor`, `IS-Contrib` and `IS-Member` from performing operations. Useful for maintenance or administrative locks. |
| `SUSPENDED` | `PATCH` | **`ACTIVE`** | Restores normal operations for all roles. |
| *Any non-DELETED* | `DELETE` | **`DELETED`** | Moves the Context to the historical cemetery. **Strict validation:** the Context MUST NOT contain any child Contexts or instances, otherwise the request is rejected with `409 Conflict`. |

> **Immutability of the Cemetery:** Once a Context reaches the `DELETED` state, it cannot be resurrected or updated via `PATCH`. However, because it physically vacates the active topology, an administrator can create a *brand new* Context using the same `name` and `parent` (resulting in the same hierarchical path), but it will possess a completely new, unique UUID.

### Inherited Operational State (`effectiveState`)

To support maintenance operations at scale without altering the physical state of thousands of sub-environments, the system strictly separates local configuration (`state`) from operational evaluation (`effectiveState`). The `effectiveState` is a **read-only field that is persistently stored** in the database (and cached in Redis); it is never computed on the fly at read time. The system recomputes and atomically cascades it to all descendant Contexts whenever the Context's own `state` or any ancestor's `state` changes.

**State Evaluation Matrix:**

| Parent's `effectiveState` | Context's Local `state` | Resulting `effectiveState` |
|---------------------------|-------------------------|----------------------------|
| *(None / Root Context)*   | *(Any state)*           | **`= Local state`**        |
| *(Any state)*             | `CREATED`               | **`CREATED`**              |
| *(Any state)*             | `DELETED`               | **`DELETED`**              |
| `CREATED`                 | `SUSPENDED` or `ACTIVE` | **`SUSPENDED`** *(inherited)* |
| `SUSPENDED`               | `SUSPENDED` or `ACTIVE` | **`SUSPENDED`** *(inherited)* |
| `ACTIVE`                  | `SUSPENDED`             | **`SUSPENDED`**            |
| `ACTIVE`                  | `ACTIVE`                | **`ACTIVE`**               |

> **Rationale for `CREATED` inheritance:** If a child Context has been locally advanced to `ACTIVE` or `SUSPENDED` by a local administrator, it is no longer a blank slate. Therefore, its parent's `CREATED` state acts purely as an operational lock, yielding an effective `SUSPENDED` state to freeze operations without reverting or obscuring the child's explicitly configured local status.

> **Architectural Rationale (Offline Restructuring):** 
> There are **no restrictions** on Context Lifecycle operations (`PUT`, `PATCH`, `DELETE` for Contexts) based on the parent's state. An `IS-Admin` has full authority to build and configure an entire nested topology while an ancestor Context is still in a `CREATED` or `SUSPENDED` state. This grants administrators the power to perform massive *Offline Restructuring*: they can prepare an entire tree of environments in the background and, when everything is perfect, simply switch the ancestor Context to `ACTIVE`. The system relies entirely on the `effectiveState` algorithm to ensure that operational users cannot access these environments until the entire ascendance chain is properly activated.

This architectural design allows an administrator to securely lock down an entire branch (e.g., a VO and its 50 VREs) via a single state update on the parent node, without destroying the historical configuration state of the children. When an ancestor is placed in a `SUSPENDED` or `CREATED` state, the system cascades the change to all descendants in the same database transaction: every descendant's persisted `effectiveState` is updated to `SUSPENDED` if its local `state` was `ACTIVE`, ensuring that a stale cached or stored value can never bypass the lock. 

**The Universal `effectiveState` Rule for Operations:**
Throughout this documentation and the API authorization rules, any restriction mentioning that operations are blocked in **non-`ACTIVE`** Contexts **always refers strictly to the `effectiveState`**. The following matrix illustrates whether a role is disabled by the Context's state lock when attempting an operation on a graph instance (such as creating or reading a **Resource** instance). The specific privileges (e.g., who can write vs read) remain strictly governed by the API reference tables; this matrix solely defines whether the Context's `effectiveState` acts as an absolute barrier (`Disabled`, yielding a `403 Forbidden`) or if it allows the role to proceed (`Enabled`). This evaluation does NOT apply to Context Lifecycle operations (like updating a Context via `PATCH`).

| `effectiveState` | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|------------------|----------------|------------------|-----------|------------|-----------|
| **`CREATED`** | ✅ Enabled | ✅ Enabled | ❌ Disabled (`403`) | ❌ Disabled (`403`) | ❌ Disabled (`403`) |
| **`ACTIVE`** | ✅ Enabled | ✅ Enabled | ✅ Enabled | ✅ Enabled | ✅ Enabled |
| **`SUSPENDED`** | ✅ Enabled | ✅ Enabled | ❌ Disabled (`403`) | ❌ Disabled (`403`) | ❌ Disabled (`403`) |
| **`DELETED`** | ❌ Disabled (`403`) | ❌ Disabled (`403`) | ❌ Disabled (`403`) | ❌ Disabled (`403`) | ❌ Disabled (`403`) |

*(Note: The `effectiveState` barrier applies equally to both read and write operations for roles `IS-Editor`, `IS-Contrib`, and `IS-Member`. `IS-M` and `IS-Admin / Ctx-M` are not restricted on reads or writes by Context state, except in `DELETED` Contexts).*

> **Client Guidance:** The `effectiveState` is exposed to allow API clients to determine in advance whether their instance operations (whether executing a read query or a mutation) will be accepted (e.g., to disable buttons in a GUI), or to understand the reason for a rejection a posteriori. Any attempt to operate when the matrix evaluates to `Disabled` will be rejected by the system.
> *(Note on `DELETED` contexts: While all operations on graph instances inside a `DELETED` Context are universally blocked for everyone, global administrators can still inspect deleted Contexts through Context collection/read APIs using the `lifecycle` visibility model and via `GET /contexts/{context-uuid}/history`.)*

## Type Management — base path `/types`

### Authorization & Token Selection

| Action | HTTP | Path | Query Parameters | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|--------|------|------|------------------|:--------------:|:----------------:|:---------:|:----------:|:---------:|
| `Execute JSON Query` | QUERY | `/types` | `limit`, `offset`, `depth`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Create` | PUT | `/types/{type-name}` | *None* | ✅ | ❌ | ❌ | ❌ | ❌ |
| `Read` | GET | `/types/{type-name}` | `depth`, `metadataPolicy` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Delete` | DELETE | `/types/{type-name}` | *None* | ✅ | ❌ | ❌ | ❌ | ❌ |

**Token Scope Rule:**

| Operation Group | Token must be issued for |
|----------------|---------------------------|
| `Execute JSON Query`| No specific target Context is required; authorization is evaluated from the token role |
| `Create`, `Delete` | No specific target Context is required; authorization is evaluated from the token role (requires `IS-M` / `Infra-M`) |
| `Read` | No specific target Context is required; authorization is evaluated from the token role |

### HTTP Contract

| Action | Additional Request Headers | Response Headers | Success HTTP Code | Error HTTP Code |
|--------|----------------------------|------------------|-------------------|-----------------|
| `Execute JSON Query` | `Content-Type`, `Accept` | `Content-Type`, `Link`, `X-Warning` | `200 OK` | `400 Bad Request`, `403 Forbidden` |
| `Create` | `Content-Type`, `Accept` | `Content-Type`, `X-Warning` | `201 Created` / `200 OK` | `400 Bad Request`, `403 Forbidden`, `409 Conflict` |
| `Read` | `Accept` | `Content-Type`, `Link`, `X-Warning` | `200 OK` | `403 Forbidden`, `404 Not Found` |
| `Delete` | *None* | `X-Warning` | `204 No Content` | `403 Forbidden`, `404 Not Found`, `409 Conflict` |

### Operational Notes

- **Creation via `PUT` vs `POST`:** The API uses `PUT` instead of `POST` for Type creation because the Type identifier (`type-name`) must be explicitly provided in the URL path by the client (e.g., `/types/{type-name}`). Accepting an identifier exclusively in the body of a `POST` request to the base collection endpoint would be an architectural anti-pattern. Note that the `name` must also be present in the JSON body representation; the server performs a strict double-check between the URL path and the body representation to ensure consistency.
- **Internal `id` ownership during creation:** The `id` field inside the Type JSON represents its internal global UUID. The REST endpoint URL strictly uses the readable `type-name`, never the UUID. If a client includes an `id` field in the creation payload, the backend attempts to honor it and store the Type with that specific UUID. Reuse by another Type definition in the same schema category produces `409 Conflict`; reuse by an element or Type definition in a different category produces `400 Bad Request`, following the registry-wide UUID ownership rule. If the client omits the `id`, the backend automatically generates a new random UUID for the Type.
- **`PUT /types/{type-name}` semantics (idempotent creation):** `PUT` consumes only `application/json` and is accepted for the creation of a new Type with the provided identifier. The body must be a complete Type definition whose `name` exactly matches the path. To strictly adhere to HTTP `PUT` idempotency, if a client submits a definition for an existing Type, the server compares the complete structural representation — including the discriminator, changelog, properties and all their constraints, Resource relationship declarations, Relation endpoints, and extension fields — while excluding only the identity/audit fields `id` and `metadata`. An identical definition returns `200 OK` (no-op); any structural difference returns `409 Conflict`, because online Type updates are forbidden. A Type marked `locked` is served read-only: any submission that would change it returns `409 Conflict`. A new definition also returns `409 Conflict` when it violates the current Type graph, including unresolved or final parents, unresolved referenced Types, inconsistent multiple inheritance, contradictory inherited cardinalities, or simultaneous `abstract: true` and `final: true`. Malformed JSON, an invalid Type representation, or path/body name mismatch returns `400 Bad Request`. Both successful outcomes return the persisted Type representation and identify its canonical name-addressed URI through `Content-Location`. A newly created Type advances the shared schema version before its Redis entry and C3 linearization are stored, so the derived entries are immediately readable at the published version and the schema projection worker is notified. *(Contrast with Query Template definitions: while Query Templates fully support online `PUT` updates, Types are structural graph contracts and strictly forbid them).*
- **Type Evolution & Versioning:** Type update via REST is intentionally not exposed (Type changes are breaking). Type changes (e.g. making a field mandatory) can break running instances and dependent systems. Internally, the framework's update logic inherently blocks Type modification if there are existing instances of that Type. To evolve a Type while keeping the **same Type name**, a framework administrator must perform an offline assessment to evaluate the impact and define a formal data migration plan before manually forcing the upgrade offline.
- **Absence of a standalone `List` endpoint:** There is intentionally no standalone `List` endpoint (e.g., `GET /types`) for schema definitions. Clients are expected to discover available Types hierarchically by invoking the `Read` endpoint on any known Type and using the `depth` parameter. For example, a client can retrieve the entire **Resource** schema tree by calling `GET /types/Resource?depth=-1`, or dynamically explore a specific sub-branch by calling `GET /types/Service?depth=-1` or any other known subtype.
- **Type read semantics:** `GET /types/{type-name}` accepts only the exact case-sensitive Type name and always returns a JSON array with the requested Type first. `depth=0` returns only that Type, a positive `depth=N` includes descendants through exactly N inheritance levels, and `depth=-1` includes all descendants; invalid values fall back to `0` and produce `X-Warning`. Multiple-inheritance paths do not duplicate a descendant. `metadataPolicy=none|root|all` controls metadata across the flat result (`root` and `all` are equivalent for this endpoint, as all returned types are peer definitions; only `none` omits it), and non-global roles receive masked metadata identities. Successful reads expose the canonical name-addressed URI through `Content-Location` and each direct parent through `Link: rel=supertype`. Versioned positive and negative cache entries are retired by schema changes, and an incomplete cached descendant traversal falls back atomically to persistence rather than returning a partial subtree.
- **Execute JSON Query (QBE):** `QUERY /types` requires a JSON object targeting one of `ResourceSchema`, `FacetSchema`, `IsRelatedToSchema`, `ConsistsOfSchema`, or `PropertySchema`; every other category is rejected with `400 Bad Request` before database execution, even when its filters would match no rows. The `depth` parameter controls descendant expansion for plain-string `name`, `source`, and `target` matches: `0` is exact, a positive value includes descendants through that many levels, and `-1` (default) is unbounded. Full Type results honor `metadataPolicy` and redact `metadata.createdBy` and `metadata.lastUpdateBy` for non-privileged roles; Relation Type definitions expose `source` and `target` as canonical Type-name strings. An `_emit` query instead returns only its flat aliases while preserving each selected value's native JSON type, ignores an explicitly supplied `metadataPolicy` with `X-Warning`, and still redacts aliases sourced from sensitive metadata fields. Pagination applies to both full and projected rows; invalid query parameters fall back according to the shared parameter rules and emit `X-Warning`.
- **Type Deletion Constraints:** `DELETE /types/{type-name}` is a protected schema operation. Query parameters are unsupported, ignored, and reported through `X-Warning`. The server MUST reject the request with `409 Conflict` when deleting the Type would leave the Type dictionary structurally inconsistent. A Type is deletable through the public REST API only when all the following conditions are true:
  - it has no concrete instances;
  - it has no subtypes;
  - it is not referenced by any other Type definition;
  - it is not part of the Information System model itself (`locked`).

  Reverse-reference checks are evaluated by Type category:
  - Deleting a **Resource** Type is rejected if that Type is used as the source **Resource** Type of any **ConsistsOf** Type definition.
  - Deleting a **Resource** Type is rejected if that Type is used as the source **Resource** Type or target **Resource** Type of any **IsRelatedTo** Type definition.
  - Deleting a **Resource** Type is rejected if that Type is used as the target **Resource** Type in any **Resource** -- **IsRelatedTo** --> **Resource** schema constraint declared by another **Resource** Type.
  - Deleting a **Facet** Type is rejected if that Type is used as the target **Facet** Type in any **Resource** -- **ConsistsOf** --> **Facet** schema constraint.
  - Deleting an **IsRelatedTo** Type is rejected if that Type is used as the relation Type in any **Resource** -- **IsRelatedTo** --> **Resource** schema constraint.
  - Deleting a **ConsistsOf** Type is rejected if that Type is used as the relation Type in any **Resource** -- **ConsistsOf** --> **Facet** schema constraint.
  - Deleting a **Property** Type is rejected if another Type definition still uses it as a direct or collection-valued complex `propertyType`.


  In **Resource** Type definitions, the source **Resource** Type of a schema constraint is the **Resource** Type that owns the definition. Deleting that owning **Resource** Type does not leave its own constraints dangling, because the whole Type definition is being removed. The deletion is still rejected if the **Resource** Type has instances, has subtypes, is used by a **ConsistsOf** or **IsRelatedTo** Type definition, or is referenced by another **Resource** Type definition.
