@cairnkeep/cli 1.1.1 → 1.1.3

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/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable user-facing changes are documented here. This project follows
4
4
  [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [1.1.3] - 2026-07-14
7
+
8
+ ### Fixed
9
+
10
+ - Allow the standard `$schema` self-reference in strict overlay manifests and
11
+ verify that the shipped example uses only declared top-level properties.
12
+
13
+ ## [1.1.2] - 2026-07-14
14
+
15
+ ### Added
16
+
17
+ - Define a provider-neutral managed-distribution contract for private overlays.
18
+ - Ship a versioned overlay manifest schema and local-first example.
19
+ - Document wrapper commands, profile locks, data-routing diagnostics, private
20
+ registry delivery, package hygiene, fleet migration, and rollback practices.
21
+
6
22
  ## [1.1.1] - 2026-07-14
7
23
 
8
24
  ### Fixed
package/README.md CHANGED
@@ -186,6 +186,7 @@ enabling them.
186
186
 
187
187
  - **Optional document RAG** (`domain_knowledge_*` via AnythingLLM) — [docs/domain-knowledge.md](docs/domain-knowledge.md)
188
188
  - **Building a private overlay** (wrap cairnkeep for your org/provider) — [docs/building-an-overlay.md](docs/building-an-overlay.md)
189
+ - **Managed overlay distributions** (wrapper CLI, policy lock, private registry, rollback) — [docs/overlay-distributions.md](docs/overlay-distributions.md)
189
190
  - **Full operating guide** — [docs/operating.md](docs/operating.md)
190
191
  - **Memory storage and deployment** — [docs/storage.md](docs/storage.md)
191
192
  - **Privacy and data flow** — [docs/privacy-and-data-flow.md](docs/privacy-and-data-flow.md)
@@ -6,6 +6,11 @@ credentials, and no organization-specific launchers. Everything specific to
6
6
  launchers, policy — belongs in a small private **overlay** that wraps cairnkeep.
7
7
  This guide shows the pattern.
8
8
 
9
+ For multi-machine or organization-wide use, package the overlay as a managed
10
+ distribution that owns the enrolled machine's `cairn` command. See
11
+ [Managed Cairnkeep distributions](overlay-distributions.md) for the command,
12
+ manifest, profile-lock, private-registry, and rollback contract.
13
+
9
14
  ## Principles
10
15
 
11
16
  1. **Consume, don't fork.** Treat cairnkeep as an upstream dependency: install it
@@ -122,3 +127,7 @@ secret storage, not generated project files.
122
127
  - [ ] Operating layer scoped as intended (global vs `--live-root`)
123
128
  - [ ] Optional integrations wired only where wanted
124
129
  - [ ] A single documented command takes a fresh machine to a working setup
130
+ - [ ] Managed machines resolve `cairn` to the overlay distribution, not raw core
131
+ - [ ] `cairn config explain` reports memory and RAG destinations without secrets
132
+ - [ ] A project profile lock prevents silent cross-overlay reconfiguration
133
+ - [ ] Private packages reject public-registry publication and exclude runtime data
@@ -0,0 +1,97 @@
1
+ # Managed Cairnkeep distributions
2
+
3
+ A private overlay becomes significantly easier to operate when it is treated as
4
+ a **distribution** rather than a directory of configuration fragments. The
5
+ distribution owns the `cairn` command on enrolled machines while consuming the
6
+ public `@cairnkeep/cli` package as an exact dependency.
7
+
8
+ This keeps Cairnkeep neutral and upgradeable. The distribution owns environment
9
+ policy, data routing, launchers, private integrations, and delivery through an
10
+ approved package registry. It should not patch or fork the core implementation.
11
+
12
+ ## Command contract
13
+
14
+ An overlay package can expose its own `cairn` binary and delegate to its pinned
15
+ core dependency:
16
+
17
+ - `cairn bootstrap <project>` runs core bootstrap and then applies the overlay.
18
+ - `cairn doctor` runs core diagnostics plus overlay policy checks.
19
+ - `cairn config explain` reports effective storage and service destinations
20
+ without printing credentials.
21
+ - `cairn overlay info` reports distribution and core versions separately.
22
+ - `cairn core <command>` explicitly bypasses overlay behavior for diagnosis.
23
+ - Other commands, especially `memory-server`, delegate to the pinned core with
24
+ the validated machine and project environment loaded.
25
+
26
+ Do not replace core source files. A small wrapper preserves the upstream command
27
+ surface while making managed bootstrap the default users reasonably expect.
28
+
29
+ ## Distribution manifest
30
+
31
+ Store non-secret capabilities and data-routing policy in
32
+ `cairnkeep.overlay.json`. The reference schema is
33
+ [`schemas/cairnkeep-overlay.schema.json`](../schemas/cairnkeep-overlay.schema.json),
34
+ with a complete example in
35
+ [`examples/overlay-distribution`](../examples/overlay-distribution/cairnkeep.overlay.json).
36
+
37
+ The distribution version and core version are independent. Pin the core exactly
38
+ and test every distribution release against it.
39
+
40
+ After a successful bootstrap, write an untracked project profile lock containing
41
+ the distribution identity, versions, and non-secret policy. Refuse a different
42
+ overlay identity before making changes. A stale lock should make `doctor` fail
43
+ with an instruction to rerun managed bootstrap.
44
+
45
+ ## Configuration and secrets
46
+
47
+ Use two explicit configuration layers:
48
+
49
+ 1. A mode-0600 machine profile in the user's configuration directory.
50
+ 2. An optional untracked, mode-0600 project override in `.ai/.env`.
51
+
52
+ Validate both files before sourcing them: reject symlinks, permissive modes, and
53
+ tracked project files. The manifest and project lock must never contain tokens,
54
+ cookies, private keys, or credential-bearing URLs.
55
+
56
+ `cairn config explain` should answer these questions directly:
57
+
58
+ - Is memory local or remote?
59
+ - Which transport is used?
60
+ - Which directory or endpoint receives memory?
61
+ - Is document RAG disabled, local, or remote?
62
+ - Which configuration files selected those values?
63
+
64
+ ## Private package delivery
65
+
66
+ A distribution can be an ordinary scoped npm package with:
67
+
68
+ - `@cairnkeep/cli` as an exact dependency;
69
+ - a `bin.cairn` entry for the wrapper;
70
+ - `publishConfig.access` set to `restricted`;
71
+ - an explicit guard that rejects the public npm registry;
72
+ - registry credentials supplied by a user credential helper or protected CI
73
+ variables, never by repository files.
74
+
75
+ Support installation from a private registry, a reviewed tarball, and a local
76
+ checkout. Avoid `postinstall` enrollment: installing a package must not silently
77
+ replace machine policy. Provide an explicit enrollment command with a retained
78
+ rollback target.
79
+
80
+ ## Release and fleet safety
81
+
82
+ Every release should verify:
83
+
84
+ - fresh managed bootstrap and idempotent reruns;
85
+ - core delegation and the explicit core escape hatch;
86
+ - profile mismatch rejection before mutation;
87
+ - storage and RAG policy enforcement;
88
+ - secret and runtime-artifact exclusion from the package;
89
+ - package installation from the produced tarball;
90
+ - checksums, an SBOM, and an immutable version;
91
+ - pre-upgrade and post-upgrade `doctor` results;
92
+ - rollback to the previous command or package version.
93
+
94
+ For a fleet migration, update the machine operating layer once, reapply managed
95
+ bootstrap to projects without altering tracked Git status, and validate every
96
+ profile lock. Storage migrations require their own backup, integrity, canary,
97
+ and rollback procedure; changing an overlay version alone must not move data.
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "../../schemas/cairnkeep-overlay.schema.json",
3
+ "schemaVersion": 1,
4
+ "id": "team-managed",
5
+ "name": "Example managed Cairnkeep distribution",
6
+ "version": "1.0.0",
7
+ "core": {
8
+ "package": "@cairnkeep/cli",
9
+ "version": "1.1.3"
10
+ },
11
+ "policy": {
12
+ "memory": {
13
+ "transport": "stdio",
14
+ "storage": "local",
15
+ "baseDir": "${HOME}/.cairnkeep"
16
+ },
17
+ "rag": {"mode": "disabled"},
18
+ "gitProvider": "none"
19
+ },
20
+ "bootstrap": {
21
+ "defaultMode": "untracked",
22
+ "launcher": ".ai/start-managed-agent.sh",
23
+ "profileLock": ".ai/cairnkeep-profile.json"
24
+ }
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cairnkeep/cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "A durable, harness-agnostic memory + context layer for coding agents (Claude Code, OpenCode).",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -17,6 +17,8 @@
17
17
  "claude/",
18
18
  "opencode/",
19
19
  "examples/anythingllm/",
20
+ "examples/overlay-distribution/",
21
+ "schemas/",
20
22
  "docs/",
21
23
  "CHANGELOG.md",
22
24
  "CONTRIBUTING.md",
@@ -0,0 +1,80 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/cairnkeep/cairnkeep/main/schemas/cairnkeep-overlay.schema.json",
4
+ "title": "Cairnkeep distribution profile",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "id", "name", "version", "core", "policy", "bootstrap"],
8
+ "properties": {
9
+ "$schema": {"type": "string", "format": "uri-reference"},
10
+ "schemaVersion": {"const": 1},
11
+ "id": {"type": "string", "pattern": "^[a-z][a-z0-9-]{1,63}$"},
12
+ "name": {"type": "string", "minLength": 1, "maxLength": 120},
13
+ "version": {
14
+ "type": "string",
15
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$"
16
+ },
17
+ "core": {
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "required": ["package", "version"],
21
+ "properties": {
22
+ "package": {"const": "@cairnkeep/cli"},
23
+ "version": {
24
+ "type": "string",
25
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$"
26
+ }
27
+ }
28
+ },
29
+ "policy": {
30
+ "type": "object",
31
+ "additionalProperties": false,
32
+ "required": ["memory", "rag", "gitProvider"],
33
+ "properties": {
34
+ "memory": {
35
+ "type": "object",
36
+ "additionalProperties": false,
37
+ "required": ["transport", "storage"],
38
+ "properties": {
39
+ "transport": {"enum": ["stdio", "http"]},
40
+ "storage": {"enum": ["local", "remote"]},
41
+ "baseDir": {"type": "string", "minLength": 1},
42
+ "endpoint": {"type": "string", "format": "uri"}
43
+ },
44
+ "allOf": [
45
+ {
46
+ "if": {"properties": {"storage": {"const": "local"}}},
47
+ "then": {"required": ["baseDir"]}
48
+ },
49
+ {
50
+ "if": {"properties": {"storage": {"const": "remote"}}},
51
+ "then": {"required": ["endpoint"]}
52
+ }
53
+ ]
54
+ },
55
+ "rag": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": ["mode"],
59
+ "properties": {
60
+ "mode": {"enum": ["disabled", "local", "remote"]},
61
+ "baseUrl": {"type": "string", "format": "uri"}
62
+ },
63
+ "if": {"properties": {"mode": {"enum": ["local", "remote"]}}},
64
+ "then": {"required": ["baseUrl"]}
65
+ },
66
+ "gitProvider": {"type": "string", "minLength": 1}
67
+ }
68
+ },
69
+ "bootstrap": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": ["defaultMode", "launcher", "profileLock"],
73
+ "properties": {
74
+ "defaultMode": {"enum": ["tracked", "untracked"]},
75
+ "launcher": {"type": "string", "minLength": 1},
76
+ "profileLock": {"type": "string", "minLength": 1}
77
+ }
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
5
+
6
+ node --input-type=module - "$ROOT" <<'NODE'
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+
10
+ const root = process.argv[2];
11
+ const schema = JSON.parse(fs.readFileSync(path.join(root, "schemas/cairnkeep-overlay.schema.json"), "utf8"));
12
+ const example = JSON.parse(fs.readFileSync(path.join(root, "examples/overlay-distribution/cairnkeep.overlay.json"), "utf8"));
13
+
14
+ if (schema.$schema !== "https://json-schema.org/draft/2020-12/schema") throw new Error("unexpected schema draft");
15
+ if (schema.properties.schemaVersion.const !== 1) throw new Error("unexpected schema version");
16
+ for (const field of schema.required) {
17
+ if (!(field in example)) throw new Error(`example missing required field: ${field}`);
18
+ }
19
+ for (const field of Object.keys(example)) {
20
+ if (!(field in schema.properties)) throw new Error(`example uses undeclared field: ${field}`);
21
+ }
22
+ if (example.core.package !== "@cairnkeep/cli") throw new Error("example must consume the public core");
23
+ if (example.policy.memory.storage !== "local") throw new Error("example must remain local-first");
24
+ if (example.policy.memory.transport !== "stdio") throw new Error("example must remain local-first");
25
+ NODE
26
+
27
+ pack_manifest=$(mktemp)
28
+ trap 'rm -f "$pack_manifest"' EXIT
29
+ npm --prefix "$ROOT" pack --dry-run --json --ignore-scripts > "$pack_manifest"
30
+ node --input-type=module - "$pack_manifest" <<'NODE'
31
+ import fs from "node:fs";
32
+
33
+ const manifest = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
34
+ const files = new Set(manifest[0].files.map((item) => item.path));
35
+ for (const required of [
36
+ "schemas/cairnkeep-overlay.schema.json",
37
+ "examples/overlay-distribution/cairnkeep.overlay.json",
38
+ "docs/overlay-distributions.md",
39
+ ]) {
40
+ if (!files.has(required)) throw new Error(`npm package missing ${required}`);
41
+ }
42
+ NODE
43
+
44
+ echo "overlay schema checks passed"