Problem Statement
openshell sandbox create --from currently accepts three kinds of input:
- A Dockerfile (or directory containing one) — built into the local Docker daemon, tag passed to the gateway
- A fully-qualified registry image reference — gateway's compute driver pulls it at sandbox creation time
- A community sandbox shorthand — expanded to a registry reference, same pull path
There is no path for a pre-built Docker archive (the .tar format produced by docker save or buildah push --format docker docker-archive:).
This matters for air-gapped environments and macOS VM-based build pipelines where a Docker archive is the natural hand-off artifact. Today, passing --from ./output.tar hits the error "local --from file is not a Dockerfile" because the existing file branch only recognises Dockerfile names.
Proposed Design
openshell sandbox create --from ./output.tar # Docker archive (.tar)
openshell sandbox create --from ./output.tar.gz # gzip-compressed Docker archive
How it fits the existing architecture
The Dockerfile path already establishes the right pattern to follow:
- CLI detects the local file, pre-processes it (Dockerfile → docker build; archive → docker load), and gets back a local image tag.
- CLI passes that tag as spec.image in CreateSandboxRequest — identical to a registry reference from the gateway's perspective.
- Gateway creates the sandbox using the tag, found in the shared local daemon. No new proto RPC is needed.
For a Docker archive the pre-processing step is:
- CLI detects .tar / .tar.gz extension → ResolvedSource::DockerArchive { path }.
- CLI calls the Docker daemon's POST /images/load via Bollard (the library already in use for Dockerfile builds), streaming the archive bytes.
- Docker loads the image and returns its embedded tag from manifest.json.
- CLI passes that tag to CreateSandboxRequest.spec.image.
This is the minimum-surface change: the gateway and proto are untouched; only the CLI gains a new ResolvedSource variant and a new pre-processing helper analogous to build_local_image.
Constraints
- Local-gateway only — same guard as Dockerfile sources (dockerfile_sources_supported_for_gateway). The CLI and gateway must share the same Docker daemon for the loaded tag to be resolvable.
- Extension-based detection — .tar and .tar.gz suffixes, consistent with how filename_looks_like_dockerfile works today. No magic-byte sniffing.
- Tag extraction — the archive's embedded tag (from manifest.json → RepoTags[0]) is used as-is, or re-tagged to openshell/sandbox-from:{timestamp} for consistency with the Dockerfile flow.
Alternatives Considered
- Stream archive bytes to the gateway over a new RPC: more complex, potentially supports remote gateways, but out of scope — remote Dockerfile sources are already unsupported, so there is no demand.
- OCI image layout archive (oci-archive:): a distinct format; out of scope here, can be a follow-up.
- Magic-byte sniffing: more robust but breaks consistency with the existing filename-heuristic approach and adds complexity for no real gain.
Agent Investigation
Traced the full Dockerfile pipeline:
- crates/openshell-cli/src/run.rs — resolve_from() classifies input; build_from_dockerfile() drives Bollard; the resulting tag is passed directly to CreateSandboxRequest.
- crates/openshell-bootstrap/src/build.rs — build_local_image() / build_image() use Bollard's build_image() with a tar-encoded context body. The archive load path (POST /images/load) is a parallel Bollard call with no build options — simpler than the build path.
- proto/openshell.proto — CreateSandboxRequest carries only SandboxSpec.image: string. No changes required.
- crates/openshell-server/ — gateway receives a tag and hands it to the active compute driver. No changes required.
Files likely affected:
- crates/openshell-cli/src/run.rs — resolve_from(), ResolvedSource, sandbox_create()
- crates/openshell-bootstrap/src/build.rs — new load_local_archive() helper (analogous to build_local_image())
- architecture/ — CLI doc update
The key design insight the investigation confirmed: no new proto RPC is needed. Bollard's POST /images/load loads the archive into the local daemon exactly like Dockerfile builds do, and the embedded tag from manifest.json passes through to the gateway as a plain image reference.
Checklist
Problem Statement
openshell sandbox create --fromcurrently accepts three kinds of input:There is no path for a pre-built Docker archive (the
.tarformat produced bydocker saveorbuildah push --format docker docker-archive:).This matters for air-gapped environments and macOS VM-based build pipelines where a Docker archive is the natural hand-off artifact. Today, passing
--from ./output.tarhits the error "local --from file is not a Dockerfile" because the existing file branch only recognises Dockerfile names.Proposed Design
How it fits the existing architecture
The Dockerfile path already establishes the right pattern to follow:
For a Docker archive the pre-processing step is:
This is the minimum-surface change: the gateway and proto are untouched; only the CLI gains a new ResolvedSource variant and a new pre-processing helper analogous to build_local_image.
Constraints
Alternatives Considered
Agent Investigation
Traced the full Dockerfile pipeline:
Files likely affected:
The key design insight the investigation confirmed: no new proto RPC is needed. Bollard's
POST /images/loadloads the archive into the local daemon exactly like Dockerfile builds do, and the embedded tag frommanifest.jsonpasses through to the gateway as a plain image reference.Checklist