From e7fbf986fbad0e600c398ef15341b6be312fad5b Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 10 Jun 2026 17:42:52 +0000 Subject: [PATCH] workstation: rename tmux persistence out of the t3 namespace [ci skip] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Viktor's correction: this feature is about the tmux web-terminal sessions, not t3 — t3 auto-saves its own threads (~/.t3 state + daily t3-backup-state). Renamed t3-tmux-sessions -> tmux-persist (units tmux-persist-save.timer / tmux-persist-restore.service, state /var/lib/tmux-persist), header rescoped to say exactly that. Same mechanism, correct taxonomy. Old units removed, state migrated, re-verified live (5 emo + 3 wizard sessions snapshotted). Co-Authored-By: Claude Fable 5 --- docs/architecture/multi-tenancy.md | 2 +- scripts/t3-provision-users.sh | 4 ++-- ...e.service => tmux-persist-restore.service} | 2 +- ...save.service => tmux-persist-save.service} | 2 +- ...mux-save.timer => tmux-persist-save.timer} | 0 .../{t3-tmux-sessions.sh => tmux-persist.sh} | 19 ++++++++++++------- 6 files changed, 17 insertions(+), 12 deletions(-) rename scripts/{t3-tmux-restore.service => tmux-persist-restore.service} (87%) rename scripts/{t3-tmux-save.service => tmux-persist-save.service} (73%) rename scripts/{t3-tmux-save.timer => tmux-persist-save.timer} (100%) rename scripts/{t3-tmux-sessions.sh => tmux-persist.sh} (85%) diff --git a/docs/architecture/multi-tenancy.md b/docs/architecture/multi-tenancy.md index c6e9fb58..b2ee5de1 100644 --- a/docs/architecture/multi-tenancy.md +++ b/docs/architecture/multi-tenancy.md @@ -553,7 +553,7 @@ Separate from the in-cluster namespace-owner model above, the **devvm** (`10.0.1 5. (Optional — Viktor's call per user) Grant direct master push: add their login to the `master` branch-protection push + merge whitelists (`PATCH /api/v1/repos/viktor/infra/branch_protections/master`). Done for `ebarzin` 2026-06-10. 6. Verify: branch push succeeds; a `master` push succeeds for whitelisted users and is rejected with `Not allowed to push to protected branch` otherwise. -**Session persistence (2026-06-10):** named tmux sessions (each running one Claude conversation) survive devvm reboots — `t3-tmux-save.timer` (5-min) snapshots every roster user's sessions (name, cwd, conversation uuid from argv or the cwd-slug transcript dir) to `/var/lib/t3-tmux-state/.tsv`, and `t3-tmux-restore.service` recreates missing sessions at boot with `claude --resume ` (per-session idempotent; also handles partial loss). Conversations themselves were always durable (`~/.claude/projects/`); this persists the session wiring. +**Web-terminal session persistence (2026-06-10):** the tmux-based web terminal's named sessions (each running one Claude conversation) survive devvm reboots — `tmux-persist-save.timer` (5-min) snapshots every terminal user's sessions (name, cwd, conversation uuid from argv or the cwd-slug transcript dir) to `/var/lib/tmux-persist/.tsv`, and `tmux-persist-restore.service` recreates missing sessions at boot with `claude --resume ` (per-session idempotent; also handles partial loss). This is a **tmux/terminal-surface** feature, deliberately outside the t3 namespace: the t3 chat surface persists its own threads (`~/.t3` state, plus the daily `t3-backup-state` dump), and Claude conversations themselves were always durable (`~/.claude/projects/`) — what this adds is the volatile tmux wiring. **Status (2026-06-10):** built + verified on the live host — capacity (8 GiB swap), config inheritance, roster-driven provisioner, per-user locked clone, per-user OIDC kubeconfig + the `oidc-power-user-readonly` ClusterRole + emo's `k8s_users` entry (applied + impersonation-verified), the Authentik `T3 Users` edge gate, **the emo Phase-5 cutover (own clone + launcher repoint + `code-shared` removal, completed 2026-06-10) and emo's contribute access (`ebarzin` write collaborator + PAT + protected `master`)**. Per the live `/etc/skel` design, non-admin `~/.claude/{rules,skills}` symlinks into the admin base are **kept** (they ARE the shared-base delivery mechanism — the plan's step to remove them is obsolete). **Remaining (held / future):** the offboarding apply-side (Phase 7), per-user MCP/auth injection, and roster-reconciled `T3 Users` membership. See `../runbooks/offboard-user.md` for deprovisioning. diff --git a/scripts/t3-provision-users.sh b/scripts/t3-provision-users.sh index 83c5d679..0f5d18a3 100644 --- a/scripts/t3-provision-users.sh +++ b/scripts/t3-provision-users.sh @@ -259,8 +259,8 @@ run systemctl enable t3-autoupdate.timer >/dev/null 2>&1 || true # tmux session persistence: periodic snapshot + boot-time restore (reboot # survival for users' named claude sessions). Safe to --now: save is a # read-only snapshot; restore is per-session idempotent. -run systemctl enable --now t3-tmux-save.timer >/dev/null 2>&1 || true -run systemctl enable t3-tmux-restore.service >/dev/null 2>&1 || true +run systemctl enable --now tmux-persist-save.timer >/dev/null 2>&1 || true +run systemctl enable tmux-persist-restore.service >/dev/null 2>&1 || true # 6) regenerate /etc/ttyd-user-map + dispatch.json from the desired state (SSoT: # a roster entry removed here DISAPPEARS, which is what the offboarding cut relies on) diff --git a/scripts/t3-tmux-restore.service b/scripts/tmux-persist-restore.service similarity index 87% rename from scripts/t3-tmux-restore.service rename to scripts/tmux-persist-restore.service index 5d5c925b..62c61d20 100644 --- a/scripts/t3-tmux-restore.service +++ b/scripts/tmux-persist-restore.service @@ -6,7 +6,7 @@ After=network.target local-fs.target [Service] Type=oneshot -ExecStart=/usr/local/bin/t3-tmux-sessions restore +ExecStart=/usr/local/bin/tmux-persist restore [Install] WantedBy=multi-user.target diff --git a/scripts/t3-tmux-save.service b/scripts/tmux-persist-save.service similarity index 73% rename from scripts/t3-tmux-save.service rename to scripts/tmux-persist-save.service index df4adbfb..deecf541 100644 --- a/scripts/t3-tmux-save.service +++ b/scripts/tmux-persist-save.service @@ -3,4 +3,4 @@ Description=Snapshot workstation tmux sessions (name -> claude conversation) for [Service] Type=oneshot -ExecStart=/usr/local/bin/t3-tmux-sessions save +ExecStart=/usr/local/bin/tmux-persist save diff --git a/scripts/t3-tmux-save.timer b/scripts/tmux-persist-save.timer similarity index 100% rename from scripts/t3-tmux-save.timer rename to scripts/tmux-persist-save.timer diff --git a/scripts/t3-tmux-sessions.sh b/scripts/tmux-persist.sh similarity index 85% rename from scripts/t3-tmux-sessions.sh rename to scripts/tmux-persist.sh index bc223745..26484587 100644 --- a/scripts/t3-tmux-sessions.sh +++ b/scripts/tmux-persist.sh @@ -1,28 +1,33 @@ #!/usr/bin/env bash -# Persist workstation tmux sessions across devvm reboots. +# Persist WEB-TERMINAL (ttyd/tmux) sessions across devvm reboots. +# +# Scope: the tmux-based web terminal only. The t3 chat surface persists its +# own threads (~/.t3 state.sqlite, backed up daily by t3-backup-state) — this +# script is about the tmux sessions, which are otherwise memory-only. Users +# come from /etc/ttyd-user-map (the terminal surface's roster-derived map). # # save — snapshot every roster user's live tmux sessions to -# /var/lib/t3-tmux-state/.tsv (name, cwd, claude session +# /var/lib/tmux-persist/.tsv (name, cwd, claude session # uuid). The uuid is sniffed from the claude process's OPEN # transcript fd (~/.claude/projects//.jsonl), so it is # correct regardless of how the session was launched (fresh via # start-claude.sh or an explicit --resume). Runs every 5 min via -# t3-tmux-save.timer. A user with no tmux server keeps their last +# tmux-persist-save.timer. A user with no tmux server keeps their last # manifest (so a post-reboot save can't wipe it before restore). # restore — recreate manifest sessions that don't currently exist, resuming # each saved conversation (claude --resume ). Per-session # idempotent: existing names are left alone, so it is safe both at -# boot (t3-tmux-restore.service) and after a partial loss. +# boot (tmux-persist-restore.service) and after a partial loss. # # v1 limitation: one window/pane per session is captured (the workstation # usage pattern — one named claude conversation per tmux session). set -euo pipefail -STATE_DIR=/var/lib/t3-tmux-state +STATE_DIR=/var/lib/tmux-persist MAP=/etc/ttyd-user-map MODE="${1:-}" -log() { echo "[t3-tmux-sessions] $*"; } +log() { echo "[tmux-persist] $*"; } users() { [[ -r "$MAP" ]] && cut -d= -f2 "$MAP" | sort -u; } @@ -106,5 +111,5 @@ restore() { case "$MODE" in save) save ;; restore) restore ;; - *) echo "usage: t3-tmux-sessions save|restore" >&2; exit 1 ;; + *) echo "usage: tmux-persist save|restore" >&2; exit 1 ;; esac