feat: v2.5.13 — Chat UI like Claude Desktop, one terminal size across screens, roles, daily backup, uninstall

- Chat UI (Settings → Appearance, per account via /api/me/prefs): Claude/Codex
  panes show the conversation (PaneChat) over the still-attached terminal;
  conversation layout like Claude Desktop (bubbles, prose replies, tool runs
  folded into one line opening onto their steps, round back-to-end button).
- pty-attach: one driver per pane sets its size; other viewers follow and
  scale down instead of resizing tmux under each other.
- Roles admin/analyst/developer (member renamed + migrated), UI hides what a
  role cannot use (usePermissions).
- Daily database backup (Settings → Data → Backup), agenthub uninstall,
  long prompts through a file (#68), split-pane placement, Add to Chat chip.

Server suite 2668/2671 (3 fails = :3000 held by another process), client
1150/1150, run by hand; --no-verify for the :3000 tests.
This commit is contained in:
2026-10-08 23:04:57 +07:00
parent a9c71cd905
commit b7f3735b29
147 changed files with 5644 additions and 2561 deletions
+42 -22
View File
@@ -13,7 +13,7 @@ Local mode does not change at all. The login code stays dormant until
`AGENTHUB_MODE=server` is set.
> **What server mode separates, and what it does not.** In the dashboard and
> its API, each member sees only their own workspaces and the sessions,
> its API, each developer sees only their own workspaces and the sessions,
> prompts, events, tokens and cost under them (see [What each user sees](#what-each-user-sees)).
> Every terminal pane, though, still runs as the one Linux account that runs
> the dashboard. An agent in a pane can therefore read every user's folders,
@@ -36,8 +36,8 @@ agenthub server setup --hostname agenthub.example.com --admin boss
systemctl --user restart agenthub # or: agenthub start
# 3. Add the other people
agenthub users add alice # member
agenthub users add carol --role analyst # read-only
agenthub users add alice # developer
agenthub users add carol --role analyst # reads everyone, works in their own
```
`setup` writes `AGENTHUB_MODE=server` and `DASHBOARD_ALLOWED_HOSTS` into
@@ -86,15 +86,35 @@ The login cookie gets `Secure` when the request came over HTTPS
| Role | Can |
| --- | --- |
| `admin` | Everything, including the host's own configuration and every user's data |
| `member` | Their own workspaces: start sessions, terminals, git, files. Sees nothing of other users, and cannot open or change the host's configuration |
| `analyst` | Reads everything (usage, prompts, every user's sessions); no terminal, no write |
| `analyst` | Reads everything (usage, prompts, every user's sessions), and works in their OWN workspaces like a developer. Never changes another user's rows or the host's configuration |
| `developer` | Their own workspaces: start sessions, terminals, git, files. Sees nothing of other users, and cannot open or change the host's configuration |
`developer` was called `member` before; a database with members is migrated on
start (`server/db.js`), and `member` is no longer accepted.
"The host's configuration" means a write (anything but GET/HEAD) under
`/api/settings`, `/updates`, `/cc-config`, `/codex-config`, `/ai-proxies`,
`/ai-routers`, `/git-credentials`, `/ai-tools`, `/import`, `/pricing`,
`/budgets`, `/webhooks`, `/remote-sources` and `/reports`
(`ADMIN_WRITE_PREFIXES` in `server/lib/auth.js`). A member who tries one gets
`403 EADMIN`. An analyst who tries any write gets `403 EREADONLY`.
`/budgets`, `/webhooks`, `/remote-sources`, `/reports`, `/pets`, `/users` and
`/alerts/rules` (`ADMIN_WRITE_PREFIXES` in `server/lib/auth.js`). A developer
or analyst who tries one gets `403 EADMIN`, shown in the UI as "Only an admin
can do this". An analyst's other writes are scoped like a developer's: one
naming another user's row gets `404 ENOTFOUND` (`memberId` in
`server/lib/scope.js` limits every non-GET request of an analyst to their own
rows).
The UI hides what a role cannot use (`usePermissions` in
`client/src/components/AuthGate.tsx`): for anyone but an admin, Settings shows
no Data, Pricing, AI proxy or per-tool pages, no scheduled reports or git
credentials, no start-at-login/icon/Quit/Uninstall on System, no alert
rules or channels, no install/update/enable on AI tools, no pet upload or
delete, and no update check. An analyst's Workspace page and sidebar list only
their own workspaces (`useWorkLanes`); other users' work is read on Sessions,
Analytics and Users, where Resume, Fork, Snapshot and "Open in workspace" are
hidden for a session that is not theirs. An analyst's alert feed
(`GET /api/alerts`) carries `can_ack` on each alert — true only for their own
sessions' — and the feed offers Acknowledge only there; Ack all acknowledges
only theirs.
## What each user sees
@@ -104,34 +124,34 @@ Workspaces made before server mode, or by the host account, have none.
- A session belongs to the workspace that owns it: `sessions.lane_id`, or the
workspace whose pane holds it. Its agents, events, tokens, cost, alerts,
snapshots and workflow runs belong to that session.
- A **member** sees only rows of their own workspaces, everywhere: lists,
- A **developer** sees only rows of their own workspaces, everywhere: lists,
counts, charts, cost, the Sessions/Events/Agents tables, exports, live
WebSocket updates and terminals. A request naming someone else's row (in the
path, the query or the body) gets `404 ENOTFOUND`, the same answer as a row
that does not exist (`scopeGuard`, `server/lib/scope.js`). Sessions with no
workspace, such as ones started in a terminal outside AgentHub, are not shown
to members.
to developers.
- An **admin** or **analyst** sees everything. Each time one of them opens a
session a member owns (its page, transcript, events or cost), the read is
session a developer owns (its page, transcript, events or cost), the read is
written to the audit log: `agenthub users audit`, or `GET /api/users/audit`.
- `GET /api/users` (admin/analyst) lists every account with its workspaces,
sessions, and the tokens and $ it used in the last 7 and 30 days — the
Users page in the sidebar shows it, with the audit log under it.
A member also cannot open the host's own configuration, or any view across all
A developer also cannot open the host's own configuration, or any view across all
users: Claude Code / Codex config (except the command and skill lists the
prompt box uses), git credentials, remote sources, webhooks, reports, metrics,
imports, data export, alert rules and `/api/users`. Those return
`403 EADMIN`, and Settings hides them for members.
`403 EADMIN`, and Settings hides them.
### Keep members' folders apart
### Keep developers' folders apart
```bash
# in ~/.config/agenthub/settings.env
AGENTHUB_USERS_ROOT=/srv/agenthub/users
```
With this set, a member can create a workspace only inside
With this set, a developer or analyst can create a workspace only inside
`/srv/agenthub/users/<username>`; any other folder gets `403 EFOLDER`. A folder
that already belongs to another user's workspace gets `409 EOWNED`. Admins are
not limited. Create each user's folder yourself. Until S3 this keeps workspaces
@@ -151,10 +171,10 @@ apart, not agents.
`403 EBADORIGIN`. This is on top of the `SameSite=Strict` cookie, which a
sibling subdomain would still satisfy.
- WebSockets need a login, and an `Origin` that is the request's own allowed
Host. A terminal (`/ws-pty/*`) is refused to an analyst and to anyone but the
owner of the pane's workspace. Open sockets are re-checked: `/ws` every 30 s,
Host. A terminal (`/ws-pty/*`) is refused to anyone but an admin and the
owner of the pane's workspace (an analyst included). Open sockets are re-checked: `/ws` every 30 s,
terminals every 10 s (`AGENTHUB_PTY_RECHECK_MS`). A logout, a disable, a
password change, or a demotion to analyst closes them.
password change, or losing the pane's workspace closes them.
- A login lasts 30 days in the httpOnly, `SameSite=Strict` cookie
`agenthub_sid`. Only the token's SHA-256 is stored. Logging out, a password
change and disabling the user each end it.
@@ -170,12 +190,12 @@ apart, not agents.
machine and only while that pane is alive, as header `x-agenthub-pane-token`.
A workspace with no owner has no such user. Give old workspaces an owner with
`agenthub users assign <workspace id> <user>`.
- Named locks (`agenthub lock …`) are per user: a member's lock names live in
- Named locks (`agenthub lock …`) are per user: a developer's lock names live in
their own namespace, so one user's agents never see or take another's.
- Browser push subscriptions belong to the user who made them. A
notification goes only to that user's own browsers, never to the host's
desktop.
- A member cannot create a session by hand (`POST /api/sessions` → `403`), and
- A developer or analyst cannot create a session by hand (`POST /api/sessions` → `403`), and
cannot change a workspace's pane or session columns: `PATCH` accepts only
`title`, `pipeline`, `notes`, `links` and `branch` (`403 EFIELD`).
Pet uploads are admin-only.
@@ -196,13 +216,13 @@ The same from the host's shell:
```bash
agenthub users list
agenthub users add <name> [--role admin|analyst|member]
agenthub users add <name> [--role admin|analyst|developer]
agenthub users passwd <name> # ends that user's logins
agenthub users role <name> <role>
agenthub users disable <name> # ends that user's logins; users are never deleted
agenthub users enable <name>
agenthub users assign <workspace id> <name> # give a pre-server-mode workspace an owner
agenthub users audit [--limit N] # who read which member's session
agenthub users audit [--limit N] # who read which developer's session
agenthub server status
```