| Version | 1.0.0 |
|---|---|
| Published | 2026-09-30 |
This document defines the OCI backend profile for the CSIL Node Packaging Specification [CSIL]. It specifies how the vocabulary defined in that specification is carried by OCI container images and OCI artifacts distributed via an OCI-compatible registry, and adds OCI-specific execution requirements.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
The roles defined in [CSIL] Terminology apply unchanged.
The OCI backend uses two distinct carriers for the metadata vocabulary defined in [CSIL] §6:
| Carrier | Identified by | Used for |
|---|---|---|
| OCI image config labels | image config media type
application/vnd.oci.image.config.v1+json, or
application/vnd.docker.container.image.v1+json |
Runtime ([CSIL] §6.2), execution ([CSIL] §6.4), SIL Kit metadata ([CSIL] §6.5, [CSIL] §6.6) |
| OCI artifact manifest annotations | manifest media type
application/vnd.oci.image.manifest.v1+json with
artifactType application/vnd.csil.application
(§3) |
Application ([CSIL] §6.3), SIL Kit metadata ([CSIL] §6.5, [CSIL] §6.6) |
Both image config media types MUST be accepted by a consumer. The OCI-native media type is the one a producer SHOULD emit; the Docker media type is widespread in existing registries and toolchains and is therefore accepted on equal terms.
Image config labels are the Labels map of the image
configuration's config object, as defined by [OCI-IMAGE]. Manifest annotations are the
annotations map of the manifest itself, not of any
descriptor it contains and not of an index referencing it.
The csil.* key vocabulary is used unchanged in both
carriers.
The platform descriptor ([CSIL] §6.8) is carried entirely by the
csil.* metadata vocabulary, so that a runtime, execution,
or application image is fully self-describing per the specification when
inspected with any OCI-aware tool, without this backend or any other
tooling having to augment it. Tooling MUST NOT source the [CSIL] §6 metadata
from the OCI image config platform.
Because a runnable OCI image also carries a native platform in its
image config (os,
architecture, variant,
os.features), these labels necessarily duplicate the config
fields. This duplication is intentional: the specification vocabulary is
the single normative source, and the image config is the container
engine's own concern. A producer MUST set the labels, and their values
MUST agree with the image config platform. Tooling reads the descriptor
from the labels; it MAY additionally cross-check them against the image
config and reject an image whose labels and config disagree.
Both carriers are JSON string maps, so [CSIL] §6.1.3's requirement that every metadata value is a UTF-8 string is met natively: a value is carried as a JSON string and read back byte for byte. No type coercion is possible or permitted.
A producer MUST NOT encode a value as a JSON number, boolean, null,
array or object. A boolean is the JSON string "true" or
"false", never the JSON literal true or
false. An integer is a JSON string of digits, never a JSON
number. A consumer encountering a non-string JSON value under a
csil. key MUST reject the layer.
An empty value is the empty JSON string "". It denotes a
present key with an empty value, which [CSIL] §6.1.4 distinguishes from an absent key.
A producer MUST NOT express "absent" by emitting an empty string, and a
consumer MUST NOT treat an empty string as absence.
[CSIL] §6.6.7 sets no upper bound on service metadata, and notes that a complex participant can exceed 10,000 entries. In this profile all such metadata is carried inside the image config or the manifest, both of which are blobs an OCI registry stores and serves as a unit. Registries commonly impose a maximum manifest size and an image config is subject to the registry's blob limits.
Producers SHOULD therefore be aware that a sufficiently large service metadata set can produce an artifact that a conformant OCI registry legitimately refuses to accept. This is a limit of the carrier.
An OCI runtime layer is implemented as a container image that carries
runtime metadata ([CSIL] §6.2) as image labels, provides the runtime
prologue as its Entrypoint (§4.1),
and optionally declares OCI-specific execution requirements (§5). Its own platform descriptor ([CSIL] §6.8) is
carried as image labels per §1.1
(csil.runtime.os, ...runtime.arch,
...runtime.variant, ...runtime.os.features,
...runtime.os.variant).
An OCI application layer is implemented as an OCI artifact: an image manifest that carries content but is not runnable. It is stored in and distributed by any OCI-compatible registry, using any client conforming to [OCI-DIST].
The artifact is an OCI image manifest
(application/vnd.oci.image.manifest.v1+json) constructed as
follows:
| Field | Requirement |
|---|---|
mediaType |
MUST be application/vnd.oci.image.manifest.v1+json |
artifactType |
MUST be application/vnd.csil.application |
config |
MUST be the empty descriptor (§3.2) |
layers |
MUST contain at least one descriptor carrying the model content (§3.3) |
annotations |
MUST carry the metadata defined in [CSIL] |
A consumer identifies a CSIL application layer by
artifactType. It MUST NOT rely on the repository or tag
name to do so (§6).
An application layer has no runnable configuration, so its
config descriptor MUST be the empty descriptor defined by
[OCI-IMAGE].
This is the OCI-native way of expressing "this manifest is not an
image", and it is what makes [CSIL] §2.2's requirement that an application
layer declare no execution information decidable in this profile: an
application layer has no image config, therefore no
Entrypoint and no Cmd, and a consumer can
verify this by inspecting the descriptor. A consumer MUST reject an
artifact declaring artifactType
application/vnd.csil.application whose config
is a runnable image config.
Model content is carried in the manifest's layers. Each
descriptor's mediaType MUST be one of:
| Media type | Content |
|---|---|
application/vnd.oci.image.layer.v1.tar |
An uncompressed tar archive of the model content |
application/vnd.oci.image.layer.v1.tar+gzip |
A gzip compressed tar archive of the model content |
A producer SHOULD use a single layer containing the complete model content, and SHOULD prefer the compressed form.
Paths within the archive are relative to the root of the model
content, and MUST NOT begin with / or contain
.. components. They are the paths the model sees at
runtime, relative to the directory the application content is
materialized into in the execution image (§4). A
participant.<N>.configuration.path ([CSIL] §6.5) is
resolved against that same root, so a configuration file stored in the
archive as SilKitConfig.yaml is declared as
SilKitConfig.yaml.
A consumer MUST preserve archive entry paths, permissions and the executable bit when materializing content into an execution image. Model content routinely includes executables that the runtime's argument vector names.
An OCI execution layer is implemented as a container image that combines a runtime image and an application artifact. It uses the runtime image as its base and adds the application files.
The runtime prologue ([CSIL] §3.2) is carried by the image's
Entrypoint. It is baked into the runtime image and
inherited by the execution image through the base image, so it never
appears in metadata. A runtime image MUST set an Entrypoint
that prepares the environment and defines a contract for its argument
vector.
A hook is invoked by supplying its effective vector as the
container's arguments, which the Entrypoint then
executes.
An execution image MUST set Cmd to its composed
start-node vector, one array element per argv element.
Tooling MUST invoke non-start-node hooks by overriding the
arguments only.
Tooling MUST NOT override the image's Entrypoint (e.g.,
with Docker's --entrypoint) to invoke a hook. Doing so
replaces the prologue, so the hook runs without the prepared
environment. Only the container's arguments (Cmd) MAY be
set.
CmdCmd is passed to the container verbatim; the container
engine performs no variable expansion on it. An effective vector
containing ${NAME} references ([CSIL] §3.5) is
therefore recorded, unexpanded, in both the image's labels and its
default Cmd.
The OCI backend additionally supports the following container-specific requirement keys.
Key prefix: csil.runtime.requires
OCI runtime images that require file system mounts declare them as indexed entries:
| Key | Description |
|---|---|
csil.runtime.requires.mount.<N>.<segment> |
Mount description |
<N> forms a contiguous zero-based sequence as
defined in [CSIL] §6.1.4.
The <segment> keys are provided as following:
| Segment | Description |
|---|---|
type |
Mount type (see below). |
destination |
In-container mount path. |
options |
Comma-separated mount options (type-dependent). |
Orchestration MUST support the tmpfs mount type,
translating each tmpfs entry to the corresponding container
engine flag (e.g., a Docker --tmpfs mount).
Example:
csil.runtime.requires.mount.0.type=tmpfs
csil.runtime.requires.mount.0.destination=/dev/shm
csil.runtime.requires.mount.0.options=rw,nosuid,nodev,exec,size=256mA node's declared data directories ([CSIL] §6.9) are backed by volumes.
For each data directory csil.data.<N>.path,
tooling MUST mount a volume into the node's container at that path
before the container is started. The volume MUST outlive that
container.
A data directory MUST be an absolute path in the container's own OS form.
Tooling MUST NOT reject an image or artifact solely because its name does not follow a specific convention, and MUST base all validation on metadata (§1.1, [CSIL] §6).
A reference under this profile takes one of the forms:
<registry>/<repository>:<tag>
<registry>/<repository>@<digest>
<registry>/<repository>A digest-qualified reference identifies exactly one immutable manifest and is the form producers SHOULD use wherever reproducibility matters.
A tag-qualified reference identifies whatever manifest the tag currently points to. Tags are mutable: the content behind a tag can change without the reference changing. Consumers that require reproducibility MUST resolve a tag to a digest and record the digest.
A reference with no tag and no digest MUST be
resolved as the tag latest, per established OCI registry
convention. A consumer MUST NOT invent a different default.