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 —
visibilityandderivableon elements,accesson attribute assignments (includingHidden); - 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.
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.
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
| Key | Where | CK language 1 | CK language 2 (default when omitted) |
|---|---|---|---|
interfaces (folder interfaces/) | model | not allowed | — |
implements | type | not allowed | [] |
methods | type, interface | not allowed | [] |
visibility | type, record, enum, attribute, association role, interface, method | not allowed (everything Public) | Public |
derivable | type, record | not allowed (everything Any) | Model |
access | attribute assignment on a type, record or association role | not allowed (ReadWrite) | ReadWrite |
targetCkInterfaceId | type association | not 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 anInternalelement 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 writesminEngineVersion: 3.5.1into everyckLanguage: 2model and every range-retaining model. A CK language 1 model normally carries none — but it inherits the highestminEngineVersionof its resolved dependencies, so a v1 model compiled against a v2 or range-retaining dependency gets one as well and is published underck-models/v3/. The engine refuses a model — or a dependency — whoseminEngineVersionis above its own version with message 126. (LocalDebugLbuilds are version999.0.0and accept everything; engine builds below version 1.0, such as private-feed0.1.*builds, skip the check.)- Catalog path
ck-models/v3/. Models with aminEngineVersionare published underck-models/v3/<letter>/<Name>/<major>/instead ofck-models/v2/. Engines before CK v2 only readv2, 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
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 model | minEngineVersion |
|---|---|
ckLanguage: 1, no range retention, v1 dependencies | not written (output unchanged) |
ckLanguage: 2 or range-retaining | 3.5.1 |
| CK language 1 model on a dependency that carries one | the 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.patchof its own release. It reads the first usable value of the assembly file version (for example3.5.2.0on anr3.5.2build), the informational version (without-slugand+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
minEngineVersionis 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. LocalDebugLbuilds are version999.0.0and read everything. - Main line rule. An engine whose major version is below 1 (private-feed builds
0.1.YYMM.NNNNof 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;extendsbecomes C# interface inheritance, and the Rt classes implement their declared interfaces explicitly; sealedRt classes forisFinaltypes and forderivable: Modeltypes without a subtype in their own model. Rt classes ofisAbstracttypes are not generated as C#abstractclasses 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}Parametersrecord per type method (sensitiveparameters are redacted as***byToString()) and a{Type}{Method}Resultrecord when the method has a result.
CK language 1 output is unchanged.
Status
| Feature | Status |
|---|---|
ckLanguage: 2, minEngineVersion, ck-models/v3/ (engine side) | Available |
Interfaces: attributes, extends, associations, methods, deprecated, compiler validation | Available |
visibility, derivable (compile time and re-checked on import) | Available |
Attribute access in the compiler (Hidden rules) | Available |
access enforcement in GraphQL | Hidden 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 modifiers | Available — 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 catalogs | Read and publish available; catalog CI not yet |
See Also
- Interfaces
- Access modifiers
- Method definitions
- GraphQL mapping
- Range retention
- Compiler messages
- Versioning rules — SemVer classification of CK language 2 changes, embedded-model downgrade guard