aios-core 4.2.13 → 4.2.15

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 (95) hide show
  1. package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
  4. package/.aios-core/data/entity-registry.yaml +27 -0
  5. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  6. package/.aios-core/development/scripts/backup-manager.js +606 -606
  7. package/.aios-core/development/scripts/branch-manager.js +389 -389
  8. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  9. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  10. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  11. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  12. package/.aios-core/development/scripts/diff-generator.js +351 -351
  13. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  14. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  15. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  16. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  17. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  18. package/.aios-core/development/scripts/modification-validator.js +554 -554
  19. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  20. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  21. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  22. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  23. package/.aios-core/development/scripts/security-checker.js +358 -358
  24. package/.aios-core/development/scripts/template-engine.js +239 -239
  25. package/.aios-core/development/scripts/template-validator.js +278 -278
  26. package/.aios-core/development/scripts/test-generator.js +843 -843
  27. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  28. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  29. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  30. package/.aios-core/development/scripts/version-tracker.js +526 -526
  31. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  32. package/.aios-core/development/tasks/build-autonomous.md +10 -4
  33. package/.aios-core/development/tasks/create-service.md +23 -0
  34. package/.aios-core/development/tasks/dev-develop-story.md +12 -6
  35. package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
  36. package/.aios-core/development/tasks/publish-npm.md +3 -3
  37. package/.aios-core/hooks/unified/README.md +1 -1
  38. package/.aios-core/install-manifest.yaml +65 -61
  39. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  42. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  43. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  44. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  46. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  47. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  48. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  49. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  50. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  51. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  52. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  53. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  54. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  55. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  56. package/README.en.md +747 -0
  57. package/README.md +4 -2
  58. package/bin/aios.js +7 -4
  59. package/package.json +1 -1
  60. package/packages/aios-pro-cli/src/recover.js +1 -1
  61. package/packages/installer/src/wizard/ide-config-generator.js +6 -6
  62. package/packages/installer/src/wizard/pro-setup.js +3 -3
  63. package/pro/license/degradation.js +220 -220
  64. package/pro/license/errors.js +450 -450
  65. package/pro/license/feature-gate.js +354 -354
  66. package/pro/license/index.js +181 -181
  67. package/pro/license/license-cache.js +523 -523
  68. package/pro/license/license-crypto.js +303 -303
  69. package/scripts/package-synapse.js +5 -5
  70. package/scripts/validate-package-completeness.js +3 -3
  71. package/.aios-core/.session/current-session.json +0 -14
  72. package/.aios-core/data/registry-update-log.jsonl +0 -191
  73. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
  74. package/.aios-core/docs/component-creation-guide.md +0 -458
  75. package/.aios-core/docs/session-update-pattern.md +0 -307
  76. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
  77. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
  78. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
  79. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
  80. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
  81. package/.aios-core/docs/template-syntax.md +0 -267
  82. package/.aios-core/docs/troubleshooting-guide.md +0 -625
  83. package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
  84. package/.aios-core/manifests/agents.csv +0 -29
  85. package/.aios-core/manifests/tasks.csv +0 -198
  86. package/.aios-core/manifests/workers.csv +0 -204
  87. package/.claude/rules/agent-authority.md +0 -105
  88. package/.claude/rules/coderabbit-integration.md +0 -93
  89. package/.claude/rules/ids-principles.md +0 -112
  90. package/.claude/rules/story-lifecycle.md +0 -139
  91. package/.claude/rules/workflow-execution.md +0 -150
  92. package/scripts/glue/README.md +0 -355
  93. package/scripts/glue/compose-agent-prompt.cjs +0 -362
  94. /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
  95. /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
@@ -1,303 +1,303 @@
1
- /**
2
- * License Crypto Module
3
- *
4
- * Provides cryptographic utilities for license cache encryption:
5
- * - Machine ID generation (deterministic fingerprint)
6
- * - PBKDF2 key derivation
7
- * - AES-256-GCM encryption/decryption
8
- * - HMAC-SHA256 integrity verification
9
- *
10
- * @module pro/license/license-crypto
11
- * @see ADR-PRO-003 - Feature Gating & Licensing
12
- * @see Story PRO-6 - License Key & Feature Gating System
13
- */
14
-
15
- 'use strict';
16
-
17
- const crypto = require('crypto');
18
- const os = require('os');
19
-
20
- /**
21
- * Configuration constants for cryptographic operations.
22
- * These values follow security best practices per ADR-PRO-003.
23
- */
24
- const CONFIG = {
25
- // PBKDF2 settings
26
- PBKDF2_ITERATIONS: 100000, // Minimum per ADR-PRO-003
27
- PBKDF2_KEY_LENGTH: 32, // 256 bits for AES-256
28
- PBKDF2_DIGEST: 'sha256',
29
-
30
- // AES-256-GCM settings
31
- AES_ALGORITHM: 'aes-256-gcm',
32
- AES_IV_LENGTH: 12, // 96 bits recommended for GCM
33
- AES_TAG_LENGTH: 16, // 128 bits auth tag
34
-
35
- // HMAC settings
36
- HMAC_ALGORITHM: 'sha256',
37
-
38
- // Salt settings
39
- SALT_LENGTH: 16, // 128 bits
40
- };
41
-
42
- /**
43
- * Generate a deterministic machine identifier.
44
- *
45
- * Combines hostname + CPU model + first MAC address to create
46
- * a unique but reproducible identifier for this machine.
47
- *
48
- * @returns {string} SHA-256 hash of machine fingerprint components
49
- */
50
- function generateMachineId() {
51
- const components = [];
52
-
53
- // Hostname
54
- const hostname = os.hostname();
55
- components.push(hostname);
56
-
57
- // CPU model (first CPU)
58
- const cpus = os.cpus();
59
- if (cpus.length > 0) {
60
- components.push(cpus[0].model);
61
- }
62
-
63
- // First non-internal MAC address
64
- const networkInterfaces = os.networkInterfaces();
65
- for (const [, interfaces] of Object.entries(networkInterfaces)) {
66
- for (const iface of interfaces) {
67
- if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {
68
- components.push(iface.mac);
69
- break;
70
- }
71
- }
72
- // Only use first valid MAC
73
- if (components.length > 2) {
74
- break;
75
- }
76
- }
77
-
78
- // Create deterministic hash
79
- const fingerprint = components.join('|');
80
- return crypto.createHash('sha256').update(fingerprint).digest('hex');
81
- }
82
-
83
- /**
84
- * Generate a cryptographically secure random salt.
85
- *
86
- * @param {number} [length=16] - Salt length in bytes
87
- * @returns {Buffer} Random salt buffer
88
- */
89
- function generateSalt(length = CONFIG.SALT_LENGTH) {
90
- return crypto.randomBytes(length);
91
- }
92
-
93
- /**
94
- * Derive an encryption key from machine ID using PBKDF2.
95
- *
96
- * Uses 100,000 iterations minimum per ADR-PRO-003 security requirements.
97
- *
98
- * @param {string} machineId - Machine identifier from generateMachineId()
99
- * @param {Buffer|string} salt - Random salt (Buffer or hex string)
100
- * @returns {Buffer} 256-bit derived key
101
- */
102
- function deriveCacheKey(machineId, salt) {
103
- const saltBuffer = Buffer.isBuffer(salt) ? salt : Buffer.from(salt, 'hex');
104
-
105
- return crypto.pbkdf2Sync(
106
- machineId,
107
- saltBuffer,
108
- CONFIG.PBKDF2_ITERATIONS,
109
- CONFIG.PBKDF2_KEY_LENGTH,
110
- CONFIG.PBKDF2_DIGEST,
111
- );
112
- }
113
-
114
- /**
115
- * Encrypt data using AES-256-GCM.
116
- *
117
- * Returns an object containing the encrypted ciphertext, initialization vector,
118
- * and authentication tag for integrity verification.
119
- *
120
- * @param {string|object} data - Data to encrypt (objects are JSON stringified)
121
- * @param {Buffer|string} key - 256-bit encryption key (Buffer or hex string)
122
- * @returns {{ ciphertext: string, iv: string, tag: string }} Encrypted data components (hex encoded)
123
- * @throws {Error} If encryption fails
124
- */
125
- function encrypt(data, key) {
126
- const keyBuffer = Buffer.isBuffer(key) ? key : Buffer.from(key, 'hex');
127
-
128
- if (keyBuffer.length !== 32) {
129
- throw new Error('Encryption key must be 256 bits (32 bytes)');
130
- }
131
-
132
- // Generate random IV
133
- const iv = crypto.randomBytes(CONFIG.AES_IV_LENGTH);
134
-
135
- // Stringify objects
136
- const plaintext = typeof data === 'object' ? JSON.stringify(data) : String(data);
137
-
138
- // Create cipher and encrypt
139
- const cipher = crypto.createCipheriv(CONFIG.AES_ALGORITHM, keyBuffer, iv, {
140
- authTagLength: CONFIG.AES_TAG_LENGTH,
141
- });
142
-
143
- let ciphertext = cipher.update(plaintext, 'utf8', 'hex');
144
- ciphertext += cipher.final('hex');
145
-
146
- // Get auth tag
147
- const tag = cipher.getAuthTag();
148
-
149
- return {
150
- ciphertext,
151
- iv: iv.toString('hex'),
152
- tag: tag.toString('hex'),
153
- };
154
- }
155
-
156
- /**
157
- * Decrypt data using AES-256-GCM.
158
- *
159
- * Verifies the authentication tag to ensure data integrity.
160
- *
161
- * @param {{ ciphertext: string, iv: string, tag: string }} encryptedData - Encrypted data object
162
- * @param {Buffer|string} key - 256-bit encryption key (Buffer or hex string)
163
- * @param {boolean} [parseJson=true] - Whether to parse result as JSON
164
- * @returns {string|object} Decrypted data
165
- * @throws {Error} If decryption fails or authentication fails
166
- */
167
- function decrypt(encryptedData, key, parseJson = true) {
168
- const { ciphertext, iv, tag } = encryptedData;
169
-
170
- const keyBuffer = Buffer.isBuffer(key) ? key : Buffer.from(key, 'hex');
171
-
172
- if (keyBuffer.length !== 32) {
173
- throw new Error('Decryption key must be 256 bits (32 bytes)');
174
- }
175
-
176
- const ivBuffer = Buffer.from(iv, 'hex');
177
- const tagBuffer = Buffer.from(tag, 'hex');
178
-
179
- // Create decipher
180
- const decipher = crypto.createDecipheriv(CONFIG.AES_ALGORITHM, keyBuffer, ivBuffer, {
181
- authTagLength: CONFIG.AES_TAG_LENGTH,
182
- });
183
-
184
- decipher.setAuthTag(tagBuffer);
185
-
186
- let plaintext = decipher.update(ciphertext, 'hex', 'utf8');
187
- plaintext += decipher.final('utf8');
188
-
189
- // Optionally parse as JSON
190
- if (parseJson) {
191
- try {
192
- return JSON.parse(plaintext);
193
- } catch {
194
- // Return as string if not valid JSON
195
- return plaintext;
196
- }
197
- }
198
-
199
- return plaintext;
200
- }
201
-
202
- /**
203
- * Compute HMAC-SHA256 for data integrity verification.
204
- *
205
- * @param {string|Buffer} data - Data to compute HMAC for
206
- * @param {Buffer|string} key - HMAC key (Buffer or hex string)
207
- * @returns {string} HMAC as hex string
208
- */
209
- function computeHMAC(data, key) {
210
- const keyBuffer = Buffer.isBuffer(key) ? key : Buffer.from(key, 'hex');
211
- const dataString = Buffer.isBuffer(data) ? data.toString('utf8') : String(data);
212
-
213
- return crypto.createHmac(CONFIG.HMAC_ALGORITHM, keyBuffer).update(dataString).digest('hex');
214
- }
215
-
216
- /**
217
- * Verify HMAC matches expected value.
218
- *
219
- * Uses timing-safe comparison to prevent timing attacks.
220
- *
221
- * @param {string|Buffer} data - Data to verify
222
- * @param {Buffer|string} key - HMAC key
223
- * @param {string} expectedHmac - Expected HMAC value (hex string)
224
- * @returns {boolean} true if HMAC matches
225
- */
226
- function verifyHMAC(data, key, expectedHmac) {
227
- const computedHmac = computeHMAC(data, key);
228
-
229
- // Use timing-safe comparison
230
- const computedBuffer = Buffer.from(computedHmac, 'hex');
231
- const expectedBuffer = Buffer.from(expectedHmac, 'hex');
232
-
233
- if (computedBuffer.length !== expectedBuffer.length) {
234
- return false;
235
- }
236
-
237
- return crypto.timingSafeEqual(computedBuffer, expectedBuffer);
238
- }
239
-
240
- /**
241
- * Mask a license key for display.
242
- *
243
- * Shows only first and last 4 characters: PRO-XXXX-****-****-XXXX
244
- * CRITICAL: Always use this function when logging or displaying keys.
245
- *
246
- * @param {string} key - Full license key
247
- * @returns {string} Masked key for display
248
- */
249
- function maskKey(key) {
250
- if (!key || typeof key !== 'string') {
251
- return '****-****-****-****';
252
- }
253
-
254
- // Handle PRO-XXXX-XXXX-XXXX-XXXX format
255
- const parts = key.split('-');
256
- if (parts.length !== 5 || parts[0] !== 'PRO') {
257
- // Non-standard format, mask all but first/last 4
258
- if (key.length <= 8) {
259
- return '****';
260
- }
261
- return `${key.slice(0, 4)}-****-****-${key.slice(-4)}`;
262
- }
263
-
264
- // Standard format: PRO-XXXX-XXXX-XXXX-XXXX
265
- return `${parts[0]}-${parts[1]}-****-****-${parts[4]}`;
266
- }
267
-
268
- /**
269
- * Validate license key format.
270
- *
271
- * Expected format: PRO-XXXX-XXXX-XXXX-XXXX
272
- * Where X is alphanumeric (A-Z0-9)
273
- *
274
- * @param {string} key - License key to validate
275
- * @returns {boolean} true if format is valid
276
- */
277
- function validateKeyFormat(key) {
278
- if (!key || typeof key !== 'string') {
279
- return false;
280
- }
281
-
282
- // Format: PRO-XXXX-XXXX-XXXX-XXXX (uppercase alphanumeric)
283
- const pattern = /^PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
284
- return pattern.test(key);
285
- }
286
-
287
- module.exports = {
288
- // Core functions per ADR-PRO-003
289
- generateMachineId,
290
- deriveCacheKey,
291
- encrypt,
292
- decrypt,
293
- computeHMAC,
294
-
295
- // Additional utilities
296
- generateSalt,
297
- verifyHMAC,
298
- maskKey,
299
- validateKeyFormat,
300
-
301
- // Exported for testing
302
- _CONFIG: CONFIG,
303
- };
1
+ /**
2
+ * License Crypto Module
3
+ *
4
+ * Provides cryptographic utilities for license cache encryption:
5
+ * - Machine ID generation (deterministic fingerprint)
6
+ * - PBKDF2 key derivation
7
+ * - AES-256-GCM encryption/decryption
8
+ * - HMAC-SHA256 integrity verification
9
+ *
10
+ * @module pro/license/license-crypto
11
+ * @see ADR-PRO-003 - Feature Gating & Licensing
12
+ * @see Story PRO-6 - License Key & Feature Gating System
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const crypto = require('crypto');
18
+ const os = require('os');
19
+
20
+ /**
21
+ * Configuration constants for cryptographic operations.
22
+ * These values follow security best practices per ADR-PRO-003.
23
+ */
24
+ const CONFIG = {
25
+ // PBKDF2 settings
26
+ PBKDF2_ITERATIONS: 100000, // Minimum per ADR-PRO-003
27
+ PBKDF2_KEY_LENGTH: 32, // 256 bits for AES-256
28
+ PBKDF2_DIGEST: 'sha256',
29
+
30
+ // AES-256-GCM settings
31
+ AES_ALGORITHM: 'aes-256-gcm',
32
+ AES_IV_LENGTH: 12, // 96 bits recommended for GCM
33
+ AES_TAG_LENGTH: 16, // 128 bits auth tag
34
+
35
+ // HMAC settings
36
+ HMAC_ALGORITHM: 'sha256',
37
+
38
+ // Salt settings
39
+ SALT_LENGTH: 16, // 128 bits
40
+ };
41
+
42
+ /**
43
+ * Generate a deterministic machine identifier.
44
+ *
45
+ * Combines hostname + CPU model + first MAC address to create
46
+ * a unique but reproducible identifier for this machine.
47
+ *
48
+ * @returns {string} SHA-256 hash of machine fingerprint components
49
+ */
50
+ function generateMachineId() {
51
+ const components = [];
52
+
53
+ // Hostname
54
+ const hostname = os.hostname();
55
+ components.push(hostname);
56
+
57
+ // CPU model (first CPU)
58
+ const cpus = os.cpus();
59
+ if (cpus.length > 0) {
60
+ components.push(cpus[0].model);
61
+ }
62
+
63
+ // First non-internal MAC address
64
+ const networkInterfaces = os.networkInterfaces();
65
+ for (const [, interfaces] of Object.entries(networkInterfaces)) {
66
+ for (const iface of interfaces) {
67
+ if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {
68
+ components.push(iface.mac);
69
+ break;
70
+ }
71
+ }
72
+ // Only use first valid MAC
73
+ if (components.length > 2) {
74
+ break;
75
+ }
76
+ }
77
+
78
+ // Create deterministic hash
79
+ const fingerprint = components.join('|');
80
+ return crypto.createHash('sha256').update(fingerprint).digest('hex');
81
+ }
82
+
83
+ /**
84
+ * Generate a cryptographically secure random salt.
85
+ *
86
+ * @param {number} [length=16] - Salt length in bytes
87
+ * @returns {Buffer} Random salt buffer
88
+ */
89
+ function generateSalt(length = CONFIG.SALT_LENGTH) {
90
+ return crypto.randomBytes(length);
91
+ }
92
+
93
+ /**
94
+ * Derive an encryption key from machine ID using PBKDF2.
95
+ *
96
+ * Uses 100,000 iterations minimum per ADR-PRO-003 security requirements.
97
+ *
98
+ * @param {string} machineId - Machine identifier from generateMachineId()
99
+ * @param {Buffer|string} salt - Random salt (Buffer or hex string)
100
+ * @returns {Buffer} 256-bit derived key
101
+ */
102
+ function deriveCacheKey(machineId, salt) {
103
+ const saltBuffer = Buffer.isBuffer(salt) ? salt : Buffer.from(salt, 'hex');
104
+
105
+ return crypto.pbkdf2Sync(
106
+ machineId,
107
+ saltBuffer,
108
+ CONFIG.PBKDF2_ITERATIONS,
109
+ CONFIG.PBKDF2_KEY_LENGTH,
110
+ CONFIG.PBKDF2_DIGEST,
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Encrypt data using AES-256-GCM.
116
+ *
117
+ * Returns an object containing the encrypted ciphertext, initialization vector,
118
+ * and authentication tag for integrity verification.
119
+ *
120
+ * @param {string|object} data - Data to encrypt (objects are JSON stringified)
121
+ * @param {Buffer|string} key - 256-bit encryption key (Buffer or hex string)
122
+ * @returns {{ ciphertext: string, iv: string, tag: string }} Encrypted data components (hex encoded)
123
+ * @throws {Error} If encryption fails
124
+ */
125
+ function encrypt(data, key) {
126
+ const keyBuffer = Buffer.isBuffer(key) ? key : Buffer.from(key, 'hex');
127
+
128
+ if (keyBuffer.length !== 32) {
129
+ throw new Error('Encryption key must be 256 bits (32 bytes)');
130
+ }
131
+
132
+ // Generate random IV
133
+ const iv = crypto.randomBytes(CONFIG.AES_IV_LENGTH);
134
+
135
+ // Stringify objects
136
+ const plaintext = typeof data === 'object' ? JSON.stringify(data) : String(data);
137
+
138
+ // Create cipher and encrypt
139
+ const cipher = crypto.createCipheriv(CONFIG.AES_ALGORITHM, keyBuffer, iv, {
140
+ authTagLength: CONFIG.AES_TAG_LENGTH,
141
+ });
142
+
143
+ let ciphertext = cipher.update(plaintext, 'utf8', 'hex');
144
+ ciphertext += cipher.final('hex');
145
+
146
+ // Get auth tag
147
+ const tag = cipher.getAuthTag();
148
+
149
+ return {
150
+ ciphertext,
151
+ iv: iv.toString('hex'),
152
+ tag: tag.toString('hex'),
153
+ };
154
+ }
155
+
156
+ /**
157
+ * Decrypt data using AES-256-GCM.
158
+ *
159
+ * Verifies the authentication tag to ensure data integrity.
160
+ *
161
+ * @param {{ ciphertext: string, iv: string, tag: string }} encryptedData - Encrypted data object
162
+ * @param {Buffer|string} key - 256-bit encryption key (Buffer or hex string)
163
+ * @param {boolean} [parseJson=true] - Whether to parse result as JSON
164
+ * @returns {string|object} Decrypted data
165
+ * @throws {Error} If decryption fails or authentication fails
166
+ */
167
+ function decrypt(encryptedData, key, parseJson = true) {
168
+ const { ciphertext, iv, tag } = encryptedData;
169
+
170
+ const keyBuffer = Buffer.isBuffer(key) ? key : Buffer.from(key, 'hex');
171
+
172
+ if (keyBuffer.length !== 32) {
173
+ throw new Error('Decryption key must be 256 bits (32 bytes)');
174
+ }
175
+
176
+ const ivBuffer = Buffer.from(iv, 'hex');
177
+ const tagBuffer = Buffer.from(tag, 'hex');
178
+
179
+ // Create decipher
180
+ const decipher = crypto.createDecipheriv(CONFIG.AES_ALGORITHM, keyBuffer, ivBuffer, {
181
+ authTagLength: CONFIG.AES_TAG_LENGTH,
182
+ });
183
+
184
+ decipher.setAuthTag(tagBuffer);
185
+
186
+ let plaintext = decipher.update(ciphertext, 'hex', 'utf8');
187
+ plaintext += decipher.final('utf8');
188
+
189
+ // Optionally parse as JSON
190
+ if (parseJson) {
191
+ try {
192
+ return JSON.parse(plaintext);
193
+ } catch {
194
+ // Return as string if not valid JSON
195
+ return plaintext;
196
+ }
197
+ }
198
+
199
+ return plaintext;
200
+ }
201
+
202
+ /**
203
+ * Compute HMAC-SHA256 for data integrity verification.
204
+ *
205
+ * @param {string|Buffer} data - Data to compute HMAC for
206
+ * @param {Buffer|string} key - HMAC key (Buffer or hex string)
207
+ * @returns {string} HMAC as hex string
208
+ */
209
+ function computeHMAC(data, key) {
210
+ const keyBuffer = Buffer.isBuffer(key) ? key : Buffer.from(key, 'hex');
211
+ const dataString = Buffer.isBuffer(data) ? data.toString('utf8') : String(data);
212
+
213
+ return crypto.createHmac(CONFIG.HMAC_ALGORITHM, keyBuffer).update(dataString).digest('hex');
214
+ }
215
+
216
+ /**
217
+ * Verify HMAC matches expected value.
218
+ *
219
+ * Uses timing-safe comparison to prevent timing attacks.
220
+ *
221
+ * @param {string|Buffer} data - Data to verify
222
+ * @param {Buffer|string} key - HMAC key
223
+ * @param {string} expectedHmac - Expected HMAC value (hex string)
224
+ * @returns {boolean} true if HMAC matches
225
+ */
226
+ function verifyHMAC(data, key, expectedHmac) {
227
+ const computedHmac = computeHMAC(data, key);
228
+
229
+ // Use timing-safe comparison
230
+ const computedBuffer = Buffer.from(computedHmac, 'hex');
231
+ const expectedBuffer = Buffer.from(expectedHmac, 'hex');
232
+
233
+ if (computedBuffer.length !== expectedBuffer.length) {
234
+ return false;
235
+ }
236
+
237
+ return crypto.timingSafeEqual(computedBuffer, expectedBuffer);
238
+ }
239
+
240
+ /**
241
+ * Mask a license key for display.
242
+ *
243
+ * Shows only first and last 4 characters: PRO-XXXX-****-****-XXXX
244
+ * CRITICAL: Always use this function when logging or displaying keys.
245
+ *
246
+ * @param {string} key - Full license key
247
+ * @returns {string} Masked key for display
248
+ */
249
+ function maskKey(key) {
250
+ if (!key || typeof key !== 'string') {
251
+ return '****-****-****-****';
252
+ }
253
+
254
+ // Handle PRO-XXXX-XXXX-XXXX-XXXX format
255
+ const parts = key.split('-');
256
+ if (parts.length !== 5 || parts[0] !== 'PRO') {
257
+ // Non-standard format, mask all but first/last 4
258
+ if (key.length <= 8) {
259
+ return '****';
260
+ }
261
+ return `${key.slice(0, 4)}-****-****-${key.slice(-4)}`;
262
+ }
263
+
264
+ // Standard format: PRO-XXXX-XXXX-XXXX-XXXX
265
+ return `${parts[0]}-${parts[1]}-****-****-${parts[4]}`;
266
+ }
267
+
268
+ /**
269
+ * Validate license key format.
270
+ *
271
+ * Expected format: PRO-XXXX-XXXX-XXXX-XXXX
272
+ * Where X is alphanumeric (A-Z0-9)
273
+ *
274
+ * @param {string} key - License key to validate
275
+ * @returns {boolean} true if format is valid
276
+ */
277
+ function validateKeyFormat(key) {
278
+ if (!key || typeof key !== 'string') {
279
+ return false;
280
+ }
281
+
282
+ // Format: PRO-XXXX-XXXX-XXXX-XXXX (uppercase alphanumeric)
283
+ const pattern = /^PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
284
+ return pattern.test(key);
285
+ }
286
+
287
+ module.exports = {
288
+ // Core functions per ADR-PRO-003
289
+ generateMachineId,
290
+ deriveCacheKey,
291
+ encrypt,
292
+ decrypt,
293
+ computeHMAC,
294
+
295
+ // Additional utilities
296
+ generateSalt,
297
+ verifyHMAC,
298
+ maskKey,
299
+ validateKeyFormat,
300
+
301
+ // Exported for testing
302
+ _CONFIG: CONFIG,
303
+ };
@@ -59,7 +59,7 @@ const FILES = {
59
59
  'core/diagnostics/collectors/relevance-matrix.js': '.aios-core/core/synapse/diagnostics/collectors/relevance-matrix.js',
60
60
 
61
61
  // Hook Entry Point
62
- 'hook/synapse-engine.js': '.claude/hooks/synapse-engine.js',
62
+ 'hook/synapse-engine.cjs': '.claude/hooks/synapse-engine.cjs',
63
63
 
64
64
  // Commands
65
65
  'commands/manager.md': '.claude/commands/synapse/manager.md',
@@ -210,7 +210,7 @@ cp -r core/ <your-project>/.aios-core/core/synapse/
210
210
  ### 2. Copy Hook Entry Point
211
211
 
212
212
  \`\`\`
213
- cp hook/synapse-engine.js <your-project>/.claude/hooks/
213
+ cp hook/synapse-engine.cjs <your-project>/.claude/hooks/
214
214
  \`\`\`
215
215
 
216
216
  ### 3. Register the Hook
@@ -223,7 +223,7 @@ Add to \`.claude/settings.local.json\`:
223
223
  "UserPromptSubmit": [
224
224
  {
225
225
  "type": "command",
226
- "command": "node .claude/hooks/synapse-engine.js"
226
+ "command": "node .claude/hooks/synapse-engine.cjs"
227
227
  }
228
228
  ]
229
229
  }
@@ -254,7 +254,7 @@ Add these lines:
254
254
  .synapse/sessions/
255
255
  .synapse/metrics/
256
256
  .synapse/cache/
257
- !.claude/hooks/synapse-engine.js
257
+ !.claude/hooks/synapse-engine.cjs
258
258
  \`\`\`
259
259
 
260
260
  ## Verification
@@ -303,7 +303,7 @@ synapse-package/
303
303
  │ ├── diagnostics/ # Observability (10 collectors)
304
304
  │ ├── scripts/ # Constitution generator
305
305
  │ └── utils/ # Path + token helpers
306
- ├── hook/ # .claude/hooks/synapse-engine.js
306
+ ├── hook/ # .claude/hooks/synapse-engine.cjs
307
307
  ├── commands/ # .claude/commands/synapse/
308
308
  ├── skills/ # .claude/skills/synapse/
309
309
  ├── runtime/ # .synapse/ domain files (manifest, domains)
@@ -32,8 +32,8 @@ const VERBOSE = process.argv.includes('--verbose') || process.env.VERBOSE === 't
32
32
  */
33
33
  const REQUIRED_PATHS = [
34
34
  // Hooks (critical - these were missing in v4.0.0)
35
- '.claude/hooks/synapse-engine.js',
36
- '.claude/hooks/precompact-session-digest.js',
35
+ '.claude/hooks/synapse-engine.cjs',
36
+ '.claude/hooks/precompact-session-digest.cjs',
37
37
  // Rules
38
38
  '.claude/rules/',
39
39
  // CLI binaries
@@ -137,7 +137,7 @@ function getTarballContents() {
137
137
  timeout: 30000,
138
138
  });
139
139
 
140
- // Text output has lines like "npm notice 1.2kB .claude/hooks/synapse-engine.js"
140
+ // Text output has lines like "npm notice 1.2kB .claude/hooks/synapse-engine.cjs"
141
141
  const lines = output.split('\n');
142
142
  return lines
143
143
  .filter((line) => line.includes('npm notice') && !line.includes('=== Tarball'))