Thing templates: revisions, publishing, and sharing¶
ThingLab templates are the unit of reuse for virtual devices. This page covers how a template becomes a stable thing to share — the model an integrator needs before publishing one, consuming another account’s, or automating either.
It complements device models (what a model is and who owns it) and the CLI reference (the commands).
A template is not one immutable object¶
A template is editable, and its meaning changes as you edit it. Every time that meaning is used, it is resolved to a revision — a frozen, content-identified record of what the template meant at that moment. Editing the template again produces a different revision; nothing you do to the template rewrites the revision already in use.
That is what makes two guarantees possible:
An instance built from a template is pinned to the revision it resolved, so editing the template afterwards never changes what that instance is emulating.
A consumer of a template you share gets a definite version to build from, not “whatever the template says today”.
Revisions are read-only through the API. A revision is created by using the template, never by writing one.
Publishing: which version is on offer¶
Publishing the template publishes its current content. The platform resolves it exactly as an instantiation would and records that revision as the offering:
Change nothing, publish again: you are re-opening the version already offered, and the response says so (
created: false).Change the template, publish again: a new revision is offered, and the previous offering stays on record as one you superseded.
Publishing is the affirmative act, so it always leaves the offering open — which makes it the way back from either of the other two states, not just the way in.
POST /simulation/v1/templates/{id}/publications
Lifecycle: published, deprecated, withdrawn¶
Every offering carries a state, and the template’s state is the state of its latest offering.
State |
New instantiations |
Notes |
|---|---|---|
|
allowed |
the offering is open |
|
allowed |
a statement of intent: this version is on its way out |
|
refused |
for the owner and for every consumer alike |
A withdrawal is a catalogue act, not an execution kill: instances already pinned
to the revision keep running. It is also scoped to the revision it names — moving
a superseded offering to withdrawn does not withdraw what you are offering now.
PATCH /simulation/v1/templates/{id}/publications/{publication_id}
{ "state": "published" | "deprecated" | "withdrawn" }
What a consumer sees¶
Templates visible to your account through an audience appear in the ordinary template read, with three view facts:
Field |
Meaning |
|---|---|
|
|
|
the offering’s state: |
|
|
Two details matter when you write a client against these:
The authoring fields are the offering’s. For a shared template they are read from the published revision, never from the owner’s live draft — the fields you choose from are the fields an instantiation will honour.
usabilityis template-local. It answers “is this offering open”, and nothing else. Instantiating additionally re-checks the model the template binds against your account at that moment, because the binding belongs to the owner’s account and was authorized against theirs. Ausabletemplate can still be refused at instantiation for that reason; the refusal names it, and the plain remedy is to copy the template into your own account and bind your own model.
Copy: taking a template into your own account¶
POST /simulation/v1/templates/{id}/copy
A copy is always a new template in the calling account. What it carries depends on what the source is:
Your own template — a full copy: authoring fields, composed components, and the model binding.
A template shared with you — the authoring fields, read from the published revision. The composed profiles and the model binding are deliberately not carried: the components name profile rows in the source account, and the model binding is a row your principal was never checked against. The response lists each of these under
not_carried, with the reason, so a copy is never silently narrower than its source.
The copy records where it came from (copied_from), and it is otherwise yours:
later changes or a withdrawal by the original owner do not reach into it.