# 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](https://code.lotusguild.org/LotusGuild/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= → `devdesk attach ` (tmux client) tmux server [devdesk-tmux.service] ── one session per project: window "claude": devdesk-claude-run → claude --resume 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 - **How the session is chosen:** - `https://dev.lotusguild.org/?arg=` opens session `` and creates it on demand in `$DEVDESK_PROJECTS_ROOT/`. `main` uses the root itself, and folders with spaces are reached by slug (`test code` → `test-code`). - With no `?arg=`, an fzf picker opens, listing running (●), dormant (○) and new (+) sessions. - **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. - **SSH:** `devdesk attach `, `devdesk ls`, `devdesk pick`, `devdesk end `. ## 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 - `/etc/devdesk/env` (conffile): - port, bind address and projects root - `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](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`: ```bash 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` and creates a release. ## Roadmap - **v1.1:** `devdesk-api` sidecar for the Repo/CI and Claude Context panels, Matrix notifications when you're away, and auto-deploy. - **v2:** Ops Alerts (Grafana, GANDALF) with "Ask Claude" prompts, and file drop.