instar 0.26.11 → 0.27.2

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 (161) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/init.d.ts +5 -0
  3. package/dist/commands/init.d.ts.map +1 -1
  4. package/dist/commands/init.js +46 -1
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/commands/server.d.ts.map +1 -1
  7. package/dist/commands/server.js +228 -36
  8. package/dist/commands/server.js.map +1 -1
  9. package/dist/commands/slack-cli.d.ts +6 -0
  10. package/dist/commands/slack-cli.d.ts.map +1 -1
  11. package/dist/commands/slack-cli.js +99 -1
  12. package/dist/commands/slack-cli.js.map +1 -1
  13. package/dist/core/GitSync.d.ts.map +1 -1
  14. package/dist/core/GitSync.js +47 -5
  15. package/dist/core/GitSync.js.map +1 -1
  16. package/dist/core/PostUpdateMigrator.d.ts +7 -1
  17. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  18. package/dist/core/PostUpdateMigrator.js +19 -1
  19. package/dist/core/PostUpdateMigrator.js.map +1 -1
  20. package/dist/core/types.d.ts +9 -0
  21. package/dist/core/types.d.ts.map +1 -1
  22. package/dist/identity/IdentityManager.d.ts +70 -0
  23. package/dist/identity/IdentityManager.d.ts.map +1 -0
  24. package/dist/identity/IdentityManager.js +179 -0
  25. package/dist/identity/IdentityManager.js.map +1 -0
  26. package/dist/identity/KeyEncryption.d.ts +40 -0
  27. package/dist/identity/KeyEncryption.d.ts.map +1 -0
  28. package/dist/identity/KeyEncryption.js +93 -0
  29. package/dist/identity/KeyEncryption.js.map +1 -0
  30. package/dist/identity/KeyRevocation.d.ts +68 -0
  31. package/dist/identity/KeyRevocation.d.ts.map +1 -0
  32. package/dist/identity/KeyRevocation.js +171 -0
  33. package/dist/identity/KeyRevocation.js.map +1 -0
  34. package/dist/identity/KeyRotation.d.ts +43 -0
  35. package/dist/identity/KeyRotation.d.ts.map +1 -0
  36. package/dist/identity/KeyRotation.js +81 -0
  37. package/dist/identity/KeyRotation.js.map +1 -0
  38. package/dist/identity/Migration.d.ts +50 -0
  39. package/dist/identity/Migration.d.ts.map +1 -0
  40. package/dist/identity/Migration.js +125 -0
  41. package/dist/identity/Migration.js.map +1 -0
  42. package/dist/identity/RecoveryPhrase.d.ts +43 -0
  43. package/dist/identity/RecoveryPhrase.d.ts.map +1 -0
  44. package/dist/identity/RecoveryPhrase.js +93 -0
  45. package/dist/identity/RecoveryPhrase.js.map +1 -0
  46. package/dist/identity/index.d.ts +13 -0
  47. package/dist/identity/index.d.ts.map +1 -0
  48. package/dist/identity/index.js +20 -0
  49. package/dist/identity/index.js.map +1 -0
  50. package/dist/identity/types.d.ts +101 -0
  51. package/dist/identity/types.d.ts.map +1 -0
  52. package/dist/identity/types.js +55 -0
  53. package/dist/identity/types.js.map +1 -0
  54. package/dist/knowledge/TreeTriage.d.ts.map +1 -1
  55. package/dist/knowledge/TreeTriage.js +9 -0
  56. package/dist/knowledge/TreeTriage.js.map +1 -1
  57. package/dist/memory/TopicMemory.d.ts +2 -0
  58. package/dist/memory/TopicMemory.d.ts.map +1 -1
  59. package/dist/memory/TopicMemory.js +55 -0
  60. package/dist/memory/TopicMemory.js.map +1 -1
  61. package/dist/messaging/slack/SlackAdapter.d.ts +15 -4
  62. package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
  63. package/dist/messaging/slack/SlackAdapter.js +131 -14
  64. package/dist/messaging/slack/SlackAdapter.js.map +1 -1
  65. package/dist/messaging/slack/SocketModeClient.d.ts +2 -0
  66. package/dist/messaging/slack/SocketModeClient.d.ts.map +1 -1
  67. package/dist/messaging/slack/SocketModeClient.js +31 -1
  68. package/dist/messaging/slack/SocketModeClient.js.map +1 -1
  69. package/dist/moltbridge/MoltBridgeClient.d.ts +124 -0
  70. package/dist/moltbridge/MoltBridgeClient.d.ts.map +1 -0
  71. package/dist/moltbridge/MoltBridgeClient.js +321 -0
  72. package/dist/moltbridge/MoltBridgeClient.js.map +1 -0
  73. package/dist/moltbridge/ProfileCompiler.d.ts +68 -0
  74. package/dist/moltbridge/ProfileCompiler.d.ts.map +1 -0
  75. package/dist/moltbridge/ProfileCompiler.js +317 -0
  76. package/dist/moltbridge/ProfileCompiler.js.map +1 -0
  77. package/dist/moltbridge/index.d.ts +12 -0
  78. package/dist/moltbridge/index.d.ts.map +1 -0
  79. package/dist/moltbridge/index.js +11 -0
  80. package/dist/moltbridge/index.js.map +1 -0
  81. package/dist/moltbridge/routes.d.ts +21 -0
  82. package/dist/moltbridge/routes.d.ts.map +1 -0
  83. package/dist/moltbridge/routes.js +224 -0
  84. package/dist/moltbridge/routes.js.map +1 -0
  85. package/dist/moltbridge/types.d.ts +93 -0
  86. package/dist/moltbridge/types.d.ts.map +1 -0
  87. package/dist/moltbridge/types.js +20 -0
  88. package/dist/moltbridge/types.js.map +1 -0
  89. package/dist/monitoring/PresenceProxy.d.ts +3 -0
  90. package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
  91. package/dist/monitoring/PresenceProxy.js +80 -3
  92. package/dist/monitoring/PresenceProxy.js.map +1 -1
  93. package/dist/monitoring/SessionMonitor.d.ts +7 -0
  94. package/dist/monitoring/SessionMonitor.d.ts.map +1 -1
  95. package/dist/monitoring/SessionMonitor.js +47 -22
  96. package/dist/monitoring/SessionMonitor.js.map +1 -1
  97. package/dist/monitoring/TriageOrchestrator.d.ts.map +1 -1
  98. package/dist/monitoring/TriageOrchestrator.js +25 -1
  99. package/dist/monitoring/TriageOrchestrator.js.map +1 -1
  100. package/dist/scaffold/templates.d.ts.map +1 -1
  101. package/dist/scaffold/templates.js +29 -1
  102. package/dist/scaffold/templates.js.map +1 -1
  103. package/dist/server/AgentServer.d.ts +1 -0
  104. package/dist/server/AgentServer.d.ts.map +1 -1
  105. package/dist/server/AgentServer.js +1 -0
  106. package/dist/server/AgentServer.js.map +1 -1
  107. package/dist/server/routes.d.ts +2 -0
  108. package/dist/server/routes.d.ts.map +1 -1
  109. package/dist/server/routes.js +21 -0
  110. package/dist/server/routes.js.map +1 -1
  111. package/dist/threadline/AuthorizationPolicy.d.ts +110 -0
  112. package/dist/threadline/AuthorizationPolicy.d.ts.map +1 -0
  113. package/dist/threadline/AuthorizationPolicy.js +226 -0
  114. package/dist/threadline/AuthorizationPolicy.js.map +1 -0
  115. package/dist/threadline/DiscoveryWaterfall.d.ts +73 -0
  116. package/dist/threadline/DiscoveryWaterfall.d.ts.map +1 -0
  117. package/dist/threadline/DiscoveryWaterfall.js +113 -0
  118. package/dist/threadline/DiscoveryWaterfall.js.map +1 -0
  119. package/dist/threadline/ListenerSessionManager.d.ts.map +1 -1
  120. package/dist/threadline/ListenerSessionManager.js +2 -0
  121. package/dist/threadline/ListenerSessionManager.js.map +1 -1
  122. package/dist/threadline/MessageSecurity.d.ts +49 -0
  123. package/dist/threadline/MessageSecurity.d.ts.map +1 -0
  124. package/dist/threadline/MessageSecurity.js +93 -0
  125. package/dist/threadline/MessageSecurity.js.map +1 -0
  126. package/dist/threadline/SecureInvitation.d.ts +79 -0
  127. package/dist/threadline/SecureInvitation.d.ts.map +1 -0
  128. package/dist/threadline/SecureInvitation.js +164 -0
  129. package/dist/threadline/SecureInvitation.js.map +1 -0
  130. package/dist/threadline/TrustAuditLog.d.ts +49 -0
  131. package/dist/threadline/TrustAuditLog.d.ts.map +1 -0
  132. package/dist/threadline/TrustAuditLog.js +111 -0
  133. package/dist/threadline/TrustAuditLog.js.map +1 -0
  134. package/dist/threadline/TrustEvaluator.d.ts +69 -0
  135. package/dist/threadline/TrustEvaluator.d.ts.map +1 -0
  136. package/dist/threadline/TrustEvaluator.js +93 -0
  137. package/dist/threadline/TrustEvaluator.js.map +1 -0
  138. package/dist/threadline/UnifiedTrustWiring.d.ts +66 -0
  139. package/dist/threadline/UnifiedTrustWiring.d.ts.map +1 -0
  140. package/dist/threadline/UnifiedTrustWiring.js +192 -0
  141. package/dist/threadline/UnifiedTrustWiring.js.map +1 -0
  142. package/dist/threadline/client/IdentityManager.d.ts +27 -6
  143. package/dist/threadline/client/IdentityManager.d.ts.map +1 -1
  144. package/dist/threadline/client/IdentityManager.js +64 -18
  145. package/dist/threadline/client/IdentityManager.js.map +1 -1
  146. package/dist/threadline/relay/SybilProtection.d.ts +78 -0
  147. package/dist/threadline/relay/SybilProtection.d.ts.map +1 -0
  148. package/dist/threadline/relay/SybilProtection.js +187 -0
  149. package/dist/threadline/relay/SybilProtection.js.map +1 -0
  150. package/package.json +9 -3
  151. package/playbook-scripts/build-state.py +529 -0
  152. package/scripts/check-contract-evidence.js +103 -0
  153. package/scripts/pre-push-gate.js +53 -0
  154. package/scripts/run-contract-tests.js +75 -0
  155. package/src/data/builtin-manifest.json +87 -63
  156. package/src/templates/hooks/build-stop-hook.sh +79 -0
  157. package/upgrades/0.26.10.md +36 -0
  158. package/upgrades/0.27.1.md +101 -0
  159. package/upgrades/0.27.2.md +36 -0
  160. package/upgrades/NEXT.md +0 -27
  161. /package/.claude/skills/secret-setup/{SKILL.md → skill.md} +0 -0
@@ -0,0 +1,179 @@
1
+ /**
2
+ * CanonicalIdentityManager — Manages the agent's canonical Ed25519 identity.
3
+ *
4
+ * Stores identity at {stateDir}/identity.json with encrypted private key.
5
+ * This is the single source of truth for agent identity across all systems
6
+ * (Threadline, MoltBridge, A2A).
7
+ *
8
+ * Spec Section 3.3: Single keypair, managed by Instar, used by both systems.
9
+ */
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { generateIdentityKeyPair } from '../threadline/ThreadlineCrypto.js';
13
+ import { deriveX25519PublicKey } from '../threadline/client/MessageEncryptor.js';
14
+ import { encryptPrivateKey, decryptPrivateKey, generateSalt } from './KeyEncryption.js';
15
+ import { generateRecoveryPhrase, deriveRecoveryKeypair, createRecoveryCommitment, generateRecoverySalt, } from './RecoveryPhrase.js';
16
+ import { computeCanonicalId, computeDisplayFingerprint, IDENTITY_SCHEMA_VERSION, } from './types.js';
17
+ // ── Manager ──────────────────────────────────────────────────────────
18
+ export class CanonicalIdentityManager {
19
+ stateDir;
20
+ identityFile;
21
+ identity = null;
22
+ constructor(stateDir) {
23
+ this.stateDir = stateDir;
24
+ this.identityFile = path.join(stateDir, 'identity.json');
25
+ }
26
+ /**
27
+ * Create a new canonical identity.
28
+ *
29
+ * Generates Ed25519 keypair, optional recovery phrase, encrypts private key,
30
+ * and writes identity.json.
31
+ *
32
+ * @returns The identity and (if generated) the recovery phrase.
33
+ * The recovery phrase is ONLY returned here — it must be shown to the user
34
+ * and never stored by the system.
35
+ */
36
+ create(options = {}) {
37
+ const keypair = generateIdentityKeyPair();
38
+ const canonicalId = computeCanonicalId(keypair.publicKey);
39
+ const displayFingerprint = computeDisplayFingerprint(canonicalId);
40
+ let recoveryPhrase;
41
+ let recoveryCommitment;
42
+ let recoverySalt;
43
+ if (!options.skipRecovery) {
44
+ recoveryPhrase = generateRecoveryPhrase();
45
+ const rSalt = generateRecoverySalt();
46
+ const recoveryKeypair = deriveRecoveryKeypair(recoveryPhrase, rSalt);
47
+ recoveryCommitment = createRecoveryCommitment(recoveryKeypair.publicKey, keypair.privateKey);
48
+ recoverySalt = rSalt.toString('base64');
49
+ }
50
+ // Encrypt private key (or store plaintext in dev mode)
51
+ let privateKeyData;
52
+ let encryption;
53
+ let keySalt;
54
+ if (options.passphrase !== undefined) {
55
+ const salt = generateSalt();
56
+ privateKeyData = encryptPrivateKey(keypair.privateKey, options.passphrase, salt);
57
+ encryption = 'xchacha20-poly1305+argon2id';
58
+ keySalt = salt.toString('base64');
59
+ }
60
+ else {
61
+ privateKeyData = keypair.privateKey.toString('base64');
62
+ encryption = 'none';
63
+ }
64
+ const file = {
65
+ version: IDENTITY_SCHEMA_VERSION,
66
+ publicKey: keypair.publicKey.toString('base64'),
67
+ privateKey: privateKeyData,
68
+ privateKeyEncryption: encryption,
69
+ ...(keySalt && { keySalt }),
70
+ canonicalId,
71
+ displayFingerprint,
72
+ ...(recoveryCommitment && { recoveryCommitment }),
73
+ ...(recoverySalt && { recoverySalt }),
74
+ createdAt: new Date().toISOString(),
75
+ };
76
+ this.writeToDisk(file);
77
+ const identity = {
78
+ version: IDENTITY_SCHEMA_VERSION,
79
+ publicKey: keypair.publicKey,
80
+ privateKey: keypair.privateKey,
81
+ x25519PublicKey: deriveX25519PublicKey(keypair.privateKey),
82
+ canonicalId,
83
+ displayFingerprint,
84
+ createdAt: file.createdAt,
85
+ recoveryCommitment,
86
+ };
87
+ this.identity = identity;
88
+ return { identity, recoveryPhrase };
89
+ }
90
+ /**
91
+ * Load an existing identity from disk.
92
+ *
93
+ * @param options.passphrase Required if the private key is encrypted.
94
+ * @returns The decrypted identity, or null if no identity exists.
95
+ * @throws Error if passphrase is wrong or file is corrupted.
96
+ */
97
+ load(options = {}) {
98
+ if (this.identity)
99
+ return this.identity;
100
+ const file = this.readFromDisk();
101
+ if (!file)
102
+ return null;
103
+ let privateKey;
104
+ if (file.privateKeyEncryption === 'none') {
105
+ privateKey = Buffer.from(file.privateKey, 'base64');
106
+ }
107
+ else if (file.privateKeyEncryption === 'xchacha20-poly1305+argon2id') {
108
+ if (!options.passphrase && options.passphrase !== '') {
109
+ throw new Error('Passphrase required to decrypt identity');
110
+ }
111
+ if (!file.keySalt) {
112
+ throw new Error('Identity file missing keySalt for encrypted key');
113
+ }
114
+ const salt = Buffer.from(file.keySalt, 'base64');
115
+ privateKey = decryptPrivateKey(file.privateKey, options.passphrase, salt);
116
+ }
117
+ else {
118
+ throw new Error(`Unknown encryption method: ${file.privateKeyEncryption}`);
119
+ }
120
+ const identity = {
121
+ version: file.version,
122
+ publicKey: Buffer.from(file.publicKey, 'base64'),
123
+ privateKey,
124
+ x25519PublicKey: deriveX25519PublicKey(privateKey),
125
+ canonicalId: file.canonicalId,
126
+ displayFingerprint: file.displayFingerprint,
127
+ createdAt: file.createdAt,
128
+ recoveryCommitment: file.recoveryCommitment,
129
+ migrationComplete: file.migrationComplete,
130
+ };
131
+ this.identity = identity;
132
+ return identity;
133
+ }
134
+ /**
135
+ * Get the current identity (must have been created or loaded first).
136
+ */
137
+ get() {
138
+ return this.identity;
139
+ }
140
+ /**
141
+ * Check if an identity file exists on disk.
142
+ */
143
+ exists() {
144
+ return fs.existsSync(this.identityFile);
145
+ }
146
+ /**
147
+ * Get the identity file path.
148
+ */
149
+ get filePath() {
150
+ return this.identityFile;
151
+ }
152
+ /**
153
+ * Read the raw identity file (without decrypting).
154
+ * Useful for checking encryption status or migration state.
155
+ */
156
+ readRaw() {
157
+ return this.readFromDisk();
158
+ }
159
+ // ── Private ─────────────────────────────────────────────────────
160
+ readFromDisk() {
161
+ try {
162
+ if (!fs.existsSync(this.identityFile))
163
+ return null;
164
+ const raw = fs.readFileSync(this.identityFile, 'utf-8');
165
+ return JSON.parse(raw);
166
+ }
167
+ catch {
168
+ return null;
169
+ }
170
+ }
171
+ writeToDisk(file) {
172
+ fs.mkdirSync(path.dirname(this.identityFile), { recursive: true });
173
+ const data = JSON.stringify(file, null, 2);
174
+ const tmpPath = `${this.identityFile}.${process.pid}.tmp`;
175
+ fs.writeFileSync(tmpPath, data, { mode: 0o600 });
176
+ fs.renameSync(tmpPath, this.identityFile);
177
+ }
178
+ }
179
+ //# sourceMappingURL=IdentityManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdentityManager.js","sourceRoot":"","sources":["../../src/identity/IdentityManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * KeyEncryption — Encrypt/decrypt Ed25519 private keys at rest.
3
+ *
4
+ * Uses Argon2id for key derivation and XChaCha20-Poly1305 for encryption,
5
+ * matching the Threadline spec (Section 3.3.2):
6
+ *
7
+ * encrypted = XChaCha20-Poly1305(
8
+ * key = Argon2id(passphrase, salt, t=3, m=65536, p=4),
9
+ * nonce = 24 bytes CSPRNG,
10
+ * data = private_key
11
+ * )
12
+ *
13
+ * Storage format: base64(nonce || ciphertext || auth_tag)
14
+ */
15
+ /** Salt size for Argon2id */
16
+ export declare const SALT_BYTES = 32;
17
+ /**
18
+ * Encrypt an Ed25519 private key for at-rest storage.
19
+ *
20
+ * @param privateKey - Raw 32-byte Ed25519 private key
21
+ * @param passphrase - User passphrase or keychain-derived secret
22
+ * @param salt - 32-byte CSPRNG salt (unique per agent, stored alongside)
23
+ * @returns base64-encoded string: nonce || ciphertext || tag
24
+ */
25
+ export declare function encryptPrivateKey(privateKey: Buffer, passphrase: string, salt: Buffer): string;
26
+ /**
27
+ * Decrypt an Ed25519 private key from at-rest storage.
28
+ *
29
+ * @param encrypted - base64-encoded string from encryptPrivateKey
30
+ * @param passphrase - Same passphrase used for encryption
31
+ * @param salt - Same salt used for encryption
32
+ * @returns Raw 32-byte Ed25519 private key
33
+ * @throws Error if passphrase is wrong or data is corrupted
34
+ */
35
+ export declare function decryptPrivateKey(encrypted: string, passphrase: string, salt: Buffer): Buffer;
36
+ /**
37
+ * Generate a new random salt for Argon2id key derivation.
38
+ */
39
+ export declare function generateSalt(): Buffer;
40
+ //# sourceMappingURL=KeyEncryption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyEncryption.d.ts","sourceRoot":"","sources":["../../src/identity/KeyEncryption.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiBH,6BAA6B;AAC7B,eAAO,MAAM,UAAU,KAAK,CAAC;AAI7B;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,MAAM,CAcR;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,MAAM,CAsBR;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * KeyEncryption — Encrypt/decrypt Ed25519 private keys at rest.
3
+ *
4
+ * Uses Argon2id for key derivation and XChaCha20-Poly1305 for encryption,
5
+ * matching the Threadline spec (Section 3.3.2):
6
+ *
7
+ * encrypted = XChaCha20-Poly1305(
8
+ * key = Argon2id(passphrase, salt, t=3, m=65536, p=4),
9
+ * nonce = 24 bytes CSPRNG,
10
+ * data = private_key
11
+ * )
12
+ *
13
+ * Storage format: base64(nonce || ciphertext || auth_tag)
14
+ */
15
+ import crypto from 'node:crypto';
16
+ import { argon2id } from '@noble/hashes/argon2.js';
17
+ import { xchacha20poly1305 } from '@noble/ciphers/chacha.js';
18
+ // ── Constants ────────────────────────────────────────────────────────
19
+ /** Argon2id parameters (spec Section 3.3.2) */
20
+ const ARGON2_TIME_COST = 3;
21
+ const ARGON2_MEMORY_KB = 65536; // 64 MB
22
+ const ARGON2_PARALLELISM = 4;
23
+ const ARGON2_OUTPUT_LENGTH = 32; // 256-bit key for XChaCha20-Poly1305
24
+ /** XChaCha20-Poly1305 nonce size */
25
+ const NONCE_BYTES = 24;
26
+ /** Salt size for Argon2id */
27
+ export const SALT_BYTES = 32;
28
+ // ── Public API ───────────────────────────────────────────────────────
29
+ /**
30
+ * Encrypt an Ed25519 private key for at-rest storage.
31
+ *
32
+ * @param privateKey - Raw 32-byte Ed25519 private key
33
+ * @param passphrase - User passphrase or keychain-derived secret
34
+ * @param salt - 32-byte CSPRNG salt (unique per agent, stored alongside)
35
+ * @returns base64-encoded string: nonce || ciphertext || tag
36
+ */
37
+ export function encryptPrivateKey(privateKey, passphrase, salt) {
38
+ // Derive encryption key via Argon2id
39
+ const key = deriveKey(passphrase, salt);
40
+ // Generate random nonce
41
+ const nonce = crypto.randomBytes(NONCE_BYTES);
42
+ // Encrypt with XChaCha20-Poly1305
43
+ const cipher = xchacha20poly1305(key, nonce);
44
+ const ciphertext = cipher.encrypt(new Uint8Array(privateKey));
45
+ // Pack: nonce || ciphertext (includes auth tag)
46
+ const packed = Buffer.concat([nonce, Buffer.from(ciphertext)]);
47
+ return packed.toString('base64');
48
+ }
49
+ /**
50
+ * Decrypt an Ed25519 private key from at-rest storage.
51
+ *
52
+ * @param encrypted - base64-encoded string from encryptPrivateKey
53
+ * @param passphrase - Same passphrase used for encryption
54
+ * @param salt - Same salt used for encryption
55
+ * @returns Raw 32-byte Ed25519 private key
56
+ * @throws Error if passphrase is wrong or data is corrupted
57
+ */
58
+ export function decryptPrivateKey(encrypted, passphrase, salt) {
59
+ const packed = Buffer.from(encrypted, 'base64');
60
+ if (packed.length < NONCE_BYTES + 16) {
61
+ throw new Error('Encrypted key data too short');
62
+ }
63
+ // Unpack: nonce || ciphertext (includes auth tag)
64
+ const nonce = packed.subarray(0, NONCE_BYTES);
65
+ const ciphertext = packed.subarray(NONCE_BYTES);
66
+ // Derive the same key
67
+ const key = deriveKey(passphrase, salt);
68
+ // Decrypt
69
+ const cipher = xchacha20poly1305(key, nonce);
70
+ try {
71
+ const plaintext = cipher.decrypt(new Uint8Array(ciphertext));
72
+ return Buffer.from(plaintext);
73
+ }
74
+ catch {
75
+ throw new Error('Decryption failed — wrong passphrase or corrupted data');
76
+ }
77
+ }
78
+ /**
79
+ * Generate a new random salt for Argon2id key derivation.
80
+ */
81
+ export function generateSalt() {
82
+ return crypto.randomBytes(SALT_BYTES);
83
+ }
84
+ // ── Internal ─────────────────────────────────────────────────────────
85
+ function deriveKey(passphrase, salt) {
86
+ return argon2id(Buffer.from(passphrase, 'utf-8'), salt, {
87
+ t: ARGON2_TIME_COST,
88
+ m: ARGON2_MEMORY_KB,
89
+ p: ARGON2_PARALLELISM,
90
+ dkLen: ARGON2_OUTPUT_LENGTH,
91
+ });
92
+ }
93
+ //# sourceMappingURL=KeyEncryption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyEncryption.js","sourceRoot":"","sources":["../../src/identity/KeyEncryption.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * KeyRevocation — Emergency key revocation via recovery phrase.
3
+ *
4
+ * Spec Section 3.10:
5
+ * - 24-hour time-lock before revocation takes effect
6
+ * - Cancellation window: primary key holder can cancel during time-lock
7
+ * - Rate limiting: max 3 attempts per 24h per agent identity
8
+ * - Audit logging of all attempts (successful, cancelled, failed)
9
+ */
10
+ import { type RevocationRequest } from './types.js';
11
+ export interface RevocationAuditEntry {
12
+ timestamp: string;
13
+ action: 'initiate' | 'cancel' | 'activate' | 'reject-rate-limit' | 'reject-commitment';
14
+ targetCanonicalId: string;
15
+ newPublicKey?: string;
16
+ reason?: string;
17
+ }
18
+ export interface RevocationState {
19
+ pending: RevocationRequest | null;
20
+ attempts: {
21
+ timestamp: string;
22
+ }[];
23
+ auditLog: RevocationAuditEntry[];
24
+ }
25
+ export declare class RevocationManager {
26
+ private readonly stateFile;
27
+ private state;
28
+ constructor(stateDir: string);
29
+ /**
30
+ * Initiate an emergency revocation using the recovery keypair.
31
+ *
32
+ * The revocation enters a 24-hour time-lock pending state.
33
+ * During this window, the legitimate key holder can cancel it.
34
+ *
35
+ * @param recoveryPrivateKey - The recovery private key (from mnemonic)
36
+ * @param recoveryPublicKey - The recovery public key
37
+ * @param targetCanonicalId - The canonical ID of the agent being revoked
38
+ * @param newPublicKey - The replacement Ed25519 public key
39
+ * @param primaryPublicKey - The current primary public key (to verify recovery commitment)
40
+ * @param recoveryCommitment - The pre-committed recovery signature from identity.json
41
+ */
42
+ initiate(recoveryPrivateKey: Buffer, recoveryPublicKey: Buffer, targetCanonicalId: string, newPublicKey: Buffer, primaryPublicKey: Buffer, recoveryCommitment: string): RevocationRequest;
43
+ /**
44
+ * Cancel a pending revocation by proving possession of the primary key.
45
+ */
46
+ cancel(primaryPrivateKey: Buffer, primaryPublicKey: Buffer): boolean;
47
+ /**
48
+ * Check if a pending revocation should be activated (time-lock expired).
49
+ */
50
+ checkAndActivate(now?: Date): RevocationRequest | null;
51
+ /**
52
+ * Get the current pending revocation, if any.
53
+ */
54
+ getPending(): RevocationRequest | null;
55
+ /**
56
+ * Get the audit log.
57
+ */
58
+ getAuditLog(): RevocationAuditEntry[];
59
+ /**
60
+ * Get remaining attempts in the current rate-limit window.
61
+ */
62
+ getRemainingAttempts(): number;
63
+ private pruneOldAttempts;
64
+ private addAudit;
65
+ private loadState;
66
+ private saveState;
67
+ }
68
+ //# sourceMappingURL=KeyRevocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyRevocation.d.ts","sourceRoot":"","sources":["../../src/identity/KeyRevocation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAC;AASpB,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;IACvF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClC,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAID,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,KAAK,CAAkB;gBAEnB,QAAQ,EAAE,MAAM;IAK5B;;;;;;;;;;;;OAYG;IACH,QAAQ,CACN,kBAAkB,EAAE,MAAM,EAC1B,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,kBAAkB,EAAE,MAAM,GACzB,iBAAiB;IA4CpB;;OAEG;IACH,MAAM,CAAC,iBAAiB,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO;IAqBpE;;OAEG;IACH,gBAAgB,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,iBAAiB,GAAG,IAAI;IAkBtD;;OAEG;IACH,UAAU,IAAI,iBAAiB,GAAG,IAAI;IAItC;;OAEG;IACH,WAAW,IAAI,oBAAoB,EAAE;IAIrC;;OAEG;IACH,oBAAoB,IAAI,MAAM;IAO9B,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,SAAS;CAMlB"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * KeyRevocation — Emergency key revocation via recovery phrase.
3
+ *
4
+ * Spec Section 3.10:
5
+ * - 24-hour time-lock before revocation takes effect
6
+ * - Cancellation window: primary key holder can cancel during time-lock
7
+ * - Rate limiting: max 3 attempts per 24h per agent identity
8
+ * - Audit logging of all attempts (successful, cancelled, failed)
9
+ */
10
+ import crypto from 'node:crypto';
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import { sign, verify } from '../threadline/ThreadlineCrypto.js';
14
+ import { RECOVERY_TIMELOCK_MS, MAX_RECOVERY_ATTEMPTS, } from './types.js';
15
+ // ── Constants ────────────────────────────────────────────────────────
16
+ const REVOCATION_DOMAIN = 'instar-emergency-revoke-v1';
17
+ const RATE_LIMIT_WINDOW_MS = 24 * 60 * 60 * 1000;
18
+ // ── Manager ──────────────────────────────────────────────────────────
19
+ export class RevocationManager {
20
+ stateFile;
21
+ state;
22
+ constructor(stateDir) {
23
+ this.stateFile = path.join(stateDir, 'revocation-state.json');
24
+ this.state = this.loadState();
25
+ }
26
+ /**
27
+ * Initiate an emergency revocation using the recovery keypair.
28
+ *
29
+ * The revocation enters a 24-hour time-lock pending state.
30
+ * During this window, the legitimate key holder can cancel it.
31
+ *
32
+ * @param recoveryPrivateKey - The recovery private key (from mnemonic)
33
+ * @param recoveryPublicKey - The recovery public key
34
+ * @param targetCanonicalId - The canonical ID of the agent being revoked
35
+ * @param newPublicKey - The replacement Ed25519 public key
36
+ * @param primaryPublicKey - The current primary public key (to verify recovery commitment)
37
+ * @param recoveryCommitment - The pre-committed recovery signature from identity.json
38
+ */
39
+ initiate(recoveryPrivateKey, recoveryPublicKey, targetCanonicalId, newPublicKey, primaryPublicKey, recoveryCommitment) {
40
+ // Rate limit check
41
+ this.pruneOldAttempts();
42
+ if (this.state.attempts.length >= MAX_RECOVERY_ATTEMPTS) {
43
+ this.addAudit('reject-rate-limit', targetCanonicalId);
44
+ this.saveState();
45
+ throw new Error(`Rate limited: max ${MAX_RECOVERY_ATTEMPTS} recovery attempts per 24 hours`);
46
+ }
47
+ // Verify the recovery commitment matches
48
+ const commitmentMessage = Buffer.concat([
49
+ Buffer.from('instar-recovery-commitment-v1', 'utf-8'),
50
+ recoveryPublicKey,
51
+ ]);
52
+ const commitmentSig = Buffer.from(recoveryCommitment, 'base64');
53
+ if (!verify(primaryPublicKey, commitmentMessage, commitmentSig)) {
54
+ this.addAudit('reject-commitment', targetCanonicalId);
55
+ this.state.attempts.push({ timestamp: new Date().toISOString() });
56
+ this.saveState();
57
+ throw new Error('Recovery commitment verification failed — recovery key does not match');
58
+ }
59
+ // Create the revocation signature
60
+ const message = buildRevocationMessage(targetCanonicalId, newPublicKey);
61
+ const recoverySignature = sign(recoveryPrivateKey, message);
62
+ const now = new Date();
63
+ const request = {
64
+ targetCanonicalId,
65
+ newPublicKey: newPublicKey.toString('base64'),
66
+ recoverySignature: recoverySignature.toString('base64'),
67
+ timestamp: now.toISOString(),
68
+ status: 'pending',
69
+ expiresAt: new Date(now.getTime() + RECOVERY_TIMELOCK_MS).toISOString(),
70
+ };
71
+ this.state.pending = request;
72
+ this.state.attempts.push({ timestamp: now.toISOString() });
73
+ this.addAudit('initiate', targetCanonicalId, newPublicKey.toString('base64'));
74
+ this.saveState();
75
+ return request;
76
+ }
77
+ /**
78
+ * Cancel a pending revocation by proving possession of the primary key.
79
+ */
80
+ cancel(primaryPrivateKey, primaryPublicKey) {
81
+ if (!this.state.pending || this.state.pending.status !== 'pending') {
82
+ return false;
83
+ }
84
+ // Prove primary key possession: sign the pending request's timestamp
85
+ const cancelMessage = Buffer.concat([
86
+ Buffer.from('instar-revoke-cancel-v1', 'utf-8'),
87
+ Buffer.from(this.state.pending.timestamp, 'utf-8'),
88
+ ]);
89
+ const signature = sign(primaryPrivateKey, cancelMessage);
90
+ if (!verify(primaryPublicKey, cancelMessage, signature)) {
91
+ return false;
92
+ }
93
+ this.state.pending.status = 'cancelled';
94
+ this.addAudit('cancel', this.state.pending.targetCanonicalId);
95
+ this.saveState();
96
+ return true;
97
+ }
98
+ /**
99
+ * Check if a pending revocation should be activated (time-lock expired).
100
+ */
101
+ checkAndActivate(now) {
102
+ if (!this.state.pending || this.state.pending.status !== 'pending') {
103
+ return null;
104
+ }
105
+ const currentTime = (now ?? new Date()).getTime();
106
+ const expiresAt = new Date(this.state.pending.expiresAt).getTime();
107
+ if (currentTime >= expiresAt) {
108
+ this.state.pending.status = 'active';
109
+ this.addAudit('activate', this.state.pending.targetCanonicalId);
110
+ this.saveState();
111
+ return this.state.pending;
112
+ }
113
+ return null;
114
+ }
115
+ /**
116
+ * Get the current pending revocation, if any.
117
+ */
118
+ getPending() {
119
+ return this.state.pending?.status === 'pending' ? this.state.pending : null;
120
+ }
121
+ /**
122
+ * Get the audit log.
123
+ */
124
+ getAuditLog() {
125
+ return [...this.state.auditLog];
126
+ }
127
+ /**
128
+ * Get remaining attempts in the current rate-limit window.
129
+ */
130
+ getRemainingAttempts() {
131
+ this.pruneOldAttempts();
132
+ return Math.max(0, MAX_RECOVERY_ATTEMPTS - this.state.attempts.length);
133
+ }
134
+ // ── Private ─────────────────────────────────────────────────────
135
+ pruneOldAttempts() {
136
+ const cutoff = Date.now() - RATE_LIMIT_WINDOW_MS;
137
+ this.state.attempts = this.state.attempts.filter(a => new Date(a.timestamp).getTime() > cutoff);
138
+ }
139
+ addAudit(action, targetCanonicalId, newPublicKey) {
140
+ this.state.auditLog.push({
141
+ timestamp: new Date().toISOString(),
142
+ action,
143
+ targetCanonicalId,
144
+ ...(newPublicKey && { newPublicKey }),
145
+ });
146
+ }
147
+ loadState() {
148
+ try {
149
+ if (fs.existsSync(this.stateFile)) {
150
+ return JSON.parse(fs.readFileSync(this.stateFile, 'utf-8'));
151
+ }
152
+ }
153
+ catch { /* ignore */ }
154
+ return { pending: null, attempts: [], auditLog: [] };
155
+ }
156
+ saveState() {
157
+ fs.mkdirSync(path.dirname(this.stateFile), { recursive: true });
158
+ const tmpPath = `${this.stateFile}.${process.pid}.tmp`;
159
+ fs.writeFileSync(tmpPath, JSON.stringify(this.state, null, 2), { mode: 0o600 });
160
+ fs.renameSync(tmpPath, this.stateFile);
161
+ }
162
+ }
163
+ // ── Helpers ──────────────────────────────────────────────────────────
164
+ function buildRevocationMessage(targetCanonicalId, newPublicKey) {
165
+ const hash = crypto.createHash('sha256');
166
+ hash.update(Buffer.from(REVOCATION_DOMAIN, 'utf-8'));
167
+ hash.update(Buffer.from(targetCanonicalId, 'utf-8'));
168
+ hash.update(newPublicKey);
169
+ return hash.digest();
170
+ }
171
+ //# sourceMappingURL=KeyRevocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyRevocation.js","sourceRoot":"","sources":["../../src/identity/KeyRevocation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * KeyRotation — Ed25519 key rotation with dual-signed proofs.
3
+ *
4
+ * Spec Section 3.10:
5
+ * - Generate new keypair
6
+ * - Sign rotation proof with BOTH old and new keys
7
+ * - Broadcast to contacts and MoltBridge
8
+ * - Old key enters 72h grace period (can verify old sigs, can't create new grants)
9
+ * - After grace period, old key permanently revoked
10
+ */
11
+ import { type RotationProof } from './types.js';
12
+ /**
13
+ * Generate a new keypair and create a dual-signed rotation proof.
14
+ *
15
+ * Both the old and new private keys sign the same rotation payload,
16
+ * proving the holder controls both keys.
17
+ */
18
+ export declare function createRotation(oldPrivateKey: Buffer, oldPublicKey: Buffer, reason: string): {
19
+ newKeypair: {
20
+ publicKey: Buffer;
21
+ privateKey: Buffer;
22
+ };
23
+ proof: RotationProof;
24
+ };
25
+ /**
26
+ * Verify a rotation proof: both signatures must be valid.
27
+ */
28
+ export declare function verifyRotationProof(proof: RotationProof): boolean;
29
+ /**
30
+ * Check if a rotation is within the grace period.
31
+ *
32
+ * During grace: old key can verify old signatures but not create new grants.
33
+ * After grace: old key is permanently revoked.
34
+ */
35
+ export declare function isWithinGracePeriod(rotationTimestamp: string, now?: Date): boolean;
36
+ /**
37
+ * Compute the canonical ID for the new key after rotation.
38
+ */
39
+ export declare function computeRotatedCanonicalId(newPublicKey: Buffer): {
40
+ canonicalId: string;
41
+ displayFingerprint: string;
42
+ };
43
+ //# sourceMappingURL=KeyRotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyRotation.d.ts","sourceRoot":"","sources":["../../src/identity/KeyRotation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAwE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAQtH;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb;IAAE,UAAU,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAmBjF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAUjE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,iBAAiB,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,OAAO,CAIlF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAMA"}