Skip to main content
Version: v5.7.0

Node (project, environment, asset, entity)

Included from Basic tierassets

JSON:API types: project, environment, asset, entity — the resource type is the node's kind. This page covers the whole node family.

The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.

Nodes form the target tree every other resource hangs off. A node is addressed by its path — alternating collection/code pairs: /api/projects/<code>[/environments/<code>][/assets/<code>][/entities/<code>…]. The authorisation path is the same string with /api stripped (e.g. /projects/demo/environments/dev), and it is what RBAC rules and the node filters elsewhere address. Codes are stored stripped and lowercased (^[a-z0-9_]+$, max 50).

Endpoints​

VerbPathBehaviour
GET/api/projectsProject index (read-filtered per node).
POST/api/projectsCreate a project — update on /projects and on the new node's own path, 404-on-deny.
GET/api/projects/{project_code}Project show.
PATCH/PUT/api/projects/{project_code}Update the bare project — same permit list and authorisation as the node-path update below.
DELETE/api/projects/{project_code}Soft delete (the node-path catch-all cannot address a bare project).
GET/api/projects/{project_code}/{node_path...}The node-path dispatch: an even pair count shows one node, an odd count indexes a child collection (filter[archived_eq] supported); a recognised trailing resource segment (changes, workflows, triggers, maintenance, bookmarks, …) dispatches to that resource instead. Shows apply the default include list project, bookmarks, template, template_version, children, children.template, children.template_version.
POST…/environments | …/assets | …/entitiesCreate a child node under the resolved parent. A kind the parent cannot hold is the named 422 (Cannot create an environment under an entity — an entity parents entities only).
PATCH/PUT<node path>Update (name, description, archived, template_version — the last ignored on non-assets). Authorised as update on the node's own path.
DELETE<node path>Soft delete the subtree.
POST<node path>/restoreUndo a soft delete; answers the restored node.
GET/POST<node path>/destroyGET previews the per-type destroy manifest; POST is the irreversible cascade, valid only on a soft-deleted node.
GET<node path>/export[?with_history=true]Streams the credential-free zip archive of the subtree.

Attributes​

Common to every kind:

AttributeTypeMeaning
codestringThe path segment. Immutable (not in the update permit list).
namestringDisplay name.
descriptionstring, nullableNull when never set.
archivedbooleanArchive flag (update verb).
created_bystringUsername of the creating user.
maintenanceobject, omitted when absentThe maintenance badge, present while an active window covers the node (its own, or an ancestor's — the innermost wins) or a scheduled one covering it starts within the server's badge horizon (api.maintenance_badge_horizon_minutes, default 60): {reason, until (nullable), window_path, scheduled, starts_at}. until falls back to a scheduled window's hard end; scheduled is true and starts_at set only on the imminent form. window_path names the quiesced node, so an inherited badge points at the window itself. Advisory: a failed read never fails serialization.

Per-kind additions:

KindExtra attributeMeaning
project—No kind-specific attribute. The former project_type (Standard/Enterprise) is retired: every project holds environments and assets, and its environments hold assets. project_type is still accepted on create and update and ignored, so older clients keep working; it no longer appears in responses.
assettask_activeWhether a node background task is currently active on the node.
assetmintmodel_validShow-only (the shown node, never lists or includes): whether a valid concretised MintModel is stored for the pinned template version's commit.
assetmintmodel_invalid_reasonShow-only; null when valid, else no_mintmodel, not_concretised, generation_failed or template_version_changed (the upstream properties_changed recheck needs a properties converge this read tier does not compute).
asseterb_file_contentShow-only; null when the pinned template version has no MintModel, else the MintModel/templates/assets/<template code>.json.erb document at the version's pinned commit (the GUI's ERB Template tab). A failed git read serves the error message as the value — the show never fails on it.
environment—No kind-specific attribute.
entity—No kind-specific attribute.

An entity is the minimal node: no template and no automation — no actions, changes, MintModel, background tasks, workflows or scheduled activities, so those sub-resources (and a change targeting the entity, answered with the named 422) are not found on an entity path. It lives under a project, environment, asset or another entity (the entities collection segment) and parents entities only. It keeps the basic node constructs: properties, settings (converged_properties and converged_settings work, with an entity converge layer), bookmarks, maintenance windows, metadata, asset owners, archive and the whole lifecycle below. Entities are core — no entitlement and no count limit. The typed v2 shape and the bulk create are resources/entity; the CLI is opschain entity (opschain docs cli/entity).

Registered hosts are their own resource with their own API family: an infrastructure node lives in an infrastructure/ collection inside the tree but is served as resources/infrastructure_node — see also the infrastructure nodes reference.

Relationships​

NameKindsMeaning
properties / settingsallTo-one properties/settings document, with related links /api/properties/<id>, /api/settings/<id>.
parentsallTo-many: the ancestors, root first (empty for a project).
parent / projectnon-rootsTo-one: the immediate parent and the root project (project ref carries a related link).
bookmarksallTo-many bookmark refs.
childrenproject, environment, entityTo-many, filtered to children the principal can read; refs the source cannot resolve stay. An entity's children are its child entities; entities never appear in a project's, environment's or asset's children — list them through the …/entities collection.
template / template_versionassetThe pinned template and version (with related links).
latest_node_background_tasksassetTo-many task refs.

self and bookmarks always; changes (all kinds); actions (asset — catalog entries backed by actions.rb or MintModel — only applicable to existing MintPress customers and those using the MintPress SDK or the packs:mintpress feature — additionally carry runner_requirement: "ruby", marking that they execute as opschain-action tasks and need a Ruby-capable runner (visibility only); scheduled_activities (kinds that can have changes), plus per-child-kind collection links (environments, assets; entities on an entity), plus project-only git_remotes, templates, workflows/workflow_runs — each of these gated by read on the corresponding …/<section> rule path; asset-only converged_properties (gated), node_background_tasks, version_history, mintmodels, mintmodel_history; parent on non-roots.

Meta​

can_have_changes, can_have_children, can_have_properties (always true), can_have_settings (always true), can_have_templates, requires_template, child_node_types, can_have_workflows, can_have_actions, and an authorisations object with per-section decisions for the current principal (e.g. properties {read, update}, changes {read, execute, delete}, secrets {settings, properties, mintmodel} — superuser-only — and self {update, delete}).

Lifecycle: delete, destroy, restore, export​

The opschain admin nodes CLI family (delete, restore, destroy, export, import) drives these lanes.

  • Soft delete (DELETE) tombstones the subtree: hidden from every list and route, path reserved, new work refused, fully reversible. It refuses while runs are active beneath (named 422) and emits api:nodes:deleted.
  • Restore (POST …/restore) undoes exactly that delete and emits api:nodes:restored. Only the root of a delete resolves — a node tombstoned as part of an ancestor's delete is restored through that ancestor.
  • Destroy (POST …/destroy) is valid only on a soft-deleted node — two deliberate steps; a live node answers the named 422 ("…Destroying is a two-step operation…"). It cascades every stored row beneath except the events table (the audit persists; chains keep rendering), releases unreferenced artifact blobs and emits api:nodes:destroyed with the manifest. The response (and the GET preview) is a node_destroy_manifest resource: {path, destroyed, counts (per-type row counts), note} — the preview's note is the standing export reminder.
  • Export (GET …/export) streams a credential-free zip; with_history=true adds the properties/settings version trails.

Every lifecycle verb authorises read plus the node's deletable rule action, 404-on-deny.

A node left behind by a promote or demote is archived (conversion_state promoted or demoted, with a converted_to link) and otherwise an ordinary node: it can be edited, unarchived, deleted and restored; only a further conversion waits until it is unarchived — see resources/entity ("Promote and demote").

The lifecycle events name the node's kind; an entity's create, update and delete request events (api:nodes:create, api:nodes:update, api:nodes:destroy) carry kind: entity in their data as well, so an audit query can tell entities apart from the other kinds.

API v2​

The per-object estate services serve the same rows over /api/v2 (opschain docs api-v2), authorised exactly as the v1 lanes above (404-on-deny) and audited with the same api:projects:* / api:nodes:* events:

  • ProjectService — ListProjects / GetProject(code) / CreateProject / UpdateProject / DeleteProject (the soft-delete lane; restore/destroy/export remain v1-only).
  • EnvironmentService — ListEnvironments(project_code) and the node-path-addressed GetEnvironment / UpdateEnvironment / DeleteEnvironment, plus CreateEnvironment(project_code, …).
  • EntityService — ListEntities(parent_path) (keyset, code-ascending, with code/search/archived filters), GetEntity(node_path), CreateEntity, the set-based BulkCreateEntities (at most 1000 per batch, per-item refusals), UpdateEntity and DeleteEntity — see resources/entity. Entities are left out of NodeService.GetNodeHierarchy unless the request sets include_entities, which requires root_path and nests the entities under the root (itself possibly an entity) at any depth.
  • AssetService — ListAssets(parent_path) rows carry the pinned template identity and task_active — and the pinned version's template_version_state / template_version_has_mintmodel and the row's latest_background_tasks (the v1 asset-list include set); GetAsset(node_path) is the server-shaped composite (MintModel validity, readable child assets, the latest background tasks, and the show-only erb_file_content); ListTemplateVersionHistory(node_path) serves the templated node's version_history windows (a non-templated node refuses with the v1 message); CreateAsset crosses the licence assets gate; UpdateAsset.template_version repoints the pinned version.

Lists take a typed archived filter (the v1 filter[archived_eq]) and cursor-paginate code-ascending; list/get responses carry the v1 meta.authorisations pair for the governing rule path. Since the three gets also carry the full v1 node-show meta — the node-kind shape flags and the per-subresource authorisation sections, rendered through the one rule the v1 serializer uses — beside the gap-C secrets_authorisations triple. The estate read model is PostgreSQL-only: the embedded store answers the typed ERROR_REASON_STORE_POSTGRES_REQUIRED posture.

  • opschain docs maintenance-windows — the badge's source of truth.
  • opschain docs resources/node_comparison — comparing two nodes.
  • opschain docs resources/mintmodel — the asset's mintmodels / mintmodel_history node-path family and the generate trigger.