Skip to content

fix(v0.4.0): state schemaVersion + Docker HEALTHCHECK / multi-arch / SHA-pin#62

Merged
VirusAlex merged 1 commit into
mainfrom
fix/v040-state-and-docker
May 1, 2026
Merged

fix(v0.4.0): state schemaVersion + Docker HEALTHCHECK / multi-arch / SHA-pin#62
VirusAlex merged 1 commit into
mainfrom
fix/v040-state-and-docker

Conversation

@VirusAlex
Copy link
Copy Markdown
Owner

Summary

Closes audit findings HIGH H5/H6/H7/H8 + medium Docker SHA-pin and gosu-binary verification. Fourth of ~5 PRs gating v0.4.0.

Changes

State schemaVersion (H8)

Pre-v0.4.0 the on-disk JSON formats had no version field — a future format change would silently misinterpret older files (or vice-versa).

  • JobState: new int schemaVersion (last component to keep the diff small at the 9 construction sites). CURRENT_SCHEMA_VERSION = 1.
  • SidecarMeta: same.
  • Both readers (JsonJobStore.loadFile/load, FileSidecarStore MetaJson) refuse files whose schemaVersion exceeds the supported version — log + skip, never destroy.
  • Legacy v0.3.x files (no field) → Jackson defaults to 0 → treated as schema 1 on read. Existing transfers resume normally.

Bumping the constant in either record now signals an intentional break, the precondition for the v1.0.0 compatibility story.

Docker HEALTHCHECK (H5)

HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
    CMD curl -fsS http://127.0.0.1:7777/api/health > /dev/null

UBI 10 minimal doesn't ship curl; the Dockerfile installs curl-minimal (~6 MiB) right before the gosu COPY. Compose depends_on: condition: service_healthy and similar primitives now work.

Multi-arch image (H7)

release.yml docker/build-push-action: platforms: linux/amd64,linux/arm64. The Dockerfile already SHA-pins the per-arch gosu binary, so cross-build is straightforward. ARM users (RPi 4+, Apple Silicon, AWS Graviton) get a working docker pull.

:latest only for non-0.x (H6)

release.yml: while VERSION matches ^0., push only :<version>, NOT :latest. Matches the README claim and make_latest flag on the GitHub Release. Pre-v0.4.0 every 0.x tag silently overwrote :latest.

SHA-pinned base images + gosu binary

  • alpine:3.20@sha256:d9e853e87e...
  • eclipse-temurin:25-jre-ubi10-minimal@sha256:c897ce903faf...
  • gosu-amd64 / gosu-arm64 SHA-256 from upstream SHA256SUMS is verified after curl via sha256sum -c. Closes the "compromised GitHub release / MITM" supply-chain hole.

Test plan

  • mvn test -Dtest=JsonJobStoreTest → 10 tests pass.
  • mvn test -Dtest=FileSidecarStoreTest → 12 tests pass.
  • mvn test -Dtest=ChunkBitmapTest → 11 tests pass.
  • mvn test -Dtest=ArchitectureTest → 8 tests pass.
  • CI green on Linux.
  • Manual: pull a v0.3.x sidecar / job state from disk, run v0.4.0 against it, confirm transfers resume normally.
  • Manual: bump CURRENT_SCHEMA_VERSION to 2 locally, write a job, downgrade pom to schemaVersion 1, confirm reader refuses with a clear log message.
  • Manual: docker build -t test . succeeds on amd64 Linux; docker buildx build --platform linux/arm64 -t test . also succeeds (gosu SHA verification passes for both arches).

🤖 Generated with Claude Code

Closes audit findings HIGH H5/H6/H7/H8 + medium Dockerfile SHA-pin and
gosu binary verification. Fourth of ~5 PRs gating v0.4.0.

State schema versioning (H8)
- JobState gains an int schemaVersion component (last position to keep
  the diff small at the 9 construction sites). CURRENT_SCHEMA_VERSION = 1.
  Existing v0.3.x JSON files have no field — Jackson defaults to 0,
  treated as schema 1 on read.
- SidecarMeta gains the same. The MetaJson DTO it serializes through
  defaults old files to schema 1 on toMeta() and refuses any future
  schemaVersion > CURRENT_SCHEMA_VERSION (rather than silently
  misinterpreting newer-format files). Same on JsonJobStore.load /
  loadFile — refused entries are skipped, never destroyed.
- Pre-v0.4.0 the on-disk JSON formats had no schemaVersion field, so a
  forward-incompatible change later would have been impossible to detect.
  Now: bumping the constant in either record signals "this version refuses
  to read older NetCopy's downgrade attempts" — a precondition for the
  v1.0.0 compatibility story.

Docker HEALTHCHECK (H5)
- HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3
  CMD curl -fsS http://127.0.0.1:7777/api/health
- UBI 10 minimal doesn't ship curl; we install curl-minimal
  (~6 MiB layer) before the COPY of the gosu binary. Compose
  `depends_on: condition: service_healthy` and similar orchestrator
  primitives now work.

Multi-arch image (H7)
- release.yml: docker/build-push-action `platforms: linux/amd64,linux/arm64`.
  The Dockerfile already SHA-pins the per-arch gosu binary, so the
  cross-build is straightforward. ARM users (RPi 4+, Apple Silicon via
  Docker Desktop, AWS Graviton) finally get a working pull.

`:latest` only for non-0.x (H6)
- release.yml docker_tags: while VERSION matches `^0.`, push only
  ghcr.io/.../netcopy:<version> — NOT :latest. Matches the README claim
  ("`latest` — Highest tagged stable release once 1.x ships") and the
  `make_latest` flag on the GitHub Release. Pre-v0.4.0 every 0.x tag
  silently overwrote :latest, contradicting the docs.

Dockerfile base-image SHA pin
- alpine:3.20 → @sha256:d9e853e87e... (digest-pinned)
- eclipse-temurin:25-jre-ubi10-minimal → @sha256:c897ce903faf...
- Decouples our build from any upstream re-tag of those names. Bump
  deliberately on each NetCopy release.

gosu SHA-256 verification
- per-arch SHA from the upstream SHA256SUMS file is now baked into the
  Dockerfile and verified after curl. Closes the
  "compromised GitHub release / MITM" supply-chain hole noted in the
  audit.

Local mvn test: schema-related suites (JsonJobStoreTest, FileSidecarStoreTest,
ChunkBitmapTest, ArchitectureTest) all green individually. The 56 errors
in the full mvn test run are the existing Windows-env Jetty-loopback
issue, unchanged from main.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@VirusAlex VirusAlex merged commit 72eb3e1 into main May 1, 2026
1 check passed
@VirusAlex VirusAlex deleted the fix/v040-state-and-docker branch May 1, 2026 03:53
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