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
depththerefore all expose the same live tree to every role. What remains restricted is the cemetery (IS-MandInfra-Monly), the Context History (seeRead History), and the identity fields ofmetadata, redacted for non-administrative roles. Context reads are not subject to theeffectiveStatebarrier: a caller whose token Context issuspendedorcreatedstill reads it (for instance throughGET /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/accessmirrors 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 withdepthwhen they need a rooted hierarchy. This global live view is intentional and emits noX-Result-Scopeheader. Thelifecycleparameter remains the independent lifecycle boundary: onlyIS-MandInfra-Mmay requestdeletedorall, while every other role is restricted tolive. 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
pathof a live Context; thepreviousPathsof a live Context; thepathof adeletedContext (cemetery). A live Context always wins over a deleted one with the same path. A path that matches only a deleted Context returns403 Forbidden; on a read that returns the token Context (Read Current), a path that matches nothing returns404 Not Found. A mutation whose token does not resolve to the Context it requires returns403 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, returns403 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 emitsX-Result-Scope: previous-pathplusX-Resolved-Context-Pathcontaining 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 HistorybyCtx-M, and parent-scope mutations; notList,Execute JSON Query, orReadby UUID), direct instance operations, and stored Query Template execution through both the management and/accesssurfaces. Query Template definition management remains global and does not resolve an operational Context. - Read shape and
depth:GET /contexts/{context-uuid}andGET /contexts/@currentalways return a flat JSON array with the requested Context first, followed by its descendants level by level: up todepthlevels, or all of them withdepth=-1. Withdepth=0(the default) the array contains only the requested Context. Every element is a complete Context representation whoseparentandchildrenare summaries, as for any Context.CreateandTracked Updatereturn 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 between403and404is 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/@currentapplies the same403vs404distinction asRead, 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
pathnor any retainedpreviousPathsentry.
403 Forbidden: the token's path resolves to a Context in the deleted state (cemetery), whatever the caller's role:@currentnever 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-MandInfra-Mcan 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-Mcan 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-Adminhas 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-MandInfra-Mare 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 returns409 Conflict. - Creation via
PUTvsPOST: The API usesPUTinstead ofPOSTfor 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 aPOSTrequest 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):PUTis strictly for Context creation. It requires definingid,name, andparent. Aparentthat does not exist returns404 Not Found; aparentthat is in the cemetery returns409 Conflict. The token is resolved first: a token whose path matches only a deleted Context returns403 Forbiddenbefore theparentis examined, so the409applies to a caller whose token resolves to a live Context, for exampleIS-Mwith a token for the root Context. The checks follow this order: role, token resolution (403), body validation (400),parent(404or409), token scope (403); a caller whose token is not the one required therefore still receives404or409for a missing or deletedparent. Thestatefield is system-managed upon creation (forced tocreated); suppliedstateandpreviousPathsfields are ignored and reported throughX-Warning. Historical paths can be introduced only through authenticatedPATCH. To strictly adhere to HTTPPUTidempotency, sending the exact same creation payload multiple times will return201 Createdthe first time and200 OK(no-op) on subsequent identical requests. However, if a client attempts to execute aPUTon an existing Context with a payload that is incongruent with the existing representation, the system will reject it with409 Conflict. The comparison covers the fields the caller can read: the additional fields of a Context take part in it only forIS-MandInfra-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 theTracked Update(PATCH) endpoint.PATCH /contexts/{context-uuid}semantics (Tracked Update): Uses theapplication/merge-patch+jsonmedia type (RFC 7396) to partially update the Context. The body must be a JSON object. It processes changes only tostate,name,parent, andpreviousPaths. The other fields follow the general system-field rule (see System Fields): the server-computed fieldsmetadata,path,effectiveState, andchildrenare dropped without warning whatever their value, so that a Context just read can be submitted back;typeandidare optional but, when present, must identify the Context addressed by the path (typemust beContext), otherwise400 Bad Request; any other top-level field is ignored and reported through anX-Warningheader.state, when present, must be the stringactiveorsuspended(createdanddeletedare system-managed).name, when present, must be a valid non-null Context name.parent, when present, must be an object containing a textual UUIDid; the unique Root may also receive JSON null as a no-op assertion of its existing parentless state. Settingparent: nullon any non-Root Context would create a second root and therefore returns409 Conflict; every accepted reparenting must also keep the hierarchy acyclic: moving a Context under itself or under one of its own descendants returns409 Conflict. A newparentthat does not exist returns404 Not Found, and one that is in the cemetery returns409 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 currentpathor one of itspreviousPathsreturns409 Conflict. A value equal to the Context's own currentpathreturns409 Conflicttoo: 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 successful200 OKno-op. The checks follow this order: role, token resolution (403), body validation (400), target (404), cemetery (409), newparent(404or409), token scope (403).- Live path ownership and reclamation: Across live Contexts, one absolute path string identifies at most one Context, either as its current
pathor as one of itspreviousPaths. 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 samename) returns409 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 retainedpreviousPathsset, 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 explicitpreviousPathsupdate never reclaims an existing identity; conflicting ownership is rejected with409 Conflict. - Reparenting and Renaming Constraints: Changing the
parentContext or thenameof 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 localstateis non-active(e.g.,suspendedorcreated). The check uses the persistedstate, not astatesubmitted in the samePATCH: anactiveContext is suspended first and moved or renamed with a second request. Attempting to move or rename a Context while its localstateisactivewill result in a409 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 ofIS-MandInfra-M. For example, moving/root/A/Xunder/root/Brequires a token for/root, as does moving it directly under/root; moving it under/root/A/Crequires a token for/root/A. The rule applies to every role allowed to update Contexts (IS-M,Infra-M,Ctx-M);IS-MandInfra-Mmay also move any Context from a token issued for the Root Context. AContext-Managertherefore moves Contexts only within the subtree whose token it holds.- Architectural Rationale: Why check the local
stateinstead of theeffectiveState? Consider a child Context whose local state isactive, but its parent issuspended. Due to inheritance, the child'seffectiveStateis naturallysuspended. If the system permitted the move based solely on theeffectiveState, an administrator could move this child under a new,activeparent. Because its local state was alreadyactive, 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 tosuspendedbefore the move guarantees that the Context remains safely offline during and after the migration, requiring a deliberate secondary action to reactivate it.
- Architectural Rationale: Why check the local
-
Read History:
GET /contexts/{context-uuid}/historyis a pure read-only endpoint that returns a flat JSON array ofEventobjects (sorted in reverse-chronological order, newest first). The path identifier must be a valid UUID; malformed values return400 Bad Request. Query parameters are unsupported and ignored with anX-Warningheader; in particular,lifecyclecannot select cemetery routing. Lifecycle visibility is derived exclusively from role:IS-M/Infra-Mcan read live or deleted Context history, whileIS-AdminandCtx-Mcan read live Context history only. This provides a complete audit trail of the Context's lifecycle. EachEventexplicitly captures the transition details:who: The principal who triggered the change. It is returned in full toIS-M,Infra-M, andIS-Admin, and as"[REDACTED]"toCtx-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 namedwhoas sensitive.what: The transition value (e.g.,created -> active).where: The specific field modified (e.g.,stateorname).when: The timestamp of the occurrence.how: The triggering mechanism or API method. It isPATCHon the Context the request addressed, andPATCH (cascade from {context-uuid})on every descendant the change propagated to, where{context-uuid}identifies the Context whosePATCHcaused the propagation (an ancestor, not necessarily the immediate parent). When a Context loses one of itspreviousPathsbecause another Context took that path as its current one, the event on the losing Context haswhat: removed {path}andhow: PUT (path reclaimed by {context-uuid})orhow: PATCH (path reclaimed by {context-uuid}), according to the operation that established the path on the reclaiming Context.
Changes to
previousPathsare 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 usewhere: previousPathsand do not replace the separatename,parent, orpathevents. Within onePATCH, the events of an explicitpreviousPathsreplacement are recorded before thestate,name, andparentevents 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 anEventinto 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):
DELETEpermanently removes the Context from active listings and transitions it to adeletedstate 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 return400 Bad Request. The target must exist in the live topology, so deleting an unknown UUID returns404 Not Found, while deleting a Context that is already in the cemetery is a no-op that returns204 No ContentforIS-MandInfra-M: the requested state is already in place, exactly as a repeated identicalPUTis a200 OKno-op, so they can repeat aDELETEsafely. Any other role receives403 Forbidden, because the cemetery is not visible to it. A non-Root target requires an administrative token for its current immediate parent;IS-MandInfra-Mmay also use a token issued for the Root Context. Deleting the Root is restricted toIS-MandInfra-Mand requires a token for the Root itself. Unsupported query parameters are ignored and reported throughX-Warning. Deletion fails with409 Conflictif 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 thelifecycleparameter:live(default),deleted,all. Access to cemetery-inclusive modes (deleted,all) is restricted toIS-M / Infra-M; for all other roles, requesting these modes returns403 Forbidden. - Execute JSON Query (QBE):
QUERY /contextsexposes the powerful JSON Query-By-Example engine specifically for the Contexts collection. Instead of polluting theGET /contextsendpoint 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 involvingmetadata.createdByormetadata.lastUpdateByfollow the shared caller-visible query rule:IS-M,Infra-M, andIS-Admincompare stored identities, while every other role compares the visible"[REDACTED]"value. The endpoint otherwise shares the exact same semantics, operators, and_emitprojection capabilities as the instance query engine.- Lifecycle visibility contract:
lifecycle=livereturns only active-topology contexts (default);lifecycle=deletedreturns only cemetery contexts;lifecycle=allreturns the union. The server applies role checks before query evaluation.
- Lifecycle visibility contract:
- Non-
activeContext behavior: When a Context iscreatedorsuspended,IS-M,Infra-M, andIS-Admincan perform instance operations regardless of that state. AdeletedContext allows no instance operation for any role (403 Forbidden; see the matrix below).Ctx-Mcan do so inside its managed subtree when its credentials address the operational Context. Roles with lesser administrative scope (IS-Editor,IS-Contrib, andIS-Member) are strictly blocked from performing any instance operation in non-activeContexts and receive403 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-Mcreating 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 increated), 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 thecreatedContext). Once the local manager has prepared everything, the CEO officially declares the division open (PATCH /contexts/childtoactive). 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
deletedstate, it cannot be resurrected or updated: aPATCHon it returns409 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 anAdd to Context, and its UUID stays occupied: aPUTthat would create a Context with that UUID returns409 Conflicttoo. A repeatedDELETEis the only mutation that succeeds, as a204 No Contentno-op forIS-MandInfra-M(403 Forbiddenfor any other role). However, because it physically vacates the active topology, an administrator can create a brand new Context using the samenameandparent(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
createdinheritance: If a child Context has been explicitly advanced toactiveorsuspended(by a caller holding its parent's token), it is no longer a blank slate. Therefore, its parent'screatedstate acts purely as an operational lock, yielding an effectivesuspendedstate 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,DELETEfor Contexts) based on the parent's state.IS-M,Infra-M, and an authorizedCtx-Mmay build and configure an entire nested topology while an ancestor Context is still in acreatedorsuspendedstate. 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
effectiveStateis 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 toDisabledwill be rejected by the system. (Note ondeletedcontexts: While all operations on graph instances issued with a token of adeletedContext are universally blocked for everyone (403), and anAdd to Contextthat names adeletedContext as its destination returns409 Conflict, global administrators can still inspect deleted Contexts through Context collection/read APIs using thelifecyclevisibility model and viaGET /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
PUTvsPOST: The API usesPUTinstead ofPOSTfor 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 aPOSTrequest to the base collection endpoint would be an architectural anti-pattern. Note that thenamemust 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
idownership during creation: Theidfield inside the Type JSON represents its internal global UUID. The REST endpoint URL strictly uses the readabletype-name, never the UUID. If a client includes anidfield 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, produces409 Conflict, following the registry-wide UUID ownership rule. If the client omits theid, the backend automatically generates a new random UUID for the Type. PUT /types/{type-name}semantics (idempotent creation):PUTconsumes onlyapplication/jsonand is accepted for the creation of a new Type with the provided identifier. The body must be a complete Type definition whosenameexactly matches the path. To strictly adhere to HTTPPUTidempotency, 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 fieldsidandmetadataand the server-assignedlockedflag. A Type definition is a closed representation: a member it does not define returns400 Bad Request(the removednotnullconstraint included; a submittedmetadataorlockedis the only exception, dropped without warning because the server assigns them), as do a cardinality with a negativeminormax,max: 0, ormingreater thanmax, and a propertyminormaxthat is not a number, or a decimal one on a property whosepropertyTypeis neitherDoublenorFloat.ResourceSchemahas nopropertiesmember; supplying one is an invalid representation and returns400 Bad Request. An identical definition returns200 OK(no-op); any structural difference returns409 Conflict, because online Type updates are forbidden. A submittedidis a consistency assertion: when it differs from the persisted UUID of the Type, the request returns409 Conflict. A Type markedlockedis served read-only: any submission that would change it returns409 Conflict. A new definition also returns409 Conflictwhen 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 (simultaneousabstract: trueandfinal: trueincluded, 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]*$returns400 Bad Request. Both successful outcomes return the persisted Type representation and identify its canonical name-addressed URI throughContent-Location. A newly created Type is immediately readable, and its instances carry the correspondingsupertypeslinearization. (Contrast with Query Template definitions: while a Query Template is updated online by submitting a higherversion, 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
Listendpoint: There is intentionally no standaloneListendpoint (e.g.,GET /types) for schema definitions. Clients are expected to discover available Types hierarchically by invoking theReadendpoint on any known Type and using thedepthparameter. For example, a client can retrieve the entire Resource schema tree by callingGET /types/Resource?depth=-1, or dynamically explore a specific sub-branch by callingGET /types/Service?depth=-1or 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=0returns only that Type, a positivedepth=Nincludes descendants through exactly N inheritance levels, anddepth=-1includes all descendants; invalid values return400 Bad Request. Multiple-inheritance paths do not duplicate a descendant.metadataPolicy=none|root|allcontrols metadata across the flat result (rootandallare equivalent for this endpoint, as all returned types are peer definitions; onlynoneomits it), and non-global roles receive masked metadata identities. Successful reads expose the canonical name-addressed URI throughContent-Locationand, throughLink: 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 /typesrequires a JSON object targeting one ofResourceSchema,FacetSchema,IsRelatedToSchema,ConsistsOfSchema, orPropertySchema; every other category is rejected with400 Bad Requestbefore database execution, even when its filters would match no rows. Thedepthparameter controls descendant expansion for plain-stringname,source, andtargetmatches:0is exact, a positive value includes descendants through that many levels, and-1(default) is unbounded. Full Type results honormetadataPolicyand redactmetadata.createdByandmetadata.lastUpdateByfor non-privileged roles; Relation Type definitions exposesourceandtargetas canonical Type-name strings. An_emitquery instead returns only its flat aliases while preserving each selected value's native JSON type, ignores an explicitly suppliedmetadataPolicywithX-Warning, and still redacts aliases sourced from sensitive metadata fields. Pagination applies to both full and projected rows; invalid query parameter values return400 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 throughX-Warning. The server MUST reject the request with409 Conflictwhen 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
templateof 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 atypekey anywhere in thetemplate, either written literally or as thedefaultValueof the template variable placed there, while an_emitalias 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
PropertyType is rejected if another Type definition still uses it as a direct or collection-valued complexpropertyType.
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.