@kontextmind/kxm 0.7.146 → 0.7.148

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 (40) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +38 -3
  3. package/docs/README.md +1 -1
  4. package/docs/adr/ADR-0002-browser-automation-steel-doks.md +8 -5
  5. package/docs/adr/ADR-0005-obscura-default-playwright.md +4 -3
  6. package/docs/adr/ADR-0006-machine-account-names.md +90 -0
  7. package/docs/adr/ADR-0007-steel-caddy-authentik.md +97 -0
  8. package/docs/adr/README.md +3 -1
  9. package/docs/contributing/ci-and-release.md +80 -43
  10. package/docs/contributing/operating-rules.md +13 -1
  11. package/docs/guides/agent-skills.md +1 -1
  12. package/docs/guides/browser-automation.md +44 -28
  13. package/docs/kb/how-credentials-retrieved-safely.md +22 -23
  14. package/docs/kb/how-to-connect-playwright-to-steel.md +8 -5
  15. package/docs/kb/how-to-recover-expired-session-or-orphan.md +11 -10
  16. package/docs/kb/why-automation-opened-different-browser.md +4 -3
  17. package/docs/operations/deploy.md +31 -0
  18. package/docs/operations/troubleshooting.md +23 -1
  19. package/docs/prompts/browser-diagnose-recover.md +2 -2
  20. package/docs/prompts/browser-start.md +1 -1
  21. package/docs/reference/configuration.md +9 -7
  22. package/package.json +2 -1
  23. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  24. package/plugins/kxm/dist/claude-hook.js +1 -1
  25. package/plugins/kxm/dist/cli.js +2 -2
  26. package/plugins/kxm/dist/extension.js +1 -1
  27. package/plugins/kxm/dist/mcp-server.js +1 -1
  28. package/plugins/kxm/dist/runtime.js +3 -3
  29. package/plugins/kxm/package.json +1 -1
  30. package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +11 -12
  31. package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +5 -4
  32. package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +4 -3
  33. package/plugins/kxm/skills/kxm-browser-session/SKILL.md +5 -5
  34. package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +1 -1
  35. package/plugins/kxm/src/browser.ts +12 -9
  36. package/plugins/kxm/src/mcp-server.ts +1 -1
  37. package/plugins/kxm/src/modes.ts +1 -1
  38. package/plugins/kxm/src/session-work.ts +3 -1
  39. package/scripts/ci-classify.mjs +69 -0
  40. package/scripts/ci-unit-shard.mjs +230 -0
@@ -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 takeover: a Steel deployment you operate, reachable over HTTPS, and an Authentik app password when that host is behind forward auth. [ADR-0002](../adr/ADR-0002-browser-automation-steel-doks.md) describes the reference deployment on Kubernetes.
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
- - A secret manager for the Authentik app password and site credentials. The bundled skills use `pass-cli`.
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
- KontextMind's Steel hosts (`steel.kontextmind.com` and `steel.theneuro.me`, including the `wss://` CDP endpoint) sit behind Authentik forward auth at the reverse proxy. Steel itself does not check an API key. Unauthenticated requests receive a 302 redirect to the Authentik login at `id.kxmd.dev`. Authentik accepts an app password only as `Authorization: Basic`. A Bearer token is refused.
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` | A KontextMind-operated deployment | Base URL of your Steel API. Always set it |
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. Wins over the other auth variables |
46
- | `STEEL_AUTH_BASIC` | unset | `base64(user:token)`, with or without a leading `Basic` prefix. Sent as `Authorization: Basic` |
47
- | `STEEL_AUTH_USER` | unset | Authentik username, for example `svc-steel`. Used with `STEEL_AUTH_TOKEN` |
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` | A `pass-cli` lookup | Deprecated. Sent as `x-steel-api-key` and as `?apiKey=` on the CDP URL, which the proxy still accepts as a temporary shim. The library warns once on stderr |
50
- | `USE_PASS_CLI` | enabled | Set to `false` to disable the legacy `STEEL_API_KEY` `pass-cli` fallback |
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 instead of falling back to the legacy key. When any of those are set, the legacy key is not sent and is not placed in a URL. The `pass-cli` fallback looks up `STEEL_API_KEY` only.
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
- > Set `STEEL_API_URL` and an Authentik credential explicitly. Without them the library falls back to KontextMind's own deployment and vault item, which are not yours to use. Never put the credential in a URL, a prompt, or a log.
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
- export STEEL_API_URL="https://steel.example.com"
59
- # Read the app password from your secret manager; <vault> and <item> are yours.
60
- export STEEL_AUTH_USER="svc-steel"
61
- export STEEL_AUTH_TOKEN="$(pass-cli item view --vault-name '<vault>' --item-title '<item>' --field password)"
62
- export USE_PASS_CLI=false
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
- `STEEL_AUTH_BASIC` is the same pair already encoded: `printf '%s:%s' "$STEEL_AUTH_USER" "$STEEL_AUTH_TOKEN" | base64 | tr -d '\n'`. Prefer the user and token pair, or the pre-encoded value, and keep them in the environment of the process that calls Steel.
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://<steel-host>/v1/devtools?sessionId=<id>` | CDP endpoint. Send `Authorization` on the WebSocket handshake; do not add the credential to this URL |
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 basic
83
- local args=(-sS -X "$1" "$STEEL_API_URL$2" -H @- -H 'Content-Type: application/json')
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
- basic="$(printf '%s:%s' "$STEEL_AUTH_USER" "$STEEL_AUTH_TOKEN" | base64 | tr -d '\n')"
86
- printf 'Authorization: Basic %s\n' "$basic" | curl "${args[@]}"
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`, or tabs crash. On Kubernetes, mount a memory-backed `emptyDir` there; the reference deployment uses 2 GiB.
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 app password | Export `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`, or `STEEL_AUTH_BASIC`. A Bearer token is not accepted |
168
- | `401` or `403` | Wrong app password, or a legacy key the shim rejected | Re-export the Authentik credential from your secret manager. Re-export `STEEL_API_KEY` only when that is the credential you still use |
169
- | Requests go to an unexpected host | `STEEL_API_URL` is unset | Export it before starting the agent |
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
- - Why Steel, and the reference deployment: [ADR-0002](../adr/ADR-0002-browser-automation-steel-doks.md)
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", "pass-cli", "security"]
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 a built-in default |
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 `pass-cli` lookup, and only when no Authentik credential is set |
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
- KontextMind's Steel hosts are behind Authentik forward auth. The client sends
37
- `Authorization: Basic` on every HTTP request and on the CDP WebSocket. A Bearer
38
- token is not accepted. The legacy key is sent as `x-steel-api-key` and as an
39
- `apiKey` query parameter, which the proxy still accepts as a temporary shim.
40
- The library writes one deprecation warning to stderr and does not print the
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
- The built-in default URL and the `pass-cli` lookup point at the maintainers'
45
- own Steel deployment and vault. The lookup reads `STEEL_API_KEY` only. Set
46
- `STEEL_API_URL` and an Authentik credential for yours, and set
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
- ## Mechanisms
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
- 1. **One secret store.** Keep secrets in a secret manager, for example Proton
52
- Pass through `pass-cli`, and load them into the environment of the process
53
- that needs them:
53
+ ## Mechanisms
54
54
 
55
- ```bash
56
- # Runs the tests with secrets injected for this process only.
57
- pass-cli run -- npm test
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 `vault: "<vault>", item: "<item>"`, never the secret itself.
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://<steel-host>/v1/devtools?sessionId=<session-id>
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. A legacy `STEEL_API_KEY` still appends `apiKey` to
54
- the URL for the temporary proxy shim; prefer the Authentik variables so the
55
- credential stays out of the URL.
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 `STEEL_API_URL`, `STEEL_AUTH_USER`, and `STEEL_AUTH_TOKEN` from your secret
26
- manager first. With `pass-cli`, for example:
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 STEEL_API_URL="https://<steel-host>"
30
- export STEEL_AUTH_USER="svc-steel"
31
- export STEEL_AUTH_TOKEN="$(pass-cli item view --vault-name "<vault>" --item-title "<item>" --field password)"
32
- basic="$(printf '%s:%s' "$STEEL_AUTH_USER" "$STEEL_AUTH_TOKEN" | base64 | tr -d '\n')"
33
-
34
- printf 'Authorization: Basic %s\n' "$basic" | curl -sS -H @- "$STEEL_API_URL/v1/sessions" | jq .
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: Basic %s\n' "$basic" \
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 `STEEL_API_URL` plus `STEEL_AUTH_USER` and
46
- `STEEL_AUTH_TOKEN` (or `STEEL_AUTH_BASIC`). A legacy `STEEL_API_KEY` still
47
- works through the proxy shim and warns once.
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
- - Ensure the reverse proxy or ingress in front of Steel passes WebSocket upgrades through. On Kubernetes with ingress-nginx, that is the `nginx.ingress.kubernetes.io/websocket-services` annotation.
56
- - If the response is a 302 to `id.kxmd.dev`, or CDP fails with 401, send `Authorization: Basic` from `STEEL_AUTH_BASIC` or from `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`. A Bearer token is not accepted. Do not put the credential in the URL. The legacy `x-steel-api-key` header and `?apiKey=` query parameter remain only for the temporary proxy shim.
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 target credentials and the Steel Authentik app password (`STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`, or `STEEL_AUTH_BASIC`) from the secret manager, for example with `pass-cli`, without logging raw values. A Bearer token is not accepted. Keep the credential in a header, not a URL.
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**:
@@ -200,21 +200,23 @@ The Runtime supervisor runs `kxm run` workflows and syncs their summaries to the
200
200
 
201
201
  ## Browser automation
202
202
 
203
- Playwright testing and verification use Obscura. Steel is for human takeover, MFA, and the live session viewer. `resolveBrowserCdpEndpoint()` in `plugins/kxm/src/browser.ts` returns the CDP URL. `resolveBrowserCdpConnect()` returns that URL and the handshake headers, and `connectBrowserOverCdp()` passes them to `chromium.connectOverCDP`. `node scripts/obscura.mjs` downloads pinned Obscura v0.2.3 and serves it. See [Browser automation](../guides/browser-automation.md).
203
+ Playwright testing and verification use Obscura. Steel is for remote and hosted browsing, human takeover, MFA, and the live session viewer. `resolveBrowserCdpEndpoint()` in `plugins/kxm/src/browser.ts` returns the CDP URL. `resolveBrowserCdpConnect()` returns that URL and the handshake headers, and `connectBrowserOverCdp()` passes them to `chromium.connectOverCDP`. `node scripts/obscura.mjs` downloads pinned Obscura v0.2.3 and serves it. Steel needs `kxm` 0.7.135 or newer. See [Browser automation](../guides/browser-automation.md) and [ADR-0007](../adr/ADR-0007-steel-caddy-authentik.md).
204
204
 
205
205
  | Variable | Default | Effect |
206
206
  |---|---|---|
207
- | `KXM_BROWSER` | `obscura` | `obscura` selects the Obscura CDP URL and sends no Steel headers. `steel` selects `formatCDPConnect()` (the session URL plus `Authorization`, or the legacy API-key header) and requires a session id. Any other value throws |
207
+ | `KXM_BROWSER` | `obscura` | `obscura` selects the Obscura CDP URL and sends no Steel headers. `steel` selects `/v1/devtools` plus `Authorization` and requires a session id. Any other value throws |
208
208
  | `OBSCURA_CDP_URL` | `http://127.0.0.1:${OBSCURA_PORT:-9222}` | CDP URL passed to `chromium.connectOverCDP()`. When set, it wins over `OBSCURA_PORT` for that URL |
209
209
  | `OBSCURA_PORT` | `9222` | TCP port for `obscura serve`. Used in the default CDP URL when `OBSCURA_CDP_URL` is unset. The launcher refuses to start when this port disagrees with the port in `OBSCURA_CDP_URL` |
210
- | `STEEL_API_URL` | A KontextMind-operated deployment | Base URL of your Steel API. Set it before using `KXM_BROWSER=steel` |
210
+ | `STEEL_API_URL` | `https://steel.kontextmind.com` | Base URL of the Steel API. `steel.theneuro.me` is an alias of that server |
211
211
  | `STEEL_UI_URL` | `$STEEL_API_URL/ui` | Base URL of the Steel session viewer |
212
- | `STEEL_AUTH_HEADER` | unset | Full `Authorization` value. Wins over the other Steel auth variables |
212
+ | `STEEL_AUTH_HEADER` | unset | Full `Authorization` value. First among the Steel auth variables |
213
213
  | `STEEL_AUTH_BASIC` | unset | `base64(user:token)`, with or without a leading `Basic` prefix. Sent as `Authorization: Basic` |
214
- | `STEEL_AUTH_USER` | unset | Authentik username. Used with `STEEL_AUTH_TOKEN`. An incomplete pair fails closed |
214
+ | `STEEL_AUTH_USER` | unset | Authentik username `svc-steel`. Used with `STEEL_AUTH_TOKEN`. An incomplete pair fails closed |
215
215
  | `STEEL_AUTH_TOKEN` | unset | Authentik app password. Used with `STEEL_AUTH_USER` |
216
- | `STEEL_API_KEY` | A `pass-cli` lookup | Deprecated. Sent as `x-steel-api-key` and as `?apiKey=` on the CDP URL for the temporary proxy shim. The library warns once. Not sent when an Authentik variable is set |
217
- | `USE_PASS_CLI` | enabled | Set to `false` to disable the legacy `STEEL_API_KEY` `pass-cli` fallback |
216
+ | `STEEL_API_KEY` | unset | Deprecated. Steel and Caddy do not enforce it. The three variables above override it. The library warns once |
217
+ | `USE_PASS_CLI` | enabled | Set to `false` to skip the legacy `STEEL_API_KEY` lookup. That lookup does not authenticate |
218
+
219
+ `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` with `STEEL_AUTH_TOKEN`, override `STEEL_API_KEY`. Load field `basic_auth` from 1Password vault `kontextmind`, item `Steel (svc-steel)`, with `op read` into `STEEL_AUTH_BASIC`. Do not write the value to disk. The CDP path is `/v1/devtools` with an `Authorization` header. Sessions return `websocketUrl` `wss://steel.kontextmind.com/`.
218
220
 
219
221
  Obscura's own `OBSCURA_TIMEZONE` (default `Europe/Berlin`) and `OBSCURA_CDP_TOKEN` (required only for a non-loopback bind) are read by the Obscura process, not by KXM.
220
222
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.146",
3
+ "version": "0.7.148",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -38,6 +38,7 @@
38
38
  "test:simulations": "npm run build && node scripts/run-bounded.mjs 1200000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/simulations/*.test.ts",
39
39
  "test:complete": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/core/*.test.ts test/simulations/*.test.ts",
40
40
  "test": "npm run build && node scripts/run-bounded.mjs 1200000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/core/*.test.ts packages/core/*/tests/unit/*.test.ts",
41
+ "test:ci-shard": "node scripts/ci-unit-shard.mjs",
41
42
  "e2e": "node scripts/obscura.mjs --ensure && playwright test",
42
43
  "test:coverage:core": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 --experimental-test-coverage --test-coverage-lines=91 --test-coverage-branches=80 --test-coverage-functions=92 --test-coverage-include=plugins/kxm/src/**/*.ts --test-coverage-exclude=plugins/kxm/src/server.ts --test-coverage-exclude=plugins/kxm/src/mcp-server.ts --test-coverage-exclude=plugins/kxm/src/runtime-supervisor.ts --test-coverage-include=packages/core/*/src/**/*.ts test/core/*.test.ts packages/core/*/tests/unit/*.test.ts",
43
44
  "test:coverage:complete": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 --experimental-test-coverage --test-coverage-lines=93 --test-coverage-branches=80 --test-coverage-functions=93 --test-coverage-include=plugins/kxm/src/**/*.ts --test-coverage-exclude=plugins/kxm/src/server.ts --test-coverage-exclude=plugins/kxm/src/mcp-server.ts --test-coverage-exclude=plugins/kxm/src/runtime-supervisor.ts --test-coverage-include=packages/core/*/src/**/*.ts test/core/*.test.ts test/simulations/*.test.ts packages/core/*/tests/unit/*.test.ts",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.146",
5
+ "version": "0.7.148",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",
@@ -724,7 +724,7 @@ function formatShipLine(ship) {
724
724
  }
725
725
  function readGitShip(cwd) {
726
726
  try {
727
- const dirty = spawnSync("git", ["-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
727
+ const dirty = spawnSync("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
728
728
  if (dirty.status !== 0) return void 0;
729
729
  const isDirty = dirty.stdout.trim().length > 0;
730
730
  const upstream = spawnSync("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
@@ -50095,7 +50095,7 @@ function formatShipLine(ship) {
50095
50095
  }
50096
50096
  function readGitShip(cwd) {
50097
50097
  try {
50098
- const dirty = spawnSync10("git", ["-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
50098
+ const dirty = spawnSync10("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
50099
50099
  if (dirty.status !== 0) return void 0;
50100
50100
  const isDirty = dirty.stdout.trim().length > 0;
50101
50101
  const upstream = spawnSync10("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
@@ -51929,7 +51929,7 @@ var DEFAULT_MODES_CONFIG = Object.freeze({
51929
51929
  browser: {
51930
51930
  description: "Remote Steel browser sessions and visual testing",
51931
51931
  tools: ["steel_session", "steel_scrape", "steel_screenshot"],
51932
- promptSnippet: "Use Steel on DOKS for browser automation; invoke takeover on MFA."
51932
+ promptSnippet: "Playwright tests use Obscura. Steel is remote browsing and takeover through Caddy and Authentik; send Authorization, never a credential in the URL."
51933
51933
  }
51934
51934
  }
51935
51935
  });
@@ -37099,7 +37099,7 @@ function formatShipLine(ship) {
37099
37099
  }
37100
37100
  function readGitShip(cwd) {
37101
37101
  try {
37102
- const dirty = spawnSync("git", ["-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
37102
+ const dirty = spawnSync("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
37103
37103
  if (dirty.status !== 0) return void 0;
37104
37104
  const isDirty2 = dirty.stdout.trim().length > 0;
37105
37105
  const upstream = spawnSync("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
@@ -17313,7 +17313,7 @@ function sessionTokenFixHint(policy) {
17313
17313
  }
17314
17314
 
17315
17315
  // plugins/kxm/src/mcp-server.ts
17316
- var VERSION = "0.7.146";
17316
+ var VERSION = "0.7.148";
17317
17317
  var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
17318
17318
  var inbox = /* @__PURE__ */ new Map();
17319
17319
  var notifiedInbox = /* @__PURE__ */ new Set();
@@ -33751,7 +33751,7 @@ var SteelAuthRedirectError = class extends Error {
33751
33751
  this.host = host;
33752
33752
  }
33753
33753
  };
33754
- var LEGACY_STEEL_AUTH_WARNING = "kxm: STEEL_API_KEY is deprecated for Steel. Authentik forward auth accepts app passwords only as Authorization: Basic. Set STEEL_AUTH_BASIC, or STEEL_AUTH_USER and STEEL_AUTH_TOKEN. The legacy x-steel-api-key header and apiKey query parameter remain for the temporary proxy shim.\n";
33754
+ var LEGACY_STEEL_AUTH_WARNING = "kxm: STEEL_API_KEY is deprecated for Steel. Steel and Caddy do not enforce it. Set STEEL_AUTH_HEADER, or STEEL_AUTH_BASIC, or STEEL_AUTH_USER and STEEL_AUTH_TOKEN. Those override STEEL_API_KEY. Send Authorization on the request, not in the URL.\n";
33755
33755
  var legacySteelAuthWarned = false;
33756
33756
  function resetLegacySteelAuthWarningForTests() {
33757
33757
  legacySteelAuthWarned = false;
@@ -34057,7 +34057,7 @@ var SteelClient = class {
34057
34057
  return res;
34058
34058
  }
34059
34059
  /**
34060
- * Launch a new Steel browser session on DOKS.
34060
+ * Launch a new Steel browser session.
34061
34061
  */
34062
34062
  async createSession(options) {
34063
34063
  const timeoutMs = options?.timeoutMs ?? this.config.timeoutMs ?? 3e5;
@@ -34340,7 +34340,7 @@ var DEFAULT_MODES_CONFIG = Object.freeze({
34340
34340
  browser: {
34341
34341
  description: "Remote Steel browser sessions and visual testing",
34342
34342
  tools: ["steel_session", "steel_scrape", "steel_screenshot"],
34343
- promptSnippet: "Use Steel on DOKS for browser automation; invoke takeover on MFA."
34343
+ promptSnippet: "Playwright tests use Obscura. Steel is remote browsing and takeover through Caddy and Authentik; send Authorization, never a credential in the URL."
34344
34344
  }
34345
34345
  }
34346
34346
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.146",
3
+ "version": "0.7.148",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,38 +1,37 @@
1
1
  ---
2
2
  name: kxm-browser-auth
3
- description: Retrieve application credentials and manage authenticated browser profiles safely via pass-cli without secret exposure.
3
+ description: Retrieve application credentials and manage authenticated browser profiles without secret exposure. Steel uses the svc-steel credential from 1Password via op read.
4
4
  ---
5
5
 
6
6
  # KXM Browser Credentials & Authenticated Profiles
7
7
 
8
- Use this skill to retrieve target application credentials and manage browser session state securely using `pass-cli` as the sole authoritative store.
8
+ Use this skill to retrieve target application credentials and manage browser session state. The Steel `svc-steel` credential comes from 1Password and is read with `op read`. It is never written to disk.
9
9
 
10
10
  ## Purpose & Scope
11
11
 
12
- - Enforce `pass-cli` as the single source of truth for credentials and API keys.
12
+ - Read the Steel credential at runtime from 1Password.
13
13
  - Prevent secrets from leaking into git repositories, logs, prompts, or model-visible tool outputs.
14
14
  - Support safe storage and retrieval of session storage state and authenticated profiles.
15
15
 
16
16
  ## Credential Retrieval Guidelines
17
17
 
18
- ### 1. Authoritative Tool: pass-cli
18
+ ### 1. Steel credential: 1Password
19
19
 
20
- Always retrieve credentials and API keys directly from `pass-cli`:
20
+ `STEEL_API_KEY` is deprecated. Steel and Caddy do not enforce it. Authenticate as `svc-steel`. Precedence is `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`. Those override `STEEL_API_KEY`. `STEEL_API_URL` defaults to `https://steel.kontextmind.com`. `kxm` 0.7.135 or newer is required.
21
21
 
22
22
  ```bash
23
- # Retrieve target login password into an environment variable or piping mechanism
24
- pass-cli item view --vault-name "<vault>" --item-title "<title>" --field password
25
-
26
- # Retrieve the Authentik app password for Steel. The proxy accepts Authorization: Basic.
27
- # STEEL_AUTH_USER is the Authentik username (for example svc-steel).
28
- pass-cli item view --vault-name "<vault>" --item-title "<steel-item>" --field password
23
+ # Vault kontextmind, item "Steel (svc-steel)", field basic_auth.
24
+ # Do not redirect this into a file.
25
+ export STEEL_AUTH_BASIC="$(op read 'op://kontextmind/Steel (svc-steel)/basic_auth')"
29
26
  ```
30
27
 
28
+ `STEEL_AUTH_USER` is `svc-steel` when you use the user and token pair. Send the value as `Authorization` on `/v1/devtools`. Never put it in the URL.
29
+
31
30
  ### 2. Secret Redaction Invariants
32
31
 
33
32
  - **Never** write plain passwords, session tokens, or API keys into markdown docs, commit messages, or prompts.
34
33
  - **Never** pass plain credentials as unredacted command line arguments in shared logs.
35
- - Use environment variable injection (`pass-cli run`) or direct in-memory pipes.
34
+ - Keep the `op read` result in the environment of the process that calls Steel.
36
35
 
37
36
  ### 3. Profile & Storage State Management
38
37