Removes 67 live references to docs/requirements/<NNN>-*.md files across
43 contributor docs, role READMEs, code comments, task files, and tests.
Requirement docs are initial-creation-only artefacts; live references
freeze a snapshot that rots once the per-feature SPOT (a contributor
doc, a code module, a lint test) takes over the live behaviour.
In the same pass, fixes documentation.md violations on the touched
files: drops em-dashes used as thought breaks, removes "SPOT for ..."
self-labels, strips historical phrasing ("the legacy", "previously",
"used to", "is no longer used", "matching the historical"), and
reflows soft-wrapped sentences so each sentence stays on one logical
line.
The `test_inventory_vars_file_spot.py` whitelist for the literal
inventory path drops the docs/requirements/003 entry and instead
skips docs/requirements/ entirely; future requirement docs that
mention the literal path are then ignored automatically.
Verified by tests/lint/repository/test_no_stale_requirement_refs.py
(green tree-wide).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.9 KiB
Local Caches 📦
Three local cache services accelerate re-deploys, reduce upstream traffic, and harden the development workflow against transient upstream outages. They share one compose profile (cache) and one override file (compose/cache.override.yml) and are gated together by profile.py: active on developer machines, inactive on CI runners.
Services 🧩
Registry Cache 🐳
registry-cache runs rpardini/docker-registry-proxy and transparently MITMs every Docker image pull from the inner dockerd, regardless of upstream registry (docker.io, ghcr.io, mcr.microsoft.com, …). The runner trusts the proxy CA via registry-ca.sh.
Pulls only. Pushes are not intercepted.
Package Cache 📚
package-cache runs sonatype/nexus3 and exposes pull-through proxies for package-manager downloads:
| Format | Repo names | Upstream |
|---|---|---|
apt |
apt-debian, apt-debian-security, apt-ubuntu, apt-ubuntu-security |
deb.debian.org, archive.ubuntu.com, security.ubuntu.com |
pypi |
pypi-proxy |
pypi.org (incl. files.pythonhosted.org) |
npm |
npm-proxy |
registry.npmjs.org |
rubygems |
gem-proxy |
rubygems.org |
go |
go-proxy |
proxy.golang.org |
helm |
helm-bitnami |
charts.bitnami.com/bitnami |
yum |
yum-rocky, yum-fedora |
download.rockylinux.org, dl.fedoraproject.org |
raw |
raw-githubusercontent, raw-codeload-github, raw-packagist, raw-alpine |
raw.githubusercontent.com, codeload.github.com, repo.packagist.org, dl-cdn.alpinelinux.org |
Bootstrap is idempotent and runs from package.sh once the stack is healthy.
Package Cache Frontend 🔐
package-cache-frontend runs nginx:alpine and reverse-proxies upstream package-manager hostnames onto the matching Nexus repo path. Combined with extra_hosts DNS-hijack on consumers, package managers can hit their real upstream URL and still flow through the cache.
Two listener layers:
- HTTPS (port 443): per-hostname server certs signed by a dedicated CA. Used by the
infinitorunner (Ansible-drivenpip install,gem install,composer install,curl https://…). The runner trusts the CA via package-frontend-ca.sh. - HTTP (port 80): plain mirrors for
deb.debian.org,archive.ubuntu.com,security.ubuntu.com,dl-cdn.alpinelinux.org. Used by inner-dockerdDockerfile builds viabuild.extra_hostsDNS-hijack. No CA-trust required in the build container.
Cert generation runs in a throw-away alpine container driven by package-frontend-certs.sh before the frontend starts.
Activation 🎚️
The cache decision is exposed via Profile.registry_cache_active() in profile.py. When active:
- compose/cache.override.yml is layered on top of the base compose.yml by compose.py and down.py via common.py
compose_file_args. - The cache services are added.
- The runner's
infinitoservice receives:- bind-mounts for the registry-cache CA, the package-cache client snippets (
pip.conf,npmrc,apt.list), and the frontend CA file extra_hostsentries DNS-hijacking the HTTPS upstream hostnames to the frontend's static IPINFINITO_PACKAGE_CACHE_FRONTEND_IPenv var for the inner compose wrapper
- bind-mounts for the registry-cache CA, the package-cache client snippets (
- Cert generation, Nexus repo bootstrap, and runner trust-store install run from compose.py.
When inactive, the override is omitted: cache services do not exist, the runner has no cache mounts or DNS-hijack, package managers go direct to upstream.
CI signals (GITHUB_ACTIONS=true, RUNNING_ON_GITHUB=true, CI=true) deactivate. Fresh runner disks per CI job give no cross-run amortization.
Coverage Matrix 📋
| Traffic | Mechanism | Cached |
|---|---|---|
Image pulls (any registry, inner dockerd) |
registry-cache MITM via HTTP_PROXY env on dockerd |
✓ |
apt-get install (Ansible task in runner) |
apt.list URL-rewrite to package-cache:8081 |
✓ |
pip install (Ansible task in runner) |
pip.conf URL-rewrite |
✓ |
npm install (Ansible task in runner) |
.npmrc URL-rewrite |
✓ |
gem, composer, go, curl https://… (Ansible task in runner) |
DNS-hijack + frontend HTTPS + runner CA-trust | ✓ |
RUN apt-get install (inner Dockerfile build, Debian/Ubuntu) |
build.extra_hosts DNS-hijack + frontend HTTP |
✓ |
RUN apk add (inner Dockerfile build, Alpine ≤3.17 HTTP) |
build.extra_hosts DNS-hijack + frontend HTTP |
✓ |
RUN pip/npm/gem/composer/go install (inner Dockerfile build) |
none today | ✗ |
RUN apk add (Alpine ≥3.18 HTTPS) |
none today | ✗ |
| App container runtime traffic (HTTPS to external API) | none today | ✗ |
The HTTPS-only inner-build gap requires per-image CA-trust bootstrap, which is out of scope for the current minimal-invasive design.
Compose Wrapper Auto-Detection 🪄
The runner's compose wrapper at roles/sys-svc-compose/files/compose.py auto-detects compose files when invoked from per-app directories under /opt/compose/<app>/:
| File | When |
|---|---|
compose.yml |
always |
compose.override.yml |
when present (role provides it) |
compose.ca.override.yml |
when present (TLS self-signed CA-inject runs) |
compose.cache.override.yml |
generated on the fly when INFINITO_PACKAGE_CACHE_FRONTEND_IP is set; emits build.extra_hosts for every service that has a build: key |
pull.py delegates to the same wrapper so pull and build --pull operations see the identical -f set.
Environment Variables 🌳
The host-side env-vars live in scripts/meta/env/cache/registry.sh and scripts/meta/env/cache/package.sh. They are sourced via scripts/meta/env/all.sh, which BASH_ENV makes available to every Makefile recipe.
Per-variable defaults and purposes are in compose.yml.md under the cache section.
INFINITO_PACKAGE_CACHE_MAX_AGE_MIN (default 129600 = 90 days) controls how long every Nexus proxy repo holds an upstream response before revalidating. The bootstrap helper applies it as contentMaxAge, metadataMaxAge, and negativeCache.timeToLive. Lower it for fast-moving upstreams (apt-debian-security, branch-pinned tarballs) when staleness becomes visible.
Operations 🛠️
| Action | Command |
|---|---|
| Start the stack with caches | make up |
| Stop the stack | make down |
| Wipe local cache state | make cache-clean |
| Manually re-bootstrap Nexus repos | bash scripts/docker/cache/package.sh (after sourcing scripts/meta/env/cache/package.sh) |
| Manually regenerate frontend certs | bash scripts/docker/cache/package-frontend-certs.sh |
| Reload nginx in the frontend | docker exec infinito-package-cache-frontend nginx -s reload |
| Inspect cache hits | docker logs -f infinito-package-cache and docker logs -f infinito-package-cache-frontend |
Cache state persists under /var/cache/infinito/core/cache/. Paths are configurable via the env scripts above.
Adding a New Upstream ➕
When a new package manager or upstream needs caching:
- Register a Nexus proxy repo in package.sh.
- If the upstream uses HTTPS, add it to
HOSTNAMESin package-frontend-certs.sh so a leaf cert is issued. - Add a server-block in upstreams.conf that reverse-proxies onto the new Nexus repo path (rewrite if upstream URL prefix differs from the Nexus repo path).
- Add an
extra_hostsentry on theinfinitoservice in compose/cache.override.yml for runner-side traffic. - If the upstream is HTTP-only and inner-
dockerdbuilds need it, also add it to_CACHE_HTTP_HOSTNAMESin compose.py so the per-appcompose.cache.override.ymlincludes it inbuild.extra_hosts.
Background 📚
- Compose-file env-var contract: compose.yml.md
- Profile gating mechanics: profile.py