workstation: rename tmux persistence out of the t3 namespace [ci skip]

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 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-10 17:42:52 +00:00
parent 2e4f48f3fc
commit e7fbf986fb
6 changed files with 17 additions and 12 deletions

View file

@ -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/<user>.tsv`, and `t3-tmux-restore.service` recreates missing sessions at boot with `claude --resume <uuid>` (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/<user>.tsv`, and `tmux-persist-restore.service` recreates missing sessions at boot with `claude --resume <uuid>` (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.

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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/<user>.tsv (name, cwd, claude session
# /var/lib/tmux-persist/<user>.tsv (name, cwd, claude session
# uuid). The uuid is sniffed from the claude process's OPEN
# transcript fd (~/.claude/projects/<slug>/<uuid>.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 <uuid>). 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