From d684cfc2e163aab0260052490ff6fecb0a4579e8 Mon Sep 17 00:00:00 2001 From: Grant Whitmer Date: Tue, 11 Aug 2026 16:34:10 -0400 Subject: [PATCH] =?UTF-8?q?G4.2:=20record=20Grant's=20ruling=20=E2=80=94?= =?UTF-8?q?=20use=20an=20existing=20fleet=20CF=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not a debt, a decision: sandbox phase, months from launch, and minting a tenth Cloudflare token to sit in the inventory costs more than it buys. A platform-specific scoped token is a launch-hardening item. Recorded so the next reader knows it was chosen rather than missed, with a do-not-re-raise note. Keeps the genuinely non-obvious part: R2's S3 credentials are DERIVED from a CF API token — access key id = the token's id, secret = SHA-256 of the token value. Co-Authored-By: Claude Opus 5 --- DNA_STRAND_MASTER_PLAN.md | 2 +- SUBSTRATE.md | 33 +++++++++++---------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/DNA_STRAND_MASTER_PLAN.md b/DNA_STRAND_MASTER_PLAN.md index fff8d31..0f83a44 100644 --- a/DNA_STRAND_MASTER_PLAN.md +++ b/DNA_STRAND_MASTER_PLAN.md @@ -258,7 +258,7 @@ Strands G0–G4 are sequential. G5–G12 are concurrent once G4 lands. ## Strand G4 — Storage wiring - **G4.1** R2 buckets: `windy-git-lfs`, `windy-git-artifacts`, `windy-git-backups` on account `193b347aedeaafe35de0b5a534b2d9aa`. -- **G4.2** **Scoped** R2 credential, minted for this cell. ⚠️ The sites cell shipped holding the account-wide god token (`godtoken02JUL26`) because v4 R2 object endpoints reject restricted tokens — record which we ended up with in `SUBSTRATE.md` and treat an account-wide token as a known, named debt, not an invisible one. +- **G4.2** R2 credential: **use an existing fleet token** (Grant's ruling, 2026-08-11 — sandbox phase, months from launch; a scoped platform token is a launch-hardening item, not a blocker). The non-obvious part worth recording: R2's S3 credentials are *derived* from a Cloudflare API token — **access key id = the token's id, secret = SHA-256 of the token value**. - **G4.3** Gitea `[storage]` → `STORAGE_TYPE = minio` pointed at R2, covering LFS, attachments, packages, avatars and actions artifacts. ⚠️ **Known R2 trap:** R2 rejects the default checksum algorithm several S3 clients send; if uploads fail with a checksum error, set the MD5 checksum option. *Accept:* a 500 MB file round-trips through LFS and the object is confirmed present in R2 with a matching etag — **verify against the exact pinned Gitea version's docs, do not trust this key name from memory.** - **G4.4** Git object stores on `/srv/windygit/git` (local NVMe). A test asserts no git object path resolves to a network mount (I-3). - **G4.5** LFS threshold policy: files **> 5 MB** or matching binary/weight extensions (`.safetensors .bin .gguf .pt .ckpt .onnx .zip .mp4 .wav`) go to LFS via a committed `.gitattributes` template applied at repo creation. **Small text files stay in git proper** — LFS-for-everything makes clones slow and operations heavy. diff --git a/SUBSTRATE.md b/SUBSTRATE.md index 1c5fe22..282bd3e 100644 --- a/SUBSTRATE.md +++ b/SUBSTRATE.md @@ -72,31 +72,20 @@ consulted, so a perfect service presents as "the app is broken." 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 +### R2 credential — RULED, not a debt (Grant, 2026-08-11) -**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 cell uses an existing fleet Cloudflare token for R2. **That is the decision, +not an oversight.** Grant's ruling, verbatim in intent: we are months from +launch, in a sandbox, and minting a tenth Cloudflare token to sit in the +inventory costs more than it buys. A platform-specific scoped token gets created +as part of launch hardening. -This is recorded, not hidden, because an account-wide token is an acceptable -named debt and an unacceptable invisible one. +Recorded here so the next reader knows it was chosen rather than missed. **Do not +re-raise it before the launch-hardening pass** — see the standing instruction +about pre-launch security-hygiene nagging. -**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. +Access key id = the API token's id; secret = SHA-256 of the token value. That +derivation is not obvious and is the thing worth writing down. ## Backups (G0.9)