Expand description
The cached, checksum-verified GitHub runner package.
Two failure modes drive this module, and every decision below is one of them being closed:
- A tampered package executes arbitrary code on the operator’s machine.
Download metadata comes from GitHub and nowhere else, the published
SHA-256 is verified before extraction, and an absent published checksum
fails closed rather than degrading into “skip the check”
(
07-security.md,05-infrastructure.md). - A stale package makes every job start failing for a reason nobody will
guess. GitHub rejects runners more than 30 days behind the latest
release (
01-current-architecture.md, edge case 7), so the published version is re-checked on a bounded interval and a superseded cache entry is refreshed before a cold start.
§The shape of the cache
Under AppPaths::state_dir, which 05-infrastructure.md names as the home
of “the retained runner package cache”:
state/
packages/
2.330.0/ one immutable entry, never mutated after it lands
.runner-package.json its manifest, written before the entry lands
run.sh, bin/, externals/ GitHub's archive, extracted
.staging/ transient; a partial install is only ever here
.leases/ <attempt-uuid>.lease, the prune guard's evidence
tool-cache/ approved tool caches, retained beside the binaries
runtime/ per-attempt job workspaces — NOT under packages/A cache entry is created by exactly one filesystem operation. Extraction
writes into .staging/<uuid>/, the manifest is written inside that
staging directory, and the directory is then renamed to packages/<version>/.
The rename is the single commit point: an entry that exists is an entry that
is complete, and a crash at any earlier moment leaves nothing but staging
litter. That is also what makes “a second install of the same version is a
no-op” true rather than aspirational — the entry is found and returned before
anything is fetched.
§The seam e3 consumes, stated deliberately
e3 creates each JIT runtime as “a copy or link from that cache plus a
unique workspace” (05-infrastructure.md). Three contracts hold that seam
together, and they are stated here because an unstated port contract becomes
a defect in the consumer:
InstalledPackage::rootis read-only to the caller. It is the shared, immutable source that every runtime is copied or linked from. Writing into it corrupts every future runtime, and nothing in this module can detect it after the fact.PackageCache::leasemust be called for the version a runtime was created from, andPackageCache::releasewhen that runtime is removed. The prune guard is only as truthful as those calls: a version with no lease is a version this module believes nothing references. The guard is fail-closed in the other direction — seePackageCache::prune— so a lease that is never released costs disk, while a lease that is never taken costs a running runner its binaries.- Job workspaces live under
AppPaths::runtime_dirand never under the package cache.PackageCache::leaseenforces this rather than documenting it: a lease whose attempt’s runtime path resolves inside the cache root is refused.
§What is built here and not yet reachable in production
Two mechanisms in this module are complete and tested but have no live caller, and both are easier to find written down here than inferred from an absence:
PinnedDigestshas no operator-facing surface. It is the entire remedyPackageError::ChecksumAbsentnames, and today it can only be supplied byPackageCache::with_pinsfrom Rust. Until a configuration or CLI path reaches it, an operator who hits that refusal has been given an instruction they cannot carry out. Wiring it is ag/f-group concern, not this module’s.PackageError::VersionRejectedcannot be produced through the real adapter.GatewayCatalogmaps everyInventoryErrorto the retryablePackageError::CatalogUnavailable, correctly — the downloads endpoint has no version-rejection response. The rejection lands at registration, which ise3’s path, andDownloadCatalogexists to carry it here whene3reports it. So the no-retry behaviour is proven and nothing yet supplies its input.
Structs§
- Cache
Ports - The ports
PackageCachereaches the world through. - Exponential
Backoff - Bounded exponential backoff, as
03-control-flows.mdrequires. - Freshness
- When a cached package stops being good enough.
- Gateway
Catalog - Adapts
c3’sInventoryGatewaytoDownloadCatalog. - Http
Fetcher - Streams the package with
reqwest, one chunk at a time. - Installed
Package - One immutable entry in the cache.
- NoBackoff
- No delay at all. For tests, and for a caller that supplies its own pacing.
- Package
Cache - The versioned, immutable, checksum-verified runner package cache.
- Pinned
Digests - Digests the operator has independently confirmed, keyed by version.
- Runner
Version - A runner package version, such as
2.330.0. - Sha256
Hex - A SHA-256 digest, normalised to 64 lowercase hex characters.
Enums§
- Package
Error - Everything that can stop a package reaching the cache.
- Published
Checksum - Which unusable shape GitHub’s optional
sha256_checksumarrived in.
Constants§
- CHECK_
INTERVAL_ HOURS - How often the published version is re-checked. “A bounded interval”
(
05-infrastructure.md); six hours keeps the check far cheaper than the demand poll while still catching a release the same day it ships. - FRESHNESS_
WINDOW_ DAYS - How far behind the latest release a cached package may be before it is
refreshed. GitHub rejects runners past this and plans to block them at
registration (
01-current-architecture.md, edge case 7). - RETRY_
BUDGET - How many times a retryable failure is retried before the install is abandoned. A terminal failure consumes none of this budget.
Traits§
- Backoff
- How long to wait between retries of a retryable failure.
- Download
Catalog - Where the published runner-download metadata comes from.
- Package
Fetcher - Streams a published package onto disk.