jaredandClaude Opus 5.5 7aa624bc13
build-deb / Lint, test, package (push) Successful in 1m49s
build-deb / Deploy to CT 126 (push) Skipped
build-deb / Notify on failure (push) Skipped
fix: v0.5.0 security and robustness fixes from the 2026-10-07 review
- Ask Claude: strip control characters, send as bracketed paste (closes #2)
- end: forget before kill, so ending from inside a session leaves no ghost (closes #3)
- reconnects use `attach --existing` and never resurrect ended/renamed sessions (closes #4)
- repo: git --no-optional-locks, no safe.directory override, refresh on connect/bell/60s (closes #5)
- uploads: unique names via mktemp, cleanup trap on hangup (closes #6)
- clipboard: write-only OSC 52 provider, tmux allow-passthrough off (closes #7)
- uploads: size check up front, streamed slices, idle timeout (closes #9)
- --list: path last in TSV rows, tolerant numbers (closes #10)
- tokens via curl config on stdin; userinfo stripped from remote URLs (closes #11)
- ttyd max clients 32 and at most 2 concurrent side-channel calls (#8; BroadcastChannel polling still open)
- tmux: fixed terminal-features index (no duplicates on re-source), no status-right clock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:15:47 -04:00

DevDesk

A persistent browser terminal for Claude Code, at https://dev.lotusguild.org.

  • Persistent sessions: closing the tab, restarting the web service or upgrading the package never stops Claude.
  • Survives reboots: after a reboot, every named session resumes its own conversation.
  • Styling: uses the LotusGuild Terminal Design System (web_template).
  • Packaging: ships as a .deb from Gitea's apt registry.
Browser ─https─▶ NPM (wildcard cert, Authelia two_factor, injects ttyd credential)
        ─http─▶ ttyd :7681  [devdesk.service]   ── /ws?arg=<name> → `devdesk attach <name>` (tmux client)
                tmux server [devdesk-tmux.service] ── one session per project:
                   window "claude": devdesk-claude-run → claude --resume <recorded id>
                   window "shell"

Why two units

The tmux server runs in its own unit, so every Claude process lives in that unit's cgroup.

  • ttyd (devdesk.service) only starts tmux clients (tmux -N never starts a server). It can restart freely, and does so on package upgrades.
  • devdesk-tmux.service is installed with --no-stop-on-upgrade, so apt never touches it.
  • Only systemctl stop devdesk-tmux (or removing the package) ends the sessions.

Sessions

  • Any name works. Sessions don't have to be repo work:
    • A name matching a project folder (tinker_tickets, or test-code for test code) starts there.
    • Anything else (pbs-upgrade, ceph-check) starts in $DEVDESK_DEFAULT_DIR, which is /root/code by default.
    • main is the projects root.
  • Opening one:
    • https://dev.lotusguild.org/?arg=<name> opens or creates that session.
    • With no ?arg=, an fzf picker opens, listing running (●), dormant (○) and new (+) sessions.
    • The Sessions panel's form takes any name plus an optional project folder.
  • Rename / end: the ✎ and × buttons on each session in the panel. Renaming keeps the session, its running Claude and its recorded conversation. Ending stops Claude; the conversation stays in Claude's history.
  • Windows: each session has a claude window (0) and a shell window (1).
  • Conversation tracking: a Claude SessionStart hook records each session's conversation ID in /var/lib/devdesk/registry.json. After a reboot the session resumes that conversation, even when other Claude instances, such as code-server, use the same directory.
  • Session status: Claude hooks (devdesk-state-hook) mark each session working, waiting on you (with Claude's message) or idle.
    • The sidebar shows it with context %, and the header shows the current session's state.
    • A pulsing "… needs you" chip jumps to other sessions that are waiting.
  • File & image drop: drag files onto the terminal or paste a screenshot.
    • They're uploaded through ttyd to /var/lib/devdesk/uploads/<session>/ (25 MB max, pruned after 7 days), and the path is pasted into Claude.
    • For images, Claude attaches them itself.
  • Ops Alerts panel: firing and pending Grafana alert rules, each with a link to the rule.
    • Ask Claude types an investigation prompt into the current session without sending it.
    • Configure DEVDESK_GRAFANA_URL, plus a Viewer token in DEVDESK_GRAFANA_TOKEN_FILE.
  • Repo / CI panel: branch, ahead/behind, uncommitted changes, HEAD's Gitea CI status and open PRs for the session's folder.
  • Claude Context panel: a Claude statusLine command (devdesk-statusline) snapshots each session's model, context-window use, cost and edits to /var/lib/devdesk/context/<session>.json. The panel shows the current session's numbers, and Claude's own footer shows a one-line summary. Sessions started before 0.2.0 need Claude restarted (/exit, then ⏎ in the menu) before they report.
  • SSH: devdesk attach <name>, devdesk new <name> [project], devdesk rename <old> <new>, devdesk end <name>, devdesk ls, devdesk pick, devdesk repo <name>, devdesk alerts.

Keys

Key Action
Shift+Enter Newline in Claude's prompt (sends Ctrl+J)
Esc Interrupt Claude
Ctrl-b 0 / 1 Claude window / shell window
Ctrl-b s Switch session (the URL follows)
Ctrl-b d Detach (the session keeps running)
Shift+drag Native browser selection (plain drag goes to tmux copy-mode, which copies via OSC 52)
Ctrl+Shift+C / V Copy selection / paste

Use the Full button for fullscreen. In Chromium it also captures Ctrl+W/Ctrl+T, which browsers otherwise keep for themselves.

Configuration

  • Put your changes in /etc/devdesk/env.local. It's read after the packaged env and never shipped, so apt upgrade never asks about it. Example: DEVDESK_REMOTE_CONTROL=1.
  • /etc/devdesk/env holds the packaged defaults; don't edit it. Its keys:
    • port, bind address, projects root and DEVDESK_DEFAULT_DIR
    • DEVDESK_GITEA_URL / DEVDESK_GITEA_ALIASES for the Repo/CI panel. Public repos need no token; for private ones put a read:repository token in /etc/devdesk/gitea-token
    • CLAUDE_BIN and CLAUDE_ARGS
    • DEVDESK_REMOTE_CONTROL=1, which also starts each session with Claude Remote Control so it can be continued from the Claude app
  • /etc/devdesk/secret.env is generated on install. It holds the ttyd credential that NPM must inject; see docs/DEPLOY.md.
  • /etc/devdesk/tmux.local.conf is optional and is sourced after the packaged tmux.conf.

Build

Needs Node ≥ 18, debhelper, bats, tmux, jq, shellcheck:

make            # dist/index.html (single inlined page) + build/ttyd (pinned, sha256-verified)
make lint test
dpkg-buildpackage -us -uc -b && lintian ../devdesk_*_amd64.deb
apt install ../devdesk_*_amd64.deb

CI (.gitea/workflows/build-deb.yml):

  • Every push lints, tests, builds and smoke-tests the package and uploads it as an artifact.
  • A v* tag also publishes to https://code.lotusguild.org/api/packages/LotusGuild/debian, creates a release, and auto-deploys to CT 126. A signed webhook runs devdesk-deploy, which upgrades the package; see docs/DEPLOY.md.

Roadmap

  • Matrix "Claude needs you" notifications when no tab is open.
S
Description
Persistent browser terminal for Claude Code (ttyd + tmux, LotusGuild TDS) at dev.lotusguild.org, packaged as a .deb
Readme MIT
1.9 MiB
2026-11-03 21:23:29 -05:00
Languages
JavaScript 52.9%
Shell 35%
CSS 9.2%
HTML 2.4%
Makefile 0.5%