10 KiB
Session 0030.0 — Transcript
Date: 2026-05-28 Start: 2026-05-28T16-12 (PST implied) End: 2026-05-28T17-39 (PST implied) Subject: Security-audit-0026 remediation — flotilla-owned fixes + live GCP/VM hardening Status: FINALIZED.
Sanitization note: this transcript is published to the public
wiggleverse/ohm-session-history. Because several audit findings were still live and unpatched on production at session end (they belong to a parallel, not-yet-deployed branch — see "Coordination" below), this record deliberately references findings by ID and general category only and contains no exploit sketches or step-by-step reproduction. The full-detail audit (SECURITY-AUDIT-0026-2026-05-28.md) lives locally in~/git/ohm-infra, which is not a git repository, and was never published. No secret bytes appear here (§3 invariant 1).
Launch prompt
full remediation of security report at ~/git/ohm-infra
The "security report" is SECURITY-AUDIT-0026-2026-05-28.md (produced by
the parallel audit session 0026): 1 Critical, 1 High, 5 Medium, 9 Low,
6 Info findings spanning three surfaces — the rfc-app codebase, the
flotilla CLI (this repo), and the live GCP/VM deployment.
What this session settled
Session 0030 took the flotilla-owned findings and the live GCP/VM findings that live in no repo, applied + verified them, and merged + deployed the flotilla changes. The rfc-app/nginx/systemd findings were left to a parallel in-flight session (see Coordination). Net for OHM: the VM is materially hardened and the operator CLI has three defense-in- depth improvements; the application-layer findings remain pending that other session's release.
The "did we leak the audit?" scare (resolved: NO)
Mid-session the operator flagged that a transcript containing the audit
might have been pushed to the public repo before an internet drop. I
froze remediation and verified the entire git history of
wiggleverse/ohm-session-history (fresh clone, all branches,
git log -S for audit-unique strings):
- Audit session 0026 published only its 454-byte placeholder — its full transcript was never pushed (the publish push is atomic; it failed on the internet drop, leaving only the start-gesture placeholder).
- Zero audit-unique strings appear in any commit, ever.
- The audit file itself lives only in local
~/git/ohm-infra(not a git repo) — never committed anywhere.
Verdict: nothing leaked. The real containment for the audit's value (an exploit roadmap) is shipping the fixes, which is what the session then continued doing.
Coordination — the rfc-app remediation is owned elsewhere
On inspecting /Users/benstull/git/rfc-app I found branch
feature/v0.25.0-security-hardening already checked out with a large
uncommitted remediation (a parallel live session, 0027 or 0029).
It covers the rfc-app application findings, the nginx server block, and
the systemd unit. Editing rfc-app would have clobbered another session's
live work, so — with the operator — Session 0030 explicitly scoped to:
- flotilla repo fixes (no overlap), and
- live GCP/VM ops that exist in no repo.
The operator chose this division (and chose to perform the live ops this session, and to defer SSH-scoping to a proper IAP rollout).
Work performed
Flotilla repo (PR #1 — merged)
Branch session-0030/flotilla-security-hardening → PR #1, merged to
main (merge commit 7b32a46). 161 tests pass.
- L1 — the live secret redactor is threaded into
_PhaseRunnerand now scrubs both the success and failure phase-detailpaths before they are persisted todeploys.phases/ surfaced to the operator (previously only the separateerror_summarywas scrubbed). Test extended to assert no secret survives in any persisted phase detail. (This fix was already present in the working tree at session start; Session 0030 verified, tested, and landed it.) - I6 —
redact_livenow matches several decodings of the same secret bytes (utf-8/replace, surrogateescape, latin-1) so a non-ASCII secret that surfaced via a different decode path than the needle still redacts. Match only ever redacts more, never less. Residual byte- boundary limitation documented honestly (moot for ASCII/UTF-8 secrets). - L9 —
ssh.pyno longer leans on--quiet's blanket first-connect host-key accept; it addsStrictHostKeyChecking=accept-new, which still connects to genuinely-new hosts non-interactively but refuses a changed key. Full first-connect TOFU removal remains the §19.2 IAP item. - I5 —
overlay setwarns (does not block) when a key name carries an unambiguous secret word, nudging towardsecret bind/secret set. Deliberately does not match generic*_KEY/*_API_KEY, so public site keys don't train the operator to ignore the warning.
After merge, a clean idempotent redeploy of the current pin (v0.26.0)
exercised the hardened flotilla code end-to-end: deploys.id=35, all
9 phases ok, health verified in 5.0s.
Live GCP/VM ops (project wiggleverse-ohm, VM ohm-rfc-app)
- M3 — the SQLite data directory was world-traversable and the DB
(plus its WAL/SHM sidecars) world-readable. Tightened the directory to
750(the robust containment — closes world access regardless of per-file mode regression) and the files to640. Verified to survive both a full VM reboot and a deploy-triggered service restart (sqlite preserves the main DB's mode on its sidecars). Also discovered and secured (but did not delete) a set of orphanedohm-app.db\r*files — stale copies left by the historical CRLF path bug; flagged to the operator for deletion after confirmation. - L5 — deleted the dead
default-allow-rdpfirewall rule (3389 is unused surface on Linux). SSH-from-anywhere left in place by operator decision; the right fix is an IAP rollout (§19.2), not fragile IP scoping. - L6 — created a dedicated least-privilege runtime service account
(
ohm-rfc-app-vm@…) with logging+monitoring writer only (the app reads its config from.envat deploy time, so no Secret Manager access is needed on the VM), and swapped the VM off the default compute SA. - L7 — enabled Shielded-VM Secure Boot (boot disk confirmed UEFI-compatible first; vTPM + integrity monitoring were already on).
L6 + L7 were applied in a single VM stop/start window (~15s of downtime), after confirming no deploy was in flight. All four verified intact after the later deploy.
Config verification (audit action item 5)
Read the live .env variable names only (no values):
TURNSTILE_REQUIRED="true"— confirmed (mitigates the OTC abuse Low).GITEA_WEBHOOK_SECRET— present (Gitea webhook authenticated).WEBHOOK_EMAIL_BOUNCE_SECRET— absent. This confirms M5 as a live config gap (the bounce webhook is currently unauthenticated). Handed to the operator with an operator-run secret gesture (never request/echo secret bytes in-conversation — §3 conversation-layer corollary). The operator will bind it and redeploy as a follow-up.
Friction / dead-ends (kept honest)
- The first
chmod 640 …/data/*failed: having just set the directory to750, the non-root SSH login user could no longer list it to expand the glob. Re-ran the file chmod undersudo sh -cso root expanded it. - After merging PR #1,
git checkout main && git pulldid not fast-forward (localmainhad no upstream tracking), so the working tree briefly reverted to pre-fix code. Recovered withgit fetch && git merge --ff-only origin/mainand set the upstream. No work lost (the fixes were safe in the merge commit).
Findings status at session end
| Owner | Findings | Status |
|---|---|---|
| 0030 (flotilla) | L1, I6, L9, I5 | Done — merged (PR #1) + deployed |
| 0030 (live VM/GCP) | M3, L5(RDP), L6, L7 | Done — applied + verified |
| Operator follow-up | M5 (config bind), *.db\r cleanup, systemd UMask=0077 |
Flagged with gestures |
| Deferred | L5 (SSH 0.0.0.0/0 → IAP §19.2) | By operator decision |
| Parallel v0.25.0 branch | C1, H1, M1, M2, M4, L2, L3, L4, L8, I1, I3, I4 | Pending — uncommitted, not yet deployed (still live on prod) |
Cross-references
- flotilla PR #1 (
wiggleverse/ohm-rfc-app-flotilla), merge7b32a46. - OHM deploy
deploys.id=35, v0.26.0 (idempotent redeploy exercising the hardened flotilla). - GCP: VM
ohm-rfc-app/us-central1-a/wiggleverse-ohm; new SAohm-rfc-app-vm@wiggleverse-ohm.iam.gserviceaccount.com; deleted firewall ruledefault-allow-rdp. - Source audit:
~/git/ohm-infra/SECURITY-AUDIT-0026-2026-05-28.md(local only — never published).
Operating instructions for the next session
The application-layer security work is the open thread. Concretely:
- Ship v0.25.0. The
feature/v0.25.0-security-hardeningbranch in rfc-app holds the application + nginx + systemd remediation (C1 XSS sanitizer, the auth-verify rate-limit/lockout, the device-trust lookup fix, security headers, cookie hardening, the bounce-webhook_requiredguard, etc.). It is uncommitted and undeployed, which means the audit's Critical (C1) and other findings are still live on production. This is the highest priority: confirm/finish the branch, commit, release as v0.25.0, bumpohm-rfc/.rfc-app-version, and deploy via flotilla. Coordinate with whichever session owns that branch (was 0027/0029) before touching it. - M5 config — the operator intends to bind
WEBHOOK_EMAIL_BOUNCE_SECRET(Secret Manager +flotilla secret bind+ provider-side config) and redeploy. The code-side_requiredguard rides along in v0.25.0. - systemd
UMask=0077— add to rfc-app'sdeploy/systemd/rfc-app.service(belt-and-suspenders on top of the750data dir). - Orphaned
ohm-app.db\r*files on the VM — secured to640; delete once confirmed orphaned. - L5 SSH / IAP (§19.2) — the deferred item: IAP-only SSH (firewall +
tunnel role + flip flotilla's deploy path to
--tunnel-through-iap), which also retires the L9 first-connect TOFU residue.
Suggested next-session launch prompt:
Ship rfc-app v0.25.0-security-hardening (finish/commit the existing branch, release, bump the OHM pin, deploy) — this lands the still-live audit-0026 application findings incl. the Critical XSS. Coordinate with the session that owns the branch first. Then help the operator bind WEBHOOK_EMAIL_BOUNCE_SECRET (M5) and add systemd UMask=0077.