Files
windy-git/SUBSTRATE.md
Grant Whitmer ef75ee5a9c G4: R2 buckets live, Gitea storage wired, checksum trap pinned
Three buckets created. S3 round-trip against R2 proven end to end
(PUT/GET/DELETE) before any of it was wired in.

Includes the R2 checksum trap: R2 rejects the algorithm S3 clients send by
default, and the resulting error reads like a credential problem and is not one.

Records a NAMED DEBT in SUBSTRATE.md: the R2 credential is currently the
account-wide god token, because no available token can mint a scoped one. Gated
— it must be replaced before G7 puts CI runners on this host, since I-5 exists
precisely to keep untrusted job code away from broadly-scoped credentials.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 14:49:26 -04:00

4.5 KiB

SUBSTRATE.md — windy-git

What runs where, what is truth, and what is only a cache.

Hosts

Rung Host Role Status
R0 Veron 1 (Veron-1-5090, WireGuard 10.10.0.6) everything current
R1 dedicated VPS (not Kit 0) control plane on first external push
R2 VPS + read replica, dedicated runner box split p95 clone > 3s

Veron 1, measured 2026-08-11: 24 cores · 251 GB RAM · 3.6 TB root, 978 GB free · load 1.52 · $0/mo.

⛔ Kit 0 (72.60.118.54) is never a host for this service. D-4, and there is a boot guard in api/app/main.py that refuses to start there in production.

Ports (all bound to localhost; the tunnel is the only ingress)

Port Service
8600 windy-git-api — our plane
3080 Gitea — host 3000 and 3300 are taken by resident projects on Veron 1
5432 Postgres
2000 cloudflared metrics (probe target)

Ingress — Cloudflare Tunnel windy-git

Zone windygit.com = 9d8637dcac3415607b2116e6099fe567 · account 193b347aedeaafe35de0b5a534b2d9aa · Free plan.

Hostname →
app.windygit.com Gitea :3080 — UI and git over HTTPS
api.windygit.com our plane :8600
models.windygit.com HF-compatible endpoint :8600 (v2)
windygit.com Cloudflare Pages — marketing, Grant-gated

No inbound port is opened. The tunnel connects outbound, so the residential dynamic IP is irrelevant.

⚠️ All hostnames are single-level subdomains, on purpose. Free Universal SSL covers windygit.com + *.windygit.com and stops there. A two-level name needs Advanced Certificate Manager ($10/mo) and without it the request dies in the TLS handshake — curl exit 35, no HTTP status at all — before the app is ever consulted, so a perfect service presents as "the app is broken."

Storage — what is truth

Store Holds Truth?
/srv/windygit/git (local NVMe) git object databases truth
Postgres windgit repos, grants, versions, tokens, mirrors truth
Gitea's own DB Gitea's private state component-owned; we never write it (I-1)
R2 windy-git-lfs LFS objects truth for blobs
R2 windy-git-artifacts CI artifacts, logs derived
R2 windy-git-backups nightly pg_dump + git bundles derived
GitHub mirror full copy of every repo belt and suspenders (I-4)
3 TB HDD periodic cold copy derived

I-3: git objects never go to object storage; LFS blobs never go to host disk.

Pinned versions

Component Version Note
Gitea 1.24.6 exact pin, never latest (G2.1). A drift test fails make check if the running version differs.
Postgres 16-alpine
Python 3.12 matches every sibling cell

Credentials

All in the fleet lockbox, injected by env, never committed. make check fails on any cfat_ / cfut_ / gh[pousr]_ / et_plt_ literal in the tree.

⚠️ NAMED DEBT — the R2 credential is account-wide

As of 2026-08-11 this cell holds the Cloudflare god token as its R2 credential. R2's S3 credentials are derived from an API token (access key id = the token's id, secret = SHA-256 of its value), and no token available to this session has permission to mint a new one — creating tokens is dashboard-only or needs a token-creating token. So the wiring was proven with the god token rather than blocked on it.

This is recorded, not hidden, because an account-wide token is an acceptable named debt and an unacceptable invisible one.

GATE: this must be replaced with a scoped R2 token BEFORE strand G7 lands CI runners on this host. I-5 says runners execute untrusted code and must not share a kernel with credentials scoped beyond their own job; a god token with R2 + Workers + Pages + WAF + SSL rights sitting on the same box as a runner is exactly the thing I-5 exists to prevent.

Minting one is a two-minute job in the Cloudflare dashboard: R2 → Manage R2 API Tokens → Create → Object Read & Write, scoped to the three windy-git-* buckets. Then set R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY in /srv/windygit/src/.env and redeploy.

⚠️ The Cloudflare god token has Zone:Read but no DNS:Edit. Use the DNS:Edit token for record creation.

Backups (G0.9)

Nightly pg_dump → R2 · nightly git bundle per repo → R2 · quarterly restore drill via make restore-drill, with a written, dated result. The ecosystem currently has no rehearsed restore for anything, anywhere.