@cairnkeep/cli 2.20.1 → 2.21.0

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 (61) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +6 -3
  3. package/SECURITY.md +13 -0
  4. package/containers/entrypoint.sh +2 -1
  5. package/docs/README.md +2 -0
  6. package/docs/learning/CURRICULUM-MAP.md +4 -2
  7. package/docs/learning/FEATURE-GUIDE.md +6 -1
  8. package/docs/learning/README.md +1 -0
  9. package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
  10. package/docs/learning/lessons/L01-safe-trial.md +1 -1
  11. package/docs/learning/lessons/L02-installation.md +1 -1
  12. package/docs/learning/lessons/L03-first-project.md +1 -1
  13. package/docs/learning/lessons/L19-validated-skill-improvement.md +1 -1
  14. package/docs/learning/lessons/L20-mcp-tool-profiles.md +1 -1
  15. package/docs/learning/lessons/L21-context-packs.md +1 -1
  16. package/docs/learning/lessons/L22-native-windows.md +1 -1
  17. package/docs/learning/lessons/L23-guided-setup.md +1 -1
  18. package/docs/learning/lessons/L24-okf-exchange.md +1 -1
  19. package/docs/learning/lessons/L25-playbooks.md +1 -1
  20. package/docs/learning/lessons/L26-context-intelligence.md +3 -0
  21. package/docs/learning/lessons/L27-ecosystem-evidence.md +1 -1
  22. package/docs/learning/lessons/L28-security-assurance.md +104 -0
  23. package/docs/learning/video-scripts/V28-security-assurance.md +72 -0
  24. package/docs/operating.md +40 -5
  25. package/docs/privacy-and-data-flow.md +14 -0
  26. package/docs/releasing.md +9 -1
  27. package/docs/security-assurance.md +122 -0
  28. package/examples/eval/bundled-fake.json +1 -1
  29. package/mcp-memory-server/dist/artifact-schema.js +2 -7
  30. package/mcp-memory-server/dist/context-pack.js +152 -31
  31. package/mcp-memory-server/dist/eval-schema.js +13 -12
  32. package/mcp-memory-server/dist/http-security.js +104 -0
  33. package/mcp-memory-server/dist/index.js +50 -12
  34. package/mcp-memory-server/dist/okf.js +35 -9
  35. package/mcp-memory-server/dist/path-security.js +49 -0
  36. package/mcp-memory-server/dist/platform-security.js +154 -31
  37. package/mcp-memory-server/dist/project-environment.js +132 -0
  38. package/mcp-memory-server/dist/security-cli.js +46 -0
  39. package/mcp-memory-server/dist/security-doctor.js +211 -0
  40. package/mcp-memory-server/package.json +2 -1
  41. package/package.json +3 -2
  42. package/scripts/bootstrap.sh +1 -0
  43. package/scripts/cairn-cli.mjs +2 -0
  44. package/scripts/completion.sh +9 -3
  45. package/scripts/setup-reconcile.mjs +3 -0
  46. package/scripts/test-bootstrap-untracked.sh +2 -0
  47. package/scripts/test-cli-dispatch.sh +16 -1
  48. package/scripts/test-completion.sh +3 -0
  49. package/scripts/test-container-runner.sh +7 -0
  50. package/scripts/test-launcher-env-precedence.sh +30 -0
  51. package/scripts/test-learning-docs.sh +19 -0
  52. package/scripts/test-security-baseline.sh +4 -0
  53. package/scripts/verify-security-baseline.mjs +79 -0
  54. package/scripts/windows-platform.mjs +4 -2
  55. package/templates/start-claude.sh.template +3 -0
  56. package/templates/start-codex.sh.template +3 -0
  57. package/templates/start-kimi.sh.template +3 -0
  58. package/templates/start-omp.sh.template +3 -0
  59. package/templates/start-opencode.sh.template +3 -0
  60. package/templates/start-pi.sh.template +3 -0
  61. package/templates/start-qwen.sh.template +3 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,41 @@ All notable user-facing changes are documented here. This project follows
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [2.21.0] - 2026-10-07
9
+
10
+ ### Added
11
+
12
+ - Add `cairn security doctor [--project PATH] [--json]`, a read-only posture
13
+ check for private managed files, MCP authority, authenticated HTTP exposure,
14
+ Host allowlisting, token strength, and remote transport encryption.
15
+ - Add a maintained threat model and security-assurance learning lesson with
16
+ explicit assets, trust boundaries, residual risks, and verification evidence.
17
+ - Add CodeQL, pull-request dependency review, advisory OpenSSF Scorecard, and a
18
+ repository baseline gate that rejects unpinned external Actions and
19
+ `pull_request_target` workflows.
20
+
21
+ ### Security
22
+
23
+ - Apply one portable-path contract across context packs, OKF, artifacts, and
24
+ evaluation paths, rejecting Windows drive/UNC paths, alternate separators,
25
+ ADS/device aliases and forbidden characters, traversal, invalid Unicode, and
26
+ case collisions while normalizing enumerated macOS filenames.
27
+ - Bound authenticated MCP HTTP request bodies to 8 MiB and set header, request,
28
+ and keep-alive timeouts; direct and container servers now share private
29
+ token-file resolution, strict decimal port parsing, canonical IPv4/DNS/IPv6
30
+ Host authorities, and empty hosts normalize to loopback.
31
+ - Bind relative HTTP token files to the selected project, verify project and
32
+ parent replacement boundaries, and parse private `.ai/.env` files through a
33
+ descriptor-bound 64 KiB limit.
34
+ - Require trusted POSIX ownership in addition to safe mode bits, preserve
35
+ explicit HTTP ports including 80/443, and apply the same raw/canonical
36
+ portable inventory to context-pack initialization, locking, and validation.
37
+ - Reject header-unsafe Bearer values, preserve ambient-over-project launcher
38
+ precedence, retain validated OKF bytes through import, and enforce portable
39
+ collisions at evaluation-bundle admission.
40
+ - Add deterministic adversarial path cases, token non-disclosure checks, and
41
+ HTTP authorization, Host, and request-limit regression coverage.
42
+
8
43
  ## [2.20.1] - 2026-10-06
9
44
 
10
45
  ### Maintenance
package/README.md CHANGED
@@ -87,9 +87,11 @@ memory candidates; durable promotion and every networked or sensitive capture
87
87
  path remain separately controlled. Ordinary uninstall retains durable data
88
88
  unless an explicit purge is requested.
89
89
 
90
- Start with [Privacy and data flow](docs/privacy-and-data-flow.md) and
91
- [Memory storage and deployment](docs/storage.md) before enabling remote HTTP,
92
- embeddings, trajectories, artifacts, work evidence, or context packs.
90
+ Start with [Privacy and data flow](docs/privacy-and-data-flow.md),
91
+ [Memory storage and deployment](docs/storage.md), and
92
+ [Security assurance and threat model](docs/security-assurance.md) before
93
+ enabling remote HTTP, embeddings, trajectories, artifacts, work evidence, or
94
+ context packs.
93
95
 
94
96
  ## Documentation
95
97
 
@@ -103,6 +105,7 @@ guides. In particular:
103
105
  - [Operating guide](docs/operating.md)
104
106
  - [Harness compatibility](docs/harness-compatibility.md)
105
107
  - [Privacy and data flow](docs/privacy-and-data-flow.md)
108
+ - [Security assurance and threat model](docs/security-assurance.md)
106
109
  - [Guided learning paths and video scripts](docs/learning/README.md)
107
110
 
108
111
  The repository supports Linux, macOS, native Windows x64, WSL, Node.js 22/24/26,
package/SECURITY.md CHANGED
@@ -35,3 +35,16 @@ Reports are especially useful for:
35
35
 
36
36
  Configuration mistakes that deliberately disable documented safeguards are
37
37
  normally outside scope unless the safe default is ineffective.
38
+
39
+ ## Assurance model
40
+
41
+ The maintained [security assurance and threat model](docs/security-assurance.md)
42
+ documents assets, trust boundaries, implemented controls, verification gates,
43
+ and residual risks. Operators can run the read-only local posture check with:
44
+
45
+ ```sh
46
+ cairn security doctor --project .
47
+ ```
48
+
49
+ This diagnostic complements dependency and repository scans; it is not a
50
+ certification, sandbox, secret manager, or multi-user access-control system.
@@ -17,6 +17,7 @@ load_secret() {
17
17
  fi
18
18
  value=$(cat "$file")
19
19
  export "$name=$value"
20
+ unset "${name}_FILE"
20
21
  fi
21
22
  }
22
23
 
@@ -34,7 +35,7 @@ case "$mode" in
34
35
  exec cairn memory-server "$@"
35
36
  ;;
36
37
  http)
37
- if [ -z "${CAIRN_MEMORY_HTTP_TOKEN:-}" ]; then
38
+ if [ -z "${CAIRN_MEMORY_HTTP_TOKEN:-}" ] && [ -z "${CAIRN_MEMORY_HTTP_TOKEN_FILE:-}" ]; then
38
39
  echo "cairnkeep: HTTP mode requires CAIRN_MEMORY_HTTP_TOKEN or CAIRN_MEMORY_HTTP_TOKEN_FILE" >&2
39
40
  exit 64
40
41
  fi
package/docs/README.md CHANGED
@@ -43,6 +43,8 @@ linked guides rather than the project landing page.
43
43
  retention, backup, migration, and uninstall behavior.
44
44
  - [Privacy and data flow](privacy-and-data-flow.md) maps what each opt-in feature
45
45
  reads, stores, and may send over the network.
46
+ - [Security assurance and threat model](security-assurance.md) records assets,
47
+ trust boundaries, controls, residual risks, and the local posture check.
46
48
  - [Git-linked work evidence](work-evidence.md) covers bounded local session-to-Git
47
49
  provenance without prompt or reasoning capture.
48
50
  - [Optional document knowledge](domain-knowledge.md) covers the separately
@@ -1,6 +1,6 @@
1
1
  # Curriculum Coverage Map
2
2
 
3
- **Baseline:** Cairnkeep 2.20.1
3
+ **Baseline:** Cairnkeep 2.21.0
4
4
  **Last reviewed:** 2026-10-06
5
5
 
6
6
  This map prevents the course from drifting behind the product. It assigns each
@@ -43,6 +43,7 @@ been rehearsed.
43
43
  | Bounded project playbooks, generic memory retrieval guidance, enforcement, and private receipts | L25 | L25 | Ready; balanced/local by default; invocation requires runtime evidence; actor identity unverified |
44
44
  | Progressive pack retrieval, frozen benchmark, context-usage receipts, review-gated memory proposals, optional provider adapter | L26 | V26 | Ready; default flat search and AnythingLLM remain compatible; OpenViking and receipt mutation are explicitly gated |
45
45
  | Branchable agent state, model-managed context, and learned decision-model comparisons | L27 | V27 | Ready; evidence runners are operator-invoked and no companion is a runtime dependency or authority |
46
+ | Threat model, local posture check, portable-path adversarial tests, and release security controls | L28 | V28 | Ready; diagnostic is read-only and evidence is not certification |
46
47
 
47
48
  ## Release Review
48
49
 
@@ -77,7 +78,7 @@ repository remains the canonical source for explanations and scripts.
77
78
  | `course-11-windows` | L22 | Native PowerShell lifecycle, ACL inspection, and reversible uninstall |
78
79
  | `course-12-guided-setup` | L23 | Deterministic setup, Pi local stdio, recovery, and backup-first uninstall |
79
80
  | `course-13-playbooks` | L25 | Policy profiles, lifecycle decisions, enforcement, receipts, and ownership-safe instructions |
80
- | Disposable source checkout | L26-L27 | Context retrieval and ecosystem evidence without modifying course projects |
81
+ | Disposable source checkout | L26-L28 | Context retrieval, ecosystem evidence, and security assurance without modifying course projects |
81
82
 
82
83
  ## Command Ownership
83
84
 
@@ -95,6 +96,7 @@ against `cairn help` so a new command cannot bypass a curriculum decision.
95
96
  | `cairn sync-kimi` | L02 |
96
97
  | `cairn sync-omp` | L02 |
97
98
  | `cairn doctor` | L03; advanced repair in L13-L14 |
99
+ | `cairn security` | L28 |
98
100
  | `cairn playbook` | L25 |
99
101
  | `cairn trajectory` | L13 |
100
102
  | `cairn artifact` | L13 |
@@ -1,6 +1,6 @@
1
1
  # Cairnkeep feature guide
2
2
 
3
- **Baseline:** Cairnkeep 2.20.1
3
+ **Baseline:** Cairnkeep 2.21.0
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
@@ -27,6 +27,9 @@ locator and actual tool invocation must be verified from runtime evidence.
27
27
  Version 2.20 adds OpenCode 2.0.24 plugin and compaction compatibility plus
28
28
  operator-invoked ecosystem evidence runners; see
29
29
  [L27](lessons/L27-ecosystem-evidence.md).
30
+ Version 2.21 adds the formal threat model, a read-only local security posture
31
+ check, portable-path adversarial coverage, bounded HTTP requests, and repository
32
+ supply-chain gates; see [L28](lessons/L28-security-assurance.md).
30
33
 
31
34
  ## The three-layer model
32
35
 
@@ -70,6 +73,7 @@ prerequisites for ordinary memory.
70
73
  | Wiki and alignment | `/wiki-ingest`, `/wiki-query`, `/wiki-lint`, alignment files | Written only when invoked; reviewable `.planning/` artifacts | Re-run lint, inspect source citations, revert tracked changes normally | `course-02-memory` |
71
74
  | Local code graph | `cairn graph build|query|status|diff|explain|path`; `/graphify` delegates | Default off; optional isolated `graphify` executable; incremental work under `graphify-out/`, published view under `.planning/graphs/` | Ignore both derived locations; check status and source before trusting a result; use `--force` only after intentional deletion; uninstall adapters separately | `course-08-graph` |
72
75
  | Repository quality | `/repo-review`, `/security-audit` | On-demand workflow; findings are hypotheses until reproduced | Require file/line evidence and regression tests | `course-03-quality` |
76
+ | Security posture | `cairn security doctor [--project PATH] [--json]` | Read-only local configuration and permission inspection; no network; token values never emitted | Resolve failures, decide warnings explicitly, and re-run after exposure or authority changes | Disposable source checkout, L28 |
73
77
  | Guided project setup | `cairn setup [PATH]` selectors; deterministic `--git`, `--harness`, `--memory`, and `--yes` flags | Arrow-key Git/memory choices and harness checkboxes in capable terminals; text fallback otherwise; reconciles only selected assets; private `.ai/cairnkeep.json`; machine sync is never automatic | Repeat deterministically, run doctor, or replay its recovery command; Git-less mode remains limited | `course-12-guided-setup` |
74
78
  | Codex project memory | Select `--harness codex --memory local` | Writes `.codex/config.toml` plus POSIX/native Windows launchers; local stdio; user-wide configuration and trust remain untouched | Review the MCP table, accept Codex project trust, run doctor, and relaunch; uninstall is backup-first | `course-01-bootstrap` |
75
79
  | Workflow playbooks and durable-context guidance | `cairn playbook check start|check|finish`; project `minimal`, `balanced`, or `strict` policy; managed `AGENTS.md` retrieval protocol | Balanced by default; private `.ai/playbooks.json`; one task-derived project search when applicable; optional receipts in `.agentfs/playbooks/`; checks are offline and execute nothing | Verify the actual memory tool event and maintained source, inspect status/digests, enforce truthful `must` evidence, inspect receipts, reset overrides, remove only the managed `AGENTS.md` block | `course-13-playbooks` |
@@ -108,6 +112,7 @@ prerequisites for ordinary memory.
108
112
  | General meta-agent loop | Not shipped | Not applicable | Playbooks select bounded existing actions; no generic configuration loop, command language, or scheduler | `course-07-evaluation` |
109
113
  | Pack retrieval and skills | `CAIRN_CONTEXT_PACKS=1`; HTTP needs separate consent; skills need exact-digest approval | Restart MCP server | `cairn pack skills`; revoke approval or disable pack; no automatic execution | `course-10-trust-context` |
110
114
  | Ecosystem evidence runners | Explicit script invocation plus caller-supplied binaries/model configuration | None; disposable processes only | Temporary state is removed by default; inspect the JSON report and pinned evidence | Disposable source checkout, L27 |
115
+ | Security assurance | `cairn security doctor`; repository security workflows and offline gates | No restart for diagnostics; runtime HTTP bounds apply after server restart | Inspect failures/warnings, run security baseline and smoke gates, read residual risks | Disposable source checkout, L28 |
111
116
 
112
117
  ## Harness and maintenance checks
113
118
 
@@ -62,6 +62,7 @@ expanded only after their labs are executable.
62
62
  | L25 | [Bounded workflow playbooks](lessons/L25-playbooks.md) | Ready | 35 min |
63
63
  | L26 | [Context intelligence with explicit authority](lessons/L26-context-intelligence.md) | Ready | 45 min |
64
64
  | L27 | [Evaluate context companions without moving authority](lessons/L27-ecosystem-evidence.md) | Ready | 30 min |
65
+ | L28 | [Verify Cairnkeep's security posture](lessons/L28-security-assurance.md) | Ready | 30 min |
65
66
 
66
67
  The [curriculum coverage map](CURRICULUM-MAP.md) records where every public
67
68
  feature is introduced, practised, and operated. It also marks design-only work
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 10 minutes
6
- **Tested with:** Cairnkeep 2.20.1
6
+ **Tested with:** Cairnkeep 2.21.0
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.20.1
6
+ **Tested with:** Cairnkeep 2.21.0
7
7
 
8
8
  ## Outcome
9
9
 
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 25 minutes
6
- **Tested with:** Cairnkeep 2.20.1 and Node.js 22 or newer
6
+ **Tested with:** Cairnkeep 2.21.0 and Node.js 22 or newer
7
7
 
8
8
  ## Outcome
9
9
 
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart
5
5
  **Time:** 25 minutes
6
- **Tested with:** Cairnkeep 2.20.1
6
+ **Tested with:** Cairnkeep 2.21.0
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.20.1
6
+ **Tested with:** Cairnkeep 2.21.0
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.20.1 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.21.0 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.20.1 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.21.0 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.20.1, Windows x64, and Node.js 22 or newer
6
+ **Tested with:** Cairnkeep 2.21.0, Windows x64, and Node.js 22 or newer
7
7
 
8
8
  ## Outcome
9
9
 
@@ -3,7 +3,7 @@
3
3
  **Status:** Ready
4
4
  **Track:** Quickstart and Operator
5
5
  **Time:** 35 minutes
6
- **Tested with:** Cairnkeep 2.20.1; Pi 0.84.1; Node.js 22, 24, and 26
6
+ **Tested with:** Cairnkeep 2.21.0; 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.20.1 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.21.0 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.20.1 and Node.js 22 or newer
4
+ **Tested with:** Cairnkeep 2.21.0 and Node.js 22 or newer
5
5
  **Time:** 35 minutes
6
6
 
7
7
  ## Outcome
@@ -1,5 +1,8 @@
1
1
  # L26 - Context intelligence with explicit authority
2
2
 
3
+ **Status:** Ready
4
+ **Tested with:** Cairnkeep 2.21.0 and Node.js 22 or newer
5
+
3
6
  ## Outcome
4
7
 
5
8
  You will measure retrieval before changing it, retrieve an enabled context pack
@@ -1,6 +1,6 @@
1
1
  # L27 - Evaluate context companions without moving authority
2
2
 
3
- **Tested with:** Cairnkeep 2.20.1 and Node.js 22 or newer
3
+ **Tested with:** Cairnkeep 2.21.0 and Node.js 22 or newer
4
4
 
5
5
  ## Outcome
6
6
 
@@ -0,0 +1,104 @@
1
+ # L28 - Verify Cairnkeep's security posture
2
+
3
+ **Status:** Ready
4
+ **Tested with:** Cairnkeep 2.21.0 and Node.js 22 or newer
5
+
6
+ ## Outcome
7
+
8
+ You will distinguish repository assurance from local deployment posture, run a
9
+ read-only security diagnostic, deliberately reproduce a weak-token failure,
10
+ and choose least authority for a network-facing MCP client.
11
+
12
+ ## Prerequisites
13
+
14
+ - Install Cairnkeep 2.21.0 or use a disposable source checkout.
15
+ - Complete [L20 - Least-authority MCP tool profiles](L20-mcp-tool-profiles.md).
16
+ - Use synthetic values only; never paste a real bearer token into a recording,
17
+ terminal transcript, or issue.
18
+
19
+ ## Exercise
20
+
21
+ Create a disposable project and inspect the local-only posture:
22
+
23
+ ```sh
24
+ lab=$(mktemp -d)
25
+ mkdir -p "$lab/.ai"
26
+ chmod 700 "$lab/.ai"
27
+ cairn security doctor --project "$lab"
28
+ cairn security doctor --project "$lab" --json
29
+ ```
30
+
31
+ With HTTP disabled, the transport checks are `SKIP`. That is not missing
32
+ coverage: local stdio is the closed-by-default path.
33
+
34
+ Now model an unsafe deployment with a deliberately weak synthetic token:
35
+
36
+ ```sh
37
+ MCP_HTTP_PORT=7801 \
38
+ CAIRN_MEMORY_HTTP_TOKEN=short \
39
+ cairn security doctor --project "$lab" --json
40
+ ```
41
+
42
+ The command exits non-zero and reports `http-token` as `FAIL`. Confirm that the
43
+ JSON does not contain `short`. Then model a bounded observation-only endpoint:
44
+
45
+ ```sh
46
+ MCP_HTTP_PORT=7801 \
47
+ MCP_HTTP_HOST=127.0.0.1 \
48
+ CAIRN_MEMORY_HTTP_TOKEN=synthetic-0123456789abcdef-0123456789abcdef \
49
+ CAIRN_MCP_TOOL_PROFILE=read-only \
50
+ cairn security doctor --project "$lab"
51
+ ```
52
+
53
+ The diagnostic passes. This does not start the server, modify the project, or
54
+ prove that a reverse proxy supplies TLS; it checks only the effective local
55
+ configuration visible to this process.
56
+
57
+ Read [Security assurance and threat model](../../security-assurance.md), then
58
+ map each control to one boundary: project storage, MCP authority, remote
59
+ transport, context content, or release supply chain.
60
+
61
+ ## Verification
62
+
63
+ From a source checkout, run the maintained offline gates:
64
+
65
+ ```sh
66
+ npm run security:baseline
67
+ scripts/test-security-baseline.sh
68
+ npm --prefix mcp-memory-server run check:security-assurance
69
+ npm --prefix mcp-memory-server run check:http-guard
70
+ ```
71
+
72
+ The baseline rejects unpinned external Actions and `pull_request_target`. The
73
+ smoke tests cover token non-disclosure, request bounds, and deterministic
74
+ portable-path adversarial cases.
75
+
76
+ ## Common failures
77
+
78
+ - A private `.ai` file is group/world accessible on POSIX: restore mode `0600`.
79
+ - HTTP is non-loopback without an explicit Host allowlist: set the public host
80
+ names and ports intentionally.
81
+ - A network client receives the `full` profile although it only observes:
82
+ choose `read-only` or a reviewed `custom` allowlist and restart the server.
83
+ - A remote URL uses plaintext HTTP: use HTTPS, unless it is an intentional
84
+ loopback-only hop.
85
+
86
+ ## Privacy and trust boundary
87
+
88
+ The diagnostic reads configuration, file metadata, and only the length and
89
+ simple repetition of a configured token. It writes nothing and performs no
90
+ network request, but JSON includes the canonical project path. A passing report
91
+ is point-in-time evidence, not a certification or a substitute for TLS, host
92
+ hardening, secret management, or multi-user authorization.
93
+
94
+ ## Recovery
95
+
96
+ Remove the disposable directory after inspecting the output:
97
+
98
+ ```sh
99
+ rm -rf -- "$lab"
100
+ ```
101
+
102
+ For a real project, correct only the named file or environment setting. Do not
103
+ delete durable memory or regenerate credentials merely to silence a warning;
104
+ first decide whether the reported exposure is intentional.
@@ -0,0 +1,72 @@
1
+ # V28 - Security evidence you can inspect
2
+
3
+ Target length: 10–12 minutes. Audience: experienced developers operating an
4
+ agent-memory service locally or behind authenticated HTTP.
5
+
6
+ ## Recording outline
7
+
8
+ ### 0:00–1:20 — Threat model before tooling
9
+
10
+ Animate five boundaries: project storage, MCP authority, remote transport,
11
+ retrieved content, and release supply chain. State that Cairnkeep is not a
12
+ sandbox, secret manager, TLS terminator, or multi-user ACL.
13
+
14
+ ### 1:20–3:20 — Safe local baseline
15
+
16
+ Type at human speed in a disposable directory:
17
+
18
+ ```sh
19
+ lab=$(mktemp -d)
20
+ mkdir -p "$lab/.ai"
21
+ chmod 700 "$lab/.ai"
22
+ cairn security doctor --project "$lab"
23
+ ```
24
+
25
+ Pause on the skipped HTTP checks. Explain that disabled network exposure is the
26
+ expected secure default, not a missing test.
27
+
28
+ ### 3:20–5:30 — Make one failure visible
29
+
30
+ Run the weak synthetic-token example from L28. Zoom into the non-zero exit and
31
+ the `http-token` failure, then search the JSON for the planted value to show it
32
+ was not emitted. Leave a two-second pause before remediation.
33
+
34
+ ### 5:30–7:20 — Reduce authority
35
+
36
+ Re-run with a long synthetic token, loopback host, and
37
+ `CAIRN_MCP_TOOL_PROFILE=read-only`. Put the tool-catalog intersection on screen:
38
+
39
+ ```text
40
+ feature gates ∩ capabilities ∩ MCP profile = exposed tools
41
+ ```
42
+
43
+ Stress that a profile removes authority; it never enables a feature.
44
+
45
+ ### 7:20–9:20 — Show repository evidence
46
+
47
+ Use a split terminal for:
48
+
49
+ ```sh
50
+ npm run security:baseline
51
+ npm --prefix mcp-memory-server run check:security-assurance
52
+ npm --prefix mcp-memory-server run check:http-guard
53
+ ```
54
+
55
+ Highlight full-SHA action pins, CodeQL, dependency review, token non-leakage,
56
+ portable Windows/POSIX path cases, and the HTTP 413 test. Do not show repository
57
+ secrets or live security-alert details.
58
+
59
+ ### 9:20–10:30 — Read the residual risks
60
+
61
+ End on `docs/security-assurance.md`, not on a green checkmark. Call out the
62
+ same-account trust boundary, lack of per-user HTTP ACLs, unencrypted local
63
+ databases, and prompt-injection residual risk. The closing message is: evidence
64
+ narrows uncertainty; it does not abolish trust.
65
+
66
+ ## Recording cautions
67
+
68
+ - Use only a temporary project and synthetic token.
69
+ - Keep commands, exit codes, and report states legible; cut dead time rather
70
+ than speeding narration.
71
+ - Do not imply that Scorecard, CodeQL, an SBOM, or provenance is a certification.
72
+ - Delete the exact temporary directory on screen at the end.
package/docs/operating.md CHANGED
@@ -829,22 +829,57 @@ is guarded and **fails closed**:
829
829
 
830
830
  | Variable | Purpose |
831
831
  |---|---|
832
- | `MCP_HTTP_PORT` | Enable HTTP mode on this port (unset → stdio) |
832
+ | `MCP_HTTP_PORT` | Enable HTTP mode with a canonical decimal port from 1 through 65535 (unset → stdio; signs, whitespace, prefixes, and suffixes are rejected) |
833
833
  | `MCP_HTTP_HOST` | Bind address (default `127.0.0.1`) |
834
- | `CAIRN_MEMORY_HTTP_TOKEN` | **Required** in HTTP mode — clients send `Authorization: Bearer <token>`; the server refuses to start without it |
834
+ | `CAIRN_MEMORY_HTTP_TOKEN` | **Required** in HTTP mode — clients send `Authorization: Bearer <token>`; the server refuses to start without a header-safe ASCII Bearer value |
835
+ | `CAIRN_MEMORY_HTTP_TOKEN_FILE` | Alternative private token file; set exactly one token source. Relative paths resolve from the server/project root. The direct server validates a private non-symlink file; the container entrypoint converts its mounted secret to the direct token and clears `_FILE` |
835
836
  | `CAIRN_MEMORY_HTTP_ALLOWED_ORIGINS` | Comma-separated browser origins allowed via CORS (default: none — no cross-origin access) |
836
- | `CAIRN_MEMORY_HTTP_ALLOWED_HOSTS` | Comma-separated allowed `Host` headers for DNS-rebinding protection (default: the bind host + `localhost` on the chosen port) |
837
+ | `CAIRN_MEMORY_HTTP_ALLOWED_HOSTS` | Comma-separated `host:port` authorities allowed for DNS-rebinding protection (default: the bind host + `localhost` on the chosen port; bracket IPv6 literals) |
837
838
  | `CAIRN_ARTIFACT_HTTP` | Additional consent for artifact tools (default off; requires `CAIRN_ARTIFACT_STORE`, a valid `X-Cairn-Project`, bearer auth, and the normal Host/CORS checks) |
838
839
 
839
840
  Requests without a valid bearer token get `401`; requests with an unexpected
840
- `Host` header get `403`. Keep HTTP mode bound to `127.0.0.1` unless you have a
841
- specific reason to expose it, and use a long random token. HTTP mode has no
841
+ `Host` header get `403`; request bodies over 8 MiB get `413`. The server also
842
+ bounds header size, header/request duration, and keep-alive time. Keep HTTP mode
843
+ bound to `127.0.0.1` unless you have a specific reason to expose it, and use a
844
+ random token of at least 32 bytes. HTTP mode has no
842
845
  per-user ACL or tenant isolation. Clients may bind sessions to separate project
843
846
  databases with validated `X-Cairn-Project` routing metadata, but that metadata
844
847
  is not an authorization boundary. See [Memory storage and deployment](storage.md)
845
848
  for the placement rules, client registration, TLS requirements, project headers,
846
849
  and backup boundaries.
847
850
 
851
+ ### Security posture check
852
+
853
+ `cairn security doctor [--project PATH] [--json]` performs a read-only local
854
+ posture check. It validates canonical project placement and its immediate
855
+ replacement boundary, permissions on private managed files, the effective MCP
856
+ profile, HTTP token strength and token-file
857
+ safety, explicit Host allowlisting for network bindings, least authority, and
858
+ remote URL encryption. It never prints a token value. A failed check exits 1;
859
+ warnings remain visible but nonfatal, and disabled optional surfaces are
860
+ reported as skipped.
861
+
862
+ The command safely parses literal `KEY=value`, single-quoted, or double-quoted
863
+ assignments from the selected project's private `.ai/.env` through a
864
+ descriptor-bound 64 KiB read; it never sources or executes that file. Ambient variables take precedence, matching launcher
865
+ override behavior. Shell expansion, command substitution, or another ambiguous
866
+ line fails the environment check instead of being evaluated. Relative token
867
+ files are resolved from the selected project, even when the command is invoked
868
+ from another directory. Unquoted values accept only letters, digits, and the
869
+ literal characters `_./:@%+,=-`; use single or double quotes for other literal
870
+ punctuation. POSIX file checks require ownership by the current account or the
871
+ trusted root account in addition to restrictive mode bits.
872
+
873
+ The generated POSIX launchers snapshot exported ambient variables before
874
+ sourcing `.ai/.env`, then restore those existing values. Project-only variables
875
+ are added, while an explicit process environment remains the highest-precedence
876
+ override, matching the Windows launchers and the posture report.
877
+
878
+ Run it after setup, after changing HTTP or MCP-profile configuration, and
879
+ before a security-sensitive deployment. It does not replace `cairn doctor`,
880
+ TLS, a secret manager, or a multi-user authorization layer. See
881
+ [Security assurance and threat model](security-assurance.md).
882
+
848
883
  ## Project playbooks
849
884
 
850
885
  Guided setup and tracked bootstrap create private `.ai/playbooks.json` mode
@@ -156,6 +156,20 @@ Sanitized test replay uses synthetic tool arguments, results, stderr, and
156
156
  project paths only; release evidence must not include credentials, private
157
157
  endpoints, memory values, or identifying local paths.
158
158
 
159
+ `cairn security doctor` reads filesystem metadata for private project files and,
160
+ when HTTP is configured, the bearer token or configured token file only far
161
+ enough to check presence, byte length, repetition, header-safe syntax, and file safety. It emits no
162
+ token content, writes no state, and makes no network request. JSON output can
163
+ contain the canonical project path and should be treated as local diagnostic
164
+ data. The command parses literal assignments from `.ai/.env` without sourcing
165
+ the file, expanding variables, or executing commands; reads are descriptor-bound
166
+ and limited to 64 KiB, and the ambient environment retains precedence. Relative
167
+ token-file paths are anchored to the selected project rather than the caller's
168
+ working directory. The accepted literal grammar excludes unquoted shell
169
+ control, grouping, redirection, escape, expansion, tilde syntax, and control
170
+ bytes. Generated launchers restore existing ambient values after loading the
171
+ file, so the diagnostic and runtime use the same precedence.
172
+
159
173
  ## Evaluation adapter and report flow
160
174
 
161
175
  `CAIRN_EVAL` is unset/off by default. With it off, `cairn eval` returns a fixed
package/docs/releasing.md CHANGED
@@ -8,7 +8,8 @@ from a workstation.
8
8
  1. Update the root `package.json` version and `CHANGELOG.md` on a pull request.
9
9
  2. Run `npm ci`, `npm run check:public`, and
10
10
  `npm --prefix mcp-memory-server test`.
11
- 3. Merge only after every required CI check passes.
11
+ 3. Merge only after every required CI and security check passes. CodeQL and
12
+ dependency review are blocking; OpenSSF Scorecard is advisory evidence.
12
13
  4. Publish a GitHub Release whose tag is exactly `v<package.json version>`.
13
14
  5. After every platform and runtime job passes, CI builds one release candidate
14
15
  keyed by the exact Git tree. It contains the npm tarball, reproducible
@@ -58,3 +59,10 @@ automatic. Confirm an unauthenticated pull after changing visibility.
58
59
 
59
60
  Treat a published version as immutable. If a release is wrong, fix it in a new
60
61
  version rather than moving its tag or replacing the npm package.
62
+
63
+ Repository Actions are restricted to the maintained allowlist and every
64
+ non-local action reference must use a full commit SHA. `npm run
65
+ security:baseline` enforces that contract, rejects `pull_request_target`, and
66
+ asserts that CodeQL, dependency review, and advisory Scorecard workflows remain
67
+ present. See [Security assurance and threat model](security-assurance.md) for
68
+ the wider threat model and residual risks.