indelible-mcp 3.9.0 → 4.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 +7 -3
- package/package.json +1 -1
- package/src/index.js +2 -2
- package/src/tools/load_context.js +14 -3
- package/src/tools/load_file.js +3 -0
- package/src/tools/save_file.js +4 -2
- package/src/tools/save_session.js +3 -1
package/README.md
CHANGED
|
@@ -67,19 +67,23 @@ indelible-mcp hook post-compact Post-compaction restore hook
|
|
|
67
67
|
## How It Works
|
|
68
68
|
|
|
69
69
|
1. Your conversation is encrypted locally with your WIF key
|
|
70
|
-
2.
|
|
71
|
-
3. The signed transaction is broadcast via Indelible's
|
|
72
|
-
4.
|
|
70
|
+
2. A minimal OP_RETURN transaction is built containing only `{protocol, encrypted}` — no metadata leaks
|
|
71
|
+
3. The signed transaction is broadcast via Indelible's federation bridges
|
|
72
|
+
4. Session metadata is automatically indexed across all federation bridges for fast retrieval
|
|
73
|
+
5. Your private key **never** leaves your machine
|
|
73
74
|
|
|
74
75
|
## Federation
|
|
75
76
|
|
|
76
77
|
Indelible uses a multi-seed architecture — your saves and loads automatically try multiple federation bridges. If one bridge is down, the next picks up. No single point of failure.
|
|
77
78
|
|
|
79
|
+
Bridges sync session metadata via SessionRelay (WebSocket peer-to-peer), so all your sessions are available from any bridge in the mesh. The web app and CLI read from bridges first, with blockchain fallback.
|
|
80
|
+
|
|
78
81
|
The federation mesh is powered by [Relay Federation](https://github.com/zcoolz/relay-federation).
|
|
79
82
|
|
|
80
83
|
## Security
|
|
81
84
|
|
|
82
85
|
- **Zero-knowledge encryption** - your WIF-derived AES-256-GCM key encrypts all data before it touches the network
|
|
86
|
+
- **Privacy-hardened transactions** - OP_RETURN contains only `{protocol, encrypted}` — no plaintext metadata on-chain
|
|
83
87
|
- **Self-sovereign keys** - generated locally with `@bsv/sdk`, never transmitted
|
|
84
88
|
- **Immutable storage** - once on BSV, your data cannot be altered or deleted
|
|
85
89
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -254,7 +254,7 @@ Commands:
|
|
|
254
254
|
|
|
255
255
|
function printHelp() {
|
|
256
256
|
console.log(`
|
|
257
|
-
Indelible MCP — Blockchain memory for Claude Code (
|
|
257
|
+
Indelible MCP — Blockchain memory for Claude Code (v4.0.0)
|
|
258
258
|
|
|
259
259
|
Setup:
|
|
260
260
|
indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
|
|
@@ -466,7 +466,7 @@ function readStdin() {
|
|
|
466
466
|
|
|
467
467
|
const SERVER_INFO = {
|
|
468
468
|
name: 'indelible',
|
|
469
|
-
version: '
|
|
469
|
+
version: '4.0.0',
|
|
470
470
|
description: 'Blockchain-backed memory and code storage for Claude Code'
|
|
471
471
|
}
|
|
472
472
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { readFileSync, existsSync } from 'node:fs'
|
|
12
|
+
import { gunzipSync } from 'node:zlib'
|
|
12
13
|
import { join } from 'node:path'
|
|
13
14
|
import { homedir } from 'node:os'
|
|
14
15
|
import { Transaction } from '@bsv/sdk'
|
|
@@ -21,6 +22,16 @@ import { loadStyle } from './load_style.js'
|
|
|
21
22
|
const RECENT_MESSAGES_FULL = 25
|
|
22
23
|
const OLDER_MESSAGES_SUMMARIZED = 15
|
|
23
24
|
|
|
25
|
+
/** Decrypt and optionally gunzip a session payload (handles gz: prefix) */
|
|
26
|
+
function decryptSession(encrypted, wif) {
|
|
27
|
+
const text = decrypt(encrypted, wif)
|
|
28
|
+
if (text.startsWith('gz:')) {
|
|
29
|
+
const buf = gunzipSync(Buffer.from(text.slice(3), 'base64'))
|
|
30
|
+
return JSON.parse(buf.toString('utf8'))
|
|
31
|
+
}
|
|
32
|
+
return JSON.parse(text)
|
|
33
|
+
}
|
|
34
|
+
|
|
24
35
|
function mergeDeltaSessions(sessions) {
|
|
25
36
|
if (sessions.length === 0) return sessions
|
|
26
37
|
const ordered = [...sessions].reverse()
|
|
@@ -195,7 +206,7 @@ export async function loadContext(numSessions = 5) {
|
|
|
195
206
|
for (const entry of recent) {
|
|
196
207
|
try {
|
|
197
208
|
if (entry.encrypted) {
|
|
198
|
-
const data =
|
|
209
|
+
const data = decryptSession(entry.encrypted, wif)
|
|
199
210
|
decrypted.push(data)
|
|
200
211
|
}
|
|
201
212
|
} catch { /* skip sessions that fail to decrypt */ }
|
|
@@ -240,12 +251,12 @@ export async function loadContext(numSessions = 5) {
|
|
|
240
251
|
for (const session of sessions) {
|
|
241
252
|
try {
|
|
242
253
|
if (session.encrypted) {
|
|
243
|
-
const data =
|
|
254
|
+
const data = decryptSession(session.encrypted, wif)
|
|
244
255
|
decrypted.push(data)
|
|
245
256
|
} else if (session.txId) {
|
|
246
257
|
const encrypted = await fetchEncryptedFromChain(session.txId)
|
|
247
258
|
if (encrypted) {
|
|
248
|
-
const data =
|
|
259
|
+
const data = decryptSession(encrypted, wif)
|
|
249
260
|
decrypted.push(data)
|
|
250
261
|
}
|
|
251
262
|
}
|
package/src/tools/load_file.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Supports v1 (plaintext metadata) and v2 (encrypted metadata).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { gunzipSync } from 'node:zlib'
|
|
7
8
|
import { writeFileSync } from 'node:fs'
|
|
8
9
|
import { dirname } from 'node:path'
|
|
9
10
|
import { mkdirSync, existsSync } from 'node:fs'
|
|
@@ -72,8 +73,10 @@ export async function loadFile(txId, options = {}) {
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
content = decrypt(encryptedFull, wif)
|
|
76
|
+
if (content.startsWith('gz:')) content = gunzipSync(Buffer.from(content.slice(3), 'base64')).toString('utf8')
|
|
75
77
|
} else {
|
|
76
78
|
content = decrypt(payload.encrypted, wif)
|
|
79
|
+
if (content.startsWith('gz:')) content = gunzipSync(Buffer.from(content.slice(3), 'base64')).toString('utf8')
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
// Decrypt metadata (v2) or use plaintext (v1)
|
package/src/tools/save_file.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* On-chain: only encrypted data, no plaintext filenames.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { gzipSync } from 'node:zlib'
|
|
7
8
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'
|
|
8
9
|
import { basename, join } from 'node:path'
|
|
9
10
|
import { homedir } from 'node:os'
|
|
@@ -40,8 +41,9 @@ export async function saveFile(filePath, options = {}) {
|
|
|
40
41
|
const filename = basename(filePath)
|
|
41
42
|
const relativePath = options.relativePath || filePath
|
|
42
43
|
|
|
43
|
-
//
|
|
44
|
-
const
|
|
44
|
+
// Gzip compress then encrypt content
|
|
45
|
+
const compressed = gzipSync(Buffer.from(content))
|
|
46
|
+
const encrypted = encrypt('gz:' + compressed.toString('base64'), wif)
|
|
45
47
|
const filenameEnc = encrypt(filename, wif)
|
|
46
48
|
const pathEnc = encrypt(relativePath, wif)
|
|
47
49
|
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { readFileSync, writeFileSync, existsSync, statSync, mkdirSync, readdirSync } from 'node:fs'
|
|
17
|
+
import { gzipSync } from 'node:zlib'
|
|
17
18
|
import { dirname, join } from 'node:path'
|
|
18
19
|
import { homedir } from 'node:os'
|
|
19
20
|
import { execSync } from 'node:child_process'
|
|
@@ -534,7 +535,8 @@ export async function saveSession(transcriptPath, summary) {
|
|
|
534
535
|
if (estimatedFee > COST_WARNING_SATS) {
|
|
535
536
|
process.stderr.write(`[indelible] WARNING: Estimated cost ~${estimatedFee} sats (~$${(estimatedFee / 1e8 * 15).toFixed(2)}). Rich mode is ${richMode ? 'ON' : 'OFF'}. Set "rich_saves": false in config to reduce cost.\n`)
|
|
536
537
|
}
|
|
537
|
-
const
|
|
538
|
+
const compressed = gzipSync(Buffer.from(sessionJson))
|
|
539
|
+
const encrypted = encrypt('gz:' + compressed.toString('base64'), wif)
|
|
538
540
|
|
|
539
541
|
// Build the blockchain payload
|
|
540
542
|
const payload = {
|