Repository navigation
acq: make oci-engine an opt-in catalog kit and retire the adapter OCI auto-install #505
Description
Context
Per ADR-0030, rootless-podman OCI provisioning moves OUT of the msb backend adapter and INTO a neutral patterns kit (integrations/isolation/acq-kits/oci-engine/). The kit shipped in agentic-coding-patterns v1.10.0 (2dd2ad6ad5f63b842d732424bed5ba9aebeed676) and declares schemaVersion: "hybrid/v1", kind: mixin, name: oci-engine. This issue tracks the quickstart-side work to complete that migration.
Rewritten 2026-10-02. The original plan called for a bespoke
ACQ_ENABLE_OCI_KITenvironment gate. That design is superseded: #500 landed the generic opt-in kit catalog andacq configure, sooci-engineshould be a catalog entry like any other kit — not a special case acq knows about by name. See "Superseded design" below for what NOT to build.
Blocked on / sequencing
Blocked on #474 merging. main currently pins
agentic-coding-patterns v1.9.0 (6c6753c60a2b24322fb2e8c0d8e8af60c56ede8f), whose
integrations/isolation/acq-kits/ contains only openchamber and paseo — there is no
oci-engine directory at that ref. The kit first appears in v1.10.0
(2dd2ad6ad5f63b842d732424bed5ba9aebeed676), and the pin bump to v1.10.0 lands in
#474.
This matters because _acq_apply_configured_extra_kits validates a configured kit name only
against the local ACQ_OPTIN_KIT_NAMES array; it does not verify the name resolves at the
pinned ref. So adding oci-engine to the catalog while the pin is still v1.9.0 would let
acq configure offer a kit whose ref points at a non-existent directory, and the adapter's
kit fetch (_acq_msb_fetch_kit) aborts the create with exit 1 rather than skipping it.
Trigger to start this work: #474 merges (carrying the
v1.10.0 pin). Do not add the catalog entry on a branch cut from a v1.9.0-pinned main, and do
not duplicate the pin bump here — it would conflict with #474.
Within this issue, keep step 1 (add the catalog entry) before step 2 (retire the adapter
auto-install) so there is never a window where neither path provides an OCI engine.
Current state
acq.backends/msb.shauto-installs podman itself:_acq_msb_ensure_oci(≈200 lines) is called fromacq_backend_provision, gated onACQ_MSB_ENSURE_OCI, which defaults to on._acq_msb_grant_oci_devsre-grants/dev/net/tun+/dev/fuseon provision AND onacq_backend_start(/devis a devtmpfs re-created each boot).sbxhas no OCI path at all, so the two backends are asymmetric today: msb sandboxes get podman unasked, sbx sandboxes never do.- The generic opt-in machinery now exists on
main(ADR-0031):ACQ_OPTIN_KIT_NAMES/ACQ_OPTIN_KIT_DESCS,_acq_optin_kit_ref,_acq_apply_configured_extra_kits(readsconfig.yamlextra_kits:),acq_configure, andcreate_time_kit_picker. Catalog selections flow throughACQ_EXTRA_KITS→_build_kit_list→acq_cli_kits_write, so they are durable across shells and re-applied on resume.
Work
1. Add oci-engine to the opt-in kit catalog
Append oci-engine to ACQ_OPTIN_KIT_NAMES and a matching one-line description to ACQ_OPTIN_KIT_DESCS in acq.backends/common.sh. That is the whole selection mechanism — no OCI-specific ref builder, readiness probe, cache, or env gate. The kit becomes selectable via a single keystroke in acq configure and in the create-time picker, and is persisted per-sandbox by the existing kit-record path.
Requires the built-in patterns pin to be at v1.10.0 or later, since _acq_optin_kit_ref builds refs from PATTERNS_KIT_REF and _acq_apply_configured_extra_kits fails closed on a kit name that does not resolve. The pin bump lands in #474.
Per the catalog-membership rule recorded in ADR-0031 (prime-agent is omitted because it is a skeleton at the current pin), a catalog entry must be functional at the pinned ref. oci-engine at v1.10.0 qualifies.
2. Retire the adapter's OCI auto-install (BREAKING)
Remove _acq_msb_ensure_oci, _acq_msb_grant_oci_devs, ACQ_MSB_ENSURE_OCI, ACQ_MSB_PODMAN_PKGS, and all call sites (provision, acq_backend_start, and the prereq/diagnostic paths that reference them). The kit performs install + device-grant declaratively, for both backends.
This is a breaking change and must be committed as such (feat!: or a BREAKING CHANGE: footer) with a release note: an OCI engine is no longer provisioned by default. Users who rely on docker run / docker compose inside a sandbox must opt into the oci-engine kit via acq configure (or ACQ_EXTRA_KITS / --kit).
Two behavioral notes that make the kit the better path, worth stating in the commit body:
- The kit's startup script is gated on podman being present and revokes a stale device grant when the engine is absent. The adapter's
_acq_msb_grant_oci_devsgrants/dev/net/tun+/dev/fuseunconditionally, so retiring it narrows device exposure. - The kit applies identically on sbx and msb, ending the current asymmetry.
Sequence step 2 after step 1 so there is never a window with neither path active.
3. Update scripts/verify-backends
_verify_oci_checks currently proves acq's auto-installed podman on msb. Repoint it at the kit path (provision with the oci-engine kit selected) and drop the ACQ_MSB_ENSURE_OCI skip branch once the auto-install is gone. The patterns kit's own scripts/verify already exercises the kit on msb via acq.
4. Docs
docs/BACKEND_GUIDE.md— remove theACQ_MSB_ENSURE_OCI/ACQ_MSB_PODMAN_PKGSenv rows and rewrite the "Running OCI images inside the sandbox (podman)" section around the opt-in kit.docs/CONCEPTS.md— describe OCI as a catalog kit chosen throughacq configure, not as an advanced env-var toggle.- ADR-0020 (
msb oci engine via podman) — supersede it, or add a status note pointing at ADR-0030 and ADR-0031: the podman-over-dind and rootless decisions still hold, but the mechanism and the default both changed.
Verification
acq configureoffersoci-engine; selecting it writesextra_kits:toconfig.yaml; a subsequentacq createapplies the kit;acq startin a fresh shell still re-applies it (the round-trip throughacq_cli_kits_write/acq_cli_kits_load).- With the kit not selected, no podman is installed and no device grant occurs on either backend.
- With the kit selected on msb: rootless podman works,
dockerresolves to podman, and/dev/net/tun+/dev/fuseare group-scoped to the agent after a stop/start cycle. - The patterns kit's own
scripts/verifypasses against a build of acq with the auto-install removed. - Full offline suite green.
ACQ_BUILTIN_KIT_COUNTassertions should remain at 4 — a catalog kit lands after the built-in block and must not inflate the built-in count.
Superseded design — do NOT build this
The original version of this issue specified a bespoke ACQ_ENABLE_OCI_KIT env gate with a dedicated _acq_oci_engine_kit_ref, an acq_oci_engine_kit_ready validation probe, a readiness cache, a "kit not available, falling back to adapter" notice, and an adapter-side _oci_kit_selected suppression flag keyed on the exact OCI kit ref. That makes oci-engine a kit acq knows about by name, which is the opposite of the goal. The generic catalog supersedes it on every axis — discoverability, durability across shells, resume behavior, per-sandbox deviation, and lines of code in acq.
Refs
- ADR-0030 (agent kits on a devenv base) — the decision moving OCI provisioning into a kit
- ADR-0031 (interactive
acq configure) — the generic opt-in catalog this now rides on - ADR-0020 (msb OCI engine via podman) — the adapter-era decision to supersede
- agentic-coding-patterns
integrations/isolation/acq-kits/oci-engine/and itsdocs/decisions/
Activity
[-]acq(msb): wire the opt-in oci-engine kit and retire the adapter's OCI auto-install (ADR-0030)[/-][+]acq: make oci-engine an opt-in catalog kit and retire the adapter OCI auto-install[/+]mogul commentedon Oct 2, 2026
Added a Blocked on / sequencing section: this work cannot start until #474 merges.
Measured reason —
mainpins patterns v1.9.0 (6c6753c), andoci-enginedoes not exist at that ref:_acq_apply_configured_extra_kitsvalidates a configured kit name only against the localACQ_OPTIN_KIT_NAMESarray — it does not check that the name resolves at the pinned ref. So adding the catalog entry on a v1.9.0-pinnedmainwould letacq configureoffer a kit whose ref points at a non-existent directory, and_acq_msb_fetch_kitaborts the create withexit 1rather than skipping it.The v1.10.0 pin bump lands in #474, so that PR is the unblock trigger. Do not duplicate the pin bump here.
AI-assisted (OpenCode).
basiliskus commentedon Oct 3, 2026
Note while reviewing #474: the whitespace-normalization bug moved here
Recording this against step 2 so it is not lost if the retirement slips. No action needed if step 2 lands as written — the parser below is inside the code this issue deletes, so fixing it separately would be wasted work.
While reviewing #474 I found that
ACQ_ENABLE_OCI_KITenabled the kit on whitespace-padded off values, because the parser lowercased without trimming and treated everything outside an exact off-list as on.4721affresolved that by deleting the bespoke gate entirely (the right fix). But the identical parser still exists for the adapter toggle this issue retires, atacq.backends/msb.sh:584-588:Measured:
The difference from the
ACQ_ENABLE_OCI_KITcase is the default. This one defaults to1, so it is an opt-out: a padded value preserves the default rather than flipping behaviour, which is milder. But the thing it fails to disable is a provisioning step that installs packages and grants/dev/net/tun+/dev/fuse, so a user who believes they opted out of podman still gets it — and per this issue's own note, the adapter grants those devices unconditionally where the kit gates on podman being present.Pre-existing, not introduced by #474: the block is unchanged at that PR's merge-base (
410f332), and #474's diff does not touch it. I did not raise it as a finding there for that reason.Two paths from here, whichever suits the sequencing:
ACQ_MSB_ENSURE_OCIoutright, and this note becomes moot. Nothing to do.tr -d '[:space:]'after the lowercase, or an explicit on-list) closes it in the meantime.Worth noting the same shape may exist on other toggles normalized this way —
ACQ_MSB_PODMAN_PKGSis a value not a boolean, but it would be worth a grep for the""|0|false|no|off)idiom elsewhere when this is picked up, rather than fixing one instance.Context: #474 is approved and the
oci-enginekit is not inACQ_OPTIN_KIT_NAMESyet (openchamber paseoat5b2404a), so after that merges the adapter auto-install remains the only route to OCI. That is the window this issue closes.test(kits): align oci catalog checks with v1.11.0
ci(markdown): switch to auditable markdownlint cli
feat(msb)!: retire oci adapter auto-install
test(kits): align oci catalog checks with v1.11.0
ci(markdown): switch to auditable markdownlint cli
feat(msb)!: retire oci adapter auto-install
test(kits): align oci catalog checks with v1.11.0