open-terminal 0.11.34 bare-metal (human-authorized host install) with a systemd template unit: reachableceo on tailscale-only :30000 (cwd ~/projects) and all 8 other accounts on :30001-30008, each with its own key kept out of ps in per-user TOMLs, all users added to the docker group. Keys live in ~/.creds + /etc/ukrrs/open-terminal, never in this repo. UAT: throwaway OpenWebUI v0.11.1 (matched to the human's Cloudron prod) driven purely over its HTTP API against the beta gateway lane - connection verify/config PASS, direct exec as uid 1001 PASS, and the chat round trip PASS: the model emitted run_command, we executed it through OpenWebUI's terminal proxy (server holds the key), and the final answer named 9/9 real project directories; earlier negative runs prove the test catches hallucinated output. Ops note OPEN-TERMINAL.md documents production wiring (prod openwebui container v0.3.10 is too old and needs an upgrade first). Also tonight: agent-stack relaunchers removed (6-, backups kept) so no screen/crush sessions auto-start on reboot; 22:00 night-profile flip observed live; docs synced for the earlier teardown. 💘 Generated with Crush Assisted-by: Crush:glm-5.2 [#610]
4.1 KiB
Open Terminal fleet — ops note (#610, deployed 2026-08-31)
OpenWebUI "Open Terminal" for every account on ultix-streaming, in computer mode (bare metal, real shell). Wired to start on reboot. UAT passed end to end the same night (see 9-uat-openwebui.out).
Layout
| account | port | bind | cwd | unit |
|---|---|---|---|---|
| reachableceo | 30000 | 100.101.187.119 (tailscale ONLY) | ~/projects | open-terminal@reachableceo |
| TSGCOO | 30001 | 〃 | ~/projects | open-terminal@TSGCOO |
| TSGCCO | 30002 | 〃 | ~ (no ~/projects yet) | open-terminal@TSGCCO |
| TSGCTO | 30003 | 〃 | ~/projects | open-terminal@TSGCTO |
| TSGBOD | 30004 | 〃 | ~ | open-terminal@TSGBOD |
| COS-RCEO | 30005 | 〃 | ~ | open-terminal@COS-RCEO |
| COS-WFO | 30006 | 〃 | ~ | open-terminal@COS-WFO |
| COS-TSG | 30007 | 〃 | ~ | open-terminal@COS-TSG |
| reachableceo-offstage | 30008 | 〃 | ~/projects | open-terminal@reachableceo-offstage |
- Binary: /usr/local/bin/open-terminal (open-terminal==0.11.34 via uv tool, managed python at /opt/uv-python, venvs at /opt/uv-tools; world-readable on purpose — non-root units exec it). Host-install exception authorized by human 2026-08-31 ("pip/uv directly on my host... mandatory").
- Template unit: /etc/systemd/system/open-terminal@.service (After tailscaled; Restart=always). Per-user TOML at /etc/ukrrs/open-terminal/.toml (key lives THERE, not on any command line — nothing leaks via ps). Per-user cwd env alongside it.
- All 8 non-founder accounts were also added to the docker group (human ruling 2026-08-31).
- Bind is 100.101.187.119 ONLY (tailscale interface; human ruling 2026-08-31 night: no LAN, no 0.0.0.0). Containers reach it fine (bridge → host IP routing); so does anything over tailscale.
API keys (NEVER in this repo)
~/.creds/open-terminal.env (reachableceo, 0600): URL + key per account in
<ACCOUNT>_URL / <ACCOUNT>_API_KEY form. Rotate a key with:
sudo sed -i "s/^api_key = .*/api_key = "$(openssl rand -hex 32)"/"
/etc/ukrrs/open-terminal/.toml && sudo systemctl restart open-terminal@
then update ~/.creds/open-terminal.env to match.
Wiring a production OpenWebUI (admin, server-side key)
Your Cloudron prod runs Open WebUI 0.11.1 — new enough. Admin UI: Settings → Admin → Integrations → Open Terminal → "+": URL: http://100.101.187.119:3000X (per-account port from the table) API key: from ~/.creds/open-terminal.env Auth: Bearer. Save → green "Connected". API-only equivalent (what the UAT does): POST /api/v1/configs/terminal_servers/verify {"url":…,"key":…} POST /api/v1/configs/terminal_servers {"TERMINAL_SERVER_CONNECTIONS":[{"id":…,"url":…,"key":…,"auth_type":"bearer","enabled":true}]} Scope per-user access via the connection's access control (each user gets THEIR OWN port; do not share one terminal across accounts — one shell each). In chat: pick the terminal (cloud icon) once per conversation; native function calling is already the default in 0.11.x.
UAT record (2026-08-31 22:48, all passed)
Throwaway openwebui:v0.11.1 (matched to Cloudron prod) on 127.0.0.1:8081, LLM via the beta gateway lane:
- P3 connection verify+list OK; P4 direct exec runs as uid 1001 with real ~/projects output; P5 model emitted run_command, WE executed it through OpenWebUI's terminal proxy (server holds the key), fed the result back, and the model answered with 9/9 REAL directory names (no hallucination — earlier runs without the tool attached fabricated names, proving the test catches it). Full log: 9-uat-openwebui.out.
Gotcha learned: for raw API callers OpenWebUI returns tool_calls for the CALLER to execute (the server-side loop needs a socket session — that's the UI path). UI chats run the loop automatically.
Hygiene notes
- open-terminal keeps per-process logs in ~/.local/state/open-terminal/logs/processes/ per user (unbounded; watch when agents go wild).
- 0.0.0.0 was rejected by ruling; if tailscale is down at boot the units retry (Restart=always) until the IP exists.
- Not in any ukrrs slice yet; fold into per-account slices when mkacct lands (#607).