Fleet changelogs · dev.ecs0.net
rdmsm4x-changelog-20260831-1255-ecs0-git-recovery-served-credential-and-docroot-cleanup

rdmsm4x — dev.ecs0.net .git recovery, a served credential, and docroot cleanup

When: 2026-08-31 12:34–12:56 EDT · Host: rdmsm4x · Author: claude@rdmsm4x Scope: rdmsm4x only. ~/dev/issues, ~/dev/sites/dev.ecs0.net, ~/Desktop/issues.

Summary

Cleared 276 fixture pages back out of the served docroot, closed the root cause that kept putting them there, recovered a deleted git repository without losing a file, and caught a live-shaped API key that was being served and republished on every build.

1. Fixture pages had returned to the served docroot

wiki/issues/ held 375 pages against a 120-ticket store: 276 orphans served, 23 real tickets unpublished. 275 of the 276 matched IDs already in ~/dev/issues/archive/; the 1 remainder was the long-slug ISSUE-20260829-01 duplicate. All 276 moved (not deleted) to ~/dev/issues/archive/refixtured-docroot-20260831/. Docroot went 375 → 97 → 120, then matched the store exactly.

2. Root cause closed — tests no longer write the canonical store

tests/isolation.py (authored by another session) jails all 8 test files with a guard_not_canonical() regression check. Verified by running the full suite: the canonical store went 120 → 120, zero tickets added, Desktop stayed clean. That is the mechanism that produced 91% fixture noise; it is closed.

3. ~/dev/sites/dev.ecs0.net/.git had been deleted — ISSUE-20260831-02

Present 2026-08-30 12:22, absent 2026-08-31 (confirmed independently by two other sessions). Every local commit after the last push (a842ceb, 08-28 20:11) was lost, roughly two days. No file content was lost — the working tree was intact.

Recovery: re-cloned the backup remote (authoritative — 6 ahead of origin, nothing origin-only) with --no-checkout, moved its .git into place, git reset (mixed only). Never a checkout, never --hard — 0 files missing vs HEAD afterwards. Committed as 6171f75 on recovery/local-history-lost-20260831, deliberately not main, so the last-pushed state stays reachable.

7 orphaned worktrees rebuilt by hand (git worktree repair cannot help once the admin dirs are gone). Six were clean; codex-agent-catalog-shell-20260827 held 50 files of live codex work from 08-29. Not committed on their behalf — captured via git stash create (makes a commit object, touches neither tree nor index) at refs/snapshots/codex-agent-catalog-shell-20260831 (5e83825).

4. SECURITY — a served Google API key, ISSUE-20260831-03

Two pages (/agy/1783381242166.html, /agy/artifact-docs.html) carried a Google API key in a Warp login callback URL and returned 200. Found while scanning the recovery commit before pushing it to GitHub.

Fixed at the ingest point in build_site.py (backup build_site.py.bak-20260831): credential-shaped source files are skipped and any published copy withdrawn, and the build now prints every withheld page instead of failing silently.

Verified both directions. Catches: both pages 404 after rebuild, zero credential-shaped content under wiki/. Does not overshoot: the first generic pattern withheld 44 pages including false positives on generated session codenames (secret: rusty_anchor_...); requiring the value to carry a digit and an uppercase letter dropped that to 34 and republished 10 legitimate pages. Site root, /issues/ and /agents.html still 200; 660 agy pages still publish.

OWNER ACTION: rotate the key. The guard stops publication; it does not invalidate a key that was already served. The same shape also appears in several non-published trees under ~/dev, so rotation is the only reliable remedy.

Verification

store 125 == served docroot 125 == ~/Desktop/issues/tickets 125 ; 0 ambiguous IDs
/ 200   /issues/ 200   /agy/ 200   /agy/artifact-docs.html 404
credential-shaped files under wiki/: 0
full test suite: canonical store 120 -> 120, 0 tickets added
worktrees registered: 8 (was 1 + 7 orphaned)

Always verify this site with curl -H 'Host: dev.ecs0.net' http://127.0.0.1:8788/... — without the Host header the gateway catch-all 404s every path, and the public URL 302s to Cloudflare Access before the origin is reached. Both mask real errors.

Commits

NOT DONE — needs Rich

Undo

Nothing was deleted — every removal was a move.