· Gamertan Tend · CI and delivery

Tending a faster, safer release path.

I wanted the release path to stop downloading and rediscovering the same approved tools on every run. Faster mattered. A smaller, more deliberate supply-chain surface mattered more.

The useful kind of repetition

A clean build should repeat the proof, not repeat every acquisition decision. Gamertan's trusted runner now uses locally built, immutable tooling images with exact Go and Hime-san versions. The image warms the complete module graph from the checked-in go.mod and go.sum; verification jobs then run with the Go proxy disabled. The accessibility image and npm cache follow the same idea: approve deliberately, reuse locally, and fail when the required material is not already present.

Source verification uses the canonical Gitea SSH endpoint on port 2222, a repository-scoped read-only deploy key, and a pinned host fingerprint. Production credentials, Docker access, and deployment authority remain outside ordinary verification jobs.

Boundary: this does not prove that a network or upstream can never be compromised. It reduces repeated acquisition, pins the material we approved, and makes an unexpected dependency on the network visible.

Two candidates taught the path to say no

The first release-candidate path exposed an artifact-handling mismatch. A later image contained the direct dependencies the build appeared to need, but not the complete six-module graph required by Tend's provenance inspection. Neither candidate reached production.

That second failure was especially useful. Tend runs go list -m all before building, rejects replacements and unpinned modules, and packages only after the full graph can be explained. With the proxy disabled, an incomplete cache could not quietly fetch its way to success. The fix was small and explicit: copy go.mod and go.sum into the tooling-image build, run go mod download all once, and prove the eventual job with no network dependency.

This was not a vulnerability incident. It was the release system doing exactly what I wanted: finding an incomplete assumption while rollback was still theoretical.

CI proves; Tend moves

The runner verifies deterministic generation, tests, race detection, vetting, dependency boundaries, deployment failure cases, and a reproducible package. Tend independently requires a clean pushed commit, records build provenance and an SPDX SBOM, builds twice, compares the bytes, and gives the archive a SHA-256 release identity.

Only that reviewed digest can enter deployment. Tend starts the inactive candidate, checks health and application smokes, validates the Caddy configuration, changes traffic atomically, records the previous slot, and keeps the prior release available. Gamertan's case-study release followed that path without rollback.

These responsibilities stay separate on purpose. CI does not receive production authority, and Tend does not pretend to be a dependency downloader, a CI service, or an infrastructure platform.

The small browser lesson counts too

After the case study went live, an already-open browser briefly paired the new HTML with the old stylesheet. The old CSS URL had a five-minute cache and a strong ETag, so nothing was corrupt; the browser was simply obeying the contract I had given it.

This release changes that contract. Pages now reference the stylesheet through a content-derived URL. When the CSS bytes change, the URL changes; unchanged bytes can be cached for a year as immutable. The old unversioned route remains available on its short cache for compatibility.

It is a tiny improvement, but it belongs to the same philosophy: make identity explicit, make reuse safe, and remove one more moment where a tired operator or confused reader has to wonder which version they are seeing.

Direction

I am not adding arbitrary hooks or turning Tend into a general orchestration platform. The next work is evidence: keep the runner image rebuild deliberate, test dependency-cache completeness, make approved artifacts easy to inspect, and carry the same calm boundary into future small Go services.