reshape: move OHM transcripts under ohm/ for org-wide multi-app layout; add org-wide root README
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
# Session 0032.0 — Transcript
|
||||
|
||||
> Date: 2026-05-28
|
||||
> Start: 2026-05-28T18-52 (PST implied) · End: 2026-05-28T19-13
|
||||
> Goal: Clear the two Session-0030 leftovers — harden OHM SSH to IAP-only
|
||||
> (audit-0026 L5/L9) and ship flotilla v1.2.0 — plus the minor leftovers
|
||||
> (audit I3/I4 as rfc-app v0.28.0, and the orphaned VM db files).
|
||||
>
|
||||
> Post-publish addendum: after first publish the operator authorized the
|
||||
> assistant to drive the rest. **PR #2 merged** (`019c8a9`), **rfc-app
|
||||
> `v0.28.0` tagged** (gitea API), **ohm-rfc pin bumped → 0.28.0**
|
||||
> (`a0e4234`), and **deployed to OHM over IAP** — `deploys.id=39`, all 9
|
||||
> phases green, **live `version=0.28.0` status=ok**. First deploy attempt
|
||||
> (`id=38`) failed at phase 3 because the VM fetches rfc-app from the
|
||||
> `git.benstull.org` mirror, which didn't yet have the tag; pushing
|
||||
> `main`+`v0.28.0` to the `benstull` remote fixed it (the v1.2.0
|
||||
> robustness released the lock cleanly on the failure). The operator also
|
||||
> set a standing rule: the assistant is empowered to merge/tag/pin/deploy;
|
||||
> only secret/config-value loading stays manual. Cut-state, ledger, and
|
||||
> operator-plate below updated to match.
|
||||
>
|
||||
> Outcome: **All four tasks landed. OHM SSH is now IAP-only (L5 closed,
|
||||
> L9 residue retired); the VM was re-verified healthy end-to-end through
|
||||
> flotilla's own SSH path. flotilla v1.2.0 was found already merged +
|
||||
> tagged by parallel session 0031 — independently re-verified (184 tests,
|
||||
> both L1-redaction and v1.2.0 verbs coexist), no new PR needed. Orphaned
|
||||
> `ohm-app.db\r*` files deleted (confirmed orphaned). rfc-app v0.28.0
|
||||
> (audit I3 + I4) opened as PR #2 for the operator to merge + tag +
|
||||
> deploy.**
|
||||
|
||||
---
|
||||
|
||||
## Plan / TODO
|
||||
|
||||
- [x] Claim session ID (→ 0032), read SESSION-PROTOCOL.md + audit-0026.
|
||||
- [x] Task 2 — flotilla v1.2.0 merge (turned out: already done by 0031; verify).
|
||||
- [x] Task 1 — IAP SSH hardening (additive → verify → delete → flip → re-verify).
|
||||
- [x] Task 4 — rm orphaned VM db files (confirm orphaned first).
|
||||
- [x] Task 3 — rfc-app v0.28.0 (audit I3 + I4) as a PR.
|
||||
- [x] Post-publish (operator-authorized): merge PR #2 → tag → pin → deploy → verify live v0.28.0.
|
||||
- [x] Scan for next parallel work (recommended #21 Part A; operator continued in the parallel session).
|
||||
- [x] Finalize + publish transcript (this file; re-published after each post-publish step).
|
||||
|
||||
---
|
||||
|
||||
## Pre-session state
|
||||
|
||||
- **flotilla** (`/Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla`):
|
||||
local `main` == `origin/main` == `ef34ffe` (already the v1.2.0 merge
|
||||
commit), tag `v1.2.0` present locally + on remote (`cae3fda`→`ef34ffe`).
|
||||
A leftover **locked** worktree `agent-ab800baf41f74f86d` (c8804a6) from
|
||||
another session sat in `.claude/worktrees/`.
|
||||
- **rfc-app** (`/Users/benstull/git/rfc-app`): `main` == `fe044ed`
|
||||
(Release v0.27.0 merge), clean. Latest tag `v0.27.0`.
|
||||
- **OHM live**: HTTP 200, `version=0.27.0`, `status=ok`. ohm-rfc pin =
|
||||
`0.27.0`.
|
||||
- **GCP** `wiggleverse-ohm`: firewall had `default-allow-ssh` (0.0.0.0/0
|
||||
→ tcp:22), no `default-allow-rdp`. IAP API **not** enabled. `ben.stull`
|
||||
= `roles/owner` + secretmanager admin/accessor; no IAP binding.
|
||||
- **Other live sessions** at claim time (from `claim-session-id.sh`):
|
||||
`0026.0` and `0027.0` still `--INPROGRESS`. This drove the
|
||||
isolated-worktree discipline for every hands-on git step.
|
||||
|
||||
---
|
||||
|
||||
## Turn-by-turn arc
|
||||
|
||||
### Arc 1 — Claim + orient
|
||||
|
||||
Claimed session **0032** via `claim-session-id.sh --start 2026-05-28T18-52
|
||||
--prompt-file …` (pushed the placeholder; clean claim at 073db8a). Read
|
||||
`SESSION-PROTOCOL.md` and `SECURITY-AUDIT-0026-2026-05-28.md` end to end.
|
||||
Skimmed (did not fully read) the 2245-line ROADMAP — the launch prompt
|
||||
fully specified the work. Relevant audit findings: **L5** (SSH+RDP world-
|
||||
open), **L9** (`ssh.py --quiet` TOFU), **L1** (phase-detail redaction —
|
||||
the flotilla merge conflict), **I3** (dead HTML-email branch), **I4**
|
||||
(sync Turnstile siteverify).
|
||||
|
||||
### Arc 2 — Task 2: flotilla v1.2.0 (already merged by 0031; verify-only)
|
||||
|
||||
Inspecting topology revealed `trial-merge-v1.2.0` is fully contained in
|
||||
`main`; `main` == `origin/main` == `ef34ffe` == tag `v1.2.0`, all pushed.
|
||||
The reflog showed two `merge origin/main: Fast-forward` hops — a parallel
|
||||
session (**0031**, branch `session-0031/merge-v1.2.0`, since deleted from
|
||||
the remote) had already performed the 3-way merge, pushed to `main`, and
|
||||
tagged `v1.2.0`. So the "stuck branch" the launch prompt described was
|
||||
resolved before this session opened.
|
||||
|
||||
Rather than trust it, **independently verified** the merge kept both
|
||||
changes: created an isolated detached worktree at `/tmp/flotilla-verify-0032`
|
||||
(`git worktree add --detach v1.2.0`) with its own venv, `pip install -e .`
|
||||
+ pytest. Confirmed in `deploy.py` `_PhaseRunner.run` that **all three**
|
||||
detail paths — `_PhaseFailure` (line 251), `ssh.SshError` (275), and the
|
||||
success path (285) — apply `self._redact(...)` (the **L1** live-redaction),
|
||||
*and* the v1.2.0 `except ssh.SshError` robustness branch + preflight probe
|
||||
are present. The L1 redaction test (`test_deploy.py:278`) explicitly
|
||||
asserts secret bytes are absent from persisted `deploys.phases` detail and
|
||||
iterates every phase. **184 tests passed.** Removed the verify worktree.
|
||||
No new PR — Task 2 was already shipped by 0031; this session's value was
|
||||
the independent re-verification.
|
||||
|
||||
### Arc 3 — Task 1: IAP SSH hardening (L5 + L9)
|
||||
|
||||
Confirmed there is **no** `default-allow-rdp` rule in this project (the
|
||||
audit's L5 RDP half doesn't apply here) — SSH was the only world-open rule.
|
||||
Did the additive steps first (none remove access):
|
||||
|
||||
1. `gcloud services enable iap.googleapis.com` (was disabled).
|
||||
2. Created firewall rule **`allow-iap-ssh`** (INGRESS, ALLOW tcp:22,
|
||||
source `35.235.240.0/20`).
|
||||
3. Granted `ben.stull@wiggleverse.org` **`roles/iap.tunnelResourceAccessor`**
|
||||
(explicit, even though `roles/owner` already implies it — hygiene for a
|
||||
future least-priv reduction per audit L6).
|
||||
|
||||
**Verified IAP SSH BEFORE removing anything** (the operator's hard gate):
|
||||
`gcloud compute ssh ohm-rfc-app --tunnel-through-iap --command …` →
|
||||
`hostname=ohm-rfc-app`, `whoami=ben_wiggleverse_org` (OS Login),
|
||||
`ohm-rfc-app.service active`. Only then **deleted `default-allow-ssh`**
|
||||
(0.0.0.0/0). SSH ingress is now IAP-only.
|
||||
|
||||
Flipped the flotilla deployment record:
|
||||
`flotilla deployment update ohm-rfc-app --vm-tunnel-through-iap` →
|
||||
`target_vm_tunnel_through_iap=True`. **Re-verified end-to-end through
|
||||
flotilla's own code path** (not just gcloud): a small script built an
|
||||
`ssh.SshTarget` from the live registry record (so `tunnel_through_iap`
|
||||
flowed through `ssh.py`, which appends `--tunnel-through-iap` at line 90)
|
||||
and ran `ssh.run_remote(...)` → returncode 0, `service active`. A real
|
||||
`flotilla deploy` would now succeed via IAP. `flotilla deploy status` →
|
||||
HTTP 200 v0.27.0 ok (HTTP path unaffected). **L5 closed; L9 first-connect
|
||||
TOFU residue retired** (PR #1 had already swapped the bare `--quiet` TOFU
|
||||
for `StrictHostKeyChecking=accept-new`; IAP now removes the first-connect
|
||||
exposure entirely).
|
||||
|
||||
### Arc 4 — Task 4: orphaned VM db files
|
||||
|
||||
With IAP SSH warm, inspected `/opt/ohm-rfc-app/backend/data/` (dir is
|
||||
`750 ohm-rfc-app` — consistent with **M3** already remediated; files are
|
||||
mode `640`, no longer the audited 644). The data dir held the live trio
|
||||
(`ohm-app.db` / `-shm` / `-wal`, May 29, held open by the service MainPID
|
||||
3287 per `/proc/<pid>/fd`) plus three stale files dated May 28 04:01.
|
||||
|
||||
A `find -name '*[[:cntrl:]]*'` matched **zero** files, and `od -c` showed
|
||||
the stale names contain a **literal backslash-`r`** (two ASCII chars
|
||||
`\` `r`), **not** a carriage return — exactly the `ohm-app.db\r*` the
|
||||
launch prompt named. Confirmed orphaned three ways: not in the service's
|
||||
open fds, stale mtime, and the anomalous backslash in the name. To delete
|
||||
safely without backslash-quoting hazards through gcloud→shell→find, used a
|
||||
**time-based predicate** (`! -newermt '2026-05-29 00:00:00'`), dry-ran it
|
||||
(matched exactly the 3 orphans, none of the live trio), then re-ran with
|
||||
`-delete`. Re-listed: only the live trio remains. `deploy status` → 200 ok.
|
||||
|
||||
### Arc 5 — Task 3: rfc-app v0.28.0 (audit I3 + I4)
|
||||
|
||||
Isolated worktree `/tmp/rfc-app-v0280-0032` on new branch
|
||||
`feature/v0.28.0-email-turnstile-async` (own venv from
|
||||
`backend/requirements.txt` + pytest). Baselined the turnstile (5) and
|
||||
email_envelope (15) tests green first.
|
||||
|
||||
- **I3** — `email_envelope.build_envelope`'s `body_html` branch is dead
|
||||
(the only caller was a test; no production path passes it). Replaced the
|
||||
`multipart/alternative` rendering with a loud guard: `body_html is not
|
||||
None` → `raise NotImplementedError`, with a comment tying it to the C1
|
||||
stored-XSS class in the mail channel. Kept the arg for documented future
|
||||
symmetry. Flipped the test to assert the guard + added a `body_html=None`
|
||||
boundary test.
|
||||
- **I4** — made `turnstile.verify_token` `async`, awaiting
|
||||
`httpx.AsyncClient`. **Dead end:** first cut had the test monkeypatch the
|
||||
global `httpx.AsyncClient`, which broke app boot (gitea/docs/TestClient
|
||||
also construct `AsyncClient`) — 4 tests failed. Fixed by isolating the
|
||||
call behind a narrow `async _siteverify_post(url, data)` seam and patching
|
||||
*that* in tests. Updated the sole caller (`main.py`) to `await`. Added a
|
||||
coroutine-contract unit test.
|
||||
|
||||
Bumped `VERSION`→0.28.0 + mirrored `frontend/package.json`; wrote the
|
||||
CHANGELOG entry (no-op upgrade steps). **Full backend suite: 365 passed.**
|
||||
Committed as `Release v0.28.0`, pushed the branch, and opened **PR #2**
|
||||
(`ben.stull/rfc-app/pulls/2`) via the gitea API using the host's cached
|
||||
git credential (never printed). Removed the worktree.
|
||||
|
||||
### Arc 6 — Post-publish: ship v0.28.0 to prod (operator-authorized)
|
||||
|
||||
After the first transcript publish, the operator said "you can merge pr #2,"
|
||||
then "full tag+deploy+pin-bump — you do it," and set a **standing rule**:
|
||||
the assistant is empowered to merge / tag / bump-pin / deploy end-to-end;
|
||||
the only manual operator gestures are **loading secret or config values**.
|
||||
(Saved as memory `feedback_empowered_to_merge_and_deploy`.)
|
||||
|
||||
Executed the release loop:
|
||||
1. **Merged PR #2** via gitea API (`Do: merge`) → merge commit `019c8a9`
|
||||
on `main`; fast-forwarded local `main` (VERSION=0.28.0).
|
||||
2. **Tagged `v0.28.0`** via the gitea tag API at `019c8a9` (annotated,
|
||||
tagger "Gitea" — matching prior releases).
|
||||
3. **Bumped the pin** `ohm-rfc/.rfc-app-version` → `0.28.0`, committed
|
||||
(`a0e4234`) + pushed to gitea `ben/ohm-rfc`. `flotilla pin check`
|
||||
resolved 0.28.0, ahead of the live v0.27.0.
|
||||
4. **First deploy attempt failed** (`deploys.id=38`, phase 3 fetch+checkout:
|
||||
`error: pathspec 'v0.28.0' did not match`). Root cause: the VM's rfc-app
|
||||
`origin` is the **mirror** `git.benstull.org/benstull/rfc-app`, which had
|
||||
tags only through v0.27.0 — the tag I made on git.wiggleverse.org hadn't
|
||||
propagated. The v1.2.0 robustness recorded the failure and **released the
|
||||
lock cleanly** (no stuck `in_progress`). Fixed by `git push benstull main
|
||||
v0.28.0`. (Saved as memory `project_rfc_app_deploys_from_mirror`.)
|
||||
5. **Re-deployed** (`deploys.id=39`) — all 9 phases green over IAP; verified
|
||||
live `{"version":"0.28.0","status":"ok"}`.
|
||||
|
||||
### Arc 7 — Next parallel-work scan (no work done)
|
||||
|
||||
The operator asked what could be kicked off in parallel beyond the
|
||||
in-flight sessions. Checked the live `--INPROGRESS` files: `0026`/`0027`
|
||||
(claimed 13-46, no launch prompt captured — likely stale) and a fresh
|
||||
`0033` (19-44). Scanned the ROADMAP item inventory + parallelization tracks:
|
||||
most open items are blocked on operator input (#22 consent copy, #25/#31b
|
||||
need screenshots) or collision-prone (#28 Parts 2+3 touch PR/comment/
|
||||
notification surfaces). Recommended **#21 Part A** (Amplitude
|
||||
instrumentation-hygiene slice — isolated analytics/DOM surface, no new
|
||||
secret) as the parallel-safe pick and drafted a paste-ready launch prompt.
|
||||
The operator then said the other session had finished and they'd continue
|
||||
the work there — so **no new work was started from this session**; the
|
||||
recommendation stands in the next-session prompt below.
|
||||
|
||||
---
|
||||
|
||||
## Cut state (end of session)
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| flotilla | `main`/`origin/main` = `ef34ffe`, tag `v1.2.0` (merged+tagged by session 0031; re-verified here, 184 tests). No change this session. |
|
||||
| rfc-app | **PR #2 merged** → `main` = `019c8a9`; **tag `v0.28.0`** created (also pushed to the `benstull` mirror). |
|
||||
| OHM live | **`deploys.id=39`, v0.28.0, HTTP 200 status=ok** (deployed over IAP; `id=38` failed first on the mirror-tag gap). Stayed healthy throughout. |
|
||||
| ohm-rfc pin | **`0.28.0`** (`a0e4234`, pushed to gitea `ben/ohm-rfc`). |
|
||||
| GCP firewall | `default-allow-ssh` **deleted**; `allow-iap-ssh` (35.235.240.0/20→tcp:22) added. SSH is IAP-only. |
|
||||
| GCP IAM | `ben.stull` granted `roles/iap.tunnelResourceAccessor`; IAP API enabled. |
|
||||
| flotilla record | `ohm-rfc-app.target_vm_tunnel_through_iap = True`. |
|
||||
| VM data dir | orphaned `ohm-app.db\r*` (×3) deleted; live trio intact, mode 640. |
|
||||
|
||||
| Ledger | Status |
|
||||
| --- | --- |
|
||||
| Task 1 — IAP SSH hardening (L5 + L9) | ✅ shipped + re-verified |
|
||||
| Task 2 — flotilla v1.2.0 | ✅ (shipped by 0031; independently re-verified) |
|
||||
| Task 3 — rfc-app v0.28.0 (I3 + I4) | ✅ shipped — PR #2 merged (019c8a9), tagged, pin bumped, **deployed to OHM (id=39), live v0.28.0** |
|
||||
| Task 4 — orphaned VM db files | ✅ deleted |
|
||||
|
||||
---
|
||||
|
||||
## §19.2 / process candidates surfaced
|
||||
|
||||
1. **No lightweight SSH-only flotilla verb.** Re-verifying the deploy SSH
|
||||
path end-to-end required a hand-rolled `ssh.run_remote` script because
|
||||
the only verb that SSHes is a full, service-restarting `deploy` (the
|
||||
`deploy status/watch/reconcile` subverbs are HTTP-only). A
|
||||
`flotilla deploy preflight` / `ssh-check <deployment>` verb (read-only
|
||||
remote probe through the configured SSH path) would make IAP-style
|
||||
cutovers and connectivity checks first-class instead of ad-hoc.
|
||||
2. **Parallel-session merge already done.** Session 0031 completed Task 2's
|
||||
merge + tag while this session was being launched, so the launch prompt
|
||||
was stale on arrival. Reinforces the value of the `--INPROGRESS`
|
||||
live-session signal and of *verifying before redoing* — but a session
|
||||
that hadn't checked could have re-merged and produced a divergent tag.
|
||||
|
||||
---
|
||||
|
||||
## What lands on the operator's plate
|
||||
|
||||
1. ~~Tag + deploy rfc-app v0.28.0~~ **DONE this session** — merged,
|
||||
tagged, pin-bumped, deployed (id=39), live v0.28.0. New standing rule:
|
||||
the assistant drives merge/tag/pin/deploy; only secret/config-value
|
||||
loading stays a manual operator gesture.
|
||||
2. **IAP is now load-bearing for all VM access.** Any manual SSH MUST use
|
||||
`--tunnel-through-iap`; the operator keeps `roles/iap.tunnelResourceAccessor`.
|
||||
If ever locked out, re-add the firewall rule via gcloud (no SSH needed)
|
||||
or use the serial console.
|
||||
3. **Leftover locked flotilla worktree** `agent-ab800baf41f74f86d` in
|
||||
`.claude/worktrees/` belongs to another session — left untouched; clear
|
||||
it if that session is dead.
|
||||
|
||||
---
|
||||
|
||||
## Prompt the operator can paste into the next Claude Code session
|
||||
|
||||
```
|
||||
OHM driver session. This session reserves its own ID at start
|
||||
(claim-session-id.sh) — the number below is advisory.
|
||||
|
||||
State as of session 0032 (2026-05-28):
|
||||
- OHM live = rfc-app v0.28.0 (deploys.id=39), HTTP 200 ok. ohm-rfc pin = 0.28.0.
|
||||
- OHM VM SSH is now IAP-ONLY (audit-0026 L5 closed, L9 residue retired).
|
||||
Manual SSH needs `gcloud compute ssh ohm-rfc-app --tunnel-through-iap
|
||||
--zone us-central1-a`. flotilla deploy handles IAP automatically
|
||||
(deployment record target_vm_tunnel_through_iap=true).
|
||||
- flotilla v1.2.0 merged + tagged (session 0031, re-verified by 0032).
|
||||
- rfc-app v0.28.0 (audit-0026 I3 + I4) is FULLY SHIPPED + LIVE.
|
||||
- NOTE: the VM fetches rfc-app from the git.benstull.org MIRROR, so after
|
||||
tagging a release on git.wiggleverse.org you MUST also `git push benstull
|
||||
main v<ver>` before `flotilla deploy` (else phase 3 fails on a missing
|
||||
pathspec). The assistant is empowered to merge/tag/pin/deploy; only
|
||||
secret/config-value loading is a manual operator gesture.
|
||||
|
||||
No open release item. Recommended next (parallel-safe, isolated surface):
|
||||
ROADMAP #21 Part A — Amplitude instrumentation-hygiene slice (session-replay
|
||||
masking of OTC/passcode/email/PII inputs, autocapture aria-labels +
|
||||
data-amp-track-* on list rows, event-name/prop-shape sweep, SPEC §21
|
||||
touch-up). No new secret; ship as the next available rfc-app minor. Defer
|
||||
the data-informed taxonomy review (needs a week of dashboard data).
|
||||
Otherwise the ROADMAP tail: audit-0026 Critical/High/Medium shipped in
|
||||
v0.27.0, I3/I4 in v0.28.0; remaining audit items are the Low ops tail (L4
|
||||
systemd sandbox, L6 dedicated SA, L7 Secure Boot, L8 server_tokens), and
|
||||
#28 Parts 2+3 (PR cross-ref offer-to-create — collision-prone, serialize
|
||||
against collaboration-track sessions).
|
||||
|
||||
Reminder: the VM fetches rfc-app from the git.benstull.org MIRROR, so after
|
||||
tagging you MUST `git push benstull main v<ver>` before `flotilla deploy`
|
||||
or phase 3 fails. VM SSH is IAP-only (manual SSH needs --tunnel-through-iap).
|
||||
The assistant is empowered to merge/tag/pin/deploy; only secret/config-value
|
||||
loading is a manual operator gesture. Standard protocol: claim a session ID
|
||||
first, finalize + publish the transcript last; give me the next-session
|
||||
handoff prompt in chat before publishing.
|
||||
```
|
||||
Reference in New Issue
Block a user