# Resource Registry — Data Representation (Instances)

## Instance Representation

### System-Generated Fields

When reading instances via the REST API, the service automatically injects operational fields to assist the client.

#### Order of System Fields

According to the JSON specification, an object is an unordered set of name/value pairs. Therefore, the exact ordering of keys in the REST API payloads is **not technically guaranteed** by the server, nor is the server expecting clients to submit payloads in a strict order.

However, purely for didactic purposes, all JSON examples presented in this documentation follow a standardized, predictable ordering for the foundational system fields at the top of the object, before declaring any type-specific data fields:

1. `type`
2. `supertypes`
3. `id`
4. `metadata`
5. `contexts`

It is important to note that the instance examples presented below are as rich as possible to illustrate the full structural representation. However, in standard REST operations, fields like `metadata` and `contexts` might not be returned in the payload depending on the client's request (e.g., via specific query parameters).

#### `type` Field

In a federated graph supporting an Open-World Paradigm and dynamic schemas, every instance must explicitly declare "what it is". The `type` field serves this primary purpose: it identifies the exact concrete schema that governs the instance. Knowing the exact type enables both the server and the clients to perform proper validation, polymorphic deserialization, and dynamic class instantiation. As detailed below, if a client does not natively recognize the specific `type` declared (e.g., because a new schema was installed at runtime), it relies on the `supertypes` array to gracefully fall back to a known abstraction.

#### `supertypes` Array

The `supertypes` array lists the ancestors of the instance's Type in C3 linearization order (see *Type Linearization* below), ending with the public base Type (**Resource**, **Facet**, **IsRelatedTo**, **ConsistsOf**, or **`Property`**); the internal primitives beneath the base Types are never listed. It provides a robust polymorphic fallback mechanism for clients parsing the JSON: if a client encounters an instance of a Type that it does not recognize (e.g., a Type dynamically installed in the infrastructure *after* the client was compiled), the client will not know how to natively instantiate or handle it. By iterating through the `supertypes` array, the client can fall back to the first ancestor Type it *does* recognize, ensuring it can still safely deserialize and interact with the instance using a known interface. Because it is returned by the server purely for this fallback reason, the client is not required to send it when performing write operations. A client that handles an instance through an ancestor Type must keep track of the Type the instance really has: the convention is a field named `expectedType`, set by the client to that Type name. The server never returns it, and accepts and drops it without warning when the client sends the instance back. *(Note: Unlike the `extendedTypes` array in a Type schema definition—which only declares the direct, first-level inheritance—the `supertypes` array returned on instances includes every ancestor up to the public base Type).*

*(Note: This array is **not** present in the representations of structurally known foundational elements—e.g., **Context**, **`Metadata`**, **`PropagationConstraint`**, **ConsistsOf**, **QueryTemplate**—because their strict hierarchy is immutable and already known by the system and any client. It is strictly injected for instances belonging to dynamically installed schema Types, such as **EService** or **SoftwareFacet**, ensuring that a client unaware of the specific dynamic schema can still understand its lineage. As a concrete corollary: while a direct instance of the base **ConsistsOf** Type does not possess the array, instances of its dynamically installed subtypes—like the **IsIdentifiedBy** instance shown in the JSON example below—DO receive the `supertypes` array).*

*(Note on Abstract Types: **Resource**, **Facet**, and **IsRelatedTo** do not appear in the list above because they are abstract and cannot be instantiated directly. If they were instantiable, they too would not possess a `supertypes` array, as they are part of the foundational model).*

*(Note on Embedded Properties: The `supertypes` array is typically associated with graph instances possessing an identity (like **Resource**, **Facet**, **IsRelatedTo**, and **ConsistsOf** instances). However, it is also injected into embedded **`Property`** values (e.g., inside an `Event` property) if those values belong to a polymorphic property type hierarchy. This is strictly required to allow strongly typed clients (like Java Jackson) to correctly deserialize nested polymorphic objects without prior knowledge of the full schema).*

##### Type Linearization (Multiple Inheritance)

Because the IS-Model permits multiple inheritance (a Type schema can define multiple `extendedTypes`), the order in which a Type inherits from its ancestors must be deterministic to ensure predictable polymorphic fallback mechanisms for clients parsing the `supertypes` array. 

The system enforces a strict **C3 Linearization**[^barrett-1996] (also known as Method Resolution Order, or MRO) when computing the `supertypes` array for any graph instance or embedded polymorphic property. When a Type extends multiple parent Types, its linearization is computed by merging its direct parents in the exact order they are declared, followed by the linearizations of those parents. The C3 algorithm guarantees two crucial properties:
1. **Monotonicity:** No parent Type will ever appear before its child Type in the resolved ancestor list.
2. **Local Precedence:** The explicit order of direct parents declared in the schema is strictly respected.

*(Note: The server proactively guarantees this integrity. If a schema designer attempts to register a new Type with an inheritance graph that produces a cycle or an unresolvable C3 conflict, the system rejects the schema registration with `409 Conflict`, as for every violation of the current Type graph (see [`PUT /types/{type-name}` semantics](08-api-contexts-and-types.md)). For a pedagogical explanation of the C3 algorithm, refer to the [Python 2.3 Method Resolution Order documentation](https://www.python.org/download/releases/2.3/mro/)).*

#### `id` Field (REST Profile Mandate)

*(REST Profile Note: While the abstract IS-Model allows for implementation-specific ID formats, the REST API strictly mandates the use of UUIDs for all `id` fields to guarantee global uniqueness and predictable flat addressability across the federated graph).*

#### Client Parsing Resilience (Open-World Extensibility)

Because the Information System operates under an **Open-World Assumption**, any **Resource** instance can dynamically have arbitrary **Facet** instances attached to it via **ConsistsOf** instances (beyond what is strictly dictated by its schema). Furthermore, through the **Schema-Mixed Mode**, any instance (including **IsRelatedTo** instances) can host arbitrary custom property fields.

Therefore, it is a strict architectural requirement that **clients MUST implement resilient JSON parsing capable of preserving unknown data**. A client parsing an instance representation must not crash when encountering unrecognized property fields or unrecognized **Facet** instances attached via **ConsistsOf** instances. Instead, it must successfully instantiate them (e.g., placing custom properties into an `extras` map, and generically parsing the attached instances) so that they are preserved. Failing to do so guarantees that the client will eventually crash or accidentally destroy data when re-serializing an instance that another user extended with a new, unforeseen aspect or property.

#### `metadata` Field

The `metadata` field encapsulates provenance and tracking information for the instance (such as creation and modification timestamps and authors). This field is strictly generated and managed by the system. It may be omitted from standard read payloads depending on the client's request parameters to save bandwidth.

##### `lastUpdateTime` Evaluation Logic

For any graph instance, the `lastUpdateTime` inside the **`Metadata`** block represents its latest modification. The specific evaluation logic depends on the instance type:

* **Resource** instance: Because it acts as a logical aggregate, its `lastUpdateTime` is specifically evaluated as the **maximum** `lastUpdateTime` among the **Resource** instance itself, all its **ConsistsOf** instances, and all its **attached** **Facet** instances across all Contexts. *(For the architectural and performance rationale regarding this global evaluation, refer to the API Authorization constraints in the REST API chapter).*
* **Facet** instance: It strictly represents its own individual modification timestamp.
* **IsRelatedTo** instance: It strictly represents its own individual modification timestamp.
* **ConsistsOf** instance: It strictly represents its own individual modification timestamp.

#### `contexts` Map

The `contexts` field is a read-only map (UUID -> Path) of the Contexts where the instance is present. It is generated by the server on every read: a client cannot set it, and a submitted value is dropped. Each value is the current `path` of the Context at the time of the read, never `null`. The map contains the memberships selected by [`membershipScope`](11-api-query-parameters.html#membershipscope): by default those in the token Context and its descendants, and the complete set only when an authorized role requests `membershipScope=global`. A map obtained with the default scope is therefore not the complete membership of the instance. **Crucial operational note for clients:** if a Context is renamed or reparented, any `Path` string a client kept becomes stale. However, the Context `UUID` (the Key of the map) remains permanently stable and is the only valid identifier for subsequent API operations. A **Resource** instance exists in a Context if and only if it belongs to that Context's "Venn diagram" (by possessing at least one **Facet** instance in that Context). The map is not returned by default and must be requested explicitly through [`contextsPolicy`](11-api-query-parameters.html#contextspolicy). *(For architectural details on how Contexts and Venn diagrams are physically implemented, see [Contexts as Record-Level Security (RLS) Groups](12-is-architectural-decisions.html#contexts-as-record-level-security-rls-groups)).*

*(Note: The `contexts` map applies exclusively to **Entity** and **Relation** instances. It conceptually makes no sense and is therefore not present on system-level objects (like **Context**, **QueryTemplate**, and schema Type definitions) nor on embedded property values (like **`Metadata`** and **`PropagationConstraint`**)).*

### Examples: Base Instances

#### Resource Instance

A full structural representation of a **Resource** instance is typically serialized as a hierarchical JSON aggregate, nesting the root **Resource** instance and its attached **Facet** instances via **ConsistsOf** instances (or its subtypes) within a single JSON document. *(Note: When **ConsistsOf** instances are nested inside the `consistsOf` array of a **Resource** instance payload, the `source` field is intentionally omitted, as it implicitly defaults to the enclosing **Resource** instance. When represented as standalone instances, the `source` field is explicitly included). (Note: The first element in the `consistsOf` array below uses `IsIdentifiedBy`. This is a concrete subtype of **ConsistsOf** in the `gcube-model`—its presence in the `consistsOf` array confirms it acts as a composition edge, not an association edge. For references on fields, types and their inheritance, please refer to the [live-registry type documentation](../model-docs/index.html), generated directly from this instance's live schema; `gcube-model` types can also be queried directly (`GET /types/{type-name}`, `QUERY /types`). Furthermore, the JSON examples are highly simplified for didactic purposes; for example, the `EService` instance shown here would not be considered valid in production as it lacks mandatory schema structures).*

```json
{
    "type": "EService",
    "supertypes": ["Service", "GCubeResource", "Resource"],
    "id": "1a50a46d-2210-4a17-bd2f-22450b109264",
    "metadata": {
        "createdBy": "luca.frosini",
        "creationTime": "2026-04-16 12:09:26.804 +0200",
        "lastUpdateBy": "luca.frosini",
        "lastUpdateTime": "2026-04-16 12:09:26.804 +0200"
    },
    "contexts": {
        "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
    },
    "consistsOf": [
        {
            "type": "IsIdentifiedBy",
            "supertypes": ["ConsistsOf"],
            "id": "641c1a36-da8d-4313-94f0-867453185d65",
            "metadata": {
                "createdBy": "luca.frosini",
                "creationTime": "2026-04-16 12:09:26.737 +0200",
                "lastUpdateBy": "luca.frosini",
                "lastUpdateTime": "2026-04-16 12:09:26.737 +0200"
            },
            "contexts": {
                "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
            },
            "propagationConstraint": {
                "type": "PropagationConstraint",
                "add": "propagate",
                "remove": "cascade"
            },
            "target": {
                "type": "SoftwareFacet",
                "supertypes": ["Facet"],
                "id": "cb6795ee-b729-47a7-ac9d-b805ae1df4d2",
                "metadata": {
                    "createdBy": "luca.frosini",
                    "creationTime": "2026-04-16 12:09:26.732 +0200",
                    "lastUpdateBy": "luca.frosini",
                    "lastUpdateTime": "2026-04-16 12:09:26.732 +0200"
                },
                "contexts": {
                    "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
                },
                "group": "test",
                "name": "test",
                "version": "1.0.0"
            }
        },
        {
            "type": "ConsistsOf",
            "id": "16395d26-47d5-419b-8562-4d7c8158e28a",
            "metadata": {
                "createdBy": "luca.frosini",
                "creationTime": "2026-04-16 12:09:26.759 +0200",
                "lastUpdateBy": "luca.frosini",
                "lastUpdateTime": "2026-04-16 12:09:26.759 +0200"
            },
            "contexts": {
                "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
            },
            "propagationConstraint": {
                "type": "PropagationConstraint",
                "add": "propagate",
                "remove": "cascade"
            },
            "target": {
                "type": "AccessPointFacet",
                "supertypes": ["Facet"],
                "id": "9d9a2a48-a82b-43d3-b499-6ce50e1e543c",
                "metadata": {
                    "createdBy": "luca.frosini",
                    "creationTime": "2026-04-16 12:09:26.755 +0200",
                    "lastUpdateBy": "luca.frosini",
                    "lastUpdateTime": "2026-04-16 12:09:26.755 +0200"
                },
                "contexts": {
                    "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
                },
                "endpoint": "https://www.d4science.org"
            }
        },
        {
            "type": "ConsistsOf",
            "id": "2e0ec586-667f-43b7-ba58-b750496a1f0b",
            "metadata": {
                "createdBy": "luca.frosini",
                "creationTime": "2026-04-16 12:09:26.784 +0200",
                "lastUpdateBy": "luca.frosini",
                "lastUpdateTime": "2026-04-16 12:09:26.784 +0200"
            },
            "contexts": {
                "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
            },
            "propagationConstraint": {
                "type": "PropagationConstraint",
                "add": "propagate",
                "remove": "cascade"
            },
            "target": {
                "type": "StateFacet",
                "supertypes": ["Facet"],
                "id": "45e42ef8-8275-4742-99f4-0cf7c8ecd862",
                "metadata": {
                    "createdBy": "luca.frosini",
                    "creationTime": "2026-04-16 12:09:26.779 +0200",
                    "lastUpdateBy": "luca.frosini",
                    "lastUpdateTime": "2026-04-16 12:09:26.779 +0200"
                },
                "contexts": {
                    "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
                },
                "date": "2022-11-30 11:32:38.959 +0100",
                "value": "ready"
            }
        }
    ]
}
```

#### Facet Instance

```json
{
    "type": "SoftwareFacet",
    "supertypes": ["Facet"],
    "id": "cb6795ee-b729-47a7-ac9d-b805ae1df4d2",
    "metadata": {
        "createdBy": "luca.frosini",
        "creationTime": "2026-04-16 12:09:26.732 +0200",
        "lastUpdateBy": "luca.frosini",
        "lastUpdateTime": "2026-04-16 12:09:26.732 +0200"
    },
    "contexts": {
        "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
    },
    "group": "test",
    "name": "test",
    "version": "1.0.0"
}
```

#### IsRelatedTo Instance

*(Note: The following represents a standalone Relation representation, read with `metadataPolicy=all` and `contextsPolicy=all`.)*

**Relation endpoint references:** Outside the composition of a **Resource** instance, the `source` and `target` of a **Relation** instance are **endpoint references**: they identify the endpoint and say what kind of instance it is, but never describe it. An endpoint reference always carries `type`, `supertypes`, and `id`; it carries `metadata` only with `metadataPolicy=all` and `contexts` only with `contextsPolicy=all`; it never carries the endpoint's custom fields, **Facet** instances, or relations. This holds for a standalone **IsRelatedTo** or **ConsistsOf** representation and for the elements of a full Operation Envelope. The only expanded endpoint is the `target` of a **ConsistsOf** instance nested in its **Resource** instance (the `consistsOf` field), which is the complete **Facet** instance, because the **Facet** instance is part of the **Resource** instance's composition; its `source` is the enclosing **Resource** instance and is omitted.

```json
{
    "type": "Uses",
    "supertypes": ["CallsFor", "IsRelatedTo"],
    "id": "8b3fdfaa-9daf-4791-9ca7-02f068153abc",
    "metadata": {
        "createdBy": "luca.frosini",
        "creationTime": "2026-04-16 12:09:26.732 +0200",
        "lastUpdateBy": "luca.frosini",
        "lastUpdateTime": "2026-04-16 12:09:26.732 +0200"
    },
    "contexts": {
        "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
    },
    "propagationConstraint": {
        "type": "PropagationConstraint",
        "add": "propagate",
        "remove": "cascade"
    },
    "source": {
        "type": "EService",
        "supertypes": ["Service", "GCubeResource", "Resource"],
        "id": "1a50a46d-2210-4a17-bd2f-22450b109264",
        "metadata": {
            "createdBy": "luca.frosini",
            "creationTime": "2026-04-16 12:09:26.804 +0200",
            "lastUpdateBy": "luca.frosini",
            "lastUpdateTime": "2026-04-16 12:09:26.804 +0200"
        },
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
        }
    },
    "target": {
        "type": "EService",
        "supertypes": ["Service", "GCubeResource", "Resource"],
        "id": "2b99a46d-1111-4a17-bd2f-33450b109299",
        "metadata": {
            "createdBy": "luca.frosini",
            "creationTime": "2026-04-16 12:09:26.814 +0200",
            "lastUpdateBy": "luca.frosini",
            "lastUpdateTime": "2026-04-16 12:09:26.814 +0200"
        },
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
        }
    }
}
```

#### ConsistsOf Instance

```json
{
    "type": "IsIdentifiedBy",
    "supertypes": ["ConsistsOf"],
    "id": "641c1a36-da8d-4313-94f0-867453185d65",
    "metadata": {
        "createdBy": "luca.frosini",
        "creationTime": "2026-04-16 12:09:26.737 +0200",
        "lastUpdateBy": "luca.frosini",
        "lastUpdateTime": "2026-04-16 12:09:26.737 +0200"
    },
    "contexts": {
        "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
    },
    "propagationConstraint": {
        "type": "PropagationConstraint",
        "add": "propagate",
        "remove": "cascade"
    },
    "source": {
        "type": "EService",
        "supertypes": ["Service", "GCubeResource", "Resource"],
        "id": "1a50a46d-2210-4a17-bd2f-22450b109264",
        "metadata": {
            "createdBy": "luca.frosini",
            "creationTime": "2026-04-16 12:09:26.804 +0200",
            "lastUpdateBy": "luca.frosini",
            "lastUpdateTime": "2026-04-16 12:09:26.804 +0200"
        },
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
        }
    },
    "target": {
        "type": "SoftwareFacet",
        "supertypes": ["Facet"],
        "id": "cb6795ee-b729-47a7-ac9d-b805ae1df4d2",
        "metadata": {
            "createdBy": "luca.frosini",
            "creationTime": "2026-04-16 12:09:26.732 +0200",
            "lastUpdateBy": "luca.frosini",
            "lastUpdateTime": "2026-04-16 12:09:26.732 +0200"
        },
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube"
        }
    }
}
```

## Query Templates Representation

The Resource Registry query engine natively supports **JSON Query-By-Example (JSON-QBE)**, adapting the classical QBE paradigm[^zloof-1975] for document graphs. This paradigm allows clients to retrieve data by providing a JSON fragment that acts as a structural blueprint of the desired result, rather than writing strings of SQL-like query language. 

**Query Templates** elevate this concept by allowing administrators to define pre-packaged, parameterized JSON-QBE payloads that clients can execute simply by name, without needing to know the underlying structural complexity. The exact, case-sensitive `name` is the template's stable public identity and the sole routing key in every API endpoint, following the same identity model as Types. The `id` UUID remains visible in the representation and participates in creation and update consistency checks, but it is never an addressable public identity.

### Example: Query Template Definition

```json
{
    "type": "QueryTemplate",
    "id": "61454553-5b0c-4e39-b848-b0fe4be4d9da",
    "metadata": {
        "createdBy": "luca.frosini",
        "creationTime": "2025-05-30 19:29:13.020 +0200",
        "lastUpdateBy": "luca.frosini",
        "lastUpdateTime": "2025-05-30 19:29:20.241 +0200"
    },
    "name": "get-all-eservice-with-state",
    "description": "Returns all EService instances with the provided state.",
    "version": "1.0.0",
    "changelog": { "1.0.0": "First Version" },
    "template": {
        "type": "EService",
        "consistsOf": [
            {
                "type": "ConsistsOf",
                "target": {
                    "type": "StateFacet",
                    "value": "$state"
                }
            }
        ]
    },
    "templateVariables": {
        "$state": {
            "type": "TemplateVariable",
            "name": "$state",
            "description": "The state of the EService, e.g. down, ready.",
            "propertyType": "String",
            "defaultValue": "down",
            "required": false,
            "regexp": ""
        }
    }
}
```

#### Field Explanations

```eval_rst
- ``template``: A JSON-by-Example payload representing the structural query. In this case, it looks for any **EService** instance that has a **ConsistsOf** instance pointing to a **StateFacet** instance. The ``value`` uses a parameter ``$state``.
- ``name``: Lowercase words separated by hyphens, matching ``^[a-z][a-z0-9]*(-[a-z0-9]+)*$`` (see [Naming Conventions](12-is-architectural-decisions.md#naming-conventions)); a create or update with any other name returns ``400 Bad Request``. The exact, case-sensitive stable public identity used to create, read, execute, update, and delete this Query Template.
- ``id``: The representation UUID. It may be supplied on creation or generated by the server and may be repeated later as a consistency assertion, but it is never used for routing.
- ``version`` & ``changelog``: Mandatory. They use the format and semantics of the Type schema fields of the same name (see [Type Schema fields](05-data-representation-contexts-schemas.md)): ``version`` matches ``^[1-9][0-9]*\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$`` and is ordered numerically component by component, and ``changelog`` maps each version to a non-empty description of its change. As for a Type, ``version`` must be the highest version listed in ``changelog``: a new version is therefore always submitted together with its own ``changelog`` entry. The map is the complete history: an update repeats every persisted entry unchanged and adds the new ones. The version decides the outcome of a write, as defined by the [Versioned Upsert Semantics](10-api-query-templates-and-access.md#operational-notes).
- ``templateVariables``: A dictionary defining all bindable parameters (e.g., ``$state``) used by the template. It is optional: when it is omitted or ``null``, the Query Template has no variables and the field is returned as ``{}``. Its complete contract follows.
```

#### `TemplateVariable` Contract

```eval_rst
Each map key and its nested ``name`` must be identical and match ``^\$[a-z][a-zA-Z0-9]{0,63}$``: a ``$`` followed by a lowerCamelCase name, with no underscore, which is reserved as the operator prefix. The canonical object fields are:
```

| Field | Required | Contract |
|---|---:|---|
| `name` | Yes | Exact duplicate of the containing map key. |
| `description` | Yes | Human-readable string. |
| `propertyType` | Yes | One of `String`, `Boolean`, `Integer`, `Double`, `Float`, or `Date`. |
| `defaultValue` | Yes | Non-null value valid for `propertyType` and every declared constraint. It is used for registration-time validation and, when `required=false`, as the runtime fallback. It cannot be a string starting with `$`. |
| `required` | No | Boolean, default `false`. When `true`, the execution payload must supply the variable; its default remains registration-only. |
| `min` | No | Inclusive numeric lower bound for numeric variables. An integer, also for `Double` and `Float` variables. |
| `max` | No | Inclusive numeric upper bound for numeric variables; for `String`, maximum length in UTF-16 code units. An integer in both cases. |
| `regexp` | No | Regular expression for `String`, satisfied when the pattern matches any part of the value; defaults to the empty expression (no additional pattern constraint). |

A returned variable always carries `type` (`TemplateVariable`), `required`, and `regexp`, with their default values when the submitted definition omits them; a `type` or `supertypes` submitted on a variable is accepted.

Values use strict native JSON types and are never coerced. `String` and `Date` use JSON strings, `Boolean` uses a JSON boolean, and numeric types use JSON numbers; a numeric-looking string is invalid. `Integer` is limited to the signed 32-bit range, floating-point values must be finite, and `Date` uses `yyyy-MM-dd HH:mm:ss.SSS Z`. An explicit JSON `null` is invalid. `min` applies to numeric variables only, `max` to numeric variables and to `String` (as maximum length), and `regexp` to `String` only; any other combination (for example `min` on a `String` or `Date`, `regexp` on a number, or any of them on a `Boolean`), and `min > max`, is invalid. Without an explicit `max`, a `String` value is limited to 1024 UTF-16 code units. Embedded `\u0000` characters are invalid.

```eval_rst
Every string node in ``template`` that exactly equals a declared variable name is replaced recursively with the variable's complete JSON value node. Partial interpolation such as ``"prefix-$state"`` is not supported, inserted values are not scanned again, and object keys are not substitution targets. Every string node that starts with ``$`` is a variable reference, so a ``template`` cannot contain a literal string value starting with ``$``. Every variable reference must be declared, and every declaration must be referenced at least once; both conditions are validated when the Query Template is saved.
```

`PUT /query-templates/{name}` validates the declaration schema, names, types, constraints, references, and defaults, substitutes all defaults, and validates the resulting query before persisting it. At execution time, the body must be absent or a JSON object. An omitted `required=false` variable uses its default; omission of a `required=true` variable returns `400 Bad Request`. Supplied values are validated before substitution. Undeclared input keys are ignored and reported through `X-Warning`; declaration names and input keys are case-sensitive.

## Schema-Mixed Encrypted Fields

Because the IS-Model allows dynamic injection of fields (Schema-Mixed Mode), the Resource Registry provides a native **`Encrypted`** complex **`Property`** Type to handle sensitive dynamic data without relying on external vaults.

What a client observes is the following. An **`Encrypted`** value always travels encrypted with the symmetric key of the Context of the request: a client writes it and reads it with that key. The same value read from two different Contexts therefore arrives as two different ciphertexts, each one decipherable only with the key of its own Context, and both yield the same plain value. When an instance is added to another Context, that Context reads the value with its own key and no rewrite of the value is needed.

This is the result of a "double-encryption" architecture, which ensures both data-at-rest security and strict transport isolation:

1. **Write (Storage):** When a client submits an **`Encrypted`** field, they provide it encrypted with the symmetric key of their operational Context. The backend service receives it, decrypts it using that specific Context key, and then re-encrypts the raw value using the service's own universal service-managed encryption key before committing it to the database. The data on the database is therefore uniquely encrypted by the service.
2. **Read (Retrieval):** Upon retrieval, the backend decrypts the value using its universal service-managed encryption key, and dynamically re-encrypts it on the fly using the symmetric key of the specific Context requested by the client (or the Context tied to the caller's token).

**Multi-Context Sharing & Hierarchical Semantics:**
This architecture elegantly resolves the complexities of sharing secrets across tenants without cryptographic leakage:
- **Sharing into a new Context (Propagation):** Because the actual data at rest is encrypted solely with the universal service-managed encryption key, sharing a secret-bearing instance (e.g., propagating a **Facet** instance to a new Context) requires zero payload mutation. The backend simply adds the new Context to the instance's visibility. Propagation automatically grants the new Context access.
- **Hierarchical Reads:** If a manager performs a hierarchical read spanning multiple Contexts (`?hierarchical=true`), the system dynamically re-encrypts all returned secrets using the symmetric key of the Context bound to the caller's operational token, ensuring the caller can seamlessly decrypt the entire payload with their single key.

*(Note: How the upstream API Gateway, such as SmartGears, provisions or retrieves these symmetric Context keys to decrypt the incoming payload from the client is an infrastructure concern handled by the Gateway itself, and falls outside the Resource Registry API contract. Similarly, it is explicitly out of scope for this API guide to document the mechanisms for Context key issuance, cryptographic key rotation protocols, and the subsequent batch re-encryption of persisted data).*

> ⚠️ **Roadmap Warning:** While effective, this double-encryption mechanism represents an interim architectural pattern. In future iterations, secret management will be offloaded to a dedicated Vault service via reference pointers. Client integrators should encapsulate their encryption/decryption routines to minimize refactoring when this transition occurs.

## Operation Envelope Representation

When performing membership operations (such as `Add to Context` or `Remove from Current Context`), the system returns an **Operation Envelope**. This response describes the set of instances affected by the "avalanche effect". The density of this envelope depends on the `Prefer: return` HTTP header.

Both forms have the same structure: a flat array with one element for every affected instance, **Resource**, **Facet**, **IsRelatedTo**, and **ConsistsOf** alike. No element is nested inside another. Every **IsRelatedTo** and **ConsistsOf** element carries its `source` and `target`, so that a client can always tell which instances a relation connects. The two forms differ only in how much of each element they return. After a removal, the envelope lists the instances that left the Context and does not distinguish those that were also deleted because it was their last Context.

### Minimal Operation Envelope (`Prefer: return=minimal`)

A minimal operation envelope returns a flat array of instances, containing only their `type` and `id` (along with `source` and `target` for **Relation** instances, each reduced to `type` and `id`). It strictly omits `supertypes`, `metadata`, `contexts`, and any custom data fields, providing a highly optimized payload for bulk synchronization checks.


```json
[
    {
        "type": "EService",
        "id": "1a50a46d-2210-4a17-bd2f-22450b109264"
    },
    {
        "type": "IsIdentifiedBy",
        "id": "a8353fdf-9daf-4791-9ca7-02f068153ad0",
        "source": {
            "type": "EService",
            "id": "1a50a46d-2210-4a17-bd2f-22450b109264"
        },
        "target": {
            "type": "SoftwareFacet",
            "id": "e32275d1-1da1-4f47-8c7d-e34a23f4330a"
        }
    },
    {
        "type": "SoftwareFacet",
        "id": "e32275d1-1da1-4f47-8c7d-e34a23f4330a"
    },
    {
        "type": "ConsistsOf",
        "id": "6ca7ee85-19d1-4f5d-8fd7-f2f399a6beeb",
        "source": {
            "type": "EService",
            "id": "1a50a46d-2210-4a17-bd2f-22450b109264"
        },
        "target": {
            "type": "EventFacet",
            "id": "c3a8bf2a-788c-486f-949f-2f72be56b479"
        }
    },
    {
        "type": "EventFacet",
        "id": "c3a8bf2a-788c-486f-949f-2f72be56b479"
    },
    {
        "type": "ConsistsOf",
        "id": "78696361-0d73-4762-8b7b-c4bb203f52dc",
        "source": {
            "type": "EService",
            "id": "1a50a46d-2210-4a17-bd2f-22450b109264"
        },
        "target": {
            "type": "StateFacet",
            "id": "0037c9b9-e75b-4f62-9400-234b902823e2"
        }
    },
    {
        "type": "StateFacet",
        "id": "0037c9b9-e75b-4f62-9400-234b902823e2"
    },
    {
        "type": "ConsistsOf",
        "id": "c616b059-0677-4628-9f7d-f01a44b54069",
        "source": {
            "type": "EService",
            "id": "1a50a46d-2210-4a17-bd2f-22450b109264"
        },
        "target": {
            "type": "AccessPointFacet",
            "id": "52b45cb3-f691-4f30-952e-0bfa875ad7a8"
        }
    },
    {
        "type": "AccessPointFacet",
        "id": "52b45cb3-f691-4f30-952e-0bfa875ad7a8"
    }
]
```

### Full Representation Operation Envelope (`Prefer: return=representation`)

A full operation envelope returns the same flat array as the minimal one, with every element in its own complete representation: `type`, `supertypes`, `id`, the custom fields of a **Facet**, and the `propagationConstraint` and custom fields of a **IsRelatedTo** or **ConsistsOf** instance, whose `source` and `target` are endpoint references (see *Relation endpoint references* above).

`metadata` and `contexts` are added only when requested through `metadataPolicy` and `contextsPolicy`: `root` adds them to every element of the array, `all` also to the endpoint references of the relations. `membershipScope` independently selects whether each emitted `contexts` map contains memberships from the token Context subtree or, for authorized universal roles, from the global Context tree.

*(Example with `contextsPolicy=root`; the `metadata` blocks are omitted.)*

```json
[
    {
        "type": "EService",
        "supertypes": [ "Service", "GCubeResource", "Resource" ],
        "id": "1a50a46d-2210-4a17-bd2f-22450b109264",
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube",
            "a7bc333f-84df-498d-ae9d-748b358267b5": "/gcube/devsec"
        }
    },
    {
        "type": "IsIdentifiedBy",
        "supertypes": [ "ConsistsOf" ],
        "id": "a8353fdf-9daf-4791-9ca7-02f068153ad0",
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube",
            "a7bc333f-84df-498d-ae9d-748b358267b5": "/gcube/devsec"
        },
        "propagationConstraint": {
            "type": "PropagationConstraint",
            "add": "propagate",
            "remove": "cascade"
        },
        "source": {
            "type": "EService",
            "supertypes": [ "Service", "GCubeResource", "Resource" ],
            "id": "1a50a46d-2210-4a17-bd2f-22450b109264"
        },
        "target": {
            "type": "SoftwareFacet",
            "supertypes": [ "Facet" ],
            "id": "e32275d1-1da1-4f47-8c7d-e34a23f4330a"
        }
    },
    {
        "type": "SoftwareFacet",
        "supertypes": [ "Facet" ],
        "id": "e32275d1-1da1-4f47-8c7d-e34a23f4330a",
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube",
            "a7bc333f-84df-498d-ae9d-748b358267b5": "/gcube/devsec"
        },
        "name": "test",
        "version": "1.0.0",
        "group": "test"
    },
    {
        "type": "ConsistsOf",
        "id": "78696361-0d73-4762-8b7b-c4bb203f52dc",
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube",
            "a7bc333f-84df-498d-ae9d-748b358267b5": "/gcube/devsec"
        },
        "propagationConstraint": {
            "type": "PropagationConstraint",
            "add": "propagate",
            "remove": "cascade"
        },
        "source": {
            "type": "EService",
            "supertypes": [ "Service", "GCubeResource", "Resource" ],
            "id": "1a50a46d-2210-4a17-bd2f-22450b109264"
        },
        "target": {
            "type": "StateFacet",
            "supertypes": [ "Facet" ],
            "id": "0037c9b9-e75b-4f62-9400-234b902823e2"
        }
    },
    {
        "type": "StateFacet",
        "supertypes": [ "Facet" ],
        "id": "0037c9b9-e75b-4f62-9400-234b902823e2",
        "contexts": {
            "4eb849d5-efbb-4430-9eb6-1968123921b4": "/gcube",
            "a7bc333f-84df-498d-ae9d-748b358267b5": "/gcube/devsec"
        },
        "value": "ready"
    }
]
```

*(Note on `StateFacet` vs `EventFacet`: In the examples throughout this document, `StateFacet` uses a simple `value` field to represent a scalar state, whereas `EventFacet` uses an `event` field. This is an intentional schema difference: `EventFacet` is designed to be backed by the complex `Event` property schema (which can encompass the "Five Ws" tracking fields), while `StateFacet` represents a simplified enumeration).*
