Skip to content

perf: split the Daimon runtime image into cacheable layers - #8

Open
apresmoi wants to merge 2 commits into
feat/operational-ecosystemfrom
perf/daimon-runtime-image-layers
Open

perf: split the Daimon runtime image into cacheable layers#8
apresmoi wants to merge 2 commits into
feat/operational-ecosystemfrom
perf/daimon-runtime-image-layers

Conversation

@apresmoi

Copy link
Copy Markdown
Collaborator

Rebuilding the Daimon runtime image after a one-line Daimon change cost a full
rebuild — apt, a 166 MB grok download, a 56 MB AGY download and the whole npm
install — because the image was built in a single RUN with
COPY --from=daimon_package /daimon.tgz above it. Docker cached nothing.

Measured: 105 s → 48 s for a Daimon-only rebuild. Cold build is unchanged
(103 s vs 105 s).

The layering

base       apt + ca-certificates + curl              never changes
grok       166 MB download + digest verify           only on a pin change
agy        56 MB download + digest verify + extract  only on a pin change
codex      npm install @openai/codex@VERSION         only on a version bump
daimon     COPY daimon.tgz + npm install it          the only usual change
build      broker + shims + manifest + receipt +     cheap, always re-runs
           remaining digest checks + symlink walk

Registry vs offline-bundle mode is selected by interpolating a global
DAIMON_DEPENDENCY_MODE build arg into FROM stage names, so BuildKit never
builds the stages of the mode that was not selected. The offline chain contains
no apt-get and no curl.

What each cached step was costing, from the cold build: apt 27.9 s, grok 6.9 s,
AGY 6.3 s, codex npm 15.3 s. A shared BuildKit /root/.npm cache mount took the
remaining Daimon npm step from 40.3 s to 27.1 s.

This is a layering change, not a content change

Both images were exported and compared file-for-file: 41,108 entries, 37,814
file digests, 17 symlinks, zero differences
in path, mode, size, content or
symlink target. package.json and package-lock.json come out byte-identical
even though the single npm install was split into two.

Verification

  • Every digest check from the old single RUN still runs. Checks moved into the
    stage that owns the artifact they guard; none were dropped, weakened or made
    conditional.
  • examples/daimon-public-host (a real runtime: daimon org) compiles against
    SPAWNFILE_DAIMON_LOCAL_RUNTIME_IDENTITY and the generated org image builds,
    so the capability-receipt and contract-manifest assertions in
    src/runtime/container.ts ran for real against the new image.
  • npm run test:source-provenance-docker passes (7 m 17 s) — the mandatory
    network-disabled offline gate, including the --target offline_dependency_probe
    build and its tamper-rejection assertions.
  • npm run typecheck, npm run test:boundaries, and the Vitest suite pass.

The layering is now a tested guarantee

The second commit adds a test that parses the Dockerfile into a
{stage -> parent} graph and asserts:

  • exact ancestry for both the registry and offline-bundle chains;
  • no strict ancestor of the Daimon layer references daimon.tgz or
    source-inputs.json
    — the property the whole restructure exists for;
  • apt-get appears only in base_registry, curl only in the two registry
    source stages.

Mutation-verified four ways — reparent the Daimon stage, move the tarball COPY
up into the codex layer, add apt-get to the offline base, swap the offline grok
COPY for a curl — all four go red, restoring goes green.

It also wires scripts/build-local-daimon-runtime.test.mjs into
.github/workflows/test.yml. That file had never run in CI: Vitest includes
src/**/*.test.ts only, and the workflow's node --test step named only the
native-helper tests. Every Dockerfile guarantee in it was vacuous on a PR.

Known, not fixed

  • The residual 48 s is 27 s npm + 4 s verification + 13 s image export. The 27 s
    is Daimon's own dependency closure reinstalling. Moving it into the cached
    layer would get to roughly 25 s, but every clean way to do that changes
    ${RUNTIME_ROOT}/package.json, a file that ships inside the attested image.
    Deferred deliberately.
  • .github/workflows/runtime-images.yml still omits Daimon from its build
    matrix, so ordinary CI never builds this Dockerfile. Out of scope here.

Stacked on feat/operational-ecosystem; this PR is the two commits above it.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant