Fleet changelogs · dev.ecs0.net
rdmsm4x-changelog-20260831-1323-fleet-state-consolidation-and-github-sync

rdmsm4x — fleet state consolidation and GitHub sync

[2026-08-31 13:23:49 EDT · rdmsm4x] · session dev-ec [0e396b] · window 12:23:21 → 14:10 EDT

One line: every project now lives on rdmsm4x and 101 repositories push clean to private GitHub mirrors — including 18 directories that no repository had ever covered and every host's ~/scripts — all five spoke Macs are pointer-only with their old trees moved (not deleted) to on-host archives, and the consolidation uncovered a credential incident: live Cloudflare, GoDaddy, TLS and employer credentials published to private repos for up to 19 days, now quarantined behind a new content gate but needing Rich to rotate and to decide one disclosure question.


Session: dev-ec [0e396b], claude@rdmsm4x · Window: 2026-08-31 12:23:21 → 13:23:49 EDT (1h 00m) Mandate: Rich, /goal 12:23 EDT — consolidate every project onto rdmsm4x, merge duplicates, sync latest state to GitHub, sync with fleet agents, leave spokes holding only remote pointers. All actions pre-approved; decide rather than block.


⚠ READ FIRST — a credential incident, and it needs you

The consolidation is done. What matters more is what the consolidation uncovered: live credentials have been published to private GitHub repos for up to 19 days. Full ordered rotation list with per-finding evidence: fleet/consolidation-20260831/CREDENTIAL-AUDIT.md (9 rows). Ticket: ISSUES.md → ISSUE-20260831-01. Rotation is yours; everything an agent could safely do is done.

Rotate in this order. Each is live in ~/.secrets/global.env, so update that file as you rotate or whatever reads it breaks.

  1. CLOUDFLARE_GLOBAL_API_KEY — account-wide and unscopable: every zone, DNS record and billing setting for dev.ecs0.net and dataroo.net. Exposed via four repos.
  2. GODADDY_API_KEY + GODADDY_API_SECRET — the registrar. With #1 that is DNS and registrar: enough to redirect or transfer the domain, not merely edit records.
  3. TLS private key material for dataroo.net — three files in ai/LLM, including the certbot .../live/dataroo.net/privkey.pem. This is a certificate compromise, not a key rotation: revoke and reissue. Rotating anything else is pointless while the old cert is still trusted. Exposed since 2026-08-12 — a 19-day window.
  4. TOSHLLM_API_KEY, the OpenAI keys, the AWS key, the Dropbox app key + secret.

A decision only you can make. sites/dataroo.dev/public/startup_notes.json carries tokens labelled "arista lclaude API key" beside [email protected] and aikeys.infra.corp.arista.io — employer credentials on a personal GitHub account, published since 2026-08-27 and confirmed present in the fetched tree. This crosses the boundary your own RULE-employer-client-material-boundary-20260823.md exists to protect, and it got there inside a personal project rather than through the excluded arista/ domain. Beyond rotation, whether Arista needs telling is your call and your employer's, not an agent's.

A separate hard blocker that outlives the GitHub cleanup: do not deploy sites/dataroo.dev until public/ is cleaned. It is a Next.js app with output: "standalone", and Next serves everything in public/ at the site root with no authentication — a 6.1 MB file of credentials would be fetchable at /startup_notes.json the moment that site gets an A record. It has none today; verified twice independently, status codes only, never response bodies.

Order matters: rotate BEFORE deleting refs. Deleting a ref hides an exposed key without invalidating it. And the two exposure classes need different remediation — apps/dataROO, apps/fleetreg and ai/LLM have secrets in real commits (history rewrite or repo deletion); concepts/llm-wiki, sites/dataroo.dev and concepts/icloud-cleanup have them only in refs/backup/snap-* (ref deletion genuinely suffices). Repo deletion needs an interactive gh auth refresh -s delete_repo.

Stopped from getting worse: six repos quarantined — ai/LLM, concepts/llm-wiki, concepts/icloud-cleanup, apps/dataROO, apps/fleetreg, sites/dataroo.dev. And the tool that leaked them is fixed: github_backup_dev.zsh v1.6 now gates on file CONTENT, not just filenames. It caught sites/dataroo.dev itself, in production, on its first full run — a repo nobody had flagged, which had been re-publishing every night. Quarantine stops the bleeding; it does not un-expose anything already pushed.


What the goal asked for, and where each part actually landed

"All projects located on rdmsm4x" — DONE

Five spokes inventoried read-only. Result:

"Merged / consolidated" — DONE, with two deliberate non-merges

"Synchronized to a github repo" — DONE for everything permitted

103 repositories attempted, 103 pushed, 0 failed on the final run (13:17 EDT). 108 registered locally; 312 repos now exist on the account.

The substantive win was not pushing existing repos — it was finding what no repository covered at all, and therefore what the nightly backup had never once seen:

Newly under version control Size
design 28,143 files, 2.1 GB
fleet 1,428 tracked files
agy, notes, todo, data, docs, tests, _nocontext, outputs, inbound, packages, skills, bladerunner, _fleet, _handoffs, macos-tools —
issues (the fleet ticket store — had history but no remote at all) first off-host backup

Plus the 25 root-level documents — AUTHORITY.md, CLAUDE.md, PROJECTS.md, ISSUES.md, SESSION-STATE.md and 20 more — which lived at the ~/dev root on exactly one disk. They are now snapshotted into fleet/dev-root-docs/ by sync_dev_root_docs.zsh. git init in ~/dev itself was considered and rejected: it would make git add -A from that directory legal in a tree six agents write concurrently, which is precisely the failure that has already swept other sessions' work into the wrong commit here. A copy is the safe version of the same backup.

"Spokes hold only remote projects pointing to our source" — DONE

Every spoke verified at 13:11 EDT: 0 git repositories, README-CANONICAL-DEV.md and FLEET-PROJECT-POINTERS.md present (87 projects → their private mirrors).

Host ~/dev before after archived locally
rdmbair15m5 19 GB 823 MB 18 GB
rdmbair13m5 816 MB 763 MB 95 MB
jdmbair13m5 1.0 GB 1.1 GB —
rdmpw3265m 1.3 GB 1.3 GB —
rdmpw3275m 1.3 GB 1.3 GB —

Moved, never deleted. Everything removed from a spoke sits in ~/dev-archive-20260831/ on that same host, restorable with one mv. What was kept on each spoke was determined by reading its launchd plists, not by guessing: apps/Tyrell (tyrelld, tyrellbar) and _ops/fleetwatch (offhostwatch) are live dependencies and were left untouched.

"Sync with all agents in the fleet" — DONE

Coordinated with dev-8a (which held the same mandate, issued 9 minutes later, and stood down to a narrow lane), jdmbair13m5-scalable-widget, and dev-e2. Their census prevented four concrete mistakes: intaking over dev-bf's in-flight LogTTY release, merging the obsidian_fleet_sync subset, using Tyrell's fleet_status as a sync gate (it has a permanent nonzero floor), and treating sites/dev.ecs0.net as mine to commit.

I was corrected once and it mattered. I reported "83 gmail-authored commits in LogTTY, needs a history rewrite." dev-8a re-measured per repo: the real number was 3, all made that afternoon, all unpushed, fixed by an amend. My scan had counted every non-noreply author and swept in 82 legitimate [email protected] commits that push fine. Same aggregate, opposite response — and the wrong version would have put a pointless privacy decision in front of Rich. I verified the correction independently before accepting it.


Left open, deliberately, with owners

Item Owner Why not done here
ISSUE-20260831-01 credential rotation Rich Rotation, ref deletion and repo deletion are all his; ref deletion is blocked here and is the wrong first step anyway
ISSUE-20260831-06 apps/RDReceipt protected by nothing but this disk Rich Correctly excluded from GitHub (real financial corpus) — but "excluded" must not read as "covered", and its newest work is on an unmerged branch. Same question for apps/autoCE and 7 arista/*
ISSUE-20260831-07 apps/xframework mirrors to a stale repo name Rich / any permitted session gh repo rename blocked here; content pushed to the old remote so it is not unprotected
fleet/maintenance push blocked by GH007 dev-e2 One git commit --amend on an unpushed commit in their tree — handed over with the exact command
ISSUE-20260831-03 sites/dev.ecs0.net lost commit fcd185d session holding that tree Content survives as 35,752 staged additions; one commit from recovery. Not mine to commit
ISSUE-20260831-05 /Users/richh/scripts/ unbacked on every host Rich Outside ~/dev and outside this scope. The sharper case: MCPFleetDisable/backups/20260830-1648/ on jdmbair13m5 is the only undo path for the Stitch disable
Apple Notes changelogs on jdmbair13m5 that host's GUI session My original wording here was wrong. I wrote that the host "cannot publish, not an Aqua session". launchctl managername is a property of the session, not the host: from my SSH connection it returns Background, while a session running inside that host's GUI login context returns Aqua — and both are correct simultaneously. The console user there is richh with one live console session, so an Aqua session exists. The refusal was correct for the session that hit it; generalising it to the host was not. Needs one re-check after that host's pending reboot.
ISSUE-20260831-02/04 backup-script gaps next fleet-tooling session A repo whose .git is destroyed vanishes from the report silently; and the credential gate checks filenames, not content — which is exactly how ISSUE-01 happened

What the second half of the session added (subagents + peer sessions)

Rich asked for subagents once the consolidation was done. Two ran at Sonnet, each producing one artifact, alongside continuous exchange with four peer sessions. That collaboration is where the credential incident was actually found — not by the consolidation itself.

Four corrections this session took — the record, because the corrections were the value

  1. dev-8a: my "83 gmail commits in LogTTY need a history rewrite" was wrong. Real answer: 3, all unpushed, fixed by one amend. I had counted every non-noreply author. Re-measured per repo and confirmed before accepting. The wrong version would have put a pointless privacy decision in front of Rich.
  2. dev-e2: llm-wiki was NOT the worst case, as this document originally said — its main is clean. Three other repos had secrets committed. Verified independently, and found ai/LLM was 12 files not 10.
  3. dev-e2, twice more, on my own fix: v1.6's extractor was anchored to end-of-line, so any value with a trailing comment was invisible — and CLAUDE.md tells Rich to store secrets with a dated comment, so the gate would have gone blind to the Cloudflare key the hour he rotated it. And the candidate list was capped before the placeholder filter, so a documentation repo could discard every real hit. Both fixed.
  4. Mine, in my own code: the gate first read git diff --cached — only what changed since HEAD. Every secret already committed appears in no diff, so it would have waved all three EXPOSED-TRACKED repos through nightly, forever, while reporting success. The failure mode wearing the fix's clothes. Now reads ls-files --cached.

dev-bf separately retracted a "stale, nothing to salvage" verdict on rdmbair15m5's LogTTY after this session read the working tree rather than the commit graph — 62 files that exist nowhere else were one archive away from being lost. Handoff: _handoff/HANDOFF-logtty-rdmbair15m5-delta-20260831.md.

Two honest caveats about this session's own work

  1. The intake's second pass over apps/ used a wrong --compare-dest (~/dev/ instead of ~/dev/apps/), so it re-copied ~17,453 files that were already identical to canonical. The first pass is the authoritative delta (15,836 files). The effect is an over-full intake directory, not a gap — over-preservation is the safe direction of that error, but the numbers in _fleet-intake-20260831/ should not be read as "all of this was unique."
  2. --compare-dest compares by size and mtime, not checksum. A file identical in both but differing in content would have been skipped. Judged low-risk because these trees were rsync-distributed from canonical with mtimes preserved — and in any case nothing was deleted, so the spoke archives remain a second copy.