@cleocode/core 2026.9.15 → 2026.9.17
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/dist/changesets/parser.d.ts.map +1 -1
- package/dist/changesets/parser.js +67 -0
- package/dist/changesets/parser.js.map +1 -1
- package/dist/crypto/credentials.d.ts +128 -26
- package/dist/crypto/credentials.d.ts.map +1 -1
- package/dist/crypto/credentials.js +183 -142
- package/dist/crypto/credentials.js.map +1 -1
- package/dist/docs/export-document.js +14705 -12949
- package/dist/docs/export-document.js.map +3 -3
- package/dist/doctor/index.d.ts +1 -1
- package/dist/doctor/index.d.ts.map +1 -1
- package/dist/doctor/index.js +1 -1
- package/dist/doctor/index.js.map +1 -1
- package/dist/doctor/nexus-schema-residency.d.ts +1 -1
- package/dist/doctor/nexus-schema-residency.d.ts.map +1 -1
- package/dist/doctor/nexus-schema-residency.js +1 -0
- package/dist/doctor/nexus-schema-residency.js.map +1 -1
- package/dist/doctor/project-identity.d.ts +124 -0
- package/dist/doctor/project-identity.d.ts.map +1 -0
- package/dist/doctor/project-identity.js +307 -0
- package/dist/doctor/project-identity.js.map +1 -0
- package/dist/doctor/superseded-store.d.ts +8 -0
- package/dist/doctor/superseded-store.d.ts.map +1 -1
- package/dist/doctor/superseded-store.js +48 -3
- package/dist/doctor/superseded-store.js.map +1 -1
- package/dist/init.d.ts +7 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +17 -2
- package/dist/init.js.map +1 -1
- package/dist/internal.d.ts +5 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -2
- package/dist/internal.js.map +1 -1
- package/dist/llm/credentials-store.d.ts +10 -0
- package/dist/llm/credentials-store.d.ts.map +1 -1
- package/dist/llm/credentials-store.js +7 -1
- package/dist/llm/credentials-store.js.map +1 -1
- package/dist/llm/plugin-facade.js +13368 -1550
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/nexus/analyze-orchestrator.d.ts +20 -3
- package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
- package/dist/nexus/analyze-orchestrator.js +159 -10
- package/dist/nexus/analyze-orchestrator.js.map +1 -1
- package/dist/nexus/assessment-store.d.ts +57 -0
- package/dist/nexus/assessment-store.d.ts.map +1 -0
- package/dist/nexus/assessment-store.js +73 -0
- package/dist/nexus/assessment-store.js.map +1 -0
- package/dist/nexus/freshness.d.ts +131 -0
- package/dist/nexus/freshness.d.ts.map +1 -0
- package/dist/nexus/freshness.js +391 -0
- package/dist/nexus/freshness.js.map +1 -0
- package/dist/nexus/graph-manifest.d.ts +88 -0
- package/dist/nexus/graph-manifest.d.ts.map +1 -0
- package/dist/nexus/graph-manifest.js +114 -0
- package/dist/nexus/graph-manifest.js.map +1 -0
- package/dist/nexus/knowledge.d.ts +21 -1
- package/dist/nexus/knowledge.d.ts.map +1 -1
- package/dist/nexus/knowledge.js +76 -37
- package/dist/nexus/knowledge.js.map +1 -1
- package/dist/nexus/path-map.d.ts +55 -0
- package/dist/nexus/path-map.d.ts.map +1 -0
- package/dist/nexus/path-map.js +87 -0
- package/dist/nexus/path-map.js.map +1 -0
- package/dist/nexus/projects-clean.d.ts +21 -28
- package/dist/nexus/projects-clean.d.ts.map +1 -1
- package/dist/nexus/projects-clean.js +168 -85
- package/dist/nexus/projects-clean.js.map +1 -1
- package/dist/nexus/query.d.ts.map +1 -1
- package/dist/nexus/query.js +3 -2
- package/dist/nexus/query.js.map +1 -1
- package/dist/nexus/registry-hygiene.d.ts +70 -0
- package/dist/nexus/registry-hygiene.d.ts.map +1 -0
- package/dist/nexus/registry-hygiene.js +113 -0
- package/dist/nexus/registry-hygiene.js.map +1 -0
- package/dist/nexus/registry.d.ts +2 -2
- package/dist/nexus/registry.d.ts.map +1 -1
- package/dist/nexus/registry.js +48 -12
- package/dist/nexus/registry.js.map +1 -1
- package/dist/paths.d.ts +26 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +99 -6
- package/dist/paths.js.map +1 -1
- package/dist/render/nexus/graph/index.d.ts.map +1 -1
- package/dist/render/nexus/graph/index.js +8 -0
- package/dist/render/nexus/graph/index.js.map +1 -1
- package/dist/resources/spawn-wrapper.d.ts +0 -11
- package/dist/resources/spawn-wrapper.d.ts.map +1 -1
- package/dist/resources/spawn-wrapper.js +62 -6
- package/dist/resources/spawn-wrapper.js.map +1 -1
- package/dist/scaffold/ensure-config.d.ts +12 -3
- package/dist/scaffold/ensure-config.d.ts.map +1 -1
- package/dist/scaffold/ensure-config.js +61 -38
- package/dist/scaffold/ensure-config.js.map +1 -1
- package/dist/scaffold/project-identity.d.ts +136 -0
- package/dist/scaffold/project-identity.d.ts.map +1 -0
- package/dist/scaffold/project-identity.js +225 -0
- package/dist/scaffold/project-identity.js.map +1 -0
- package/dist/selfimprove/fix-gen.js +13319 -1556
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/store/agent-api-key.d.ts +114 -0
- package/dist/store/agent-api-key.d.ts.map +1 -0
- package/dist/store/agent-api-key.js +136 -0
- package/dist/store/agent-api-key.js.map +1 -0
- package/dist/store/agent-registry-accessor.d.ts +4 -2
- package/dist/store/agent-registry-accessor.d.ts.map +1 -1
- package/dist/store/agent-registry-accessor.js +39 -89
- package/dist/store/agent-registry-accessor.js.map +1 -1
- package/dist/store/api-key-kdf.d.ts +7 -0
- package/dist/store/api-key-kdf.d.ts.map +1 -1
- package/dist/store/api-key-kdf.js +7 -0
- package/dist/store/api-key-kdf.js.map +1 -1
- package/dist/store/backup-crypto.d.ts +49 -0
- package/dist/store/backup-crypto.d.ts.map +1 -1
- package/dist/store/backup-crypto.js +154 -0
- package/dist/store/backup-crypto.js.map +1 -1
- package/dist/store/credential-transfer.d.ts +303 -0
- package/dist/store/credential-transfer.d.ts.map +1 -0
- package/dist/store/credential-transfer.js +774 -0
- package/dist/store/credential-transfer.js.map +1 -0
- package/dist/store/exodus/column-transforms.d.ts +79 -4
- package/dist/store/exodus/column-transforms.d.ts.map +1 -1
- package/dist/store/exodus/column-transforms.js +121 -4
- package/dist/store/exodus/column-transforms.js.map +1 -1
- package/dist/store/exodus/index.d.ts +1 -0
- package/dist/store/exodus/index.d.ts.map +1 -1
- package/dist/store/exodus/index.js +1 -0
- package/dist/store/exodus/index.js.map +1 -1
- package/dist/store/exodus/migrate.d.ts +17 -1
- package/dist/store/exodus/migrate.d.ts.map +1 -1
- package/dist/store/exodus/migrate.js +155 -33
- package/dist/store/exodus/migrate.js.map +1 -1
- package/dist/store/exodus/on-open.d.ts +14 -0
- package/dist/store/exodus/on-open.d.ts.map +1 -1
- package/dist/store/exodus/on-open.js +115 -6
- package/dist/store/exodus/on-open.js.map +1 -1
- package/dist/store/exodus/reconcile.d.ts +103 -0
- package/dist/store/exodus/reconcile.d.ts.map +1 -0
- package/dist/store/exodus/reconcile.js +728 -0
- package/dist/store/exodus/reconcile.js.map +1 -0
- package/dist/store/exodus/recovery.d.ts.map +1 -1
- package/dist/store/exodus/recovery.js +14 -2
- package/dist/store/exodus/recovery.js.map +1 -1
- package/dist/store/exodus/runtime-targets.d.ts +39 -0
- package/dist/store/exodus/runtime-targets.d.ts.map +1 -0
- package/dist/store/exodus/runtime-targets.js +63 -0
- package/dist/store/exodus/runtime-targets.js.map +1 -0
- package/dist/store/exodus/table-name-map.d.ts.map +1 -1
- package/dist/store/exodus/table-name-map.js +5 -0
- package/dist/store/exodus/table-name-map.js.map +1 -1
- package/dist/store/exodus/table-order.d.ts +35 -0
- package/dist/store/exodus/table-order.d.ts.map +1 -0
- package/dist/store/exodus/table-order.js +62 -0
- package/dist/store/exodus/table-order.js.map +1 -0
- package/dist/store/exodus/verify-migration.d.ts +4 -1
- package/dist/store/exodus/verify-migration.d.ts.map +1 -1
- package/dist/store/exodus/verify-migration.js +142 -10
- package/dist/store/exodus/verify-migration.js.map +1 -1
- package/dist/store/global-salt.d.ts +16 -0
- package/dist/store/global-salt.d.ts.map +1 -1
- package/dist/store/global-salt.js +22 -7
- package/dist/store/global-salt.js.map +1 -1
- package/dist/store/legacy-tasks-lineage.d.ts +86 -0
- package/dist/store/legacy-tasks-lineage.d.ts.map +1 -0
- package/dist/store/legacy-tasks-lineage.js +285 -0
- package/dist/store/legacy-tasks-lineage.js.map +1 -0
- package/dist/store/migration-manager.d.ts +27 -0
- package/dist/store/migration-manager.d.ts.map +1 -1
- package/dist/store/migration-manager.js +63 -0
- package/dist/store/migration-manager.js.map +1 -1
- package/dist/store/portable-bundle-import.d.ts +117 -0
- package/dist/store/portable-bundle-import.d.ts.map +1 -0
- package/dist/store/portable-bundle-import.js +589 -0
- package/dist/store/portable-bundle-import.js.map +1 -0
- package/dist/store/portable-bundle-relocate.d.ts +66 -0
- package/dist/store/portable-bundle-relocate.d.ts.map +1 -0
- package/dist/store/portable-bundle-relocate.js +304 -0
- package/dist/store/portable-bundle-relocate.js.map +1 -0
- package/dist/store/portable-bundle-scan.d.ts +167 -0
- package/dist/store/portable-bundle-scan.d.ts.map +1 -0
- package/dist/store/portable-bundle-scan.js +569 -0
- package/dist/store/portable-bundle-scan.js.map +1 -0
- package/dist/store/portable-bundle.d.ts +146 -0
- package/dist/store/portable-bundle.d.ts.map +1 -0
- package/dist/store/portable-bundle.js +674 -0
- package/dist/store/portable-bundle.js.map +1 -0
- package/dist/store/regenerators.d.ts +3 -1
- package/dist/store/regenerators.d.ts.map +1 -1
- package/dist/store/regenerators.js +6 -2
- package/dist/store/regenerators.js.map +1 -1
- package/dist/store/schema/cleo-global/nexus.d.ts +98 -0
- package/dist/store/schema/cleo-global/nexus.d.ts.map +1 -1
- package/dist/store/schema/cleo-global/nexus.js +19 -0
- package/dist/store/schema/cleo-global/nexus.js.map +1 -1
- package/dist/store/schema/nexus-schema.d.ts +102 -0
- package/dist/store/schema/nexus-schema.d.ts.map +1 -1
- package/dist/store/schema/nexus-schema.js +24 -0
- package/dist/store/schema/nexus-schema.js.map +1 -1
- package/dist/store/split-brain-import.d.ts +176 -0
- package/dist/store/split-brain-import.d.ts.map +1 -0
- package/dist/store/split-brain-import.js +680 -0
- package/dist/store/split-brain-import.js.map +1 -0
- package/dist/store/sqlite.d.ts +29 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +57 -5
- package/dist/store/sqlite.js.map +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +30 -5
- package/dist/system/health.js.map +1 -1
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +60 -0
- package/dist/upgrade.js.map +1 -1
- package/dist/validation/doctor/checks.d.ts +14 -0
- package/dist/validation/doctor/checks.d.ts.map +1 -1
- package/dist/validation/doctor/checks.js +33 -0
- package/dist/validation/doctor/checks.js.map +1 -1
- package/migrations/drizzle-cleo-global/20260924000000_t12354-project-paths/migration.sql +40 -0
- package/migrations/drizzle-nexus/20260924000000_t12315-nexus-parse-cache/migration.sql +21 -0
- package/package.json +13 -13
- package/templates/CLEO-INJECTION.md +3 -3
- package/templates/cleo-gitignore +7 -0
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Credential encryption — AES-256-GCM with
|
|
2
|
+
* Credential encryption — AES-256-GCM with device-bound key derivation.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Every key derives from the 32-byte `machine-key` in the CLEO home. That key
|
|
5
|
+
* NEVER leaves the device; credentials cross devices only through the
|
|
6
|
+
* passphrase-sealed path in `store/credential-transfer.ts`.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
* + project path
|
|
9
|
-
* = HMAC-SHA256(machine-key, project-path) → per-project AES key
|
|
8
|
+
* Two KDFs:
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* - **Project** (T12326): `HMAC-SHA256(machine-key, "cleo:project-credential:v2\0"
|
|
11
|
+
* ‖ projectId)`, ciphertext version `0x02`. Keyed by project IDENTITY, so a
|
|
12
|
+
* moved or renamed project still decrypts. The pre-T12326 path-bound KDF
|
|
13
|
+
* (`HMAC-SHA256(machine-key, projectPath)`, version `0x01`) is read-only and
|
|
14
|
+
* migrated on first successful decrypt ({@link decryptProjectSecret}).
|
|
15
|
+
* - **Global** (ADR-037 §5): `HMAC-SHA256(machine-key ‖ global-salt, id)`,
|
|
16
|
+
* version `0x01`, for project-independent credentials.
|
|
13
17
|
*
|
|
14
18
|
* @see docs/specs/SIGNALDOCK-UNIFIED-AGENT-REGISTRY.md Section 3.6
|
|
15
19
|
* @module crypto/credentials
|
|
@@ -18,39 +22,39 @@ import { execFileSync } from 'node:child_process';
|
|
|
18
22
|
import { createCipheriv, createDecipheriv, createHmac, randomBytes } from 'node:crypto';
|
|
19
23
|
import { chmod, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
20
24
|
import { dirname, join } from 'node:path';
|
|
21
|
-
import {
|
|
25
|
+
import { getCleoHome } from '../paths.js';
|
|
26
|
+
import { getGlobalSalt, loadGlobalSaltAt } from '../store/global-salt.js';
|
|
22
27
|
/** AES-256-GCM constants. */
|
|
23
28
|
const ALGORITHM = 'aes-256-gcm';
|
|
24
29
|
const IV_LENGTH = 12;
|
|
25
30
|
const AUTH_TAG_LENGTH = 16;
|
|
26
31
|
const KEY_LENGTH = 32;
|
|
27
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Version byte of global-KDF ciphertexts (F-006: enables future algorithm
|
|
34
|
+
* migration). Project ciphertexts carry their own versions — see
|
|
35
|
+
* {@link PROJECT_CIPHERTEXT_VERSION_PROJECT_ID}.
|
|
36
|
+
*/
|
|
28
37
|
const CIPHERTEXT_VERSION = 0x01;
|
|
29
38
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
39
|
+
* Absolute path to the machine key file: `<cleoHome>/machine-key`.
|
|
40
|
+
*
|
|
41
|
+
* Resolved through {@link getCleoHome} — the same root as `global-salt` and
|
|
42
|
+
* the agent registry's key reader — so `CLEO_HOME` relocates all of them
|
|
43
|
+
* together. (Before T12326 this function re-derived the XDG path itself and
|
|
44
|
+
* ignored `CLEO_HOME`, so a relocated home read the salt from one directory
|
|
45
|
+
* and the key from another.) On every platform the default resolves to the
|
|
46
|
+
* same file as before.
|
|
32
47
|
*
|
|
33
|
-
*
|
|
48
|
+
* The machine key is DEVICE-LOCAL by design: it is never exported in any
|
|
49
|
+
* backup bundle. Credentials move between devices through
|
|
50
|
+
* `store/credential-transfer.ts` instead.
|
|
51
|
+
*
|
|
52
|
+
* @param cleoHome - CLEO home to resolve against (defaults to the ambient home).
|
|
53
|
+
* @returns Absolute path to the machine key.
|
|
54
|
+
* @task T12326
|
|
34
55
|
*/
|
|
35
|
-
function getMachineKeyPath() {
|
|
36
|
-
|
|
37
|
-
if (process.platform === 'win32') {
|
|
38
|
-
const appData = process.env['LOCALAPPDATA'] ?? process.env['APPDATA'];
|
|
39
|
-
if (appData)
|
|
40
|
-
return join(appData, 'cleo', 'Data', 'machine-key');
|
|
41
|
-
}
|
|
42
|
-
else if (process.platform === 'darwin') {
|
|
43
|
-
const home = process.env['HOME'];
|
|
44
|
-
if (home)
|
|
45
|
-
return join(home, 'Library', 'Application Support', 'cleo', 'machine-key');
|
|
46
|
-
}
|
|
47
|
-
// Linux / fallback: XDG_DATA_HOME or ~/.local/share
|
|
48
|
-
const dataHome = process.env['XDG_DATA_HOME'] ?? join(process.env['HOME'] ?? '', '.local', 'share');
|
|
49
|
-
if (!dataHome) {
|
|
50
|
-
throw new Error('Cannot determine data directory. Set HOME or XDG_DATA_HOME environment variable. ' +
|
|
51
|
-
'Machine key cannot be stored securely without a persistent data directory.');
|
|
52
|
-
}
|
|
53
|
-
return join(dataHome, 'cleo', 'machine-key');
|
|
56
|
+
export function getMachineKeyPath(cleoHome = getCleoHome()) {
|
|
57
|
+
return join(cleoHome, 'machine-key');
|
|
54
58
|
}
|
|
55
59
|
/**
|
|
56
60
|
* Read or auto-generate the machine key (32 random bytes).
|
|
@@ -58,8 +62,8 @@ function getMachineKeyPath() {
|
|
|
58
62
|
*
|
|
59
63
|
* @throws If the machine key exists but has wrong permissions (not 0600).
|
|
60
64
|
*/
|
|
61
|
-
async function getMachineKey() {
|
|
62
|
-
const keyPath = getMachineKeyPath();
|
|
65
|
+
async function getMachineKey(cleoHome) {
|
|
66
|
+
const keyPath = getMachineKeyPath(cleoHome);
|
|
63
67
|
try {
|
|
64
68
|
// Verify key file permissions
|
|
65
69
|
const stats = await stat(keyPath);
|
|
@@ -122,102 +126,156 @@ async function getMachineKey() {
|
|
|
122
126
|
throw err;
|
|
123
127
|
}
|
|
124
128
|
}
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Ciphertext framing (shared by every KDF)
|
|
131
|
+
// ============================================================================
|
|
125
132
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* @remarks
|
|
130
|
-
* **KDF COORDINATION NOTE (T380/ADR-041 §D5, ADR-037 §5)**
|
|
131
|
-
*
|
|
132
|
-
* This KDF is intentionally NOT changed in T380. The current scheme
|
|
133
|
-
* (`HMAC-SHA256(machine-key, projectPath)`) is project-path-bound, which is
|
|
134
|
-
* correct for the current project-tier signaldock.db layout.
|
|
135
|
-
*
|
|
136
|
-
* ADR-037 §5 specifies the replacement KDF:
|
|
137
|
-
* ```
|
|
138
|
-
* apiKey = HMAC-SHA256(machine-key || globalSalt, agentId)
|
|
139
|
-
* ```
|
|
140
|
-
* where `globalSalt` is a 32-byte per-machine random value stored at
|
|
141
|
-
* `$XDG_DATA_HOME/cleo/global-salt`.
|
|
142
|
-
*
|
|
143
|
-
* The replacement KDF MUST be implemented as part of the global-signaldock
|
|
144
|
-
* migration (T310 + T362). Swapping it here, before that migration, would
|
|
145
|
-
* silently invalidate all stored encrypted API keys without providing the
|
|
146
|
-
* global-salt infrastructure needed to re-encrypt them.
|
|
147
|
-
*
|
|
148
|
-
* Once T310/T362 land, this function should be REPLACED (not extended) with
|
|
149
|
-
* the new `deriveAgentKey(agentId: string)` helper defined in that epic.
|
|
150
|
-
* Remove this comment block when the replacement lands.
|
|
151
|
-
*
|
|
152
|
-
* @see ADR-037 §5 — full KDF design and migration plan
|
|
153
|
-
* @see T310 — GlobalAgentRegistryAccessor + KDF refactor
|
|
154
|
-
* @see T362 — parallel KDF coordination
|
|
155
|
-
* @task T380
|
|
156
|
-
*/
|
|
157
|
-
async function deriveProjectKey(projectPath) {
|
|
158
|
-
const machineKey = await getMachineKey();
|
|
159
|
-
return createHmac('sha256', machineKey).update(projectPath).digest();
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Encrypt a plaintext string using AES-256-GCM with a per-project key.
|
|
163
|
-
*
|
|
164
|
-
* Output format: base64(version + iv + ciphertext + authTag)
|
|
165
|
-
* - version: 1 byte (0x01 = AES-256-GCM)
|
|
166
|
-
* - iv: 12 bytes
|
|
167
|
-
* - ciphertext: variable length
|
|
168
|
-
* - authTag: 16 bytes
|
|
169
|
-
*
|
|
170
|
-
* @param plaintext - The string to encrypt (e.g. an API key).
|
|
171
|
-
* @param projectPath - Absolute path to the project directory (used for key derivation).
|
|
172
|
-
* @returns Base64-encoded ciphertext.
|
|
133
|
+
* Encrypt `plaintext` under `key` and frame it as
|
|
134
|
+
* base64(version ‖ iv ‖ ciphertext ‖ authTag).
|
|
173
135
|
*/
|
|
174
|
-
|
|
175
|
-
const key = await deriveProjectKey(projectPath);
|
|
136
|
+
function sealWithKey(plaintext, key, version) {
|
|
176
137
|
const iv = randomBytes(IV_LENGTH);
|
|
177
138
|
const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
|
|
178
139
|
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
179
140
|
const authTag = cipher.getAuthTag();
|
|
180
|
-
|
|
181
|
-
const version = Buffer.from([CIPHERTEXT_VERSION]);
|
|
182
|
-
const packed = Buffer.concat([version, iv, encrypted, authTag]);
|
|
183
|
-
return packed.toString('base64');
|
|
141
|
+
return Buffer.concat([Buffer.from([version]), iv, encrypted, authTag]).toString('base64');
|
|
184
142
|
}
|
|
185
143
|
/**
|
|
186
|
-
*
|
|
144
|
+
* Split a framed ciphertext into its parts.
|
|
187
145
|
*
|
|
188
|
-
* @
|
|
189
|
-
* @param projectPath - Absolute path to the project directory (must match the one used for encryption).
|
|
190
|
-
* @returns The original plaintext string.
|
|
191
|
-
* @throws If decryption fails (wrong key, corrupted data, or machine key mismatch).
|
|
146
|
+
* @throws {Error} If the payload is shorter than the fixed framing.
|
|
192
147
|
*/
|
|
193
|
-
|
|
194
|
-
const key = await deriveProjectKey(projectPath);
|
|
148
|
+
function unframe(ciphertext) {
|
|
195
149
|
const packed = Buffer.from(ciphertext, 'base64');
|
|
196
150
|
if (packed.length < 1 + IV_LENGTH + AUTH_TAG_LENGTH) {
|
|
197
151
|
throw new Error('Cannot decrypt credentials: ciphertext too short');
|
|
198
152
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
153
|
+
return {
|
|
154
|
+
version: packed[0] ?? -1,
|
|
155
|
+
iv: packed.subarray(1, 1 + IV_LENGTH),
|
|
156
|
+
encrypted: packed.subarray(1 + IV_LENGTH, packed.length - AUTH_TAG_LENGTH),
|
|
157
|
+
authTag: packed.subarray(packed.length - AUTH_TAG_LENGTH),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Decrypt a framed ciphertext under `key`. Returns `null` when the GCM auth
|
|
162
|
+
* tag rejects the key — the caller decides whether another key is worth trying.
|
|
163
|
+
*/
|
|
164
|
+
function openWithKey(parts, key) {
|
|
165
|
+
const decipher = createDecipheriv(ALGORITHM, key, parts.iv, { authTagLength: AUTH_TAG_LENGTH });
|
|
166
|
+
decipher.setAuthTag(parts.authTag);
|
|
210
167
|
try {
|
|
211
|
-
|
|
212
|
-
return decrypted.toString('utf8');
|
|
168
|
+
return Buffer.concat([decipher.update(parts.encrypted), decipher.final()]).toString('utf8');
|
|
213
169
|
}
|
|
214
170
|
catch {
|
|
215
|
-
|
|
216
|
-
'If this database was moved from another machine, re-register agents: ' +
|
|
217
|
-
'cleo agent register --id <id> --api-key <key>');
|
|
171
|
+
return null;
|
|
218
172
|
}
|
|
219
173
|
}
|
|
220
174
|
// ============================================================================
|
|
175
|
+
// Project KDF — keyed by project IDENTITY, not project path (T12326)
|
|
176
|
+
// ============================================================================
|
|
177
|
+
/**
|
|
178
|
+
* Ciphertext version written by the pre-T12326 project KDF,
|
|
179
|
+
* `HMAC-SHA256(machine-key, projectPath)`. Read-only: decrypted during
|
|
180
|
+
* migration, never written.
|
|
181
|
+
*/
|
|
182
|
+
export const PROJECT_CIPHERTEXT_VERSION_LEGACY_PATH = 0x01;
|
|
183
|
+
/**
|
|
184
|
+
* Ciphertext version written by the project-identity KDF,
|
|
185
|
+
* `HMAC-SHA256(machine-key, "cleo:project-credential:v2\0" ‖ projectId)`.
|
|
186
|
+
*/
|
|
187
|
+
export const PROJECT_CIPHERTEXT_VERSION_PROJECT_ID = 0x02;
|
|
188
|
+
/** Domain-separation prefix for the project-identity KDF message. */
|
|
189
|
+
const PROJECT_ID_KDF_CONTEXT = 'cleo:project-credential:v2\0';
|
|
190
|
+
/**
|
|
191
|
+
* Derive the project-identity key.
|
|
192
|
+
*
|
|
193
|
+
* @throws {Error} If `projectId` is empty — an empty id would collapse every
|
|
194
|
+
* project onto one key.
|
|
195
|
+
*/
|
|
196
|
+
async function deriveProjectIdKey(projectId, cleoHome) {
|
|
197
|
+
if (projectId.length === 0) {
|
|
198
|
+
throw new Error('Cannot derive a project credential key: projectId is empty');
|
|
199
|
+
}
|
|
200
|
+
const machineKey = await getMachineKey(cleoHome);
|
|
201
|
+
return createHmac('sha256', machineKey)
|
|
202
|
+
.update(PROJECT_ID_KDF_CONTEXT + projectId, 'utf8')
|
|
203
|
+
.digest();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Derive the LEGACY path-bound project key, `HMAC-SHA256(machine-key, path)`.
|
|
207
|
+
* Used only to read pre-T12326 ciphertexts during migration.
|
|
208
|
+
*/
|
|
209
|
+
async function deriveLegacyPathKey(projectPath, cleoHome) {
|
|
210
|
+
const machineKey = await getMachineKey(cleoHome);
|
|
211
|
+
return createHmac('sha256', machineKey).update(projectPath).digest();
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Encrypt a project-scoped secret under the project-identity KDF.
|
|
215
|
+
*
|
|
216
|
+
* The key is `HMAC-SHA256(machine-key, "cleo:project-credential:v2\0" ‖
|
|
217
|
+
* projectId)`: bound to this device (machine-key) and to the project's
|
|
218
|
+
* identity, but NOT to where the project directory lives. Moving or renaming
|
|
219
|
+
* the checkout keeps the key; a different device does not (see
|
|
220
|
+
* `store/credential-transfer.ts` for the portable path).
|
|
221
|
+
*
|
|
222
|
+
* @param plaintext - The secret (e.g. an agent API key).
|
|
223
|
+
* @param projectId - Stable project identity.
|
|
224
|
+
* @param options - Key options (which CLEO home's machine-key).
|
|
225
|
+
* @returns Base64 ciphertext, version byte {@link PROJECT_CIPHERTEXT_VERSION_PROJECT_ID}.
|
|
226
|
+
* @task T12326
|
|
227
|
+
*/
|
|
228
|
+
export async function encryptProjectSecret(plaintext, projectId, options = {}) {
|
|
229
|
+
const key = await deriveProjectIdKey(projectId, options.cleoHome);
|
|
230
|
+
return sealWithKey(plaintext, key, PROJECT_CIPHERTEXT_VERSION_PROJECT_ID);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Decrypt a project-scoped secret, migrating legacy ciphertexts on the way.
|
|
234
|
+
*
|
|
235
|
+
* - Version `0x02` ciphertexts open with the project-identity key.
|
|
236
|
+
* - Version `0x01` (legacy, path-bound) ciphertexts are tried against each of
|
|
237
|
+
* `legacyProjectPaths`; the first that authenticates wins and the secret is
|
|
238
|
+
* re-encrypted under the project-identity KDF and returned as `rewrapped`
|
|
239
|
+
* for the caller to persist. The legacy ciphertext is never needed again.
|
|
240
|
+
*
|
|
241
|
+
* @param ciphertext - Base64 ciphertext from either KDF.
|
|
242
|
+
* @param context - Project identity plus legacy path candidates.
|
|
243
|
+
* @returns The plaintext, which KDF opened it, and any re-wrapped ciphertext.
|
|
244
|
+
* @throws {Error} If no key authenticates. The message names the paths tried
|
|
245
|
+
* so the caller can report which credential to re-enter.
|
|
246
|
+
* @task T12326
|
|
247
|
+
*/
|
|
248
|
+
export async function decryptProjectSecret(ciphertext, context) {
|
|
249
|
+
const parts = unframe(ciphertext);
|
|
250
|
+
if (parts.version === PROJECT_CIPHERTEXT_VERSION_PROJECT_ID) {
|
|
251
|
+
const plaintext = openWithKey(parts, await deriveProjectIdKey(context.projectId, context.cleoHome));
|
|
252
|
+
if (plaintext === null) {
|
|
253
|
+
throw new Error(`Cannot decrypt project credential for project ${context.projectId}: ` +
|
|
254
|
+
'the machine key differs from the one that encrypted it (another device, or a ' +
|
|
255
|
+
'regenerated machine-key), or the data is corrupted.');
|
|
256
|
+
}
|
|
257
|
+
return { plaintext, kdf: 'project-id', rewrapped: null };
|
|
258
|
+
}
|
|
259
|
+
if (parts.version === PROJECT_CIPHERTEXT_VERSION_LEGACY_PATH) {
|
|
260
|
+
const candidates = [...new Set(context.legacyProjectPaths ?? [])];
|
|
261
|
+
for (const candidate of candidates) {
|
|
262
|
+
const plaintext = openWithKey(parts, await deriveLegacyPathKey(candidate, context.cleoHome));
|
|
263
|
+
if (plaintext !== null) {
|
|
264
|
+
return {
|
|
265
|
+
plaintext,
|
|
266
|
+
kdf: 'legacy-path',
|
|
267
|
+
rewrapped: await encryptProjectSecret(plaintext, context.projectId, context),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
throw new Error('Cannot decrypt legacy path-bound project credential: none of the candidate project ' +
|
|
272
|
+
`paths opened it (${candidates.length === 0 ? 'none supplied' : candidates.join(', ')}). ` +
|
|
273
|
+
'It was encrypted at a path this device no longer knows, or on another device.');
|
|
274
|
+
}
|
|
275
|
+
throw new Error(`Unknown project ciphertext version (${parts.version}). Expected ` +
|
|
276
|
+
`${PROJECT_CIPHERTEXT_VERSION_PROJECT_ID} or ${PROJECT_CIPHERTEXT_VERSION_LEGACY_PATH}.`);
|
|
277
|
+
}
|
|
278
|
+
// ============================================================================
|
|
221
279
|
// Global (project-independent) KDF — ADR-037 §5
|
|
222
280
|
// ============================================================================
|
|
223
281
|
/**
|
|
@@ -247,9 +305,9 @@ export async function decrypt(ciphertext, projectPath) {
|
|
|
247
305
|
* @see getGlobalSalt — global-salt source of truth (store/global-salt.ts)
|
|
248
306
|
* @task T11710
|
|
249
307
|
*/
|
|
250
|
-
async function deriveGlobalKey(id) {
|
|
251
|
-
const machineKey = await getMachineKey();
|
|
252
|
-
const globalSalt = getGlobalSalt();
|
|
308
|
+
async function deriveGlobalKey(id, cleoHome) {
|
|
309
|
+
const machineKey = await getMachineKey(cleoHome);
|
|
310
|
+
const globalSalt = cleoHome === undefined ? getGlobalSalt() : loadGlobalSaltAt(cleoHome);
|
|
253
311
|
// HMAC key = machine-key || globalSalt (concatenation); message = id.
|
|
254
312
|
const hmacKey = Buffer.concat([machineKey, globalSalt]);
|
|
255
313
|
return createHmac('sha256', hmacKey).update(id).digest();
|
|
@@ -270,21 +328,15 @@ async function deriveGlobalKey(id) {
|
|
|
270
328
|
*
|
|
271
329
|
* @param plaintext - The string to encrypt (e.g. an LLM API key).
|
|
272
330
|
* @param id - The stable identity used for key derivation (e.g. an agentId).
|
|
331
|
+
* @param options - Key options (which CLEO home's machine-key + salt).
|
|
273
332
|
* @returns Base64-encoded ciphertext.
|
|
274
333
|
*
|
|
275
334
|
* @see decryptGlobal — the inverse operation.
|
|
276
335
|
* @task T11710
|
|
277
336
|
*/
|
|
278
|
-
export async function encryptGlobal(plaintext, id) {
|
|
279
|
-
const key = await deriveGlobalKey(id);
|
|
280
|
-
|
|
281
|
-
const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
|
|
282
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
283
|
-
const authTag = cipher.getAuthTag();
|
|
284
|
-
// Pack: version + iv + ciphertext + authTag (matches encrypt() format exactly).
|
|
285
|
-
const version = Buffer.from([CIPHERTEXT_VERSION]);
|
|
286
|
-
const packed = Buffer.concat([version, iv, encrypted, authTag]);
|
|
287
|
-
return packed.toString('base64');
|
|
337
|
+
export async function encryptGlobal(plaintext, id, options = {}) {
|
|
338
|
+
const key = await deriveGlobalKey(id, options.cleoHome);
|
|
339
|
+
return sealWithKey(plaintext, key, CIPHERTEXT_VERSION);
|
|
288
340
|
}
|
|
289
341
|
/**
|
|
290
342
|
* Decrypt a base64-encoded ciphertext produced by {@link encryptGlobal} using
|
|
@@ -293,6 +345,7 @@ export async function encryptGlobal(plaintext, id) {
|
|
|
293
345
|
* @param ciphertext - Base64-encoded string from {@link encryptGlobal}.
|
|
294
346
|
* @param id - The identity used at encryption time. A mismatched `id` derives a
|
|
295
347
|
* different key and fails the GCM auth tag (throws).
|
|
348
|
+
* @param options - Key options (which CLEO home's machine-key + salt).
|
|
296
349
|
* @returns The original plaintext string.
|
|
297
350
|
* @throws If decryption fails (wrong id, corrupted data, or machine-key/
|
|
298
351
|
* global-salt mismatch).
|
|
@@ -300,30 +353,18 @@ export async function encryptGlobal(plaintext, id) {
|
|
|
300
353
|
* @see encryptGlobal — the inverse operation.
|
|
301
354
|
* @task T11710
|
|
302
355
|
*/
|
|
303
|
-
export async function decryptGlobal(ciphertext, id) {
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
throw new Error('Cannot decrypt credentials: ciphertext too short');
|
|
308
|
-
}
|
|
309
|
-
const version = packed[0];
|
|
310
|
-
if (version !== CIPHERTEXT_VERSION) {
|
|
311
|
-
throw new Error(`Unknown ciphertext version (${version}). Expected ${CIPHERTEXT_VERSION}. ` +
|
|
356
|
+
export async function decryptGlobal(ciphertext, id, options = {}) {
|
|
357
|
+
const parts = unframe(ciphertext);
|
|
358
|
+
if (parts.version !== CIPHERTEXT_VERSION) {
|
|
359
|
+
throw new Error(`Unknown ciphertext version (${parts.version}). Expected ${CIPHERTEXT_VERSION}. ` +
|
|
312
360
|
'Re-register agents to re-encrypt with the current format.');
|
|
313
361
|
}
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
const encrypted = packed.subarray(1 + IV_LENGTH, packed.length - AUTH_TAG_LENGTH);
|
|
317
|
-
const decipher = createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
|
|
318
|
-
decipher.setAuthTag(authTag);
|
|
319
|
-
try {
|
|
320
|
-
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
321
|
-
return decrypted.toString('utf8');
|
|
322
|
-
}
|
|
323
|
-
catch {
|
|
362
|
+
const plaintext = openWithKey(parts, await deriveGlobalKey(id, options.cleoHome));
|
|
363
|
+
if (plaintext === null) {
|
|
324
364
|
throw new Error('Cannot decrypt global credentials. Machine key / global-salt mismatch, ' +
|
|
325
365
|
'wrong id, or corrupted data. If this database was moved from another ' +
|
|
326
366
|
'machine, re-register the credential under its original id.');
|
|
327
367
|
}
|
|
368
|
+
return plaintext;
|
|
328
369
|
}
|
|
329
370
|
//# sourceMappingURL=credentials.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/crypto/credentials.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/crypto/credentials.ts"],"names":[],"mappings":"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"}
|