@loicngr/kobo 2.1.25 → 2.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +30 -3
- package/CHANGELOG.md +15 -0
- package/CONFIGURATION.md +156 -49
- package/README.md +2 -1
- package/SECURITY.md +33 -6
- package/dist/mcp-server/kobo-tasks-server.js +36 -1021
- package/dist/mcp-server/managed-mcp-dispatcher.js +960 -0
- package/dist/mcp-server/workspace-dialogue-client.js +26 -1
- package/dist/server/index.js +52 -19
- package/dist/server/middleware/host-check-middleware.js +8 -5
- package/dist/server/middleware/network-auth-middleware.js +51 -29
- package/dist/server/middleware/security-headers-middleware.js +19 -0
- package/dist/server/middleware/traffic-limits-middleware.js +74 -0
- package/dist/server/routes/auth.js +73 -0
- package/dist/server/routes/internal-mcp.js +137 -0
- package/dist/server/routes/mcp-credentials.js +30 -0
- package/dist/server/routes/mcp.js +156 -76
- package/dist/server/routes/usage.js +24 -0
- package/dist/server/routes/workspaces.js +19 -3
- package/dist/server/services/agent/orchestrator.js +27 -17
- package/dist/server/services/external-mcp-credentials-service.js +110 -0
- package/dist/server/services/forge/github/provider.js +15 -3
- package/dist/server/services/forge/types.js +8 -0
- package/dist/server/services/internal-mcp-capability-service.js +98 -0
- package/dist/server/services/mcp-connection-info-service.js +1 -1
- package/dist/server/services/memory-mcp-service.js +9 -7
- package/dist/server/services/memory-service.js +15 -0
- package/dist/server/services/network-access-service.js +43 -73
- package/dist/server/services/network-session-service.js +182 -0
- package/dist/server/services/provider-accounts-service.js +202 -0
- package/dist/server/services/settings-service.js +29 -7
- package/dist/server/services/terminal-websocket-service.js +5 -0
- package/dist/server/services/usage/poller.js +44 -3
- package/dist/server/services/websocket-service.js +5 -0
- package/dist/server/utils/paths.js +2 -1
- package/dist/server/utils/startup-display.js +1 -2
- package/dist/shared/models.js +6 -0
- package/dist/shared/provider-accounts.js +1 -0
- package/docker-compose.vps.yml +6 -0
- package/docs/deployment-vps.md +24 -12
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-D58VS9o2.js +2 -0
- package/src/client/dist/pwa/assets/{AutoLoopFinalReviewSettings-Dafi7Tue.js → AutoLoopFinalReviewSettings-CK3r_fVB.js} +1 -1
- package/src/client/dist/pwa/assets/{ChangelogPage-DH-zUtlf.js → ChangelogPage-CAAUEn5X.js} +1 -1
- package/src/client/dist/pwa/assets/ComparisonPanel-BCb7vB5E.js +2 -0
- package/src/client/dist/pwa/assets/CreatePage-DasH1Uk4.js +2 -0
- package/src/client/dist/pwa/assets/{DashboardPage-B8gTooRi.js → DashboardPage-XE52EABP.js} +1 -1
- package/src/client/dist/pwa/assets/DiffViewer-CncKlXne.js +8 -0
- package/src/client/dist/pwa/assets/{DocumentsPanel-DXjjhGUg.js → DocumentsPanel-L4pbQfz1.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-CiVNMTbq.js → DrawerToggleButton-DiOJm5he.js} +1 -1
- package/src/client/dist/pwa/assets/GitPanel-WJlzy2VJ.js +3 -0
- package/src/client/dist/pwa/assets/{GitPanel-CRaLJzjc.css → GitPanel-yHvU38dp.css} +1 -1
- package/src/client/dist/pwa/assets/{HealthPage-lc2Cuuks.js → HealthPage-hJY5MurK.js} +1 -1
- package/src/client/dist/pwa/assets/MainLayout-DgWElK7w.js +2 -0
- package/src/client/dist/pwa/assets/{MemoryPanel-SrR94UVW.js → MemoryPanel-DtEvh_b7.js} +1 -1
- package/src/client/dist/pwa/assets/{MemoryProposalList-B5uVf3KU.js → MemoryProposalList-kgFCm9zz.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnDropdown-Cxr3lBPB.js → QBtnDropdown-CWPIugs8.js} +1 -1
- package/src/client/dist/pwa/assets/{QExpansionItem-xIXDevKz.js → QExpansionItem-D-EGL2b3.js} +1 -1
- package/src/client/dist/pwa/assets/{QScrollArea-DeczMHVs.js → QScrollArea-BMrqK-xd.js} +1 -1
- package/src/client/dist/pwa/assets/QSelect-DFWyDWCE.js +1 -0
- package/src/client/dist/pwa/assets/{QTooltip-be9mqhmB.js → QTooltip-Bk8dLUtU.js} +1 -1
- package/src/client/dist/pwa/assets/{SchedulePanel-R6H-D-m5.js → SchedulePanel-CuxmxCKi.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-CaeYoIDw.js → SearchPage-DKnQPUqY.js} +1 -1
- package/src/client/dist/pwa/assets/{SettingsPage-Dcq1Jeoo.css → SettingsPage-B4rmd2WX.css} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-CO3vIpPd.js +9 -0
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-Dw1qUIFU.js → SplitWorkspacePage-WhQyX8fn.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-Dqw88QqH.js → TerminalPanel-BY0lPq6k.js} +10 -10
- package/src/client/dist/pwa/assets/{TerminalPanel-Dz9EE-Ph.css → TerminalPanel-BcbwuFVR.css} +1 -1
- package/src/client/dist/pwa/assets/{ToolCallItem-BMn7eela.js → ToolCallItem-DYubi-J0.js} +3 -3
- package/src/client/dist/pwa/assets/{TouchPan-BwCpe_Oh.js → TouchPan-DDotyYLN.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-yeGYf6xT.js → TourReplayButton-kAnEMcy2.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-BJPw65vU.js → WorkspaceAttentionLabels-DiYt3rP3.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-BEBwwr81.css +1 -0
- package/src/client/dist/pwa/assets/WorkspacePage-K-1ce50d.js +4 -0
- package/src/client/dist/pwa/assets/{browser-C9U-mSDm.js → browser-DVYLH8vl.js} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-DYdRW2T4.js → build-path-tree-DEuOdIIk.js} +1 -1
- package/src/client/dist/pwa/assets/custom-sounds-Ggw6V9iF.js +1 -0
- package/src/client/dist/pwa/assets/de-Bk3zQrin.js +17 -0
- package/src/client/dist/pwa/assets/{editor.api-iu5YkEl3.js → editor.api-BC38eXAS.js} +1 -1
- package/src/client/dist/pwa/assets/{efforts-ja6uJadn.js → efforts-TGacUPcJ.js} +1 -1
- package/src/client/dist/pwa/assets/es-f3xuAWA9.js +17 -0
- package/src/client/dist/pwa/assets/{expand-template-BbgnJ8YH.js → expand-template-DM8gq3Mo.js} +1 -1
- package/src/client/dist/pwa/assets/{format-CLXLy6Ov.js → format-OwxXBnDJ.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-DeKgxYJe.js → formatters-YgaCZcwJ.js} +1 -1
- package/src/client/dist/pwa/assets/fr-B6c21SG3.js +17 -0
- package/src/client/dist/pwa/assets/{freemarker2-2IrnDDvu.js → freemarker2-l7R42fI2.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-CeLN7FCP.js → handlebars-DFmPrgjH.js} +1 -1
- package/src/client/dist/pwa/assets/{html-Bn3rhcp3.js → html-CERIAlPM.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-WaE4cZK9.js +1 -0
- package/src/client/dist/pwa/assets/index-CpWBGKdM.js +20 -0
- package/src/client/dist/pwa/assets/it-DUstDpC2.js +17 -0
- package/src/client/dist/pwa/assets/{javascript-BMvON8oT.js → javascript-BBYncplF.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-D_AQLvQl.js → liquid-C1HqYyYU.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-DDJzMB8Q.js → mdx-E74dsP22.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-DFKwR-_X.js → monaco-contributions-DHvm-kp5.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-4AwFLnjV.js → monaco.contribution-DYdn9CwR.js} +2 -2
- package/src/client/dist/pwa/assets/network-auth-DTZUKbny.js +1 -0
- package/src/client/dist/pwa/assets/{notify-theme-CMzdjL91.js → notify-theme-FuTL7HuS.js} +1 -1
- package/src/client/dist/pwa/assets/{permissionModes-DLWiBfdb.js → permissionModes-D-Nexnoo.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-C5mEKdlH.js → project-color-_snXWYUp.js} +1 -1
- package/src/client/dist/pwa/assets/{python-BOqcp9xc.js → python-DNAAGBcX.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-BrGwBzd7.js → razor-CB3g5p_T.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-mOGf86uY.js → typescript-0kpz8bqP.js} +1 -1
- package/src/client/dist/pwa/assets/{use-search-index-status-P7O7EaV_.js → use-search-index-status-D38yFOD_.js} +1 -1
- package/src/client/dist/pwa/assets/{use-tours-CMFPIW6f.js → use-tours-BWkHp34X.js} +2 -2
- package/src/client/dist/pwa/assets/{use-worktree-restore-DF6P7h8g.js → use-worktree-restore-DUjZr44N.js} +1 -1
- package/src/client/dist/pwa/assets/{workspace-templates-C_bgIQvs.js → workspace-templates-D-lPJQ4G.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-YcMBzYAm.js → xml-BbOuqdUe.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-BP_67Sds.js → yaml-gasFQtfk.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/mcp-server/README.md +13 -19
- package/src/mcp-server/kobo-tasks-server.ts +42 -1146
- package/src/mcp-server/managed-mcp-dispatcher.ts +1071 -0
- package/src/mcp-server/workspace-dialogue-client.ts +26 -1
- package/src/client/dist/pwa/assets/ActivityFeed-xJMPnXKA.js +0 -2
- package/src/client/dist/pwa/assets/ComparisonPanel-DwaQyEgb.js +0 -2
- package/src/client/dist/pwa/assets/CreatePage-DyF_efbs.js +0 -2
- package/src/client/dist/pwa/assets/DiffViewer-BivWNITj.js +0 -8
- package/src/client/dist/pwa/assets/GitPanel-DQjgGtoN.js +0 -3
- package/src/client/dist/pwa/assets/MainLayout-D3pGcTx8.js +0 -2
- package/src/client/dist/pwa/assets/QSelect-GQAsf7pB.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-BCWdQHS5.js +0 -9
- package/src/client/dist/pwa/assets/WorkspacePage-BHO1Q8PG.js +0 -4
- package/src/client/dist/pwa/assets/WorkspacePage-BnklZAYZ.css +0 -1
- package/src/client/dist/pwa/assets/custom-sounds-DPsfYErA.js +0 -1
- package/src/client/dist/pwa/assets/de-Bqil7RJE.js +0 -17
- package/src/client/dist/pwa/assets/es-CNcMm1Zz.js +0 -17
- package/src/client/dist/pwa/assets/fr-BObHEohg.js +0 -17
- package/src/client/dist/pwa/assets/i18n-Df_y96N2.js +0 -1
- package/src/client/dist/pwa/assets/index-CH4JpI2a.js +0 -20
- package/src/client/dist/pwa/assets/it-DDMC7Emk.js +0 -17
- package/src/client/dist/pwa/assets/network-auth-CMERg4Qk.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -6,7 +6,9 @@ Guidance for AI coding agents (Claude Code, Cursor, etc.) working on this reposi
|
|
|
6
6
|
|
|
7
7
|
**Kōbō** (工房, Japanese for "workshop") orchestrates Claude Code and OpenAI Codex agents across isolated git worktrees. Each "workspace" is a self-contained mission with its own worktree, branch, agent sessions, optional dev server, optional Notion/Sentry context, and a dedicated MCP tools server. A Vue 3 UI lets the human track progress, read live agent output, review Git changes, and manage the lifecycle. A workspace can switch engines through a handoff to a fresh session while preserving its worktree and history.
|
|
8
8
|
|
|
9
|
-
Single-user dev tool, local by default. The server binds `127.0.0.1` unless the opt-in "network access" setting is enabled
|
|
9
|
+
Single-user dev tool, local by default. The server binds `127.0.0.1` unless the opt-in "network access" setting is enabled. Operator HTTP access uses the network token or a 12-hour HttpOnly browser session; browsers use 60-second single-use path-bound tickets for WebSocket upgrades, never the operator token in the URL or localStorage. Behind-a-reverse-proxy mode also gates loopback operator requests. Rotation, network disable and proxy-mode changes revoke sessions, tickets and active WebSockets. `/api/health` stays exempt. Host/Origin checks apply separately; public proxy origin must be declared explicitly. No multi-tenant concerns.
|
|
10
|
+
|
|
11
|
+
External `/api/mcp` and global stdio require dedicated hashed credentials with tool/workspace grants, expiry and revocation, even on loopback. Manage them through operator-only `/api/mcp-credentials`; global stdio uses `KOBO_MCP_TOKEN`. Managed workspace stdio uses per-launch `KOBO_MCP_SESSION_TOKEN` through OS-loopback-only `/api/internal-mcp`, never the operator token or direct SQLite writes. The backend enforces Plan/read-only permissions and revokes capabilities when stopping begins. Keep report/progress exceptions narrowly scoped. See [SECURITY.md](./SECURITY.md).
|
|
10
12
|
|
|
11
13
|
## Tech stack
|
|
12
14
|
|
|
@@ -103,7 +105,8 @@ src/
|
|
|
103
105
|
│ ├── pages/ # WorkspacePage, CreatePage, SettingsPage
|
|
104
106
|
│ └── router/
|
|
105
107
|
├── mcp-server/ # standalone MCP server spawned per workspace
|
|
106
|
-
│ ├── kobo-tasks-server.ts #
|
|
108
|
+
│ ├── kobo-tasks-server.ts # stdio bridge; forwards discovery/calls to authenticated backend
|
|
109
|
+
│ ├── managed-mcp-dispatcher.ts # workspace tool definitions/dispatch, executed in backend
|
|
107
110
|
│ └── kobo-tasks-handlers.ts # pure handlers (list_tasks, mark_task_done)
|
|
108
111
|
├── shared/ # model catalogues, prompts, dialogue contracts, shared utilities
|
|
109
112
|
└── __tests__/ # backend Vitest suites; client tests live under client/src/__tests__/
|
|
@@ -149,6 +152,30 @@ src/
|
|
|
149
152
|
|
|
150
153
|
Tasks have `role` (`work | finalization`) and structured `verification`. MCP and HTTP share transactional task mutations. An auto-loop task cannot become done without passing reported checks. Finalization runs after all other tasks/criteria, is invalidated by scope changes, and is mandatory before completion. The proof is agent-reported evidence, not server-side execution of arbitrary commands.
|
|
151
154
|
|
|
155
|
+
## Provider account switching
|
|
156
|
+
|
|
157
|
+
The usage popover integrates optional `ccswap` for Claude Code and Codex. Select
|
|
158
|
+
accounts for the viewed session's engine, falling back to the workspace engine.
|
|
159
|
+
Missing executable hides account controls; it must not hide existing Claude
|
|
160
|
+
usage. Codex account support is separate from quota snapshot support.
|
|
161
|
+
|
|
162
|
+
`provider-accounts-service.ts` owns subprocess invocation and sanitization. Use
|
|
163
|
+
provider allowlists, numeric slots and bounded `execFile`, never shell text. Keep
|
|
164
|
+
credential mutations serialized with their validation. Return only display
|
|
165
|
+
metadata; do not forward raw CLI JSON, stderr, tokens or credential paths. Inherit
|
|
166
|
+
the Kōbō process user, HOME, PATH and credential-directory environment: in Docker,
|
|
167
|
+
the CLI must run inside that container, not on the host. Do not install tools or
|
|
168
|
+
perform real switches in tests.
|
|
169
|
+
|
|
170
|
+
A switch changes the provider-wide credential store, not a workspace identity.
|
|
171
|
+
Do not automatically stop agents, migrate conversations, copy settings/skills or
|
|
172
|
+
promise that an existing process hot-reloads authentication. Invalidate Claude
|
|
173
|
+
quota data around switches and reject results from earlier in-flight polls.
|
|
174
|
+
Keep frontend requests scoped across provider/session changes and handle missing,
|
|
175
|
+
empty, disabled, unrecognized and failed account states without automatic retries.
|
|
176
|
+
See [configuration](./CONFIGURATION.md#switch-claude-code-and-codex-accounts-with-ccswap)
|
|
177
|
+
and [VPS setup](./docs/vps-projects-and-skills.md#switch-engine-accounts-inside-the-container).
|
|
178
|
+
|
|
152
179
|
## Persistent memory
|
|
153
180
|
|
|
154
181
|
Kōbō memory is independent of provider chat history/native memory and shared by Claude Code and Codex. `memoryMode` is a global setting defaulting to `hybrid`: Manual permits reads only; Hybrid applies workspace writes and proposes project/global writes; Automatic applies writes to the explicitly targeted scope. Mode is checked per operation, so a setting change affects existing agent sessions; pending/rejected proposals are never automatically approved or revived. Plan/review/report-only capabilities cannot write.
|
|
@@ -268,7 +295,7 @@ also bypasses the auto-loop next-iteration UI path; normal backend immediate
|
|
|
268
295
|
message delivery pauses a ready loop. Do not flush existing queues on toggle,
|
|
269
296
|
remove compaction/permission/handoff guards, or queue rejected forced sends.
|
|
270
297
|
|
|
271
|
-
`POST /api/mcp` exposes stateless Streamable HTTP discovery/dialogue tools, behind
|
|
298
|
+
`POST /api/mcp` exposes stateless Streamable HTTP discovery/dialogue tools, behind Host/Origin, network enablement and dedicated MCP credential gates. The global stdio server forwards dialogue calls through this backend so agent runtime ownership remains centralized. `workspace-message-service.ts` shares delivery with WebSocket chat.
|
|
272
299
|
|
|
273
300
|
Optional `send_workspace_message.idempotency_key` uses `mcp_message_requests` (migration v43). Reserve before dispatch, preserve fingerprints and terminal outcomes, and never automatically resend an unfinished request after restart. The user event and accepted receipt commit in one SQLite transaction; broadcast follows commit. Dispatch may precede persistence, so interrupted requests are explicitly `delivery_unknown`, not an exactly-once execution promise. The backend alone calls `reconcileMessageRequests` on startup. History retention does not delete the ledger; workspace deletion cascades it.
|
|
274
301
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to Kōbō are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/). Each release is an `## <version>`
|
|
5
5
|
section — the in-app "What's new" dialog reads this file.
|
|
6
6
|
|
|
7
|
+
## 2.1.28
|
|
8
|
+
|
|
9
|
+
- fix(ci): authenticate packaged mcp smoke tests
|
|
10
|
+
- fix(security): harden authentication and mcp access
|
|
11
|
+
- feat(models): Add Haiku 5.5
|
|
12
|
+
|
|
13
|
+
## 2.1.27
|
|
14
|
+
|
|
15
|
+
- fix(security): harden authentication and mcp access
|
|
16
|
+
- feat(models): Add Haiku 5.5
|
|
17
|
+
|
|
18
|
+
## 2.1.26
|
|
19
|
+
|
|
20
|
+
- feat: switch Claude and Codex accounts with ccswap
|
|
21
|
+
|
|
7
22
|
## 2.1.25
|
|
8
23
|
|
|
9
24
|
- feat: improve merge options, diff controls and progress reporting
|
package/CONFIGURATION.md
CHANGED
|
@@ -887,11 +887,28 @@ destination worktree and sends file/image references to the agent and chat.
|
|
|
887
887
|
Keyed retries include attachment identity and never upload or send twice. See
|
|
888
888
|
[Sending files and images](src/mcp-server/README.md#sending-files-and-images).
|
|
889
889
|
|
|
890
|
-
|
|
890
|
+
Every external MCP client, including loopback and global stdio clients, must use
|
|
891
|
+
a dedicated MCP key. The operator token and browser session cookie are not accepted
|
|
892
|
+
by `/api/mcp`. Send the MCP key as `Authorization: Bearer <key>`. The endpoint retains the same Host/Origin, network-access and
|
|
893
|
+
reverse-proxy policy. No network setting changes automatically.
|
|
894
|
+
|
|
895
|
+
Keys are independent, stored only as hashes and have explicit tool and workspace
|
|
896
|
+
grants, optional expiration and individual revocation. The default tool grant is
|
|
897
|
+
read-only; writes and destructive tools require an explicit grant. Each key must
|
|
898
|
+
select workspace IDs or deliberately grant all workspaces. An MCP key cannot
|
|
899
|
+
authenticate to operator REST, terminal or credential-management endpoints.
|
|
900
|
+
|
|
901
|
+
An authenticated operator manages credentials through `/api/mcp-credentials`:
|
|
902
|
+
GET lists metadata and available tools; POST accepts `name`, optional `tools`,
|
|
903
|
+
`workspaceIds` or `allWorkspaces: true`, and optional ISO `expiresAt`; DELETE
|
|
904
|
+
`/api/mcp-credentials/<id>` revokes one key. Creation returns the raw `token` once.
|
|
905
|
+
Do not send that token to logs, commit it, or reuse it as the operator credential.
|
|
891
906
|
|
|
892
907
|
See [Connect an external LLM](src/mcp-server/README.md#connect-an-external-llm) for connection examples, tool arguments, cursor polling and authentication details.
|
|
893
908
|
|
|
894
|
-
The **External LLM access (MCP)** panel within global Network access settings
|
|
909
|
+
The **External LLM access (MCP)** panel within global Network access settings
|
|
910
|
+
provides HTTP/stdio configuration templates. Supply the dedicated MCP key for the
|
|
911
|
+
intended client. Client names are display labels, not permission identities.
|
|
895
912
|
|
|
896
913
|
External senders can supply `idempotency_key` once per intended message and reuse it on retries. The durable receipt survives backend restarts and event retention. An uncertain delivery is explicitly reported and never automatically sent again; see the MCP guide for response codes and limitations.
|
|
897
914
|
|
|
@@ -1058,8 +1075,9 @@ To disable network access, toggle it off and restart again.
|
|
|
1058
1075
|
|
|
1059
1076
|
### The network access token
|
|
1060
1077
|
|
|
1061
|
-
When network access is enabled,
|
|
1062
|
-
|
|
1078
|
+
When network access is enabled, remote API and WebSocket access requires
|
|
1079
|
+
operator authentication. The shared operator token opens a browser session;
|
|
1080
|
+
external MCP clients use separate scoped credentials.
|
|
1063
1081
|
|
|
1064
1082
|
- **Auto-generated**: Kōbō generates a random token the first time you enable
|
|
1065
1083
|
the feature. You never have to type one in.
|
|
@@ -1075,11 +1093,12 @@ non-loopback WebSocket connection must supply a shared token.
|
|
|
1075
1093
|
|
|
1076
1094
|
**Option A: QR code (recommended for phones)**
|
|
1077
1095
|
|
|
1078
|
-
The Settings panel shows a QR code
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1096
|
+
The Settings panel shows a QR code using a URL fragment (`#token=<token>`),
|
|
1097
|
+
which browsers do not send in HTTP requests. The client immediately removes the
|
|
1098
|
+
fragment and exchanges the token for a 12-hour HttpOnly, SameSite=Strict session
|
|
1099
|
+
cookie (Secure for a configured HTTPS proxy). The operator token is not retained in localStorage.
|
|
1100
|
+
Previously stored tokens are removed during migration. Use QR links only on
|
|
1101
|
+
trusted devices; do not publish or bookmark a credential-bearing link.
|
|
1083
1102
|
|
|
1084
1103
|
**Option B: manual URL**
|
|
1085
1104
|
|
|
@@ -1087,22 +1106,27 @@ session. The token is stored in the browser's `localStorage` under
|
|
|
1087
1106
|
`http://<lan-ip>:<port>/`).
|
|
1088
1107
|
2. Open it on the remote device.
|
|
1089
1108
|
3. A login dialog appears: paste the token from the Settings panel and confirm.
|
|
1090
|
-
4. The
|
|
1109
|
+
4. The HttpOnly session cookie authenticates subsequent visits until expiry or revocation.
|
|
1091
1110
|
|
|
1092
1111
|
**Host machine (localhost)**: connect to `http://localhost:<port>` as usual.
|
|
1093
1112
|
No token is needed unless **Behind a reverse proxy** is enabled.
|
|
1094
1113
|
|
|
1095
1114
|
### Token transport
|
|
1096
1115
|
|
|
1097
|
-
| Connection type |
|
|
1116
|
+
| Connection type | Authentication |
|
|
1098
1117
|
|---|---|
|
|
1099
|
-
|
|
|
1100
|
-
|
|
|
1101
|
-
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1118
|
+
| Browser API requests | HttpOnly session cookie after login |
|
|
1119
|
+
| Operator scripts / REST | `X-Kobo-Token: <operator-token>` header |
|
|
1120
|
+
| External MCP HTTP (`/api/mcp`) | Dedicated MCP key as `Authorization: Bearer <key>` |
|
|
1121
|
+
| Browser WebSocket connections | Short-lived, single-use ticket in the upgrade URL, bound to its WS path |
|
|
1122
|
+
|
|
1123
|
+
The browser handles session and ticket exchanges automatically. Tickets expire
|
|
1124
|
+
after 60 seconds and cannot be replayed, reused for a different terminal, or used
|
|
1125
|
+
after their session expires. The permanent operator token is no longer accepted
|
|
1126
|
+
in a WebSocket query string. Rotation, disabling network access or changing proxy
|
|
1127
|
+
mode invalidates browser sessions, pending tickets and existing main/terminal
|
|
1128
|
+
WebSocket connections. Agent processes are not stopped by credential revocation.
|
|
1129
|
+
Server restart also expires browser sessions.
|
|
1106
1130
|
|
|
1107
1131
|
The IP address used for the loopback exemption is taken **from the OS socket**,
|
|
1108
1132
|
never from forwarded headers (`X-Forwarded-For` etc.), so a remote client cannot
|
|
@@ -1154,19 +1178,15 @@ Practical consequences:
|
|
|
1154
1178
|
count as the real interface. In development the Quasar dev server is the one
|
|
1155
1179
|
exception, since it proxies to the backend and so never matches the `Host`;
|
|
1156
1180
|
the `dev` npm script declares it through `KOBO_DEV_CLIENT_ORIGIN`.
|
|
1157
|
-
- **Behind-a-reverse-proxy mode
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
mode also disables the loopback exemption, so every `/api/*` request has to
|
|
1162
|
-
carry the token — but the SPA itself is served outside `/api/*` and behind no
|
|
1163
|
-
token, so without a declared hostname a rebinding page can be served the real
|
|
1164
|
-
interface and ask the user to paste their token into it.
|
|
1181
|
+
- **Behind-a-reverse-proxy mode requires a declared public hostname.** Set
|
|
1182
|
+
`KOBO_NETWORK_ACCESS_PROXY_HOST`; missing or invalid configuration rejects
|
|
1183
|
+
public requests. Internal loopback clients remain reachable but still require
|
|
1184
|
+
authentication. The bundled proxy Compose files configure this explicitly.
|
|
1165
1185
|
|
|
1166
1186
|
#### `KOBO_NETWORK_ACCESS_PROXY_HOST`
|
|
1167
1187
|
|
|
1168
|
-
Set it to the
|
|
1169
|
-
|
|
1188
|
+
Set it to the public origin your proxy serves Kōbō under. This is required in
|
|
1189
|
+
behind-a-reverse-proxy mode:
|
|
1170
1190
|
|
|
1171
1191
|
```yaml
|
|
1172
1192
|
environment:
|
|
@@ -1175,9 +1195,33 @@ environment:
|
|
|
1175
1195
|
- KOBO_NETWORK_ACCESS_PROXY_HOST=kobo.example.com
|
|
1176
1196
|
```
|
|
1177
1197
|
|
|
1178
|
-
A
|
|
1179
|
-
|
|
1180
|
-
|
|
1198
|
+
A bare hostname means HTTPS. For a nonstandard port, set the complete origin,
|
|
1199
|
+
e.g. `https://kobo.example.com:8443`. Browser Origin checks compare the scheme,
|
|
1200
|
+
hostname and port exactly. Local HTTP proxy staging must explicitly declare
|
|
1201
|
+
`http://kobo.localhost`. Paths, credentials, query strings and fragments are
|
|
1202
|
+
invalid. Loopback healthchecks and authenticated internal calls remain reachable.
|
|
1203
|
+
Leaving this variable unset rejects public proxy traffic.
|
|
1204
|
+
|
|
1205
|
+
### Browser response protections
|
|
1206
|
+
|
|
1207
|
+
The server sends `Content-Security-Policy: frame-ancestors 'self'` and
|
|
1208
|
+
`X-Frame-Options: SAMEORIGIN`, preserving Kōbō's split panes while blocking
|
|
1209
|
+
embedding by another site. It also sends `X-Content-Type-Options: nosniff`,
|
|
1210
|
+
`Referrer-Policy: no-referrer` and `Cache-Control: no-store` for API responses.
|
|
1211
|
+
The public HTTPS Compose examples add one-year HSTS at Traefik, without applying
|
|
1212
|
+
it to subdomains or HTTP-only local staging. Kōbō does not trust forwarded headers
|
|
1213
|
+
to decide whether a connection is HTTPS. A broader script/style CSP is not enabled;
|
|
1214
|
+
it needs separate compatibility testing with Quasar and Monaco.
|
|
1215
|
+
|
|
1216
|
+
Application limits apply per OS peer address (forwarded addresses are not trusted):
|
|
1217
|
+
3,000 API requests per minute and 20 login attempts per minute. Requests exceeding
|
|
1218
|
+
these limits receive HTTP 429. MCP permits 32 concurrent requests, with at most
|
|
1219
|
+
four bodies larger than 1 MiB or of unknown/chunked length; ordinary attachment
|
|
1220
|
+
uploads also have four slots. Authentication runs before body buffering. Main
|
|
1221
|
+
WebSocket messages are capped at 1 MiB, terminal messages at 128 KiB; sockets
|
|
1222
|
+
have per-peer and global caps and inbound message-rate limits. Behind a proxy,
|
|
1223
|
+
clients share the proxy peer's bucket, so configure client-IP limits at the proxy
|
|
1224
|
+
for deployments with many simultaneous clients.
|
|
1181
1225
|
|
|
1182
1226
|
### Security caveats
|
|
1183
1227
|
|
|
@@ -1300,7 +1344,7 @@ retrieved once — see [Token / login flow](#token--login-flow).
|
|
|
1300
1344
|
| `${HOME}/.claude.json:/root/.claude.json` | RW | Claude Code auth + MCP server registrations. **Must already exist as a file on the host** (run `claude /login` locally first if it doesn't) — Docker silently creates an empty *directory* instead of erroring on a missing bind-mount source, which breaks auth in a confusing way. |
|
|
1301
1345
|
| `${HOME}/.claude:/root/.claude` | RW | Claude Code's full state directory (skills/plugins cache, session transcripts, memory) — Claude Code writes into this tree during normal operation. |
|
|
1302
1346
|
| `${HOME}/.codex:/root/.codex` | RW | Codex's full state directory (`auth.json`, `config.toml`). Harmless to mount even if you only use Claude Code. |
|
|
1303
|
-
| `/var/run/docker.sock:/var/run/docker.sock` | RW,
|
|
1347
|
+
| `/var/run/docker.sock:/var/run/docker.sock` | RW, opt-in in both `example.yml` and `vps.yml` | Lets the dev-server panel start/stop/tail logs for your projects' own `docker compose` stacks. **DANGER**: grants the container root-equivalent control over the host — anyone who can run a command inside `kobo` can spawn a privileged container that mounts the host filesystem. Leave this mount commented out unless required. |
|
|
1304
1348
|
|
|
1305
1349
|
### Ports
|
|
1306
1350
|
|
|
@@ -1316,25 +1360,32 @@ reachable, matching the real production posture.
|
|
|
1316
1360
|
|
|
1317
1361
|
### Token / login flow
|
|
1318
1362
|
|
|
1319
|
-
All four files pre-enable network access via
|
|
1320
|
-
|
|
1363
|
+
All four files pre-enable network access via environment variables. Startup logs
|
|
1364
|
+
never contain the token. Retrieve it deliberately from a trusted local terminal:
|
|
1321
1365
|
|
|
1322
1366
|
```bash
|
|
1323
|
-
docker compose -f <compose-file>
|
|
1367
|
+
docker compose -f <compose-file> exec kobo node -e 'const fs = require("node:fs"); console.log(JSON.parse(fs.readFileSync(process.env.KOBO_HOME + "/settings.json", "utf8")).global.networkAccessToken)'
|
|
1324
1368
|
```
|
|
1325
1369
|
|
|
1326
|
-
Open
|
|
1327
|
-
|
|
1370
|
+
Open the plain application URL and paste the token into its login dialog. Do not
|
|
1371
|
+
put the token in a URL, command argument, log collector or shared transcript.
|
|
1372
|
+
For an npm installation, read `networkAccessToken` from your private
|
|
1373
|
+
`$KOBO_HOME/settings.json` (default `~/.config/kobo/settings.json`).
|
|
1374
|
+
Kōbō restricts its home to mode `0700` and settings, temporary settings and
|
|
1375
|
+
settings backups to `0600`, including existing files on startup or the next disk read.
|
|
1376
|
+
|
|
1377
|
+
Existing logs from older releases may still contain a valid token. After upgrading,
|
|
1378
|
+
rotate it from Settings and apply your log-retention policy to retained copies.
|
|
1379
|
+
Kōbō does not delete historical logs automatically.
|
|
1328
1380
|
|
|
1329
1381
|
### Quick local test — `docker-compose.local.yml`
|
|
1330
1382
|
|
|
1331
1383
|
```bash
|
|
1332
1384
|
docker build -t kobo:local . # once
|
|
1333
1385
|
docker compose -f docker-compose.local.yml up -d
|
|
1334
|
-
docker compose -f docker-compose.local.yml logs kobo | grep Token
|
|
1335
1386
|
```
|
|
1336
1387
|
|
|
1337
|
-
Open `http://localhost:3000
|
|
1388
|
+
Open `http://localhost:3000/`; retrieve the token using [Token / login flow](#token--login-flow) if prompted.
|
|
1338
1389
|
|
|
1339
1390
|
By default the file bind-mounts `./kobo-projects` (next to it) at `/projects` — put a real git repo
|
|
1340
1391
|
in there (or set `KOBO_PROJECTS_DIR` to point elsewhere) before creating a workspace. When you get
|
|
@@ -1353,10 +1404,9 @@ and lose all Kōbō state).
|
|
|
1353
1404
|
```bash
|
|
1354
1405
|
docker build -t kobo:local . # once, if not already built
|
|
1355
1406
|
docker compose -f docker-compose.local-traefik.yml up -d
|
|
1356
|
-
docker compose -f docker-compose.local-traefik.yml logs kobo | grep Token
|
|
1357
1407
|
```
|
|
1358
1408
|
|
|
1359
|
-
Open `http://kobo.localhost
|
|
1409
|
+
Open `http://kobo.localhost/` and sign in using [Token / login flow](#token--login-flow) — `.localhost` resolves to `127.0.0.1` on
|
|
1360
1410
|
most OSes without touching `/etc/hosts`. This exercises the exact routing/auth/mount setup used by
|
|
1361
1411
|
`docker-compose.example.yml`, minus a real domain and TLS certificate.
|
|
1362
1412
|
|
|
@@ -1386,17 +1436,16 @@ Before using it:
|
|
|
1386
1436
|
2. Replace `kobo.example.com` with your real domain, and `you@example.com` with a real email (Let's Encrypt account).
|
|
1387
1437
|
3. Populate a `.env` file next to the compose file with `GH_TOKEN` / `GITLAB_TOKEN` if you use the forge integration.
|
|
1388
1438
|
4. Point every bind mount (`~/.ssh`, `~/.gitconfig`, `~/.claude.json`, `~/.claude`, `~/.codex`) at real paths on the host — see [Volumes](#volumes) for what each one is for, in particular the "`~/.claude.json` must already exist as a file" gotcha.
|
|
1389
|
-
5.
|
|
1439
|
+
5. Leave the Docker socket unmounted unless your project workflows require it; opting in grants host-level control. See [Volumes](#volumes).
|
|
1390
1440
|
6. Network access + behind-a-reverse-proxy are already pre-enabled via the `KOBO_NETWORK_ACCESS_*` env vars in the file — no manual bootstrap step.
|
|
1391
1441
|
|
|
1392
|
-
|
|
1442
|
+
Start it, then retrieve the token using the login flow above:
|
|
1393
1443
|
|
|
1394
1444
|
```bash
|
|
1395
1445
|
docker compose -f docker-compose.example.yml up -d
|
|
1396
|
-
docker compose -f docker-compose.example.yml logs kobo | grep Token
|
|
1397
1446
|
```
|
|
1398
1447
|
|
|
1399
|
-
Open `https://kobo.example.com
|
|
1448
|
+
Open `https://kobo.example.com/` and sign in using [Token / login flow](#token--login-flow). SSH access: `ssh -p 2222 root@<host>` (key-based
|
|
1400
1449
|
only). See [Ports](#ports) and [Volumes](#volumes) above for the full rationale on the Docker socket
|
|
1401
1450
|
and SSH tradeoffs before deploying this to a real machine.
|
|
1402
1451
|
|
|
@@ -1455,9 +1504,8 @@ An old image alone is not a complete rollback after database migrations.
|
|
|
1455
1504
|
— check `docker inspect <container> --format '{{json .State.Health}}'`. This should not happen on
|
|
1456
1505
|
a current image (`/api/health` is exempt from the token gate, see [The image](#the-image)); if you
|
|
1457
1506
|
see a 401 in the health log on a custom-built image, you're missing that exemption.
|
|
1458
|
-
-
|
|
1459
|
-
|
|
1460
|
-
the `kobo` service.
|
|
1507
|
+
- **The token is absent from startup logs** — this is intentional. Use the local
|
|
1508
|
+
[token retrieval command](#token--login-flow), then the browser login dialog.
|
|
1461
1509
|
|
|
1462
1510
|
## Agent runtimes
|
|
1463
1511
|
|
|
@@ -1473,7 +1521,7 @@ claude /login
|
|
|
1473
1521
|
|
|
1474
1522
|
Kōbō talks to the embedded [`@anthropic-ai/claude-agent-sdk`](https://github.com/anthropics/claude-agent-sdk-typescript), which reuses the same login. The `claude` CLI is **not** required at runtime; you only need it for `/login`, `mcp add`, and other one-off setup commands. As a fallback you can export `ANTHROPIC_API_KEY` instead.
|
|
1475
1523
|
|
|
1476
|
-
The model selector is populated from [`src/shared/models.ts`](./src/shared/models.ts), also used by backend validation. This checkout includes Fable 5.1, Mythos 5, Opus 5,
|
|
1524
|
+
The model selector is populated from [`src/shared/models.ts`](./src/shared/models.ts), also used by backend validation. This checkout includes Fable 5.1, Mythos 5.1/5, Opus 5.5, Sonnet 5.5, Haiku 5.5 (`claude-haiku-5-5`) and older variants. Pick the default in **Settings → Agents**. Catalogue entries describe what Kōbō offers; provider access still depends on your account. `auto` leaves model selection to the runtime.
|
|
1477
1525
|
|
|
1478
1526
|
#### Immediate chat messages
|
|
1479
1527
|
|
|
@@ -1549,6 +1597,65 @@ When a Codex session is already working, **Send now** sends a queued chat messag
|
|
|
1549
1597
|
|
|
1550
1598
|
The selector reads [`src/shared/codex-models.ts`](./src/shared/codex-models.ts). This checkout lists GPT-6 Astra, GPT-5.6 Sol/Terra/Luna, GPT-5.5 and older variants; availability depends on your account and authentication. Kōbō exposes effort values `auto`, `none`, `low`, `medium`, `high`, `xhigh`, and `max`, as declared in the engine's capabilities. The runtime must support the selected model/effort combination. Both selectors switch when you change the engine.
|
|
1551
1599
|
|
|
1600
|
+
### Switch Claude Code and Codex accounts with ccswap
|
|
1601
|
+
|
|
1602
|
+
The usage popover in the chat footer can show the current **managed account**
|
|
1603
|
+
and switch to another account registered in [ccswap](https://github.com/errhythm/ccswap).
|
|
1604
|
+
It follows the selected session's engine, falling back to the workspace engine.
|
|
1605
|
+
Choose an account and explicitly click **Switch account**. Claude Code and Codex
|
|
1606
|
+
have separate account lists; switching one does not switch the other.
|
|
1607
|
+
|
|
1608
|
+
ccswap is optional and is not bundled with Kōbō. Without its executable, Kōbō
|
|
1609
|
+
shows no account selector or installation notice; existing Claude usage indicators
|
|
1610
|
+
continue to work. Codex account controls do not imply that Claude-style quota
|
|
1611
|
+
buckets are available for Codex.
|
|
1612
|
+
|
|
1613
|
+
Install and register accounts **as the same operating-system user and in the
|
|
1614
|
+
same environment as the Kōbō server**. The integration uses ccswap's JSON CLI
|
|
1615
|
+
(tested with 0.37.0). With `uv` already installed:
|
|
1616
|
+
|
|
1617
|
+
```bash
|
|
1618
|
+
uv tool install ccswap
|
|
1619
|
+
# After logging into Claude Code with each account:
|
|
1620
|
+
ccswap add
|
|
1621
|
+
# After `codex login` with each Codex account:
|
|
1622
|
+
ccswap codex add
|
|
1623
|
+
ccswap list --provider claude
|
|
1624
|
+
ccswap codex list
|
|
1625
|
+
```
|
|
1626
|
+
|
|
1627
|
+
Register the current login before switching so ccswap can preserve it. Account
|
|
1628
|
+
registration, login renewal, aliases and removal remain CLI operations. A disabled
|
|
1629
|
+
account cannot be selected in Kōbō. If no managed account matches the current
|
|
1630
|
+
login, the panel reports an unknown account rather than guessing from a slot.
|
|
1631
|
+
|
|
1632
|
+
**Scope:** switching changes that provider's active credentials for the server
|
|
1633
|
+
user, shared by all Kōbō workspaces and other tools using that same login store.
|
|
1634
|
+
It is not a per-workspace account assignment. Kōbō keeps its worktrees, conversation
|
|
1635
|
+
history, settings and skills; it does not copy profiles or automatically restart
|
|
1636
|
+
agents. The displayed account identifies the active credential store, not proof
|
|
1637
|
+
that an already-running process has adopted it. Stop and resume the affected
|
|
1638
|
+
sessions if they still use the previous account, especially with Codex. Existing
|
|
1639
|
+
API-key or custom-provider authentication can take precedence over a saved login.
|
|
1640
|
+
|
|
1641
|
+
The adapter inherits absolute `CLAUDE_CONFIG_DIR` on Linux and `CODEX_HOME`.
|
|
1642
|
+
Empty or relative authentication-directory overrides are refused: server and workspace
|
|
1643
|
+
processes can have different working directories. Claude switching is also refused
|
|
1644
|
+
with `CLAUDE_SECURESTORAGE_CONFIG_DIR` set, or with any nonempty `CLAUDE_CONFIG_DIR`
|
|
1645
|
+
on macOS (even the explicit default path), because the corresponding Keychain
|
|
1646
|
+
write is not verified by this integration. Use the native login workflow for
|
|
1647
|
+
those configurations.
|
|
1648
|
+
|
|
1649
|
+
Claude usage is invalidated and refreshed after a switch. A failed refresh is
|
|
1650
|
+
not shown as the new account's old quota. Switching failures do not trigger an
|
|
1651
|
+
automatic retry: refresh the account list to check the actual state before trying
|
|
1652
|
+
again. The server exposes display metadata only, never ccswap's credential files
|
|
1653
|
+
or raw command output.
|
|
1654
|
+
|
|
1655
|
+
For Docker/VPS, installing ccswap on your PC or on the VPS host is insufficient:
|
|
1656
|
+
it must be reachable on **Kōbō's container PATH**, with the same runtime home and
|
|
1657
|
+
credential-directory environment. See [VPS account setup](./docs/vps-projects-and-skills.md#switch-engine-accounts-inside-the-container).
|
|
1658
|
+
|
|
1552
1659
|
### Permission modes
|
|
1553
1660
|
|
|
1554
1661
|
Claude Code uses SDK permission modes; Codex uses sandbox and approval flags. These controls differ by engine. A Git worktree itself is not a security boundary.
|
package/README.md
CHANGED
|
@@ -64,7 +64,8 @@ Kōbō's production build is an installable PWA — use your browser's **Install
|
|
|
64
64
|
- **Interactive Q&A**: an agent can pause mid-session to ask you a question through the UI instead of guessing.
|
|
65
65
|
|
|
66
66
|

|
|
67
|
-
- **Quota-aware**: 5-hour / 7-day Claude usage
|
|
67
|
+
- **Quota-aware**: 5-hour / 7-day Claude usage is shown in the footer; engine-reported rate limits guide quota waits; auto-loops can resume after a reset. Explicit cron/wakeup schedules can also resume work; a manual workspace does not restart solely because its quota resets.
|
|
68
|
+
- **Account switching**: with optional [ccswap](https://github.com/errhythm/ccswap), view and switch the active Claude Code or Codex account from the usage popover. Credentials are shared across workspaces using that engine; [setup and session behavior](./CONFIGURATION.md#switch-claude-code-and-codex-accounts-with-ccswap).
|
|
68
69
|
- **Disk-space purge**: reclaim a merged workspace's `node_modules`/`vendor` weight without losing its chat history — see [`CONFIGURATION.md`](./CONFIGURATION.md#auto-purge-worktree-on-pr-merged).
|
|
69
70
|
- **Lifecycle scripts**: shell scripts run on setup, cleanup, archive, session end, PR merge, or auto-loop stop, with output streamed into the chat.
|
|
70
71
|
- **Observability**: a per-session timeline (duration, tools, tokens, errors) and a downloadable redacted diagnostic JSON.
|
package/SECURITY.md
CHANGED
|
@@ -17,11 +17,37 @@ the account running Kōbō and the selected engine's permission mode. A Git work
|
|
|
17
17
|
isolates a checkout, not processes, credentials or the filesystem. Only open
|
|
18
18
|
projects and configure executable hooks/integrations you trust.
|
|
19
19
|
|
|
20
|
-
The default listener is loopback.
|
|
21
|
-
per-user authorization.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
The default listener is loopback. Network access uses an operator token; it is not
|
|
21
|
+
per-user authorization. Browsers exchange it for a 12-hour HttpOnly, SameSite=Strict
|
|
22
|
+
cookie (Secure behind HTTPS proxies), never persist it in localStorage, and obtain
|
|
23
|
+
60-second single-use tickets for each WebSocket path. Rotation, disabling network
|
|
24
|
+
access, or changing proxy mode revokes sessions, pending tickets and active sockets,
|
|
25
|
+
including terminals. Restarting Kōbō also expires sessions. A settings change may
|
|
26
|
+
establish a new session for the browser that performed it using the returned token.
|
|
27
|
+
|
|
28
|
+
Behind a reverse proxy, enable the corresponding setting and declare the exact
|
|
29
|
+
public origin with `KOBO_NETWORK_ACCESS_PROXY_HOST` (bare domains default to HTTPS).
|
|
30
|
+
Health checks are exempt. Host and Origin validation remain separate protections.
|
|
31
|
+
Use HTTPS or a trusted private network; do not offer this installation as a shared
|
|
32
|
+
public service. Configure HSTS at the HTTPS terminator. The supplied public Compose
|
|
33
|
+
examples include it. Socket and request size/rate/concurrency limits are bounded
|
|
34
|
+
in-process; proxy limits should complement them. Client IPs come from OS sockets,
|
|
35
|
+
so callers behind the same proxy share limits.
|
|
36
|
+
|
|
37
|
+
External MCP requires a separately issued Bearer credential, including on loopback.
|
|
38
|
+
Each key has explicit tool and workspace grants, optional expiry, and independent
|
|
39
|
+
revocation; only its hash is stored. The UI defaults to read-only tools, selected
|
|
40
|
+
workspaces and 30-day expiry. Operator cookies/tokens cannot authenticate `/api/mcp`,
|
|
41
|
+
and an MCP key cannot authenticate operator APIs or terminal upgrades. Global stdio
|
|
42
|
+
forwards to that endpoint using `KOBO_MCP_TOKEN`; it does not read the database offline.
|
|
43
|
+
|
|
44
|
+
Managed workspace stdio uses a per-launch `KOBO_MCP_SESSION_TOKEN` through the local
|
|
45
|
+
`/api/internal-mcp` dispatcher. The backend owns authorization and database writes;
|
|
46
|
+
Plan and read-only review launches cannot invoke mutating managed tools even if the
|
|
47
|
+
provider approves a tool. Launch capabilities are revoked when stopping begins.
|
|
48
|
+
Operational progress and narrowly scoped review/handoff reports retain their own
|
|
49
|
+
capabilities. This tool boundary does not sandbox arbitrary code executed by an
|
|
50
|
+
agent or other integrations running as the same OS account.
|
|
25
51
|
|
|
26
52
|
Git workflow preferences express which automatic actions are wanted. They are not
|
|
27
53
|
a sandbox for arbitrary shell commands or external MCP tools. Claude and Codex
|
|
@@ -33,7 +59,8 @@ network token.
|
|
|
33
59
|
|
|
34
60
|
KOBO_HOME stores conversations, workspaces, backups and settings. Direct integration
|
|
35
61
|
commands and credentials live in `integrations.json`, written atomically with mode
|
|
36
|
-
0600 on Unix.
|
|
62
|
+
0600 on Unix. Settings and their temporary files/backups are also private (0600),
|
|
63
|
+
and new Kōbō home directories use 0700. They are plaintext local secrets: protect the host account and backups.
|
|
37
64
|
They are excluded from settings exports and are never returned by the connection
|
|
38
65
|
status API. A replacement connection must be entered in full. MCP error text and
|
|
39
66
|
stderr are withheld because third-party processes may echo secrets.
|