Add bbox agents import IMAGE (BYO agents Phase 5)#216
Open
JAORMX wants to merge 2 commits into
Open
Conversation
Self-describing OCI images: bbox agents import IMAGE extracts an embedded agent manifest declared by the org.stacklok.broodbox.agent config label (or the well-known /usr/share/broodbox/agent.yaml path) and registers a runnable agent with no local YAML authoring. - internal/infra/ocimage: Fetcher interface + RemoteFetcher using go-containerregistry. Walks layers topmost-first, extracts the manifest file, pins the ref to the resolved digest. Default keychain auth (docker login) + 60s context timeout. 401/403/404 error enrichment with actionable hints. - cmd/bbox/agents_import.go: overload import to accept a file OR an image ref. isImageRef heuristic requires / or @sha256: so a typo'd local path does not trigger a network pull. --name overrides the manifest's name. Warns only on real repo mismatch (same-repo tag->digest pin is silent). Reuses the validate->UpsertAgent-> receipt tail shared with the file path. - Tests: hermetic extractFromImage tests via in-memory v1.Image fixtures (no network); e2e CLI tests via an injected fake Fetcher; enrichRemoteError unit tests; edge cases (fetcher error, no image field, --name to builtin, collision, malformed manifest). - Docs: run-image.md and USER_GUIDE.md document the self-describing image convention and minimum image contract. Doctor image-pullability is deferred (would break doctor's pure/ offline invariant); signed manifests and multi-arch selection beyond the host platform are out of scope.
Panel-review second-pass fixes: - Reject foreign-layer URLs (CWE-918): readLayerPath now checks partial.Descriptor for an OCI foreign-blob urls field and refuses the layer before any I/O. Foreign URLs are fetched on the host outside the sandbox egress policy and are vulnerable to DNS rebinding to cloud metadata. The agent manifest has no legitimate reason to be a foreign layer. - Normalize tar entry names: cleanTarPath strips a leading / and ./ from both the target path and tar entry names before comparison, so entries emitted by docker/podman build (./usr/share/...) match an absolute target (/usr/share/...). Previously these were silently skipped, producing a false 'manifest not found'. - RemoteFetcher doc: clarify the zero value pulls anonymously; use NewRemoteFetcher for host credentials. - Tests: foreign-layer rejection (mock layer panics on Uncompressed, proving no I/O), leading-./ and relative tar entry matches, keychain-wiring assertion, cancelled-context timeout test, isImageRef ../ and absolute-path rows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements #204 — BYO agents Phase 5: self-describing OCI images.
bbox agents import IMAGEextracts an embedded agent manifest from an OCI image and registers a runnable agent with no local YAML authoring.An image ships its agent definition either at a path named by the
org.stacklok.broodbox.agentconfig label, or at the well-known fallback path/usr/share/broodbox/agent.yaml. The imported ref is pinned to its resolved digest so the registered agent is reproducible.What changed
internal/infra/ocimage/(new) —Fetcherinterface +RemoteFetcherusing go-containerregistry. Pulls the image config, reads the label (or falls back to the well-known path), walks layers topmost-first, and extracts the manifest file. Pins the ref torepo@sha256:.... Default keychain auth (docker login/podman logincreds work out of the box) + a 60s context timeout so a slow/malicious registry cannot hang the CLI. 401/403/404 errors are enriched with actionable hints. Lives ininternal/infra/(I/O layer) per the DDD rules; imports no domain code.cmd/bbox/agents_import.go— overloadsimportto accept a local manifest file or an OCI image ref, auto-detected.isImageRefrequires a/or@sha256:so a typo'd local path (aidr.yaml) does not trigger a network pull against docker.io.--nameoverrides the manifest's name. Warns only on a real repository mismatch (same-repo tag→digest pin is silent). Reuses the exact validate → collision-gate →UpsertAgent→ receipt tail the file path uses, so behavior, receipts, and safety match.docs/run-image.mdanddocs/USER_GUIDE.mddocument the self-describing image convention (label + well-known path, topmost-layer-wins, digest pinning) and the minimum image contract.Design decisions (resolving the issue's open questions)
import IMAGElive-pulls just the manifest (config + the one layer holding the manifest file). The full image is pulled at VM run time by the go-microvm runner.importonly needs the manifest bytes + digest.runDoctoris pure/offline (env + imageValidator injected); adding network I/O would break that invariant. A future--pullflag or separate command is the right home.Tests
Hermetic — no network in CI:
extractFromImageunit tests via in-memoryv1.Imagefixtures (built withcrane.Layer+mutate.AppendLayers+mutate.ConfigFile): label present, label absent→well-known path, neither→error, non-topmost layer, topmost shadows base, malformed bytes, empty/whitespace label, label to missing file.pinnedDigestRefnormalization (tag stripped, repo preserved).enrichRemoteErrortable test (401/403/404/non-transport).isImageReftable test (file vs image vs typo'd path vs docker hub short ref).Fetcher: image import → digest-pinned image in config + receipt OK + doctor passes + round-trips through loader; manifest declares different repo → warning + override; same-repo tag → no warning;--nameoverride;--nameto a builtin → rejected; collision with existing custom agent (without/with--force); noimagefield → succeeds silently; fetcher error → CLI surfaces the ref; malformed manifest → parse error.Verification
Out of scope
agents doctorimage-pullability check (deferred — would break doctor's pure/offline invariant).remote.Image's host-platform default.run-imagereading an embedded manifest (run-image stays flag-driven).Closes #204.