Skip to content

Marketplace (Planned)

The marketplace is a registry for discovering, installing, and updating device-type libraries — the reusable templates described in Type System. It is the distribution layer above the bundle format: where Creating Types covers authoring a device type, this page covers how a published bundle is resolved per tenant and customised through overrides.

A published library lands in the platform’s global type catalogue; organisations commission instances against a pinned (typeId, version) and customise through overrides — never per-org forks. Availability never depends on a hardware identity. The hosted catalogue that will front this — its address and routing — is part of the distribution model still to be announced.

The marketplace is deliberately constrained to device-type templates — the one template kind that is actually built. Templates for processes, alarm sets, dashboards, electrical models, mission models, and scene graphs are explicitly out of scope here; that boundary is a deferral marker, not a framework. The working presumption is that non-device-type templates will eventually reuse the same materialiser machinery, but nothing is locked and nothing is built. For now, “marketplace” means device-type libraries.

A library is a versioned package of related device types, distributed as a bundle:

ArtefactContents
.cspkgA library — one or more device types plus shared assets

A .cspkg carries the device-type definition as authored YAML — manifest.yaml, alarms/alarms.yaml, commands/commands.yaml, optional simulation.yaml, and components/*.yaml. See Type System for the bundle layout, Tags for the tag schema, and Alarms & Commands for those manifests.

Resolution is a pure function:

(masterBundle, version, overrides[]) → resolvedTypeDefinition
  • masterBundle — the canonical device-type package as published (e.g. consystence.pump.centrifugal).
  • version — the pinned semver of the master bundle (e.g. 1.2.0).
  • overrides[] — an ordered list of per-tenant JSON Patch operations layered on the master.

The output, resolvedTypeDefinition, is the fully materialised type — tags, states, alarms, commands, simulation profile, and faceplate component tree, with all binding expressions ({tags.X}, {instance.X}, …) preserved for the renderer to resolve at render time. Because the function is deterministic (same inputs → same output), its results are cached, keyed by (bundleId, version, overridesHash) where overridesHash is a SHA-256 of the normalised override set. Caching is what makes many instances of one type cheap: a tenant’s hundred pump-* instances all reuse the single materialised definition.

The materialiser works on JSON internally. Authors write YAML in the .cspkg; conversion to JSON happens at bundle ingest (upload or load-from-disk), and JSON is the representation throughout the materialiser, cache, override storage, and wire format. The author experience is unchanged — YAML in, JSON hidden.

This is a two-stage pipeline. The materialiser produces a template definition; the renderer (see Type System and the server-driven UI) then binds that definition to a specific instance and live state to produce the semantic UI tree. The renderer transforms; the materialiser stays instance-unaware.

flowchart LR
    B["Master bundle .cspkg<br/>consystence.pump.centrifugal"] --> M
    V["Pinned version<br/>1.2.0"] --> M
    O["Tenant overrides[]<br/>JSON Patch RFC 6902"] --> M
    M["Materialiser<br/>pure + cached by<br/>bundleId,version,overridesHash"] --> R["resolvedTypeDefinition<br/>tags + states + alarms<br/>+ commands + faceplate"]
    R --> RN["Renderer<br/>binds instance + live state"]
    RN --> UI["Semantic UI tree<br/>per instance"]

Overrides let a tenant customise a published type without forking it — a site sets a low-flow threshold, a partner relabels faceplate strings into local operator vocabulary, an engineer adds an alarm after a safety review.

  • Paths use JSON Pointer (RFC 6901) — e.g. /alarms/1/threshold, /alarms/- (append), /faceplate/children/0/label.
  • Operations use JSON Patch (RFC 6902), applied in order.
  • Conflicts resolve last-wins by default (JSON Patch semantics). Whether production moves to error-on-conflict is a deferred decision, not yet locked.
  • Isolation is by construction. Each tenant’s override set is a distinct cache key, so one organisation’s overrides cannot leak into another’s — both resolve independently against the same master bundle.

When a master bundle is republished (e.g. 1.2.0 → 1.3.0), a tenant’s overrides reapply against the new version. If an override targets a path the new structure no longer has, the planned default is to fail at materialise time so the broken intent surfaces explicitly rather than being silently dropped — though the exact invalidation policy (error / warn-and-skip / smart-migrate) and content-addressed (predicate) paths are deferred markers, not yet locked.

Consystence’s own first-party device-type libraries are meant to follow the same rules as any third-party integrator’s library. The goal state is versioned .cspkg bundles resolved through the same materialiser; today the first-party libraries load from unpacked YAML directories on disk at server startup into a platform-wide global registry. (The device-type libraries and the control blocks beneath them together form the Consystence Standard Library. The control-block library is maintained as a single behaviour master: ControlLogix blocks are checked against its test vectors; PLCnext has a generated runtime program built from the same source, run on a virtual PLCnext only and checked with live tests, with no conformance replay and per-block vector replay on PLCnext planned; and the library is distributed to control targets through the control-code pipeline rather than the type registry.) The design rule is the data rule: a device type’s full behaviour — control/routing model, state encoding, alarms, commands, simulation, components — belongs in the bundle manifest as data, not in platform code. The rule is partly in place today: some platform features, such as KPI roles, simulation wiring and code generation, still recognise specific first-party device types, and moving that behaviour into the manifests is ongoing work.

The practical test: any typeId-keyed branch, switch, or allowlist in platform code breaks the rule, because it would make a library update a code release — a shortcut a third party could never use. The goal for the marketplace follows: publishing or updating a library, first-party or third-party, should be a pure data/package operation that never needs a Consystence release. Today the first-party libraries ship with the platform, so updating them still means a platform release.

The publishing flow and the commercial model for paid libraries are to be announced — how libraries are submitted, reviewed, and priced is a future decision, and no publishing API exists yet. Whether an organisation may install or publish will be governed by its roles — owner, admin, member — the same role model the rest of the platform uses.

There are no marketplace or registry endpoints yet, and no separate marketplace credential. The platform’s API accepts these credentials (see Authentication, which also covers site-PIN sign-in on a site server, and Auth Model):

  • Organisation sign-in — a Bearer token from Entra External ID at id.consystence.com, for people.
  • Tenant API key — sent in the X-Api-Key header (prefix csk_); a key is read-only unless created as an operator key. See API Keys.

Once marketplace endpoints are published they will appear in the rendered OpenAPI at API Reference.

CapabilityState
Materialiser (bundle, version, overrides[]) → resolvedTypeDefinitionSpecified; validated; deferred to first production surface
JSON Pointer / JSON Patch override pathsSpecified; conflict + invalidation defaults not yet locked
Per-tenant override isolationBy construction (distinct cache key)
Hosted cataloguePlanned
Publishing flow + authoring UIPlanned
Commercial model for paid librariesTo be announced
Non-device-type templatesOut of scope (deferred)