-
Notifications
You must be signed in to change notification settings - Fork 0
CLI Reference
secure-oci is the primary interface for the complete local lifecycle:
| Goal | Recommended command |
|---|---|
| identify an input | secure-oci detect PATH |
| build one image layout | secure-oci image build [OPTIONS] EXECUTABLE |
| compose images, tags, or platforms | secure-oci image compose -o OUTPUT LAYOUT... |
| inspect or verify a layout |
secure-oci image inspect LAYOUT / secure-oci image verify LAYOUT
|
| run with Docker or Podman | secure-oci container run [OPTIONS] IMAGE |
| choose container or microVM | secure-oci launch --isolation=... |
| administer native/KubeVirt microVMs | secure-oci vm ACTION [OPTIONS] |
| publish, sign, and attest | secure-oci publish [OPTIONS] LAYOUT IMAGE |
| deploy an immutable image | secure-oci deploy [OPTIONS] IMAGE@sha256:... |
| rollback Kubernetes | secure-oci rollback [OPTIONS] DEPLOYMENT |
The short forms build, compose, inspect, verify, run, and microvm
remain fully supported. Run secure-oci or secure-oci help for the command
map, secure-oci COMMAND --help for flags, and secure-oci version for the
binary version.
secure-oci image build -o ./oci-image \
--platform linux/amd64 \
--image example/service --tag v1 \
--entrypoint /app/service \
--label security.mtls=required \
--config ./secure-oci.json \
./service| Flag | Default | Meaning |
|---|---|---|
-o, --output
|
oci-image |
new, atomically installed layout |
--platform |
empty | convenient linux/amd64 or linux/arm64 selector |
--arch, --os
|
amd64, linux
|
separate platform fields |
--image, --tag
|
platform-factory, latest` |
local OCI reference annotation |
--entrypoint |
/app/<input name> |
executable path inside the image |
--profile |
detected | static, glibc, musl, python, node, java, or dotnet |
--created |
Unix epoch | RFC3339 creation timestamp |
--compression |
best |
deterministic best or large-image fast gzip |
--label |
none | repeatable key=value image label |
--extra-file |
none | repeatable /container/path=host/path payload |
--config |
none | strict JSON runtime configuration |
--format |
json |
machine-readable json or concise text result |
--platform overrides the separate --arch and --os values. The command
prints a stable JSON result containing the layout, manifest digest, platform,
profile, image reference, and validation status. Set SECURE_OCI_TRACE_ID to
correlate the build with CI or deployment evidence.
secure-oci image compose -o ./catalog \
./api-amd64 ./api-arm64 ./worker-amd64
secure-oci image verify ./catalogThis creates a deterministic, verified index supporting multiple architectures, tags, and applications while deduplicating blobs by digest.
The composition step is also integrated directly into build:
secure-oci build -o service-multi \
--image example/service --tag v1 \
--platform linux/amd64=./service-amd64 \
--platform linux/arm64=./service-arm64Each executable is independently detected and built for its declared platform. The verified intermediate layouts are composed atomically; temporary layouts are removed whether the operation succeeds or fails.
secure-oci publish --dry-run --sign --sbom \
--provenance provenance.json --policy policy.json --evidence evidence.json \
service-multi ghcr.io/example/service:v1
secure-oci publish --yes --sign --sbom \
--provenance provenance.json --policy policy.json --evidence evidence.json \
service-multi ghcr.io/example/service:v1Use --format reference to print only the immutable registry reference:
IMAGE_REF=$(secure-oci publish --yes --sign --sbom --format reference \
--provenance provenance.json --policy policy.json --evidence evidence.json \
service-multi ghcr.io/example/service:v1)
secure-oci deploy --name service "$IMAGE_REF"The source layout is verified before any registry operation. The native Distribution client uploads blobs and installs the manifest by digest; native SBOM, provenance and Ed25519 evidence are linked to that immutable subject before policy permits the mutable tag to move. Upload sessions persist across process crashes and reconcile their offset with the registry.
A catalog containing several image names requires --source-ref. Production
publication is fail-closed: --sbom, --sign, provenance (--provenance or
--journal), --policy and --evidence are required. Registry mutation also
requires --yes; --dry-run previews the native operations.
For a configured project, the production shortcut performs dependency freeze, two digest-identical builds, native evidence publication and the configured run:
secure-oci launch --publish --yesThe project image must include its registry. --publish=HOST:CONTAINER
remains the port-forwarding form and does not trigger Registry publication.
secure-oci deploy --dry-run --name api --namespace production \
ghcr.io/example/api@sha256:<64 lowercase hexadecimal characters>
secure-oci deploy --name api --namespace production \
ghcr.io/example/api@sha256:<digest>
secure-oci rollback --dry-run --namespace production api
secure-oci rollback --yes --namespace production --to-revision 2 apiDeployments require an immutable SHA-256 image reference. Generated workloads
run as non-root with RuntimeDefault seccomp, a read-only root filesystem,
privilege escalation disabled, and every Linux capability dropped. The CLI
applies JSON directly to kubectl without a shell, then waits for rollout
completion. Rollback requires explicit confirmation and waits for the restored
revision.
source <(secure-oci completion bash)
secure-oci completion zsh > "${fpath[1]}/_secure-oci"
secure-oci completion fish | source
secure-oci completion powershellCompletion scripts are generated without inspecting credentials, registries, or clusters.
run accepts the explicit isolation selector directly, while launch remains
as a backward-compatible alias:
secure-oci run --isolation=container --runtime=podman \
-p 127.0.0.1:8080:8080 \
-p 127.0.0.1:8443:8443 \
ghcr.io/example/api@sha256:<digest>
secure-oci run --isolation=microvm --layout=./api-layout \
-p 127.0.0.1:8080:8080/tcp \
--port 127.0.0.1:5353:53/udpWithout --isolation, run retains the hardened Docker default for backward
compatibility. No heuristic silently turns a container request into a microVM
request; the isolation boundary stays visible and auditable.
-p, --port, and --publish are equivalent repeatable flags in both
container and microVM execution:
secure-oci run --network bridge \
-p 8080:80 \
-p 8443:443/tcp \
--port 127.0.0.1:5353:53/udp \
"$IMAGE_REF"Accepted forms are:
-
PORT— identical host and guest/container port; -
HOST:GUEST; -
IP:HOST:GUEST, including a bracketed IPv6 address; - any form suffixed with
/tcpor/udp(TCP by default).
The historical microVM form --port 8080 remains valid and now simply means
one forwarding whose host and guest ports are both 8080.
Pass a local OCI layout in place of the image reference:
secure-oci run --runtime podman --network bridge \
-p 127.0.0.1:8080:8080 \
./oci-imageThe CLI:
- strictly verifies the layout;
- derives its unique
org.opencontainers.image.ref.name; - asks Docker or Podman whether that image already exists;
- when absent, streams an OCI archive directly to
runtime load; - verifies that the expected image reference is now available;
- starts the hardened container.
The archive is never materialized on disk and memory usage does not grow with the image size. If the layout contains several image names, selection must be explicit:
secure-oci run --runtime docker --layout ./catalog \
--network bridge -p 8080:8080 example/service:v1This automatic path applies only when a local layout is supplied. For a registry reference, Docker or Podman retains its normal pull-if-missing behavior.
# 1. Build your service statically - CGO must be disabled, or the binary
# won't be a static ELF and CI's verify-elf.sh will reject it.
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o service ./cmd/your-service
# 2. Turn it into an OCI layout.
go run ./cmd/oci-builder -binary ./service -output ./oci-image -arch amd64 \
-label security.tls.minimum=1.2 -label security.mtls=requiredRunning go run ./cmd/oci-builder with no arguments prints the full
flag reference and exits 0 (it does not treat a no-args invocation as an
error — that's what -h is for too).
| Flag | Default | Meaning |
|---|---|---|
-binary |
(required) | path to the statically compiled executable to package |
-output |
(required) | path for the new OCI layout directory - must not already exist |
-arch |
host architecture |
amd64 or arm64; any other value is rejected |
-os |
linux |
OCI operating system field; any value other than linux is rejected |
-entrypoint |
/app/service |
absolute, clean container path for the binary inside the image |
-image |
`platform-factory | image name used in the org.opencontainers.image.ref.name annotation |
-tag |
latest |
tag used in that same annotation |
-created |
1970-01-01T00:00:00Z |
RFC3339 timestamp written into the OCI config's created field (kept fixed for reproducibility unless you have a reason to change it) |
-compression |
best |
deterministic gzip mode: best or fast; use fast for very large payloads |
-label |
(none) |
key=value image label; repeatable (pass it more than once for multiple labels) |
-extra-file |
(none) |
/container/path=host/path; repeatable. Adds an additional file to the image at a fixed path, alongside the entrypoint - see "Packaging a dynamically-linked binary" below |
The entrypoint doesn't have to be static. -extra-file places any additional file in the layer at a fixed container path, read from a host path at build time - typically the ELF interpreter and every shared library a dynamically-linked binary needs:
go run ./cmd/oci-builder -binary ./legacy -output ./oci-image -arch amd64 -entrypoint /app/legacy \
-extra-file /lib64/ld-linux-x86-64.so.2=/lib64/ld-linux-x86-64.so.2 \
-extra-file /lib/x86_64-linux-gnu/libc.so.6=/lib/x86_64-linux-gnu/libc.so.6Rules, enforced the same way entrypoint validation is:
- each destination must be an absolute, clean container path (same rule as
-entrypoint) - destinations can't collide with each other or with the entrypoint's own path - a duplicate is a build error, not a silent overwrite
- every extra file is written mode
0555(read + execute), matching how real Linux distributions ship shared libraries - this isn't just convention: a binary's ELF interpreter (its dynamic linker, e.g.ld-linux-x86-64.so.2) is loaded directly by the kernel's ownexecve()path, which requires the execute bit; an ordinary shared library isdlopen'd by userspacemmap()and would technically work without it, but there's no reason to withhold it
scripts/local/package-dynamic-binary.sh BINARY OUTPUT [ENTRYPOINT] automates all of this: it runs ldd on the binary, turns every real dependency into an -extra-file (preserving each dependency's own absolute path unchanged, so the guest's dynamic linker finds it exactly where it expects), and invokes cmd/oci-builder. See Scripts Reference.
This is what makes a genuinely legacy binary - one that can't be statically recompiled - work through the Dockerfile consumer or MicroVM Support exactly like any other layout; both read the actual declared entrypoint from the OCI config rather than assuming a fixed path.
| Code | Meaning |
|---|---|
0 |
success (or no-args usage print) |
1 |
build failure (invalid binary, output already exists, I/O error) |
2 |
malformed arguments (bad flag, invalid -created timestamp, invalid -label) |
- Format is strictly
key=value- a barekeywith no=is rejected. - An empty key (
=value) is rejected. -
Duplicate keys are rejected outright - the builder never silently
lets a later
-labeloverwrite an earlier one; you get a build error instead, so a mistake in a build script surfaces immediately rather than silently changing which label value "wins." - Pairs are sorted before being written, so label output order in the
image config is deterministic regardless of the order you passed
-labelflags in on the command line.
$ go run ./cmd/oci-builder -binary ./service -output ./oci-image -arch riscv64
unsupported architecture "riscv64" (supported: amd64, arm64)
$ echo $?
2
$ go run ./cmd/oci-builder -binary ./missing-file -output ./oci-image
oci-builder: stat binary: stat ./missing-file: no such file or directory
$ echo $?
1
See Troubleshooting for the full list of user-facing error messages and their fixes.
On success, the CLI prints the manifest digest and exits 0:
created OCI layout ./oci-image (sha256:e9c41c132f70e6b5067d25aea7cd3a7e44c12b771724c3fe3662e1f06b0fd92c)
That digest is the manifest's own content address — the same digest
index.json's single manifest descriptor points at, and the same one
docker buildx imagetools inspect/scripts/ci/verify-attestation-index.py
key off of after a registry publish (see
Testing and CI/CD).
See Architecture and OCI Layout for what's actually inside the layout this command produces.
secure-oci launch selects container or microVM execution explicitly:
secure-oci launch --isolation=container \
--runtime=podman --network=bridge \
--publish=127.0.0.1:8080:8080/tcp \
ghcr.io/acme/service@sha256:<digest>
secure-oci launch --isolation=microvm \
--layout=oci-image \
--publish=127.0.0.1:8080:8080/tcpContainer network flags include repeated --publish, --dns, and
--add-host, plus --hostname and named networks. --network=host is
rejected unless --allow-host-network is also present. MicroVM publications
support repeated TCP and UDP rules with IPv4 or bracketed IPv6 host
addresses. See Large-image streaming for scaling
limits and the complete network contract.
When a project contains a supported .config_image.* or .config_img.*
manifest, the CLI can operate the complete lifecycle without repeating build
flags:
secure-oci project show
secure-oci freeze
secure-oci project build
secure-oci project runfreeze is a friendly alias for project freeze. Each action accepts
--config FILE or a starting directory. The run action builds a missing
layout, loads it transparently into Docker/Podman for container isolation, or
starts it through the microVM lifecycle. See
Project Configuration and Dependency Freezing
for the schema, language adapters and shared dependency behavior.
© 2026 CYPT71
platform-factory
Core
- Architecture and OCI Layout
- Next-generation Architecture
- Architecture Decision Records
- Security Model
- Threat Model and Residual Risks
- Independent Security Review Process
- CLI Reference
- Project Configuration and Dependency Freezing
- mTLS Configuration
- Meine Graal
CI/CD
Running an image
- Production Adoption Guide
- Dockerfile Consumer
- Local Dev (Podman/macOS)
- MicroVM Support
- MicroVM Administration
- Large-image streaming
Operating