Commit Graph

4 Commits

Author SHA1 Message Date
Feng Ruohang b6d47b739c fix: harden healthcheck and distroless lanes per adversarial review
Findings from an adversarial review (Codex, gpt-5.6-sol at max effort)
of 2ff594f4b and 4c34d2309, each independently verified before fixing:

- SBOM generation: buildx attaches a provenance attestation, so every
  per-arch digest names an OCI index; Syft's platform default on an
  amd64 runner cannot resolve an arm64-only index and the step dies.
  Pass --platform explicitly on all four Syft calls (the two classic
  lanes had the same latent defect - the renamed workflow has not run
  yet, which is why it never fired).
- Release ordering: the HEALTHCHECK survival check now runs against
  the pushed architecture image before the versioned and rolling
  multi-arch manifests are created, so a broken health config blocks
  their promotion; the comment now states honestly that the
  arch-suffixed tags are already public at that point.
- Gate assertions: tar's member-argument mode exits non-zero on any
  missing name, which under pipefail masked a found forbidden file
  when exactly one of them existed; -tv prints symlinks as
  'name -> target', defeating $-anchored greps; and the licenses
  check proved only one-of-three. Export the rootfs once and assert
  every required and forbidden entry individually (busybox/sh and
  usr/bin/mc[li] now covered), and match the image healthcheck as an
  exact array instead of a substring.
- Probe target vs CLI-configured servers: a probe process cannot see
  PID 1's argv, so --url gains EnvVar MINIO_HEALTHCHECK_URL as the
  documented way to point the baked-in HEALTHCHECK at a server whose
  address/TLS comes from command-line arguments (verified end to end:
  server on --address :9010, env var alone turns the container
  healthy). Baseline regenerated for the new env token.
- IPv6 zone identifiers: serialize probe URLs via url.URL.String()
  so [fe80::1%eth0]:9000 becomes a valid %25-escaped URL (tests added).
- Boolean flags: read --json/--quiet via Bool() so --json=false is
  false, instead of IsSet() which treats any occurrence as true.
- Docker's HEALTHCHECK timeout raised to 10s: an outer deadline equal
  to the probe's own 5s always SIGKILLed the probe before it could
  print its diagnostic line.
- test-release path filter now also triggers on cmd/healthcheck-main.go
  and cmd/main.go, so subcommand regressions run the image gate.

Not adopted: require_text's comment-insensitivity in verify-rebrand.sh
(snapshot-tripwire by design, consistent with its other assertions -
the semantic check lives in the CI gate now), and full
staging-then-promote tag publishing (a workflow-wide redesign shared
with the classic lanes, tracked as follow-up).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 17:27:22 +08:00
Feng Ruohang 2ff594f4bb feat: add the native silo healthcheck subcommand
Add 'silo healthcheck [live|ready|cluster|cluster-read]', a thin
anonymous HTTP client for the server's own /minio/health/* endpoints,
so containers without a shell, curl, or mc can still run health
checks. Design: silo.pgsty.com/compatibility/feature/healthcheck/

The check vocabulary maps 1:1 onto the health API paths; the probe
target is derived from the server's own --address/MINIO_ADDRESS
contract with HTTPS auto-detected from the certs directory, and can
be overridden with --url. Exit codes are 0/1 only (Docker reserves 2);
diagnostics (x-minio-server-status, quorum headers) go into a single
output line for docker inspect. The request is strictly anonymous (a
credentialed request would be rejected by the reserved-path guard),
the transport bypasses HTTP_PROXY, and certificate verification is
skipped to match kubelet HTTPS probe behavior. Cluster checks default
to a 15s deadline so the server's 10s cluster_deadline can elapse.

Compatibility notes: the preserved /minio/health/* path literals and
the MINIO_ADDRESS env var are upstream wire/config surface, reused on
purpose; the rebrand-guard baseline is regenerated for the new route
literals (tests included) with zero new exported symbols. The docker
entrypoint argv translation learns the new command name.

Verified: unit tests, entrypoint tests, go vet, plus an end-to-end
run against a live server covering all four checks, --maintenance
(412), --json, usage errors, unreachable and timeout paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 16:32:16 +08:00
Feng Ruohang c46b16ec62 chore: cut over to pgsty/silo and main
The transitional references land in one commit, because they are only correct
together: the repository is pgsty/silo, its default branch is main, and nothing
in the tree should point a user at the old names.

Changed:

- Workflow branch filters. go.yml and vulncheck.yml gated on `branches: master`
  for both push and pull_request, so renaming the default branch would have
  taken automatic CI offline with no error and no signal - the workflows would
  simply never trigger again. They now name main.
- Release target. goreleaser's `release.github.name` becomes silo, which is
  what actually decides where a tagged build publishes. sign-release-rpms.sh's
  GH_REPO default follows.
- The OCI `image.source` label, the Helm chart `sources` entry, the security
  advisory link in the issue-template config, and the go.mod comment citing the
  LDAP TLS fix.
- 115 occurrences across README, README_ZH, SECURITY, CONTRIBUTING and 30 docs
  pages, including 72 links that also carried the master branch in their path.
  Those matter most: GitHub redirects clone, fetch, push and web URLs after a
  rename, but raw.githubusercontent.com does not, and neither follows a branch
  rename - every one of those links would 404 twice over.
- Three error strings in cmd/erasure-sets.go, cmd/storage-errors.go and
  internal/config/errors.go that print an issue URL to operators. These are Go
  string literals inside rebrand-guard's brand allowlist, so the baseline is
  regenerated. The regeneration removes exactly those three entries and adds
  none; all twelve other protected sets, including the 9014 exported symbols,
  are byte-identical.
- The transitional-naming disclaimers in README, README_ZH, SECURITY and
  CONTRIBUTING are dropped, since they no longer describe anything.

Deliberately unchanged, all three because they exist to reject or freeze the old
name rather than to point at it:

- buildscripts/minio-upgrade.sh pins pgsty/minio@sha256:b6bfe72... - the frozen
  pre-rebrand image is the control group for the MinIO-to-Silo upgrade test.
- helm-migration-guard rejects any rendered container still pulling pgsty/minio.
- verify-rebrand.sh rejects the same in the delivery surfaces.

Also unchanged: docs/config/README.md links to pgsty/mc/blob/master, and that
repository's default branch really is still master. It moves when mc does.

verify-rebrand.sh gains three assertions so this cannot silently regress: no
source reference may name pgsty/minio outside the three allowlisted guards, no
link may target pgsty/silo's master branch, and go.yml and vulncheck.yml must
filter on main. Both new rejections were negative-tested - reintroducing a
master branch filter and adding a pgsty/minio URL each fail the gate with the
specific message.

This commit assumes the rename actually happens. Until the GitHub branch and
repository renames are executed, the links it introduces do not resolve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 09:28:06 +08:00
Feng Ruohang bd8df51665 ci: gate the rebrand on compatibility, packaging and provenance evidence
A rebrand of this size cannot be reviewed by reading the diff. The risk is not
what was renamed but what was renamed by accident: an environment variable, a
metric, a header, a route, a storage marker or an exported symbol that some
deployment depends on. So the invariants get a machine check.

rebrand-guard extracts the identifiers a product rename must not touch - the
go.mod module path, github.com/minio/* imports, MINIO_* variables, minio_*
metric literals, x-minio-* headers, route string literals, the hardcoded grid
routes in internal/grid/manager.go, the reserved bucket root, .minio.sys
markers, arn:minio and minio:s3 policy values, every exported symbol in cmd/
and internal/, and every branded string literal - and compares them to a
checked-in baseline. Any removal or addition fails the build, in both
directions, so a future change cannot quietly drop one either.

The recorded delta against the pre-rebrand tree is: module path, 137 imports,
19 metrics, 15 storage markers, 58 policy values and all 9014 exported symbols
byte-identical; environment 437 -> 436, headers 85 -> 84, routes 331 -> 330.
Each of those three is accounted for. MINIO_CONFIG_DIR and MINIO_VERSION were a
shell local and a compose variable in test scripts, never server settings.
x-minio-common was a docker-compose extension key, never an HTTP header.
/api/health/upload was the SUBNET upload path in the deleted callhome.go.

verify-rebrand.sh covers what the identifier guard cannot see: that the retired
upstream delivery paths stay deleted, that the packaging and container files
name silo, that silo.service keeps its Conflicts and both EnvironmentFile
lines, that silo.env contains no active assignment that would shadow the legacy
file, that no active delivery surface still publishes pgsty/minio, and that no
non-test Go source contains a dl.min.io, subnet.min.io, api.min.io,
slack.min.io or play.min.io URL.

Both, plus the entrypoint argv test, run in go.yml, release.yml and
test-release.yml. test-release.yml also gets the path filters right: it was
watching minio.service, which no longer exists, so the packaging gate would
have gone silently dormant. It now watches the unit, defaults, sysusers,
lifecycle scripts, helm chart and both guards, and asserts the four-file
payload, the absence of any cross-name Provides/Obsoletes/Conflicts, and that a
container started with the legacy `minio server` argv really runs silo as PID 1.

release.yml and docker-release.yml additionally verify that the checked-out
revision matches GITHUB_SHA before building. GitHub's OIDC certificate records
GITHUB_SHA, not the ref passed to actions/checkout, so a manual dispatch from
the wrong ref would have produced provenance describing different source from
the bytes being published. They now emit SBOMs and attestations for archives,
packages and images, and docker-release verifies the release workflow's
attestation on every artifact it consumes. Images publish to pgsty/silo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 08:48:39 +08:00