Skip to content

Add apple-container sandbox runtime contract and fail-closed validation - #56122

Draft
lpcox wants to merge 1 commit into
mainfrom
lpcox-apple-container-runtime-contract
Draft

Add apple-container sandbox runtime contract and fail-closed validation#56122
lpcox wants to merge 1 commit into
mainfrom
lpcox-apple-container-runtime-contract

Conversation

@lpcox

@lpcox lpcox commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Apple Container stack — layer 1 (contract & validation)

This is layer 1 of the gh-aw Apple Container stack. It adds the compiler/runtime contract and fail-closed validation for AWF's Apple Virtualization.framework workload preview. It does not yet generate runnable Apple Container setup.

Depends functionally on github/gh-aw-firewall#7764 (stacked on #7760 and #7762), which is not merged or released yet. Nothing in this PR can produce a working run until that lands and ships.

Runtime model

sandbox.agent.runtime: apple-container is now a recognised runtime profile:

Profile field Value Why
NetworkIsolation true AWF requires strict --network-isolation; the guest has no NIC
LegacySecurity false iptables rules govern nothing without a NIC
Rootless true the container CLI is unprivileged
SupportsRuntimeInstall false no generated provisioning in this layer
SupportsHostAccess false only capability sockets cross the VM boundary

Docker is not removed: AWF keeps Squid, the API proxy, the CLI proxy, and the MCP gateway in Docker Compose on the host. Only the agent workload moves to Apple Container.

AWF config generation

  • Emits both required selectors as a unit — container.containerRuntime: "apple-container" and appleContainer.previewEnabled: true. Neither is ever emitted without the other.
  • Suppresses network.topologyAttach. AWF fails closed on any non-empty value here for this runtime (config.topologyAttach && config.topologyAttach.length > 0), because externally owned peers are not published to macOS loopback.
  • Suppresses --tty, matching docker-sbx and cloud-hypervisor.
  • Embedded awf-config.schema.json mirrors the AWF source schema: the apple-container enum value, the appleContainer section (previewEnabled, cpus int ≥1 default 4, memory string ^[1-9][0-9]*[KMGTP]?$ default 8G, initImage digest-pinned, cliPath), and the container.images.appleInit role.

Sizing/init-image/CLI-path are intentionally not exposed as gh-aw frontmatter yet — the compiler emits only the two selectors and lets AWF apply its documented defaults. Adding user-facing knobs before there is a runnable path would be speculative surface area.

Version gating

New AWFAppleContainerMinVersion = v0.28.9, deliberately above DefaultFirewallVersion (v0.28.7).

The latest published AWF release (v0.28.8) still ships a containerRuntime enum of ["gvisor", "sbx", "cloud-hypervisor"] and has no appleContainer section, so v0.28.9 is the earliest release that can contain #7764. The runtime therefore fails closed by default: a workflow must explicitly pin sandbox.agent.version (or firewall.version). Bump the constant if #7764 lands later; never lower it.

Runner acceptance

Accepted syntax, exactly:

runs-on: [self-hosted, macOS, ARM64]

Extra pool labels are allowed (e.g. [self-hosted, macOS, ARM64, apple-container]). Matching is case-insensitive and order-independent.

Rejected, each with an actionable error naming the accepted syntax:

  • every GitHub-hosted macos-* label, including -xlarge Apple Silicon images — those runners are VMs without nested virtualization (kern.hv_support != 1)
  • a runner group with no labels
  • GitHub Actions expressions
  • contradicting arch labels (x64, x86, x86_64, amd64) — Rosetta is refused
  • contradicting OS labels (linux, windows)
  • an omitted runner (defaults to ubuntu-latest)

The blanket macOS rejection stays fully intact for runs-on-slim, safe-output job runners, and every other runtime. The exemption applies only to the agent job's own runs-on when this workflow's own frontmatter selects apple-container, and validateSandboxConfig independently re-validates the merged runner, so imports cannot bypass the host requirement.

Feature compatibility

Rejected at compile time: arc-dind topology, enclaves, sandbox.agent.mounts, filesystem.allowWrite, ssl_bump, Vertex AI credential isolation, plus the raw AWF arguments AWF refuses (--legacy-security, --enable-host-access, --allow-host-ports, --dns-over-https, --topology-attach, --dind, --docker-host-path-prefix, --build-local, --agent-image, --sysroot-image, --chroot-binaries-source-path, --volume, --tty, --ssl-bump, --google-api-key, --no-network-isolation).

allow-host-ports, services: port mappings, and runtime-install are already rejected by the shared profile rules.

The AWF CLI proxy (--difc-proxy-host / tools.github.mode: gh-proxy) is supported under apple-container — it is in AWF's capability-socket allowlist — so unlike cloud-hypervisor it is deliberately not rejected.

Images

appleInit is added to the closed container.images role set and required whenever the runtime is selected. It is added to the image-tag digest contract alongside agent, and excluded from Docker predownload: Apple Container maintains a separate image store that docker pull cannot populate.

Explicitly deferred to layer 2

Apple Container CLI/runtime install steps, runtime-aware image pulling into the Apple image store, MCP gateway transport rewiring onto capability sockets, public documentation, the live smoke workflow, and any broad golden-fixture regeneration.

Validation

  • go test ./pkg/workflow ./pkg/parser ./pkg/constants — pass (86 new subtests)
  • make lint — clean
  • make recompile — 294/294 workflows, no lock file drift
  • End-to-end compile of an apple-container workflow verified: agent job on [self-hosted, macOS, ARM64], framework jobs still on ubuntu-slim, both selectors present, topologyAttach absent
  • End-to-end negative cases verified: macos-26, macos-15-xlarge, runner group only, omitted runs-on, and default AWF version all fail closed

Pre-existing unrelated failures on this machine (/private symlink and chi-square tests in pkg/cli / pkg/fileutil) reproduce on the base commit.

Introduce `sandbox.agent.runtime: apple-container` as a recognised runtime
profile for AWF's Apple Virtualization.framework workload preview
(gh-aw-firewall#7764). This is the contract/validation layer only: it defines
the declarative surface, emits the AWF config selectors, and fails closed on
everything the compiler can know statically. Generated runtime setup, image
provisioning, and MCP transport rewiring are deliberately left out.

Runtime model and profile:

- Add `AgentRuntimeAppleContainer` and its profile: network isolation on,
  rootless AWF invocation, no host access, no compiler-generated runtime
  installation. Docker is still required — AWF keeps Squid, the API proxy, the
  CLI proxy, and the MCP gateway in Docker Compose on the host, and only the
  agent workload moves to Apple Container.
- Add the value to the frontmatter schema, regenerate autocomplete data and the
  generated frontmatter reference, and update the internal agent-runtime
  instructions so tools do not emit an invalid configuration.

AWF config generation:

- Emit BOTH required selectors as a unit: `container.containerRuntime:
  "apple-container"` and `appleContainer.previewEnabled: true`. Neither is ever
  emitted without the other.
- Suppress `network.topologyAttach`, which AWF rejects for this runtime because
  externally owned peers are not published to macOS loopback and cannot be
  bridged into the NIC-less guest.
- Suppress `--tty`, matching the docker-sbx and cloud-hypervisor handling.
- Mirror the AWF source schema in the embedded `awf-config.schema.json`: the
  `apple-container` enum value, the `appleContainer` section (previewEnabled,
  cpus, memory, initImage, cliPath with AWF's exact types, defaults, and
  patterns), and the `container.images.appleInit` role.

Version gating:

- Add `AWFAppleContainerMinVersion` (v0.28.9) and `awfSupportsAppleContainer`.
  The constant is deliberately above `DefaultFirewallVersion` because
  gh-aw-firewall#7764 is not in any published release yet (v0.28.8 still has no
  `apple-container` enum value and no `appleContainer` section), so the runtime
  fails closed until a workflow explicitly pins a supporting AWF version.

Runner validation:

- Accept only an explicitly self-hosted bare-metal Apple Silicon label set:
  `runs-on: [self-hosted, macOS, ARM64]` (extra pool labels allowed).
- Reject every GitHub-hosted `macos-*` label, including the `-xlarge` Apple
  Silicon images, because those runners are virtual machines without nested
  virtualization.
- Reject runner groups without labels, GitHub Actions expressions, contradicting
  OS/arch labels, and an omitted runner, rather than guessing. Every rejection
  names the exact accepted syntax.
- Keep the blanket macOS rejection for every other runner field (`runs-on-slim`,
  safe-output job runners) and for every other runtime. The exemption applies
  only to the agent job's own `runs-on` when this workflow's frontmatter selects
  apple-container, and `validateSandboxConfig` re-validates the merged runner so
  imports cannot bypass the host requirement.

Feature compatibility:

- Reject arc-dind topology, enclaves, `sandbox.agent.mounts`,
  `filesystem.allowWrite`, `ssl_bump`, Vertex AI credential isolation, and the
  raw AWF arguments AWF refuses under this runtime (`--legacy-security`,
  `--enable-host-access`, `--allow-host-ports`, `--dns-over-https`,
  `--topology-attach`, `--dind`, `--docker-host-path-prefix`, `--build-local`,
  `--agent-image`, `--sysroot-image`, `--chroot-binaries-source-path`,
  `--volume`, `--tty`, `--ssl-bump`, `--google-api-key`, and
  `--no-network-isolation`).
- `allow-host-ports`, GitHub Actions `services:` port mappings, and
  `runtime-install` are already rejected by the shared profile rules.
- Require the `appleInit` image role whenever a `sandbox.agent.images` manifest
  is present, add it to the image-tag digest contract, and exclude it from
  Docker predownload because Apple Container uses a separate image store.

All existing runtimes and Linux-only behaviour are unchanged; `make recompile`
produces no lock file drift.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Hey @lpcox 👋 — thanks for shipping layer 1 of the Apple Container stack! This looks great:

Focused scope — contract and validation only, with clear deferred items (setup, provisioning, transport rewiring) for layer 2.
Comprehensive testing — 86 new subtests covering runtime profile, config generation, version gating, runner validation, and negative cases.
Fail-closed design — explicit version gating (v0.28.9), blanket macOS-hosted rejection, topology isolation, and clear error messages for invalid configs.
Well-documented — PR body clearly explains the runtime model, feature compatibility matrix, and validation strategy.

The PR is ready for review and looks well-aligned with the project's standards for safety and correctness.

Generated by ✅ Contribution Check · copilot · auto · 52.6 AIC · ⌖ 14.5 AIC · ⊞ 9.3K ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants