@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
package/.kxm/README.md
CHANGED
|
@@ -26,8 +26,26 @@ a `default` workflow, `gates.yaml` and `template-provenance.yaml`.
|
|
|
26
26
|
| `run/` | SSH control sockets from `kxm ssh` | Ignored |
|
|
27
27
|
|
|
28
28
|
The role files under `.kxm/roles/` and the model files under `.kxm/models/`
|
|
29
|
-
carry the developer policy. The assignment runner reads them at
|
|
30
|
-
`refs/remotes/origin/main
|
|
29
|
+
carry the developer policy. The assignment runner reads them at one commit
|
|
30
|
+
(`HEAD`, which must be an ancestor of `refs/remotes/origin/main`). A rename
|
|
31
|
+
does not rewrite a commit that still uses the old ids. An old id resolves
|
|
32
|
+
only when that id is absent and the other side is present.
|
|
33
|
+
|
|
34
|
+
Ids in this checkout follow one convention per kind:
|
|
35
|
+
|
|
36
|
+
| Kind | Convention | This checkout |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| Role | The purpose: `planner`, `writer`, `reviewer-arch`, `reviewer-cli` | Unchanged |
|
|
39
|
+
| Agent | The same id as its `role` | `planner`, `writer`, `reviewer-arch`, `reviewer-cli` |
|
|
40
|
+
| Route | `<harness>-<model-slug>[-<provider>]`, with `.` written as `-` | `grok-grok-4-7`, `claude-fable`, `pi-qwen3-coder-plus-openrouter` |
|
|
41
|
+
| Workflow | `default`, `land`, or `<role>-only` | `default`, `land`, `writer-only`, `reviewer-arch-only`, `reviewer-cli-only` |
|
|
42
|
+
| Agent step | The agent's role id | `writer`, `reviewer-arch`, `reviewer-cli` |
|
|
43
|
+
|
|
44
|
+
`coordinator`, `implementer`, `critic-arch`, and `critic-cli` are aliases of
|
|
45
|
+
the agent ids. `implement`, `review-arch`, and `review-cli` are aliases of
|
|
46
|
+
the step ids. Route aliases include `grok-native`, `fable-claude`, and
|
|
47
|
+
`sol-codex`. Using an alias prints `kxm: deprecated <kind> id '<from>' resolves to '<to>'`.
|
|
48
|
+
`opus-claude` does not resolve to another model.
|
|
31
49
|
|
|
32
50
|
The [configuration reference](../docs/reference/config-reference.md#workspace-layout-tracked-ignored-and-state)
|
|
33
51
|
describes every file, the ignore rules to add, and the state KXM keeps outside
|
package/.kxm/roles/planner.yaml
CHANGED
|
@@ -3,7 +3,9 @@ id: planner
|
|
|
3
3
|
purpose: planner
|
|
4
4
|
permission: read-only
|
|
5
5
|
description: Plans the change before implementation.
|
|
6
|
-
# fable primary
|
|
6
|
+
# claude-fable is primary. The Pi fallback is a different vendor.
|
|
7
7
|
roster:
|
|
8
|
-
- route: fable
|
|
8
|
+
- route: claude-fable
|
|
9
|
+
effort: medium
|
|
10
|
+
- route: pi-qwen3-8-flash-openrouter
|
|
9
11
|
effort: medium
|
|
@@ -3,7 +3,9 @@ id: reviewer-arch
|
|
|
3
3
|
purpose: reviewer-arch
|
|
4
4
|
permission: read-only
|
|
5
5
|
description: Independent architecture critic.
|
|
6
|
-
# fable is the required arch critic.
|
|
6
|
+
# claude-fable is the required arch critic. The Pi fallback is a different vendor.
|
|
7
7
|
roster:
|
|
8
|
-
- route: fable
|
|
8
|
+
- route: claude-fable
|
|
9
|
+
effort: medium
|
|
10
|
+
- route: pi-glm-5-3-flash-openrouter
|
|
9
11
|
effort: medium
|
|
@@ -3,7 +3,9 @@ id: reviewer-cli
|
|
|
3
3
|
purpose: reviewer-cli
|
|
4
4
|
permission: read-only
|
|
5
5
|
description: Independent CLI and docs critic.
|
|
6
|
-
# sol is the required cli critic.
|
|
6
|
+
# codex-gpt-5-6-sol is the required cli critic. The Pi fallback is a different vendor.
|
|
7
7
|
roster:
|
|
8
|
-
- route: sol
|
|
8
|
+
- route: codex-gpt-5-6-sol
|
|
9
|
+
effort: low
|
|
10
|
+
- route: pi-qwen3-8-flash-openrouter
|
|
9
11
|
effort: low
|
package/.kxm/roles/writer.yaml
CHANGED
|
@@ -5,8 +5,11 @@ permission: edit
|
|
|
5
5
|
description: Primary implementation agent.
|
|
6
6
|
# Rotation priority = order. Effort default: medium for implementation.
|
|
7
7
|
roster:
|
|
8
|
-
- route: grok-
|
|
8
|
+
- route: grok-grok-4-7
|
|
9
9
|
effort: medium
|
|
10
|
-
- route:
|
|
10
|
+
- route: pi-qwen3-coder-plus-openrouter
|
|
11
|
+
effort: medium
|
|
12
|
+
- route: agy-gemini-3-8-flash-high
|
|
13
|
+
effort: medium
|
|
14
|
+
- route: agy-gemini-3-8-flash-medium
|
|
11
15
|
effort: medium
|
|
12
|
-
- route: gemini-agy
|
|
@@ -1,37 +1,45 @@
|
|
|
1
1
|
schema: kxm.workflow.v1
|
|
2
2
|
description: Canonical 4-stage KXM delivery workflow
|
|
3
|
-
coordinator:
|
|
3
|
+
coordinator: planner
|
|
4
4
|
limits:
|
|
5
5
|
maxTransitions: 8
|
|
6
6
|
steps:
|
|
7
|
-
- id:
|
|
7
|
+
- id: writer
|
|
8
|
+
aliases:
|
|
9
|
+
- implement
|
|
8
10
|
kind: agent
|
|
9
|
-
agent:
|
|
11
|
+
agent: writer
|
|
10
12
|
repositories:
|
|
11
13
|
control: write
|
|
12
14
|
maxAttempts: 3
|
|
13
15
|
on:
|
|
14
|
-
passed:
|
|
16
|
+
passed: reviewer-arch
|
|
15
17
|
failed:
|
|
16
18
|
target: $terminal
|
|
17
19
|
terminalStatus: failed
|
|
18
|
-
- id:
|
|
20
|
+
- id: reviewer-arch
|
|
21
|
+
aliases:
|
|
22
|
+
- review-arch
|
|
23
|
+
- critic-arch
|
|
19
24
|
kind: agent
|
|
20
|
-
agent:
|
|
25
|
+
agent: reviewer-arch
|
|
21
26
|
maxAttempts: 2
|
|
22
27
|
on:
|
|
23
|
-
passed:
|
|
28
|
+
passed: reviewer-cli
|
|
24
29
|
failed:
|
|
25
|
-
target:
|
|
30
|
+
target: writer
|
|
26
31
|
maxTransitions: 2
|
|
27
|
-
- id:
|
|
32
|
+
- id: reviewer-cli
|
|
33
|
+
aliases:
|
|
34
|
+
- review-cli
|
|
35
|
+
- critic-cli
|
|
28
36
|
kind: agent
|
|
29
|
-
agent:
|
|
37
|
+
agent: reviewer-cli
|
|
30
38
|
maxAttempts: 2
|
|
31
39
|
on:
|
|
32
40
|
passed: verify
|
|
33
41
|
failed:
|
|
34
|
-
target:
|
|
42
|
+
target: writer
|
|
35
43
|
maxTransitions: 2
|
|
36
44
|
- id: verify
|
|
37
45
|
kind: gate
|
|
@@ -45,5 +53,5 @@ steps:
|
|
|
45
53
|
target: $terminal
|
|
46
54
|
terminalStatus: completed
|
|
47
55
|
implementation-failure:
|
|
48
|
-
target:
|
|
56
|
+
target: writer
|
|
49
57
|
maxTransitions: 2
|
package/.kxm/workflows/land.yaml
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
schema: kxm.workflow.v1
|
|
2
2
|
description: One read-only architecture critic step.
|
|
3
|
-
|
|
3
|
+
aliases:
|
|
4
|
+
- review-arch-only
|
|
5
|
+
coordinator: planner
|
|
4
6
|
limits:
|
|
5
7
|
maxTransitions: 2
|
|
6
8
|
steps:
|
|
7
|
-
- id:
|
|
9
|
+
- id: reviewer-arch
|
|
10
|
+
aliases:
|
|
11
|
+
- critic-arch
|
|
8
12
|
kind: agent
|
|
9
|
-
agent:
|
|
13
|
+
agent: reviewer-arch
|
|
10
14
|
repositories:
|
|
11
15
|
control: read
|
|
12
16
|
maxAttempts: 1
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
schema: kxm.workflow.v1
|
|
2
2
|
description: One read-only CLI critic step.
|
|
3
|
-
|
|
3
|
+
aliases:
|
|
4
|
+
- review-cli-only
|
|
5
|
+
coordinator: planner
|
|
4
6
|
limits:
|
|
5
7
|
maxTransitions: 2
|
|
6
8
|
steps:
|
|
7
|
-
- id:
|
|
9
|
+
- id: reviewer-cli
|
|
10
|
+
aliases:
|
|
11
|
+
- critic-cli
|
|
8
12
|
kind: agent
|
|
9
|
-
agent:
|
|
13
|
+
agent: reviewer-cli
|
|
10
14
|
repositories:
|
|
11
15
|
control: read
|
|
12
16
|
maxAttempts: 1
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
schema: kxm.workflow.v1
|
|
2
|
-
description: One
|
|
3
|
-
|
|
2
|
+
description: One writer step with a drive receipt.
|
|
3
|
+
aliases:
|
|
4
|
+
- implement-only
|
|
5
|
+
coordinator: planner
|
|
4
6
|
limits:
|
|
5
7
|
maxTransitions: 2
|
|
6
8
|
steps:
|
|
7
|
-
- id:
|
|
9
|
+
- id: writer
|
|
10
|
+
aliases:
|
|
11
|
+
- implement
|
|
8
12
|
kind: agent
|
|
9
|
-
agent:
|
|
13
|
+
agent: writer
|
|
10
14
|
repositories:
|
|
11
15
|
control: write
|
|
12
16
|
maxAttempts: 1
|
package/CHANGELOG.md
CHANGED
|
@@ -13,8 +13,9 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
13
13
|
effective value is written on the one-shot evidence. A `cancelling` run whose
|
|
14
14
|
executing attempt's child already exited settles `executing_unrecorded`.
|
|
15
15
|
Admission is released when a drive closes with a handoff, so a later drive
|
|
16
|
-
is admitted, and `runs status` names the attempt. `
|
|
17
|
-
`
|
|
16
|
+
is admitted, and `runs status` names the attempt. `writer-only`,
|
|
17
|
+
`reviewer-arch-only`, and `reviewer-cli-only` are one-step workflows
|
|
18
|
+
(`implement-only`, `review-arch-only`, and `review-cli-only` still resolve), driven with
|
|
18
19
|
`kxm lane run <unit> --workflow <id> --brief <file>`. See
|
|
19
20
|
[kxm lane](docs/reference/cli-reference.md#kxm-lane),
|
|
20
21
|
[runs status](docs/reference/cli-reference.md#kxm-runs-status),
|
|
@@ -141,6 +142,44 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
141
142
|
|
|
142
143
|
### Changed
|
|
143
144
|
|
|
145
|
+
- **Docs match the 2026-09-27 Steel and machine-account infrastructure.**
|
|
146
|
+
Steel (`steel.kontextmind.com`, alias `steel.theneuro.me`) is reached only
|
|
147
|
+
through Caddy on `kxmd-proxy` (VM 230) and Authentik forward auth. Direct
|
|
148
|
+
LAN, tailnet, and host-forward access is blocked. Sessions return
|
|
149
|
+
`websocketUrl` `wss://steel.kontextmind.com/` (previously
|
|
150
|
+
`ws://steel-browser/`). The CDP path is `/v1/devtools` with an
|
|
151
|
+
`Authorization` header. Allowed groups are `steel-users`, `kxmd-users`,
|
|
152
|
+
`kxmd-admins`, and `kxmd-owners`. `STEEL_API_URL` defaults to
|
|
153
|
+
`https://steel.kontextmind.com`. `STEEL_API_KEY` is deprecated and is not
|
|
154
|
+
enforced by Steel or Caddy. Migrate to `STEEL_AUTH_HEADER`, then
|
|
155
|
+
`STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`; those
|
|
156
|
+
override the key. The `svc-steel` credential is 1Password vault
|
|
157
|
+
`kontextmind`, item `Steel (svc-steel)`, field `basic_auth`, read with
|
|
158
|
+
`op read` and never written to disk. Steel requires `kxm` 0.7.135 or
|
|
159
|
+
newer. Playwright stays on Obscura ([ADR-0005](docs/adr/ADR-0005-obscura-default-playwright.md)).
|
|
160
|
+
The Proxmox boot order and VM names are in
|
|
161
|
+
[Deploy KXM](docs/operations/deploy.md#boot-the-kxmd-proxmox-host).
|
|
162
|
+
Machine accounts follow
|
|
163
|
+
[ADR-0006](docs/adr/ADR-0006-machine-account-names.md). The DOKS deployment
|
|
164
|
+
record is superseded by
|
|
165
|
+
[ADR-0007](docs/adr/ADR-0007-steel-caddy-authentik.md).
|
|
166
|
+
|
|
167
|
+
- **Workforce ids use one convention, and old ids still resolve.**
|
|
168
|
+
Role ids stay `planner`, `writer`, `reviewer-arch`, and `reviewer-cli`.
|
|
169
|
+
Agent ids and agent-step ids use those same names. Route ids are
|
|
170
|
+
`<harness>-<model-slug>[-<provider>]`. `kxm init` writes `planner.yaml`
|
|
171
|
+
and `writer.yaml`; `coordinator` and `implementer` remain aliases.
|
|
172
|
+
`opus-claude` is removed because `opus` is not an admitted selector.
|
|
173
|
+
`qwen-token-plan/*` and `zai-coding-cn/*` left `.kxm/routes.yaml` because
|
|
174
|
+
those harnesses are not allowlisted. `node scripts/workforce-lint.mjs`
|
|
175
|
+
fails `npm run check` and `npm run validate:pr` when a route, a roster
|
|
176
|
+
entry, or an id breaks the convention, and when a roster entry omits
|
|
177
|
+
`effort` or names one outside `off`, `minimal`, `low`, `medium`, `high`,
|
|
178
|
+
`xhigh`, and `max`. An admitted selector with no route is a warning.
|
|
179
|
+
The Claude helper accepts `fable` only. #343's architecture critic ran on
|
|
180
|
+
`opus` because the justfile recipe review-arch hardcoded that model while
|
|
181
|
+
`reviewer-arch` listed only `fable-claude`. That recipe is gone; a request
|
|
182
|
+
for `opus` fails closed.
|
|
144
183
|
- **Dispatch reads role and model files, and agents bind a role.**
|
|
145
184
|
`scripts/roster-policy.mjs` builds the developer policy from
|
|
146
185
|
`.kxm/models/*.yaml` and `.kxm/roles/*.yaml` at `refs/remotes/origin/main`.
|
|
@@ -148,9 +187,9 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
148
187
|
and that role's roster. A step `model` does not override that route.
|
|
149
188
|
`kxm routes` prints `policy` (`admitted`, `disabled`) and `membership`
|
|
150
189
|
from the role files. `.kxm/routes.yaml` keeps admitted and disabled
|
|
151
|
-
selectors. `reviewer-arch` resolves to `fable
|
|
152
|
-
|
|
153
|
-
|
|
190
|
+
selectors. `reviewer-arch` resolves to `claude-fable` (alias `fable-claude`).
|
|
191
|
+
`agy-gemini-3-8-flash-high` (alias `gemini-agy`) is in the writer lineup.
|
|
192
|
+
An agent `tools.preset`
|
|
154
193
|
may only narrow its role preset; that rule is recorded and enforced in P3.
|
|
155
194
|
|
|
156
195
|
- **Role and model files are live `kxm.role.v2` and `kxm.model.v2`.**
|
|
@@ -455,9 +494,10 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
455
494
|
into `chromium.connectOverCDP`. Obscura stays the default and sends no Steel
|
|
456
495
|
headers. `STEEL_AUTH_HEADER` overrides the value. The CDP URL omits the credential when
|
|
457
496
|
those variables are set. A 302 to the identity provider fails closed and does
|
|
458
|
-
not follow the login redirect. `STEEL_API_KEY`
|
|
459
|
-
|
|
460
|
-
|
|
497
|
+
not follow the login redirect. `STEEL_API_KEY` is deprecated. Steel and
|
|
498
|
+
Caddy do not enforce it. `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then
|
|
499
|
+
`STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN` override it. A client that still
|
|
500
|
+
has only the legacy key warns once and is not authenticated. See
|
|
461
501
|
[Browser automation](docs/guides/browser-automation.md).
|
|
462
502
|
|
|
463
503
|
### Removed
|
package/docs/README.md
CHANGED
|
@@ -76,7 +76,7 @@ KXM connects coding agents through a durable, authenticated [hub](glossary.md#hu
|
|
|
76
76
|
| [Architecture](concepts/architecture.md) | Integrators, maintainers | The components, message and workflow lifecycles, and KXM's limits |
|
|
77
77
|
| [Trust model](concepts/trust-model.md) | Operators, security reviewers | Who holds which credential, project boundaries, and what provenance proves |
|
|
78
78
|
| [Data and storage](concepts/data-and-storage.md) | Operators, security reviewers | What each store holds, where it lives and how long it is kept |
|
|
79
|
-
| [Architecture decision records](adr/README.md) | Maintainers | The decision records: [browser automation](adr/ADR-0002-browser-automation-steel-doks.md), [Obscura for Playwright](adr/ADR-0005-obscura-default-playwright.md), [SQLite-only store](adr/ADR-0003-sqlite-only-store.md), [edge identity](adr/ADR-0004-edge-identity-authentik.md) |
|
|
79
|
+
| [Architecture decision records](adr/README.md) | Maintainers | The decision records: [browser automation](adr/ADR-0002-browser-automation-steel-doks.md), [Steel through Caddy](adr/ADR-0007-steel-caddy-authentik.md), [Obscura for Playwright](adr/ADR-0005-obscura-default-playwright.md), [machine account names](adr/ADR-0006-machine-account-names.md), [SQLite-only store](adr/ADR-0003-sqlite-only-store.md), [edge identity](adr/ADR-0004-edge-identity-authentik.md) |
|
|
80
80
|
| [KXM contract package](contracts/README.md) | Maintainers, reviewers | The normative specifications for the local-first architecture, listed below |
|
|
81
81
|
|
|
82
82
|
### Contracts
|
|
@@ -4,13 +4,13 @@ id: "ADR-0002"
|
|
|
4
4
|
type: "adr"
|
|
5
5
|
title: "Self-hosted Steel on DOKS for reusable browser automation and human takeover"
|
|
6
6
|
project: "kxm"
|
|
7
|
-
status: "
|
|
7
|
+
status: "superseded"
|
|
8
8
|
owner: "@operator"
|
|
9
9
|
created: "2026-09-14"
|
|
10
10
|
updated: "2026-09-27"
|
|
11
11
|
authority: "decision"
|
|
12
12
|
confidence: "verified"
|
|
13
|
-
summary: "
|
|
13
|
+
summary: "The 2026-09-14 choice of self-hosted Steel. The DOKS deployment in this record was superseded on 2026-09-27 by ADR-0007. Obscura is the Playwright default (ADR-0005)."
|
|
14
14
|
tags: ["architecture", "decision", "browser", "steel", "doks", "playwright"]
|
|
15
15
|
related: ["docs/guides/browser-automation.md", "docs/guides/agent-skills.md", "docs/adr/ADR-0005-obscura-default-playwright.md"]
|
|
16
16
|
details:
|
|
@@ -20,11 +20,15 @@ details:
|
|
|
20
20
|
- "Provide dual exploratory (agent-browser) and regression (Playwright) interfaces"
|
|
21
21
|
- "Enforce strict credential isolation via pass-cli"
|
|
22
22
|
supersedes: null
|
|
23
|
-
superseded_by:
|
|
23
|
+
superseded_by: "ADR-0007"
|
|
24
24
|
---
|
|
25
25
|
|
|
26
26
|
# ADR-0002: Self-hosted Steel on DOKS for reusable browser automation
|
|
27
27
|
|
|
28
|
+
## Status
|
|
29
|
+
|
|
30
|
+
Superseded on 2026-09-27 by [ADR-0007](ADR-0007-steel-caddy-authentik.md) for where Steel runs and how clients authenticate. [ADR-0005](ADR-0005-obscura-default-playwright.md) keeps Obscura as the Playwright default. The sections below are the 2026-09-14 decision. They are not the current deployment. Steel and Caddy do not enforce `STEEL_API_KEY`.
|
|
31
|
+
|
|
28
32
|
## Context and problem statement
|
|
29
33
|
|
|
30
34
|
AI coding agents and orchestration workflows in KXM require browser interaction for UI exploration, DOM mapping, bug reproduction, and end-to-end regression testing. Existing approaches suffered from three core issues:
|
|
@@ -100,8 +104,7 @@ AI coding agents and orchestration workflows in KXM require browser interaction
|
|
|
100
104
|
|
|
101
105
|
- **Verification**: Health endpoint `$STEEL_API_URL/v1/health` verified with HTTP 200 and Let's Encrypt TLS.
|
|
102
106
|
- **Integration Test**: `test/core/browser.test.ts` validates session lifecycle, CDP endpoint formatting, takeover transitions, and secret redaction.
|
|
103
|
-
- **Security Check**: `pass-cli`
|
|
104
|
-
- **Edge auth (2026-09-27)**: `steel.kontextmind.com` and `steel.theneuro.me`, including the CDP WebSocket, are behind Authentik forward auth. Steel does not check `STEEL_API_KEY`. Clients send `Authorization: Basic`. Unauthenticated requests are redirected to `id.kxmd.dev`. The legacy `x-steel-api-key` header and `apiKey` query parameter remain a temporary proxy shim.
|
|
107
|
+
- **Security Check**: `pass-cli` was the credential store named in this 2026-09-14 decision. Current clients read the `svc-steel` credential with `op read`, as [ADR-0007](ADR-0007-steel-caddy-authentik.md) records.
|
|
105
108
|
|
|
106
109
|
## Related
|
|
107
110
|
|
|
@@ -12,7 +12,7 @@ authority: "decision"
|
|
|
12
12
|
confidence: "verified"
|
|
13
13
|
summary: "Playwright testing and verification connect to pinned Obscura v0.2.3 over CDP. Steel stays the browser for human takeover, MFA, and the live session viewer."
|
|
14
14
|
tags: ["architecture", "decision", "browser", "obscura", "playwright", "cdp"]
|
|
15
|
-
related: ["docs/adr/ADR-0002-browser-automation-steel-doks.md", "docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-obscura.md"]
|
|
15
|
+
related: ["docs/adr/ADR-0002-browser-automation-steel-doks.md", "docs/adr/ADR-0007-steel-caddy-authentik.md", "docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-obscura.md"]
|
|
16
16
|
details:
|
|
17
17
|
decision_drivers:
|
|
18
18
|
- "Playwright tests must run without a Steel cluster or a Playwright-managed browser download"
|
|
@@ -26,7 +26,7 @@ details:
|
|
|
26
26
|
|
|
27
27
|
## Status
|
|
28
28
|
|
|
29
|
-
Accepted on 2026-09-27. This record does not supersede [ADR-0002](ADR-0002-browser-automation-steel-doks.md). Steel remains the browser for human takeover, MFA, and the live session viewer.
|
|
29
|
+
Accepted on 2026-09-27. This record does not supersede [ADR-0002](ADR-0002-browser-automation-steel-doks.md). [ADR-0007](ADR-0007-steel-caddy-authentik.md) is the current Steel deployment. Steel remains the browser for remote and hosted sessions, human takeover, MFA, and the live session viewer.
|
|
30
30
|
|
|
31
31
|
## Context
|
|
32
32
|
|
|
@@ -79,7 +79,8 @@ Obscura v0.2.3 is a headless Chromium build that speaks the Chrome DevTools Prot
|
|
|
79
79
|
|
|
80
80
|
## Related
|
|
81
81
|
|
|
82
|
-
- [ADR-0002: Self-hosted Steel on DOKS](ADR-0002-browser-automation-steel-doks.md)
|
|
82
|
+
- [ADR-0002: Self-hosted Steel on DOKS (superseded deployment)](ADR-0002-browser-automation-steel-doks.md)
|
|
83
|
+
- [ADR-0007: Steel through Caddy and Authentik](ADR-0007-steel-caddy-authentik.md)
|
|
83
84
|
- [Browser automation](../guides/browser-automation.md)
|
|
84
85
|
- [How do I connect Playwright to Obscura?](../kb/how-to-connect-playwright-to-obscura.md)
|
|
85
86
|
- [Environment variables and limits](../reference/configuration.md#browser-automation)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema: "kxm.doc.v1"
|
|
3
|
+
id: "ADR-0006"
|
|
4
|
+
type: "adr"
|
|
5
|
+
title: "Machine account names"
|
|
6
|
+
project: "kxm"
|
|
7
|
+
status: "accepted"
|
|
8
|
+
owner: "@operator"
|
|
9
|
+
created: "2026-09-27"
|
|
10
|
+
updated: "2026-09-27"
|
|
11
|
+
authority: "decision"
|
|
12
|
+
confidence: "verified"
|
|
13
|
+
summary: "New machine accounts use svc-<system>-<purpose>, or svc-<tenant>-<system>-<purpose> when they belong to one tenant. Test accounts add a test- prefix and stay out of production groups."
|
|
14
|
+
tags: ["architecture", "decision", "authentik", "accounts"]
|
|
15
|
+
related: ["docs/adr/ADR-0004-edge-identity-authentik.md", "docs/operations/deploy.md", "docs/contributing/operating-rules.md"]
|
|
16
|
+
details:
|
|
17
|
+
decision_drivers:
|
|
18
|
+
- "One readable pattern for platform and tenant machine accounts"
|
|
19
|
+
- "Test and witness accounts must be unable to enter production groups"
|
|
20
|
+
- "Existing names stay until an approved inventory authorizes a rename"
|
|
21
|
+
supersedes: null
|
|
22
|
+
superseded_by: null
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# ADR-0006: Machine account names
|
|
26
|
+
|
|
27
|
+
## Status
|
|
28
|
+
|
|
29
|
+
Accepted on 2026-09-27.
|
|
30
|
+
|
|
31
|
+
## Context
|
|
32
|
+
|
|
33
|
+
Authentik machine accounts were created with local names such as `kxm-agent`, `kxm-witness-*`, `witness9`, `kxmdproof`, `kxm-provisioner`, and `agent-ilo-asus`. Those names do not say whether the account is platform-wide, tenant-scoped, or a test identity. Renaming them without an inventory would drop grants that still point at the old name.
|
|
34
|
+
|
|
35
|
+
## Decision drivers
|
|
36
|
+
|
|
37
|
+
1. A reader can tell the scope of an account from its name.
|
|
38
|
+
2. Test, witness, and proof accounts must not sit in production groups.
|
|
39
|
+
3. Authentik's own accounts keep the names Authentik assigns.
|
|
40
|
+
4. A rename of an account that already exists waits for an approved inventory.
|
|
41
|
+
|
|
42
|
+
## Considered options
|
|
43
|
+
|
|
44
|
+
1. **`svc-` names, with a `test-` prefix for non-production accounts.**
|
|
45
|
+
2. **Keep creating ad hoc names.**
|
|
46
|
+
3. **Put every machine account under Authentik's `ak-*` prefix.**
|
|
47
|
+
|
|
48
|
+
### Option 1: `svc-` names (chosen)
|
|
49
|
+
|
|
50
|
+
- Good, because platform and tenant scope are visible in the name.
|
|
51
|
+
- Good, because a `test-` prefix can be excluded from production groups.
|
|
52
|
+
- Bad, because accounts that already exist keep their old names until an inventory is approved.
|
|
53
|
+
|
|
54
|
+
### Option 2: ad hoc names (rejected)
|
|
55
|
+
|
|
56
|
+
- Good, because nothing already issued has to change.
|
|
57
|
+
- Bad, because the next account repeats the same ambiguity.
|
|
58
|
+
|
|
59
|
+
### Option 3: `ak-*` for every machine account (rejected)
|
|
60
|
+
|
|
61
|
+
- Good, because one prefix would match Authentik-managed users.
|
|
62
|
+
- Bad, because `ak-*` is Authentik's own namespace for outposts and internal users.
|
|
63
|
+
|
|
64
|
+
## Decision
|
|
65
|
+
|
|
66
|
+
Create machine accounts with these shapes. `<tenant>` is the tenant slug.
|
|
67
|
+
|
|
68
|
+
| Scope | Name |
|
|
69
|
+
|---|---|
|
|
70
|
+
| Platform-wide | `svc-<system>-<purpose>` |
|
|
71
|
+
| Tenant-scoped | `svc-<tenant>-<system>-<purpose>` |
|
|
72
|
+
| Test, witness, or proof | The same shapes with a `test-` prefix |
|
|
73
|
+
|
|
74
|
+
A `test-` account is never a member of a production group.
|
|
75
|
+
|
|
76
|
+
Authentik-managed accounts are exempt. That includes outposts and any account whose name starts with `ak-`.
|
|
77
|
+
|
|
78
|
+
The `svc-steel` credential that reaches the Steel server is the account operators use today. This record does not rename it. Names already recorded in plans and handoffs, including `kxm-agent`, `kxm-witness-*`, `witness9`, `kxmdproof`, `kxm-provisioner`, and `agent-ilo-asus`, stay as written until an approved inventory lists each rename. Do not invent a replacement for a specific account.
|
|
79
|
+
|
|
80
|
+
## Consequences
|
|
81
|
+
|
|
82
|
+
- New accounts follow the table above.
|
|
83
|
+
- This repository does not assign new names to the recorded accounts.
|
|
84
|
+
- Group names such as `kxmd-owners` are groups, not machine accounts, and this record does not rename them.
|
|
85
|
+
|
|
86
|
+
## Related
|
|
87
|
+
|
|
88
|
+
- [Edge identity](ADR-0004-edge-identity-authentik.md)
|
|
89
|
+
- [Deploy KXM](../operations/deploy.md#name-machine-accounts)
|
|
90
|
+
- [Operating rules](../contributing/operating-rules.md)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema: "kxm.doc.v1"
|
|
3
|
+
id: "ADR-0007"
|
|
4
|
+
type: "adr"
|
|
5
|
+
title: "Steel is reached only through Caddy and Authentik"
|
|
6
|
+
project: "kxm"
|
|
7
|
+
status: "accepted"
|
|
8
|
+
owner: "@operator"
|
|
9
|
+
created: "2026-09-27"
|
|
10
|
+
updated: "2026-09-27"
|
|
11
|
+
authority: "decision"
|
|
12
|
+
confidence: "verified"
|
|
13
|
+
summary: "The Steel server steel.kontextmind.com is an LXC behind Caddy and Authentik forward auth. Clients use the svc-steel credential. STEEL_API_KEY is not enforced. Playwright stays on Obscura."
|
|
14
|
+
tags: ["architecture", "decision", "browser", "steel", "authentik"]
|
|
15
|
+
related: ["docs/adr/ADR-0002-browser-automation-steel-doks.md", "docs/adr/ADR-0005-obscura-default-playwright.md", "docs/guides/browser-automation.md", "docs/adr/ADR-0006-machine-account-names.md"]
|
|
16
|
+
details:
|
|
17
|
+
decision_drivers:
|
|
18
|
+
- "Steel must not be reachable on the LAN, the tailnet, or a host forward"
|
|
19
|
+
- "Authentik groups are the access list"
|
|
20
|
+
- "Credentials stay in 1Password and are read into the process only"
|
|
21
|
+
supersedes: "ADR-0002"
|
|
22
|
+
superseded_by: null
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# ADR-0007: Steel is reached only through Caddy and Authentik
|
|
26
|
+
|
|
27
|
+
## Status
|
|
28
|
+
|
|
29
|
+
Accepted on 2026-09-27. This record supersedes the DigitalOcean Kubernetes deployment in [ADR-0002](ADR-0002-browser-automation-steel-doks.md). [ADR-0005](ADR-0005-obscura-default-playwright.md) still makes Obscura the Playwright default. Steel remains the browser for remote and hosted sessions, human takeover, MFA, and the live session viewer.
|
|
30
|
+
|
|
31
|
+
## Context
|
|
32
|
+
|
|
33
|
+
Steel now runs as LXC 240 on the Proxmox host, at startup order 40. Its public name is `steel.kontextmind.com`. `steel.theneuro.me` is an alias of that same server. Caddy on VM 230 (`kxmd-proxy`) is the only listener, and it forwards authentication to Authentik. Direct LAN, tailnet, and host-forward access is blocked.
|
|
34
|
+
|
|
35
|
+
Sessions return `websocketUrl` `wss://steel.kontextmind.com/`. The previous value was `ws://steel-browser/`. Clients connect Chrome DevTools Protocol at `/v1/devtools` and send `Authorization` on the handshake. The URL does not carry a credential.
|
|
36
|
+
|
|
37
|
+
Steel and Caddy do not enforce `STEEL_API_KEY`. Clients authenticate as the Authentik user `svc-steel`.
|
|
38
|
+
|
|
39
|
+
## Decision drivers
|
|
40
|
+
|
|
41
|
+
1. The browser host is not a second network entrance beside the proxy.
|
|
42
|
+
2. Membership in a known Authentik group is the allow list.
|
|
43
|
+
3. The credential is read at runtime and is not written to disk.
|
|
44
|
+
4. Playwright tests keep the Obscura default from ADR-0005.
|
|
45
|
+
|
|
46
|
+
## Considered options
|
|
47
|
+
|
|
48
|
+
1. **Caddy plus Authentik forward auth, with direct paths blocked.**
|
|
49
|
+
2. **Keep the DOKS ingress and a Steel API key.**
|
|
50
|
+
3. **Publish the LXC on the tailnet or a host forward.**
|
|
51
|
+
|
|
52
|
+
### Option 1: Caddy and Authentik only (chosen)
|
|
53
|
+
|
|
54
|
+
- Good, because one proxy terminates TLS and applies the group check.
|
|
55
|
+
- Good, because a client that still sends `STEEL_API_KEY` does not get a session.
|
|
56
|
+
- Bad, because clients older than `kxm` 0.7.135 still expect `ws://steel-browser/`.
|
|
57
|
+
|
|
58
|
+
### Option 2: DOKS and `STEEL_API_KEY` (rejected)
|
|
59
|
+
|
|
60
|
+
- Good, because existing scripts that sent `x-steel-api-key` would keep working.
|
|
61
|
+
- Bad, because that cluster is no longer where Steel runs, and neither Steel nor Caddy checks the key.
|
|
62
|
+
|
|
63
|
+
### Option 3: tailnet or host-forward access (rejected)
|
|
64
|
+
|
|
65
|
+
- Good, because an operator on the tailnet could open the API without the proxy.
|
|
66
|
+
- Bad, because it bypasses Authentik and the group check.
|
|
67
|
+
|
|
68
|
+
## Decision
|
|
69
|
+
|
|
70
|
+
Reach Steel only at `https://steel.kontextmind.com` (alias `steel.theneuro.me`) through Caddy and Authentik forward auth. `STEEL_API_URL` defaults to `https://steel.kontextmind.com`.
|
|
71
|
+
|
|
72
|
+
Authenticate with the `svc-steel` credential. Precedence is `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` together with `STEEL_AUTH_TOKEN`. Those variables override `STEEL_API_KEY`. `STEEL_API_KEY` is deprecated: Steel and Caddy do not enforce it, and a value in the URL is not accepted. Migrate by setting one of the three Authentik variables and dropping the key from the environment.
|
|
73
|
+
|
|
74
|
+
Read the credential from 1Password at runtime. The vault is `kontextmind`, the item is `Steel (svc-steel)`, and the field is `basic_auth`. Use `op read`. Do not write the value to disk.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
export STEEL_AUTH_BASIC="$(op read 'op://kontextmind/Steel (svc-steel)/basic_auth')"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The CDP path is `/v1/devtools` with an `Authorization` header.
|
|
81
|
+
|
|
82
|
+
These Authentik groups may use the server: `steel-users`, `kxmd-users`, `kxmd-admins`, and `kxmd-owners`.
|
|
83
|
+
|
|
84
|
+
`kxm` 0.7.135 or newer is required. Playwright testing stays on Obscura unless `KXM_BROWSER=steel`.
|
|
85
|
+
|
|
86
|
+
## Consequences
|
|
87
|
+
|
|
88
|
+
- A connection to the LXC by LAN address, tailnet address, or host forward fails.
|
|
89
|
+
- A client that only sets `STEEL_API_KEY` is not authenticated.
|
|
90
|
+
- The `svc-steel` name is the live account. [ADR-0006](ADR-0006-machine-account-names.md) does not rename it ahead of an approved inventory.
|
|
91
|
+
|
|
92
|
+
## Related
|
|
93
|
+
|
|
94
|
+
- [ADR-0002](ADR-0002-browser-automation-steel-doks.md)
|
|
95
|
+
- [ADR-0005](ADR-0005-obscura-default-playwright.md)
|
|
96
|
+
- [Browser automation](../guides/browser-automation.md)
|
|
97
|
+
- [Deploy KXM](../operations/deploy.md#boot-the-kxmd-proxmox-host)
|
package/docs/adr/README.md
CHANGED
|
@@ -7,10 +7,12 @@ An architecture decision record (ADR) captures one significant decision about KX
|
|
|
7
7
|
| ADR | Decision | Status | Date |
|
|
8
8
|
|---|---|---|---|
|
|
9
9
|
| [ADR-001](../contracts/architecture.md) | Local Runtime, project authority, and aggregate hub | Accepted target | — |
|
|
10
|
-
| [ADR-0002](ADR-0002-browser-automation-steel-doks.md) | Self-hosted Steel
|
|
10
|
+
| [ADR-0002](ADR-0002-browser-automation-steel-doks.md) | Self-hosted Steel on DOKS (deployment superseded) | Superseded by ADR-0007 | 2026-09-14 |
|
|
11
11
|
| [ADR-0003](ADR-0003-sqlite-only-store.md) | SQLite as the only store | Accepted | 2026-09-17 |
|
|
12
12
|
| [ADR-0004](ADR-0004-edge-identity-authentik.md) | Edge identity with Authentik; the hub owns no browser identity | Accepted | 2026-09-20 |
|
|
13
13
|
| [ADR-0005](ADR-0005-obscura-default-playwright.md) | Obscura is the default browser for Playwright; Steel stays for takeover | Accepted | 2026-09-27 |
|
|
14
|
+
| [ADR-0006](ADR-0006-machine-account-names.md) | Machine account names (`svc-` shapes, `test-` prefix, Authentik exemptions) | Accepted | 2026-09-27 |
|
|
15
|
+
| [ADR-0007](ADR-0007-steel-caddy-authentik.md) | Steel is reached only through Caddy and Authentik | Accepted | 2026-09-27 |
|
|
14
16
|
|
|
15
17
|
ADR-001 is the original decision record for the local Runtime. It lives with the contracts in [`docs/contracts/architecture.md`](../contracts/architecture.md) because it is the root of those contracts, and it keeps its original three-digit number. Records in this directory continue the sequence from 0002. There is no ADR-0001.
|
|
16
18
|
|