# Resource Registry — API Management (Instances & Admin)

## Instance Management — base path `/instances`

> 💡 **Architectural Note: Nested JSON vs Flat URIs**  
> Throughout this chapter, you will see deep conceptual structures (like **Facet** instances or **ConsistsOf** instances attached to a **Resource** instance) represented as heavily nested JSON objects. This nesting is used for readability, payload efficiency, and representing the structural graph topology in a single response.  
> However, **the REST URIs remain strictly flat**. You do not update a **Facet** instance by constructing a nested path like `/instances/{resource-type}/{resource-uuid}/{consists-of-type}/{consists-of-uuid}/{facet-type}/{facet-uuid}`. Because every instance (**Resource** instance, **Facet** instance, **ConsistsOf** instance, or **IsRelatedTo** instance) is a topologically independent graph instance with a globally unique UUID, the API addresses every single instance individually via `/instances/{type}/{instance-uuid}`. For the design rationale behind this separation, see [Flat Addressability vs Nested ROA Paths](12-is-architectural-decisions.html#rest-api-flat-addressability-vs-nested-roa-paths).

### Authorization & Token Selection

| Action | HTTP | Path | Query Parameters | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|--------|------|------|------------------|:--------------:|:----------------:|:---------:|:----------:|:---------:|
| `List` | GET | `/instances/{type}` | `limit`, `offset`, `polymorphic`, `hierarchical`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Execute JSON Query` | QUERY | `/instances` (`POST /instances/query` fallback) | `limit`, `offset`, `polymorphic`, `hierarchical`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Exists` | HEAD | `/instances/{type}/{instance-uuid}` | `hierarchical`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Read` | GET | `/instances/{type}/{instance-uuid}` | `hierarchical`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `Create` | POST | `/instances/{type}` | `hierarchical`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ⚠️ | ⚠️ | ❌ |
| `Create / Update` | PUT | `/instances/{type}/{instance-uuid}` | `hierarchical`, `syncExistingHierarchy`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ⚠️ | ⚠️ | ❌ |
| `Merge Update` | PATCH | `/instances/{type}/{instance-uuid}` | `metadataPolicy`, `includeContexts` | ✅ | ✅ | ⚠️ | ⚠️ | ❌ |
| `Remove from Current Context` | DELETE | `/instances/{type}/{instance-uuid}` | `propagationMode`, `dryRun`, `metadataPolicy`, `includeContexts` | ✅ | ✅ | ⚠️ | ⚠️ | ❌ |
| `Add to Context` | PUT | `/instances/{type}/{instance-uuid}/contexts/{new-context-uuid}` | `propagationMode`, `dryRun`, `metadataPolicy`, `includeContexts` | ✅ | ⚠️ | ⚠️ | ❌ | ❌ |
| `List Instance Contexts` | GET | `/instances/{type}/{instance-uuid}/contexts` | *None* | ✅ | ✅ | ✅ | ✅ | ✅ |

**Token Scope Rule:**

| Operation Group | Token must be issued for |
|----------------|---------------------------|
| `List`, `Execute JSON Query`, `Exists`, `Read` | The operational Context from which visibility is evaluated |
| `Create`, `Create / Update`, `Merge Update`, `Remove from Current Context` | The operational Context where the instance is managed |
| `Add to Context` | The operational Context from which the membership operations are orchestrated; for `IS-Admin / Ctx-M` / `IS-Editor`, the newly specified Contexts must stay inside the managed subtree |
| `List Instance Contexts` | The operational Context from which membership visibility is evaluated |

### HTTP Contract

| Action | Additional Request Headers | Response Headers | Success HTTP Code | Error HTTP Code |
|--------|----------------------------|------------------|-------------------|-----------------|
| `List` | `Accept` | `Content-Type`, `Link`, `X-Warning`, `X-Result-Scope` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found` |
| `Execute JSON Query` | `Content-Type`, `Accept` | `Content-Type`, `Link`, `X-Warning`, `X-Result-Scope` | `200 OK` | `400 Bad Request`, `403 Forbidden` |
| `Exists` | `Accept` | `Content-Length`, `Content-Type`, `Link`, `X-Warning`, `X-Result-Scope` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found` |
| `Read` | `Accept` | `Content-Type`, `Link`, `X-Warning`, `X-Result-Scope` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found` |
| `Create` | `Content-Type`, `Accept` | `Content-Type`, `X-Warning` | `201 Created` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `Create / Update`        | `Content-Type`, `Accept` | `Content-Type`, `X-Warning` | `201 Created` / `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `Merge Update`        | `Content-Type: application/merge-patch+json`, `Accept` | `Content-Type`, `X-Warning` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `Remove from Current Context` | `Accept`, `Prefer` (return) | `Content-Type`, `X-Warning`, `Preference-Applied` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `Add to Context` | `Accept`, `Prefer` (return) | `Content-Type`, `X-Warning`, `Preference-Applied` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `List Instance Contexts` | `Accept` | `Content-Type`, `Link`, `X-Warning`, `X-Result-Scope` | `200 OK` | `403 Forbidden`, `404 Not Found` |

### Operational Notes

- **`HEAD` Is the Bodyless Read:** `HEAD /instances/{type}/{instance-uuid}` is the automatic bodyless view of the corresponding `GET`, not an independent operation with reduced semantics. It accepts the same `hierarchical`, `metadataPolicy`, and `includeContexts` parameters and returns the same status and representation headers (plus the computed `Content-Length`), while omitting only the response body.
- **`List` Endpoint `403` Absence:** The `List` endpoint never returns `403 Forbidden` for data visibility boundaries. Results are strictly filtered according to the token's operational Context. If no instances exist within the token's scope, it simply returns an empty list `[]`, even if those instances exist in other Contexts. *(Note: The only scenario where a `List` operation returns a `403 Forbidden` is if the token Context itself is in a non-`ACTIVE` state, which acts as a global administrative block for standard roles, as detailed in the Context Management chapter).*
- **Path vs Body Consistency (Double-Check):** When creating or updating an instance via `PUT` or `PATCH` on `/instances/{type}/{instance-uuid}`, the `type` and `id` parameters in the URL path must exactly match the `type` and `id` fields provided inside the JSON payload. Even though `PATCH` acts as a partial merge, the `type` and `id` fields are strictly mandatory in the JSON body. If they are missing or do not match, the server strictly rejects the request with a `400 Bad Request` to prevent accidental data corruption or schema-mismatch errors.
- **Type-in-Path Semantics (Exact Type Enforcement):** For all endpoints targeting a specific instance using the path variable `{type}` (e.g., `/{type}/{instance-uuid}`), the client **MUST specify the exact concrete Type** of the instance. Specifying an abstract supertype (e.g., calling `/instances/Resource/{uuid}` when the instance is actually an `EService`) is strictly invalid for any operation (`GET`, `PUT`, `PATCH`, `DELETE`, `HEAD`) and will be rejected with a `404 Not Found` (as the requested composite key `(type, instance-uuid)` effectively does not exist, which safely prevents any existence leakage of the UUID). The system enforces strict 1:1 parity between the concrete instance schema and the `{type}` path parameter. *(Note: This exact-match restriction applies strictly to single-instance targeting endpoints. The collection endpoint `GET /instances/{type}` fully supports querying by abstract supertypes when combined with the `polymorphic=true` parameter).*
- **Instance Creation Scope:** While flat URIs address every graph instance individually for reads and updates, standalone creation via `POST /instances/{type}` or `PUT /instances/{type}/{instance-uuid}` is structurally valid only for **Resource**, **IsRelatedTo**, and **ConsistsOf** instances. As dictated by the core IS-Model topology, a **Facet** instance cannot exist independently and must be created exclusively as a nested element (either within a parent **Resource** mutation payload, or within a standalone **ConsistsOf** mutation payload acting as its `target`).
- **`Create` (`POST`) — Optional Client-Supplied UUIDs:** Unlike `PUT`, `POST /instances/{type}` does not require an `id` on the root instance or on any nested Facet/ConsistsOf/IsRelatedTo. Any `id` present in the payload — root or nested — is honored as-is and validated against the same uniqueness domain as `PUT` (see "UUID Reuse Across Types" below): an `id` already bound to an element of the *same* type is `409 Conflict`; of a *different* type, `400 Bad Request`. Because the whole payload tree is created inside a single database transaction, a conflict anywhere in the tree — root or nested — rolls back the entire request atomically; nothing is partially persisted.
- **`Create` (`POST`) Is Intentionally Not Idempotent:** Two identical `POST` requests that omit every `id` always produce two distinct instances (Resource, Facet, ConsistsOf, or IsRelatedTo alike, depending on what is being created) — this is deliberate `POST` semantics per [RFC 9110 §9.3.3](https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.3), not a limitation. Clients that need retry-safety for a specific element should supply that element's own `id`; see the point above for what happens on a resubmit in that case.
- **Lookup (`403` vs `404`):** For instance lookup endpoints (`HEAD`/`GET` on `/instances/{type}/{instance-uuid}`), the API intentionally distinguishes `404 Not Found` (no instance exists for the requested lookup key) from `403 Forbidden` (an instance matching the lookup key exists, but is outside the caller's authorization scope). This distinction is preserved by design for operational diagnosability. Given the D4Science exposure model (write surfaces internal; `/access` facade internet-facing but read-scoped), this is an explicit and accepted security/operational trade-off. An attacker could theoretically deduce existence by attempting a `PUT` with a specific UUID and receiving a `409 Conflict`, so returning `403 Forbidden` on `GET` does not significantly alter the threat model.
- **Point Read Scope Contract (`hierarchical`):** Point lookups (`HEAD` / `GET /instances/{type}/{instance-uuid}`) intentionally support `hierarchical` for scope alignment with collection discovery. Without `hierarchical=true`, lookup is restricted to the token Context; with `hierarchical=true`, lookup is evaluated against the authorized descendant subtree.
- **Discovery -> Fetch Symmetry Rule:** If a client discovers an instance through a hierarchical collection operation (`GET /instances/{type}?hierarchical=true` or `QUERY /instances?hierarchical=true`), it should keep the same `hierarchical=true` scope on the subsequent point read. Dropping the flag narrows the scope and can legitimately turn a previous discovery into `403 Forbidden` on point fetch.
- **Lookup vs Create Domain:** Lookup operations evaluate the composite key `(type, instance-uuid)` against visibility/existence semantics. Create/Update uniqueness validation (`PUT`) evaluates the identifier against an implementation-defined uniqueness domain (which may be equal to or broader than `(type, instance-uuid)`), returning `409 Conflict` when identifier occupancy violates backend uniqueness constraints. This contract avoids false assumptions by clients: lookup and creation/update are related but not guaranteed to share identical internal uniqueness scopes.
- **UUID Reuse Across Types:** A UUID is a graph-wide identifier, never scoped to a single type. Supplying an `id` (top-level or nested, e.g. a `consistsOf` entry) that already identifies an existing element of the *same* type is a `409 Conflict` (ordinary already-exists semantics). Supplying an `id` that already identifies an existing element of a *different* type — whether via a direct `PUT`/`PATCH` that changes an instance's declared type, or a nested element reusing another element's `id` under a different type — is rejected with `400 Bad Request`: a malformed request, not a context-visibility concern.
- **`List Instance Contexts` returns:** Returns a map of Context UUID → Context path. For all roles, the returned Contexts are strictly filtered to the competence subtree defined by the authorization token. The response emits `Link: rel="up"` pointing back to the owning instance URI (`/instances/{type}/{instance-uuid}`). *(Note: If an `IS-M / Infra-M` uses a token issued for a specific VO, they will only see Contexts within that VO. To obtain full global visibility, they must explicitly use a token issued for the Root Context `/`).* The response includes `X-Result-Scope: role-filtered` only when this subtree restriction actually removes a membership; `state-filtered` is additionally reported when a non-administrative caller actually loses a non-`ACTIVE` descendant membership.

### Contextual Mutations & Update Intent (`PUT` vs `PATCH`)

*(Note on Response Shape: All `PUT` and `PATCH` operations on the base instance path `/instances/{type}/{instance-uuid}` always return a representation with the same structural shape as a standard `Read` operation for the targeted instance. Even if query parameters like `syncExistingHierarchy=true` or `hierarchical=true` are used to distribute the state across multiple Contexts, the API response remains strictly bounded to the requested target instance. These operations do not trigger broad topological traversal (unlike membership operations) and therefore **do not** return an Operation Envelope).*

Because **Resource** instances act as structural hubs for their nested **Facet** instances (via **ConsistsOf** instances), and because all these graph nodes are shared across Contexts by reference rather than being duplicated, updating a **Resource** instance's topology in one Context can have broader repercussions. To effectively manage these shared topologies, the API explicitly distinguishes between `PATCH` (Merge) and `PUT` (Full Replace) operations, providing an escalating mechanism of topological impact.

**Important (Global Property Mutations):** Because property fields are intrinsically bound to the single underlying instance, modifying a property field via `PATCH` or `PUT` applies globally across *all* Contexts where that instance is shared. Therefore, the concept of a hierarchical Context sync applies exclusively to topological structural changes (i.e., modifying the graph structure, such as attaching or detaching **Facet** instances), never to property field mutations.

#### `PATCH` (Merge Semantic)

`PATCH` applies a **Merge** semantic (`application/merge-patch+json`), modifying only the provided fields and leaving the rest of the instance intact. In accordance with [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396), arrays containing standard data fields (e.g., a list of strings) are treated as atomic values; providing such an array in a `PATCH` payload will completely replace the existing array.
- **Mandatory Identity Fields:** Even though a Merge Patch normally allows clients to send only the fields they wish to change, the Resource Registry strictly requires the top-level `type` and `id` fields to be present in the JSON body. These fields must exactly match the `type` and `id` provided in the URL path. If they are omitted or mismatched, the request is instantly rejected with a `400 Bad Request`.
- **Null and Omission Semantics:** An omitted property is left unchanged, whereas an explicitly submitted `null` is an update instruction that removes the property in accordance with RFC 7396. Optional properties may therefore be removed through `PATCH`; mandatory properties cannot be removed, and attempting to do so results in `409 Conflict` during schema validation. The model does not expose `notnull` as an independent constraint.
- **Pragmatic Deviation on Graph Arrays:** The Resource Registry API intentionally diverges from the RFC rule for structural graph arrays like `consistsOf` when the `PATCH` is performed on a **Resource** instance. If a `PATCH` payload on a **Resource** instance includes a `consistsOf` array, the backend performs a surgical merge: it patches only the explicitly referenced **ConsistsOf** and **Facet** instances (requiring both the `id` of the **ConsistsOf** instance and the `id` of its target **Facet** instance to safely match the instances) without dropping the omitted ones. If a client attempts to execute a `PATCH` omitting these nested IDs (e.g., intending to create a new nested structure on the fly), the request is strictly rejected with a `400 Bad Request`. To create new topological structures like adding a new **Facet** instance, clients must use a `PUT` operation. `PATCH` does not support hierarchical parameters.

#### `PUT` on Resource Instances (Topological Impact)

When the target of a `PUT` operation is a **Resource** instance, the API enforces a **Full Replace** semantic for the target Context. Any **ConsistsOf** instance or nested **Facet** instance omitted from the payload will be systematically removed from the Context. This enables atomic, transaction-level replacements of mandatory **Facet** instances without breaking schema validation (e.g., solving the "Cardinality Chicken-and-Egg" problem). The topological impact of a `PUT` operation on a **Resource** instance is governed by its query parameters:

##### 1. Single Context Update (Local Copy-on-Write)
Without hierarchy parameters, the topological effect (adding, removing, or replacing **Facet** instances) is confined strictly to the current token Context. This operation handles any combination of structural changes: adding new **Facet** instances (by including new **ConsistsOf** JSON objects with a freshly client-generated `id` that does not yet exist in the system), removing existing ones (by omitting them from the payload), or both simultaneously.

This simultaneous action is what enables the **Local Copy-on-Write** (Forking) pattern: to create a local variation of a shared **Facet** instance without affecting other Contexts, the client can perform a `PUT` on the parent **Resource** instance, assigning a freshly client-generated `id` to both the new **ConsistsOf** instance and its nested **Facet** instance, instead of the `id` of the shared instances.

The system will create a new isolated clone for the current Context and seamlessly drop the old association locally. (Note: It is crucial to generate fresh ids for both. If the client reused the existing **ConsistsOf** instance's `id` while only generating a new `id` for the **Facet** instance, it would attempt to retarget an existing shared **ConsistsOf** instance to a different **Facet** across different Contexts, which strictly violates the topological rule that a **ConsistsOf** instance points to exactly one immutable **Facet** target). 

While a fork could theoretically be achieved using two distinct operations (a `Remove from Current Context` on the existing **ConsistsOf** instance, followed by a standalone creation of a new **ConsistsOf** instance along with its new target **Facet** instance), performing it in two steps is restricted by `min`/`max` constraints on the **Resource**:
- If `max: 1` but no `min`, you could remove then create. 
- If `min: 1` but no `max`, you could create then remove. 
- If both `min: 1` and `max: 1` are set, any sequential order violates the schema. 

More importantly, even without schema constraints, using a single `PUT` on the parent **Resource** is the only way to guarantee that both the addition and the removal are executed atomically within a single transaction.

For example, consider a **Resource** instance `R` currently composed of **Facet** instances `[F1, F2, F3, F4]` in the current Context, where `F1` satisfies a strict `min: 1, max: 1` schema constraint. 
If a `PUT` request provides a new configuration `[F1b, F2, F3, F5]`: the old `F1` and `F4` are removed, while the new `F1b` and `F5` are added, all simultaneously. 
Because it executes as a single atomic transaction, the `min: 1, max: 1` constraint (transitioning from `F1` to `F1b`) is never violated.

*(Note: Schema validation is strictly evaluated at the very end of the transaction. If the resulting topological state of the **Resource** instance violates its schema, the entire operation is safely rolled back and returns a `409 Conflict`).*

##### 2. Subtree Synchronization (`?syncExistingHierarchy=true`)
Safely pushes topological changes (like attaching or detaching **Facet** instances) only to descendant Contexts where the **Resource** instance is already explicitly shared. 
This effectively acts as a combined Create/Update + `Add to Context` + `Remove from Current Context` avalanche of nested elements across the applicable descendant Contexts.

Similar to the Single Context Update scenario, attempting to manually replicate these topological shifts across the hierarchy via sequential `Remove from Current Context` and `Add to Context` operations on the nested instances in each child Context is heavily restricted by `min`/`max` constraints on the parent **Resource**:
- If `max: 1` but no `min`, you could remove then add. 
- If `min: 1` but no `max`, you could add then remove. 
- If both `min: 1` and `max: 1` are set (e.g., swapping `F1` for `F1b`), any manual sequential propagation in the descendant Contexts strictly violates the schema. 

By executing the `[F1, F2, F3, F4]` -> `[F1b, F2, F3, F5]` mutation described above with `?syncExistingHierarchy=true`, the identical simultaneous atomic replacement occurs not just in the current Context, but simultaneously across all applicable descendant Contexts, safely bypassing the schema deadlock. 

**Important (Strict Uniformization):** Because the `PUT` operation enforces a **Full Replace**, any local topological deviations (e.g., an extra **Facet** instance previously added only in a child Context) are systematically wiped out to perfectly align the target Context with the submitted payload. 
Because the resulting topology becomes completely uniform across all synced Contexts, the schema validation result is globally deterministic: it is mathematically impossible to adhere to the schema in one Context while violating it in another.

Crucially, a client can also achieve this in two separate phases: they might first update the local Context without the flag, and later issue the identical `PUT [F1b, F2, F3, F5] ?syncExistingHierarchy=true` payload. 
In this second phase, the operation behaves as a "pure sync": it makes no local topological changes (since the state already matches) but atomically forces the alignment down the hierarchy. This "pure sync" pattern is extremely valuable for reconciling descendant Contexts that have become topologically misaligned (e.g., due to operational drift or mistakes). 
Reconciling them in a single shot via `PUT ?syncExistingHierarchy=true` is infinitely safer than the manual alternative (i.e., removing the entire **Resource** instance from the descendant Context and re-adding it with the corrected facets). 
Removing a **Resource** instance from a Context normally triggers the avalanche destruction of its associated **IsRelatedTo** instances. Even if a client attempted to bypass the standard avalanche using a `surgical` removal of the **Resource** instance, the problem persists: a relation cannot logically exist in a Context if its `source` and `target` **Resource** instances are missing. 
Therefore, removing the **Resource** instance inherently drops all its incoming and outgoing **IsRelatedTo** instances from that Context. 
The client would then be forced to meticulously track down and perform surgical `Add to Context` operations to restore all those `N` relations (and potentially recreate from scratch any relations that existed *only* in that local Context). The atomic "pure sync" completely avoids this operational nightmare and collateral damage, perfectly restoring the intended schema and structure. (The orphaned `F1` and `F4` continue to exist in any external Contexts; if no longer referenced anywhere, they are destroyed by the Garbage Collector). 

*(Note: On a creation, this flag is logically ignored as the instance does not yet exist anywhere, and the system safely emits an `X-Warning` header).*

##### 3. Hierarchical Distribution (`?hierarchical=true`)
Operates identically to the **Subtree Synchronization** mode, but with one fundamental expansion in blast radius: instead of pushing changes *only* to descendant Contexts where the **Resource** instance is already explicitly shared, it forcefully propagates the **Resource** instance itself (acting as an `Add to Context` avalanche) and all its topological changes to *every* descendant Context within the authorized subtree, systematically overriding local absences. 

This mode enforces the exact same strict uniformization, shares the exact same atomic transaction guarantees, and completely bypasses the manual schema violation limitations described above. 

*(Note: Unlike `syncExistingHierarchy`, which is logically ignored on creation (emitting an `X-Warning` header), `hierarchical` is fully supported and actively used during the initial creation of a **Resource** instance to establish a baseline configuration across the entire subtree from day one).*

#### `PUT` on Other Instances (Full Replace Semantic)

When the target of a `PUT` operation is not a **Resource** instance, it simply replaces its property fields entirely:
- **Facet instances:** The operation replaces its property fields entirely.
- **ConsistsOf instances:** The operation replaces its property fields entirely. *(System Constraint: A `PUT` or `PATCH` operation cannot be used to change the `source` or `target` of an existing relation. A relation is intrinsically bound to its original endpoints upon creation. To change them, the existing relation must be deleted and a new one created).*
- **IsRelatedTo instances:** The operation replaces its property fields entirely. *(System Constraint: Identical to `ConsistsOf`, a `PUT` or `PATCH` operation cannot alter the `source` or `target` fields of the relation).*

> **Note on Hierarchical Parameters:** Because `syncExistingHierarchy` and `hierarchical` exclusively govern the topological distribution of a **Resource** instance, they have no structural meaning when targeting a **Facet**, **ConsistsOf**, or **IsRelatedTo** instance directly. If a client mistakenly passes these query parameters when mutating these specific types, the system safely ignores them and emits an `X-Warning` header (e.g., `199 resource-registry "ignored-query-parameters=hierarchical; ignored-count=1; reason=Not applicable for this instance type"`).

For detailed operational patterns (including **Facet** instance Forking, Copy-on-Write, and resolving cardinality constraints), refer to the [API Operational Cookbook & Advanced Use Cases](appendix/05-api-operational-cookbook.md).

### Membership Operations (`Remove from Current Context` / `Add to Context`)

> **Conceptual Note (Sharing & Unsharing):** Because Contexts act as logical environments over a global graph, the act of "Adding to Context" can be conceptually viewed as **"sharing"** an instance into another Context, making it visible and usable there. Conversely, "Remove from Current Context" acts as **"unsharing"**. This documentation and the broader ecosystem may occasionally use the terms *share* and *unshare* informally to describe these membership operations.

The API provides two ways to manage an instance's Context membership:
1. **Single Remove (`DELETE`):** The `Remove from Current Context` operation (`DELETE /instances/{type}/{instance-uuid}`) removes it from the token's Context.
2. **Single Add (`PUT`):** The `Add to Context` operation (`PUT /instances/{type}/{instance-uuid}/contexts/{new-context-uuid}`) adds an instance from a *source* Context (determined by the authorization token) to a *destination* Context (specified in the URL). 

> **Implementation Detail (Implicit Pull on Relations):** If the direct target of the `Add to Context` operation is a **Relation** instance, the database enforces strict topological integrity. A `Relation` cannot exist in a Context if its source and target are missing. Therefore, the system ALWAYS implicitly pulls the target **Entity** instance into the Context (if it isn't already there), actively bypassing the `unpropagate` propagation constraint defined by the `Relation`. This guarantees that the graph is always structurally sound within any given Context boundary.

**Authorization Rules:**
- `IS-Admin / Ctx-M` and `IS-Editor` can only add/remove instances across Contexts from a parent Context to one of its child/descendant Contexts (all target UUIDs must belong to their competence subtree).
- `IS-M` and `Infra-M` can add/remove instances across Contexts from *any* Context to *any* other Context.

Adding or removing from a Context triggers an avalanche effect. Before successfully returning, the system **always** strictly validates the resulting state in the target Contexts to prevent logical inconsistencies (e.g., ensuring mandatory **Facet** instances are present, or ensuring that a **Relation** instance is not added to a Context where its source **Resource** instance is missing). If this final validation fails, the operation is aborted and returns a `409 Conflict` (topological/schema violation). If successful, both operations (`Add to Context`, `Remove from Current Context`) return the full list of instances affected by the avalanche effect.

Even though the `Add to Context` operation triggers a complex avalanche process, it is exposed via `PUT` because it is strictly **idempotent**. Calling the endpoint 1 time or 100 times results in the same final graph state (the instance and required dependencies are present in the target Context). The URI explicitly identifies the *membership* association. Note that `propagationMode=surgical` is strictly rejected on this standard endpoint (`400 Bad Request`); surgical injection is reserved exclusively for the Admin API (`PUT /admin/instances/...`).

### Operation Envelope and Parameter Matrix

Because these operations can trigger avalanche effects, their canonical response is an **Operation Envelope**. The HTTP status code is always `200 OK` (if successful), but the density of the response body and the effect of query parameters (`metadataPolicy`, `includeContexts`) depend strictly on the `Prefer: return` header. The default behavior is to return the full representation.

Successful operation-envelope responses always emit `Preference-Applied` with the applied payload shape (`return=representation` or `return=minimal`). When the request omits `Prefer`, the response still emits `Preference-Applied: return=representation` to make the default explicit.

| `dryRun` | `Prefer: return` header | Success HTTP Code | Action | Body Density | `metadataPolicy`, `includeContexts` |
|:---:|---|---|---|---|---|
| `false` | *Missing* or `representation` | `200 OK` | **Real Execution** | Full Operation Envelope | **Applied** (enrich the returned envelope elements) |
| `false` | `minimal` | `200 OK` | **Real Execution** | Minimal Operation Envelope | **Ignored** (no structural payload to enrich) |
| `true` | *Missing* or `representation` | `200 OK` | **Simulation Only** | Simulated Full Operation Envelope | **Applied** (enrich the simulated envelope elements) |
| `true` | `minimal` | `200 OK` | **Simulation Only** | Simulated Minimal Operation Envelope | **Ignored** (no structural payload to enrich) |

**Content Negotiation (`Prefer` header) and Payload Shape:**
Returning the full array of complex objects can be bandwidth-intensive for large avalanche operations. Clients can use the standard HTTP `Prefer` header to explicitly request different shapes of the response payload.
*   `Prefer: return=minimal`: Returns a highly optimized flat array payload containing only the core topological fields (`type`, `id`, and for **Relation** instances: `source`, `target`). Custom fields, `supertypes`, `metadata`, `contexts`, and `propagationConstraint` are **strictly omitted**. If a client explicitly passes inclusion query parameters (e.g., `metadataPolicy=root`), the system ignores them to honor the minimal structural contract, and emits a `X-Warning` header (e.g., `199 resource-registry "ignored-query-parameters=metadataPolicy; ignored-count=1; reason=Overridden by Prefer: return=minimal"`).
*   `Prefer: return=representation` (Default): Returns the instances in full detail. The system fields `metadata` and `contexts` are included only if explicitly requested via their respective query parameters (`metadataPolicy`, `includeContexts`), whereas `propagationConstraint` is inherently included as a standard structural field for all **Relation** instances. The structural rules are as follows:
    *   The envelope payload is typically a flat array at the Infrastructure Topology level containing only the **Resource** instances and **IsRelatedTo** instances (with **ConsistsOf** and **Facet** instances properly nested inside their parent **Resource**). *(Note: If an operation directly targets a **Facet** or **ConsistsOf** instance, such as deleting a specific **Facet**, those specific instances will be returned directly in the array, as the avalanche was not triggered by a source **Resource** instance).*
    *   **Resource** instances are returned **in full**, meaning their attached **ConsistsOf** instances and **Facet** instances are nested *inside* the **Resource** instance.
    *   **IsRelatedTo** instances are returned in full, but their `source` and `target` fields are serialized as minimal references containing inherently only `"type"` and `"id"`. *(Note: As per standard enrichment rules, these reference pointers will be explicitly populated with `"metadata"` and `"contexts"` blocks if the client explicitly requests them via `metadataPolicy=all` and `includeContexts=true`, allowing clients to perform deep synchronization checks if required).*

### Try-Check-Confirm Pattern (`dryRun=true`)

Because these operations can trigger powerful and unpredictable avalanches, it is highly recommended to adopt a "Try-Check-Confirm" pattern using `dryRun=true`, and using `Prefer: return=representation` to simulate the operation without persisting changes.

### `DELETE` vs `/admin/` `DELETE` Paradigm (Reference Counting)

The `Delete` operation on the base instance endpoint (`DELETE /instances/{type}/{instance-uuid}`) functions as a removal from the *current* Context (triggering local `REMOVE` propagation constraints). The instance is physically destroyed (Garbage Collected) only when removed from its very last Context.

The `Admin Remove from Context` operation (available exclusively to global admins) uses the admin path `DELETE /admin/instances/{type}/{instance-uuid}`. When executed with a token representing the Root Context and `?hierarchical=true`, it completely annihilates the instance across all Contexts. However, if executed with a token representing a specific Virtual Organization (VO), it performs a surgical hierarchical wipe, removing the instance from that specific subtree while allowing it to survive in parallel Contexts. Because of its destructive nature, this explicit path is strictly reserved for global administrators (`IS-Manager`, `Infrastructure-Manager`).

Because both operations trigger avalanche effects (via propagation constraints), they support the `dryRun=true` query parameter and the `Prefer: return=representation` header. Following the same "Try-Check-Confirm" pattern, clients can simulate deletion to receive a `200 OK` with the flat Normalized Graph of all affected instances before committing.

### Additional Constraints

- `hierarchical` is available to `IS-M / Infra-M`, `IS-Admin / Ctx-M` and `IS-Editor` on read/query operations. It is also available to `IS-Contrib` and `IS-Member` but only for read operations (see [`hierarchical` parameter](11-api-query-parameters.html#hierarchical)).
- `List Instance Contexts`: Returns a map of Context UUID → Context path. For all roles, the returned Contexts are strictly filtered to the competence subtree defined by the authorization token. Emits `Link: rel="up"` pointing back to the owning instance URI. *(Note: If an `IS-M / Infra-M` uses a token issued for a specific VO, they will only see Contexts within that VO. To obtain full global visibility, they must explicitly use a token issued for the Root Context `/`).* The response includes `X-Result-Scope: role-filtered` only when this subtree restriction actually removes a membership; `state-filtered` is additionally reported when a non-administrative caller actually loses a non-`ACTIVE` descendant membership.
- **Non-`ACTIVE` Context behavior:** See the detailed behavior documented in [Context Management](08-api-contexts-and-types.html#operational-notes).

## Admin API — base path `/admin`

This section groups all the Global Privileged Operations. These APIs are intentionally segregated under the `/admin` path to allow strict API Gateway and WAF filtering. They are designed for administrative interventions and are restricted to global administrators (`IS-Manager` / `Infrastructure-Manager`).

### Authorization & Token Selection

| Action | HTTP | Path | Query Parameters | IS-M / Infra-M | IS-Admin / Ctx-M | IS-Editor | IS-Contrib | IS-Member |
|--------|------|------|------------------|:--------------:|:----------------:|:---------:|:----------:|:---------:|
| `Admin Remove from Context` | DELETE | `/admin/instances/{type}/{instance-uuid}` | `hierarchical`, `propagationMode`, `dryRun`, `metadataPolicy`, `includeContexts` | ✅ | ❌ | ❌ | ❌ | ❌ |
| `Admin Add to Context` | PUT | `/admin/instances/{type}/{instance-uuid}/contexts/{new-context-uuid}` | `hierarchical`, `propagationMode`, `dryRun`, `metadataPolicy`, `includeContexts`, `bypassHierarchyCheck` | ✅ | ❌ | ❌ | ❌ | ❌ |
| `Execute Raw Query` | QUERY | `/admin/raw-query` (`POST /admin/raw-query` fallback) | `hierarchical`, `metadataPolicy`, `includeContexts`, `rawResult` | ✅ | ❌ | ❌ | ❌ | ❌ |
| `System Dump` | GET | `/admin/system/dump` | — | ✅ | ❌ | ❌ | ❌ | ❌ |
| `System Restore` | POST | `/admin/system/restore` | — | ✅ | ❌ | ❌ | ❌ | ❌ |

**Token Scope Rule:**

| Operation Group | Token must be issued for |
|----------------|---------------------------|
| All `/admin/*` operations | Administrative role (`IS-M` / `Infra-M`) is required. For `Admin Remove from Context` and `Admin Add to Context`, the operational token Context dictates the starting root of the subtree the operation applies to (use a root-Context token for true global scope). `System Dump` and `System Restore` are whole-system operations: the token's Context is irrelevant, any `IS-M` / `Infra-M` token works regardless of which Context it was issued for. |

### HTTP Contract

| Action | Additional Request Headers | Response Headers | Success HTTP Code | Error HTTP Code |
|--------|----------------------------|------------------|-------------------|-----------------|
| `Admin Remove from Context` | `Accept`, `Prefer` (return) | `Content-Type`, `X-Warning`, `Preference-Applied` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `Admin Add to Context` | `Accept`, `Prefer` (return) | `Content-Type`, `X-Warning`, `Preference-Applied` | `200 OK` | `400 Bad Request`, `403 Forbidden`, `404 Not Found`, `409 Conflict` |
| `Execute Raw Query` | `Content-Type`, `Accept` | `Content-Type`, `X-Warning` | `200 OK` | `400 Bad Request`, `403 Forbidden` |
| `System Dump` | `Accept` (`application/zip`) | `Content-Type` (`application/zip`), `Content-Disposition` (`attachment`) | `200 OK` | `403 Forbidden` |
| `System Restore` | `Content-Type` (`application/zip`) | — | `200 OK` | `400 Bad Request`, `403 Forbidden`, `409 Conflict` |

### Operational Notes

- **Admin Remove from Context:** A standard user `DELETE /instances/` removes an instance from a single Context. The admin `DELETE /admin/instances/` acts hierarchically. If the admin provides a token for `/VO1` and uses `?hierarchical=true`, the instance is cleanly wiped from `/VO1` and all its VREs, but survives in `/VO2`. If they provide a Root token, the instance is completely annihilated globally.
- **Admin Add to Context:** Exactly like the standard `Add to Context`, this operation fundamentally acts as a transfer from a *source* Context to a *destination* Context. The *destination* Context is explicitly provided in the URL (`{new-context-uuid}`) and must resolve to a live Context; an unknown or cemetery target returns `404 Not Found` before target-scope evaluation. The *source* Context—which determines the exact topological blueprint (the specific set of outgoing **Relation** instances to evaluate for propagation)—is always exclusively and non-hierarchically determined by the Context bound to the administrator's authorization token. An instance available only in a descendant or parallel Context is therefore rejected with `403 Forbidden`. Even if the administrator holds a globally-scoped role (e.g., `IS-M`), their token is still bound to a specific Context (e.g., the Root Context or a specific VO). This explicitly anchors the operation to a deterministic source topology, preventing any ambiguity about which topological branch is being copied. `bypassHierarchyCheck=true` waives only destination containment and never source lookup. Combining this with `hierarchical=true` atomically applies the source blueprint to the destination Context and its entire descendant subtree.
- **Execute Raw Query:** Body: JSON payload `{"query": "..."}`. ⚠️ NOT for production (OrientDB SQL, backend may change). Should not be used programmatically or implemented in a client library — intended for manual administrative diagnostics only. Only read-only statements are accepted. Any valid read-only OrientDB SQL statement is supported (e.g., `SELECT`, `TRAVERSE`, `MATCH`, `EXPLAIN`). Because the endpoint executes under a read-only database user, metadata/system-class reads and vendor-specific read functions are permitted, whereas destructive operations are inherently rejected at the engine level. The database session is scoped to the token Context; `hierarchical=true` extends its visibility to descendants. With `rawResult=false` (default), element rows are formatted through the Information System model and honour `metadataPolicy`/`includeContexts`, while scalar and projection rows have no Information System representation and are therefore preserved in backend-native JSON without being dropped; explicitly requested enrichment parameters are ignored for those rows with `X-Warning`. With `rawResult=true`, every row retains the backend-native JSON shape and field names unmodified. Serialization is all-or-nothing: a row-formatting failure never produces a partial successful result.
- **System Dump:** Produces a complete logical dump of the entire Resource Registry state as a closed seven-entry ZIP archive (`Content-Disposition: attachment; filename="resource-registry-dump.zip"`), independently of the caller's token Context: `manifest.json`, `RegistryMetadata`, every registered Type schema, every Context (live and cemetery alike), every Query Template, and every Resource and IsRelatedTo relation instance, each with full metadata and embedded Context membership. Facet entities and ConsistsOf relations are not dumped separately — they are embedded within their owning Resource's own representation, exactly as when reading a Resource "as-a-whole". Serialization is all-or-nothing: any collection or row serialization failure aborts the download instead of producing a successful incomplete archive. Every entry can be read with `information-system-model` alone: `manifest.json` (a `DumpManifest`) describes the archive — `formatVersion` and `createdAt` — while `registry-metadata.json` (a `RegistryMetadata`) describes the registry the information comes from — `models`, every model by name (`information-system-model`, the domain model, and the runtime model holding the types created through REST, marked `runtime: true`), each with its `version` from the classpath, its `types` (`{ typeName: version }`) and its `revisionCount`, next to the global `revisionCount`.
- **System Restore:** Ingests the exact seven-entry ZIP archive produced by `System Dump` and recreates the entire Resource Registry state in strict topological order: Types, Contexts, Query Templates, Resources, then IsRelatedTo relations, and finally `RegistryMetadata`, kept as dumped except its global `revisionCount`, which stays the target's. Missing, duplicate, unexpected, malformed, incompatible, or referentially invalid archive content is rejected with `400 Bad Request` before any write — incompatible meaning an unsupported `formatVersion`, or an `information-system-model` version in `registry-metadata.json` newer than the target's; every Resource and IsRelatedTo relation must declare at least one valid Context membership. Every original UUID and every original `metadata` object (creator, creation/last-update timestamps) is preserved verbatim. Requires the target system to be empty (no Context, Query Template, Resource, or IsRelatedTo relation may already exist, and no Type may extend Resource, Facet, IsRelatedTo, ConsistsOf, or Property) — the state immediately after a service startup against an empty database. This is a hard precondition with no override: restoring into an already-populated system is rejected outright (`409 Conflict`), never attempted as a merge. Archive preflight completes before the first write, but the subsequent restore is not one global transaction: an unexpected persistence failure can leave a partially populated target, which must be recreated before retrying.

### High-Impact Topological Operations Summary

To consolidate the reader's mental model before proceeding to Query Templates, the API provides several powerful mechanisms that can drastically alter the graph structure across multiple boundaries. Because of their significant **high-impact** nature and potential for massive avalanche consequences, it is vital to fully understand their blast radius. To ensure operational comprehension and safety, the following table summarizes these high-impact structural operations and their typical safe use cases:

| Operation Name | Method, Path & Parameters | Blast Radius | Dry-run? | Typical Safe Use |
|---|---|---|:---:|---|
| **Single Context Update** | `PUT /instances/{type}/{instance-uuid}` | **Current Context.** Replaces local topology and overwrites properties globally. | ❌ | Forking a shared element (Local Copy-on-Write) or updating a field globally. |
| **Subtree Synchronization** | `PUT /instances/{type}/{instance-uuid}?syncExistingHierarchy=true` | **Shared Subtree.** Descendant Contexts where the instance is already explicitly shared. | ❌ | Safe topological alignment across the hierarchy without forcing inclusion in new Contexts. |
| **Hierarchical Distribution** | `PUT /instances/{type}/{instance-uuid}?hierarchical=true` | **Current Context + Entire descendant subtree.** | ❌ | Forced blanket replication of a mandatory baseline configuration for all child tenants. |
| **Shallow Add to Context** | `PUT /instances/{type}/{instance-uuid}/contexts/{new-context-uuid}?propagationMode=shallow` | **Target Context + Avalanche** (stops at existing instances). | ✅ | Safe, standard sharing of an instance and its strictly missing dependencies. |
| **Deep Add to Context** | `PUT /instances/{type}/{instance-uuid}/contexts/{new-context-uuid}?propagationMode=deep` | **Target Context + Full Transitive Closure Avalanche.** | ✅ | Forced re-evaluation and importation of a deeply interconnected graph. |
| **Surgical Add to Context** | `PUT /admin/instances/{type}/{instance-uuid}/contexts/{new-context-uuid}?propagationMode=surgical` | **Target Context (Targeted instance only).** Stops Add-Propagation. | ✅ | Manual topology repair or targeted injection of a single node. *(Restricted to IS-M / Infra-M).* |
| **Cross-Boundary Add** | `PUT /admin/instances/{type}/{instance-uuid}/contexts/{new-context-uuid}?bypassHierarchyCheck=true` | **Cross-subtree** (breaks logical tree boundaries). | ✅ | Injecting harvested infrastructure data or migrating cross-tenant instances. *(Restricted to IS-M / Infra-M).* |
| **Admin Hierarchical Add** | `PUT /admin/instances/{type}/{instance-uuid}/contexts/{new-context-uuid}?hierarchical=true` | **Target Context + Entire descendant subtree.** | ✅ | Forced blanket propagation of an external instance into a specific target subtree. *(Restricted to IS-M / Infra-M).* |
| **Standard Removal** | `DELETE /instances/{type}/{instance-uuid}?propagationMode=shallow` | **Current Context + Garbage Collector Avalanche** (for orphans). | ✅ | Standard removal. The instance dies physically only if removed from its final Context. |
| **Surgical Removal** | `DELETE /instances/{type}/{instance-uuid}?propagationMode=surgical` | **Current Context (Targeted instance only).** Stops Remove-Propagation. | ✅ | Emergency "undo" facility to eliminate a relation without collateral resource damage. |
| **Admin Hierarchical Wipe** | `DELETE /admin/instances/{type}/{instance-uuid}?hierarchical=true` | **Entire descendant subtree** (Or global ecosystem if invoked from Root). | ✅ | Brutal global decommissioning of a corrupted or obsolete instance everywhere it exists. *(Restricted to IS-M / Infra-M).* |

*(Note: The operations restricted to global administrators are exclusively available via the `/admin/` API subset).*
