@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +38 -3
- 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/ci-and-release.md +80 -43
- 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/configuration.md +9 -7
- package/package.json +2 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/claude-hook.js +1 -1
- package/plugins/kxm/dist/cli.js +2 -2
- package/plugins/kxm/dist/extension.js +1 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +3 -3
- 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/src/browser.ts +12 -9
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/modes.ts +1 -1
- package/plugins/kxm/src/session-work.ts +3 -1
- package/scripts/ci-classify.mjs +69 -0
- package/scripts/ci-unit-shard.mjs +230 -0
package/CHANGELOG.md
CHANGED
|
@@ -142,6 +142,37 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
142
142
|
|
|
143
143
|
### Changed
|
|
144
144
|
|
|
145
|
+
- **Pull-request CI runs the unit suite on Linux Node 24, and `CI / required` is the aggregate check.**
|
|
146
|
+
Docs and plan markdown skip the code jobs. `engine.test.ts` is split by
|
|
147
|
+
test name. `permission.test.ts` and `runtime.test.ts` run one file at a
|
|
148
|
+
time, and every other unit file runs in one light lane. Pushes to `main`
|
|
149
|
+
still run `validate:pr` on Linux and Windows for Node 22.19.0 and Node 24.
|
|
150
|
+
A pull request that touches path, process, shell, spawn, package, lockfile,
|
|
151
|
+
or workflow files also runs the unit lanes on Windows. Playwright stays on
|
|
152
|
+
Obscura. See [CI and release](docs/contributing/ci-and-release.md).
|
|
153
|
+
|
|
154
|
+
- **Docs match the 2026-09-27 Steel and machine-account infrastructure.**
|
|
155
|
+
Steel (`steel.kontextmind.com`, alias `steel.theneuro.me`) is reached only
|
|
156
|
+
through Caddy on `kxmd-proxy` (VM 230) and Authentik forward auth. Direct
|
|
157
|
+
LAN, tailnet, and host-forward access is blocked. Sessions return
|
|
158
|
+
`websocketUrl` `wss://steel.kontextmind.com/` (previously
|
|
159
|
+
`ws://steel-browser/`). The CDP path is `/v1/devtools` with an
|
|
160
|
+
`Authorization` header. Allowed groups are `steel-users`, `kxmd-users`,
|
|
161
|
+
`kxmd-admins`, and `kxmd-owners`. `STEEL_API_URL` defaults to
|
|
162
|
+
`https://steel.kontextmind.com`. `STEEL_API_KEY` is deprecated and is not
|
|
163
|
+
enforced by Steel or Caddy. Migrate to `STEEL_AUTH_HEADER`, then
|
|
164
|
+
`STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`; those
|
|
165
|
+
override the key. The `svc-steel` credential is 1Password vault
|
|
166
|
+
`kontextmind`, item `Steel (svc-steel)`, field `basic_auth`, read with
|
|
167
|
+
`op read` and never written to disk. Steel requires `kxm` 0.7.135 or
|
|
168
|
+
newer. Playwright stays on Obscura ([ADR-0005](docs/adr/ADR-0005-obscura-default-playwright.md)).
|
|
169
|
+
The Proxmox boot order and VM names are in
|
|
170
|
+
[Deploy KXM](docs/operations/deploy.md#boot-the-kxmd-proxmox-host).
|
|
171
|
+
Machine accounts follow
|
|
172
|
+
[ADR-0006](docs/adr/ADR-0006-machine-account-names.md). The DOKS deployment
|
|
173
|
+
record is superseded by
|
|
174
|
+
[ADR-0007](docs/adr/ADR-0007-steel-caddy-authentik.md).
|
|
175
|
+
|
|
145
176
|
- **Workforce ids use one convention, and old ids still resolve.**
|
|
146
177
|
Role ids stay `planner`, `writer`, `reviewer-arch`, and `reviewer-cli`.
|
|
147
178
|
Agent ids and agent-step ids use those same names. Route ids are
|
|
@@ -158,6 +189,7 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
158
189
|
`opus` because the justfile recipe review-arch hardcoded that model while
|
|
159
190
|
`reviewer-arch` listed only `fable-claude`. That recipe is gone; a request
|
|
160
191
|
for `opus` fails closed.
|
|
192
|
+
|
|
161
193
|
- **Dispatch reads role and model files, and agents bind a role.**
|
|
162
194
|
`scripts/roster-policy.mjs` builds the developer policy from
|
|
163
195
|
`.kxm/models/*.yaml` and `.kxm/roles/*.yaml` at `refs/remotes/origin/main`.
|
|
@@ -472,9 +504,10 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
472
504
|
into `chromium.connectOverCDP`. Obscura stays the default and sends no Steel
|
|
473
505
|
headers. `STEEL_AUTH_HEADER` overrides the value. The CDP URL omits the credential when
|
|
474
506
|
those variables are set. A 302 to the identity provider fails closed and does
|
|
475
|
-
not follow the login redirect. `STEEL_API_KEY`
|
|
476
|
-
|
|
477
|
-
|
|
507
|
+
not follow the login redirect. `STEEL_API_KEY` is deprecated. Steel and
|
|
508
|
+
Caddy do not enforce it. `STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then
|
|
509
|
+
`STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN` override it. A client that still
|
|
510
|
+
has only the legacy key warns once and is not authenticated. See
|
|
478
511
|
[Browser automation](docs/guides/browser-automation.md).
|
|
479
512
|
|
|
480
513
|
### Removed
|
|
@@ -495,6 +528,8 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
495
528
|
|
|
496
529
|
### Fixed
|
|
497
530
|
|
|
531
|
+
- **Dry-run ship status no longer rewrites `.git/index`.** `git status` refreshes the index under an optional lock. The ship-status read passes `--no-optional-locks`, so a dry run leaves the checkout untouched.
|
|
532
|
+
|
|
498
533
|
- **A committed checkout counts as authored work, and a one-shot outcome must be a standalone JSON object.** The authoring witness includes `HEAD` with porcelain status and both diffs, so a write that commits its edits is `changed` and can stay `passed`. A `rev-parse` failure keeps that empty head term only when the repository has no commits; any other git failure is `unwitnessed`. A one-shot outcome is accepted when the whole reply is one JSON object, or when that object stands alone on the final line. A closing code fence around the final object is allowed. An object followed by prose, a truncated reply, and an ambiguous tail settle `failed`.
|
|
499
534
|
|
|
500
535
|
- **The test suite no longer passes `--test-timeout`.** Under `node --test` that flag bounds each file, so coverage on CI timed out `test/core/engine.test.ts` at three minutes. The wall clock in `scripts/run-bounded.mjs` still bounds each script.
|
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
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# CI and release
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
nightly, every merge to `main`
|
|
6
|
-
verified before it reaches npm.
|
|
7
|
-
page explains which checks run where, how a merge becomes a published
|
|
8
|
-
and which smoke tests stay manual. It is for contributors and
|
|
3
|
+
Pull requests run lint, typecheck, and the unit suite on Linux Node 24.
|
|
4
|
+
Pushes to `main` also run that suite and the full OS × Node matrix.
|
|
5
|
+
The complete suite with coverage floors runs nightly, every merge to `main`
|
|
6
|
+
cuts a patch release, and every release is verified before it reaches npm.
|
|
7
|
+
This page explains which checks run where, how a merge becomes a published
|
|
8
|
+
version, and which smoke tests stay manual. It is for contributors and
|
|
9
|
+
maintainers.
|
|
9
10
|
|
|
10
11
|
## The pipeline at a glance
|
|
11
12
|
|
|
@@ -14,9 +15,10 @@ release and an npm publish; the nightly job adds the slower suites.
|
|
|
14
15
|
|
|
15
16
|
```mermaid
|
|
16
17
|
flowchart LR
|
|
17
|
-
PR[Pull request] -->|"
|
|
18
|
+
PR[Pull request] -->|"unit shards, docs lint, plugin validation"| REQ["CI / required"]
|
|
19
|
+
REQ --> MERGE{Merged?}
|
|
18
20
|
MERGE -->|yes| MAIN[main]
|
|
19
|
-
MAIN -->|"
|
|
21
|
+
MAIN -->|"unit shards plus validate:pr matrix"| PUSH[Push CI]
|
|
20
22
|
MAIN -->|"Auto-Release: next patch tag"| TAG[Tag vX.Y.Z]
|
|
21
23
|
TAG -->|dispatch| REL[Release workflow]
|
|
22
24
|
REL -->|"verify, stamp version, pack"| GH[GitHub release<br/>kxm-X.Y.Z.tgz]
|
|
@@ -35,46 +37,75 @@ together.
|
|
|
35
37
|
| Trigger | Workflow (job) | What it runs |
|
|
36
38
|
|---|---|---|
|
|
37
39
|
| Before you push | Local | `npm run verify` |
|
|
38
|
-
| Pull request
|
|
40
|
+
| Pull request, push to `main`, or manual | `ci.yml` (`required`) | Aggregates the lanes below into one pass/fail check named `CI / required` |
|
|
39
41
|
| Pull request and push | `ci.yml` (Docs lint) | `lint:docs` and `check:versions`, always |
|
|
40
|
-
|
|
|
42
|
+
| Code pull request and code push | `ci.yml` (Unit engine and Unit, Linux Node 24) | `engine.test.ts` split by test name; `permission.test.ts` and `runtime.test.ts` one file at a time; every other unit file together. Typecheck and `check-generated` run on the light lane |
|
|
43
|
+
| Platform-sensitive pull request | `ci.yml` (Unit, Windows Node 24) | The same four unit lanes on `windows-latest` |
|
|
44
|
+
| Push to `main`, or manual | `ci.yml` (Validate matrix) | `validate:pr` on Linux and Windows for Node 22.19.0 and Node 24; not on a pull request |
|
|
45
|
+
| Code pull request and code push | `ci.yml` (Plugin validation) | `claude plugin validate --strict` on the marketplace and the plugin |
|
|
41
46
|
| Daily at 04:00 UTC, or manual | `nightly.yml` | `test:coverage:complete`, `check`, `check:generated`, `npm pack --dry-run` |
|
|
42
47
|
| Merged pull request | `auto-release.yml` | Tags the merge commit and dispatches `release.yml` |
|
|
43
48
|
| Tag push or dispatch | `release.yml` | Verifies, packs and publishes (see [Release flow](#release-flow)) |
|
|
44
49
|
| Manual only | `smoke.yml` | Real Pi smoke, currently disabled (see [Smoke tests](#smoke-tests)) |
|
|
45
|
-
| Pull request, or manual | `e2e.yml` | `npm run e2e` on `ubuntu-latest`: Obscura v0.2.3 plus the Playwright smoke test.
|
|
50
|
+
| Pull request, or manual | `e2e.yml` | `npm run e2e` on `ubuntu-latest`: Obscura v0.2.3 plus the Playwright smoke test. Separate from `ci.yml` |
|
|
46
51
|
|
|
47
52
|
The npm scripts behind those rows:
|
|
48
53
|
|
|
49
54
|
| Script | Composition |
|
|
50
55
|
|---|---|
|
|
51
56
|
| `verify` | `npm test` (core and package unit tests), `check`, `check:generated` |
|
|
57
|
+
| `test:ci-shard` | One unit lane: build, then `engine <index> <total>`, `serial`, or `light` over `test/core/*.test.ts` and `packages/core/*/tests/unit/*.test.ts` |
|
|
52
58
|
| `validate:pr` | `build`, `typecheck`, a compact contract and smoke set of nine `test/core` files, `check:versions`, and the generated-`dist` check |
|
|
53
|
-
| `validate:ci` | `test:coverage` (core and package tests, 91/80/92 floors), `check`, `npm pack --dry-run`;
|
|
59
|
+
| `validate:ci` | `test:coverage` (core and package tests, 91/80/92 floors), `check`, `npm pack --dry-run`; the Release workflow runs it, and it stays available locally |
|
|
54
60
|
| `test:coverage:complete` | Core, simulation and package tests with 93/80/93 floors |
|
|
55
61
|
| `check` | `typecheck`, `lint:docs`, `check:versions` |
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`npm
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
63
|
+
What moved off the pull-request lane, and what did not:
|
|
64
|
+
|
|
65
|
+
- The four `validate:pr` cells — Linux and Windows, Node 22.19.0 and Node 24 —
|
|
66
|
+
run on a push to `main` and on `workflow_dispatch`. They do not run on a
|
|
67
|
+
pull request. The nine files inside `validate:pr` still run on a code pull
|
|
68
|
+
request, because they are part of the Linux Node 24 unit suite.
|
|
69
|
+
- Node 22.19.0 does not run the unit suite on a pull request. It runs
|
|
70
|
+
`validate:pr` on `main`.
|
|
71
|
+
- Windows runs the unit suite on a pull request only when the classifier marks
|
|
72
|
+
the change platform-sensitive. Every push to `main` that changes code still
|
|
73
|
+
runs `validate:pr` on Windows.
|
|
74
|
+
- Coverage, `test/simulations`, and `npm pack --dry-run` stay in the nightly
|
|
75
|
+
complete suite. They were not part of pull-request CI before this split.
|
|
76
|
+
- Plugin validation still runs on code pull requests and code pushes.
|
|
77
|
+
|
|
78
|
+
Run `npm run verify` locally before every push. A nightly failure is a release
|
|
79
|
+
blocker: it is the only place the simulation suite and coverage floors run.
|
|
80
|
+
|
|
81
|
+
### Lanes and the required check
|
|
82
|
+
|
|
83
|
+
Code pull requests run Docs lint, two Linux Node 24 engine shards, a serial
|
|
84
|
+
lane (`permission.test.ts` then `runtime.test.ts`), a light lane for every
|
|
85
|
+
other unit file, and Plugin validation. The light lane typechecks and checks
|
|
86
|
+
generated bundles. `engine.test.ts` is split by test name because that file
|
|
87
|
+
alone was 174 seconds; the serial lane keeps the next two longest files off
|
|
88
|
+
the light pool, which was 268 seconds when every non-engine file shared one
|
|
89
|
+
job. Linux jobs use the npm cache from
|
|
90
|
+
`actions/setup-node`. Restoring a `node_modules` tarball was slower than
|
|
91
|
+
`npm ci` on the Linux runners (about 24s versus 17s on 2026-09-24), so that
|
|
92
|
+
cache stays on the Windows jobs, where `npm ci` is the slow step.
|
|
93
|
+
|
|
94
|
+
The job `required` always runs. Its check name is `CI / required`. It fails
|
|
95
|
+
when a lane fails or is cancelled, and it passes when a lane was skipped
|
|
96
|
+
because the change did not need it. Add `CI / required` as a required status
|
|
97
|
+
check in the `protect-main` ruleset. Skipped matrix legs are not required
|
|
98
|
+
names, so they do not block auto-merge. This repository change does not edit
|
|
99
|
+
that ruleset.
|
|
100
|
+
|
|
101
|
+
A newer push cancels an older pull request run. Runs on `main` are never
|
|
102
|
+
cancelled. Unit and Validate matrices use `fail-fast`.
|
|
103
|
+
|
|
104
|
+
The Validate matrix still runs on Node 22.19.0 and Node 24, on Linux and
|
|
105
|
+
Windows, for pushes to `main` and for `workflow_dispatch`. Linux uses the ARC
|
|
106
|
+
scale set `kontextmind-doks` with a three-minute job timeout. Windows uses
|
|
107
|
+
GitHub-hosted `windows-latest` with a fifteen-minute timeout so `npm ci` can
|
|
108
|
+
finish. Those four job names are not the required check anymore.
|
|
78
109
|
|
|
79
110
|
### CI jobs stay queued while a runner is online
|
|
80
111
|
|
|
@@ -111,20 +142,26 @@ autoscaler. Do not relabel `km-gh-rn01` or push an empty commit as a routing
|
|
|
111
142
|
workaround.
|
|
112
143
|
|
|
113
144
|
> [!NOTE]
|
|
114
|
-
> Windows
|
|
115
|
-
>
|
|
116
|
-
>
|
|
117
|
-
>
|
|
118
|
-
> `test/core/worker.test.ts
|
|
145
|
+
> Windows jobs use GitHub-hosted `windows-latest`, not a self-hosted homelab
|
|
146
|
+
> runner and not the ARC scale set. Do not add a `kontextmind-doks` label to a
|
|
147
|
+
> Windows runner. Nightly complete coverage and release stay on Linux.
|
|
148
|
+
> Platform-sensitive pull requests run the unit suite on Windows, which
|
|
149
|
+
> includes `test/core/worker.test.ts`. The main Validate legs run the compact
|
|
150
|
+
> `validate:pr` gate.
|
|
119
151
|
|
|
120
152
|
### The docs-only classifier
|
|
121
153
|
|
|
122
154
|
The first job, Classify changes, lists the changed paths and sets `code=false`
|
|
123
|
-
when every path matches `*.md
|
|
124
|
-
and PR templates, or `dependabot.yml`.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
`ci-contract.test.ts`
|
|
155
|
+
when every path matches `*.md` (including `plans/**/*.md`), `docs/**`,
|
|
156
|
+
`.kxm/assets/**`, `LICENSE`, the issue and PR templates, or `dependabot.yml`.
|
|
157
|
+
A non-markdown file under `plans/` is code. Unit lanes, Plugin validation,
|
|
158
|
+
and the Validate matrix are skipped. Docs lint runs, and `CI / required`
|
|
159
|
+
passes. `scripts/ci-classify.mjs` and `ci-contract.test.ts` pin this behavior.
|
|
160
|
+
|
|
161
|
+
`platform=true` when a path is a package manifest, the lockfile, a file under
|
|
162
|
+
`scripts/` or `.github/workflows/`, or a filename that names path, process,
|
|
163
|
+
shell, spawn, worker, supervisor, repo-root, or ssh-remote behavior. A
|
|
164
|
+
platform-sensitive pull request also runs the Windows Node 24 unit lanes.
|
|
128
165
|
|
|
129
166
|
> [!WARNING]
|
|
130
167
|
> Several tests and code paths read documentation files by path. A pull request
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: "Operating rules"
|
|
3
3
|
description: "Standing instructions from the operator that every agent session on this repository follows, with the date each was given. Read before planning, dispatching, landing, or editing the roadmap."
|
|
4
4
|
audience: "agents and maintainers"
|
|
5
|
-
updated: "2026-09-
|
|
5
|
+
updated: "2026-09-27"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Operating rules
|
|
@@ -58,6 +58,18 @@ replacement.
|
|
|
58
58
|
`impl-bg`) retire last, after one real unit has run through the one-step
|
|
59
59
|
workflows. (2026-09-26.)
|
|
60
60
|
|
|
61
|
+
## Accounts
|
|
62
|
+
|
|
63
|
+
- **Machine account names follow [ADR-0006](../adr/ADR-0006-machine-account-names.md).**
|
|
64
|
+
Platform-wide accounts are `svc-<system>-<purpose>`. Tenant-scoped accounts
|
|
65
|
+
are `svc-<tenant>-<system>-<purpose>`, where `<tenant>` is the tenant slug.
|
|
66
|
+
Test, witness, and proof accounts use those shapes with a `test-` prefix
|
|
67
|
+
and are never members of production groups. Authentik-managed accounts
|
|
68
|
+
(outposts and `ak-*`) are exempt. Recorded names such as `kxm-agent`,
|
|
69
|
+
`kxm-witness-*`, `witness9`, and `kxmdproof` stay until an approved
|
|
70
|
+
inventory. Do not invent a replacement for a specific account.
|
|
71
|
+
(2026-09-27.)
|
|
72
|
+
|
|
61
73
|
## Decisions and debt
|
|
62
74
|
|
|
63
75
|
- **Every option comes with pros and cons and two recommendations**, the
|
|
@@ -68,7 +68,7 @@ Tools map the same way: peer tools to `kxm-peer`, workflow tools to `kxm-workflo
|
|
|
68
68
|
|
|
69
69
|
## Browser automation skills
|
|
70
70
|
|
|
71
|
-
Playwright testing and verification use Obscura ([ADR-0005](../adr/ADR-0005-obscura-default-playwright.md)). The Steel skills cover human takeover, MFA, and the live session viewer.
|
|
71
|
+
Playwright testing and verification use Obscura ([ADR-0005](../adr/ADR-0005-obscura-default-playwright.md)). The Steel skills cover remote and hosted browsing, human takeover, MFA, and the live session viewer. Steel is `https://steel.kontextmind.com`, reached only through Caddy and Authentik ([ADR-0007](../adr/ADR-0007-steel-caddy-authentik.md)). Send `Authorization: Basic` (`STEEL_AUTH_HEADER`, then `STEEL_AUTH_BASIC`, then `STEEL_AUTH_USER` and `STEEL_AUTH_TOKEN`) and keep the credential out of URLs. `STEEL_API_KEY` is deprecated and is not enforced. `kxm` 0.7.135 or newer is required for Steel. `kxm-browser-verify` owns `vision`. The other browser skills own no `kxm` command. See [Browser automation](browser-automation.md).
|
|
72
72
|
|
|
73
73
|
| Skill | Use it to |
|
|
74
74
|
|---|---|
|