@zcloak/ai-agent 1.0.0
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 +5 -0
- package/dist/bind.d.ts +22 -0
- package/dist/bind.js +145 -0
- package/dist/bind.js.map +1 -0
- package/dist/cli.d.ts +31 -0
- package/dist/cli.js +126 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +34 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto.d.ts +113 -0
- package/dist/crypto.js +252 -0
- package/dist/crypto.js.map +1 -0
- package/dist/daemon.d.ts +94 -0
- package/dist/daemon.js +271 -0
- package/dist/daemon.js.map +1 -0
- package/dist/delete.d.ts +22 -0
- package/dist/delete.js +231 -0
- package/dist/delete.js.map +1 -0
- package/dist/doc.d.ts +23 -0
- package/dist/doc.js +180 -0
- package/dist/doc.js.map +1 -0
- package/dist/error.d.ts +45 -0
- package/dist/error.js +79 -0
- package/dist/error.js.map +1 -0
- package/dist/feed.d.ts +20 -0
- package/dist/feed.js +83 -0
- package/dist/feed.js.map +1 -0
- package/dist/identity.d.ts +50 -0
- package/dist/identity.js +99 -0
- package/dist/identity.js.map +1 -0
- package/dist/identity_cmd.d.ts +23 -0
- package/dist/identity_cmd.js +136 -0
- package/dist/identity_cmd.js.map +1 -0
- package/dist/idl.d.ts +99 -0
- package/dist/idl.js +213 -0
- package/dist/idl.js.map +1 -0
- package/dist/key-store.d.ts +88 -0
- package/dist/key-store.js +171 -0
- package/dist/key-store.js.map +1 -0
- package/dist/pow.d.ts +24 -0
- package/dist/pow.js +86 -0
- package/dist/pow.js.map +1 -0
- package/dist/register.d.ts +24 -0
- package/dist/register.js +191 -0
- package/dist/register.js.map +1 -0
- package/dist/rpc.d.ts +107 -0
- package/dist/rpc.js +60 -0
- package/dist/rpc.js.map +1 -0
- package/dist/serve.d.ts +55 -0
- package/dist/serve.js +455 -0
- package/dist/serve.js.map +1 -0
- package/dist/session.d.ts +104 -0
- package/dist/session.js +189 -0
- package/dist/session.js.map +1 -0
- package/dist/sign.d.ts +33 -0
- package/dist/sign.js +355 -0
- package/dist/sign.js.map +1 -0
- package/dist/types/common.d.ts +63 -0
- package/dist/types/common.js +8 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/config.d.ts +28 -0
- package/dist/types/config.js +8 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/registry.d.ts +72 -0
- package/dist/types/registry.js +13 -0
- package/dist/types/registry.js.map +1 -0
- package/dist/types/sign-event.d.ts +134 -0
- package/dist/types/sign-event.js +13 -0
- package/dist/types/sign-event.js.map +1 -0
- package/dist/utils.d.ts +113 -0
- package/dist/utils.js +382 -0
- package/dist/utils.js.map +1 -0
- package/dist/verify.d.ts +23 -0
- package/dist/verify.js +207 -0
- package/dist/verify.js.map +1 -0
- package/dist/vetkey.d.ts +27 -0
- package/dist/vetkey.js +507 -0
- package/dist/vetkey.js.map +1 -0
- package/package.json +55 -0
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cryptographic Primitives for VetKey Operations
|
|
4
|
+
*
|
|
5
|
+
* Two categories of operations:
|
|
6
|
+
*
|
|
7
|
+
* 1. IBE (Identity-Based Encryption) — Uses @dfinity/vetkeys for BLS12-381 operations.
|
|
8
|
+
* Used for per-operation Kind5 PrivatePost encryption.
|
|
9
|
+
*
|
|
10
|
+
* 2. AES-256-GCM — Uses Node.js built-in crypto module.
|
|
11
|
+
* Used for daemon mode fast file encryption/decryption.
|
|
12
|
+
* VKDA binary format: [magic "VKDA":4B][version:1B][nonce:12B][ciphertext+GCM tag]
|
|
13
|
+
*
|
|
14
|
+
* All formats are byte-level compatible with the Rust vetkey-tool implementation.
|
|
15
|
+
*/
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.generateTransportKeypair = generateTransportKeypair;
|
|
21
|
+
exports.ibeEncrypt = ibeEncrypt;
|
|
22
|
+
exports.ibeDecrypt = ibeDecrypt;
|
|
23
|
+
exports.decryptVetkey = decryptVetkey;
|
|
24
|
+
exports.makeIbeIdentity = makeIbeIdentity;
|
|
25
|
+
exports.vetkeyToAes256 = vetkeyToAes256;
|
|
26
|
+
exports.aes256Encrypt = aes256Encrypt;
|
|
27
|
+
exports.aes256Decrypt = aes256Decrypt;
|
|
28
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
29
|
+
const vetkeys_1 = require("@dfinity/vetkeys");
|
|
30
|
+
const error_1 = require("./error");
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Constants
|
|
33
|
+
// ============================================================================
|
|
34
|
+
/** VKDA magic header bytes ("VKDA" in ASCII) */
|
|
35
|
+
const AES_DAEMON_MAGIC = Buffer.from([0x56, 0x4b, 0x44, 0x41]);
|
|
36
|
+
/** VKDA format version */
|
|
37
|
+
const AES_DAEMON_VERSION = 0x01;
|
|
38
|
+
/** AES-256-GCM nonce size in bytes */
|
|
39
|
+
const AES_GCM_NONCE_BYTES = 12;
|
|
40
|
+
/** AES-256-GCM authentication tag size in bytes */
|
|
41
|
+
const AES_GCM_TAG_BYTES = 16;
|
|
42
|
+
/** VKDA header overhead: magic(4) + version(1) + nonce(12) + tag(16) = 33 bytes */
|
|
43
|
+
const VKDA_OVERHEAD = 4 + 1 + AES_GCM_NONCE_BYTES + AES_GCM_TAG_BYTES;
|
|
44
|
+
/** HKDF domain separator for VetKey → AES-256 key derivation (must match Rust) */
|
|
45
|
+
const VETKEY_AES256_DOMAIN = "vetkey-aes256-file-encryption";
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// IBE Operations (via @dfinity/vetkeys)
|
|
48
|
+
// ============================================================================
|
|
49
|
+
/**
|
|
50
|
+
* Generate an ephemeral transport key pair for secure VetKey delivery.
|
|
51
|
+
*
|
|
52
|
+
* The transport secret key is used to decrypt the EncryptedVetKey received
|
|
53
|
+
* from the canister. The public key is sent to the canister so it can
|
|
54
|
+
* encrypt the VetKey for this specific requester.
|
|
55
|
+
*
|
|
56
|
+
* @returns [transportSecretKey, transportPublicKeyBytes (48 bytes, compressed G1)]
|
|
57
|
+
*/
|
|
58
|
+
function generateTransportKeypair() {
|
|
59
|
+
const tsk = vetkeys_1.TransportSecretKey.random();
|
|
60
|
+
const publicKeyBytes = tsk.publicKeyBytes();
|
|
61
|
+
return [tsk, publicKeyBytes];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* IBE-encrypt plaintext using the derived public key and identity string.
|
|
65
|
+
*
|
|
66
|
+
* Uses the Fujisaki-Okamoto transform internally (handled by @dfinity/vetkeys).
|
|
67
|
+
* Output format: [header:8B][C1:96B][C2:32B][C3:plaintext_len+16B] (152 bytes overhead)
|
|
68
|
+
*
|
|
69
|
+
* @param dpkBytes - IBE derived public key (96 bytes, compressed G2 point)
|
|
70
|
+
* @param ibeIdentity - IBE identity string (e.g. "{principal}:{hash}:{timestamp}")
|
|
71
|
+
* @param plaintext - Data to encrypt
|
|
72
|
+
* @returns IBE ciphertext bytes
|
|
73
|
+
*/
|
|
74
|
+
function ibeEncrypt(dpkBytes, ibeIdentity, plaintext) {
|
|
75
|
+
try {
|
|
76
|
+
const dpk = vetkeys_1.DerivedPublicKey.deserialize(dpkBytes);
|
|
77
|
+
const identity = vetkeys_1.IbeIdentity.fromString(ibeIdentity);
|
|
78
|
+
const seed = vetkeys_1.IbeSeed.random();
|
|
79
|
+
const ciphertext = vetkeys_1.IbeCiphertext.encrypt(dpk, identity, plaintext, seed);
|
|
80
|
+
return ciphertext.serialize();
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
throw (0, error_1.encryptionError)(`IBE encrypt failed: ${e instanceof Error ? e.message : String(e)}`, e);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Full IBE decrypt: transport-decrypt VetKey, then IBE-decrypt ciphertext.
|
|
88
|
+
*
|
|
89
|
+
* Complete flow:
|
|
90
|
+
* 1. Deserialize EncryptedVetKey (192 bytes)
|
|
91
|
+
* 2. Transport-decrypt and verify BLS signature → VetKey
|
|
92
|
+
* 3. Deserialize IBE ciphertext
|
|
93
|
+
* 4. IBE-decrypt using VetKey → plaintext
|
|
94
|
+
*
|
|
95
|
+
* @param encryptedKeyBytes - Transport-encrypted VetKey (192 bytes)
|
|
96
|
+
* @param dpkBytes - IBE derived public key (96 bytes)
|
|
97
|
+
* @param ibeIdentity - IBE identity string
|
|
98
|
+
* @param ciphertextBytes - IBE ciphertext
|
|
99
|
+
* @param transportSecret - Transport secret key (for decrypting the VetKey)
|
|
100
|
+
* @returns Decrypted plaintext
|
|
101
|
+
*/
|
|
102
|
+
function ibeDecrypt(encryptedKeyBytes, dpkBytes, ibeIdentity, ciphertextBytes, transportSecret) {
|
|
103
|
+
try {
|
|
104
|
+
// Step 1-2: Transport-decrypt the VetKey
|
|
105
|
+
const encryptedVetKey = vetkeys_1.EncryptedVetKey.deserialize(encryptedKeyBytes);
|
|
106
|
+
const dpk = vetkeys_1.DerivedPublicKey.deserialize(dpkBytes);
|
|
107
|
+
// decryptAndVerify expects raw bytes for the input (derivation ID / IBE identity)
|
|
108
|
+
const identityBytes = new TextEncoder().encode(ibeIdentity);
|
|
109
|
+
const vetKey = encryptedVetKey.decryptAndVerify(transportSecret, dpk, identityBytes);
|
|
110
|
+
// Step 3-4: IBE-decrypt the ciphertext
|
|
111
|
+
const ibeCiphertext = vetkeys_1.IbeCiphertext.deserialize(ciphertextBytes);
|
|
112
|
+
return ibeCiphertext.decrypt(vetKey);
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
throw (0, error_1.decryptionError)(`IBE decrypt failed: ${e instanceof Error ? e.message : String(e)}`, e);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Transport-decrypt an EncryptedVetKey and return raw VetKey bytes.
|
|
120
|
+
*
|
|
121
|
+
* Used by daemon mode to obtain the VetKey for AES-256 key derivation.
|
|
122
|
+
* The derivation ID serves as the IBE identity in this context.
|
|
123
|
+
*
|
|
124
|
+
* @param encryptedKeyBytes - Transport-encrypted VetKey (192 bytes)
|
|
125
|
+
* @param dpkBytes - IBE derived public key (96 bytes)
|
|
126
|
+
* @param derivationId - Derivation ID string (used as IBE identity)
|
|
127
|
+
* @param transportSecret - Transport secret key
|
|
128
|
+
* @returns Raw VetKey bytes (48 bytes, compressed G1 point)
|
|
129
|
+
*/
|
|
130
|
+
function decryptVetkey(encryptedKeyBytes, dpkBytes, derivationId, transportSecret) {
|
|
131
|
+
try {
|
|
132
|
+
const encryptedVetKey = vetkeys_1.EncryptedVetKey.deserialize(encryptedKeyBytes);
|
|
133
|
+
const dpk = vetkeys_1.DerivedPublicKey.deserialize(dpkBytes);
|
|
134
|
+
// decryptAndVerify expects raw bytes for the input (derivation ID)
|
|
135
|
+
const derivationIdBytes = new TextEncoder().encode(derivationId);
|
|
136
|
+
const vetKey = encryptedVetKey.decryptAndVerify(transportSecret, dpk, derivationIdBytes);
|
|
137
|
+
return vetKey.signatureBytes();
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
throw (0, error_1.decryptionError)(`VetKey transport decryption failed: ${e instanceof Error ? e.message : String(e)}`, e);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// IBE Identity Generation
|
|
145
|
+
// ============================================================================
|
|
146
|
+
/**
|
|
147
|
+
* Generate an IBE identity string for Kind5 PrivatePost.
|
|
148
|
+
*
|
|
149
|
+
* Format: "{principal}:{short_hash_16_hex}:{timestamp_ms}"
|
|
150
|
+
* - short_hash: first 16 hex chars of SHA-256(content)
|
|
151
|
+
* - timestamp_ms: current time in milliseconds
|
|
152
|
+
*
|
|
153
|
+
* Must match the Rust implementation exactly for cross-compatibility.
|
|
154
|
+
*
|
|
155
|
+
* @param principal - ICP principal text
|
|
156
|
+
* @param content - Content bytes to hash
|
|
157
|
+
* @returns IBE identity string
|
|
158
|
+
*/
|
|
159
|
+
function makeIbeIdentity(principal, content) {
|
|
160
|
+
const hash = crypto_1.default.createHash("sha256").update(content).digest("hex");
|
|
161
|
+
const shortHash = hash.slice(0, 16); // First 16 hex chars
|
|
162
|
+
const timestamp = Date.now();
|
|
163
|
+
return `${principal}:${shortHash}:${timestamp}`;
|
|
164
|
+
}
|
|
165
|
+
// ============================================================================
|
|
166
|
+
// AES-256-GCM Operations (Daemon Mode)
|
|
167
|
+
// ============================================================================
|
|
168
|
+
/**
|
|
169
|
+
* Derive an AES-256 key from VetKey bytes using HKDF-SHA256.
|
|
170
|
+
*
|
|
171
|
+
* Domain separator: "vetkey-aes256-file-encryption" (must match Rust implementation)
|
|
172
|
+
*
|
|
173
|
+
* @param vetkeyBytes - Raw VetKey bytes (48 bytes, compressed G1 point)
|
|
174
|
+
* @returns AES-256 key (32 bytes)
|
|
175
|
+
*/
|
|
176
|
+
function vetkeyToAes256(vetkeyBytes) {
|
|
177
|
+
if (vetkeyBytes.length !== 48) {
|
|
178
|
+
throw (0, error_1.encryptionError)(`Invalid VetKey length: expected 48, got ${vetkeyBytes.length}`);
|
|
179
|
+
}
|
|
180
|
+
// HKDF-SHA256 with VetKey as IKM and domain separator as info
|
|
181
|
+
// Using empty salt (matches Rust hkdf::Hkdf::new(None, ikm))
|
|
182
|
+
return Buffer.from(crypto_1.default.hkdfSync("sha256", vetkeyBytes, Buffer.alloc(0), VETKEY_AES256_DOMAIN, 32));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Encrypt plaintext using AES-256-GCM in VKDA format.
|
|
186
|
+
*
|
|
187
|
+
* Output format: [magic "VKDA":4B][version 0x01:1B][nonce:12B][ciphertext+GCM tag]
|
|
188
|
+
* This format is byte-level compatible with the Rust vetkey-tool implementation.
|
|
189
|
+
*
|
|
190
|
+
* @param key - AES-256 key (32 bytes)
|
|
191
|
+
* @param plaintext - Data to encrypt
|
|
192
|
+
* @returns VKDA-formatted ciphertext
|
|
193
|
+
*/
|
|
194
|
+
function aes256Encrypt(key, plaintext) {
|
|
195
|
+
// Generate random 12-byte nonce
|
|
196
|
+
const nonce = crypto_1.default.randomBytes(AES_GCM_NONCE_BYTES);
|
|
197
|
+
// Encrypt with AES-256-GCM
|
|
198
|
+
const cipher = crypto_1.default.createCipheriv("aes-256-gcm", key, nonce);
|
|
199
|
+
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
200
|
+
const tag = cipher.getAuthTag(); // 16 bytes
|
|
201
|
+
// Assemble VKDA format: magic + version + nonce + ciphertext + tag
|
|
202
|
+
return Buffer.concat([
|
|
203
|
+
AES_DAEMON_MAGIC,
|
|
204
|
+
Buffer.from([AES_DAEMON_VERSION]),
|
|
205
|
+
nonce,
|
|
206
|
+
encrypted,
|
|
207
|
+
tag,
|
|
208
|
+
]);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Decrypt VKDA-formatted ciphertext using AES-256-GCM.
|
|
212
|
+
*
|
|
213
|
+
* Validates the VKDA magic header and version, then performs
|
|
214
|
+
* authenticated GCM decryption.
|
|
215
|
+
*
|
|
216
|
+
* @param key - AES-256 key (32 bytes)
|
|
217
|
+
* @param data - VKDA-formatted ciphertext
|
|
218
|
+
* @returns Decrypted plaintext
|
|
219
|
+
*/
|
|
220
|
+
function aes256Decrypt(key, data) {
|
|
221
|
+
// Validate minimum size
|
|
222
|
+
if (data.length < VKDA_OVERHEAD) {
|
|
223
|
+
throw (0, error_1.decryptionError)(`Data too short: ${data.length} bytes (minimum ${VKDA_OVERHEAD} bytes for VKDA format)`);
|
|
224
|
+
}
|
|
225
|
+
// Validate magic header
|
|
226
|
+
if (data[0] !== 0x56 || // 'V'
|
|
227
|
+
data[1] !== 0x4b || // 'K'
|
|
228
|
+
data[2] !== 0x44 || // 'D'
|
|
229
|
+
data[3] !== 0x41 // 'A'
|
|
230
|
+
) {
|
|
231
|
+
throw (0, error_1.decryptionError)("Invalid VKDA magic header (expected 'VKDA')");
|
|
232
|
+
}
|
|
233
|
+
// Validate version
|
|
234
|
+
if (data[4] !== AES_DAEMON_VERSION) {
|
|
235
|
+
throw (0, error_1.decryptionError)(`Unsupported VKDA version: ${data[4]} (expected ${AES_DAEMON_VERSION})`);
|
|
236
|
+
}
|
|
237
|
+
// Extract components
|
|
238
|
+
const nonce = data.subarray(5, 5 + AES_GCM_NONCE_BYTES); // bytes 5-16
|
|
239
|
+
const ciphertextWithTag = data.subarray(5 + AES_GCM_NONCE_BYTES); // bytes 17+
|
|
240
|
+
const ciphertext = ciphertextWithTag.subarray(0, ciphertextWithTag.length - AES_GCM_TAG_BYTES);
|
|
241
|
+
const tag = ciphertextWithTag.subarray(ciphertextWithTag.length - AES_GCM_TAG_BYTES);
|
|
242
|
+
// Decrypt with AES-256-GCM
|
|
243
|
+
try {
|
|
244
|
+
const decipher = crypto_1.default.createDecipheriv("aes-256-gcm", key, nonce);
|
|
245
|
+
decipher.setAuthTag(tag);
|
|
246
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
throw (0, error_1.decryptionError)(`AES-256-GCM decryption failed (authentication tag mismatch or corrupted data): ${e instanceof Error ? e.message : String(e)}`, e);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon Lifecycle Management — PID file, socket path, runtime directory
|
|
3
|
+
*
|
|
4
|
+
* Manages the daemon's file system footprint:
|
|
5
|
+
* - Runtime directory: ~/.vetkey-tool/ (created with 0o700 permissions)
|
|
6
|
+
* - PID file: ~/.vetkey-tool/{sanitized_id}.pid
|
|
7
|
+
* - Socket file: ~/.vetkey-tool/{sanitized_id}.sock
|
|
8
|
+
*
|
|
9
|
+
* Prevents duplicate daemon instances by checking PID files and verifying
|
|
10
|
+
* whether the process is still alive. Stale PID/socket files from crashed
|
|
11
|
+
* daemons are automatically cleaned up.
|
|
12
|
+
*
|
|
13
|
+
* The DaemonRuntime class implements cleanup-on-drop semantics via
|
|
14
|
+
* process exit handlers, ensuring PID and socket files are removed
|
|
15
|
+
* even on unexpected termination (SIGTERM, SIGINT, uncaught exceptions).
|
|
16
|
+
*/
|
|
17
|
+
/** Get the runtime directory path (~/.vetkey-tool/) */
|
|
18
|
+
export declare function runtimeDir(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Sanitize a derivation ID for use in file names.
|
|
21
|
+
*
|
|
22
|
+
* Replaces special characters (:, /, \) with underscores.
|
|
23
|
+
* If the resulting path would exceed the Unix socket path limit,
|
|
24
|
+
* falls back to a SHA-256 hash prefix (16 hex characters) to keep
|
|
25
|
+
* the path short enough.
|
|
26
|
+
*
|
|
27
|
+
* @param derivationId - Raw derivation ID (e.g. "abc-def:default")
|
|
28
|
+
* @returns Safe file name prefix
|
|
29
|
+
*/
|
|
30
|
+
export declare function sanitizeDerivationId(derivationId: string): string;
|
|
31
|
+
/** Get the socket file path for a derivation ID */
|
|
32
|
+
export declare function socketPath(derivationId: string): string;
|
|
33
|
+
/** Get the PID file path for a derivation ID */
|
|
34
|
+
export declare function pidPath(derivationId: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Manages the lifecycle of a daemon instance.
|
|
37
|
+
*
|
|
38
|
+
* On creation:
|
|
39
|
+
* - Creates the runtime directory if needed
|
|
40
|
+
* - Checks for existing running instances (PID file + process alive check)
|
|
41
|
+
* - Cleans up stale PID/socket files from crashed daemons
|
|
42
|
+
* - Writes the current PID to the PID file
|
|
43
|
+
*
|
|
44
|
+
* On cleanup (via destroy() or process exit handlers):
|
|
45
|
+
* - Removes the PID file
|
|
46
|
+
* - Removes the socket file
|
|
47
|
+
*/
|
|
48
|
+
export declare class DaemonRuntime {
|
|
49
|
+
private _socketPath;
|
|
50
|
+
private _pidPath;
|
|
51
|
+
private _derivationId;
|
|
52
|
+
private cleanedUp;
|
|
53
|
+
/** Bound cleanup handler for process exit events */
|
|
54
|
+
private exitHandler;
|
|
55
|
+
private constructor();
|
|
56
|
+
/**
|
|
57
|
+
* Create a new DaemonRuntime, performing all startup checks.
|
|
58
|
+
*
|
|
59
|
+
* @param derivationId - Derivation ID for this daemon instance
|
|
60
|
+
* @returns Initialized DaemonRuntime
|
|
61
|
+
* @throws ToolError with code DAEMON if another instance is already running
|
|
62
|
+
*/
|
|
63
|
+
static create(derivationId: string): DaemonRuntime;
|
|
64
|
+
/** Socket file path */
|
|
65
|
+
get socketFilePath(): string;
|
|
66
|
+
/** PID file path */
|
|
67
|
+
get pidFilePath(): string;
|
|
68
|
+
/** Derivation ID */
|
|
69
|
+
get derivationId(): string;
|
|
70
|
+
/**
|
|
71
|
+
* Clean up PID and socket files.
|
|
72
|
+
*
|
|
73
|
+
* Safe to call multiple times — subsequent calls are no-ops.
|
|
74
|
+
* Called automatically on process exit.
|
|
75
|
+
*/
|
|
76
|
+
cleanup(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Explicitly destroy the runtime (alias for cleanup).
|
|
79
|
+
* Removes PID and socket files, unregisters handlers.
|
|
80
|
+
*/
|
|
81
|
+
destroy(): void;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Find the socket path for a running daemon (used by stop/status commands).
|
|
85
|
+
*
|
|
86
|
+
* Checks if the socket file exists and the daemon is actually running
|
|
87
|
+
* (via PID file check).
|
|
88
|
+
*
|
|
89
|
+
* @param derivationId - Derivation ID to look up
|
|
90
|
+
* @returns Socket path if daemon is running
|
|
91
|
+
* @throws ToolError if no running daemon is found
|
|
92
|
+
*/
|
|
93
|
+
export declare function findRunningDaemon(derivationId: string): string;
|
|
94
|
+
//# sourceMappingURL=daemon.d.ts.map
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Daemon Lifecycle Management — PID file, socket path, runtime directory
|
|
4
|
+
*
|
|
5
|
+
* Manages the daemon's file system footprint:
|
|
6
|
+
* - Runtime directory: ~/.vetkey-tool/ (created with 0o700 permissions)
|
|
7
|
+
* - PID file: ~/.vetkey-tool/{sanitized_id}.pid
|
|
8
|
+
* - Socket file: ~/.vetkey-tool/{sanitized_id}.sock
|
|
9
|
+
*
|
|
10
|
+
* Prevents duplicate daemon instances by checking PID files and verifying
|
|
11
|
+
* whether the process is still alive. Stale PID/socket files from crashed
|
|
12
|
+
* daemons are automatically cleaned up.
|
|
13
|
+
*
|
|
14
|
+
* The DaemonRuntime class implements cleanup-on-drop semantics via
|
|
15
|
+
* process exit handlers, ensuring PID and socket files are removed
|
|
16
|
+
* even on unexpected termination (SIGTERM, SIGINT, uncaught exceptions).
|
|
17
|
+
*/
|
|
18
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
19
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.DaemonRuntime = void 0;
|
|
23
|
+
exports.runtimeDir = runtimeDir;
|
|
24
|
+
exports.sanitizeDerivationId = sanitizeDerivationId;
|
|
25
|
+
exports.socketPath = socketPath;
|
|
26
|
+
exports.pidPath = pidPath;
|
|
27
|
+
exports.findRunningDaemon = findRunningDaemon;
|
|
28
|
+
const fs_1 = require("fs");
|
|
29
|
+
const path_1 = require("path");
|
|
30
|
+
const os_1 = require("os");
|
|
31
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
32
|
+
const error_1 = require("./error");
|
|
33
|
+
/** Runtime directory name under home directory */
|
|
34
|
+
const RUNTIME_DIR_NAME = ".vetkey-tool";
|
|
35
|
+
/**
|
|
36
|
+
* Maximum socket path length.
|
|
37
|
+
* macOS limits Unix socket paths to 104 bytes, Linux to 108 bytes.
|
|
38
|
+
* We use 100 as a safe threshold.
|
|
39
|
+
*/
|
|
40
|
+
const MAX_SOCKET_PATH_LEN = 100;
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Path Utilities
|
|
43
|
+
// ============================================================================
|
|
44
|
+
/** Get the runtime directory path (~/.vetkey-tool/) */
|
|
45
|
+
function runtimeDir() {
|
|
46
|
+
return (0, path_1.join)((0, os_1.homedir)(), RUNTIME_DIR_NAME);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Sanitize a derivation ID for use in file names.
|
|
50
|
+
*
|
|
51
|
+
* Replaces special characters (:, /, \) with underscores.
|
|
52
|
+
* If the resulting path would exceed the Unix socket path limit,
|
|
53
|
+
* falls back to a SHA-256 hash prefix (16 hex characters) to keep
|
|
54
|
+
* the path short enough.
|
|
55
|
+
*
|
|
56
|
+
* @param derivationId - Raw derivation ID (e.g. "abc-def:default")
|
|
57
|
+
* @returns Safe file name prefix
|
|
58
|
+
*/
|
|
59
|
+
function sanitizeDerivationId(derivationId) {
|
|
60
|
+
const sanitized = derivationId
|
|
61
|
+
.replace(/:/g, "_")
|
|
62
|
+
.replace(/\//g, "_")
|
|
63
|
+
.replace(/\\/g, "_");
|
|
64
|
+
// Check if the full socket path would be too long
|
|
65
|
+
const dir = runtimeDir();
|
|
66
|
+
const fullPath = (0, path_1.join)(dir, `${sanitized}.sock`);
|
|
67
|
+
if (fullPath.length > MAX_SOCKET_PATH_LEN) {
|
|
68
|
+
// Use SHA-256 hash prefix for long derivation IDs
|
|
69
|
+
const hash = crypto_1.default.createHash("sha256").update(derivationId).digest("hex");
|
|
70
|
+
return `vk_${hash.slice(0, 16)}`;
|
|
71
|
+
}
|
|
72
|
+
return sanitized;
|
|
73
|
+
}
|
|
74
|
+
/** Get the socket file path for a derivation ID */
|
|
75
|
+
function socketPath(derivationId) {
|
|
76
|
+
const name = sanitizeDerivationId(derivationId);
|
|
77
|
+
return (0, path_1.join)(runtimeDir(), `${name}.sock`);
|
|
78
|
+
}
|
|
79
|
+
/** Get the PID file path for a derivation ID */
|
|
80
|
+
function pidPath(derivationId) {
|
|
81
|
+
const name = sanitizeDerivationId(derivationId);
|
|
82
|
+
return (0, path_1.join)(runtimeDir(), `${name}.pid`);
|
|
83
|
+
}
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Process Detection
|
|
86
|
+
// ============================================================================
|
|
87
|
+
/**
|
|
88
|
+
* Check if a process with the given PID is still alive.
|
|
89
|
+
*
|
|
90
|
+
* Uses `kill -0` which checks process existence without sending a signal.
|
|
91
|
+
* Works on both macOS and Linux without requiring native dependencies.
|
|
92
|
+
*
|
|
93
|
+
* @param pid - Process ID to check
|
|
94
|
+
* @returns true if the process exists, false otherwise
|
|
95
|
+
*/
|
|
96
|
+
function isProcessAlive(pid) {
|
|
97
|
+
try {
|
|
98
|
+
// kill -0 checks existence without actually sending a signal
|
|
99
|
+
process.kill(pid, 0);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
// EPERM = process exists but we don't have permission → still alive
|
|
104
|
+
if (e && typeof e === "object" && "code" in e && e.code === "EPERM") {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
// ESRCH = no such process → dead
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// DaemonRuntime
|
|
113
|
+
// ============================================================================
|
|
114
|
+
/**
|
|
115
|
+
* Manages the lifecycle of a daemon instance.
|
|
116
|
+
*
|
|
117
|
+
* On creation:
|
|
118
|
+
* - Creates the runtime directory if needed
|
|
119
|
+
* - Checks for existing running instances (PID file + process alive check)
|
|
120
|
+
* - Cleans up stale PID/socket files from crashed daemons
|
|
121
|
+
* - Writes the current PID to the PID file
|
|
122
|
+
*
|
|
123
|
+
* On cleanup (via destroy() or process exit handlers):
|
|
124
|
+
* - Removes the PID file
|
|
125
|
+
* - Removes the socket file
|
|
126
|
+
*/
|
|
127
|
+
class DaemonRuntime {
|
|
128
|
+
constructor(socketFilePath, pidFilePath, derivationId) {
|
|
129
|
+
this.cleanedUp = false;
|
|
130
|
+
this._socketPath = socketFilePath;
|
|
131
|
+
this._pidPath = pidFilePath;
|
|
132
|
+
this._derivationId = derivationId;
|
|
133
|
+
// Register cleanup handler for process exit
|
|
134
|
+
this.exitHandler = () => this.cleanup();
|
|
135
|
+
process.on("exit", this.exitHandler);
|
|
136
|
+
// Note: SIGTERM/SIGINT signal handlers are managed by serve.ts
|
|
137
|
+
// to coordinate with the server shutdown. We only handle 'exit' here.
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Create a new DaemonRuntime, performing all startup checks.
|
|
141
|
+
*
|
|
142
|
+
* @param derivationId - Derivation ID for this daemon instance
|
|
143
|
+
* @returns Initialized DaemonRuntime
|
|
144
|
+
* @throws ToolError with code DAEMON if another instance is already running
|
|
145
|
+
*/
|
|
146
|
+
static create(derivationId) {
|
|
147
|
+
const dir = runtimeDir();
|
|
148
|
+
const sock = socketPath(derivationId);
|
|
149
|
+
const pid = pidPath(derivationId);
|
|
150
|
+
// Create runtime directory with restricted permissions (0o700)
|
|
151
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
152
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true, mode: 0o700 });
|
|
153
|
+
}
|
|
154
|
+
// Check for existing running instance
|
|
155
|
+
if ((0, fs_1.existsSync)(pid)) {
|
|
156
|
+
try {
|
|
157
|
+
const pidStr = (0, fs_1.readFileSync)(pid, "utf-8").trim();
|
|
158
|
+
const existingPid = parseInt(pidStr, 10);
|
|
159
|
+
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
160
|
+
throw (0, error_1.daemonError)(`Daemon already running (PID ${existingPid}, derivation_id: ${derivationId}). ` +
|
|
161
|
+
`Use 'zcloak-ai vetkey stop --key-name ...' to stop it first.`);
|
|
162
|
+
}
|
|
163
|
+
// Stale PID file — clean up
|
|
164
|
+
console.error(`Removing stale PID file (PID ${pidStr} no longer running)`);
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
// Re-throw ToolError (daemon already running)
|
|
168
|
+
if (e instanceof Error && e.name === "ToolError")
|
|
169
|
+
throw e;
|
|
170
|
+
// Other errors (corrupted PID file) — just clean up
|
|
171
|
+
console.error(`Removing corrupted PID file: ${e}`);
|
|
172
|
+
}
|
|
173
|
+
safeUnlink(pid);
|
|
174
|
+
safeUnlink(sock);
|
|
175
|
+
}
|
|
176
|
+
// Remove stale socket file if it exists without a PID file
|
|
177
|
+
if ((0, fs_1.existsSync)(sock)) {
|
|
178
|
+
console.error("Removing stale socket file");
|
|
179
|
+
safeUnlink(sock);
|
|
180
|
+
}
|
|
181
|
+
// Write current PID to PID file
|
|
182
|
+
(0, fs_1.writeFileSync)(pid, `${process.pid}\n`, { mode: 0o600 });
|
|
183
|
+
return new DaemonRuntime(sock, pid, derivationId);
|
|
184
|
+
}
|
|
185
|
+
/** Socket file path */
|
|
186
|
+
get socketFilePath() {
|
|
187
|
+
return this._socketPath;
|
|
188
|
+
}
|
|
189
|
+
/** PID file path */
|
|
190
|
+
get pidFilePath() {
|
|
191
|
+
return this._pidPath;
|
|
192
|
+
}
|
|
193
|
+
/** Derivation ID */
|
|
194
|
+
get derivationId() {
|
|
195
|
+
return this._derivationId;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Clean up PID and socket files.
|
|
199
|
+
*
|
|
200
|
+
* Safe to call multiple times — subsequent calls are no-ops.
|
|
201
|
+
* Called automatically on process exit.
|
|
202
|
+
*/
|
|
203
|
+
cleanup() {
|
|
204
|
+
if (this.cleanedUp)
|
|
205
|
+
return;
|
|
206
|
+
this.cleanedUp = true;
|
|
207
|
+
safeUnlink(this._pidPath);
|
|
208
|
+
safeUnlink(this._socketPath);
|
|
209
|
+
// Unregister process exit handler
|
|
210
|
+
process.removeListener("exit", this.exitHandler);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Explicitly destroy the runtime (alias for cleanup).
|
|
214
|
+
* Removes PID and socket files, unregisters handlers.
|
|
215
|
+
*/
|
|
216
|
+
destroy() {
|
|
217
|
+
this.cleanup();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.DaemonRuntime = DaemonRuntime;
|
|
221
|
+
/**
|
|
222
|
+
* Find the socket path for a running daemon (used by stop/status commands).
|
|
223
|
+
*
|
|
224
|
+
* Checks if the socket file exists and the daemon is actually running
|
|
225
|
+
* (via PID file check).
|
|
226
|
+
*
|
|
227
|
+
* @param derivationId - Derivation ID to look up
|
|
228
|
+
* @returns Socket path if daemon is running
|
|
229
|
+
* @throws ToolError if no running daemon is found
|
|
230
|
+
*/
|
|
231
|
+
function findRunningDaemon(derivationId) {
|
|
232
|
+
const sock = socketPath(derivationId);
|
|
233
|
+
const pid = pidPath(derivationId);
|
|
234
|
+
if (!(0, fs_1.existsSync)(sock)) {
|
|
235
|
+
throw (0, error_1.daemonError)(`No running daemon found for derivation_id '${derivationId}'. ` +
|
|
236
|
+
`Socket file not found: ${sock}`);
|
|
237
|
+
}
|
|
238
|
+
// Optionally verify the PID is still alive
|
|
239
|
+
if ((0, fs_1.existsSync)(pid)) {
|
|
240
|
+
try {
|
|
241
|
+
const pidStr = (0, fs_1.readFileSync)(pid, "utf-8").trim();
|
|
242
|
+
const existingPid = parseInt(pidStr, 10);
|
|
243
|
+
if (!isNaN(existingPid) && !isProcessAlive(existingPid)) {
|
|
244
|
+
// Stale files — clean up
|
|
245
|
+
safeUnlink(sock);
|
|
246
|
+
safeUnlink(pid);
|
|
247
|
+
throw (0, error_1.daemonError)(`Daemon for '${derivationId}' is no longer running (PID ${existingPid} is dead). ` +
|
|
248
|
+
`Stale files have been cleaned up.`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
if (e instanceof Error && e.name === "ToolError")
|
|
253
|
+
throw e;
|
|
254
|
+
// Corrupted PID file but socket exists — try connecting anyway
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return sock;
|
|
258
|
+
}
|
|
259
|
+
// ============================================================================
|
|
260
|
+
// Internal Helpers
|
|
261
|
+
// ============================================================================
|
|
262
|
+
/** Safely delete a file, ignoring errors if it doesn't exist */
|
|
263
|
+
function safeUnlink(filePath) {
|
|
264
|
+
try {
|
|
265
|
+
(0, fs_1.unlinkSync)(filePath);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// Ignore — file may not exist
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
package/dist/delete.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* zCloak.ai File Deletion with 2FA Verification Tool
|
|
4
|
+
*
|
|
5
|
+
* Implements secure file deletion requiring 2FA (WebAuthn passkey) authorization.
|
|
6
|
+
* The deletion flow: prepare → user authenticates via browser → confirm & delete.
|
|
7
|
+
* Uses @dfinity JS SDK to interact directly with ICP canister, no dfx required.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* zcloak-ai delete prepare <file_path> Prepare 2FA request and generate authentication URL
|
|
11
|
+
* zcloak-ai delete check <challenge> Check 2FA verification status
|
|
12
|
+
* zcloak-ai delete confirm <challenge> <file_path> Confirm 2FA and delete file if authorized
|
|
13
|
+
*
|
|
14
|
+
* All commands support --identity=<pem_path> to specify identity file.
|
|
15
|
+
*/
|
|
16
|
+
import { Session } from './session';
|
|
17
|
+
/**
|
|
18
|
+
* Entry point when invoked via cli.ts.
|
|
19
|
+
* Receives a Session instance with pre-parsed arguments.
|
|
20
|
+
*/
|
|
21
|
+
export declare function run(session: Session): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=delete.d.ts.map
|