Skip to main content

CK Language 2

CK language 2 is an opt-in extension of the Construction Kit YAML language. A model that declares ckLanguage: 2 in its ckModel.yaml can use:

  • Interfaces — versioned contracts (Named-1) with attribute, association and method members that types implement;
  • Access modifiers — visibility and derivable on elements, access on attribute assignments (including Hidden);
  • Method definitions — typed method signatures on types and interfaces (definitions only; see Status);
  • stricter defaults: types and records of a v2 model can only be derived inside their own model unless they say otherwise.

A model without ckLanguage is a CK language 1 model. It compiles exactly as before — byte for byte — and keeps its semantics: everything is public and derivable, there are no interfaces and no methods.

Availability

CK language 2 needs the CK v2 engine (minEngineVersion 3.5.1, see Engine and catalog guard). It is introduced in phases; this page describes what is available today. Do not import a ckLanguage: 2 model into an environment before every service there runs the CK v2 engine — older engines ignore the new keys and would, for example, expose Hidden attributes.

Operating rule: shared catalogs

Do not publish a ckLanguage: 2 or range-retaining version of a base model (for example System or Basic) into a shared catalog — the GitHub catalogs (octo-ckc) or any catalog other teams compile against — until every service that consumes that catalog runs the CK v2 engine.

Catalogs list the versions of both roots. After one such publish, every dependent compiled against the catalog resolves the new base version, inherits its minEngineVersion and is published under ck-models/v3/ — even when the dependent itself is CK language 1. Older engines keep seeing only the older ck-models/v2/ versions of all those dependents. Evaluate CK language 2 and range retention in the local catalog only.

Upgrade and rollback​

Introducing the CK v2 engine needs no database migration and no System model update: all System models stay CK language 1, new fields are only written for CK language 2 models, and the new collections (CkInterface, CkTypeInterfaceImplementation) are created by the next import but stay empty until a CK language 2 model is imported. A tenant that has only seen CK language 1 imports has exactly the same construction kit documents as before, so services can be rolled back to the previous engine. After the first ckLanguage: 2 (or range-retaining) import into a tenant a rollback is no longer safe.

Opting In​

$schema: https://schemas.meshmakers.cloud/construction-kit-meta.schema.json
modelId: Acme.Assets-1.0.0
ckLanguage: 2
description: Example CK language 2 model
dependencies:
- System-[2.5,3.0)

ckLanguage accepts 1 and 2; omitted means 1. Any other value in the source is a schema error (message 27). A compiled model that declares a language the running engine does not support is refused — also when it is only a dependency: the compiler reports message 91; on import into a tenant the refusal is an import error with the same text.

Every CK language 2 key used in a model without ckLanguage: 2 is rejected with message 90:

Error 90 types/types.yaml: Model 'Acme.Plant-1.0.0' uses the CK v2 feature 'implements' at 'Acme.Plant-1.0.0/Pump-1',
which requires 'ckLanguage: 2' in ckModel.yaml (declared: 1).

What Changes With ckLanguage: 2​

KeyWhereCK language 1CK language 2 (default when omitted)
interfaces (folder interfaces/)modelnot allowed—
implementstypenot allowed[]
methodstype, interfacenot allowed[]
visibilitytype, record, enum, attribute, association role, interface, methodnot allowed (everything Public)Public
derivabletype, recordnot allowed (everything Any)Model
accessattribute assignment on a type, record or association rolenot allowed (ReadWrite)ReadWrite
targetCkInterfaceIdtype associationnot allowed—

Values are PascalCase (Public/Internal, Model/Any, ReadWrite/ReadOnly/MethodOnly/Hidden); lowercase values are schema errors.

The default for derivable is the important difference: in a v2 model, another model can only derive from a type or record that declares derivable: Any. This makes "being a base class for other models" an explicit decision of the model author. See derivable.

Mixing CK Language 1 and 2​

  • A v1 model can depend on a v2 model and the other way round.
  • The rules of an element are those of the model that declares it. A v1 model that derives from a type of a v2 dependency is still refused when that type is derivable: Model (message 113), and it can never reference an Internal element of it (message 112).
  • A v1 model cannot use CK language 2 keys itself (message 90) — to implement an interface of a dependency, the model must move to ckLanguage: 2.

Versioning consequence of moving to ckLanguage: 2​

Raising ckLanguage from 1 to 2 is a Minor change on its own. But because the derivable default flips from Any to Model, it is a Major change unless every type and record of the model declares derivable: Any explicitly. The SemVer gate reports this as one derivable change per element. If dependents of your model derive from its types, declare derivable: Any on exactly those types when you adopt CK language 2.

Engine and Catalog Guard​

Older engines read compiled models tolerantly and would silently drop keys they do not know (implements, access, visibility, ...). Two mechanisms keep a v2 model away from them:

  • minEngineVersion. The compiler writes minEngineVersion: 3.5.1 into every ckLanguage: 2 model and every range-retaining model. A CK language 1 model normally carries none — but it inherits the highest minEngineVersion of its resolved dependencies, so a v1 model compiled against a v2 or range-retaining dependency gets one as well and is published under ck-models/v3/. The engine refuses a model — or a dependency — whose minEngineVersion is above its own version with message 126. (Local DebugL builds are version 999.0.0 and accept everything; engine builds below version 1.0, such as private-feed 0.1.* builds, skip the check.)
  • Catalog path ck-models/v3/. Models with a minEngineVersion are published under ck-models/v3/<letter>/<Name>/<major>/ instead of ck-models/v2/. Engines before CK v2 only read v2, so they never see a v3 model. CK v2 engines read both roots (v3 first). A model version lives in exactly one root.
<catalog>/ck-models/v2/s/System/2/ck-system-2.5.0.json # CK language 1
<catalog>/ck-models/v3/a/Acme.Assets/1/ck-acme.assets-1.0.0.json # CK language 2
Remote catalogs

The local file-system catalog and the GitHub catalogs read and publish the ck-models/v3/ root. The catalog CI pipelines that publish models to the shared GitHub catalogs do not handle CK language 2 models yet; that follows in a later phase. Until then, keep CK language 2 models out of the shared catalogs. Because dependents inherit minEngineVersion, this applies to base models in particular — see the operating rule at the top of this page.

Engine version contract​

Compiled modelminEngineVersion
ckLanguage: 1, no range retention, v1 dependenciesnot written (output unchanged)
ckLanguage: 2 or range-retaining3.5.1
CK language 1 model on a dependency that carries onethe highest value of its resolved dependencies
  • 3.5.1 is the minimum. It is the first libraries release with the complete CK v2 reader. The value is a constant, not the version of the compiling engine, so compiler output does not depend on the build configuration. A model that was published earlier keeps the value it was published with (published models are immutable), for example 3.4.0.
  • What the running engine reports. The engine compares major.minor.patch of its own release. It reads the first usable value of the assembly file version (for example 3.5.2.0 on an r3.5.2 build), the informational version (without -slug and +commit) and the assembly version, so a patch release such as 3.5.0 and 3.5.2 can be told apart.
  • Message 126. A model whose minEngineVersion is above the running engine is refused with message 126, which names the model, the required version and the running engine. With a required version of 3.5.1, engines 3.4.x and 3.5.0 refuse the model; 3.5.1, 3.5.2 and 3.6.0 read it. Local DebugL builds are version 999.0.0 and read everything.
  • Main line rule. An engine whose major version is below 1 (private-feed builds 0.1.YYMM.NNNN of the main line) skips the check, because it carries no number that can be compared with the release line. Main-line environments are covered by the main-line floor of the engine inventory and by rebuilt images.

A Complete Example​

The examples on these pages use one model, Acme.Assets, that compiles with the CK v2 compiler:

# interfaces/interfaces.yaml
$schema: https://schemas.meshmakers.cloud/construction-kit-elements.schema.json
interfaces:
- interfaceId: Named-1
description: Anything with a human-readable name.
attributes:
- id: ${System}/Name
name: Name
- id: ${System}/Description
name: Description
isOptional: true
- interfaceId: Identified-1
description: A named thing with a serial number.
extends:
- ${this}/Named-1
attributes:
- id: ${this}/SerialNumber
name: SerialNumber
- interfaceId: Monitoring-1
description: Something that monitors identified things.
associations:
- id: ${this}/Monitors
targetCkInterfaceId: ${this}/Identified-1
multiplicity: N
- interfaceId: Calibratable-1
description: Can be calibrated.
methods:
- methodId: Calibrate-1
parameters:
- name: mode
valueType: Enum
valueCkEnumId: ${this}/CalibrationMode
- name: referenceValue
valueType: Double
isOptional: true
result:
valueType: Record
valueCkRecordId: ${this}/CalibrationResult
# types/types.yaml
$schema: https://schemas.meshmakers.cloud/construction-kit-elements.schema.json
types:
- typeId: Device
description: Base type for devices; other models may derive from it.
isAbstract: true
derivable: Any
derivedFromCkTypeId: ${System}/Entity
implements:
- ${this}/Identified-1
attributes:
- id: ${System}/Name
name: Name
- id: ${System}/Description
name: Description
isOptional: true
- id: ${this}/SerialNumber
name: SerialNumber
access: ReadOnly
- id: ${this}/Firmware
name: Firmware
isOptional: true
access: MethodOnly
- id: ${this}/ApiKeyHash
name: ApiKeyHash
isOptional: true
access: Hidden
methods:
- methodId: UpdateFirmware-1
description: Installs a firmware version.
parameters:
- name: version
valueType: String
- name: signingKey
valueType: String
sensitive: true
errors:
- code: FIRMWARE_REJECTED
description: The device refused the image.
authorization:
roles: [ AssetManagement ]
execution:
timeoutSeconds: 60
- typeId: Sensor
description: A sensor; only this model may derive from it (derivable defaults to Model).
derivedFromCkTypeId: ${this}/Device
implements:
- ${this}/Calibratable-1
- typeId: Gateway
description: Monitors devices.
derivedFromCkTypeId: ${this}/Device
implements:
- ${this}/Monitoring-1
associations:
- id: ${this}/Monitors
targetCkTypeId: ${System}/Entity
targetCkInterfaceId: ${this}/Identified-1

The remaining files declare the attributes SerialNumber, Firmware, ApiKeyHash (all String), CalibrationOffset (Double) and InternalNote (String, visibility: Internal), the enum CalibrationMode, the record CalibrationResult (member Offset) and the association role Monitors (N to N).

Generated Code​

For CK language 2 models the source generator (Meshmakers.Octo.ConstructionKit.SourceGeneration) additionally emits:

  • a C# interface IRt<Name> per CK interface (Named-1 → IRtNamed, Named-2 → IRtNamed2) with get-only properties for the attribute members; extends becomes C# interface inheritance, and the Rt classes implement their declared interfaces explicitly;
  • sealed Rt classes for isFinal types and for derivable: Model types without a subtype in their own model. Rt classes of isAbstract types are not generated as C# abstract classes for now (repository APIs require instantiable entity classes); this is revisited when the System models move to CK language 2;
  • method id constants (DeviceUpdateFirmwareMethodId = "Acme.Assets/Device.UpdateFirmware-1"), a {Type}{Method}Parameters record per type method (sensitive parameters are redacted as *** by ToString()) and a {Type}{Method}Result record when the method has a result.

CK language 1 output is unchanged.

Status​

FeatureStatus
ckLanguage: 2, minEngineVersion, ck-models/v3/ (engine side)Available
Interfaces: attributes, extends, associations, methods, deprecated, compiler validationAvailable
visibility, derivable (compile time and re-checked on import)Available
Attribute access in the compiler (Hidden rules)Available
access enforcement in GraphQLHidden and MethodOnly enforced; ReadOnly not enforced yet — see Access modifiers
CK interfaces in the GraphQL schema (incl. extends), CK meta API for interfaces, methods and modifiersAvailable — see GraphQL mapping
Method invocation (GraphQL mutations, handlers)Not yet — methods are definitions only
Querying by interface (runtime.byInterface, GetRtEntitiesByType by interface)Not yet
Range retention (OctoCkRangeRetention)Preview, behind a flag, default off
Compatibility checker for public surfaces (Phase 2)Not yet
ck-models/v3/ in the GitHub catalogsRead and publish available; catalog CI not yet

See Also​