Skip to content

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 (POST /contexts/query fallback) 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 No specific target Context is required: every role reads every live Context with any token (see Context visibility below)
Read History IS-M and Infra-M: any Context, live or deleted, with any token. IS-Admin: any live Context, with any token. Ctx-M: a token for the target Context or any ancestor of it
Read Current The current token Context (token-bound endpoint semantics)
Create Parent Context of the target Context, for every role. (Root exception: only IS-M and Infra-M may create the Root, using a token issued for that Root Context itself.)
Tracked Update, Delete Parent Context of the target Context. A Tracked Update that changes parent requires instead the closest common ancestor of the current and the new parent (see Reparenting and Renaming Constraints). IS-M and Infra-M may also use a token issued for the Root Context, whatever the position of the target. (Root exception: only IS-M and Infra-M may update or delete the Root, using a token issued for that 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 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, 404 Not Found, 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

  • Context visibility: The tree of live Contexts is visible to every Resource Registry role, whatever the Context of its token. The token Context decides where a caller may operate, never which live Contexts it may see. List, query, point read, and point read with depth therefore all expose the same live tree to every role. What remains restricted is the cemetery (IS-M and Infra-M only), the Context History (see Read History), and the identity fields of metadata, redacted for non-administrative roles. Context reads are not subject to the effectiveState barrier: a caller whose token Context is suspended or created still reads it (for instance through GET /contexts/@current) and can see why its instance operations are refused; the barrier applies to instance operations only.
  • Global live collection semantics: GET /contexts, QUERY /contexts, and their /access mirrors operate on the complete live Context collection for every Resource Registry role. The token carries the role used to authorize the endpoint but its Context does not bound collection visibility. Results are flat and paginated rather than a recursive tree rooted at the token Context; clients use point reads with depth when they need a rooted hierarchy. This global live view is intentional and emits no X-Result-Scope header. The lifecycle parameter remains the independent lifecycle boundary: only IS-M and Infra-M may request deleted or all, while every other role is restricted to live. Metadata visibility continues to follow the caller's role.
  • Token path resolution: Every operation that derives an operational Context from the caller's token matches the token path, in this order, against: the current path of a live Context; the previousPaths of a live Context; the path of a deleted Context (cemetery). A live Context always wins over a deleted one with the same path. A path that matches only a deleted Context returns 403 Forbidden; on a read that returns the token Context (Read Current), a path that matches nothing returns 404 Not Found. A mutation whose token does not resolve to the Context it requires returns 403 Forbidden, whatever the reason: what is missing is the authority over that Context, not the target of the request. The same holds for every direct instance operation, read or write, and for stored Query Template execution, whatever the caller's role: a token path that matches only a deleted Context, or no Context at all, returns 403 Forbidden. The creation of the Root is the one operation whose token names a Context that does not exist yet: it is not affected by a deleted Root with the same path. 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 the Context operations that use the token Context (Read Current, Read History by Ctx-M, and parent-scope mutations; not List, Execute JSON Query, or Read by UUID), direct instance operations, and stored Query Template execution through both the management and /access surfaces. Query Template definition management remains global and does not resolve an operational Context.
  • Read shape and depth: GET /contexts/{context-uuid} and GET /contexts/@current always return a flat JSON array with the requested Context first, followed by its descendants level by level: up to depth levels, or all of them with depth=-1. With depth=0 (the default) the array contains only the requested Context. Every element is a complete Context representation whose parent and children are summaries, as for any Context. Create and Tracked Update return instead the created or updated Context as a single JSON object, not an array.
  • 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 only when the Context is in the deleted state (cemetery) and the caller does not hold a manager role (IS-M / Infra-M). A live Context is readable by every role (see Context visibility).
  • 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), whatever the caller's role: @current never designates a deleted Context. 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.
  • Context lifecycle permissions: IS-M and Infra-M can manage every Context: besides the token of the parent, they may update or delete any Context from a token issued for the Root Context, so that a Context stays manageable when no token can be issued for its parent (for example, a VO that exists only in the Information System). Creation always requires a token for the immediate parent. Ctx-M can manage a target only from its immediate parent Context (or, for a reparenting, from the closest common ancestor of the current and the new parent) and receives filtered metadata; it can never create, update, or delete the Context represented by its own token. IS-Admin has universal visibility across live Contexts but cannot access the cemetery or create, update, or delete Contexts.
  • The Root Context Exception: The Context topology is either empty or a strict single-root tree. Only its unique Root Context may have parent: null. IS-M and Infra-M are the only roles that may create, update, or delete the Root, using a token issued for that exact Root Context. When the topology is empty, either role may create the Root. Once a Root exists, any attempt to create another parentless Context returns 409 Conflict.
  • 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. A parent that does not exist returns 404 Not Found; a parent that is in the cemetery returns 409 Conflict. The token is resolved first: a token whose path matches only a deleted Context returns 403 Forbidden before the parent is examined, so the 409 applies to a caller whose token resolves to a live Context, for example IS-M with a token for the root Context. The checks follow this order: role, token resolution (403), body validation (400), parent (404 or 409), token scope (403); a caller whose token is not the one required therefore still receives 404 or 409 for a missing or deleted 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. The comparison covers the fields the caller can read: the additional fields of a Context take part in it only for IS-M and Infra-M; for any other role the stored ones are ignored and left untouched. Creating a Context whose current path matches another Context's retained previous path reclaims it, as every newly established current path does (see Live path ownership and reclamation below): the retained value is removed atomically and the removal is recorded 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) to partially update the Context. The body must be a JSON object. It processes changes only to state, name, parent, and previousPaths. The other fields follow the general system-field rule (see System Fields): the server-computed fields metadata, path, effectiveState, and children are dropped without warning whatever their value, so that a Context just read can be submitted back; type and id are optional but, when present, must identify the Context addressed by the path (type must be Context), otherwise 400 Bad Request; any 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 an object containing a textual UUID id; the unique Root may also receive JSON null as a no-op assertion of its existing parentless state. Setting parent: null on any non-Root Context would create a second root and therefore returns 409 Conflict; every accepted reparenting must also keep the hierarchy acyclic: moving a Context under itself or under one of its own descendants returns 409 Conflict. A new parent that does not exist returns 404 Not Found, and one that is in the cemetery returns 409 Conflict, as on creation. 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 within the submitted array are normalized, and administrators may add historical paths for remediation. Reasserting a value already retained by the same Context is a no-op, while adding a value owned by another live Context as either its current path or one of its previousPaths returns 409 Conflict. A value equal to the Context's own current path returns 409 Conflict too: a path is either the current path or a previous path of a Context, never both. 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. The checks follow this order: role, token resolution (403), body validation (400), target (404), cemetery (409), new parent (404 or 409), token scope (403).
  • Live path ownership and reclamation: Across live Contexts, one absolute path string identifies at most one Context, either as its current path or as one of its previousPaths. Two live Contexts can never share a current path: a creation, rename, or reparent that would give a Context the current path of another live Context (for example, two siblings with the same name) returns 409 Conflict. A newly established current path is authoritative over retained former paths: Context creation, rename, reparent, and descendant path recomputation atomically remove that value from every retained previousPaths set, including the same Context when it returns to one of its former paths. Each automatic removal records a History event on the affected Context. An explicit previousPaths update never reclaims an existing identity; conflicting ownership is rejected with 409 Conflict.
  • 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). The check uses the persisted state, not a state submitted in the same PATCH: an active Context is suspended first and moved or renamed with a second request. Attempting to move or rename a Context while its local state is active will result in a 409 Conflict.
  • Reparenting authorization: A move touches two positions in the tree, so the token must be issued for the Context that contains both: the closest common ancestor of the current parent and the new parent. When the new parent lies below the current parent, that ancestor is the current parent itself, which is the ordinary parent-token rule. A token for any other Context, a higher ancestor included, returns 403 Forbidden, except the Root token of IS-M and Infra-M. For example, moving /root/A/X under /root/B requires a token for /root, as does moving it directly under /root; moving it under /root/A/C requires a token for /root/A. The rule applies to every role allowed to update Contexts (IS-M, Infra-M, Ctx-M); IS-M and Infra-M may also move any Context from a token issued for the Root Context. A Context-Manager therefore moves Contexts only within the subtree whose token it holds.
    • 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 and 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. It is returned in full to IS-M, Infra-M, and IS-Admin, and as "[REDACTED]" to Ctx-M, consistently with metadata identity visibility. This is an explicit sensitivity rule for the system-produced Context History field; it does not classify arbitrary application fields named who as sensitive.
    • 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. It is PATCH on the Context the request addressed, and PATCH (cascade from {context-uuid}) on every descendant the change propagated to, where {context-uuid} identifies the Context whose PATCH caused the propagation (an ancestor, not necessarily the immediate parent). When a Context loses one of its previousPaths because another Context took that path as its current one, the event on the losing Context has what: removed {path} and how: PUT (path reclaimed by {context-uuid}) or how: PATCH (path reclaimed by {context-uuid}), according to the operation that established the path on the reclaiming Context.

    Changes to previousPaths are recorded independently: direct and cascaded path changes add the outgoing path, explicit replacements record each addition and removal, and a path reclaimed through a creation, rename, reparent, or descendant path recomputation records the removed path and the UUID of the reclaiming Context. These events use where: previousPaths and do not replace the separate name, parent, or path events. Within one PATCH, the events of an explicit previousPaths replacement are recorded before the state, name, and parent events of the same request.

    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: PATCH (cascade from {context-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 UUID returns 404 Not Found, while deleting a Context that is already in the cemetery is a no-op that returns 204 No Content for IS-M and Infra-M: the requested state is already in place, exactly as a repeated identical PUT is a 200 OK no-op, so they can repeat a DELETE safely. Any other role receives 403 Forbidden, because the cemetery is not visible to it. A non-Root target requires an administrative token for its current immediate parent; IS-M and Infra-M may also use a token issued for the Root Context. Deleting the Root is restricted to IS-M and Infra-M and requires a token for the Root itself. 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. Deleting the empty Root returns the topology to the valid empty bootstrap state; the next Context creation must establish one new unique Root. 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). Predicates involving metadata.createdBy or metadata.lastUpdateBy follow the shared caller-visible query rule: IS-M, Infra-M, and IS-Admin compare stored identities, while every other role compares the visible "[REDACTED]" value. The endpoint otherwise 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 created or suspended, IS-M, Infra-M, and IS-Admin can perform instance operations regardless of that state. A deleted Context allows no instance operation for any role (403 Forbidden; see the matrix below). Ctx-M can do so inside its managed subtree when its credentials address the operational Context. Roles with lesser administrative scope (IS-Editor, IS-Contrib, and IS-Member) are strictly blocked from performing any instance operation in non-active Contexts and receive 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.
---
config:
  theme: neutral
  flowchart:
    edgeLabelBackground: "#ffffff"
---
stateDiagram-v2
    [*] --> created : Create (PUT)
    created --> active : Tracked Update (PATCH)
    created --> suspended : 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 Transitions of a non-Root Context require a token for its parent Context and an authorized Context-lifecycle role (IS-M, Infra-M, or Ctx-M); IS-M and Infra-M may use instead a token for the Root Context. Root creation and every Root transition require a Root token and are restricted to IS-M and Infra-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).
created PATCH suspended Configures the Context as suspended before it was ever opened; it stays closed to IS-Editor, IS-Contrib, and 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: a PATCH on it returns 409 Conflict, whatever the caller's role, because the Context exists but is in a terminal state. The same holds when it is named as the destination of an Add to Context, and its UUID stays occupied: a PUT that would create a Context with that UUID returns 409 Conflict too. A repeated DELETE is the only mutation that succeeds, as a 204 No Content no-op for IS-M and Infra-M (403 Forbidden for any other role). 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. 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 explicitly advanced to active or suspended (by a caller holding its parent's token), 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. IS-M, Infra-M, and an authorized Ctx-M may build and configure an entire nested topology while an ancestor Context is still in a created or suspended state. This enables Offline Restructuring while the effective-state algorithm keeps operational users out.

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 atomic operation: the effectiveState of every descendant whose local state was active becomes suspended, so no descendant can be read or written as active once the lock is in place.

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 ✅ Enabled ❌ Disabled (403) ❌ Disabled (403) ❌ Disabled (403)
active ✅ Enabled ✅ Enabled ✅ Enabled ✅ Enabled ✅ Enabled ✅ Enabled
suspended ✅ Enabled ✅ Enabled ✅ Enabled ❌ Disabled (403) ❌ Disabled (403) ❌ Disabled (403)
deleted ❌ Disabled (403) ❌ Disabled (403) ❌ Disabled (403) ❌ Disabled (403) ❌ Disabled (403) ❌ Disabled (403)

(Note: The effectiveState barrier applies equally to reads and writes for IS-Editor, IS-Contrib, and IS-Member. Administrative roles are not restricted 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 issued with a token of a deleted Context are universally blocked for everyone (403), and an Add to Context that names a deleted Context as its destination returns 409 Conflict, 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 (POST /types/query fallback) limit, offset, depth, metadataPolicy ✅ ✅ ✅ ✅ ✅ ✅
Create PUT /types/{type-name} metadataPolicy ✅ ❌ ❌ ❌ ❌ ❌
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 400 Bad Request, 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. A UUID that is already in use, by another Type definition or by any other registry element, produces 409 Conflict, 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 category-specific structural representation — including the discriminator, changelog, supported property declarations and all their constraints, Resource relationship declarations, and Relation endpoints — while excluding only the identity/audit fields id and metadata and the server-assigned locked flag. A Type definition is a closed representation: a member it does not define returns 400 Bad Request (the removed notnull constraint included; a submitted metadata or locked is the only exception, dropped without warning because the server assigns them), as do a cardinality with a negative min or max, max: 0, or min greater than max, and a property min or max that is not a number, or a decimal one on a property whose propertyType is neither Double nor Float. ResourceSchema has no properties member; supplying one is an invalid representation and returns 400 Bad Request. An identical definition returns 200 OK (no-op); any structural difference returns 409 Conflict, because online Type updates are forbidden. A submitted id is a consistency assertion: when it differs from the persisted UUID of the Type, the request returns 409 Conflict. 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, an inheritance cycle, inconsistent multiple inheritance, or contradictory inherited cardinalities. Malformed JSON, an invalid Type representation (simultaneous abstract: true and final: true included, since the definition contradicts itself whatever the registry holds), path/body name mismatch, or a Type name that does not match ^[A-Z][a-zA-Z0-9]*$ 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 is immediately readable, and its instances carry the corresponding supertypes linearization. (Contrast with Query Template definitions: while a Query Template is updated online by submitting a higher version, Types are structural graph contracts and strictly forbid online updates).
  • 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 return 400 Bad Request. 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, through Link: rel=supertype, each declared parent that is served as a Type definition (see HATEOAS Navigation). A read never returns 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 parameter values return 400 Bad Request (see Parameter Value Handling).
  • 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 referenced in the template of any Query Template (the Query Template is deleted or updated first), so that no stored Query Template ever names a Type that no longer exists; a Type is referenced when its name is the value of a type key anywhere in the template, either written literally or as the defaultValue of the template variable placed there, while an _emit alias is not a reference;
    • 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 source or 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, every schema constraint has the Resource Type that owns the definition as one of its endpoints: as its source or, for an inbound IsRelatedTo declaration, as its target. 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 IsRelatedTo or ConsistsOf Type definition, or is referenced by another Resource Type definition.