@cairnkeep/cli 2.22.3 → 2.22.4

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 (58) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/containers/healthcheck.mjs +27 -33
  3. package/containers/quadlet/cairnkeep.container +1 -1
  4. package/docs/containers.md +12 -0
  5. package/docs/domain-knowledge.md +15 -0
  6. package/docs/learning/CURRICULUM-MAP.md +1 -1
  7. package/docs/learning/FEATURE-GUIDE.md +1 -1
  8. package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
  9. package/docs/learning/lessons/L01-safe-trial.md +3 -3
  10. package/docs/learning/lessons/L02-installation.md +2 -2
  11. package/docs/learning/lessons/L03-first-project.md +1 -1
  12. package/docs/learning/lessons/L19-validated-skill-improvement.md +1 -1
  13. package/docs/learning/lessons/L20-mcp-tool-profiles.md +1 -1
  14. package/docs/learning/lessons/L21-context-packs.md +1 -1
  15. package/docs/learning/lessons/L22-native-windows.md +2 -2
  16. package/docs/learning/lessons/L23-guided-setup.md +1 -1
  17. package/docs/learning/lessons/L24-okf-exchange.md +1 -1
  18. package/docs/learning/lessons/L25-playbooks.md +1 -1
  19. package/docs/learning/lessons/L26-context-intelligence.md +1 -1
  20. package/docs/learning/lessons/L27-ecosystem-evidence.md +1 -1
  21. package/docs/learning/lessons/L28-security-assurance.md +52 -2
  22. package/docs/learning/video-scripts/V28-security-assurance.md +45 -0
  23. package/docs/native-windows.md +10 -0
  24. package/docs/operating.md +10 -0
  25. package/docs/privacy-and-data-flow.md +6 -0
  26. package/docs/releasing.md +3 -2
  27. package/docs/security-assurance.md +67 -0
  28. package/examples/eval/bundled-fake.json +1 -1
  29. package/mcp-memory-server/dist/capability-registry.js +2 -15
  30. package/mcp-memory-server/dist/context-pack-retrieval.js +6 -6
  31. package/mcp-memory-server/dist/domain-retrieval-provider.js +100 -45
  32. package/mcp-memory-server/dist/eval-plan.js +15 -22
  33. package/mcp-memory-server/dist/eval-runner.js +17 -10
  34. package/mcp-memory-server/dist/eval-workspace.js +82 -23
  35. package/mcp-memory-server/dist/failure-signature.js +29 -8
  36. package/mcp-memory-server/dist/graph-cli.js +25 -10
  37. package/mcp-memory-server/dist/okf.js +4 -9
  38. package/mcp-memory-server/dist/playbook-receipt.js +3 -17
  39. package/mcp-memory-server/dist/skill-store.js +10 -26
  40. package/mcp-memory-server/dist/stable-file.js +85 -0
  41. package/mcp-memory-server/dist/work-evidence-store.js +7 -5
  42. package/omp/extensions/cairnkeep-capture.ts +29 -17
  43. package/package.json +1 -1
  44. package/scripts/lib/opencode-v2-plugin-harness.mjs +20 -3
  45. package/scripts/lib/stable-file.mjs +75 -0
  46. package/scripts/playbook-instructions.mjs +42 -20
  47. package/scripts/setup-core.mjs +5 -36
  48. package/scripts/setup-reconcile.mjs +32 -32
  49. package/scripts/setup.mjs +7 -31
  50. package/scripts/test-ci-sharding.sh +5 -0
  51. package/scripts/test-container-health.mjs +56 -0
  52. package/scripts/test-container.sh +8 -1
  53. package/scripts/test-learning-docs.sh +8 -0
  54. package/scripts/test-omp-staged-security.mjs +110 -0
  55. package/scripts/test-runtime-file-security.mjs +311 -0
  56. package/scripts/test-windows-native.mjs +3 -0
  57. package/scripts/verify-omp-capture.mjs +1 -0
  58. package/scripts/windows-platform.mjs +54 -22
package/CHANGELOG.md CHANGED
@@ -5,6 +5,60 @@ All notable user-facing changes are documented here. This project follows
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [2.22.4] - 2026-10-07
9
+
10
+ ### Security
11
+
12
+ - Remove ambiguous nested path expressions from failure signatures. Crafted
13
+ slash-heavy messages no longer cause exponential backtracking; existing v1
14
+ fixture signatures remain byte-equivalent. Add timeout-isolated regression
15
+ inputs and a reproducible generated corpus.
16
+ - Bind capability compatibility configuration, skill artifacts/targets/backups
17
+ and work-evidence reads to inspected descriptors with fixed byte bounds,
18
+ identity rechecks and strict UTF-8. Reject replacement, growth and special-file
19
+ substitution; preserve private-ledger permissions and ordinary project-file
20
+ modes. Keep schemas, feature defaults and explicit approval gates unchanged.
21
+ Update security guidance and the learning/video supplement; historical
22
+ analysis findings remain subject to individual triage.
23
+ - Bind evaluation inputs/note snapshots, OKF import/export, graph artifacts,
24
+ playbook receipts and progressive-context cache reads to bounded inspected
25
+ descriptors. Reject replacement and growth before accepting content; publish
26
+ snapshots from the validated bytes instead of reopening source paths.
27
+ Rebuild unsafe derived caches deterministically without changing pack
28
+ enablement or skill approval. Add public-reader regression controls and
29
+ update security, learning and video guidance.
30
+ - Bound setup policy/state/ownership and instruction-file reads to 1 MiB;
31
+ bound OMP staged candidates to 8 KiB and exclude linked or oversized files.
32
+ Recheck observed setup/instruction targets before publication; preserve
33
+ caller edits detected after inspection, original instruction modes and
34
+ failed-operation state. Keep private approvals and machine sync explicit.
35
+ On Windows, use content ownership rather than POSIX executable-mode bits
36
+ when reconciling generated files; keep private-state ACL enforcement.
37
+ - Publish evaluation overlays and Windows managed files through exclusive
38
+ descriptor-written temporary files. Reject substituted targets, clean only
39
+ owned temporary files, and never unlink live Windows data to retry a denied
40
+ replacement. Bound compressed and expanded memory imports to 512 MiB.
41
+ - Keep container health checks on a validated numeric loopback port, using the
42
+ server's bounded credential resolver. Refuse provider URL credentials and
43
+ redirects, bound AnythingLLM query responses to 8 MiB, and sanitize provider
44
+ HTTP, network and stream failures without including response bodies.
45
+ Reject workspace dot segments, including shorthand OpenViking traversal.
46
+ Preserve default retrieval providers and successful response shapes.
47
+
48
+ ### CI
49
+
50
+ - Preserve the bundled evaluation task set's canonical LF bytes in native
51
+ Windows source checkouts. Add a real Git CRLF-conversion regression control;
52
+ keep the installed-package exact-byte integrity check unchanged.
53
+ - Canonicalize bounded-read fixture paths before injecting file changes, covering
54
+ Windows short temporary-directory names and a real POSIX alias control.
55
+ - Run focused bounded-input controls before the longer Windows smoke shards on
56
+ all supported Node versions; retain every complete-catalog and install gate.
57
+ - Wait for completed OpenCode V2 fixture capture before reading its database,
58
+ under a bounded event-stream completion deadline. A deterministic delayed
59
+ completion control rejects the old file-existence readiness assumption;
60
+ production capture behavior is unchanged.
61
+
8
62
  ## [2.22.3] - 2026-10-07
9
63
 
10
64
  ### Fixed
@@ -727,7 +781,8 @@ stored memories.
727
781
  - Preserve executable permissions and Bash 3.2 portability on macOS.
728
782
  - Add backup-first uninstall and SQLite-safe memory export/import guidance.
729
783
 
730
- [Unreleased]: https://github.com/cairnkeep/cairnkeep/compare/v2.20.0...HEAD
784
+ [Unreleased]: https://github.com/cairnkeep/cairnkeep/compare/v2.22.4...HEAD
785
+ [2.22.4]: https://github.com/cairnkeep/cairnkeep/compare/v2.22.3...v2.22.4
731
786
  [2.20.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.19.1...v2.20.0
732
787
  [2.19.1]: https://github.com/cairnkeep/cairnkeep/compare/v2.19.0...v2.19.1
733
788
  [2.19.0]: https://github.com/cairnkeep/cairnkeep/compare/v2.18.0...v2.19.0
@@ -1,22 +1,6 @@
1
- import { readFileSync } from "node:fs";
2
-
3
- const port = process.env.MCP_HTTP_PORT?.trim();
4
- if (!port) {
5
- process.exit(0);
6
- }
7
-
8
- let token = process.env.CAIRN_MEMORY_HTTP_TOKEN?.trim();
9
- const tokenFile = process.env.CAIRN_MEMORY_HTTP_TOKEN_FILE?.trim();
10
- if (!token && tokenFile) {
11
- try {
12
- token = readFileSync(tokenFile, "utf8").trim();
13
- } catch {
14
- process.exit(1);
15
- }
16
- }
17
- if (!token) {
18
- process.exit(1);
19
- }
1
+ import { realpathSync } from "node:fs";
2
+ import { pathToFileURL } from "node:url";
3
+ import { parseHttpPort, resolveHttpToken } from "../mcp-memory-server/dist/http-security.js";
20
4
 
21
5
  const body = {
22
6
  jsonrpc: "2.0",
@@ -29,18 +13,28 @@ const body = {
29
13
  },
30
14
  };
31
15
 
32
- try {
33
- const response = await fetch(`http://127.0.0.1:${port}/mcp`, {
34
- method: "POST",
35
- headers: {
36
- Authorization: `Bearer ${token}`,
37
- "Content-Type": "application/json",
38
- Accept: "application/json, text/event-stream",
39
- },
40
- body: JSON.stringify(body),
41
- signal: AbortSignal.timeout(4000),
42
- });
43
- process.exit(response.ok ? 0 : 1);
44
- } catch {
45
- process.exit(1);
16
+ export async function containerHealthcheck(env = process.env) {
17
+ const port = parseHttpPort(env.MCP_HTTP_PORT);
18
+ if (!port.enabled) return "error" in port ? 1 : 0;
19
+ const token = resolveHttpToken(env);
20
+ if (!token.ok) return 1;
21
+ try {
22
+ const response = await fetch(`http://127.0.0.1:${port.port}/mcp`, {
23
+ method: "POST",
24
+ headers: {
25
+ Authorization: `Bearer ${token.token}`,
26
+ "Content-Type": "application/json",
27
+ Accept: "application/json, text/event-stream",
28
+ },
29
+ body: JSON.stringify(body),
30
+ redirect: "manual",
31
+ signal: AbortSignal.timeout(4000),
32
+ });
33
+ await response.body?.cancel();
34
+ return response.ok ? 0 : 1;
35
+ } catch { return 1; }
36
+ }
37
+
38
+ if (process.argv[1] && pathToFileURL(realpathSync(process.argv[1])).href === import.meta.url) {
39
+ process.exitCode = await containerHealthcheck();
46
40
  }
@@ -9,7 +9,7 @@ ContainerName=cairnkeep
9
9
  Exec=http
10
10
  UserNS=keep-id:uid=10001,gid=10001
11
11
  Volume=cairnkeep-data.volume:/data:Z
12
- Secret=cairnkeep-http-token,type=mount,target=http-token
12
+ Secret=cairnkeep-http-token,type=mount,target=http-token,uid=10001,gid=10001,mode=0400
13
13
  Environment=MCP_HTTP_HOST=0.0.0.0
14
14
  Environment=MCP_HTTP_PORT=7801
15
15
  Environment=CAIRN_MEMORY_HTTP_ALLOWED_HOSTS=localhost:7801,127.0.0.1:7801
@@ -80,6 +80,18 @@ cairn-container http \
80
80
 
81
81
  The container always adds its own loopback address for health checks.
82
82
 
83
+ The health-check security follow-up validates `MCP_HTTP_PORT` with the server's
84
+ numeric port parser and uses its bounded private token resolver. Invalid
85
+ configuration fails without issuing a request; probes target only
86
+ `127.0.0.1` and refuse redirects. The existing
87
+ `/usr/local/lib/cairnkeep/container-healthcheck.mjs` command remains available
88
+ as an alias to the packaged probe. Stdio mode requires no HTTP probe.
89
+ File-based probes require a private token readable by the container user;
90
+ world-readable secret mounts fail closed. The Quadlet example explicitly mounts
91
+ its secret as UID/GID 10001 with mode `0400`. For other engines, verify the
92
+ effective in-container ownership and mode rather than relying on a secret
93
+ declaration that the engine may not honor.
94
+
83
95
  One HTTP server and bearer token form one trust domain. They are not a
84
96
  multi-tenant authorization boundary; see [Memory storage and
85
97
  deployment](storage.md).
@@ -32,6 +32,17 @@ work exactly the same without them.
32
32
  | `CAIRN_ANYTHINGLLM_PROJECTS_FILE` | Optional config path for the bundled sync script. Default `${XDG_CONFIG_HOME:-~/.config}/cairnkeep/anythingllm-projects.json`. |
33
33
  | `CAIRN_ANYTHINGLLM_STATE_FILE` | Optional state path for the bundled sync script. Default `${XDG_STATE_HOME:-~/.local/state}/cairnkeep/anythingllm-sync.json`. |
34
34
 
35
+ The query security follow-up validates HTTP(S) URLs and rejects embedded
36
+ credentials, query strings and fragments before fetching. Redirects are not
37
+ followed, and workspace names cannot be empty or URL dot segments. Responses
38
+ are streamed under an 8 MiB cap and must be UTF-8 JSON
39
+ objects; successful answer-field selection is unchanged. Failed requests
40
+ report only a controlled failure class or HTTP status, never a provider error
41
+ body or raw network/stream diagnostic. The existing two-minute query deadline
42
+ remains. Configure HTTPS outside loopback: remote plaintext HTTP remains
43
+ accepted for compatibility, not as a recommendation. Document synchronization
44
+ uses a separate script and does not inherit these query-specific guarantees.
45
+
35
46
  The workspaces the tools target come from the project's **memory config** — the
36
47
  first of `.agent/memory.json`, `.opencode/memory.json`, `.claude/memory.json`, or
37
48
  `memory.json` that exists:
@@ -117,6 +128,10 @@ CAIRN_OPENVIKING_BASE_URL=http://127.0.0.1:1933
117
128
 
118
129
  Plain HTTP is limited to loopback. Use HTTPS for any other host. Credentials in
119
130
  the URL, redirects, invalid response shapes, and oversized bodies are rejected.
131
+ The response cap is 1 MiB. Network and stream failures are payload-free in the
132
+ security follow-up, including failures during response cleanup.
133
+ Shorthand workspace names and explicit `viking://resources` URIs share the
134
+ same decoded traversal checks before any request is sent.
120
135
  Authenticated remote MCP use requires the additional explicit consent setting
121
136
  `CAIRN_OPENVIKING_MCP_HTTP=1`.
122
137
 
@@ -1,6 +1,6 @@
1
1
  # Curriculum Coverage Map
2
2
 
3
- **Baseline:** Cairnkeep 2.22.3
3
+ **Baseline:** Cairnkeep 2.22.4
4
4
  **Last reviewed:** 2026-10-07
5
5
 
6
6
  This map prevents the course from drifting behind the product. It assigns each
@@ -1,6 +1,6 @@
1
1
  # Cairnkeep feature guide
2
2
 
3
- **Baseline:** Cairnkeep 2.22.3
3
+ **Baseline:** Cairnkeep 2.22.4
4
4
 
5
5
  This is the step-back map for choosing and operating Cairnkeep features. The
6
6
  [course labs](https://github.com/cairnkeep/cairnkeep-course-labs) provide one
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 10 minutes
6
- **Tested with:** Cairnkeep 2.22.3
6
+ **Tested with:** Cairnkeep 2.22.4
7
7
 
8
8
  ## Outcome
9
9
 
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 15 minutes
6
- **Tested with:** Cairnkeep 2.22.3
6
+ **Tested with:** Cairnkeep 2.22.4
7
7
 
8
8
  ## Outcome
9
9
 
@@ -26,7 +26,7 @@ ephemeral; the named course volume is persistent.
26
26
  1. Pull the public image:
27
27
 
28
28
  ```bash
29
- podman pull ghcr.io/cairnkeep/cairnkeep:2.22.3
29
+ podman pull ghcr.io/cairnkeep/cairnkeep:2.22.4
30
30
  ```
31
31
 
32
32
  2. Start the stdio server with a read-only root filesystem and no Linux
@@ -38,7 +38,7 @@ ephemeral; the named course volume is persistent.
38
38
  --read-only --cap-drop=all --security-opt=no-new-privileges \
39
39
  --tmpfs=/tmp:rw,noexec,nosuid,size=64m,mode=1777 \
40
40
  --volume cairnkeep-course-data:/data:Z,U \
41
- ghcr.io/cairnkeep/cairnkeep:2.22.3 stdio
41
+ ghcr.io/cairnkeep/cairnkeep:2.22.4 stdio
42
42
  ```
43
43
 
44
44
  3. The process waits quietly for MCP input. Press `Ctrl-C` after confirming it
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 25 minutes
6
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
6
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
7
7
 
8
8
  ## Outcome
9
9
 
@@ -47,7 +47,7 @@ sync. Codex project trust remains an explicit operator decision.
47
47
  2. Install Cairnkeep:
48
48
 
49
49
  ```bash
50
- npm install --global @cairnkeep/cli@2.22.3
50
+ npm install --global @cairnkeep/cli@2.22.4
51
51
  cairn --version
52
52
  ```
53
53
 
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 25 minutes
6
- **Tested with:** Cairnkeep 2.22.3
6
+ **Tested with:** Cairnkeep 2.22.4
7
7
 
8
8
  ## Outcome
9
9
 
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Evidence and Evaluation
5
5
  **Time:** 50 minutes
6
- **Tested with:** Cairnkeep 2.22.3
6
+ **Tested with:** Cairnkeep 2.22.4
7
7
 
8
8
  ## Outcome
9
9
 
@@ -1,7 +1,7 @@
1
1
  # L20 - Least-authority MCP tool profiles
2
2
 
3
3
  **Status:** Ready
4
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
5
5
  **Time:** 25 minutes
6
6
 
7
7
  ## Outcome
@@ -1,7 +1,7 @@
1
1
  # L21 - Immutable context packs and skill approval
2
2
 
3
3
  **Status:** Ready
4
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
5
5
  **Time:** 40 minutes
6
6
 
7
7
  ## Outcome
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Operator
5
5
  **Time:** 30 minutes
6
- **Tested with:** Cairnkeep 2.22.3, Windows x64, and Node.js 22 or newer
6
+ **Tested with:** Cairnkeep 2.22.4, Windows x64, and Node.js 22 or newer
7
7
 
8
8
  ## Outcome
9
9
 
@@ -16,7 +16,7 @@ perform a reversible uninstall without relying on WSL or Git Bash.
16
16
  From PowerShell:
17
17
 
18
18
  ```powershell
19
- npm install --global @cairnkeep/cli@2.22.3
19
+ npm install --global @cairnkeep/cli@2.22.4
20
20
  cairn version
21
21
 
22
22
  $Lab = Join-Path $env:TEMP 'Cairnkeep Windows Lab'
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart and Operator
5
5
  **Time:** 35 minutes
6
- **Tested with:** Cairnkeep 2.22.3; Pi 0.84.1; Node.js 22, 24, and 26
6
+ **Tested with:** Cairnkeep 2.22.4; Pi 0.84.1; Node.js 22, 24, and 26
7
7
 
8
8
  ## Outcome
9
9
 
@@ -1,7 +1,7 @@
1
1
  # L24 - Reviewed knowledge exchange
2
2
 
3
3
  **Status:** Ready
4
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
5
5
  **Time:** 35 minutes
6
6
 
7
7
  ## Outcome
@@ -1,7 +1,7 @@
1
1
  # L25 - Bounded workflow playbooks
2
2
 
3
3
  **Status:** Ready
4
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
5
5
  **Time:** 35 minutes
6
6
 
7
7
  ## Outcome
@@ -1,7 +1,7 @@
1
1
  # L26 - Context intelligence with explicit authority
2
2
 
3
3
  **Status:** Ready
4
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
5
5
 
6
6
  ## Outcome
7
7
 
@@ -1,6 +1,6 @@
1
1
  # L27 - Evaluate context companions without moving authority
2
2
 
3
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
3
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
4
4
 
5
5
  ## Outcome
6
6
 
@@ -1,7 +1,7 @@
1
1
  # L28 - Verify Cairnkeep's security posture
2
2
 
3
3
  **Status:** Ready
4
- **Tested with:** Cairnkeep 2.22.3 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.22.4 and Node.js 22 or newer
5
5
 
6
6
  ## Outcome
7
7
 
@@ -11,7 +11,7 @@ and choose least authority for a network-facing MCP client.
11
11
 
12
12
  ## Prerequisites
13
13
 
14
- - Install Cairnkeep 2.22.3 or use a disposable source checkout.
14
+ - Install Cairnkeep 2.22.4 or use a disposable source checkout.
15
15
  - Complete [L20 - Least-authority MCP tool profiles](L20-mcp-tool-profiles.md).
16
16
  - Use synthetic values only; never paste a real bearer token into a recording,
17
17
  terminal transcript, or issue.
@@ -68,6 +68,14 @@ scripts/test-security-baseline.sh
68
68
  npm --prefix mcp-memory-server run check:security-assurance
69
69
  npm --prefix mcp-memory-server run check:http-guard
70
70
  node mcp-memory-server/scripts/smoke-context-pack-state.mjs
71
+ node mcp-memory-server/scripts/smoke-note-signatures.mjs
72
+ node mcp-memory-server/scripts/smoke-runtime-file-reads.mjs
73
+ node mcp-memory-server/scripts/smoke-bounded-input-reads.mjs
74
+ node mcp-memory-server/scripts/smoke-workspace-file-security.mjs
75
+ node mcp-memory-server/scripts/smoke-outbound-security.mjs
76
+ node scripts/test-runtime-file-security.mjs
77
+ node scripts/test-omp-staged-security.mjs
78
+ node scripts/test-container-health.mjs
71
79
  ```
72
80
 
73
81
  The baseline rejects unpinned external Actions, `pull_request_target` and
@@ -91,6 +99,48 @@ under bounded reads. A resolved review thread or green CodeQL job does not mean
91
99
  all historical alerts are fixed: inspect current findings and test the actual
92
100
  boundary without dismissing results merely to improve an advisory score.
93
101
 
102
+ In a checkout containing the runtime-security follow-up, the signature control
103
+ keeps existing v1 fingerprints byte-equivalent and isolates crafted path inputs
104
+ behind a five-second child-process timeout. The runtime file-read control
105
+ substitutes valid capability configuration, skill targets/ledgers/backups and
106
+ work-evidence records after inspection. Readers reject those replacements,
107
+ bound growth, reject special files without waiting for a writer and close their
108
+ descriptors. Ordinary project configuration may remain `0644`; private evidence
109
+ and approval ledgers still require private ownership/ACLs. Build the server
110
+ before running source-checkout controls. The generated parser corpus is
111
+ reproducible regression coverage, not integrated coverage-guided fuzzing.
112
+
113
+ The bounded-input follow-up extends descriptor-bound reads to evaluation,
114
+ OKF import/export, graph artifacts, private playbook receipts and derived
115
+ progressive-context caches. Compare a replaced valid file with same-inode
116
+ growth: rejecting oversized content after `readFile` has allocated it is not
117
+ a bounded read. Note snapshots publish the bytes that were inspected and
118
+ hashed, not a later reopened file. Unsafe derived caches can be rebuilt from
119
+ immutable content; read-only doctor reports them without deletion. Cache
120
+ rebuilding never changes project enablement or skill approval. This exercise
121
+ does not certify directory-wide transactions against other same-account
122
+ processes.
123
+
124
+ The publication follow-up extends the exercise to setup, instruction files,
125
+ evaluation overlays and native Windows managed writes. Inspect a concurrent
126
+ caller edit: rechecking the observed target before publication must preserve
127
+ it. A denied Windows replacement must retain the live database, not unlink it
128
+ to retry. A substituted overlay symlink must leave its outside sentinel intact.
129
+ Exclusive descriptor-written temporaries retain modes and clean only their own
130
+ files. These are point-in-time safeguards, not all-project transactions or
131
+ protection against another hostile process using your account. Native Windows
132
+ and container/package verification remain required before shipping.
133
+
134
+ The OMP control reads only synthetic staged candidates, never invokes a model,
135
+ and shows that linked, growing or over-8-KiB content cannot become wakeup
136
+ evidence or `/cairn-staged` output. It does not approve or persist a candidate.
137
+ The outbound and health controls stub network calls: provider redirects and
138
+ oversized bodies fail, private error sentinels never appear in diagnostics,
139
+ and a malformed health port never becomes a network destination. Successful
140
+ retrieval still sends a query to the explicitly configured external provider.
141
+ AnythingLLM responses have an 8 MiB cap; OpenViking retains its 1 MiB cap and
142
+ separate consent gates. Prefer HTTPS outside loopback.
143
+
94
144
  ## Common failures
95
145
 
96
146
  - A private `.ai` file is group/world accessible on POSIX: restore mode `0600`.
@@ -3,6 +3,11 @@
3
3
  Target length: 10–12 minutes. Audience: experienced developers operating an
4
4
  agent-memory service locally or behind authenticated HTTP.
5
5
 
6
+ The source-checkout security follow-up blocks below are optional supplemental
7
+ clips, not part of the original 10–12-minute timing. Record them as a separate
8
+ segment after their branch passes the complete release gates; do not present
9
+ unreleased controls as installed-package behavior.
10
+
6
11
  ## Recording outline
7
12
 
8
13
  ### 0:00–1:20 — Threat model before tooling
@@ -69,6 +74,46 @@ rejection, bounded growth and cleanup; use no real private pointer/approval
69
74
  data. Distinguish resolved old review comments from current analysis findings,
70
75
  and do not imply a green CI run certifies an empty security backlog.
71
76
 
77
+ For the runtime-security follow-up, show
78
+ `node mcp-memory-server/scripts/smoke-note-signatures.mjs` and
79
+ `node mcp-memory-server/scripts/smoke-runtime-file-reads.mjs` from a built source
80
+ checkout. Explain that a tiny crafted path used to stall signature parsing,
81
+ while the fixed parser preserves the existing v1 fixture fingerprints. The
82
+ child-process timeout makes a regression fail instead of freezing the demo.
83
+ Then show valid-file replacement rejection through capability, skill and
84
+ work-evidence readers. Explain fixed byte bounds and descriptor cleanup, and
85
+ distinguish private ledger permissions from ordinary project-file modes. Do not
86
+ call the generated corpus coverage-guided fuzzing or claim that this patch
87
+ resolves every historical analysis alert. Use only the synthetic fixtures.
88
+
89
+ In a checkout with the bounded-input follow-up, run
90
+ `node mcp-memory-server/scripts/smoke-bounded-input-reads.mjs`. Zoom into
91
+ valid-file substitution and same-inode growth through evaluation, OKF, graph,
92
+ receipt and cache entry points. Contrast a byte cap checked after allocation
93
+ with a fixed descriptor-read allocation. Show that note snapshots publish the
94
+ validated bytes and rejected derived cache data is rebuilt, while read-only
95
+ doctor never deletes it. Explain that this does not enable a pack or approve a
96
+ skill, make directory-wide publication transactional, or substitute for native
97
+ Windows verification. Keep a pause between the failure and remediation.
98
+
99
+ For the publication follow-up, show
100
+ `node scripts/test-runtime-file-security.mjs` beside
101
+ `node mcp-memory-server/scripts/smoke-workspace-file-security.mjs`. Pause on the
102
+ concurrent caller edit and denied-replacement controls: the previous content
103
+ must survive. Contrast a direct path write with an exclusive, flushed temporary
104
+ file followed by publication. State that immediate parent/target checks are
105
+ not directory-wide transactions or a same-account sandbox. Native Windows
106
+ verification is still required; POSIX fixture execution is not proof of it.
107
+
108
+ Run `node scripts/test-omp-staged-security.mjs` and explain the 8 KiB candidate
109
+ bound, symlink exclusion and unchanged agent-review requirement. Then show
110
+ `node mcp-memory-server/scripts/smoke-outbound-security.mjs` and
111
+ `node scripts/test-container-health.mjs`: synthetic provider errors must stay
112
+ out of diagnostics, redirects are refused, response bytes are bounded, and
113
+ the health probe cannot turn an unvalidated port into an outside destination.
114
+ These controls stub network calls; never show real credentials or imply that
115
+ the exercise contacted a provider. Leave a reading pause after each result.
116
+
72
117
  Briefly open `.github/workflows/publish.yml` beside the permission table in
73
118
  `docs/releasing.md`. Contrast the read-only preparation job with the necessary
74
119
  npm and OCI publication grants. Explain that explicit maps deny unspecified
@@ -125,6 +125,16 @@ access, or background synchronization.
125
125
  uses `sqlite3.exe` `.backup` for a consistent snapshot of each live WAL database
126
126
  and then writes the same portable `.tgz` format used on Unix.
127
127
 
128
+ The archive/publication security follow-up binds compressed input to one
129
+ descriptor with a 512 MiB cap; decompressed tar output is independently capped
130
+ at 512 MiB. Managed writes use exclusive descriptor-written temporary files,
131
+ flush them and recheck the immediate parent/target before publication. Sharing
132
+ violations get bounded retries with at most 250 ms total backoff. A denied
133
+ replacement never unlinks the live destination; its prior contents survive.
134
+ These checks do not make a multi-database import transactional, and a post-write
135
+ ACL failure is not a rollback. Keep the pre-import backup and diagnose the
136
+ failure before retrying.
137
+
128
138
  ## Scheduling and process cleanup
129
139
 
130
140
  `cairn audit-timer` registers the opt-in `Cairnkeep Memory Audit` task through
package/docs/operating.md CHANGED
@@ -100,6 +100,16 @@ selected asset is a regular file under non-symlinked scaffold directories and
100
100
  that POSIX launchers are executable, without adopting or rewriting custom files.
101
101
  An empty ownership map is valid when all required assets were already present.
102
102
 
103
+ In the security follow-up, policy/state/owned-asset and `AGENTS.md` reads are
104
+ descriptor-bound and capped at 1 MiB. A changed, linked, oversized or invalid
105
+ UTF-8 input fails closed instead of being accepted through a later path read.
106
+ Publication rechecks observed targets and preserves concurrent caller edits
107
+ detected before replacement. Instruction reconciliation retains the original
108
+ file mode; unsafe instruction files are not rewritten. These checks are not an
109
+ all-project transaction or a sandbox against another process using your account.
110
+ Correct the reported input and rerun the printed recovery command; do not
111
+ delete caller-owned files or the ownership record to bypass a failure.
112
+
103
113
  Setup never installs or refreshes machine-level harness assets. Its
104
114
  `machine_sync.automatic` field is always false, and the human output labels each
105
115
  reported command as not run automatically. The JSON result's
@@ -683,6 +683,12 @@ skill readable; Cairnkeep never copies it into a harness or executes it.
683
683
  - The OpenViking adapter is query-only. It sends the requested workspace target
684
684
  and query to the configured endpoint, but performs no synchronization, watch,
685
685
  commit, update, or deletion. Non-loopback endpoints must use HTTPS.
686
+ - In the query security follow-up, AnythingLLM and OpenViking do not follow
687
+ redirects, bound response bytes and suppress provider error bodies and raw
688
+ network/stream diagnostics. Successful requests still send the selected
689
+ query to the configured provider. AnythingLLM retains remote HTTP support
690
+ for compatibility; choose HTTPS outside loopback. Synchronization scripts
691
+ have their own authority and do not inherit query-only guarantees.
686
692
 
687
693
  Pack digests and proposal digests protect integrity and stale-state detection;
688
694
  they do not authenticate a publisher or make extracted statements true.
package/docs/releasing.md CHANGED
@@ -47,8 +47,9 @@ does not require republishing the npm package.
47
47
  anonymous pull before announcing the release.
48
48
 
49
49
  The native-Windows lanes retain stable check names for branch protection. Each
50
- lane exercises the Windows CLI and packed global install on its Node version,
51
- while the complete memory-server smoke catalog is deterministically balanced
50
+ lane exercises the Windows CLI and packed global install on its Node version.
51
+ Focused bounded-input controls run first so platform-specific failures surface
52
+ before the longer smoke shards. The complete memory-server smoke catalog is deterministically balanced
52
53
  across the three lanes. The Linux Node 22/24/26 matrix independently runs the
53
54
  complete catalog on every supported runtime. This preserves coverage while
54
55
  removing three serial copies of the slow Windows suite from the critical path.