Skip to main content
Version: v5.6.1

Template and template version

templates

JSON:API types: template and template_version. One page covers both: a template is the project-scoped definition (name, type, git remote), and each of its versions pins a git revision assets are templated from.

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

Endpoints​

Reads dispatch through the node-path router under the project:

VerbPathBehaviour
GET/api/projects/{p}/templatesIndex — each template gated by read on its own rule path (<project path>/templates/<code>); meta.authorisations reports update/delete on the project's …/templates path.
GET/api/projects/{p}/templates/{id}Show (404 on a miss, an out-of-project template, or a read denial).
POST/api/projects/{p}/templatesCreate — project readable plus update on …/templates, 404-on-deny. 201, or 422.
GET/api/projects/{p}/templates/{id}/versionsVersion index (ordered by updated_at); readability inherits the template's. Filterable with filter[<attribute>_eq]=<value> over the serialized attributes plus filter[created_at_gt]=<ts> — the asset screen's new-template probe sends created_at_gt + state_eq + archived_eq — and sortable with filter[sorts][]=<attr> asc|desc over the serialized attributes (the versions screen sends created_at desc); a sort attribute outside the wire shape is a 400.
GET/api/projects/{p}/templates/{id}/versions/{version}Version show, addressed by the version label, not a row id.
PATCH/PUT/api/projects/{p}/templates/{template_id}/versions/{version}Upsert: creates the version when the label is new, updates it otherwise. Requires update on the template's rule path, 404-on-deny. Request meta flags: fetch_revision (resolve the revision now) and ignore_in_use (override the in-use guards).
POST…/versions/{version}/assign_to_assetsRepoints the named assets (asset_paths) at this version — requires update on the template's path plus update on every named asset path. A broken version, or one without a resolved git commit, refuses ("cannot be assigned as it is broken or has not resolved a Git commit"). 200 with the version re-serialized.
PATCH/PUT/api/projects/{p}/templates/{id}Update — permitted attributes name, description, git_remote_name (re-resolved within the project), archived, disabled; a JSON null archived is ignored. Requires update on the template's own rule path, 404-on-deny. Archiving or disabling refuses with 422 (Can't archive/disable templates that are in use) while any unarchived node uses the template; a supplied archived also refuses when it matches the stored state (Template is already archived/active) or while the template's commits carry active changes. 200 with the updated template.
DELETE/api/projects/{p}/templates/{id}Delete — only a template with no versions and no pinned assets (the templates create --version rollback lane); a referenced template refuses by name with 422. Requires project read plus delete on the project's …/templates path. 204.

Every template and template version is created with its empty owned properties and settings documents (the upstream PropertiesOwner/SettingsOwner persistence) — the serializer's properties/settings relationship ids are therefore always populated. A one-shot idempotent boot backfill heals owners created before that port.

template attributes​

AttributeTypeMeaning
codestringUnique (case-insensitively) among the project's unarchived templates; ^[a-z0-9_]+$, max 50. Also the tail of the template's RBAC rule path.
namestringDisplay name, unique per (project, template_type) among unarchived.
descriptionstring, nullableNull when never set.
archivedbooleanArchive flag.
disabledbooleanDisable flag.
template_typestringThe template's kind. Create on this server accepts only asset (the default when blank); any other supplied value answers "Template type is not included in the list". The has_mintmodel version attribute is conditional on this type.
created_at / updated_atstring (ISO8601, 6 fractional digits)Row timestamps.

Relationships: properties, settings (to-one documents with related links), git_remote (to-one — the project remote the versions fetch from, required at create and resolved by name within the project), template_versions (to-many), nodes (to-many — the assets templated from it, filtered to nodes the principal can read), project (to-one). Links: self, template_versions (= self + /versions), project.

Every relationship is includable on the index and the show with the standard ?include= parameter. In particular include=git_remote sideloads the remote serialized exactly as its own show document, masked credentials included (previously the serializer exposed the relationship but the include engine could not resolve it, so the CLI re-fetched the git_remotes index to map the id to a name).

template_version attributes​

AttributeTypeMeaning
versionstringThe version label (the URL segment), max length enforced on upsert.
descriptionstring, nullableNull when never set; an omitted key preserves the stored value.
statestringThe version's readiness. This server writes ready (set at create and whenever a revision resolves) and treats broken as the unassignable state; the full upstream state vocabulary is not declared in this repository, so no exhaustive list is documented here.
created_bystringUsername of the creating user.
archivedbooleanArchive flag. Cannot be set on create ("archived on create" refusal); archiving a version that assets still use refuses unless ignore_in_use.
initialization_errorJSON, nullableThe stored initialisation failure, served verbatim; null when none.
lockedbooleanVersion lock: a locked version refuses modification ("Template version cannot be modified as the version is currently locked/archived"-shaped 422s) and float_git_rev cannot be enabled on it. Changing git_rev on an in-use version likewise refuses unless ignore_in_use.
float_git_revbooleanWhether the version tracks its git_rev's moving tip instead of a fixed commit.
float_refresh_errorJSON, nullableThe last float-refresh failure, served verbatim; null when none.
has_mintmodelboolean, conditionalRendered only for asset-type templates (the key is absent otherwise): whether the version's repository carries a MintModel.
created_at / updated_atstring (ISO8601, 6 fractional digits)Row timestamps.
git_revstring, nullableThe requested revision (branch/tag/sha). On upsert, an omitted or "" git_rev falls back to the version's current revision.
commit_shastring, nullableThe resolved commit, null until resolution.
git_remote_namestring, nullableThe owning template's git remote name.
sealed_packsarray of {name, version}The installed pack versions this version's pinned set sealed when it resolved (installed: declarations in .opschain/packs.yaml), pack name ascending — name is the pack store name, version the exact installed version. Always an array, empty when the version mounts no installed pack; git-sourced and bundled packs are not listed. The pack store shows the other direction as referenced_by (opschain docs resources/pack), and a re-resolve cuts a new version whose list moves — this one's never changes.
invalid_action_filesarray of {file, action, error}The declarative action files at the version's pinned commit that failed validation or compilation at the version's last catalog resolution — file the repository path (.opschain/templates/<template>/actions/…, .opschain/actions/…, or a mounted pack's actions/… path), action the identity the file would have listed under, error the validation error verbatim. Each listed action appears in the asset catalog as an invalid entry (invalid on the action resource), a change created from it is refused with the same error, and a workflow step that reaches it ends system_error with that error as its last_error_message. The resolution that records the list also emits warn:template:action_file_invalid once per file (opschain docs events). Always an array, empty when every file compiles or the version was never resolved.

Relationships: properties, settings, project, template, and pending_commit (to-one commit while a resolution is pending; data: null otherwise). Links: self (…/templates/<template_id>/versions/<version>), template.

Assets and versions​

An asset pins exactly one template version (template_version / template relationships on the asset, see opschain docs resources/node). Repointing happens from either end: the asset update's template_version attribute, or this page's assign_to_assets — both close the active template-version-history window, open the new one, and carry the previous successful generated actions forward.

MintModel versions​

A version whose pinned commit carries the template's MintModel document (MintModel/templates/assets/<template code>.json.erb, stored as has_mintmodel at revision resolution) renders that document on every generation request as the first phase of the derive job itself:

  • The server converges the asset's properties (the repository-committed document, overlaid by the converged mintpress.mintmodel_overrides, then by the mintpress subtree with those overrides removed) and the derive job stages the upstream render payload in its workspace — mintmodel.tar holding the commit's MintModel/ folder (already materialised by the snapshot fetch) plus properties.json and mp_context.json, with the mintmodel_request.json document (erb_file, parent codes) — under OPSCHAIN_MINTMODEL_DATA_PATH, the data-directory contract of the opschain-mintmodel-render CLI.
  • The CLI is co-packaged in the mintpress-runner image (beside the :7001 transformer, which serves only the MintModel diff lane) and runs before opschain-template-actions in the same job — no daemon dependency, so a render works even while the transformer is down, and a runner image without the CLI fails the generation naming the missing binary. This server never renders ERB or derives step trees itself; the result document rides the job-result channel back.
  • A successful render stores the concrete model in mintmodels, deduplicated by the SHA-256 of its canonical JSON (the upstream find_or_create_by_sha semantics — re-rendering an identical model reuses the row), and the result's actions — full-path enriched exactly as upstream (name→step_name, duplicate paths uniquified case-insensitively) — in asset_mintmodel_actions, keyed on the asset, the pinned commit and the SHA-256 of the staged properties. A later generation against an unchanged key reuses the stored row without re-rendering (no render phase is dispatched at all); a changed commit or properties re-renders.
  • The asset's action catalog merges those stored step trees with the actions.rb and declarative-action-file sources (actions.rb is only applicable to existing MintPress customers and those using the MintPress SDK or the packs:mintpress feature), and the runner discovery workspace receives the formatted trees as ~/.opschain/mintmodel_actions.json for actions.rb's OpsChain.mintmodel_actions — staged through the job's prepare spec with no size limit on current runners (a runner build predating protocol 3 receives them base64-encoded inside the job command instead, where a catalog over the 96 KiB budget degrades to an empty list with a warn:mintmodel:actions_unavailable event) — and an actions.rb that consumes them after a fresh render is re-exported once against the stored result.
  • A render failure fails the generation request with MintModel actions could not be derived: <cause> on the task's error_message (the actions history serves it), never runs the template-actions phase, and caches the cause as the row's generation_error; the next generation request re-renders.

API v2​

TemplateService on /api/v2 (opschain docs api-v2) serves the same catalog with the same authorisation split and refusals, audited with the same api:templates:* / api:template_versions:* events:

  • ListTemplates(project_code) (typed archived/template_type filter, per-row read on each template's rule path) and GetTemplate(id). Every template get carries its git_remote relation — the equivalence: the same fields the v1 git_remotes show serializes, the secret-bearing ones (user, ssh_key_data, password, passphrase) masked as {{hidden}} when stored and empty when not, never a value. TEMPLATE_VIEW_FULL adds the version rows (newest first; also on list rows) and the pinned nodes on the get.
  • CreateTemplate / UpdateTemplate / DeleteTemplate — the v1 permit lists as typed optional fields, the same in-use and referenced-template refusals.
  • ListTemplateVersions(template_id) (newest first; typed archived/state/created_after), GetTemplateVersion by label, and UpsertTemplateVersion with the v1 payload plus ignore_in_use / fetch_revision as typed flags.
  • DeleteTemplateVersion(template_id, version) (the ruling — upstream DestroyTemplateVersionCommand semantics): an actively assigned version refuses ("currently in use"); one assigned in the past — or still referenced by another record — is archived instead, answered as archived: true with the row (the v1 206); otherwise the version and its owned documents are destroyed. Requires delete on the template's rule path; audited api:template_versions:destroy.
  • CancelTemplateVersionRefresh(template_id, version) (upstream CancelTemplateVersionRefreshCommand semantics): refuses unless the version is initializing ("No SHA refresh is in progress…"); a cancelled version drops its pending commit and returns to ready on its resolved commit — or becomes broken when none resolved — recording the info|error:template_version:refresh_cancelled event. Requires update on the template's rule path; audited api:template_versions:cancel_refresh. Revision resolution is synchronous in this server, so only a row adopted mid-refresh can be cancellable.

The v1 facade still serves neither route (the route-parity waivers stand for v1); both verbs live on the v2 surface only. The catalog read model is PostgreSQL-only: the embedded store answers the typed ERROR_REASON_STORE_POSTGRES_REQUIRED posture.

  • opschain docs resources/node — the asset side of the pin.
  • opschain docs resources/git_remote — where versions fetch from.