@cuongtran001/kanna 1.67.0 → 1.69.0
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/README.md +75 -522
- package/dist/client/assets/{BoardsPage-C2T8sSTQ.js → BoardsPage-DTmZ_q-O.js} +1 -1
- package/dist/client/assets/{BoardsRoutePage-yFcf16Sj.js → BoardsRoutePage-BPkIHGWS.js} +1 -1
- package/dist/client/assets/{ChatPage-CrWeosTh.js → ChatPage-ZzKjjZmf.js} +25 -25
- package/dist/client/assets/{CronJobRow-CoxRFgk8.js → CronJobRow-B5-QF03b.js} +1 -1
- package/dist/client/assets/{CronJobsPage-BBWb3cpr.js → CronJobsPage-DWdIwkaO.js} +1 -1
- package/dist/client/assets/{FilePreviewSheet-_u3Hky2d.js → FilePreviewSheet-B-vqza_x.js} +1 -1
- package/dist/client/assets/{FlowSurface-Bsfc3AhT.js → FlowSurface-_lY_OPzt.js} +1 -1
- package/dist/client/assets/{GenUIBlock-BDVTy_sF.js → GenUIBlock-Da_7u2fH.js} +2 -2
- package/dist/client/assets/{KannaSocketProvider-DojWJOnY.js → KannaSocketProvider-D_TAtyAC.js} +1 -1
- package/dist/client/assets/{LocalProjectsPage-D-j7cBdi.js → LocalProjectsPage-B1Y2QWrs.js} +1 -1
- package/dist/client/assets/SettingsPage-DRexyxcJ.js +9 -0
- package/dist/client/assets/{StackBoardsRoutePage-1yJ_BBfb.js → StackBoardsRoutePage-BCJ_yqiG.js} +1 -1
- package/dist/client/assets/{ToolCallMessage-CCIa8iLC.js → ToolCallMessage-aj1aW8JX.js} +2 -2
- package/dist/client/assets/{VChartSurface-q39jjUGY.js → VChartSurface-B-AuGNwZ.js} +1 -1
- package/dist/client/assets/{WorkflowsPage-vq6SjdqW.js → WorkflowsPage-C0unZXdl.js} +1 -1
- package/dist/client/assets/{WorkflowsSection-COfXr2vr.js → WorkflowsSection-B01wmTQh.js} +1 -1
- package/dist/client/assets/{chart-renderer-Ci_QT6Iu.js → chart-renderer-DUVu_Vom.js} +1 -1
- package/dist/client/assets/{chatSounds-DnyvKe5_.js → chatSounds-Cvzp1r73.js} +1 -1
- package/dist/client/assets/chevron-left-B5c0kejC.js +1 -0
- package/dist/client/assets/{clipboard.adapter-CY3seClY.js → clipboard.adapter-Glj1PZVs.js} +1 -1
- package/dist/client/assets/{columnStyle-5oHawwqK.js → columnStyle-CpWHYDn4.js} +1 -1
- package/dist/client/assets/copyFeedbackStore-CDI6qd4R.js +1 -0
- package/dist/client/assets/{createScopedStore-63a9fiw3.js → createScopedStore-DrKjeeKh.js} +1 -1
- package/dist/client/assets/{dist-BiDIRB9T.js → dist-DWGlhxRR.js} +1 -1
- package/dist/client/assets/{ellipsis-BliZ9vcZ.js → ellipsis-C9LYN4YF.js} +1 -1
- package/dist/client/assets/{flow-kinds-Cixxo5yn.js → flow-kinds-BihdLKhe.js} +1 -1
- package/dist/client/assets/{folder-Du8Oad_p.js → folder-BTayxG0a.js} +1 -1
- package/dist/client/assets/{formatDuration-B426Ob_B.js → formatDuration-B8ikYfEe.js} +1 -1
- package/dist/client/assets/{genui-BsxRiyN8.js → genui-BIJkDpoV.js} +1 -1
- package/dist/client/assets/{globe-BaWK1JOT.js → globe-B_2hG84S.js} +1 -1
- package/dist/client/assets/{host-mYDSOTHR.js → host-ClmGCQXH.js} +1 -1
- package/dist/client/assets/{index-7GCrJp1V.js → index-DsG0DNQb.js} +3 -3
- package/dist/client/assets/{info-5g3HZ2W0.js → info-CV2VB0vT.js} +1 -1
- package/dist/client/assets/{input-BzbnW5S3.js → input-DWu2QcwM.js} +1 -1
- package/dist/client/assets/{lexicalToReact-C9sAa7UU.js → lexicalToReact-AltwA__G.js} +3 -3
- package/dist/client/assets/{log-D__4mSvd.js → log-CEUqK_69.js} +1 -1
- package/dist/client/assets/{monitor-DRVD7KR_.js → monitor-BOWOG6h3.js} +1 -1
- package/dist/client/assets/{pendingActionsStore-DgmsoQZR.js → pendingActionsStore-zMxtbSpz.js} +1 -1
- package/dist/client/assets/{play-Bo0GmR-f.js → play-DDrwd-g4.js} +1 -1
- package/dist/client/assets/{plus-BiOoPRJi.js → plus-jdBWxuX5.js} +1 -1
- package/dist/client/assets/{popover-3H9mjx_a.js → popover-BRtltq1y.js} +1 -1
- package/dist/client/assets/{pushClient-C2HhavNR.js → pushClient-Dz4O4LzE.js} +1 -1
- package/dist/client/assets/runDetached-D6TwdT-n.js +1 -0
- package/dist/client/assets/{segmented-control-KOyATpoR.js → segmented-control-Dl8hai_i.js} +1 -1
- package/dist/client/assets/{select-Cvq7WkNO.js → select-B8SJJjUL.js} +1 -1
- package/dist/client/assets/{settings-header-button-CLzvHbcR.js → settings-header-button-B6GkTTTH.js} +1 -1
- package/dist/client/assets/{sidebarPendingActions-DTESQFoV.js → sidebarPendingActions-LftMvPZA.js} +1 -1
- package/dist/client/assets/{square-pen-DDDMUViZ.js → square-pen-Dm8mftBD.js} +1 -1
- package/dist/client/assets/{state-mark-CjH7aUDS.js → state-mark-B-yoBxy1.js} +1 -1
- package/dist/client/assets/{status-pill-CQtea72n.js → status-pill-B3Gc-f4G.js} +1 -1
- package/dist/client/assets/{terminal-DHQBFZsP.js → terminal-BES_nOaH.js} +1 -1
- package/dist/client/assets/{timer.adapter-DPSmKprb.js → timer.adapter-M55jg760.js} +1 -1
- package/dist/client/assets/{tools-OxeTgYbw.js → tools-BDCVK80z.js} +1 -1
- package/dist/client/assets/{triangle-alert-mekfRAD6.js → triangle-alert-DirRSWaW.js} +1 -1
- package/dist/client/assets/{useAppGlobalState-CybVO6dE.js → useAppGlobalState-EnWKWmxp.js} +1 -1
- package/dist/client/assets/{useKannaState--bxEVaQ6.js → useKannaState-DsRFMjtu.js} +1 -1
- package/dist/client/assets/{useTheme-CNl_KG2I.js → useTheme-CD4e9oIU.js} +1 -1
- package/dist/client/index.html +33 -33
- package/package.json +1 -1
- package/src/shared/beacon-conformance.test.ts +65 -0
- package/dist/client/assets/SettingsPage-BqVsGGl_.js +0 -9
- package/dist/client/assets/chevron-left-BlkA174r.js +0 -1
- package/dist/client/assets/editor-icons-_AZa9Fmm.js +0 -1
- package/dist/client/assets/runDetached-DygAwgeI.js +0 -1
package/README.md
CHANGED
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
<h1 align="center">Kanna</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>A
|
|
9
|
-
</p>
|
|
10
|
-
|
|
11
|
-
<p align="center">
|
|
12
|
-
<em>OAuth token pooling, multi-provider chat (Claude + Codex + OpenRouter), subagent orchestration, custom MCP servers, a workflow status panel, durable tool-approval protocol, in-app self-update, and more.</em>
|
|
8
|
+
<strong>A web UI for the Claude Code and Codex agents, built for long sessions across many projects.</strong>
|
|
13
9
|
</p>
|
|
14
10
|
|
|
15
11
|
<p align="center">
|
|
@@ -20,7 +16,9 @@
|
|
|
20
16
|
</p>
|
|
21
17
|
|
|
22
18
|
<p align="center">
|
|
23
|
-
|
|
19
|
+
<a href="https://kanna-wiki.lowbit.link"><strong>Documentation</strong></a> ·
|
|
20
|
+
<a href="https://kanna-wiki.lowbit.link/getting-started/install/">Install guide</a> ·
|
|
21
|
+
<a href="CHANGELOG.md">Changelog</a>
|
|
24
22
|
</p>
|
|
25
23
|
|
|
26
24
|
<br />
|
|
@@ -33,530 +31,122 @@
|
|
|
33
31
|
</picture>
|
|
34
32
|
</p>
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## About Kanna
|
|
34
|
+
## What Kanna is
|
|
39
35
|
|
|
40
|
-
Kanna
|
|
41
|
-
|
|
42
|
-
**Headline features:**
|
|
43
|
-
|
|
44
|
-
- **OAuth token pool** — register multiple Claude OAuth tokens; Kanna rotates across them per chat with automatic fallover on rate-limit and an explicit disabled state.
|
|
45
|
-
- **Multi-provider chat** — switch between Claude, Codex (OpenAI), and OpenRouter from the composer with per-provider model + reasoning-effort controls and Codex fast mode. OpenRouter populates its model picker live from the public catalog (tool-capable models).
|
|
46
|
-
- **Subagent orchestration** — first-class subagent CRUD, `@agent/` mentions, parallel runs, live activity labels, MCP progress notifications, and `mcp__kanna__delegate_subagent` so the main agent itself can delegate — including **keep-alive multi-turn** sessions (`send_subagent_message` / `close_subagent`) and **background** runs that report back as a fresh turn.
|
|
47
|
-
- **Custom MCP servers** — register your own `stdio` / `http` / `sse` / `ws` MCP servers from Settings (with OAuth 2.1 for network transports); they merge into every Claude session and their tools surface as `mcp__<name>__<tool>`.
|
|
48
|
-
- **Workflow status panel** — read-only per-chat panel surfacing Claude Code's native Workflow tool runs (live status, drill-in progress, token totals) via disk-watch.
|
|
49
|
-
- **Agent self-scheduled wake** — `mcp__kanna__schedule_wakeup` lets the agent re-enter the chat to harvest long-running background work, with a runaway-loop cap.
|
|
50
|
-
- **Durable tool-approval protocol** (`KANNA_MCP_TOOL_CALLBACKS=1`) — pending `AskUserQuestion` / `ExitPlanMode` / built-in shims survive server restart and replay to the client on reconnect.
|
|
51
|
-
- **Cloudflare `expose_port` MCP tool** — agent-callable port exposure with always-ask or auto-expose modes, replacing bash-output sniffing.
|
|
52
|
-
- **In-app self-update** — one-click pull/rebuild/reload with a host-agnostic supervisor (works under pm2, systemd, docker, plain shell) or direct pm2 reload; install any prior release straight from the changelog UI.
|
|
53
|
-
- **Git worktree isolation** per chat, **bulk import** of existing `~/.claude/projects/` sessions, **proactive context compaction**, **per-turn token cost**, **local skills & slash commands** in the composer `/` picker, **password gate** for HTTP/WS/API, **PWA / mobile layout**, **mermaid rendering** in transcripts, **standalone HTML transcript export**, and **customizable keybindings**.
|
|
54
|
-
|
|
55
|
-
See the full inventory in [Features](#features) below.
|
|
36
|
+
Kanna runs on your machine and puts a browser UI in front of the coding agents you already use: the Claude Agent SDK, the Codex app-server, and OpenRouter models. Every chat belongs to a project, every event is saved to disk, and the UI works on a desktop, a phone, or through a tunnel.
|
|
56
37
|
|
|
57
38
|
## Quickstart
|
|
58
39
|
|
|
59
40
|
```bash
|
|
41
|
+
curl -fsSL https://bun.sh/install | bash # if you don't have Bun 1.3.11+
|
|
60
42
|
bun install -g @cuongtran001/kanna
|
|
43
|
+
kanna # opens http://localhost:3210
|
|
61
44
|
```
|
|
62
45
|
|
|
63
|
-
|
|
46
|
+
Add a Claude OAuth token, a Codex login, or an OpenRouter key in **Settings → Providers**, open a project folder, and start a chat. The [install guide](https://kanna-wiki.lowbit.link/getting-started/install/) covers requirements and flags.
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
curl -fsSL https://bun.sh/install | bash
|
|
67
|
-
```
|
|
48
|
+
## How it fits together
|
|
68
49
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
kanna
|
|
73
|
-
|
|
50
|
+
<p align="center">
|
|
51
|
+
<picture>
|
|
52
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/kanna-architecture-dark.svg" />
|
|
53
|
+
<img src="assets/diagrams/kanna-architecture.svg" alt="Architecture diagram: the Kanna web app talks to a Bun server over WebSocket; its agent coordinator records every event, runs turns on the Claude Agent SDK or the Codex app-server, and those agents edit your project worktree and call your MCP servers and Kanna's own tools." width="900" />
|
|
54
|
+
</picture>
|
|
55
|
+
</p>
|
|
74
56
|
|
|
75
|
-
|
|
57
|
+
One Bun process owns everything. The browser sends commands over a WebSocket, the agent coordinator runs each turn on a provider, and every change lands in an append-only event log under `~/.kanna/data`. Refresh the page or restart the server and the chat comes back exactly as it was.
|
|
76
58
|
|
|
77
59
|
## Features
|
|
78
60
|
|
|
79
|
-
**
|
|
80
|
-
|
|
81
|
-
- **Multi-provider support** — switch between Claude, Codex (OpenAI), and OpenRouter from the chat input, with per-provider model selection, reasoning-effort controls, and Codex fast mode
|
|
82
|
-
- **OpenRouter** — set an OpenRouter API key in Settings; the model picker populates live from OpenRouter's catalog (tool-capable models), routed through its Anthropic-compatible endpoint
|
|
83
|
-
- **OAuth token pool** — register multiple Claude OAuth tokens; Kanna rotates across them per chat
|
|
84
|
-
- **Custom MCP servers** — register `stdio` / `http` / `sse` / `ws` MCP servers from Settings (OAuth 2.1 for network transports); merged into every Claude session
|
|
85
|
-
|
|
86
|
-
**Chat & transcript**
|
|
87
|
-
|
|
88
|
-
- **Rich transcript rendering** — hydrated tool calls, collapsible tool groups, plan-mode dialogs, and interactive prompts with full result display
|
|
89
|
-
- **Inline diff viewer** — file and commit diffs rendered directly in the transcript
|
|
90
|
-
- **Embedded terminal** — per-project xterm terminal in a resizable side panel (macOS/Linux)
|
|
91
|
-
- **File & image uploads** — drag-and-drop attachments into the composer
|
|
92
|
-
- **Slash commands & @-mentions** — in-composer pickers for slash commands (including local Claude Code skills/commands), file mentions, and subagents
|
|
93
|
-
- **Plan mode** — review and approve agent plans before execution
|
|
94
|
-
- **Subagent orchestration** — run and track parallel subagents within a turn, plus keep-alive multi-turn sessions and non-blocking background runs that report back as a fresh turn
|
|
95
|
-
- **Workflow status panel** — read-only per-chat view of Claude Code's native Workflow tool runs with live status and drill-in progress
|
|
96
|
-
- **Agent self-scheduled wake** — the agent can re-enter the chat to harvest long-running background work (`schedule_wakeup`), with a runaway-loop cap
|
|
97
|
-
- **Background tasks** — long-running tasks tracked out-of-band with a status indicator
|
|
98
|
-
- **Per-turn token cost** — token usage and estimated USD cost shown inline per turn
|
|
99
|
-
- **Auto-continue** — optionally continue a turn automatically when the agent stops short
|
|
100
|
-
- **Proactive compaction** — context-window meter with automatic transcript compaction before limits are hit
|
|
101
|
-
|
|
102
|
-
**Projects & sessions**
|
|
103
|
-
|
|
104
|
-
- **Project-first sidebar** — chats grouped under projects, with live status indicators (idle, running, waiting, failed)
|
|
105
|
-
- **Drag-and-drop project ordering** — reorder project groups in the sidebar with persistent ordering
|
|
106
|
-
- **Local project discovery** — auto-discovers projects from both Claude and Codex local history
|
|
107
|
-
- **Bulk import Claude Code sessions** — one-click import of existing `~/.claude/projects/` sessions with full transcript and seamless resume via the Claude Agent SDK
|
|
108
|
-
- **Git worktree isolation** — run a chat in an isolated worktree without disturbing your working tree
|
|
109
|
-
- **Session resumption** — resume agent sessions with full context preservation
|
|
110
|
-
- **Auto-generated titles** — chat titles generated in the background via Claude Haiku
|
|
111
|
-
- **Quick responses** — lightweight structured queries (e.g. title generation) via Haiku with automatic Codex fallback
|
|
112
|
-
|
|
113
|
-
**Persistence & realtime**
|
|
114
|
-
|
|
115
|
-
- **Persistent local history** — refresh-safe routes backed by append-only JSONL event logs and compacted snapshots
|
|
116
|
-
- **WebSocket-driven** — real-time subscription model with reactive state broadcasting
|
|
117
|
-
- **Standalone transcript export** — export a chat as a self-contained HTML viewer
|
|
118
|
-
|
|
119
|
-
**Access & notifications**
|
|
120
|
-
|
|
121
|
-
- **Password protection** — optional launch password gating the app, WebSocket, and API routes
|
|
122
|
-
- **Public share link** — `--share` creates a temporary `trycloudflare.com` URL with a terminal QR code
|
|
123
|
-
- **Cloudflare tunnel via `expose_port` tool** — opt-in; the agent proactively calls the Kanna `expose_port` MCP tool with a port. In `always-ask` mode Kanna shows an inline "expose via Cloudflare" card for you to accept; in `auto-expose` mode `cloudflared tunnel --url` spawns immediately. Both modes are gated by the Cloudflare Tunnel setting
|
|
124
|
-
- **Web push & sound notifications** — browser push and sound alerts when a chat needs attention
|
|
125
|
-
- **Customizable keybindings** — user-editable keyboard shortcuts
|
|
126
|
-
- **In-app self-update** — one-click update that pulls, rebuilds, and hot-reloads (host-agnostic supervisor or pm2)
|
|
127
|
-
- **Mobile-friendly** — responsive layout, installable as a standalone PWA
|
|
128
|
-
|
|
129
|
-
## Architecture
|
|
130
|
-
|
|
131
|
-
```mermaid
|
|
132
|
-
flowchart LR
|
|
133
|
-
Browser["Browser<br/>React + Zustand"]
|
|
134
|
-
|
|
135
|
-
subgraph Server["Bun Server (src/server/**)"]
|
|
136
|
-
direction TB
|
|
137
|
-
WS["WSRouter<br/>subscriptions + commands"]
|
|
138
|
-
Auth["Auth gate"]
|
|
139
|
-
Agent["AgentCoordinator<br/>multi-provider turns"]
|
|
140
|
-
ES["EventStore<br/>append-only JSONL + snapshots"]
|
|
141
|
-
RM["ReadModels<br/>derived views"]
|
|
142
|
-
Diff["DiffStore"]
|
|
143
|
-
Term["TerminalManager"]
|
|
144
|
-
Up["Uploads"]
|
|
145
|
-
Disc["Discovery"]
|
|
146
|
-
Push["Push"]
|
|
147
|
-
Tun["Share / Tunnel"]
|
|
148
|
-
Upd["UpdateManager"]
|
|
149
|
-
|
|
150
|
-
subgraph Adapters["*.adapter.ts (IO seal exempt)"]
|
|
151
|
-
direction LR
|
|
152
|
-
FsA["fs / chokidar"]
|
|
153
|
-
DbA["bun:sqlite / pg"]
|
|
154
|
-
SpA["Bun.spawn / child_process"]
|
|
155
|
-
HtA["node:http / fetch"]
|
|
156
|
-
PtyA["Bun.Terminal (PTY)"]
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
WS --> Agent
|
|
160
|
-
WS --> ES
|
|
161
|
-
WS --> RM
|
|
162
|
-
Agent --> ES
|
|
163
|
-
Agent -.spawn.-> SpA
|
|
164
|
-
ES -.fs.-> FsA
|
|
165
|
-
Diff -.fs+spawn.-> SpA
|
|
166
|
-
Diff -.fs.-> FsA
|
|
167
|
-
Term -.pty.-> PtyA
|
|
168
|
-
Up -.fs.-> FsA
|
|
169
|
-
Disc -.fs.-> FsA
|
|
170
|
-
Tun -.spawn+http.-> SpA
|
|
171
|
-
Tun -.http.-> HtA
|
|
172
|
-
Upd -.spawn.-> SpA
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
subgraph Shared["src/shared/** (pure)"]
|
|
176
|
-
Proto["protocol types"]
|
|
177
|
-
Types["domain types"]
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
subgraph External["External processes"]
|
|
181
|
-
CC["Claude Agent SDK / claude CLI"]
|
|
182
|
-
CX["Codex App Server"]
|
|
183
|
-
FS["Local FS<br/>~/.kanna/data/, project dirs"]
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
Browser <-->|WebSocket| WS
|
|
187
|
-
Browser -.types.-> Shared
|
|
188
|
-
Server -.types.-> Shared
|
|
189
|
-
|
|
190
|
-
SpA --> CC
|
|
191
|
-
SpA --> CX
|
|
192
|
-
PtyA --> CC
|
|
193
|
-
FsA --> FS
|
|
194
|
-
```
|
|
61
|
+
**Chat with any agent**
|
|
195
62
|
|
|
196
|
-
**
|
|
197
|
-
|
|
198
|
-
-
|
|
199
|
-
-
|
|
200
|
-
-
|
|
201
|
-
|
|
202
|
-
**Key patterns:** Event sourcing for all state mutations. CQRS with separate write (event log) and read (derived snapshots) paths. Reactive broadcasting — subscribers get pushed fresh snapshots on every state change. Multi-provider agent coordination with tool gating for user-approval flows. Provider-agnostic transcript hydration for unified rendering.
|
|
203
|
-
|
|
204
|
-
### Workflow: adding code that touches IO
|
|
205
|
-
|
|
206
|
-
```mermaid
|
|
207
|
-
flowchart TD
|
|
208
|
-
Start(["You need fs / spawn / http / DB / Bun globals"]) --> Layer{"Which layer?"}
|
|
209
|
-
Layer -->|src/shared or src/client| Reject["ESLint errors at CI"]
|
|
210
|
-
Reject --> Move["Move the module to src/server/**<br/>or inject through a typed parameter"]
|
|
211
|
-
Move --> Server
|
|
212
|
-
Layer -->|src/server| Server{"File responsibility?"}
|
|
213
|
-
Server -->|leaf IO wrapper| RenameAdapter["Name it foo.adapter.ts<br/>(exempt from seal)"]
|
|
214
|
-
Server -->|mixed domain + IO| SiblingAdapter["Extract calls into foo-io.adapter.ts<br/>keep domain logic in foo.ts<br/>import helpers from the adapter"]
|
|
215
|
-
Server -->|domain only| Port["Take a typed port parameter<br/>provided by caller's adapter"]
|
|
216
|
-
RenameAdapter --> Lint["bun run lint"]
|
|
217
|
-
SiblingAdapter --> Lint
|
|
218
|
-
Port --> Lint
|
|
219
|
-
Lint --> CI(["CI: lint + tests + build"])
|
|
220
|
-
```
|
|
63
|
+
- **Claude, Codex and OpenRouter** in one composer, with per-chat model, reasoning effort and context-window controls. [Providers & models](https://kanna-wiki.lowbit.link/features/providers-models/)
|
|
64
|
+
- **OAuth token pool**: register several Claude tokens and Kanna rotates between them, failing over when one hits a rate limit. [OAuth pool](https://kanna-wiki.lowbit.link/getting-started/oauth-pool-setup/)
|
|
65
|
+
- **Live output**: on Claude chats, thinking, text and tool input stream in as the model writes them.
|
|
66
|
+
- **Rich transcript**: grouped tool calls, inline diffs, plan-mode approval, mermaid diagrams checked before you see them, and interactive charts and reports the agent composes ([generative UI](https://kanna-wiki.lowbit.link/features/generative-ui/)).
|
|
67
|
+
- **Slash commands everywhere**: `/clear`, `/compact`, `/cron`, plus your local Claude Code skills, on every provider. [Slash commands](https://kanna-wiki.lowbit.link/features/slash-commands/)
|
|
221
68
|
|
|
222
|
-
|
|
69
|
+
**Let it work while you're away**
|
|
223
70
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
|
|
71
|
+
<p align="center">
|
|
72
|
+
<picture>
|
|
73
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/kanna-turn-queue-dark.svg" />
|
|
74
|
+
<img src="assets/diagrams/kanna-turn-queue.svg" alt="Fan-in diagram: a typed message, a cron job, a loop wake, a background subagent and a board card all enter one durable per-chat queue, which starts a turn only when the chat is idle and writes its output to the transcript." width="900" />
|
|
75
|
+
</picture>
|
|
76
|
+
</p>
|
|
229
77
|
|
|
230
|
-
|
|
78
|
+
- **Subagents**: named agents with their own prompts and tools. The main agent delegates, runs them in parallel or in the background, and gets the result back as a new turn. [Subagents](https://kanna-wiki.lowbit.link/guides/user/subagents/)
|
|
79
|
+
- **Autonomous loops**: give a goal and a verify command; Kanna works through a durable task list until the check passes and stops on its own. [Loops](https://kanna-wiki.lowbit.link/features/loops/)
|
|
80
|
+
- **Cron jobs**: `/cron` schedules a recurring instruction, down to the second, in the same chat or a fresh one. [Cron jobs](https://kanna-wiki.lowbit.link/features/cron-jobs/)
|
|
81
|
+
- **Durable queue**: messages, wakes and scheduled runs wait in a per-chat queue that survives a server restart.
|
|
82
|
+
- **Background tasks and workflows**: dev servers, long builds and Claude Code workflows show live status, and Kanna keeps their session alive while they run.
|
|
231
83
|
|
|
232
|
-
|
|
84
|
+
**Organize the work**
|
|
233
85
|
|
|
234
|
-
|
|
86
|
+
<p align="center">
|
|
87
|
+
<picture>
|
|
88
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/kanna-board-card-dark.svg" />
|
|
89
|
+
<img src="assets/diagrams/kanna-board-card.svg" alt="State diagram of a board card: Kanna moves it to In progress on Start work and creates its worktree and chat, the agent advances it one column when the work is verified, and only you move it to Done, which asks whether to merge, discard or keep the worktree." width="900" />
|
|
90
|
+
</picture>
|
|
91
|
+
</p>
|
|
235
92
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
93
|
+
- **Projects and session tabs**: chats grouped by project with live status, a split-pane workspace, a project quick switcher, and one-click import of existing Claude Code sessions. [Projects & sessions](https://kanna-wiki.lowbit.link/features/projects-sessions/)
|
|
94
|
+
- **Kanban boards**: *Start work* on a card creates a branch, a git worktree and a chat, so several agents can work at once without touching each other's files. Boards sync with GitHub issues. [Boards](https://kanna-wiki.lowbit.link/features/boards/)
|
|
95
|
+
- **Multi-repo stacks**: group several repositories so one chat can read and edit across all of them. [Stacks](https://kanna-wiki.lowbit.link/features/multi-repo-stacks/)
|
|
239
96
|
|
|
240
|
-
|
|
97
|
+
**Extend and connect**
|
|
241
98
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
99
|
+
- **Your MCP servers** (`stdio`, `http`, `sse`, `ws`, with OAuth 2.1) are available in every chat. [Advanced](https://kanna-wiki.lowbit.link/features/advanced/)
|
|
100
|
+
- **Kanna plugins** add sidebar pages, panels and `/` commands, with a server half running in its own process. [Plugins](https://kanna-wiki.lowbit.link/features/plugins/)
|
|
101
|
+
- **Beacons** pair another machine you own, so the agent can read files or run commands there within limits you set. [Beacons](https://kanna-wiki.lowbit.link/guides/user/beacons/)
|
|
102
|
+
- **Package updates** for installed skills, Claude Code plugins and Codex plugins, checked and applied from Settings. [Package auto-update](https://kanna-wiki.lowbit.link/features/package-auto-update/)
|
|
245
103
|
|
|
246
|
-
|
|
104
|
+
**Run it anywhere**
|
|
247
105
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
bun run build
|
|
253
|
-
```
|
|
106
|
+
- **Password gate**, LAN or Tailscale binding, and Cloudflare tunnels (`--share`, `--cloudflared`), plus an agent tool that exposes a dev server on request. [Self-host](https://kanna-wiki.lowbit.link/guides/ops/self-host/)
|
|
107
|
+
- **Read-only share links** that render a chat exactly as you see it. [Session share](https://kanna-wiki.lowbit.link/sharing/session-share/)
|
|
108
|
+
- **Resumable uploads** for multi-gigabyte files, even behind a tunnel's request-size limit.
|
|
109
|
+
- **Web push, PWA install, phone layout**, customizable keybindings, and **in-app self-update** under pm2, systemd, Docker or a plain shell. [Self-update](https://kanna-wiki.lowbit.link/guides/ops/self-update/)
|
|
254
110
|
|
|
255
111
|
## Usage
|
|
256
112
|
|
|
257
113
|
```bash
|
|
258
|
-
kanna
|
|
259
|
-
kanna --port 4000
|
|
260
|
-
kanna --
|
|
261
|
-
kanna --
|
|
262
|
-
kanna --
|
|
263
|
-
kanna --
|
|
264
|
-
kanna
|
|
114
|
+
kanna # localhost only, opens a browser
|
|
115
|
+
kanna --port 4000 # custom port
|
|
116
|
+
kanna --remote # bind 0.0.0.0 (LAN, Tailscale)
|
|
117
|
+
kanna --password <secret> # require a password for the app, WebSocket and API
|
|
118
|
+
kanna --share # temporary public trycloudflare.com URL + terminal QR
|
|
119
|
+
kanna --cloudflared <token> # run a named Cloudflare tunnel
|
|
120
|
+
kanna plugin ls # manage Kanna plugins
|
|
265
121
|
```
|
|
266
122
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
### Network access (Tailscale / LAN)
|
|
270
|
-
|
|
271
|
-
By default Kanna binds to `127.0.0.1` (localhost only). Use `--host` to bind a specific interface, or `--remote` as a shorthand for `0.0.0.0`:
|
|
272
|
-
|
|
273
|
-
```bash
|
|
274
|
-
kanna --remote # bind all interfaces — browser opens localhost:3210
|
|
275
|
-
kanna --host dev-box # bind to a specific hostname — browser opens http://dev-box:3210
|
|
276
|
-
kanna --host 192.168.1.x # bind to a specific LAN IP
|
|
277
|
-
kanna --host 100.64.x.x # bind to a specific Tailscale IP
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
When `--host <hostname>` is given, the browser opens `http://<hostname>:3210` automatically. Other machines on your network can connect to the same URL:
|
|
281
|
-
|
|
282
|
-
### Password protection
|
|
283
|
-
|
|
284
|
-
Use `--password` to require a launch password before the app or websocket can connect:
|
|
285
|
-
|
|
286
|
-
```bash
|
|
287
|
-
kanna --password my-secret
|
|
288
|
-
bun run dev --password my-secret
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
Kanna verifies the password once, then sets a browser-session cookie. The password itself is not stored in the browser.
|
|
292
|
-
When password protection is enabled, the backend requires authentication for API routes and `/ws`. The SPA shell still loads, `/health` remains public for restart detection, and the same in-app password screen is used in both dev and production.
|
|
293
|
-
|
|
294
|
-
### Public share link
|
|
295
|
-
|
|
296
|
-
Use `--share` to create a temporary public `trycloudflare.com` URL and print a terminal QR code:
|
|
297
|
-
|
|
298
|
-
```bash
|
|
299
|
-
kanna --share
|
|
300
|
-
kanna --share --port 4000
|
|
301
|
-
kanna --cloudflared <token>
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
`--share` is incompatible with `--host` and `--remote`. It does not open a browser automatically.
|
|
305
|
-
|
|
306
|
-
Without a token, it prints:
|
|
307
|
-
|
|
308
|
-
```text
|
|
309
|
-
QR Code:
|
|
310
|
-
...
|
|
311
|
-
|
|
312
|
-
Public URL:
|
|
313
|
-
https://<random>.trycloudflare.com
|
|
314
|
-
|
|
315
|
-
Local URL:
|
|
316
|
-
http://localhost:3210
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
With `--cloudflared <token>`, Kanna runs `cloudflared tunnel run --token <token> --url <local-url>`.
|
|
320
|
-
If Kanna can detect the public hostname from cloudflared output, it prints the same QR/public/local block.
|
|
321
|
-
If not, it keeps the tunnel running, warns that no public hostname was detected, and prints the local URL so you can use the hostname already configured for that tunnel in Cloudflare.
|
|
322
|
-
|
|
323
|
-
### Auto-expose detected ports
|
|
324
|
-
|
|
325
|
-
When the agent runs a Bash command in a chat (`bun run dev`, `go run`, `uvicorn`, etc.), Kanna can detect any listening port from the command's stdout and offer to expose it through a Cloudflare quick tunnel without leaving the chat.
|
|
326
|
-
|
|
327
|
-
Enable from **Settings → Cloudflare Tunnel**:
|
|
328
|
-
|
|
329
|
-
- **Toggle** — opt-in (off by default)
|
|
330
|
-
- **Mode** — `Always ask` (one card per detected port; click Expose to spawn) or `Auto-expose` (spawn immediately on detection)
|
|
331
|
-
- **`cloudflared` path** — defaults to `cloudflared` on `$PATH`
|
|
332
|
-
|
|
333
|
-
Each detected port shows up inline in the transcript. Click **Expose**, watch the spinner until cloudflared returns the `*.trycloudflare.com` URL, then click **Stop** when done. Tunnels are also stopped automatically when the chat closes or the server restarts.
|
|
334
|
-
|
|
335
|
-
Requires the `cloudflared` binary installed locally — `brew install cloudflared` on macOS, or see [Cloudflare's downloads](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/).
|
|
123
|
+
`kanna --help` lists every flag. State lives in `~/.kanna`; back it up to keep your chat history. Deployment recipes for [pm2](https://kanna-wiki.lowbit.link/guides/ops/pm2/), [systemd](https://kanna-wiki.lowbit.link/guides/ops/systemd/), [Docker](https://kanna-wiki.lowbit.link/guides/ops/docker/) and a [Cloudflare tunnel](https://kanna-wiki.lowbit.link/guides/ops/cloudflare-tunnel/) are in the wiki.
|
|
336
124
|
|
|
337
125
|
## Development
|
|
338
126
|
|
|
339
127
|
```bash
|
|
128
|
+
git clone https://github.com/cuongtranba/kanna.git && cd kanna
|
|
340
129
|
bun install
|
|
341
|
-
bun run setup:hooks
|
|
342
|
-
bun run dev
|
|
130
|
+
bun run setup:hooks # gitleaks + commit-message hooks, once per clone
|
|
131
|
+
bun run dev # Vite client on :5174, server on :5175
|
|
132
|
+
bun run check # typecheck, lint, build
|
|
133
|
+
bun run test # never bare `bun test`
|
|
343
134
|
```
|
|
344
135
|
|
|
345
|
-
|
|
346
|
-
`--share` is also supported in dev mode and exposes the Vite client URL publicly:
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
bun run dev --share
|
|
350
|
-
bun run dev --cloudflared <token>
|
|
351
|
-
bun run dev --port 3333 --share
|
|
352
|
-
```
|
|
136
|
+
Start with [`CLAUDE.md`](CLAUDE.md) and the [contributing guide](https://kanna-wiki.lowbit.link/guides/contributing/overview/). The server is event-sourced, IO is sealed behind `*.adapter.ts` files, and the lint gates enforce both; the [architecture page](https://kanna-wiki.lowbit.link/guides/contributing/architecture/) explains the shape. Releases are cut by release-please; see [Releasing](https://kanna-wiki.lowbit.link/guides/contributing/releasing/).
|
|
353
137
|
|
|
354
|
-
|
|
355
|
-
`--share` remains incompatible with `--host` and `--remote`.
|
|
356
|
-
Use `bun run dev --port 4000` to run the Vite client on `4000` and the backend on `4001`.
|
|
138
|
+
## Documentation website
|
|
357
139
|
|
|
358
|
-
|
|
140
|
+
The docs at [kanna-wiki.lowbit.link](https://kanna-wiki.lowbit.link) are built from [`wiki/`](wiki/) in this repo: an Astro Starlight site with its own `package.json`, kept apart from the root build, lint and tests. Every push to `main` that touches `wiki/**` rebuilds it and deploys it to GitHub Pages.
|
|
359
141
|
|
|
360
142
|
```bash
|
|
361
|
-
|
|
362
|
-
bun run dev:server # http://localhost:5175
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
## Scripts
|
|
366
|
-
|
|
367
|
-
| Command | Description |
|
|
368
|
-
| -------------------- | ------------------------------------ |
|
|
369
|
-
| `bun run build` | Build client + standalone export viewer |
|
|
370
|
-
| `bun run check` | Typecheck, lint, and build |
|
|
371
|
-
| `bun run lint` | ESLint over `src/` (zero-warning gate) |
|
|
372
|
-
| `bun run dev` | Run client + server together |
|
|
373
|
-
| `bun run dev:client` | Vite dev server only (`:5174`) |
|
|
374
|
-
| `bun run dev:server` | Bun backend only (`:5175`) |
|
|
375
|
-
| `bun run start` | Start production server |
|
|
376
|
-
| `bun test` | Run the test suite |
|
|
377
|
-
| `bun run setup:hooks` | Wire the gitleaks pre-commit hook (once per clone/worktree) |
|
|
378
|
-
| `bun run scan:secrets` | Full working-tree secret scan on demand |
|
|
379
|
-
|
|
380
|
-
## Project Structure
|
|
381
|
-
|
|
382
|
-
Abridged — the actual tree has more modules, each with co-located `*.test.ts`:
|
|
383
|
-
|
|
384
|
-
```
|
|
385
|
-
src/
|
|
386
|
-
├── client/ React UI layer
|
|
387
|
-
│ ├── app/ App router, pages, central state hook, socket client
|
|
388
|
-
│ ├── components/ chat-ui, messages, settings, ui primitives, modals
|
|
389
|
-
│ ├── hooks/ mobile/standalone detection, theme, mention/slash suggestions
|
|
390
|
-
│ ├── stores/ Zustand stores (chat input, preferences, terminal, tasks…)
|
|
391
|
-
│ └── lib/ formatters, path utils, transcript parsing, keybindings
|
|
392
|
-
├── server/ Bun backend
|
|
393
|
-
│ ├── cli.ts · cli-runtime.ts CLI entry, flag parsing, supervisor
|
|
394
|
-
│ ├── server.ts HTTP/WS server + static serving
|
|
395
|
-
│ ├── auth.ts password gate for HTTP/WS/API
|
|
396
|
-
│ ├── ws-router.ts WebSocket routing & subscriptions
|
|
397
|
-
│ ├── agent.ts AgentCoordinator (multi-provider turns)
|
|
398
|
-
│ ├── codex-app-server.ts Codex App Server JSON-RPC client
|
|
399
|
-
│ ├── oauth-pool/ Claude OAuth token rotation
|
|
400
|
-
│ ├── provider-catalog.ts provider/model/effort normalization
|
|
401
|
-
│ ├── openrouter-models.ts live OpenRouter catalog (tool-capable models)
|
|
402
|
-
│ ├── quick-response.ts structured queries w/ provider fallback
|
|
403
|
-
│ ├── event-store.ts JSONL persistence, replay & compaction
|
|
404
|
-
│ ├── read-models.ts derived view models
|
|
405
|
-
│ ├── events.ts event type definitions
|
|
406
|
-
│ ├── discovery.ts auto-discover Claude/Codex projects
|
|
407
|
-
│ ├── local-catalog.ts local Claude skills/slash-command discovery
|
|
408
|
-
│ ├── claude-session-importer.ts bulk import existing sessions
|
|
409
|
-
│ ├── diff-store.ts per-chat diff hydration
|
|
410
|
-
│ ├── terminal-manager.ts embedded-terminal PTY sessions
|
|
411
|
-
│ ├── uploads.ts attachment intake
|
|
412
|
-
│ ├── subagent-orchestrator.ts parallel + keep-alive + background subagent runs
|
|
413
|
-
│ ├── workflow-registry.ts workflow status panel (disk-watch read-model)
|
|
414
|
-
│ ├── background-tasks.ts out-of-band task tracking
|
|
415
|
-
│ ├── worktree-store.ts git worktree isolation
|
|
416
|
-
│ ├── push/ web-push notifications
|
|
417
|
-
│ ├── share.ts · cloudflare-tunnel/ trycloudflare / expose_port tunnels
|
|
418
|
-
│ ├── update-manager.ts · update-strategy.ts self-update
|
|
419
|
-
│ ├── kanna-mcp.ts Kanna MCP tools (built-in shims)
|
|
420
|
-
│ ├── mcp-validator.ts · mcp-oauth.adapter.ts custom MCP connect-test + OAuth
|
|
421
|
-
│ └── keybindings.ts persisted keybindings
|
|
422
|
-
└── shared/ Shared between client & server
|
|
423
|
-
├── types.ts core domain types, provider catalog, transcript entries
|
|
424
|
-
├── tools.ts tool-call normalization & hydration
|
|
425
|
-
├── protocol.ts WebSocket wire envelopes
|
|
426
|
-
├── ports.ts default ports & dev-mode offsets
|
|
427
|
-
├── share.ts share/tunnel shared types
|
|
428
|
-
├── token-pricing.ts per-turn token cost (USD)
|
|
429
|
-
└── branding.ts app name & data-directory paths
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
## Data Storage
|
|
433
|
-
|
|
434
|
-
All state is stored locally at `~/.kanna/data/`:
|
|
435
|
-
|
|
436
|
-
| File | Purpose |
|
|
437
|
-
| ---------------- | ----------------------------------------- |
|
|
438
|
-
| `projects.jsonl` | Project open/remove events |
|
|
439
|
-
| `chats.jsonl` | Chat create/rename/delete events |
|
|
440
|
-
| `messages.jsonl` | Transcript message entries |
|
|
441
|
-
| `turns.jsonl` | Agent turn start/finish/cancel events |
|
|
442
|
-
| `snapshot.json` | Compacted state snapshot for fast startup |
|
|
443
|
-
|
|
444
|
-
Event logs are append-only JSONL. On startup, Kanna replays the log tail after the last snapshot, then compacts if the logs exceed 2 MB.
|
|
445
|
-
|
|
446
|
-
## Self-hosting on macOS (pm2 + Cloudflare tunnel)
|
|
447
|
-
|
|
448
|
-
Run Kanna as a background service on macOS under [pm2](https://pm2.keymetrics.io/), exposed through a named Cloudflare tunnel. The in-app **Update** button then pulls the latest commit, rebuilds, and hot-reloads the pm2 process — no terminal round-trip needed.
|
|
449
|
-
|
|
450
|
-
### 1. Link the repo as the global install
|
|
451
|
-
|
|
452
|
-
`bun link` makes the global `kanna` binary resolve to your checkout:
|
|
453
|
-
|
|
454
|
-
```bash
|
|
455
|
-
cd ~/path/to/kanna
|
|
143
|
+
cd wiki
|
|
456
144
|
bun install
|
|
457
|
-
bun run
|
|
458
|
-
bun
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
After this, `~/.bun/install/global/node_modules/@cuongtran001/kanna` is a symlink to your repo.
|
|
462
|
-
|
|
463
|
-
### 2. Create a named Cloudflare tunnel
|
|
464
|
-
|
|
465
|
-
In the [Cloudflare Zero Trust dashboard](https://one.dash.cloudflare.com/) → **Networks → Tunnels → Create tunnel** (type: **Cloudflared**):
|
|
466
|
-
|
|
467
|
-
1. Name the tunnel (e.g. `kanna`) and copy the **connector token** Cloudflare shows you. You will paste it as `KANNA_CLOUDFLARED_TOKEN` in the next step.
|
|
468
|
-
2. Add a **public hostname** route: pick your subdomain (e.g. `kanna.example.com`) and point service to `HTTP` → `localhost:5174` (or whatever `--port` you plan to run). Kanna binds `127.0.0.1` automatically when `--cloudflared` is set, so the tunnel is the only ingress.
|
|
469
|
-
3. Save. The hostname's TLS is terminated at Cloudflare's edge.
|
|
470
|
-
|
|
471
|
-
### 3. Write `scripts/pm2.env` (untracked secrets)
|
|
472
|
-
|
|
473
|
-
`scripts/deploy.sh` reads this file and passes the values to kanna as `--cloudflared <TOKEN> --password <PW>`. Without it, deploy launches kanna with no token and no password — kanna will then run as plain HTTP on localhost, **`trustProxy` will not auto-enable**, and every `/auth/login` POST through the tunnel will return **403** because the CSRF origin check compares the browser's `https://` Origin against the server's `http://` `req.url`.
|
|
474
|
-
|
|
475
|
-
Create `scripts/pm2.env` (gitignored) with at least:
|
|
476
|
-
|
|
477
|
-
```env
|
|
478
|
-
KANNA_CLOUDFLARED_TOKEN=<paste the connector token from step 2>
|
|
479
|
-
KANNA_PASSWORD=<a long random password>
|
|
480
|
-
# Optional: pass through to spawned Claude Code agents
|
|
481
|
-
# CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
Generate a strong password with `openssl rand -base64 24`.
|
|
485
|
-
|
|
486
|
-
### 4. (Migrating from launchd) Unload the old agent
|
|
487
|
-
|
|
488
|
-
If you previously ran Kanna under launchd, unload it once so pm2 can take over:
|
|
489
|
-
|
|
490
|
-
```bash
|
|
491
|
-
launchctl bootout gui/$(id -u)/io.silentium.kanna || true
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
### 5. First deploy
|
|
495
|
-
|
|
496
|
-
`scripts/deploy.sh` installs pm2 if missing, renders `scripts/pm2.config.cjs` from the template (via `envsubst` from `brew install gettext`), and starts the pm2 process:
|
|
497
|
-
|
|
498
|
-
```bash
|
|
499
|
-
./scripts/deploy.sh
|
|
500
|
-
pm2 list # kanna should be "online"
|
|
501
|
-
pm2 logs kanna --lines 50
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
`pm2 save` persists the running process list. To resurrect after a reboot, run `pm2 startup` once (pm2 prints the exact command) and then `pm2 save` again.
|
|
505
|
-
|
|
506
|
-
The pm2 config sets `KANNA_RELOADER=pm2` and `KANNA_REPO_DIR=<repo>` so the in-app Update button triggers the pm2 reload pipeline (see next section). Override the pm2 process name with `KANNA_PM2_PROCESS_NAME` before running `./scripts/deploy.sh` if you need to run multiple instances.
|
|
507
|
-
|
|
508
|
-
### 6. Redeploy / update
|
|
509
|
-
|
|
510
|
-
Two ways to ship a new build:
|
|
511
|
-
|
|
512
|
-
**a. From the UI (fastest).** Click **Update** in the running app. The server runs `git pull --ff-only` → conditional `bun install` → `bun run build` → `pm2.reload` internally, and the UI reconnects to the fresh build. If any step fails, the UI shows a red banner with the stderr tail and the old build keeps serving.
|
|
513
|
-
|
|
514
|
-
**b. From the terminal.** Useful for non-Kanna deploys (e.g., pm2 config edits) or when the UI is unreachable:
|
|
515
|
-
|
|
516
|
-
```bash
|
|
517
|
-
git pull
|
|
518
|
-
./scripts/deploy.sh
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### 7. Troubleshooting: 403 on login
|
|
522
|
-
|
|
523
|
-
If the login screen rejects the correct password with **403** behind a Cloudflare (or any HTTPS-terminating) tunnel, the server is running without `trustProxy` enabled. The CSRF origin check then compares the browser's `https://kanna.example.com` `Origin` against the local `http://127.0.0.1:<port>` `req.url` and rejects them as mismatched. Two ways to enable it:
|
|
524
|
-
|
|
525
|
-
- **Recommended.** Pass `--cloudflared <TOKEN>` (or `--share`) on the kanna command line. Both flags auto-enable `trustProxy` and bind to `127.0.0.1`. With `scripts/pm2.env` populated, `scripts/deploy.sh` does this for you — verify with `pm2 logs kanna --lines 20` that the startup line includes `--cloudflared`.
|
|
526
|
-
- **Running cloudflared separately?** Use `--cloudflared` on kanna anyway and let kanna spawn the tunnel; the standalone `cloudflared` daemon does not set `trustProxy` for you. (There is no standalone `--trust-proxy` CLI flag today.)
|
|
527
|
-
|
|
528
|
-
Other things to check if the 403 persists:
|
|
529
|
-
|
|
530
|
-
- Cloudflare tunnel **public hostname** points to `http://localhost:<KANNA_PORT>`, not `https://` — kanna terminates plain HTTP locally.
|
|
531
|
-
- The public hostname's **TLS mode** is `Full` or `Flexible` (Cloudflare → Origin is HTTP), not `Full (strict)` against a self-signed origin.
|
|
532
|
-
- No `Access` policy in front of the hostname is stripping or rewriting the `Origin` header.
|
|
533
|
-
|
|
534
|
-
### 8. Update strategies
|
|
535
|
-
|
|
536
|
-
The update mechanism is abstracted behind `UpdateChecker` + `UpdateReloader` interfaces in `src/server/update-strategy.ts`, selected at startup by `KANNA_RELOADER`:
|
|
537
|
-
|
|
538
|
-
| `KANNA_RELOADER` | Check | Reload | Notes |
|
|
539
|
-
|---|---|---|---|
|
|
540
|
-
| unset / `supervisor` | npm registry for `@cuongtran001/kanna` | `<pm> install -g @cuongtran001/kanna@latest`, exit 76, supervisor respawns | Default. End-user path. `<pm>` auto-detected: `bun`/`npm`/`pnpm`/`yarn`. Override via `KANNA_UPDATE_COMMAND`. |
|
|
541
|
-
| `pm2` | `git fetch` + `HEAD` vs `origin/main` | `git pull --ff-only` → cond. `bun install` → `bun run build` → `pm2 reload` | Dev/self-host path. Requires `KANNA_REPO_DIR`. |
|
|
542
|
-
|
|
543
|
-
**Host-agnostic supervisor mode.** When `KANNA_RELOADER` is unset (default), the in-app Update button works under any process host (pm2, systemd, docker, screen, plain shell) — the internal supervisor catches the child's exit-76 and respawns. The package manager used to install the new version is auto-detected from the running binary path:
|
|
544
|
-
|
|
545
|
-
- `~/.bun/bin/kanna` → `bun install -g`
|
|
546
|
-
- `~/.local/share/pnpm/kanna` (or any `pnpm/` path) → `pnpm add -g`
|
|
547
|
-
- `~/.yarn/bin/kanna` (or any `.yarn/` path) → `yarn global add`
|
|
548
|
-
- anything else (e.g. `/usr/local/bin/kanna`, `~/.npm-global/bin/kanna`) → `npm install -g`
|
|
549
|
-
|
|
550
|
-
If the detected manager is not on `PATH`, kanna falls back through `bun → npm → pnpm → yarn`. To override the install command entirely — useful for custom installers, monorepo wrappers, docker pulls, ansible, etc. — set `KANNA_UPDATE_COMMAND`. Placeholders `{package}` and `{version}` are substituted; the result is executed via `sh -c`.
|
|
551
|
-
|
|
552
|
-
```bash
|
|
553
|
-
# Force npm regardless of detection
|
|
554
|
-
KANNA_UPDATE_COMMAND="npm install -g {package}@{version}" pm2 start kanna
|
|
555
|
-
# Custom: chain pre-install hook
|
|
556
|
-
KANNA_UPDATE_COMMAND="my-deploy-hook && npm install -g {package}@{version}" kanna
|
|
145
|
+
bun run dev # live preview of the docs site
|
|
146
|
+
bun run build # the same build CI deploys
|
|
557
147
|
```
|
|
558
148
|
|
|
559
|
-
|
|
149
|
+
Pages live in `wiki/src/content/docs/`. Change a page in the same PR as the behavior it describes.
|
|
560
150
|
|
|
561
151
|
## Star History
|
|
562
152
|
|
|
@@ -568,43 +158,6 @@ To add another reload mechanism (e.g., docker, systemd) at the strategy layer, i
|
|
|
568
158
|
</picture>
|
|
569
159
|
</a>
|
|
570
160
|
|
|
571
|
-
## Releasing
|
|
572
|
-
|
|
573
|
-
Releases are automated. Merging to `main` runs
|
|
574
|
-
[release-please](.github/workflows/release-please.yml), which opens a release PR;
|
|
575
|
-
merging that PR tags the version, creates the GitHub Release, and publishes
|
|
576
|
-
`@cuongtran001/kanna` to npm with a provenance attestation.
|
|
577
|
-
|
|
578
|
-
### Recovering a failed publish
|
|
579
|
-
|
|
580
|
-
If the `publish` job fails, the version is tagged on GitHub but missing from npm.
|
|
581
|
-
**Re-running the workflow will not fix it** — release-please reports
|
|
582
|
-
`release_created: false` once the release exists, so `publish` is skipped.
|
|
583
|
-
Publish the existing tag instead:
|
|
584
|
-
|
|
585
|
-
```bash
|
|
586
|
-
gh workflow run release-please.yml -f tag=v1.32.0
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
This rebuilds and republishes from the tag through CI, so the package keeps its
|
|
590
|
-
provenance signature. A local `npm publish` would not.
|
|
591
|
-
|
|
592
|
-
### npm token expiry
|
|
593
|
-
|
|
594
|
-
`NPM_TOKEN` is an npm granular access token, and npm caps those at **90 days**.
|
|
595
|
-
When it expires, `npm publish` fails with a misleading `E404 Not Found` rather
|
|
596
|
-
than an auth error — npm masks a permission failure as a missing package. Rotate
|
|
597
|
-
the token at [npmjs.com](https://www.npmjs.com/settings/cuongtran001/tokens)
|
|
598
|
-
(scope: all packages, permission: read/write), then:
|
|
599
|
-
|
|
600
|
-
```bash
|
|
601
|
-
gh secret set NPM_TOKEN
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
## Contributing
|
|
605
|
-
|
|
606
|
-
Contributions are welcome! Feel free to open PRs
|
|
607
|
-
|
|
608
161
|
## License
|
|
609
162
|
|
|
610
163
|
[MIT](LICENSE)
|