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.
- package/README.md +1 -1
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +46 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +228 -36
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/slack-cli.d.ts +6 -0
- package/dist/commands/slack-cli.d.ts.map +1 -1
- package/dist/commands/slack-cli.js +99 -1
- package/dist/commands/slack-cli.js.map +1 -1
- package/dist/core/GitSync.d.ts.map +1 -1
- package/dist/core/GitSync.js +47 -5
- package/dist/core/GitSync.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +7 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +19 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +9 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/identity/IdentityManager.d.ts +70 -0
- package/dist/identity/IdentityManager.d.ts.map +1 -0
- package/dist/identity/IdentityManager.js +179 -0
- package/dist/identity/IdentityManager.js.map +1 -0
- package/dist/identity/KeyEncryption.d.ts +40 -0
- package/dist/identity/KeyEncryption.d.ts.map +1 -0
- package/dist/identity/KeyEncryption.js +93 -0
- package/dist/identity/KeyEncryption.js.map +1 -0
- package/dist/identity/KeyRevocation.d.ts +68 -0
- package/dist/identity/KeyRevocation.d.ts.map +1 -0
- package/dist/identity/KeyRevocation.js +171 -0
- package/dist/identity/KeyRevocation.js.map +1 -0
- package/dist/identity/KeyRotation.d.ts +43 -0
- package/dist/identity/KeyRotation.d.ts.map +1 -0
- package/dist/identity/KeyRotation.js +81 -0
- package/dist/identity/KeyRotation.js.map +1 -0
- package/dist/identity/Migration.d.ts +50 -0
- package/dist/identity/Migration.d.ts.map +1 -0
- package/dist/identity/Migration.js +125 -0
- package/dist/identity/Migration.js.map +1 -0
- package/dist/identity/RecoveryPhrase.d.ts +43 -0
- package/dist/identity/RecoveryPhrase.d.ts.map +1 -0
- package/dist/identity/RecoveryPhrase.js +93 -0
- package/dist/identity/RecoveryPhrase.js.map +1 -0
- package/dist/identity/index.d.ts +13 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +20 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/types.d.ts +101 -0
- package/dist/identity/types.d.ts.map +1 -0
- package/dist/identity/types.js +55 -0
- package/dist/identity/types.js.map +1 -0
- package/dist/knowledge/TreeTriage.d.ts.map +1 -1
- package/dist/knowledge/TreeTriage.js +9 -0
- package/dist/knowledge/TreeTriage.js.map +1 -1
- package/dist/memory/TopicMemory.d.ts +2 -0
- package/dist/memory/TopicMemory.d.ts.map +1 -1
- package/dist/memory/TopicMemory.js +55 -0
- package/dist/memory/TopicMemory.js.map +1 -1
- package/dist/messaging/slack/SlackAdapter.d.ts +15 -4
- package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
- package/dist/messaging/slack/SlackAdapter.js +131 -14
- package/dist/messaging/slack/SlackAdapter.js.map +1 -1
- package/dist/messaging/slack/SocketModeClient.d.ts +2 -0
- package/dist/messaging/slack/SocketModeClient.d.ts.map +1 -1
- package/dist/messaging/slack/SocketModeClient.js +31 -1
- package/dist/messaging/slack/SocketModeClient.js.map +1 -1
- package/dist/moltbridge/MoltBridgeClient.d.ts +124 -0
- package/dist/moltbridge/MoltBridgeClient.d.ts.map +1 -0
- package/dist/moltbridge/MoltBridgeClient.js +321 -0
- package/dist/moltbridge/MoltBridgeClient.js.map +1 -0
- package/dist/moltbridge/ProfileCompiler.d.ts +68 -0
- package/dist/moltbridge/ProfileCompiler.d.ts.map +1 -0
- package/dist/moltbridge/ProfileCompiler.js +317 -0
- package/dist/moltbridge/ProfileCompiler.js.map +1 -0
- package/dist/moltbridge/index.d.ts +12 -0
- package/dist/moltbridge/index.d.ts.map +1 -0
- package/dist/moltbridge/index.js +11 -0
- package/dist/moltbridge/index.js.map +1 -0
- package/dist/moltbridge/routes.d.ts +21 -0
- package/dist/moltbridge/routes.d.ts.map +1 -0
- package/dist/moltbridge/routes.js +224 -0
- package/dist/moltbridge/routes.js.map +1 -0
- package/dist/moltbridge/types.d.ts +93 -0
- package/dist/moltbridge/types.d.ts.map +1 -0
- package/dist/moltbridge/types.js +20 -0
- package/dist/moltbridge/types.js.map +1 -0
- package/dist/monitoring/PresenceProxy.d.ts +3 -0
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +80 -3
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/SessionMonitor.d.ts +7 -0
- package/dist/monitoring/SessionMonitor.d.ts.map +1 -1
- package/dist/monitoring/SessionMonitor.js +47 -22
- package/dist/monitoring/SessionMonitor.js.map +1 -1
- package/dist/monitoring/TriageOrchestrator.d.ts.map +1 -1
- package/dist/monitoring/TriageOrchestrator.js +25 -1
- package/dist/monitoring/TriageOrchestrator.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +29 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +21 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/AuthorizationPolicy.d.ts +110 -0
- package/dist/threadline/AuthorizationPolicy.d.ts.map +1 -0
- package/dist/threadline/AuthorizationPolicy.js +226 -0
- package/dist/threadline/AuthorizationPolicy.js.map +1 -0
- package/dist/threadline/DiscoveryWaterfall.d.ts +73 -0
- package/dist/threadline/DiscoveryWaterfall.d.ts.map +1 -0
- package/dist/threadline/DiscoveryWaterfall.js +113 -0
- package/dist/threadline/DiscoveryWaterfall.js.map +1 -0
- package/dist/threadline/ListenerSessionManager.d.ts.map +1 -1
- package/dist/threadline/ListenerSessionManager.js +2 -0
- package/dist/threadline/ListenerSessionManager.js.map +1 -1
- package/dist/threadline/MessageSecurity.d.ts +49 -0
- package/dist/threadline/MessageSecurity.d.ts.map +1 -0
- package/dist/threadline/MessageSecurity.js +93 -0
- package/dist/threadline/MessageSecurity.js.map +1 -0
- package/dist/threadline/SecureInvitation.d.ts +79 -0
- package/dist/threadline/SecureInvitation.d.ts.map +1 -0
- package/dist/threadline/SecureInvitation.js +164 -0
- package/dist/threadline/SecureInvitation.js.map +1 -0
- package/dist/threadline/TrustAuditLog.d.ts +49 -0
- package/dist/threadline/TrustAuditLog.d.ts.map +1 -0
- package/dist/threadline/TrustAuditLog.js +111 -0
- package/dist/threadline/TrustAuditLog.js.map +1 -0
- package/dist/threadline/TrustEvaluator.d.ts +69 -0
- package/dist/threadline/TrustEvaluator.d.ts.map +1 -0
- package/dist/threadline/TrustEvaluator.js +93 -0
- package/dist/threadline/TrustEvaluator.js.map +1 -0
- package/dist/threadline/UnifiedTrustWiring.d.ts +66 -0
- package/dist/threadline/UnifiedTrustWiring.d.ts.map +1 -0
- package/dist/threadline/UnifiedTrustWiring.js +192 -0
- package/dist/threadline/UnifiedTrustWiring.js.map +1 -0
- package/dist/threadline/client/IdentityManager.d.ts +27 -6
- package/dist/threadline/client/IdentityManager.d.ts.map +1 -1
- package/dist/threadline/client/IdentityManager.js +64 -18
- package/dist/threadline/client/IdentityManager.js.map +1 -1
- package/dist/threadline/relay/SybilProtection.d.ts +78 -0
- package/dist/threadline/relay/SybilProtection.d.ts.map +1 -0
- package/dist/threadline/relay/SybilProtection.js +187 -0
- package/dist/threadline/relay/SybilProtection.js.map +1 -0
- package/package.json +9 -3
- package/playbook-scripts/build-state.py +529 -0
- package/scripts/check-contract-evidence.js +103 -0
- package/scripts/pre-push-gate.js +53 -0
- package/scripts/run-contract-tests.js +75 -0
- package/src/data/builtin-manifest.json +87 -63
- package/src/templates/hooks/build-stop-hook.sh +79 -0
- package/upgrades/0.26.10.md +36 -0
- package/upgrades/0.27.1.md +101 -0
- package/upgrades/0.27.2.md +36 -0
- package/upgrades/NEXT.md +0 -27
- /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"}
|