Loading flows from an OCI registry
Release flows and resources as an OCI artifact and let flowgen load them: a flow with oci_sync pulls the artifact and writes every flow and resource into the system cache, where the runtime picks them up. Promoting or rolling back a release is moving a tag.
Artifact layout
One artifact carries both directories side by side:
artifact
├── flows/
│ └── orders/sync.yaml
└── resources/
└── scripts/transform.rhai Push it with oci_push, oras push, or a Docker image.
Packaging with Docker
Every file in the image’s final filesystem is emitted as an event, so the final stage of the Dockerfile must be FROM scratch. A base image such as busybox or alpine adds its own /bin, /lib and /etc files, which arrive as binary events and break tasks that expect JSON.
FROM scratch
COPY flows/ /flows/
COPY resources/ /resources/ docker build -t registry.example.com/team/configs:prod .
docker push registry.example.com/team/configs:prod If your CI requires a base image for provenance scanning, use a multi-stage build and keep the final stage FROM scratch:
FROM your-registry/base:latest AS source
COPY flows/ /source/flows/
COPY resources/ /source/resources/
FROM scratch
COPY /source/ / To check the layer before pushing, list its entries; every entry should be under flows/ or resources/:
docker save registry.example.com/team/configs:prod -o configs.tar
mkdir -p /tmp/configs && tar -xf configs.tar -C /tmp/configs
tar -tzf /tmp/configs/blobs/sha256/* # or /tmp/configs/*/layer.tar on older Docker Flow
examples/oci/sync_workspace.yaml mirrors the artifact into the system cache:
- A
bufferwithflush_on_completioncollects each pull into one batch. - A script routes each file by its top-level directory.
flows/*are keyed by the path with theflows/prefix and the file extension stripped, soflows/orders/sync.yamlbecomesorders/sync, matching the flow’s path-based identity.resources/*are keyed by the path with theresources/prefix stripped. Any other file is dropped. - Two
nats_kv_storeentry puts withprune: truewrite new and changed keys and delete the keys the artifact no longer holds.
A batch cut short by the buffer timeout is skipped, and the flows put leaves allow_empty off, so a partial or empty pull deletes no flows.
When the manifest digest has not changed, oci_sync skips the layer fetch, so a tick without changes costs one manifest HEAD. See Resources for how the runtime reads back from resources.*.