Structured image builds and images.json - #17
Merged
Merged
Conversation
A container that pins a source repo described its build as a shell line. That string is opaque to anything but a local docker/podman run: an external builder cannot tell the repo from the Dockerfile from the secrets, so it ends up keeping its own copy of the pins, which then drift. ImageBuildSpec now carries the build in fields (repo, ref, name, variant, version, dockerfile/assetDockerfile, assets, target, buildArgs, secrets) and derives the command from them; `cmd` stays as an escape hatch. `decker build` writes every spec to manifests/<recipe>/images.json, keyed by the tag the manifests reference, so a builder in CI or in a cluster builds exactly what the run will pull. Build inputs that are not in the source repo — a Dockerfile that patches it, the patches — can sit with the recipe that needs them: assetsDir points a spec at the directory that owns them, so a recipe in its own repo ships its own. Those bytes hash into the image tag (a patched build cannot collide with a pristine one) and are copied next to images.json for whoever builds. Also here, all in support of running a mainnet-shaped L1: - MAINNET_SYSTEM_CONTRACTS predeploys the EIP-4788/2935/7002/7251 contracts the EL system-calls every block; without code there the state diff takes a shape mainnet never produces. - l1 artifacts take withdrawals: "none" for BLS (0x00) credentials, so a devnet can run without per-block withdrawal sweeps. - scripts/trie-padding.ts creates N fresh accounts before load: decker's genesis is 12 accounts, and a builder whose root hasher is only exercised on mainnet-deep tries can compute wrong roots against a dozen leaves. - lighthouse takes config.feeRecipient: a builder that proves the proposer payment against state needs a recipient that exists in genesis.
canercidam
reviewed
Sep 22, 2026
| - **Renderers:** Run your recipe on any target (podman, docker, process-compose and anything you want) | ||
| - **CLI:** Hack on the clone, run immediately with preinstalled binary | ||
|
|
||
| ## Images |
Member
There was a problem hiding this comment.
Could we move this to notes/images.md?
Per review: the README keeps a one-line pointer, and notes/images.md has room to show the shapes — a spec, an images.json entry, an out-of-tree asset dir — instead of describing them in prose.
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.
A container that pins a source repo used to describe its build as a shell line. That string is only useful to a local docker or podman run: nothing else can tell the repo from the Dockerfile from the secrets, so anything that builds decker's images elsewhere ends up keeping a second copy of the pins, and the two drift.
ImageBuildSpecnow carries the build in fields — repo, ref, name, variant, version, dockerfile or assetDockerfile, assets, target, buildArgs, secrets — and the build command is derived from them.cmdstays as an escape hatch for a build no combination of fields expresses.decker buildwrites every spec tomanifests/<recipe>/images.json, keyed by the tag the manifests actually reference. A builder in CI or in a cluster reads that one file and builds exactly what the run will pull. WithDECKER_IMAGE_MODE=pulldecker builds nothing itself and only references the tags.Build inputs that are not in the source repo — a Dockerfile that patches it, the patches it applies — can sit with the recipe that needs them.
assetsDirpoints a spec at the directory that owns them, so a recipe living in its own repo ships its own build inputs instead of adding them here. Those bytes hash into the image tag, so a patched image can never collide with a pristine one, and they are copied next toimages.jsonfor whoever does the building.The rest is in support of running a mainnet-shaped L1:
MAINNET_SYSTEM_CONTRACTSpredeploys the EIP-4788, 2935, 7002 and 7251 contracts the EL system-calls every block. With no code at those addresses the block's state diff takes a shape mainnet never produces.withdrawals: "none"for BLS credentials, so a devnet can run without per-block withdrawal sweeps.scripts/trie-padding.tscreates N fresh accounts before load. decker's genesis is a dozen accounts, and a builder whose root hasher is only ever exercised on mainnet-deep tries can compute wrong roots against a trie that shallow.config.feeRecipient, because a builder that proves the proposer payment against state needs a recipient that exists in genesis.Tested with
deno lint,deno checkover all tracked TypeScript, 14 unit tests and the e2e suite against the compiled binary. New coverage: the derived build command, the images.json payload, assets owned by an out-of-tree recipe, and an e2e test asserting that every tag in images.json is referenced by the rendered manifests.