Skip to content

feat(ci): resolve stack images by short SHA, drop repin train#726

Open
bussyjd wants to merge 4 commits into
mainfrom
feat/sha-image-resolve
Open

feat(ci): resolve stack images by short SHA, drop repin train#726
bussyjd wants to merge 4 commits into
mainfrom
feat/sha-image-resolve

Conversation

@bussyjd

@bussyjd bussyjd commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces digest-in-git + repin PR ceremony with a single image policy:

  • Templates ship image: ghcr.io/obolnetwork/…:__OBOL_IMAGE__ (no digests in git).
  • Apply time (CopyInfrastructure / images.Resolve): production → repo:<GitCommit>@sha256:<index-digest> when GHCR is reachable; short-SHA alone if offline. Dev → repo:dev-<sha>.
  • Release gate: verify-release-images.sh checks GHCR has tags for the commit short SHA.
  • Publish: docker-publish-x402 builds/pushes only (includes job-broker).

Supersedes #725 — that PR added job-broker to the old repin regime. This lands job-broker publish and removes the regime.

Net: −419 LOC (662 added / 1081 removed). Deleted release-prep.yml, repin-x402-images.sh, verify-x402-pins.sh, docs/release-x402-pins.md.

Risk level: medium (release path change; first tag after merge needs GHCR images for that short SHA — already true for commits that hit the publish path).

Scope

  • Code (internal/images, internal/defaults, templates, tests)
  • Charts / manifests (placeholder image refs)
  • Flows / QA scripts
  • Docs / skills (docs/release-images.md, release-train)
  • Images / dependencies (publish matrix + release gate)
  • CI workflows

Validation

Unit tests:

go test ./internal/images/ ./internal/defaults/ ./internal/embed/ ./internal/x402/ ./internal/stack/ ./cmd/obol/ -count=1
# all ok

# Live GHCR digest bind (temporary -tags live test, then removed)
# Resolve("…/x402-verifier") with GitCommit=656e5f6
# → ghcr.io/obolnetwork/x402-verifier:656e5f6@sha256:bf209f10…  PASS

# Registry existence for last known pins
# x402-verifier / serviceoffer-controller / x402-buyer :656e5f6  OK
# job-broker :656e5f6  MISS (expected until first publish after merge)

VERIFY_RELEASE_IMAGES_OFFLINE=true .github/scripts/verify-release-images.sh HEAD  # ok
bash -n .github/scripts/verify-release-images.sh .github/scripts/lib-ghcr.sh

Release train after merge

merge to main → docker-publish-x402 tags :shortsha → smoke → tag on main

No repin PR. Before tagging: .github/scripts/verify-release-images.sh HEAD.

Docs: docs/release-images.md.

Review Notes

Stop storing x402 image digests in git. Embedded templates use
:__OBOL_IMAGE__; CopyInfrastructure rewrites via internal/images to
repo:<GitCommit>@sha256:<index-digest> at apply time (short-SHA tag
privileged; digest bound from GHCR when reachable). Dev still uses
dev-<sha>.

Delete release-prep, repin/verify-x402-pins scripts, and continuous
open-repin. Release gate is verify-release-images.sh (GHCR tags exist
for the commit short SHA). Publish job-broker with the other components.

Supersedes #725 (job-broker publish without expanding the repin regime).
@bussyjd bussyjd requested review from OisinKyne and mcdee July 9, 2026 07:13
@bussyjd

bussyjd commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Ready to merge for v0.13.0-rc3 — blocked only on required review (cannot self-approve).

CI: Go build/vet/unit tests pass; gitleaks/helm/lint green.

Once approved I will:

  1. Squash-merge this PR
  2. Wait for docker-publish-x402 (short-SHA tags incl. job-broker)
  3. verify-release-images.sh then tag v0.13.0-rc3

Please approve when you can.

@bussyjd bussyjd enabled auto-merge (squash) July 9, 2026 07:13
@bussyjd

bussyjd commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@OisinKyne @mcdee CI is green and auto-merge is armed — only missing: one approving review so we can cut v0.13.0-rc3 (closes the job-broker publish GA follow-up from rc2).

Cut multi-arch publish from ~20–40m to a few minutes:

- Dockerfile.x402 multi-target: one shared builder builds all five
  pure-Go binaries; final distroless stages stay separate
- docker-bake.hcl + bake-action: single job, shared GHA cache scope
- FROM --platform=\$BUILDPLATFORM + GOARCH=\$TARGETARCH (CGO off) —
  drop QEMU emulation that dominated wall time
- BuildKit cache mounts for modules + go-build
- .dockerignore: exclude web/** bulk (keep public-storefront), tests

Thin per-component Dockerfiles kept for local stack builds with the
same cross-compile + cache-mount pattern.
gh workflow run docker-publish-x402.yml --ref main
```

CLI `version.GitCommit` selects `repo:<short-sha>`; apply-time `images.Resolve` binds the GHCR index digest for security. Digests are never committed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apply-time images.Resolve binds the GHCR index digest for security. Digests are never committed.

Does this mean if a sha changes on server side we pull the new one? its still better than latest etc but it might not grant us super surety we're keeping a single digest dockerimage across restarts etc. maybe not the end of the world but something to keep on the security considerations

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — you're right about the mutability gap.

Before: every apply re-HEAded GHCR for repo:<short-sha>, so if a package writer overwrote that tag, the next stack up would bind a new digest (still better than :latest, but not “same image across restarts” for a given install).

Now (d1d0aa1):

  1. First resolve for (repo, GitCommit) fetches the multi-arch index digest and persists it to $OBOL_CONFIG_DIR/image-digests.json.
  2. Later applies / restarts with the same CLI version reuse the pin and do not re-query GHCR.
  3. OBOL_REFRESH_IMAGE_DIGESTS=true is the explicit escape hatch to re-bind.
  4. Ops policy remains: never retag a published short SHA; package write ACL on ghcr.io/obolnetwork/* is still part of the trust root.

Trade-off vs digests-in-git is documented under Security considerations in docs/release-images.md. Cross-host / fresh install still first-fetches GHCR (same short SHA → same digest if tags weren't retagged).

Thanks for flagging it.

bussyjd and others added 2 commits July 9, 2026 16:31
Address review: apply-time re-fetch of GHCR digests would pick up a
retagged short-SHA on the next stack up. Bind the multi-arch index
digest on first resolve for each repo:GitCommit, store it in
$OBOL_CONFIG_DIR/image-digests.json, and reuse it on later applies
(unless OBOL_REFRESH_IMAGE_DIGESTS=true). Document the threat model
in docs/release-images.md.
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.

2 participants