@kontextmind/kxm 0.7.145 → 0.7.147
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/.claude-plugin/marketplace.json +1 -1
- package/.kxm/README.md +20 -2
- package/.kxm/agents/{coordinator.yaml → planner.yaml} +2 -0
- package/.kxm/agents/{critic-arch.yaml → reviewer-arch.yaml} +2 -0
- package/.kxm/agents/{critic-cli.yaml → reviewer-cli.yaml} +2 -0
- package/.kxm/agents/{implementer.yaml → writer.yaml} +2 -0
- package/.kxm/roles/planner.yaml +4 -2
- package/.kxm/roles/reviewer-arch.yaml +4 -2
- package/.kxm/roles/reviewer-cli.yaml +4 -2
- package/.kxm/roles/writer.yaml +6 -3
- package/.kxm/workflows/default.yaml +20 -12
- package/.kxm/workflows/land.yaml +1 -1
- package/.kxm/workflows/{review-arch-only.yaml → reviewer-arch-only.yaml} +7 -3
- package/.kxm/workflows/{review-cli-only.yaml → reviewer-cli-only.yaml} +7 -3
- package/.kxm/workflows/{implement-only.yaml → writer-only.yaml} +8 -4
- package/CHANGELOG.md +48 -8
- package/docs/README.md +1 -1
- package/docs/adr/ADR-0002-browser-automation-steel-doks.md +8 -5
- package/docs/adr/ADR-0005-obscura-default-playwright.md +4 -3
- package/docs/adr/ADR-0006-machine-account-names.md +90 -0
- package/docs/adr/ADR-0007-steel-caddy-authentik.md +97 -0
- package/docs/adr/README.md +3 -1
- package/docs/contributing/harness-routing-internals.md +17 -11
- package/docs/contributing/operating-rules.md +13 -1
- package/docs/guides/agent-skills.md +1 -1
- package/docs/guides/browser-automation.md +44 -28
- package/docs/kb/how-credentials-retrieved-safely.md +22 -23
- package/docs/kb/how-to-connect-playwright-to-steel.md +8 -5
- package/docs/kb/how-to-recover-expired-session-or-orphan.md +11 -10
- package/docs/kb/why-automation-opened-different-browser.md +4 -3
- package/docs/operations/deploy.md +31 -0
- package/docs/operations/troubleshooting.md +23 -1
- package/docs/prompts/browser-diagnose-recover.md +2 -2
- package/docs/prompts/browser-start.md +1 -1
- package/docs/reference/cli-reference.md +23 -24
- package/docs/reference/config-reference.md +69 -62
- package/docs/reference/configuration.md +9 -7
- package/docs/reference/harness-routing.md +6 -5
- package/docs/reference/workflow-catalog.md +3 -3
- package/package.json +3 -3
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +1145 -805
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +636 -308
- package/plugins/kxm/dist/runtime.js +710 -382
- package/plugins/kxm/dist/server.js +49 -5
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +11 -12
- package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +5 -4
- package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +4 -3
- package/plugins/kxm/skills/kxm-browser-session/SKILL.md +5 -5
- package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +3 -3
- package/plugins/kxm/src/browser.ts +12 -9
- package/plugins/kxm/src/cli/project.ts +2 -1
- package/plugins/kxm/src/cli/roles.ts +3 -4
- package/plugins/kxm/src/engine.ts +9 -6
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/modes.ts +1 -1
- package/plugins/kxm/src/policy-draft.mjs +11 -5
- package/plugins/kxm/src/project-config.ts +49 -11
- package/plugins/kxm/src/runtime-service.ts +2 -1
- package/plugins/kxm/src/template.ts +39 -10
- package/plugins/kxm/src/workflow-manager.ts +34 -28
- package/plugins/kxm/src/workforce-names.d.mts +38 -0
- package/plugins/kxm/src/workforce-names.mjs +317 -0
- package/schemas/agent.schema.json +7 -0
- package/schemas/model.schema.json +12 -0
- package/schemas/workflow.schema.json +14 -0
- package/scripts/harness-run.mjs +1 -1
- package/scripts/native-critic.mjs +5 -5
- package/scripts/roster-policy.mjs +9 -3
- package/scripts/workforce-lint.mjs +18 -0
|
@@ -5,6 +5,12 @@ This page records how the KXM repository applies [harness routing](../reference/
|
|
|
5
5
|
> [!IMPORTANT]
|
|
6
6
|
> The files are the authority, not this page: `.kxm/routes.yaml`, `.kxm/roles/*.yaml`, `.kxm/models/*.yaml`, and `.kxm/prices.yaml`. Product routing decisions are recorded under Tracking → Decided in `plans/implementation-plan.md`.
|
|
7
7
|
|
|
8
|
+
## Current ids (2026-09-27)
|
|
9
|
+
|
|
10
|
+
The command transcripts below are the 2026-09-23 capture. The files now use one id shape per kind. A route id is `<harness>-<model-slug>[-<provider>]`, with `.` written as `-`. An agent id equals its role. A workflow id is `default`, `land`, or `<role>-only`. An agent step id equals that role.
|
|
11
|
+
|
|
12
|
+
This checkout's routes are `grok-grok-4-7`, `pi-qwen3-coder-plus-openrouter`, `agy-gemini-3-8-flash-high`, `agy-gemini-3-8-flash-medium`, `claude-fable`, `codex-gpt-5-6-sol`, `pi-qwen3-8-flash-openrouter`, and `pi-glm-5-3-flash-openrouter`. Agents are `planner`, `writer`, `reviewer-arch`, and `reviewer-cli`. `opus-claude` was removed because `opus` is not an admitted selector. `qwen-token-plan/*` and `zai-coding-cn/*` left the admitted list because those harnesses are not allowlisted; their `prices.yaml` rows stay. Old ids resolve as aliases. Every roster entry names an effort. `reviewer-arch` dispatches `claude-fable`. #343's architecture critic ran on opus because the justfile recipe review-arch hardcoded that model; the role file on that commit listed only `fable-claude`. The helper now refuses `opus`.
|
|
13
|
+
|
|
8
14
|
## This checkout's routes and roster
|
|
9
15
|
|
|
10
16
|
`node scripts/kxm.mjs role get writer`:
|
|
@@ -64,11 +70,11 @@ On the capture machine, `kxm harness list` showed `claude` detected but logged o
|
|
|
64
70
|
The issue-127 runner (`kxm assign`, see the [assignment runner](assignment-runner.md)) reads `.kxm/roles/*.yaml` and `.kxm/models/*.yaml` at `refs/remotes/origin/main`. See [Developer assignment policy](../reference/config-reference.md#developer-assignment-policy). Routes there name the harness, the model and the vendor explicitly:
|
|
65
71
|
|
|
66
72
|
```yaml
|
|
67
|
-
grok-
|
|
73
|
+
grok-grok-4-7:
|
|
68
74
|
harness: grok
|
|
69
|
-
model: grok-4.
|
|
75
|
+
model: grok-4.7
|
|
70
76
|
vendor: xai
|
|
71
|
-
|
|
77
|
+
pi-qwen3-coder-plus-openrouter:
|
|
72
78
|
harness: pi
|
|
73
79
|
model: openrouter/qwen/qwen3-coder-plus
|
|
74
80
|
vendor: alibaba
|
|
@@ -107,19 +113,19 @@ These extend the generic examples on the reference page with this checkout's adm
|
|
|
107
113
|
| | Native `grok` | Pi + OpenRouter |
|
|
108
114
|
|---|---|---|
|
|
109
115
|
| Selector | `xai/grok-4.6`: admitted, and first in the writer roster | `openrouter/x-ai/grok-4.6`: not admitted |
|
|
110
|
-
| Developer roster | `grok-
|
|
116
|
+
| Developer roster | `grok-grok-4-7`, the writer route with `edit` | Refused: `native vendor cannot use Pi` |
|
|
111
117
|
| Readiness | `grok` shows auth `yes` | `pi auth check --provider openrouter` returned `not_ready` |
|
|
112
118
|
| Billing | grok.com subscription (OAuth) | $2.00 input, $6.00 output, $0.50 cached input |
|
|
113
119
|
| Context | 500K (Pi's `xai` row; `grok models` does not print one) | 500,000 |
|
|
114
120
|
|
|
115
|
-
Pi's own `xai` provider reported `ready` (OAuth) on the capture machine. When the grok quota runs out, the next writer in the lineup is `
|
|
121
|
+
Pi's own `xai` provider reported `ready` (OAuth) on the capture machine. When the grok quota runs out, the next writer in the lineup is `pi-qwen3-coder-plus-openrouter`, a different vendor.
|
|
116
122
|
|
|
117
123
|
### GPT-5.6 Sol
|
|
118
124
|
|
|
119
125
|
| | Native `codex` | Pi + OpenRouter |
|
|
120
126
|
|---|---|---|
|
|
121
127
|
| Selector | `openai/gpt-5.6-sol`: admitted, the CLI critic | `openrouter/openai/gpt-5.6-sol`: not admitted |
|
|
122
|
-
| Developer roster | `sol
|
|
128
|
+
| Developer roster | `codex-gpt-5-6-sol`: `reviewer-cli`, `read-only` | Refused |
|
|
123
129
|
| Billing | ChatGPT subscription | $2.00 input, $10.00 output, $0.20 cached input |
|
|
124
130
|
| Context | Pi's `openai-codex` row, the same ChatGPT backend, lists 272K | 1,050,000 |
|
|
125
131
|
|
|
@@ -130,7 +136,7 @@ In the inventory, `openai/gpt-5.6-sol` has sources `openrouter+nous` and carries
|
|
|
130
136
|
| | Native `claude` | Pi + OpenRouter |
|
|
131
137
|
|---|---|---|
|
|
132
138
|
| Selector | `anthropic/fable`: admitted, planner and architecture critic | `openrouter/anthropic/claude-fable-5.1`: not admitted |
|
|
133
|
-
| Developer roster | `fable
|
|
139
|
+
| Developer roster | `claude-fable`: `planner` and `reviewer-arch`, `read-only` | Refused |
|
|
134
140
|
| Readiness | Detected `yes`, auth `no`, dispatch `no (not_authenticated)` | `not_ready` |
|
|
135
141
|
| Billing | claude.ai subscription | $10.00 input, $50.00 output, $0.25 cached input |
|
|
136
142
|
| Context | 1M (Pi's `anthropic` row) | 1,000,000 |
|
|
@@ -159,10 +165,10 @@ The code differs from that decision: `.kxm/routes.yaml` admits `google/gemini-3.
|
|
|
159
165
|
|
|
160
166
|
| Model | Vendor-plan route | OpenRouter route | Notes |
|
|
161
167
|
|---|---|---|---|
|
|
162
|
-
| Qwen3.8 Flash | `qwen-token-plan/qwen3.8-flash`:
|
|
163
|
-
| Qwen3 Coder Plus | none | `openrouter/qwen/qwen3-coder-plus`:
|
|
164
|
-
| GLM 5.3 and 5.3 Flash | `zai-coding-cn/glm-5.3` and `…/glm-5.3-flash`: admitted
|
|
165
|
-
| DeepSeek V4.1 Flash | `qwen-token-plan/deepseek-v4.1-flash`: admitted | `deepseek/deepseek-v4.1-flash` in the OpenRouter feed
|
|
168
|
+
| Qwen3.8 Flash | `qwen-token-plan/qwen3.8-flash`: removed from the admitted list on 2026-09-27; `qwen-token-plan` is not allowlisted | `openrouter/qwen/qwen3.8-flash`: admitted, route `pi-qwen3-8-flash-openrouter`, read-only fallback for planner and reviewer-cli | `prices.yaml` keeps both rows. |
|
|
169
|
+
| Qwen3 Coder Plus | none | `openrouter/qwen/qwen3-coder-plus`: route `pi-qwen3-coder-plus-openrouter` | The only admitted Pi writer: exact model, `edit` permission. |
|
|
170
|
+
| GLM 5.3 and 5.3 Flash | `zai-coding-cn/glm-5.3` and `…/glm-5.3-flash`: removed from the admitted list on 2026-09-27; `zai-coding-cn` is not allowlisted | `openrouter/z-ai/glm-5.3-flash`: admitted, route `pi-glm-5-3-flash-openrouter`, read-only fallback for reviewer-arch | Z.ai has no native harness. |
|
|
171
|
+
| DeepSeek V4.1 Flash | `qwen-token-plan/deepseek-v4.1-flash`: removed from the admitted list on 2026-09-27 | `deepseek/deepseek-v4.1-flash` in the OpenRouter feed | Bills a DeepSeek model through Alibaba's plan. `deepseek` is a native-vendor Pi brake, so this is not a route. |
|
|
166
172
|
|
|
167
173
|
The developer runner is stricter. Its only Pi writer is `openrouter/qwen/qwen3-coder-plus`, and it does not allowlist `qwen-token-plan` or `zai-coding-cn`. Today OpenRouter is the right answer here for Qwen3 Coder Plus, Qwen3.8 Flash and GLM 5.3 Flash.
|
|
168
174
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: "Operating rules"
|
|
3
3
|
description: "Standing instructions from the operator that every agent session on this repository follows, with the date each was given. Read before planning, dispatching, landing, or editing the roadmap."
|
|
4
4
|
audience: "agents and maintainers"
|
|
5
|
-
updated: "2026-09-
|
|
5
|
+
updated: "2026-09-27"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Operating rules
|
|
@@ -58,6 +58,18 @@ replacement.
|
|
|
58
58
|
`impl-bg`) retire last, after one real unit has run through the one-step
|
|
59
59
|
workflows. (2026-09-26.)
|
|
60
60
|
|
|
61
|
+
## Accounts
|
|
62
|
+
|
|
63
|
+
- **Machine account names follow [ADR-0006](../adr/ADR-0006-machine-account-names.md).**
|
|
64
|
+
Platform-wide accounts are `svc-<system>-<purpose>`. Tenant-scoped accounts
|
|
65
|
+
are `svc-<tenant>-<system>-<purpose>`, where `<tenant>` is the tenant slug.
|
|
66
|
+
Test, witness, and proof accounts use those shapes with a `test-` prefix
|
|
67
|
+
and are never members of production groups. Authentik-managed accounts
|
|
68
|
+
(outposts and `ak-*`) are exempt. Recorded names such as `kxm-agent`,
|
|
69
|
+
`kxm-witness-*`, `witness9`, and `kxmdproof` stay until an approved
|
|
70
|
+
inventory. Do not invent a replacement for a specific account.
|
|
71
|
+
(2026-09-27.)
|
|
72
|
+
|
|
61
73
|
## Decisions and debt
|
|
62
74
|
|
|
63
75
|
- **Every option comes with pros and cons and two recommendations**, the
|
|
@@ -68,7 +68,7 @@ Tools map the same way: peer tools to `kxm-peer`, workflow tools to `kxm-workflo
|
|
|
68
68
|
|
|
69
69
|
## Browser automation skills
|
|
70
70
|
|
|
71
|
-
Playwright testing and verification use Obscura ([ADR-0005](../adr/ADR-0005-obscura-default-playwright.md)). The Steel skills cover human takeover, MFA, and the live session viewer.
|
|
71
|
+
Playwright testing and verification use Obscura ([ADR-0005](../adr/ADR-0005-obscura-default-playwright.md)). The Steel skills cover remote and hosted browsing, human takeover, MFA, and the live session viewer. Steel is `https://steel.kontextmind.com`, reached only through Caddy and Authentik ([ADR-0007](../adr/ADR-0007-steel-caddy-authentik.md)). Send `Authorization: Basic` (`STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`) and keep the credential out of URLs. `STEEL_API_KEY` is deprecated and is not enforced. `kxm` 0.7.135 or newer is required for Steel. `kxm-browser-verify` owns `vision`. The other browser skills own no `kxm` command. See [Browser automation](browser-automation.md).
|
|
72
72
|
|
|
73
73
|
| Skill | Use it to |
|
|
74
74
|
|---|---|
|
|
@@ -5,9 +5,9 @@ Give agents a real browser without giving them your desktop. Playwright testing
|
|
|
5
5
|
## Before you begin
|
|
6
6
|
|
|
7
7
|
- For Playwright: Node, and `node scripts/obscura.mjs` (it downloads pinned Obscura v0.2.3). [ADR-0005](../adr/ADR-0005-obscura-default-playwright.md) records that default.
|
|
8
|
-
- For
|
|
8
|
+
- For Steel: `kxm` 0.7.135 or newer. [ADR-0007](../adr/ADR-0007-steel-caddy-authentik.md) is the current server. [ADR-0002](../adr/ADR-0002-browser-automation-steel-doks.md) is the superseded Kubernetes deployment.
|
|
9
9
|
- `curl` and `jq`. Optionally `agent-browser` for exploration. Playwright tests use Obscura; do not run `playwright install`.
|
|
10
|
-
-
|
|
10
|
+
- The 1Password CLI (`op`) for the `svc-steel` credential. Read it at runtime. Do not write it to disk.
|
|
11
11
|
- The `kxm-browser-*` skills from the plugin or Pi package. See [Agent skills](agent-skills.md#browser-automation-skills).
|
|
12
12
|
|
|
13
13
|
## Components
|
|
@@ -36,33 +36,37 @@ Set `video: "off"` in Playwright. Obscura does not record video. Connect with th
|
|
|
36
36
|
|
|
37
37
|
The KXM browser library reads these variables, and the shell procedure below uses the same names so both agree.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
The Steel server is `https://steel.kontextmind.com`. `steel.theneuro.me` is an alias of that same server. The only path is Caddy on `kxmd-proxy` (VM 230) with Authentik forward auth. Direct LAN, tailnet, and host-forward connections are blocked. Unauthenticated requests receive a 302 redirect to `id.kxmd.dev`.
|
|
40
|
+
|
|
41
|
+
Sessions return `websocketUrl` `wss://steel.kontextmind.com/`. The previous value was `ws://steel-browser/`. Connect Chrome DevTools Protocol at `/v1/devtools` and send `Authorization` on the handshake. The URL never carries a credential.
|
|
42
|
+
|
|
43
|
+
Authentik accepts the `svc-steel` credential as `Authorization: Basic`. A Bearer token is refused. These groups may connect: `steel-users`, `kxmd-users`, `kxmd-admins`, and `kxmd-owners`. Steel needs `kxm` 0.7.135 or newer.
|
|
40
44
|
|
|
41
45
|
| Variable | Default | Effect |
|
|
42
46
|
|---|---|---|
|
|
43
|
-
| `STEEL_API_URL` |
|
|
47
|
+
| `STEEL_API_URL` | `https://steel.kontextmind.com` | Base URL of the Steel API |
|
|
44
48
|
| `STEEL_UI_URL` | `$STEEL_API_URL/ui` | Base URL of the session viewer |
|
|
45
|
-
| `STEEL_AUTH_HEADER` | unset | Full `Authorization` value.
|
|
46
|
-
| `STEEL_AUTH_BASIC` | unset | `base64(user:token)`, with or without a leading `Basic` prefix
|
|
47
|
-
| `STEEL_AUTH_USER` | unset | Authentik username
|
|
49
|
+
| `STEEL_AUTH_HEADER` | unset | Full `Authorization` value. First in the precedence below |
|
|
50
|
+
| `STEEL_AUTH_BASIC` | unset | `base64(user:token)`, with or without a leading `Basic` prefix |
|
|
51
|
+
| `STEEL_AUTH_USER` | unset | Authentik username `svc-steel`. Used with `STEEL_AUTH_TOKEN` |
|
|
48
52
|
| `STEEL_AUTH_TOKEN` | unset | Authentik app password. Used with `STEEL_AUTH_USER` |
|
|
49
|
-
| `STEEL_API_KEY` |
|
|
50
|
-
| `USE_PASS_CLI` | enabled |
|
|
53
|
+
| `STEEL_API_KEY` | unset | Deprecated. Steel and Caddy do not enforce it. See the migration note |
|
|
54
|
+
| `USE_PASS_CLI` | enabled | Turns the legacy `STEEL_API_KEY` lookup off when set to `false` |
|
|
51
55
|
|
|
52
|
-
Set one Authentik credential. Precedence is `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` together with `STEEL_AUTH_TOKEN`. If only one of the user or token pair is set, configuration fails
|
|
56
|
+
Set one Authentik credential. Precedence is `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` together with `STEEL_AUTH_TOKEN`. Those three override `STEEL_API_KEY`. If only one of the user or token pair is set, configuration fails closed. When any Authentik variable is set, the legacy key is not sent.
|
|
53
57
|
|
|
54
58
|
> [!WARNING]
|
|
55
|
-
>
|
|
59
|
+
> `STEEL_API_KEY` is deprecated. Steel and Caddy do not enforce it. A client that still sends `x-steel-api-key` or `?apiKey=` is not authenticated. Migrate by setting one Authentik variable above and removing `STEEL_API_KEY`. Read the credential with `op read`. Never write it to disk, a URL, a prompt, or a log.
|
|
56
60
|
|
|
57
61
|
```bash
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
export
|
|
62
|
+
# Optional. This is already the default.
|
|
63
|
+
export STEEL_API_URL="https://steel.kontextmind.com"
|
|
64
|
+
# Vault kontextmind, item "Steel (svc-steel)", field basic_auth.
|
|
65
|
+
# The value stays in this process. Do not redirect it to a file.
|
|
66
|
+
export STEEL_AUTH_BASIC="$(op read 'op://kontextmind/Steel (svc-steel)/basic_auth')"
|
|
63
67
|
```
|
|
64
68
|
|
|
65
|
-
`
|
|
69
|
+
`STEEL_AUTH_USER` is `svc-steel` when you set the user and token pair instead of `STEEL_AUTH_BASIC`. Keep the value in the environment of the process that calls Steel.
|
|
66
70
|
|
|
67
71
|
| Endpoint | Purpose |
|
|
68
72
|
|---|---|
|
|
@@ -70,20 +74,29 @@ export USE_PASS_CLI=false
|
|
|
70
74
|
| `GET /v1/sessions/<id>` | Inspect one session; `GET /v1/sessions` lists them all |
|
|
71
75
|
| `POST /v1/sessions/<id>/release` | Release a session |
|
|
72
76
|
| `POST /v1/scrape`, `POST /v1/screenshot` | One-shot page fetch or screenshot without a session |
|
|
73
|
-
| `wss
|
|
77
|
+
| `wss://steel.kontextmind.com/v1/devtools?sessionId=<id>` | CDP path. Send `Authorization` on the handshake. The session `websocketUrl` is `wss://steel.kontextmind.com/` |
|
|
74
78
|
| `$STEEL_UI_URL?sessionId=<id>` | Session viewer for human takeover |
|
|
75
79
|
|
|
76
80
|
## Run a browser task
|
|
77
81
|
|
|
78
|
-
1. Define a helper that sends `Authorization` on stdin, so the secret never appears in a process list:
|
|
82
|
+
1. Define a helper that sends `Authorization` on stdin, so the secret never appears in a process list. `STEEL_API_URL` defaults to `https://steel.kontextmind.com` when unset:
|
|
79
83
|
|
|
80
84
|
```bash
|
|
81
85
|
steel() { # usage: steel METHOD PATH [JSON-BODY]
|
|
82
|
-
local
|
|
83
|
-
|
|
86
|
+
local header args
|
|
87
|
+
args=(-sS -X "$1" "${STEEL_API_URL:-https://steel.kontextmind.com}$2" -H @- -H 'Content-Type: application/json')
|
|
84
88
|
if [ -n "${3:-}" ]; then args+=(-d "$3"); fi
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
if [ -n "${STEEL_AUTH_HEADER:-}" ]; then
|
|
90
|
+
header="$STEEL_AUTH_HEADER"
|
|
91
|
+
elif [ -n "${STEEL_AUTH_BASIC:-}" ]; then
|
|
92
|
+
case "$STEEL_AUTH_BASIC" in
|
|
93
|
+
Basic\ *) header="$STEEL_AUTH_BASIC" ;;
|
|
94
|
+
*) header="Basic $STEEL_AUTH_BASIC" ;;
|
|
95
|
+
esac
|
|
96
|
+
else
|
|
97
|
+
header="Basic $(printf '%s:%s' "$STEEL_AUTH_USER" "$STEEL_AUTH_TOKEN" | base64 | tr -d '\n')"
|
|
98
|
+
fi
|
|
99
|
+
printf 'Authorization: %s\n' "$header" | curl "${args[@]}"
|
|
87
100
|
}
|
|
88
101
|
```
|
|
89
102
|
|
|
@@ -157,16 +170,18 @@ The KXM browser library tracks these states in the process that owns the session
|
|
|
157
170
|
|
|
158
171
|
- **Timeouts.** Sessions end on their own when the timeout expires. Set a longer timeout at creation when a person will need time for a login step.
|
|
159
172
|
- **Orphan sweeps.** List sessions with `steel GET /v1/sessions` and release any you do not track. The library flags untracked sessions older than 10 minutes, and tracked ones idle that long unless a human holds them.
|
|
160
|
-
- **Shared memory.** Give Chromium a large `/dev/shm
|
|
173
|
+
- **Shared memory.** Give Chromium a large `/dev/shm` on Steel LXC 240, or tabs crash.
|
|
161
174
|
- **No shared profiles.** Concurrent sessions must not write to the same browser profile.
|
|
162
175
|
|
|
163
176
|
## Troubleshooting
|
|
164
177
|
|
|
165
178
|
| Symptom | Cause | Fix |
|
|
166
179
|
|---|---|---|
|
|
167
|
-
| `302` to `id.kxmd.dev` | The request had no Authentik
|
|
168
|
-
| `401` or `403` | Wrong
|
|
169
|
-
|
|
|
180
|
+
| `302` to `id.kxmd.dev` | The request had no Authentik credential | Export `STEEL_AUTH_BASIC` from `op read`, or the user and token pair. A Bearer token is not accepted |
|
|
181
|
+
| `401` or `403` | Wrong `svc-steel` credential, or the account is outside the allowed groups | Re-read `basic_auth` with `op read`. `STEEL_API_KEY` does not authenticate |
|
|
182
|
+
| Connection refused on a LAN, tailnet, or host-forward address | Those paths are blocked | Use `https://steel.kontextmind.com` through Caddy |
|
|
183
|
+
| `websocketUrl` is `ws://steel-browser/` | The client is older than `kxm` 0.7.135 | Upgrade `kxm`. The server returns `wss://steel.kontextmind.com/` |
|
|
184
|
+
| Requests go to an unexpected host | `STEEL_API_URL` points somewhere else | Unset it to use `https://steel.kontextmind.com`, or set that URL |
|
|
170
185
|
| Playwright opens a local browser | The client called `chromium.launch()` or `chromium.connect()` | Use the worker-scoped fixture and `connectOverCDP` against Obscura |
|
|
171
186
|
| Steel CDP connects without auth | `connectOverCDP` was called with only the URL | Call `connectOverCDP(url, { headers })` with the headers from `formatCDPConnect()` or `connectBrowserOverCdp()` |
|
|
172
187
|
| `Access to private/internal IP address` | Obscura was started without `--allow-private-network` | Run `node scripts/obscura.mjs`, which passes that flag |
|
|
@@ -199,5 +214,6 @@ The KXM browser library tracks these states in the process that owns the session
|
|
|
199
214
|
|
|
200
215
|
- The seven browser skills: [Agent skills](agent-skills.md#browser-automation-skills)
|
|
201
216
|
- Why Obscura is the Playwright default: [ADR-0005](../adr/ADR-0005-obscura-default-playwright.md)
|
|
202
|
-
-
|
|
217
|
+
- Where Steel runs now: [ADR-0007](../adr/ADR-0007-steel-caddy-authentik.md)
|
|
218
|
+
- The superseded Kubernetes deployment: [ADR-0002](../adr/ADR-0002-browser-automation-steel-doks.md)
|
|
203
219
|
- Estimate the `browser` mode's prompt footprint: [`kxm explain`](../reference/cli-reference.md#kxm-explain)
|
|
@@ -11,7 +11,7 @@ updated: "2026-09-27"
|
|
|
11
11
|
authority: "instruction"
|
|
12
12
|
confidence: "verified"
|
|
13
13
|
summary: "How the Steel client resolves Authentik Basic auth and how browser work keeps secrets out of model context."
|
|
14
|
-
tags: ["browser", "credentials", "
|
|
14
|
+
tags: ["browser", "credentials", "1password", "security"]
|
|
15
15
|
related: ["docs/guides/browser-automation.md", "docs/guides/agent-skills.md"]
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -28,37 +28,36 @@ secret to disk or to a log:
|
|
|
28
28
|
|
|
29
29
|
| Setting | Resolved from |
|
|
30
30
|
|---|---|
|
|
31
|
-
| API URL | An explicit override, then `STEEL_API_URL`, then
|
|
31
|
+
| API URL | An explicit override, then `STEEL_API_URL`, then `https://steel.kontextmind.com` |
|
|
32
32
|
| Authentik `Authorization` | `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN` |
|
|
33
|
-
| Legacy API key | An explicit override, then `STEEL_API_KEY`, then a
|
|
33
|
+
| Legacy API key | Deprecated. An explicit override, then `STEEL_API_KEY`, then a leftover lookup, and only when no Authentik credential is set. Steel and Caddy do not enforce it |
|
|
34
34
|
| Session viewer URL | An explicit override, then `STEEL_UI_URL`, then `<api-url>/ui` |
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
`Authorization: Basic` on every HTTP request and on the CDP WebSocket
|
|
38
|
-
token is
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
key. When an Authentik variable is set, the key is not sent and is not placed
|
|
42
|
-
in a URL.
|
|
36
|
+
The Steel server is behind Caddy and Authentik forward auth. The client sends
|
|
37
|
+
`Authorization: Basic` on every HTTP request and on the CDP WebSocket at
|
|
38
|
+
`/v1/devtools`. A Bearer token is refused. A credential in the URL is refused.
|
|
39
|
+
`STEEL_API_KEY` is deprecated. When an Authentik variable is set, that variable
|
|
40
|
+
overrides the key, and the key is not sent.
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`USE_PASS_CLI=false` to turn the lookup off.
|
|
42
|
+
Read the `svc-steel` credential at runtime and do not write it to disk. The
|
|
43
|
+
1Password vault is `kontextmind`, the item is `Steel (svc-steel)`, and the
|
|
44
|
+
field is `basic_auth`:
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
```bash
|
|
47
|
+
export STEEL_AUTH_BASIC="$(op read 'op://kontextmind/Steel (svc-steel)/basic_auth')"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`kxm` 0.7.135 or newer is required. Sessions return `websocketUrl`
|
|
51
|
+
`wss://steel.kontextmind.com/`.
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
Pass through `pass-cli`, and load them into the environment of the process
|
|
53
|
-
that needs them:
|
|
53
|
+
## Mechanisms
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```
|
|
55
|
+
1. **One secret store.** Keep the Steel credential in 1Password and load it
|
|
56
|
+
into the environment of the process that needs it, as the `op read` command
|
|
57
|
+
above. Do not redirect the command into a file.
|
|
59
58
|
|
|
60
59
|
2. **References, not values.** A prompt receives a credential reference such
|
|
61
|
-
as `
|
|
60
|
+
as `op://kontextmind/Steel (svc-steel)/basic_auth`, never the secret itself.
|
|
62
61
|
3. **Log sanitization.** The KXM browser client redacts `apiKey=` query values,
|
|
63
62
|
`Authorization` header values, `Basic` credentials, `steel_…` keys, and any
|
|
64
63
|
field named like a key, secret, token, auth or password before it logs or
|
|
@@ -43,16 +43,19 @@ const { url, headers } = formatCDPConnect(
|
|
|
43
43
|
);
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
The URL has this shape
|
|
46
|
+
The URL has this shape. The session field `websocketUrl` is the origin
|
|
47
|
+
`wss://steel.kontextmind.com/` (it used to be `ws://steel-browser/`). The
|
|
48
|
+
connect path is `/v1/devtools`:
|
|
47
49
|
|
|
48
50
|
```text
|
|
49
|
-
wss
|
|
51
|
+
wss://steel.kontextmind.com/v1/devtools?sessionId=<session-id>
|
|
50
52
|
```
|
|
51
53
|
|
|
52
54
|
`headers` is `{ Authorization: "Basic <base64>" }`. Pass that object to
|
|
53
|
-
Playwright. Do not log it.
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
Playwright. Do not log it. `STEEL_API_KEY` is deprecated and is not enforced
|
|
56
|
+
by Steel or Caddy. Do not put `apiKey` on the URL. `STEEL_AUTH_HEADER`, then
|
|
57
|
+
`STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`, override it.
|
|
58
|
+
`kxm` 0.7.135 or newer is required.
|
|
56
59
|
|
|
57
60
|
## 2. Connect in Playwright
|
|
58
61
|
|
|
@@ -22,23 +22,24 @@ browser can keep running on your Steel deployment until its timeout.
|
|
|
22
22
|
|
|
23
23
|
## 1. List active sessions
|
|
24
24
|
|
|
25
|
-
Load
|
|
26
|
-
|
|
25
|
+
Load the `svc-steel` credential from 1Password into this process. Do not write
|
|
26
|
+
it to disk. `STEEL_API_URL` defaults to `https://steel.kontextmind.com`.
|
|
27
|
+
`STEEL_API_KEY` is deprecated and is not enforced.
|
|
27
28
|
|
|
28
29
|
```bash
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
printf 'Authorization:
|
|
30
|
+
export STEEL_AUTH_BASIC="$(op read 'op://kontextmind/Steel (svc-steel)/basic_auth')"
|
|
31
|
+
case "$STEEL_AUTH_BASIC" in
|
|
32
|
+
Basic\ *) header="$STEEL_AUTH_BASIC" ;;
|
|
33
|
+
*) header="Basic $STEEL_AUTH_BASIC" ;;
|
|
34
|
+
esac
|
|
35
|
+
printf 'Authorization: %s\n' "$header" | curl -sS -H @- "${STEEL_API_URL:-https://steel.kontextmind.com}/v1/sessions" | jq .
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
## 2. Release an orphaned session
|
|
38
39
|
|
|
39
40
|
```bash
|
|
40
|
-
printf 'Authorization:
|
|
41
|
-
| curl -sS -X POST -H @- "$STEEL_API_URL/v1/sessions/<session-id>/release"
|
|
41
|
+
printf 'Authorization: %s\n' "$header" \
|
|
42
|
+
| curl -sS -X POST -H @- "${STEEL_API_URL:-https://steel.kontextmind.com}/v1/sessions/<session-id>/release"
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
## 3. Sweep orphans with the KXM client
|
|
@@ -42,6 +42,7 @@ You expected automation to run on Obscura, or on a Steel takeover session, but a
|
|
|
42
42
|
opens a local browser.
|
|
43
43
|
- **Fix:** use `resolveObscuraCdpEndpoint()` (default
|
|
44
44
|
`http://127.0.0.1:9222`). For a Steel takeover session, set
|
|
45
|
-
`KXM_BROWSER=steel` and load `
|
|
46
|
-
`
|
|
47
|
-
|
|
45
|
+
`KXM_BROWSER=steel` and load `STEEL_AUTH_BASIC` with `op read` (or
|
|
46
|
+
`STEEL_AUTH_HEADER`, or `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`).
|
|
47
|
+
`STEEL_API_KEY` is deprecated. Steel and Caddy do not enforce it.
|
|
48
|
+
`kxm` 0.7.135 or newer is required for Steel.
|
|
@@ -279,6 +279,35 @@ server {
|
|
|
279
279
|
}
|
|
280
280
|
```
|
|
281
281
|
|
|
282
|
+
## Boot the kxmd Proxmox host
|
|
283
|
+
|
|
284
|
+
The Proxmox host starts these guests in the order below. The VMID is the Proxmox guest id. `up` is the delay, in seconds, after that guest starts.
|
|
285
|
+
|
|
286
|
+
| Order | Guest | VMID | Role | up |
|
|
287
|
+
|---|---|---|---|---|
|
|
288
|
+
| 1 | firewall | 200 | Firewall | |
|
|
289
|
+
| 2 | gateway | 201 | Gateway | |
|
|
290
|
+
| 3 | kxmd-pg | 220 | Postgres | 30 |
|
|
291
|
+
| 4 | kxmd-temporal | 221 | Temporal | 40 |
|
|
292
|
+
| 5 | kxmd-services | 300 | `kxm-control` | 30 |
|
|
293
|
+
| 6 | kxmd-studio | 241 | Hub | 20 |
|
|
294
|
+
|
|
295
|
+
VM 300 is named `kxmd-services`. VM 230 is named `kxmd-proxy` and runs Caddy. Steel is LXC 240 and uses Proxmox startup order 40.
|
|
296
|
+
|
|
297
|
+
Steel's public name is `steel.kontextmind.com`. `steel.theneuro.me` is an alias of that same server. Clients reach it only through Caddy and Authentik forward auth. Direct LAN, tailnet, and host-forward access is blocked. See [ADR-0007](../adr/ADR-0007-steel-caddy-authentik.md) and [Browser automation](../guides/browser-automation.md).
|
|
298
|
+
|
|
299
|
+
## Name machine accounts
|
|
300
|
+
|
|
301
|
+
New Authentik machine accounts follow [ADR-0006](../adr/ADR-0006-machine-account-names.md):
|
|
302
|
+
|
|
303
|
+
| Scope | Name |
|
|
304
|
+
|---|---|
|
|
305
|
+
| Platform-wide | `svc-<system>-<purpose>` |
|
|
306
|
+
| Tenant-scoped | `svc-<tenant>-<system>-<purpose>` |
|
|
307
|
+
| Test, witness, or proof | The same shapes with a `test-` prefix |
|
|
308
|
+
|
|
309
|
+
`<tenant>` is the tenant slug. A `test-` account is never in a production group. Outposts and `ak-*` accounts are Authentik-managed and are exempt. Names already in use, including `kxm-agent`, `kxm-witness-*`, `witness9`, and `kxmdproof`, wait for an approved inventory. Do not invent a new name for one of them. The live Steel account remains `svc-steel`.
|
|
310
|
+
|
|
282
311
|
## Plan for scale
|
|
283
312
|
|
|
284
313
|
Measure concurrent agents, request rate, event-loop delay, database size, disk latency and reconnect frequency for your workload. Terminal messages are purged after `KXM_MESSAGE_RETENTION_MS` (7 days by default), and finished workflow runs and their journals after 7 days. Keep free disk space ahead of the database's growth, and include workflow data in privacy reviews: verified evidence snapshots stay in a run after its source messages are purged.
|
|
@@ -304,4 +333,6 @@ See [Troubleshoot KXM](troubleshooting.md) for more.
|
|
|
304
333
|
- Move to a new release: [Upgrade KXM](upgrade.md)
|
|
305
334
|
- Keep run facts flowing to the hub: [Operate Runtime sync and leases](runtime-sync.md)
|
|
306
335
|
- Understand who can do what: [Trust model](../concepts/trust-model.md)
|
|
336
|
+
- Name machine accounts: [ADR-0006](../adr/ADR-0006-machine-account-names.md)
|
|
337
|
+
- Reach Steel: [ADR-0007](../adr/ADR-0007-steel-caddy-authentik.md)
|
|
307
338
|
- Every hub route: [Hub HTTP API reference](../reference/http-api.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Troubleshoot KXM
|
|
2
2
|
|
|
3
|
-
This page is a reference of symptoms, causes and fixes, grouped by area. Start with the quick check, then go to the area that matches: install, hub and authentication, Claude Code, peer messaging, Pi workers, workflows, Runtime runs, context and memory, or operations.
|
|
3
|
+
This page is a reference of symptoms, causes and fixes, grouped by area. Start with the quick check, then go to the area that matches: install, hub and authentication, Claude Code, peer messaging, Pi workers, workflows, Runtime runs, context and memory, Steel, or operations.
|
|
4
4
|
|
|
5
5
|
## Start with a quick check
|
|
6
6
|
|
|
@@ -239,6 +239,26 @@ See [Context and memory](../guides/context-and-memory.md).
|
|
|
239
239
|
|
|
240
240
|
For backup and restore errors such as `backup_no_stores`, see [Back up and restore KXM](backup-and-restore.md#troubleshooting). The dashboard's action keys do not act on runs; see [Monitor KXM](monitoring.md#keys).
|
|
241
241
|
|
|
242
|
+
## Steel
|
|
243
|
+
|
|
244
|
+
Steel is `https://steel.kontextmind.com` (`steel.theneuro.me` is an alias). Caddy and Authentik forward auth are the only path. Playwright tests use Obscura. The procedure is [Browser automation](../guides/browser-automation.md). `kxm` 0.7.135 or newer is required.
|
|
245
|
+
|
|
246
|
+
| Symptom | Cause | Fix |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| Connection refused or timeout on a LAN, tailnet, or host-forward address | Direct access to LXC 240 is blocked | Use `https://steel.kontextmind.com` |
|
|
249
|
+
| `302` to `id.kxmd.dev` | No Authentik credential on the request | Set `STEEL_AUTH_HEADER`, or `STEEL_AUTH_BASIC`, or `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN` |
|
|
250
|
+
| `401` or `403` | The `svc-steel` credential is wrong, or the account is outside `steel-users`, `kxmd-users`, `kxmd-admins`, and `kxmd-owners` | Re-read the credential with `op read`. `STEEL_API_KEY` is not enforced |
|
|
251
|
+
| `websocketUrl` is `ws://steel-browser/` | The client predates `kxm` 0.7.135 | Upgrade `kxm`. The server returns `wss://steel.kontextmind.com/` |
|
|
252
|
+
| CDP fails and the URL contains `apiKey` | `STEEL_API_KEY` was placed on `/v1/devtools` | Connect to `/v1/devtools` with an `Authorization` header and no credential in the URL |
|
|
253
|
+
|
|
254
|
+
Read the credential into the process only:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
export STEEL_AUTH_BASIC="$(op read 'op://kontextmind/Steel (svc-steel)/basic_auth')"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The vault is `kontextmind`, the item is `Steel (svc-steel)`, and the field is `basic_auth`. Do not write that value to disk.
|
|
261
|
+
|
|
242
262
|
## Collect a useful bug report
|
|
243
263
|
|
|
244
264
|
Include:
|
|
@@ -263,4 +283,6 @@ Never attach tokens, private prompts, credentials, raw `pi-agent-*.log` files, o
|
|
|
263
283
|
- [Monitor KXM](monitoring.md)
|
|
264
284
|
- [Deploy KXM](deploy.md)
|
|
265
285
|
- [CLI reference](../reference/cli-reference.md)
|
|
286
|
+
- [Browser automation](../guides/browser-automation.md)
|
|
287
|
+
- [Steel through Caddy and Authentik](../adr/ADR-0007-steel-caddy-authentik.md)
|
|
266
288
|
- [Claude Code plugin](../../plugins/kxm/README.md)
|
|
@@ -52,5 +52,5 @@ Use this prompt to troubleshoot unresponsive sessions, CDP attachment errors, au
|
|
|
52
52
|
- For each orphan, invoke `POST /v1/sessions/:id/release`.
|
|
53
53
|
|
|
54
54
|
4. **Verify WebSocket / CDP Ingress**:
|
|
55
|
-
-
|
|
56
|
-
- If the response is a 302 to `id.kxmd.dev`, or CDP fails with 401, send `Authorization: Basic` from `STEEL_AUTH_BASIC
|
|
55
|
+
- Steel is reached only through Caddy and Authentik forward auth. Direct LAN, tailnet, and host-forward connections are blocked. The CDP path is `/v1/devtools` with an `Authorization` header.
|
|
56
|
+
- If the response is a 302 to `id.kxmd.dev`, or CDP fails with 401 or 403, send `Authorization: Basic` from `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`. A Bearer token is not accepted. Do not put the credential in the URL. `STEEL_API_KEY` is deprecated. Steel and Caddy do not enforce it. Sessions return `websocketUrl` `wss://steel.kontextmind.com/`. `kxm` 0.7.135 or newer is required.
|
|
@@ -42,7 +42,7 @@ Use this prompt to initialize a remote browser session on self-hosted Steel for
|
|
|
42
42
|
## Instructions for the agent
|
|
43
43
|
|
|
44
44
|
1. **Verify Credential Reference**:
|
|
45
|
-
- Resolve the
|
|
45
|
+
- Resolve the `svc-steel` credential with `op read 'op://kontextmind/Steel (svc-steel)/basic_auth'` into `STEEL_AUTH_BASIC` (or set `STEEL_AUTH_HEADER`, or `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`). Do not log the value or write it to disk. A Bearer token is not accepted. `STEEL_API_KEY` is deprecated and is not enforced. Keep the credential in a header, not a URL. `STEEL_API_URL` defaults to `https://steel.kontextmind.com`. `kxm` 0.7.135 or newer is required.
|
|
46
46
|
- Do not print credentials to the chat or save them to tracked files.
|
|
47
47
|
|
|
48
48
|
2. **Launch Remote Steel Session**:
|