@tyvm/knowhow 0.0.109-dev.e88af1e → 0.0.110

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.
Files changed (82) hide show
  1. package/autodoc/README.md +324 -0
  2. package/autodoc/chat-guide.md +268 -365
  3. package/autodoc/cli-reference.md +399 -473
  4. package/autodoc/config-reference.md +431 -330
  5. package/autodoc/embeddings-guide.md +223 -322
  6. package/autodoc/generate-guide.md +261 -301
  7. package/autodoc/language-plugin-guide.md +221 -247
  8. package/autodoc/modules-guide.md +242 -215
  9. package/autodoc/plugins-guide.md +470 -469
  10. package/autodoc/quickstart-guide.md +67 -70
  11. package/autodoc/skills-guide.md +455 -339
  12. package/autodoc/worker-guide.md +301 -308
  13. package/package.json +1 -1
  14. package/src/agents/tools/list.ts +2 -2
  15. package/src/ai.ts +81 -37
  16. package/src/chat/CliChatService.ts +1 -1
  17. package/src/chat/modules/SystemModule.ts +2 -2
  18. package/src/clients/anthropic.ts +1 -1
  19. package/src/clients/index.ts +25 -6
  20. package/src/clients/openai.ts +8 -5
  21. package/src/clients/types.ts +29 -6
  22. package/src/clients/withRetry.ts +89 -0
  23. package/src/commands/agent.ts +30 -0
  24. package/src/commands/modules.ts +365 -30
  25. package/src/config.ts +1 -1
  26. package/src/hashes.ts +8 -9
  27. package/src/index.ts +4 -2
  28. package/src/processors/Base64ImageDetector.ts +73 -0
  29. package/src/services/MediaProcessorService.ts +79 -10
  30. package/src/services/modules/index.ts +24 -19
  31. package/tests/processors/Base64ImageDetector.test.ts +160 -0
  32. package/tests/unit/clients/AIClient.test.ts +446 -0
  33. package/tests/unit/clients/withRetry.test.ts +319 -0
  34. package/tests/unit/commands/github-credentials.test.ts +1 -2
  35. package/ts_build/package.json +1 -1
  36. package/ts_build/src/agents/tools/list.js +2 -2
  37. package/ts_build/src/agents/tools/list.js.map +1 -1
  38. package/ts_build/src/ai.d.ts +3 -3
  39. package/ts_build/src/ai.js +51 -23
  40. package/ts_build/src/ai.js.map +1 -1
  41. package/ts_build/src/chat/CliChatService.js +1 -1
  42. package/ts_build/src/chat/CliChatService.js.map +1 -1
  43. package/ts_build/src/chat/modules/SystemModule.js +2 -2
  44. package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
  45. package/ts_build/src/clients/anthropic.js +1 -1
  46. package/ts_build/src/clients/anthropic.js.map +1 -1
  47. package/ts_build/src/clients/index.js +7 -6
  48. package/ts_build/src/clients/index.js.map +1 -1
  49. package/ts_build/src/clients/openai.js +4 -4
  50. package/ts_build/src/clients/openai.js.map +1 -1
  51. package/ts_build/src/clients/types.d.ts +12 -6
  52. package/ts_build/src/clients/withRetry.d.ts +2 -0
  53. package/ts_build/src/clients/withRetry.js +60 -0
  54. package/ts_build/src/clients/withRetry.js.map +1 -0
  55. package/ts_build/src/commands/agent.js +25 -0
  56. package/ts_build/src/commands/agent.js.map +1 -1
  57. package/ts_build/src/commands/modules.js +297 -17
  58. package/ts_build/src/commands/modules.js.map +1 -1
  59. package/ts_build/src/config.js +1 -1
  60. package/ts_build/src/config.js.map +1 -1
  61. package/ts_build/src/hashes.js +5 -7
  62. package/ts_build/src/hashes.js.map +1 -1
  63. package/ts_build/src/index.js +1 -1
  64. package/ts_build/src/index.js.map +1 -1
  65. package/ts_build/src/processors/Base64ImageDetector.d.ts +3 -0
  66. package/ts_build/src/processors/Base64ImageDetector.js +42 -0
  67. package/ts_build/src/processors/Base64ImageDetector.js.map +1 -1
  68. package/ts_build/src/services/MediaProcessorService.d.ts +5 -4
  69. package/ts_build/src/services/MediaProcessorService.js +53 -8
  70. package/ts_build/src/services/MediaProcessorService.js.map +1 -1
  71. package/ts_build/src/services/modules/index.js +17 -13
  72. package/ts_build/src/services/modules/index.js.map +1 -1
  73. package/ts_build/tests/processors/Base64ImageDetector.test.js +111 -0
  74. package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -1
  75. package/ts_build/tests/unit/clients/AIClient.test.d.ts +1 -0
  76. package/ts_build/tests/unit/clients/AIClient.test.js +339 -0
  77. package/ts_build/tests/unit/clients/AIClient.test.js.map +1 -0
  78. package/ts_build/tests/unit/clients/withRetry.test.d.ts +1 -0
  79. package/ts_build/tests/unit/clients/withRetry.test.js +225 -0
  80. package/ts_build/tests/unit/clients/withRetry.test.js.map +1 -0
  81. package/ts_build/tests/unit/commands/github-credentials.test.js +1 -2
  82. package/ts_build/tests/unit/commands/github-credentials.test.js.map +1 -1
@@ -1,405 +1,369 @@
1
- # Worker System Guide (Knowhow CLI)
1
+ # Knowhow Worker System Guide
2
2
 
3
- The **Knowhow worker** is how you expose your **local machine** to the Knowhow cloud so **AI agents running on `knowhow.tyvm.ai`** can call your tools and access your workspace.
4
-
5
- A worker runs a local **MCP server** and keeps a persistent **WebSocket connection** to the Knowhow cloud. The cloud can then invoke the MCP tools that you explicitly allow.
3
+ The **Knowhow worker** is the bridge between your **local machine** and the **Knowhow cloud** (`knowhow.tyvm.ai`). It runs a **local MCP server** that exposes selected tools (including local filesystem/code tooling) to AI agents running in the cloud.
6
4
 
7
5
  ---
8
6
 
9
- ## 1) What the worker is
7
+ ## 1. What the worker is
8
+
9
+ When you run `knowhow worker`, Knowhow starts a **local worker process** that:
10
+
11
+ - Loads your local Knowhow configuration from `./.knowhow/knowhow.json`
12
+ - Creates an **MCP (Model Context Protocol) server** locally
13
+ - Connects back to the Knowhow cloud via **WebSockets**
14
+ - Exposes a curated set of **tools** to the cloud so agents can call them
10
15
 
11
- A **worker** is a process started by `knowhow worker` that:
16
+ ### How the worker connects
17
+ The worker opens a WebSocket to:
12
18
 
13
- - Loads the CLI’s tool registry (agent tools + worker tools).
14
- - Starts a local **MCP server** over WebSockets.
15
- - Connects to **Knowhow cloud** at `knowhow.tyvm.ai` (via a configured API URL).
16
- - Advertises only the tools allowed by your `knowhow.json` configuration.
17
- - Optionally enables:
18
- - **Sharing/visibility controls**
19
- - **Tunnel-based port forwarding**
20
- - **Docker sandbox mode**
21
- - **Passkey-based locking/unlocking**
19
+ - `https://knowhow.tyvm.ai/ws/worker` (exact base URL comes from `KNOWHOW_API_URL`)
20
+ - Sends an `Authorization: Bearer <JWT>` header and other metadata (like a “Root” header)
22
21
 
23
- In `src/worker.ts`, this is implemented by:
22
+ ### Tool exposure model (high-level)
23
+ Tools are gathered from:
24
+ - Built-in agent tools (`./agents/tools/...`)
25
+ - Worker-specific tools (`./workers/tools/...`)
26
+ - MCP tools configured in your Knowhow setup (via configured MCP servers)
24
27
 
25
- - Creating an MCP server: `mcpServer.createServer(...).withTools(toolsToUse)`
26
- - Connecting to the cloud WebSocket endpoint: `new WebSocket(`${API_URL}/ws/worker`, { headers })`
27
- - Running the MCP-over-WebSocket transport: `mcpServer.runWsServer(ws)`
28
+ If passkey auth is enabled (see below), the worker may start **locked** and block all tool usage until unlocked.
28
29
 
29
30
  ---
30
31
 
31
- ## 2) `knowhow worker` — starting a worker
32
+ ## 2. `knowhow worker` — starting a worker (what happens on startup)
32
33
 
33
- Command:
34
+ At a high level, startup does the following:
34
35
 
35
- ```bash
36
- knowhow worker
37
- ```
36
+ 1. **Load config**
37
+ - Reads `./.knowhow/knowhow.json` via `getConfig()`
38
38
 
39
- At runtime, the worker does the following (high level):
40
-
41
- 1. **Loads config** from `./.knowhow/knowhow.json` (`getConfig()`).
42
- 2. Handles special flags:
43
- - `--passkey-reset` clears passkey config and exits.
44
- - `--passkey` starts a browser-based registration flow and exits.
45
- 3. Decides whether to run in **Docker sandbox mode**:
46
- - If already inside Docker (`process.env.KNOWHOW_DOCKER === "true"`), it disables sandbox to avoid nested Docker.
47
- - Otherwise, sandbox selection priority is:
48
- 1. CLI flag `--sandbox` / `--no-sandbox`
49
- 2. `config.worker.sandbox`
50
- 3. default: `false` (host mode)
51
- 4. If in **host mode**:
52
- - Registers the MCP tools locally by:
53
- - `Tools.defineTools(includedTools, combinedTools)`
54
- - `Tools.defineTools(workerTools.definitions, workerTools.tools)`
55
- - `await Mcp.addTools(Tools)`
56
- - Ensures `worker.allowedTools` exists:
57
- - If `config.worker?.allowedTools` is missing, it auto-generates:
58
- - `allowedTools: Tools.getToolNames()`
59
- - saves it to config
60
- - prints a message and **returns early** (so you can edit allowed tools before running again)
61
- 5. If **registration** is enabled (`--register`):
62
- - Calls `registerWorkerPath(process.cwd())` and exits.
63
- 6. If **passkey auth** is enabled in config:
64
- - Starts in a **locked** state.
65
- - Wraps each allowed tool to block calls while locked, returning:
66
- - `error: "WORKER_LOCKED"`
67
- - a message instructing the caller to use `unlock`.
68
- - Registers special auth tools:
69
- - `unlock` (two-step flow)
70
- - `lock`
71
- 7. Connects to the cloud via WebSockets:
72
- - `API_URL/ws/worker` for the MCP tool channel
73
- - Optional `API_URL/ws/tunnel` for the tunnel system
74
- 8. Loops forever, pinging every ~5 seconds, and reconnecting on disconnect.
39
+ 2. **Optional security setup / reset**
40
+ - If you pass `--passkey` or `--passkey-reset`, worker runs those flows and exits (it does not start the worker loop).
41
+
42
+ 3. **Resolve sandbox mode**
43
+ - If `--sandbox` is set, it runs the worker inside a Docker sandbox container (see section 8).
44
+ - If running *already inside Docker* (`process.env.KNOWHOW_DOCKER === "true"`), sandbox is forced off to avoid nested containers.
45
+
46
+ 4. **Define tools and MCP transport**
47
+ - In host mode (not Docker sandbox entrypoint), it:
48
+ - defines tools (`Tools.defineTools(...)`)
49
+ - registers them with the MCP system (`await Mcp.addTools(Tools)`)
50
+ - creates an `McpServerService` and associates a client name: `knowhow-worker`
51
+
52
+ 5. **Passkey auth gating (optional)**
53
+ - If `config.worker.auth.passkey.publicKey` and `credentialId` exist:
54
+ - worker starts **locked**
55
+ - it registers auth tools: `unlock`, `lock`
56
+ - it wraps each exposed tool to return `WORKER_LOCKED` when locked
57
+
58
+ 6. **Register hot reload tool**
59
+ - It registers `reloadConfig`, enabling agents/tools to hot-reload MCP/tool configuration without restarting the process.
60
+
61
+ 7. **Tunnel configuration**
62
+ - It evaluates whether `worker.tunnel.enabled` (or forced tunnel mode) is active (see section 7).
63
+
64
+ 8. **Connect/reconnect loop**
65
+ - The worker continuously:
66
+ - loads JWT (`loadJwt()`)
67
+ - reconnects WebSocket
68
+ - pings the connection every 5 seconds
69
+ - pauses reconnection if the JWT is unauthorized (WebSocket close code `1008`)
75
70
 
76
71
  ---
77
72
 
78
- ## 3) CLI flags
73
+ ## 3. CLI flags
79
74
 
80
- These flags are defined under the `worker` command in `src/cli.ts`.
75
+ These flags are handled by the worker command entrypoint (`src/worker.ts`).
81
76
 
82
77
  ### `--share` / `--unshare` (visibility control)
83
78
 
84
- - `--share` makes the worker accessible to your organization.
85
- - `--unshare` makes it private to you.
79
+ When connecting to the cloud, the worker sets a shared header:
80
+
81
+ - `--share` → `headers.Shared = "true"`
82
+ - `--unshare` → `headers.Shared = "false"`
86
83
 
87
- Implementation detail (`src/worker.ts`): the worker sets a WebSocket header:
84
+ If neither is passed, the worker logs:
88
85
 
89
- - `headers.Shared = "true"` when `--share` is used
90
- - `headers.Shared = "false"` when `--unshare` is used
91
- - otherwise: “Worker is private (only you can use it)”
86
+ - “Worker is private (only you can use it)”
87
+
88
+ **What it means:** shared workers may be usable by others in your organization; unshared workers are private.
89
+
90
+ ---
92
91
 
93
92
  ### `--sandbox` / `--no-sandbox` (Docker sandbox mode)
94
93
 
95
- - `--sandbox` runs the worker inside Docker for isolation.
96
- - `--no-sandbox` runs it on the host.
94
+ - `--sandbox` forces sandbox mode **on**
95
+ - `--no-sandbox` forces sandbox mode **off**
97
96
 
98
- Sandbox selection priority is:
97
+ The chosen preference is persisted to config:
99
98
 
100
- 1. CLI flags
101
- 2. `config.worker.sandbox`
102
- 3. default: `false`
99
+ ```ts
100
+ worker.sandbox = true | false
101
+ ```
103
102
 
104
- Implementation detail:
105
- - When `shouldUseSandbox` is true, the worker calls `runWorkerInSandbox(...)`.
106
- - If Docker isnt available, sandbox mode exits with an error.
107
- - Sandbox always rebuilds the worker image:
108
- - `Docker.buildWorkerImage()`
103
+ **Default behavior:** if neither is passed, it uses `config.worker?.sandbox ?? false`.
104
+
105
+ > Note: if the process detects its already running inside Docker (`KNOWHOW_DOCKER=true`), it forces sandbox mode off to prevent nested containers.
106
+
107
+ ---
109
108
 
110
109
  ### `--register` (register worker path)
111
110
 
112
- Registers the current directory as a worker in the local worker registry:
111
+ If you run:
113
112
 
114
- ```bash
115
- knowhow worker --register
116
- ```
113
+ - `knowhow worker --register`
114
+
115
+ the worker calls `registerWorkerPath(process.cwd())` and exits.
117
116
 
118
- Implementation detail: `registerWorkerPath(process.cwd())`.
117
+ **Purpose:** register the current worker directory path for Knowhow worker discovery/management in the ecosystem.
118
+
119
+ ---
119
120
 
120
121
  ### `--passkey` / `--passkey-reset` (passkey security setup)
121
122
 
122
- - `--passkey` starts the passkey registration flow (requires you to be logged in).
123
- - `--passkey-reset` removes passkey requirement from config.
123
+ - `--passkey-reset`
124
+ - calls `PasskeySetupService.reset()`
125
+ - clears passkey data from worker auth config
126
+ - exits
124
127
 
125
- Implementation detail:
126
- - `--passkey` uses `PasskeySetupService.setup(jwt)`
127
- - `--passkey-reset` uses `PasskeySetupService.reset()`
128
- - If you’re not logged in, `--passkey` errors and tells you to run `knowhow login`.
128
+ - `--passkey`
129
+ - requires you to be logged in (`knowhow login`)
130
+ - calls `PasskeySetupService.setup(jwt)`
131
+ - exits
132
+
133
+ After passkey setup, the worker can start in a **locked** state and require a passkey assertion to unlock tool access.
129
134
 
130
135
  ---
131
136
 
132
- ## 4) `worker.allowedTools` — configuring which tools to expose
137
+ ## 4. `worker.allowedTools` — configuring exposed tools
138
+
139
+ This setting controls **which tools the cloud agent is allowed to call**.
140
+
141
+ ### Initial list generation (first run)
142
+ On first run, if:
143
+ - you did **not** pass `--allowedTools`, and
144
+ - there is no existing `config.worker.allowedTools`,
145
+
146
+ then the worker auto-generates the initial allowlist:
133
147
 
134
- ### How the initial list is created (first run)
148
+ - `Tools.getToolNames()`
149
+ - saves it into `./.knowhow/knowhow.json` as:
135
150
 
136
- When running in host mode:
151
+ ```json
152
+ {
153
+ "worker": {
154
+ "allowedTools": [ ... ]
155
+ }
156
+ }
157
+ ```
137
158
 
138
- - If `config.worker.allowedTools` is **missing**, the worker:
139
- - auto-generates it as:
140
- - `allowedTools: Tools.getToolNames()`
141
- - writes it to `.knowhow/knowhow.json`
142
- - prints:
143
- > “Worker tools configured! Update knowhow.json to adjust which tools are allowed by the worker.”
144
- - then **exits early** (so you can edit the list before actually serving tools)
159
+ The worker then returns early with a message instructing you to update the config.
145
160
 
146
- So the typical workflow is:
161
+ ---
147
162
 
148
- 1. Start worker once
149
- 2. Edit `worker.allowedTools`
150
- 3. Start worker again
163
+ ### MCP tool naming convention
164
+ MCP tools from configured MCP servers are exposed with names like:
151
165
 
152
- ### Tool naming (including MCP tools)
166
+ - `mcp_0_<server>_<toolname>`
153
167
 
154
- The guide expects the following naming convention for MCP tool exposure:
168
+ Example:
169
+ - If your MCP server is named `browser` and it has a tool `navigate`,
170
+ the exposed tool name may look like:
171
+ - `mcp_0_browser_navigate`
155
172
 
156
- - **MCP tools** appear as:
157
- - `mcp_0_<server>_<toolname>`
173
+ (Exact numbering/indices depend on how MCP servers are ordered/registered.)
158
174
 
159
- The worker’s tool registry can include both:
160
- - built-in worker/tools
161
- - agent tools
162
- - configured MCP tools (for example browser automation)
175
+ ---
163
176
 
164
177
  ### Example `allowedTools` list
165
-
166
- Example (illustrative):
178
+ Here’s a realistic partial example:
167
179
 
168
180
  ```json
169
181
  {
170
182
  "worker": {
171
183
  "allowedTools": [
172
- "readFile",
173
- "writeFile",
174
- "searchFiles",
175
- "exec",
184
+ "agents_md_search",
185
+ "exec_run",
186
+ "file_read",
187
+ "file_write",
188
+ "mcp_0_browser_newPage",
176
189
  "mcp_0_browser_navigate",
177
- "mcp_0_browser_click"
190
+ "reloadConfig"
178
191
  ]
179
192
  }
180
193
  }
181
194
  ```
182
195
 
183
- > Tip: Keep this list tight. Tools are gated by your explicit configuration, and (optionally) by passkey locking.
196
+ > Tip: if you enable passkey auth, the worker also injects auth tools (`unlock`, `lock`) into the allowed tool set at runtime.
184
197
 
185
198
  ---
186
199
 
187
- ## 5) Connecting to the cloud
200
+ ## 5. Connecting to the cloud
188
201
 
189
- After you run:
202
+ After `knowhow login`, the worker retrieves a JWT using `loadJwt()` and connects via WebSockets.
190
203
 
191
- ```bash
192
- knowhow login
193
- ```
204
+ ### WebSocket handshake
205
+ The worker connects to:
194
206
 
195
- the worker retrieves your JWT token (`loadJwt()`) and connects to Knowhow cloud using WebSockets:
207
+ - `API_URL + "/ws/worker"`
196
208
 
197
- - **MCP/tool channel**:
198
- - `ws://${API_URL}/ws/worker` (API URL is derived from `KNOWHOW_API_URL`)
199
- - Optional **tunnel channel**:
200
- - `ws://${API_URL}/ws/tunnel`
209
+ with headers:
201
210
 
202
- Headers sent with the WebSocket connection include:
211
+ - `Authorization: Bearer <JWT>`
212
+ - `User-Agent: knowhow-worker/<version>/<hostname>`
213
+ - `Root: <workspace root path in ~ notation>`
203
214
 
204
- - `Authorization: Bearer <jwt>`
205
- - `User-Agent: knowhow-worker/1.1.1/<hostname>`
206
- - `Root: <workspace root path representation>`
207
- - `Shared: "true"` or `"false"` if share/unshare flags are used
208
-
209
- Reconnect behavior:
210
- - If the worker WebSocket closes, it logs and reconnects.
211
- - The worker also periodically pings (`await connection.ws.ping()`), and will reconnect if ping fails.
215
+ ### Reconnect behavior
216
+ - The worker runs an infinite loop.
217
+ - If the socket closes with code `1008`, it assumes the JWT is expired:
218
+ - it records the failing JWT in `unauthorizedJwt`
219
+ - it waits for the JWT to change (by reloading it) before retrying
212
220
 
213
221
  ---
214
222
 
215
- ## 6) Sharing the worker
223
+ ## 6. Sharing the worker
216
224
 
217
- - By default (no `--share` / `--unshare`):
218
- - the worker is treated as **private**.
219
- - With `--share`:
220
- - the worker advertises `Shared: "true"` and is accessible to others in your organization.
221
- - With `--unshare`:
222
- - the worker advertises `Shared: "false"` (explicitly private).
225
+ Use:
223
226
 
224
- ---
227
+ - `knowhow worker --share`
228
+ to make the worker accessible to others (organization-level sharing)
225
229
 
226
- ## 7) Tunnel system (`worker.tunnel`)
230
+ - `knowhow worker --unshare`
231
+ to force it back to private mode
227
232
 
228
- The worker can also forward inbound requests to **your local ports** through the Knowhow cloud using a tunnel.
233
+ This affects the `Shared` header sent during WebSocket connection.
229
234
 
230
- ### Enable it
235
+ ---
231
236
 
232
- In `knowhow.json`:
237
+ ## 7. Tunnel system (`worker.tunnel`)
233
238
 
234
- ```json
235
- {
236
- "worker": {
237
- "tunnel": {
238
- "enabled": true
239
- }
240
- }
241
- }
242
- ```
243
-
244
- ### `allowedPorts`
239
+ The tunnel system provides **port forwarding** so cloud agents can reach services on your local machine through controlled port access.
245
240
 
246
- When tunnel is enabled, you must configure which ports the tunnel will be allowed to forward:
241
+ ### Enable tunnel
242
+ In config:
247
243
 
248
244
  ```json
249
245
  {
250
246
  "worker": {
251
247
  "tunnel": {
252
248
  "enabled": true,
253
- "allowedPorts": [3000, 5432]
249
+ "allowedPorts": [3000, 5173]
254
250
  }
255
251
  }
256
252
  }
257
253
  ```
258
254
 
259
- If tunnel is enabled but `allowedPorts` is empty, the worker warns:
260
-
261
- > “Tunnel enabled but no allowedPorts configured. Add tunnel.allowedPorts to knowhow.json”
255
+ ### `enabled: true`
256
+ When enabled, the worker also opens a **separate tunnel WebSocket** (in addition to the worker WebSocket).
262
257
 
263
- ### Other tunnel config (from code)
258
+ ### `allowedPorts`
259
+ - The worker warns if tunnel is enabled but `allowedPorts` is empty.
260
+ - Allowed ports are enforced by the tunnel layer (so you don’t accidentally expose all local ports).
264
261
 
265
- The worker also reads (optional) tunnel settings:
262
+ ### Tunnel mode forcing
263
+ If you use a tunnel-related workflow (e.g. the CLI passes `allowedTools` as an override from tunnel mode), tunnel is **forced on**:
266
264
 
267
- - `worker.tunnel.localHost`
268
- - If not set:
269
- - inside Docker: uses `host.docker.internal`
270
- - otherwise: uses `127.0.0.1`
271
- - `worker.tunnel.portMapping`
272
- - Logged as “Container port → Host port”
273
- - `worker.tunnel.maxConcurrentStreams` (default 50)
274
- - `worker.tunnel.enableUrlRewriting` (default enabled)
275
- - `worker.tunnel.enableUrlRewriting !== false` enables URL rewriting
276
- - Tunnel URL rewriting is based on either a `secret` or `workerId` in tunnel metadata
265
+ ```ts
266
+ const tunnelEnabled = options?.allowedTools ? true : config.worker?.tunnel?.enabled ?? false;
267
+ ```
277
268
 
278
269
  ---
279
270
 
280
- ## 8) Docker sandbox mode
281
-
282
- Sandbox mode runs the worker in Docker for isolation.
283
-
284
- ### Enable it
271
+ ## 8. Docker sandbox mode (security hardening)
285
272
 
286
- Either:
273
+ Docker sandbox mode runs the worker inside Docker to isolate filesystem/process access.
287
274
 
288
- ```bash
289
- knowhow worker --sandbox
290
- ```
275
+ ### Enable sandbox via config or flags
276
+ - Config:
277
+ - `worker.sandbox: true`
278
+ - Flag:
279
+ - `knowhow worker --sandbox`
291
280
 
292
- or in config:
281
+ ### How it runs
282
+ When sandbox is enabled, the worker:
293
283
 
294
- ```json
295
- {
296
- "worker": {
297
- "sandbox": true
298
- }
299
- }
300
- ```
284
+ 1. checks Docker availability
285
+ 2. builds a worker image using:
286
+ - `.knowhow/Dockerfile.worker`
287
+ 3. runs a Docker container with:
288
+ - `workspaceDir: process.cwd()`
289
+ - JWT + API URL + config passed into the container
290
+ - share/unshare flags passed through
301
291
 
302
- ### Configuration: `worker.volumes`
292
+ ### `worker.volumes` / `worker.envFile`
293
+ Your configuration can define how the container mounts and environment variables are provided.
303
294
 
304
- When sandboxing, you typically need to mount your workspace and any other resources into the container.
305
-
306
- This guide documents the expected config keys passed into the Docker runner:
295
+ > The exact structure for `worker.volumes` and `worker.envFile` is handled by Docker helper services in the repo (not shown in the excerpt), but the worker code passes `config` into the Docker runner, so those settings live under `worker.*`.
307
296
 
297
+ **Example (template):**
308
298
  ```json
309
299
  {
310
300
  "worker": {
311
301
  "sandbox": true,
312
302
  "volumes": [
313
- { "host": ".", "container": "/workspace" }
314
- ]
315
- }
316
- }
317
- ```
318
-
319
- > The worker code passes the entire `config` into `Docker.runWorkerContainer(...)`, so `worker.volumes` is expected to be consumed by the Docker layer.
320
-
321
- ### Configuration: `worker.envFile`
322
-
323
- Similarly, you can pass environment variables into the sandboxed container using a file path:
324
-
325
- ```json
326
- {
327
- "worker": {
328
- "sandbox": true,
329
- "envFile": ".knowhow/worker.env"
303
+ { "source": "./", "target": "/workspace" }
304
+ ],
305
+ "envFile": ".env"
330
306
  }
331
307
  }
332
308
  ```
333
309
 
334
- > As above, the worker passes `config` through to the Docker runner.
335
-
336
- ### Notes specific to nested containers
337
-
338
- If you run the worker inside an environment where:
339
-
340
- - `KNOWHOW_DOCKER=true`
341
-
342
- then the worker automatically disables sandbox mode (prevents “nested Docker”).
343
-
344
310
  ---
345
311
 
346
- ## 9) Passkey security
312
+ ## 9. Passkey security
347
313
 
348
- Passkey auth protects your worker by requiring a **hardware passkey** to unlock tool access.
314
+ Passkey auth protects the worker so only the owner can unlock tool access.
349
315
 
350
316
  ### Setup and reset
317
+ - Setup:
318
+ - `knowhow worker --passkey`
319
+ - Reset:
320
+ - `knowhow worker --passkey-reset`
351
321
 
352
- - Register/enable passkey auth:
322
+ ### How passkeys block unauthorized access
323
+ If passkey config exists in:
353
324
 
354
- ```bash
355
- knowhow worker --passkey
356
- ```
325
+ - `config.worker.auth.passkey.publicKey`
326
+ - `config.worker.auth.passkey.credentialId`
357
327
 
358
- - Remove passkey requirement:
328
+ then on startup the worker:
359
329
 
360
- ```bash
361
- knowhow worker --passkey-reset
362
- ```
330
+ 1. creates a `WorkerPasskeyAuthService`
331
+ 2. starts **locked**
332
+ 3. wraps every configured tool such that:
333
+ - if locked, tool calls return:
363
334
 
364
- ### What happens at startup
335
+ - `error: "WORKER_LOCKED"`
336
+ - message instructing to call unlock first
365
337
 
366
- If config contains passkey credentials:
338
+ 4. registers auth tools:
339
+ - `unlock` and `lock`
367
340
 
368
- - `config.worker.auth.passkey.publicKey`
369
- - `config.worker.auth.passkey.credentialId`
341
+ ### Unlock flow (tool behavior)
342
+ The worker’s `unlock` tool is a two-step flow:
370
343
 
371
- then the worker:
344
+ 1. Call `unlock()` **with no parameters**
345
+ → it returns a `challenge` (and `credentialId`), and you must sign it using WebAuthn in the browser.
372
346
 
373
- - enables passkey auth
374
- - starts **locked**
375
- - wraps each configured allowed tool so that when locked it returns:
347
+ 2. Call `unlock({ signature, credentialId, authenticatorData, clientDataJSON, challenge })`
348
+ it verifies the assertion and unlocks the session.
376
349
 
377
- ```json
378
- {
379
- "error": "WORKER_LOCKED",
380
- "message": "Worker is locked. Call the `unlock` tool with your passkey assertion to unlock it first."
381
- }
382
- ```
350
+ There is also a standalone `getChallenge` tool in the codebase (`makeGetChallengeTool`), which is typically used by clients/UI flows, but the worker startup injects `unlock` and `lock` explicitly.
383
351
 
384
- ### How unlocking works (tools)
352
+ ### Session duration
353
+ Passkey gating uses:
385
354
 
386
- When passkey auth is enabled, the worker registers these tools:
387
-
388
- - `getChallenge` (returns a challenge string)
389
- - `unlock` (two-step tool)
390
- - **Call without assertion fields** → returns a challenge
391
- - **Call with assertion fields** → verifies assertion and unlocks
392
- - `lock` (re-locks the worker)
393
-
394
- **Important behavior:** the wrapper gating applies to your *configured allowed tools*, while the auth tools (`unlock`, `lock`, and the unlock flow challenge) are added so callers can regain access.
355
+ - `config.worker.auth.sessionDurationHours` (defaulted in code to 3 hours if not specified)
395
356
 
396
357
  ---
397
358
 
398
- ## 10) Worker in production (systemd / background)
359
+ ## 10. Worker in production (systemd / background)
399
360
 
400
- The worker runs an infinite loop that reconnects automatically, so it’s well-suited for a supervisor.
361
+ For production-like usage you should:
362
+ - start `knowhow worker` at boot
363
+ - ensure it runs in the correct working directory (the repo/workspace containing `./.knowhow/knowhow.json`)
364
+ - consider enabling sandbox + tunnel only when required
401
365
 
402
- ### systemd example
366
+ ### Example: systemd service
403
367
 
404
368
  Create `/etc/systemd/system/knowhow-worker.service`:
405
369
 
@@ -411,14 +375,14 @@ Wants=network-online.target
411
375
 
412
376
  [Service]
413
377
  Type=simple
414
- WorkingDirectory=/path/to/your/worker-directory
415
- ExecStart=/usr/local/bin/knowhow worker --register --share --sandbox
378
+ WorkingDirectory=/path/to/your/project
379
+ ExecStart=/usr/bin/knowhow worker --no-sandbox
416
380
  Restart=always
417
381
  RestartSec=5
418
382
  Environment=NODE_ENV=production
419
383
 
420
- # Optional: load environment variables
421
- # EnvironmentFile=/path/to/your/envfile
384
+ # Optional: pass share mode
385
+ # ExecStart=/usr/bin/knowhow worker --share --no-sandbox
422
386
 
423
387
  [Install]
424
388
  WantedBy=multi-user.target
@@ -432,95 +396,124 @@ sudo systemctl enable --now knowhow-worker
432
396
  sudo journalctl -u knowhow-worker -f
433
397
  ```
434
398
 
435
- ### Background process example
399
+ ### Example: keep logs and use sandbox
436
400
 
437
- ```bash
438
- nohup knowhow worker --share > /var/log/knowhow-worker.log 2>&1 &
401
+ ```ini
402
+ ExecStart=/usr/bin/knowhow worker --sandbox
439
403
  ```
440
404
 
441
405
  ---
442
406
 
443
- ## Example `knowhow.json` worker configuration
407
+ # Example worker configuration (`knowhow.json`)
444
408
 
445
- Place this in `./.knowhow/knowhow.json` (the worker edits/reads it).
409
+ A complete example showing the major worker settings:
446
410
 
447
411
  ```json
448
412
  {
449
413
  "worker": {
414
+ "sandbox": true,
450
415
  "allowedTools": [
451
- "exec",
452
- "readFile",
453
- "writeFile",
416
+ "agents_md_search",
417
+ "exec_run",
418
+ "file_read",
419
+ "file_write",
420
+ "mcp_0_browser_newPage",
454
421
  "mcp_0_browser_navigate",
455
- "mcp_0_browser_click"
422
+ "reloadConfig"
456
423
  ],
457
- "sandbox": false,
424
+ "workerId": "",
458
425
  "tunnel": {
459
426
  "enabled": true,
460
- "allowedPorts": [3000, 5432]
427
+ "allowedPorts": [3000, 5173]
461
428
  },
462
429
  "auth": {
430
+ "sessionDurationHours": 3,
463
431
  "passkey": {
464
- "publicKey": "-----BEGIN PUBLIC KEY-----...",
465
- "credentialId": "base64url-credential-id"
466
- },
467
- "sessionDurationHours": 3
468
- },
469
- "volumes": [],
470
- "envFile": ".knowhow/worker.env"
432
+ "publicKey": "BASE64URL_PUBLIC_KEY",
433
+ "credentialId": "BASE64URL_CREDENTIAL_ID"
434
+ }
435
+ }
471
436
  }
472
437
  }
473
438
  ```
474
439
 
475
440
  ---
476
441
 
477
- ## Example workflows
478
-
479
- ### Workflow A: Configure allowed tools (safe first run)
442
+ # Example workflows
480
443
 
481
- 1. Run once to auto-generate `worker.allowedTools`:
444
+ ## Workflow A: First-time setup (generate allowed tools)
445
+ 1. `knowhow login`
446
+ 2. Run:
482
447
  ```bash
483
448
  knowhow worker
484
449
  ```
485
- 2. Edit `.knowhow/knowhow.json` and narrow `worker.allowedTools`.
486
- 3. Run again:
450
+ 3. On first run, the worker prints a message and auto-writes:
451
+ - `worker.allowedTools = Tools.getToolNames()`
452
+ 4. Edit `./.knowhow/knowhow.json` to narrow the allowlist.
453
+ 5. Run again:
487
454
  ```bash
488
- knowhow worker --share
455
+ knowhow worker
489
456
  ```
490
457
 
491
- ### Workflow B: Expose a local web app through the tunnel
458
+ ---
459
+
460
+ ## Workflow B: Share worker with organization
461
+ ```bash
462
+ knowhow worker --share
463
+ ```
464
+
465
+ This sets the `Shared` header to `true` during WebSocket connection.
466
+
467
+ ---
492
468
 
493
- 1. Enable tunnel and allow the port:
469
+ ## Workflow C: Enable tunnel for local web apps
470
+ 1. Add to config:
494
471
  ```json
495
472
  {
496
473
  "worker": {
497
- "tunnel": { "enabled": true, "allowedPorts": [3000] }
474
+ "tunnel": {
475
+ "enabled": true,
476
+ "allowedPorts": [3000, 8080]
477
+ }
498
478
  }
499
479
  }
500
480
  ```
501
- 2. Start the worker:
502
- ```bash
503
- knowhow worker --share
504
- ```
505
- 3. Your cloud agent can then reach forwarded services via tunnel-generated subdomains (URL rewriting enabled by default).
481
+ 2. Restart the worker.
482
+ 3. Agents can then reach forwarded ports through the tunnel mechanism.
506
483
 
507
- ### Workflow C: Secure the worker with passkey locking
484
+ ---
508
485
 
509
- 1. Log in:
486
+ ## Workflow D: Lock down worker with a passkey
487
+ 1. Ensure logged in:
510
488
  ```bash
511
489
  knowhow login
512
490
  ```
513
- 2. Register the passkey:
491
+ 2. Setup passkey:
514
492
  ```bash
515
493
  knowhow worker --passkey
516
494
  ```
517
- 3. Edit `worker.allowedTools` to include only what you want agents to do.
518
- 4. Start the worker normally (it starts locked):
495
+ 3. Start worker normally:
519
496
  ```bash
520
497
  knowhow worker
521
498
  ```
522
- 5. The agent must call `unlock` using the challenge + WebAuthn assertion to use the other tools.
499
+
500
+ Agents/tools will be blocked until they perform the `unlock` passkey flow.
501
+
502
+ To remove it:
503
+ ```bash
504
+ knowhow worker --passkey-reset
505
+ ```
506
+
507
+ ---
508
+
509
+ ## Workflow E: Production deployment (systemd)
510
+ Use the systemd unit example above, then enable and watch logs:
511
+
512
+ ```bash
513
+ sudo systemctl enable --now knowhow-worker
514
+ sudo journalctl -u knowhow-worker -f
515
+ ```
523
516
 
524
517
  ---
525
518
 
526
- If you want, paste your current `./.knowhow/knowhow.json` worker block and I can suggest a minimal `allowedTools` list and a safe tunnel configuration for your use case.
519
+ If you share your current `./.knowhow/knowhow.json` (redact secrets), I can help you produce a safe `worker.allowedTools` allowlist and an example tunnel + sandbox setup tailored to your MCP servers.