@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.
Files changed (73) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/README.md +20 -2
  3. package/.kxm/agents/{coordinator.yaml → planner.yaml} +2 -0
  4. package/.kxm/agents/{critic-arch.yaml → reviewer-arch.yaml} +2 -0
  5. package/.kxm/agents/{critic-cli.yaml → reviewer-cli.yaml} +2 -0
  6. package/.kxm/agents/{implementer.yaml → writer.yaml} +2 -0
  7. package/.kxm/roles/planner.yaml +4 -2
  8. package/.kxm/roles/reviewer-arch.yaml +4 -2
  9. package/.kxm/roles/reviewer-cli.yaml +4 -2
  10. package/.kxm/roles/writer.yaml +6 -3
  11. package/.kxm/workflows/default.yaml +20 -12
  12. package/.kxm/workflows/land.yaml +1 -1
  13. package/.kxm/workflows/{review-arch-only.yaml → reviewer-arch-only.yaml} +7 -3
  14. package/.kxm/workflows/{review-cli-only.yaml → reviewer-cli-only.yaml} +7 -3
  15. package/.kxm/workflows/{implement-only.yaml → writer-only.yaml} +8 -4
  16. package/CHANGELOG.md +48 -8
  17. package/docs/README.md +1 -1
  18. package/docs/adr/ADR-0002-browser-automation-steel-doks.md +8 -5
  19. package/docs/adr/ADR-0005-obscura-default-playwright.md +4 -3
  20. package/docs/adr/ADR-0006-machine-account-names.md +90 -0
  21. package/docs/adr/ADR-0007-steel-caddy-authentik.md +97 -0
  22. package/docs/adr/README.md +3 -1
  23. package/docs/contributing/harness-routing-internals.md +17 -11
  24. package/docs/contributing/operating-rules.md +13 -1
  25. package/docs/guides/agent-skills.md +1 -1
  26. package/docs/guides/browser-automation.md +44 -28
  27. package/docs/kb/how-credentials-retrieved-safely.md +22 -23
  28. package/docs/kb/how-to-connect-playwright-to-steel.md +8 -5
  29. package/docs/kb/how-to-recover-expired-session-or-orphan.md +11 -10
  30. package/docs/kb/why-automation-opened-different-browser.md +4 -3
  31. package/docs/operations/deploy.md +31 -0
  32. package/docs/operations/troubleshooting.md +23 -1
  33. package/docs/prompts/browser-diagnose-recover.md +2 -2
  34. package/docs/prompts/browser-start.md +1 -1
  35. package/docs/reference/cli-reference.md +23 -24
  36. package/docs/reference/config-reference.md +69 -62
  37. package/docs/reference/configuration.md +9 -7
  38. package/docs/reference/harness-routing.md +6 -5
  39. package/docs/reference/workflow-catalog.md +3 -3
  40. package/package.json +3 -3
  41. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  42. package/plugins/kxm/dist/cli.js +1145 -805
  43. package/plugins/kxm/dist/mcp-server.js +1 -1
  44. package/plugins/kxm/dist/runtime-supervisor.js +636 -308
  45. package/plugins/kxm/dist/runtime.js +710 -382
  46. package/plugins/kxm/dist/server.js +49 -5
  47. package/plugins/kxm/package.json +1 -1
  48. package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +11 -12
  49. package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +5 -4
  50. package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +4 -3
  51. package/plugins/kxm/skills/kxm-browser-session/SKILL.md +5 -5
  52. package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +1 -1
  53. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +3 -3
  54. package/plugins/kxm/src/browser.ts +12 -9
  55. package/plugins/kxm/src/cli/project.ts +2 -1
  56. package/plugins/kxm/src/cli/roles.ts +3 -4
  57. package/plugins/kxm/src/engine.ts +9 -6
  58. package/plugins/kxm/src/mcp-server.ts +1 -1
  59. package/plugins/kxm/src/modes.ts +1 -1
  60. package/plugins/kxm/src/policy-draft.mjs +11 -5
  61. package/plugins/kxm/src/project-config.ts +49 -11
  62. package/plugins/kxm/src/runtime-service.ts +2 -1
  63. package/plugins/kxm/src/template.ts +39 -10
  64. package/plugins/kxm/src/workflow-manager.ts +34 -28
  65. package/plugins/kxm/src/workforce-names.d.mts +38 -0
  66. package/plugins/kxm/src/workforce-names.mjs +317 -0
  67. package/schemas/agent.schema.json +7 -0
  68. package/schemas/model.schema.json +12 -0
  69. package/schemas/workflow.schema.json +14 -0
  70. package/scripts/harness-run.mjs +1 -1
  71. package/scripts/native-critic.mjs +5 -5
  72. package/scripts/roster-policy.mjs +9 -3
  73. package/scripts/workforce-lint.mjs +18 -0
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.145",
14
+ "version": "0.7.147",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
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
@@ -1,6 +1,8 @@
1
1
  schema: kxm.agent.v1
2
2
  purpose: Coordinate the pinned workflow and emit schema-validated commands.
3
3
  role: planner
4
+ aliases:
5
+ - coordinator
4
6
  tools:
5
7
  preset: coordinator
6
8
  defaultRepositoryAccess: read
@@ -1,6 +1,8 @@
1
1
  schema: kxm.agent.v1
2
2
  purpose: Architecture critic for the approved workflow change.
3
3
  role: reviewer-arch
4
+ aliases:
5
+ - critic-arch
4
6
  tools:
5
7
  preset: read-only
6
8
  defaultRepositoryAccess: read
@@ -1,6 +1,8 @@
1
1
  schema: kxm.agent.v1
2
2
  purpose: CLI and verification critic for the approved workflow change.
3
3
  role: reviewer-cli
4
+ aliases:
5
+ - critic-cli
4
6
  tools:
5
7
  preset: read-only
6
8
  defaultRepositoryAccess: read
@@ -1,6 +1,8 @@
1
1
  schema: kxm.agent.v1
2
2
  purpose: Implement the approved change within the declared repository scope.
3
3
  role: writer
4
+ aliases:
5
+ - implementer
4
6
  tools:
5
7
  preset: workspace-writer
6
8
  defaultRepositoryAccess: none
@@ -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 (proven planning).
6
+ # claude-fable is primary. The Pi fallback is a different vendor.
7
7
  roster:
8
- - route: fable-claude
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-claude
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-codex
8
+ - route: codex-gpt-5-6-sol
9
+ effort: low
10
+ - route: pi-qwen3-8-flash-openrouter
9
11
  effort: low
@@ -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-native
8
+ - route: grok-grok-4-7
9
9
  effort: medium
10
- - route: qwen-openrouter-pi
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: coordinator
3
+ coordinator: planner
4
4
  limits:
5
5
  maxTransitions: 8
6
6
  steps:
7
- - id: implement
7
+ - id: writer
8
+ aliases:
9
+ - implement
8
10
  kind: agent
9
- agent: implementer
11
+ agent: writer
10
12
  repositories:
11
13
  control: write
12
14
  maxAttempts: 3
13
15
  on:
14
- passed: review-arch
16
+ passed: reviewer-arch
15
17
  failed:
16
18
  target: $terminal
17
19
  terminalStatus: failed
18
- - id: review-arch
20
+ - id: reviewer-arch
21
+ aliases:
22
+ - review-arch
23
+ - critic-arch
19
24
  kind: agent
20
- agent: critic-arch
25
+ agent: reviewer-arch
21
26
  maxAttempts: 2
22
27
  on:
23
- passed: review-cli
28
+ passed: reviewer-cli
24
29
  failed:
25
- target: implement
30
+ target: writer
26
31
  maxTransitions: 2
27
- - id: review-cli
32
+ - id: reviewer-cli
33
+ aliases:
34
+ - review-cli
35
+ - critic-cli
28
36
  kind: agent
29
- agent: critic-cli
37
+ agent: reviewer-cli
30
38
  maxAttempts: 2
31
39
  on:
32
40
  passed: verify
33
41
  failed:
34
- target: implement
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: implement
56
+ target: writer
49
57
  maxTransitions: 2
@@ -3,7 +3,7 @@
3
3
  # `kxm run land --dry-run --json`.
4
4
  schema: kxm.workflow.v1
5
5
  description: Land the current branch through verify, docs, rebase, merge, release, and milestone gates.
6
- coordinator: coordinator
6
+ coordinator: planner
7
7
  limits:
8
8
  maxTransitions: 24
9
9
  steps:
@@ -1,12 +1,16 @@
1
1
  schema: kxm.workflow.v1
2
2
  description: One read-only architecture critic step.
3
- coordinator: coordinator
3
+ aliases:
4
+ - review-arch-only
5
+ coordinator: planner
4
6
  limits:
5
7
  maxTransitions: 2
6
8
  steps:
7
- - id: critic-arch
9
+ - id: reviewer-arch
10
+ aliases:
11
+ - critic-arch
8
12
  kind: agent
9
- agent: critic-arch
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
- coordinator: coordinator
3
+ aliases:
4
+ - review-cli-only
5
+ coordinator: planner
4
6
  limits:
5
7
  maxTransitions: 2
6
8
  steps:
7
- - id: critic-cli
9
+ - id: reviewer-cli
10
+ aliases:
11
+ - critic-cli
8
12
  kind: agent
9
- agent: critic-cli
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 implementer step with a drive receipt.
3
- coordinator: coordinator
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: implement
9
+ - id: writer
10
+ aliases:
11
+ - implement
8
12
  kind: agent
9
- agent: implementer
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. `implement-only`,
17
- `review-arch-only`, and `review-cli-only` are one-step workflows, driven with
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-claude`. `opus-claude` is
152
- admitted and named by no roster, so it is absent from `routes` and the
153
- lineups. `gemini-agy` is in the writer lineup. An agent `tools.preset`
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` still sends the legacy
459
- `x-steel-api-key` header and `apiKey` query parameter for the temporary proxy
460
- shim, and warns once. See
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: "accepted"
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: "Adopt self-hosted Steel on DigitalOcean Kubernetes (DOKS) with agent-browser and Playwright as KXM's primary browser automation infrastructure."
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: null
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` verified as the authoritative store for Steel credentials in the operators' password manager.
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)
@@ -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 for reusable browser automation and human takeover | Accepted | 2026-09-14 |
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