On most factory floors, the artifact problem is actually two problems running in parallel. The application containers — the inference runtime, the data bridge, the protocol adapter — live in a container registry with proper tags and manifests. The model weights that make those containers useful live somewhere else entirely: a shared NFS mount, a notebook server’s /models directory, a USB stick that someone labelled with a marker. When Line 7’s anomaly detector misbehaves at 2 a.m., the container is reproducible. The model that feeds it is not.
This post is about collapsing those two problems into one. The answer is an OCI registry that speaks the same distribution protocol for both artifacts, a lifecycle discipline called GitOps that governs what version of each artifact belongs on each device, and KitOps ModelKit that gives model weights the same first-class OCI treatment that container images have had for a decade.
The single-lifecycle property this post generalizes is what makes the worked example’s recurring column read ~€90 over three years — power only, because nothing on the box carries a per-cycle license (FTPFI Part 5).
Part 2 established the Margo pull model — the device subscribes to a desired-state specification, and the orchestration layer never needs to push to it. The same pull principle applies here, one layer deeper: the device declares which model version it wants, and the registry delivers it over the same port-443 channel, signed and versioned like any other OCI artifact.
The evolution arc: from scattered artifacts to a single source of truth#
The “before” state has a recognizable shape. Application images are versioned (inference-runtime:1.0.1), but the GGUF weights that live beside them carry whatever filename the data-science team assigned last time they exported from training (motor_anomaly_q4_v7_FINAL_USE_THIS.gguf). The application image is pulled from a registry with a digest. The model file is rsynced from a build server, or copied manually, or arrived on a USB key. Nobody can answer “what exact model is running on station 7 right now” without SSH access.
The “after” state has one registry URL, two artifact types, and a Git repository that describes which version of each belongs on each device. A factory-floor engineer can answer the station-7 question by reading a YAML file. An incident responder can roll back by changing one line and letting the pull reconciliation loop catch up. Andrii’s analysis of the full model-serving stack is in the AI-Native Factory Stack series; this post focuses on the lifecycle layer, not the inference mechanics.
OCI as the distribution substrate#
The Open Container Initiative — an Linux Foundation project governing the image, runtime, and distribution specifications — defines the wire protocol that any OCI-compatible registry speaks. Container images use it. Helm charts use it. Supply-chain attestations and SBOMs use it. ModelKit uses it.
That matters operationally. A single self-hosted registry (Harbor, Zot, or a cloud registry with OCI artifact support) can hold every artifact type the edge device needs. The pull client does not care whether it is pulling a container image or a model bundle; the distribution spec is identical. Air-gapped networks gain a single egress point to mirror. Audit logs gain a unified manifest trail. Security scanning tools that speak OCI can scan model artifacts with the same pipeline they use for container images.
KitOps and ModelKit#
KitOps is the enterprise implementation of the CNCF ModelPack specification. ModelPack was accepted to CNCF Sandbox in 2025, and KitOps is its production toolchain. A ModelKit is an OCI artifact — specifically an OCI image with a manifest that separates the model weights, the training datasets, the inference code, and the documentation into distinct, independently-cacheable layers. Pulling a new model version only transfers the layers that changed, not the full weights bundle. On a 100 Mbit/s factory network, that distinction matters.
A minimal Kitfile for an edge anomaly detector looks like this:
manifestVersion: "1.0.0"
package:
name: motor-anomaly-detector
version: 1.2.0
description: Edge anomaly detection for motor vibration analysis
model:
name: motor-anomaly-v1
path: models/
framework: gguf
description: Q4_K_M quantized model for on-box NPU inference
datasets:
- name: vibration-calibration
path: data/calibration/
description: Per-line vibration baselines for threshold tuning
code:
- path: src/
description: Inference wrapper and pre-processing pipeline
docs:
- path: README.md
The kit CLI packs this into an OCI artifact and pushes it to the same registry that holds the application containers:
# On the build host after training
kit pack . -t registry.edge.local/motor-anomaly-detector:1.2.0
kit push registry.edge.local/motor-anomaly-detector:1.2.0
# On the target edge device (or via the Margo pull reconciler)
kit pull registry.edge.local/motor-anomaly-detector:1.2.0
kit unpack registry.edge.local/motor-anomaly-detector:1.2.0 --dir /opt/models/motor-anomaly
The digest returned by kit push is the artifact’s identity. Pin that digest in your GitOps repository and you have an immutable reference to the exact model version on every device that pulls it. No more filename ambiguity.
Connecting GitOps: the pull config#
A GitOps repository holds the desired state for each device or device group. With a Margo-style desired state, both the application container image and the ModelKit it depends on can be expressed through a single registry endpoint. The following is illustrative pseudo-YAML, not literal Margo schema:
# Illustrative pseudo-YAML — not literal Margo schema
apiVersion: margo.org/v1alpha1
kind: ApplicationProfile
metadata:
name: anomaly-detection-line-7
spec:
components:
- name: model-sync
image: "<kitops-cli-image-from-project-registry>"
command: ["kit", "pull",
"registry.edge.local/motor-anomaly-detector@sha256:a3f8...",
"--dir", "/opt/models/motor-anomaly"]
mounts:
- hostPath: /opt/models
containerPath: /opt/models
- name: inference-runtime
image: registry.edge.local/inference-runtime:1.0.1
env:
- name: MODEL_PATH
value: /opt/models/motor-anomaly
mounts:
- hostPath: /opt/models
containerPath: /opt/models
The init component pulls the pinned ModelKit digest before the inference runtime starts. The Margo agent on the device reconciles this manifest against current state on every pull cycle. No SSH. No rsync script. No “did someone update the model on this box?” ambiguity.
MTTR collapse: the operational consequence#
DORA’s research on high-performing engineering teams tracks Mean Time to Restore as one of four key delivery metrics. Elite teams restore service in under one hour; lower-performing teams measure MTTR in hours or days. GitOps’s contribution to that number is specific: when the authoritative state lives in Git and the device self-reconciles toward it, the restore path is a commit. Roll back the manifest, let the pull loop catch up.
Applied to edge model deployments, the same mechanic holds. A model version that causes drift in motor temperature readings can be reverted by pinning the previous digest in the manifest. The DORA evidence (via Atlassian’s DevOps framework reference) shows that GitOps-disciplined teams can drive MTTR from hours toward under an hour for high-performing configurations. That is a directional claim, not a guarantee — actual time depends on device network latency, pull-loop frequency, and reconciliation implementation. But the mechanism is sound: immutable artifacts plus a declarative desired state equal a deterministic restore path.
Why agents require this#
The agentic layer — covered in Part 4 — depends on model reproducibility as a precondition. An agent harness that governs autonomous decisions at the edge is only auditable if it can answer “which model version made this inference?” at the time of the decision. A ModelKit digest in a GitOps manifest gives you that answer without instrumentation overhead. The model version is declared, pinned, and logged through the same reconciliation cycle as the application code it runs beside.
This is the link between the lifecycle layer (this post) and the inference governance layer (Part 4). The OCI registry is not just a distribution convenience — it is the substrate on which reproducible agents become possible. The hub post, The Open Standards Stack of the Software-Defined Edge, maps all nineteen standards layers that connect at this junction.
The single-registry principle#
One registry, governed by OCI, carrying container images and ModelKit artifacts under the same signed manifest discipline, managed by a GitOps desired-state loop that every device polls rather than being pushed to — this is what the lifecycle layer of a software-defined edge looks like when it is working.
The device at Line 7 does not care whether the next pull delivers a patched container image or a new model version. It pulls what the manifest says, verifies the digest, and reconciles. That predictability is the gap between a factory floor that runs pilots and one that runs production.
Leadership companion. The design-value argument behind choosing sovereign, boring infrastructure over managed orchestration is explored in The Year I Stopped Trusting the Orchestrator on javatask.systems.
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.