# Sandbox CI: the keyless real-kernel confinement proofs. This master-only # reference stays outside the pull-request verdict; rationale lives in # .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md. # A separate workflow from ci.yml because the axis is different — these jobs # fan out over OS×runner (kernel capabilities), not node versions. The Landlock # launcher is built from native/landlock-run on each Landlock leg and installed # from the same tarballs the main-repository release workflow publishes. name: Sandbox on: push: branches: [master] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read env: # CI runs must never report to the production telemetry endpoint baked # into apps/cli/cordis.yml (AppCLIEntry disables the row when set). DSH_TELEMETRY_DISABLED: '1' jobs: # Keyless real-kernel sandbox proofs (sandbox Agent Note § Testing): each ladder # rung is only provable on a host where it enforces, so this job fans out # an OS×runner matrix — bwrap and Landlock on Linux (separate legs: the # Landlock files force the bwrap rung off, so each leg proves exactly one # rung; Landlock twice, once per architecture, each confining through the # locally built launcher), Seatbelt on macOS (sandbox-exec ships with # the OS). One node # version only: kernel confinement does not vary by node, and ci.yml's # node matrix already covers the node axis. # # The e2e files self-skip where their runner is absent, so a leg that lost # its runner (no bwrap, kernel without Landlock, macOS without # sandbox-exec) would otherwise pass as a false green — the same trap # e2e.yml's key preflight guards against. Each leg therefore asserts BOTH # its platform files actually ran: `Test Files 2 passed (2)`, no skips. sandbox-e2e: strategy: fail-fast: false matrix: include: - os: ubuntu-latest runner: bwrap - os: ubuntu-24.04 runner: landlock - os: ubuntu-24.04-arm runner: landlock - os: macos-latest runner: seatbelt name: sandbox e2e (${{ matrix.runner }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} timeout-minutes: 20 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v6 with: node-version: 24 - name: Install (immutable) run: pnpm install --frozen-lockfile # The bwrap rung needs bubblewrap on PATH and unprivileged user # namespaces. Ubuntu 24.04 gates the latter behind an AppArmor knob; # lift it best-effort — on images where the knob is absent the # functional probe (and the run-guard below) is the arbiter anyway. - name: Install bubblewrap (unrestrict userns) if: matrix.runner == 'bwrap' run: | sudo apt-get update -q sudo apt-get install -yq bubblewrap sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \ || echo "apparmor userns knob absent — the functional probe decides" - name: Build Landlock launcher for this architecture if: matrix.runner == 'landlock' run: | sudo apt-get update -q sudo apt-get install -yq musl-tools pnpm --dir native/landlock-run run build:ts pnpm --dir native/landlock-run run build:native # The unit suite runs on ubuntu in `checks`; this is the one darwin leg # in the workflow, so run it here too — the platform-dependent unit # expectations (Seatbelt path canonicalization: /tmp IS /private/tmp) # take their darwin branch only on this runner. - name: Unit tests (darwin parity) if: matrix.runner == 'seatbelt' run: pnpm run test - name: Sandbox e2e (real kernel confinement, world-verified) # NO_COLOR: vitest force-enables ANSI color under GITHUB_ACTIONS even # without a TTY, which would thread escape codes through the summary # line the run-guard greps. env: NO_COLOR: 1 run: | set -u +e -o pipefail out=$(pnpm exec vitest run --config vitest.e2e.config.ts \ packages/sandbox/sandbox-local/tests/${{ matrix.runner }}.e2e.ts \ packages/shell/bash-sandbox/tests/${{ matrix.runner }}.e2e.ts 2>&1); status=$? echo "$out" [ "$status" -eq 0 ] # Both platform files must have RUN — a self-skip (runner missing on # the very platform that exists to prove it) is a failure, not a pass. echo "$out" | grep -qE 'Test Files[[:space:]]+2 passed \(2\)' # Publish-path rehearsal, Landlock legs only. Build the launcher on its # native architecture, then install the local native and harness tarballs # together so registry state cannot mask source/package drift. - name: Build packages for the pack rehearsal if: matrix.runner == 'landlock' run: pnpm run build - name: Packed-distribution e2e (pack → install → confine) if: matrix.runner == 'landlock' env: NO_COLOR: 1 run: | set -u +e -o pipefail out=$(pnpm exec vitest run --config vitest.e2e.config.ts \ packages/sandbox/sandbox-local/tests/packed-install.e2e.ts 2>&1); status=$? echo "$out" [ "$status" -eq 0 ] echo "$out" | grep -qE 'Test Files[[:space:]]+1 passed \(1\)'