fix(cli): e2e encrypt vault entries with libsodium
Some checks failed
CI / Docker build (linux/amd64) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-08 12:10:23 +01:00
parent 02a165dd76
commit a90046a8e3
3 changed files with 24 additions and 6 deletions

View File

@@ -1354,16 +1354,30 @@ Your message mode is "${messageMode}".
const client = allClients()[0];
if (!client) return text("vault_set: not connected", true);
const entryType = vType ?? "env";
let plaintext = value;
// Read plaintext
let plaintextBytes: Uint8Array;
if (entryType === "file") {
const { existsSync, readFileSync } = await import("node:fs");
if (!existsSync(value)) return text(`vault_set: file not found: ${value}`, true);
plaintext = readFileSync(value, "base64");
plaintextBytes = new Uint8Array(readFileSync(value));
} else {
plaintextBytes = new TextEncoder().encode(value);
}
const encoded = Buffer.from(plaintext).toString("base64");
const ok = await client.vaultSet(key, encoded, "placeholder-nonce", "placeholder-sealed", entryType, mount_path, description);
// E2E encrypt: crypto_secretbox with random Kf, then seal Kf with mesh pubkey
const { encryptFile, sealKeyForPeer } = await import("../crypto/file-crypto");
const { ciphertext, nonce, key: kf } = await encryptFile(plaintextBytes);
const sealedKey = await sealKeyForPeer(kf, client.getMeshPubkey());
// Convert ciphertext to base64 for storage
const { ensureSodium } = await import("../crypto/keypair");
const sodium = await ensureSodium();
const ciphertextB64 = sodium.to_base64(ciphertext, sodium.base64_variants.ORIGINAL);
const ok = await client.vaultSet(key, ciphertextB64, nonce, sealedKey, entryType, mount_path, description);
if (!ok) return text("vault_set: broker did not acknowledge", true);
return text(`Vault entry "${key}" stored (${entryType}).`);
return text(`Vault entry "${key}" stored (${entryType}, E2E encrypted).`);
}
case "vault_list": {
const client = allClients()[0];

View File

@@ -194,6 +194,10 @@ export class BrokerClient {
getSessionPubkey(): string | null { return this.sessionPubkey; }
/** Session secret key hex (null before first connection). */
getSessionSecretKey(): string | null { return this.sessionSecretKey; }
/** Mesh member public key hex (stable across sessions). */
getMeshPubkey(): string { return this.mesh.pubkey; }
/** Mesh member secret key hex (stable across sessions). */
getMeshSecretKey(): string { return this.mesh.secretKey; }
private makeReqId(): string {
return Math.random().toString(36).slice(2) + Date.now().toString(36);