diff --git a/AGENTS.md b/AGENTS.md index 4411ac11..f3d98c60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,7 +15,7 @@ strict subset of it**. Roles are `Admin` / `Member` / `Developer`; what each may per-role permission grants, resolved on every request. If a design question turns on "which user", the answer depends on the surface: real for the **app** -permissions (gitea, music, photos, email, calendar…), and still always **the owner** for anything +permissions (gitea, music, photos, calendar…), and still always **the owner** for anything that executes code or touches the disk — terminal, chat, tasks, files, desktop, browser are `kind: 'execution'` and can never be granted. `src/servers/permissions/registry.ts` is the authority. diff --git a/CLAUDE.md b/CLAUDE.md index 66994993..353df69c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Officer is a self-hosted platform built around one person — the server owner — which since 2026-08-07 also admits **additional accounts holding a strict subset of it**. It bundles an AI agent, a terminal, -a file browser, a code editor, email, a bitcoin wallet, a remote desktop and customisable dashboards +a file browser, a code editor, a bitcoin wallet, a remote desktop and customisable dashboards behind a single web app. **The owner/member split, and where the line falls.** This file said "single-user is a hard invariant, @@ -30,7 +30,7 @@ written: `users` holds six rows. The accurate statement is narrower and more use since `getOwnerHomeDir` ignores the email it is passed. That rule lives there once and covers the HTTP routes, the websocket doors and the dock together. -So "which user is this" has a real answer for the **app** surface (gitea, music, photos, email, +So "which user is this" has a real answer for the **app** surface (gitea, music, photos, calendar…) and for the **confined** one (terminal, chat, files), and is still always "the owner" for anything under `execution`. @@ -58,7 +58,7 @@ One Bun process (`src/server.tsx`) serves everything: Long-running and privileged work lives in **sidecars**: separate processes that dial back in over `/api/sidecar/register` and are tracked in `src/servers/sidecar-registry.ts`. PM2 runs them (the generated `ecosystem.config.cjs` — see below): `officer` (the server), `officer-anthropic-proxy`, `officer-claude-code`, -`officer-opencode`, `officer-email`, `officer-pty`, `officer-vnc`, `officer-music`, `officer-vault`, +`officer-opencode`, `officer-pty`, `officer-vnc`, `officer-music`, `officer-vault`, `officer-slskd`, `officer-headscale`, `officer-transmission`, `officer-invoiceshelf`, `officer-wallet`, `officer-photos`, `officer-notify`, `officer-caldav`, `officer-memos`, `officer-jellyfin`, `officer-gitea` — twenty as of 2026-08-06, and a list that goes stale every time a sidecar lands. `pm2 jlist` is the @@ -125,7 +125,7 @@ imported by their package name (`officerdev`, `hooks`, `state`, `types`, `helper Two stores, and the split matters: **Postgres** (`src/databases/officer_db`) holds the account, passkeys, settings, dashboards, -email accounts, queue and pipeline jobs. Since 2026-08-16 there is a SECOND database, +queue and pipeline jobs. Since 2026-08-16 there is a SECOND database, `officer_marketplace` (`src/databases/marketplace_db`), and the split is deliberate: it is a public catalogue served by a process with no login, no session and no user, so sharing a connection would put an unauthenticated surface one query away from every private table. It runs only on the host serving @@ -135,9 +135,9 @@ with the plugin ones commented out. Types inferred from the schema in `src/types **The filesystem** holds everything the agent authors. `OFFICER_ITEMS_DIR` (`$OFFICER_ROOT/capabilities`) contains one directory per item under `skills/`, `tools/`, `tasks/`, `processes/`, `extensions/` — no -database rows, no scope tiers. `DATA_PATH//` holds the managed home, attachments and the -per-account email SQLite stores — those are the **email sidecar's**, and nothing in the platform opens -them. +database rows, no scope tiers. `DATA_PATH//` holds the managed home and attachments. It also holds +`email_accounts/`, which is the **email plugin's** — the platform pre-creates the directory and never opens +what is inside it. **None of those paths is configured.** Since 2026-08-13 `src/servers/data-path.ts` derives the install root as `resolve(process.cwd(), '..')` and hangs `data/`, `permissions/` and `dockers/` off it. That @@ -372,7 +372,7 @@ link-focusable). Half the app still does this; none of the new code should. copy it. A disabled entry renders as a ``; a disabled `` is not a thing. A control that _mutates_ rather than navigates stays a `