- run_resume falls back to a new conversation only when the transcript file is gone; other failures end at the menu with the recorded id intact. - The SessionStart hook resolves the session name under the registry lock; rename holds it across tmux rename-session and the registry move (dd_registry_locked, nestable). - dd_load_env parses KEY=value like systemd instead of sourcing the files. - devdesk-deploy: DPkg::Lock::Timeout, explicit FAILED lines on stdout and in the log; the hook answers synchronously and CI checks the HTTP status. logrotate for the deploy log. - postinst drops duplicate terminal-features entries; hooks sweep stale temp files. Closes #12 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
5.5 KiB
Markdown
99 lines
5.5 KiB
Markdown
# Deploying DevDesk (CT 126 · dev.lotusguild.org)
|
||
|
||
## 1. Install the package
|
||
Add the Gitea apt repository once:
|
||
```bash
|
||
curl -fsSL https://code.lotusguild.org/api/packages/LotusGuild/debian/repository.key \
|
||
-o /etc/apt/keyrings/gitea-LotusGuild.asc
|
||
echo "deb [signed-by=/etc/apt/keyrings/gitea-LotusGuild.asc] https://code.lotusguild.org/api/packages/LotusGuild/debian bookworm main" \
|
||
> /etc/apt/sources.list.d/lotusguild.list
|
||
apt update && apt install devdesk
|
||
```
|
||
On install, `/etc/devdesk/secret.env` is generated, `devdesk-tmux.service` and `devdesk.service` are enabled and started, and ttyd listens on `:7681`. Requests without the credential get a **401**.
|
||
|
||
## 2. Nginx Proxy Manager (CT 139)
|
||
- **Proxy host:** `dev.lotusguild.org` → `http://10.10.10.58:7681`.
|
||
- Websockets Support **on**.
|
||
- SSL: the existing wildcard `*.lotusguild.org` certificate, with Force SSL.
|
||
- **Advanced tab:** paste [`npm-advanced.conf`](npm-advanced.conf), replacing `__DEVDESK_BASIC__` with the output of:
|
||
```bash
|
||
. /etc/devdesk/secret.env && printf '%s' "$DEVDESK_CREDENTIAL" | base64 -w0
|
||
```
|
||
- **Why the credential matters:** ttyd only checks the credential, so anything on the LAN that bypasses NPM is refused. If you rotate it, delete `secret.env`, run `dpkg-reconfigure devdesk`, update NPM, then `systemctl restart devdesk`.
|
||
|
||
## 3. Authelia (CT 167)
|
||
Add these to `access_control.rules` in `/etc/authelia/configuration.yml`, **above** any broader `*.lotusguild.org` rule:
|
||
```yaml
|
||
- domain: dev.lotusguild.org
|
||
policy: two_factor
|
||
subject: ['user:jared']
|
||
- domain: dev.lotusguild.org
|
||
policy: deny
|
||
```
|
||
Then run `authelia validate-config --config /etc/authelia/configuration.yml && systemctl restart authelia`.
|
||
|
||
## 4. DNS
|
||
- **Cloudflare:** add a CNAME `dev` → apex, DNS-only (grey cloud).
|
||
- **Pi-hole:** add `"10.10.10.27 dev.lotusguild.org"` to `dns.hosts` in `pve-infra/containers/163-pihole/etc/pihole/pihole.toml`. A push auto-deploys it, and LAN clients then reach NPM directly instead of hairpinning.
|
||
|
||
## 5. Auto-deploy on release
|
||
A `v*` tag makes CI publish the package, then send a signed webhook (the house HMAC pattern) to CT 126's existing `webhook` listener on :9000. The listener runs `/usr/lib/devdesk/devdesk-deploy <ref>`, which waits for that version in the apt registry and upgrades only `devdesk` with `--force-confold`. Running Claude sessions are untouched. The log is `/var/log/devdesk-deploy.log` (rotated monthly).
|
||
|
||
The hook answers when the deploy has finished, with the script's last line, and with HTTP 500 if it failed. CI's **Deploy to CT 126** job therefore fails when the install fails, and the failure alert goes to Matrix.
|
||
|
||
**Setup, done once:**
|
||
1. Generate a secret.
|
||
2. Add this hook to `/etc/webhook/hooks.json`, next to `126-config-deploy`. The file is local and untracked because it holds secrets.
|
||
```json
|
||
{
|
||
"id": "devdesk-deploy",
|
||
"execute-command": "/usr/lib/devdesk/devdesk-deploy",
|
||
"command-working-directory": "/",
|
||
"pass-arguments-to-command": [{"source": "payload", "name": "ref"}],
|
||
"include-command-output-in-response": true,
|
||
"include-command-output-in-response-on-error": true,
|
||
"trigger-rule": {"and": [
|
||
{"match": {"type": "payload-hash-sha256", "secret": "<WEBHOOK_SECRET>",
|
||
"parameter": {"source": "header", "name": "X-Gitea-Signature"}}},
|
||
{"match": {"type": "regex", "regex": "^refs/tags/v[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||
"parameter": {"source": "payload", "name": "ref"}}}
|
||
]}
|
||
}
|
||
```
|
||
3. Run `systemctl restart webhook`.
|
||
4. Store the same secret as the devdesk repo's Actions secret `WEBHOOK_SECRET`.
|
||
|
||
## 6. Firewall (required)
|
||
The ttyd credential is the only thing between the LAN and a root shell, so only NPM may reach :7681. CT 126 ships this in pve-infra (`containers/126-codingenvironment/etc/nftables.conf`, installed by its `deploy.sh`):
|
||
|
||
```
|
||
table inet devdesk {
|
||
chain input {
|
||
type filter hook input priority filter - 10; policy accept;
|
||
tcp dport 7681 ip saddr { 127.0.0.1, 10.10.10.27 } accept
|
||
tcp dport 7681 ip6 saddr ::1 accept
|
||
tcp dport 7681 counter reject with tcp reset
|
||
}
|
||
}
|
||
```
|
||
|
||
Check from another LAN host: `curl -m3 http://10.10.10.58:7681/` must fail with "connection refused". To rotate the credential: `rm /etc/devdesk/secret.env && dpkg-reconfigure devdesk`, put the new `Authorization` value in NPM proxy host 58's Advanced tab, then `systemctl restart devdesk`.
|
||
|
||
## 7. Matrix "Claude needs you" (optional)
|
||
1. Deploy a hookshot transform named `Devdesk` (`hookshot/devdesk.js` in the matrix repo; pushing it to `main` auto-deploys it on LXC 151). Look up the hook UUID as described in that repo's README.
|
||
2. `umask 077; echo https://matrix.lotusguild.org/webhook/<uuid> > /etc/devdesk/matrix-webhook`.
|
||
3. Nothing to restart: the next wait that lasts `DEVDESK_MATRIX_DELAY` seconds with nobody attached posts a message.
|
||
|
||
## Operations
|
||
| Task | Command |
|
||
|---|---|
|
||
| Sessions | `devdesk ls` |
|
||
| Attach over SSH | `devdesk attach <name>` (or `devdesk pick`) |
|
||
| New session (optionally in a project) | `devdesk new <name> [project]`, or the Sessions panel form |
|
||
| Rename a session | `devdesk rename <old> <new>`, or ✎ in the Sessions panel |
|
||
| End a session | `devdesk end <name>`, × in the Sessions panel, or `[x]` in the Claude window's exit menu |
|
||
| Restart the web side (safe) | `systemctl restart devdesk` |
|
||
| **Stop every session** | `systemctl stop devdesk-tmux` |
|
||
| Logs | `journalctl -u devdesk -u devdesk-tmux` |
|
||
| UI-only update | replace `/usr/share/devdesk/index.html` (ttyd re-reads it per request) |
|