inillucent 1.0.32 → 2.0.1

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 CHANGED
@@ -39,9 +39,9 @@ package by name, for example `npm install @blackrainbowlabs/cli-linux-x64`.
39
39
 
40
40
  | Program | What it is |
41
41
  |---|---|
42
- | `inillucent` | the command line: 30 commands, such as `query`, `exec`, `describe`, `import`, `export` and `search` |
42
+ | `inillucent` | the command line: 34 commands, such as `query`, `exec`, `describe`, `import`, `export` and `search` |
43
43
  | `inillucent-shell` | an interactive shell that works like `sqlite3`, with 63 of its 65 dot commands |
44
- | `inillucent-mcp` | an MCP server: 28 of the same commands served to an AI agent |
44
+ | `inillucent-mcp` | an MCP server: 29 of the same commands served to an AI agent |
45
45
  | `inillucent-migrate` | builds a database from a legacy retrieval index. `inillucent migrate` copies a SQLite file or a PostgreSQL or MySQL database |
46
46
 
47
47
  ## From a shell
@@ -106,6 +106,24 @@ spans two calls. To run several statements as one transaction, use the `batch` c
106
106
  `query()` returns a BLOB column as a `Uint8Array`, so bytes read by one query can be bound into the
107
107
  next.
108
108
 
109
+ ## Encrypted databases
110
+
111
+ Pass `key` beside `db`:
112
+
113
+ ```js
114
+ const key = `x'${'5a'.repeat(32)}'`;
115
+ await inillucent('exec', { db: 'app.rdb', key, sql: 'CREATE TABLE note (body TEXT)' });
116
+ const rows = await query('SELECT body FROM note', { db: 'app.rdb', key });
117
+ ```
118
+
119
+ The key travels to the program in the `INILLUCENT_KEY` environment variable. It is never put on the command line, because a command line is visible in the process list. When you pass no key, the environment the program inherits is left alone.
120
+
121
+ A key written `x'` followed by 64 hexadecimal digits and `'` is a raw 32 byte key. Any other text is a passphrase, which is stretched with 600,000 rounds of PBKDF2 on every open, so a raw key opens faster.
122
+
123
+ A database created with a key is encrypted. Opening it without the key, or with a wrong key, fails with status `corrupt`. Opening a plaintext database with a key fails the same way.
124
+
125
+ A call without `key` on an encrypted database returns `{ ok: false, status: 'corrupt' }`.
126
+
109
127
  ## Errors
110
128
 
111
129
  ```js
@@ -146,7 +164,7 @@ client's configuration:
146
164
  }
147
165
  ```
148
166
 
149
- `inillucent-mcp` serves 28 of the command line's commands as MCP tools. The tools are generated from
167
+ `inillucent-mcp` serves 29 of the command line's commands as MCP tools. The tools are generated from
150
168
  the same command table as the command line. `--readonly` refuses every statement that changes data.
151
169
  `--root DIR` refuses every path outside `DIR`.
152
170
 
@@ -1,33 +1,33 @@
1
- #!/usr/bin/env node
2
- // The `inillucent-mcp` shim: resolve the platform binary and become it.
3
- //
4
- // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
- // execve and this has to work on Windows. The child's exit code and its signal
6
- // are both passed back up: a wrapper that always exited 0 would break every
7
- // script that branches on the exit code, and inillucent's exit codes carry
8
- // meaning - 3 is "the engine has not built that".
9
- import { spawn } from 'node:child_process';
10
- import { resolveBinary } from '../resolve.mjs';
11
-
12
- let binary;
13
- try {
14
- binary = resolveBinary('inillucent-mcp');
15
- } catch (why) {
16
- process.stderr.write(String(why.message ?? why) + '\n');
17
- process.exit(1);
18
- }
19
-
20
- const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
- child.on('error', (why) => {
22
- process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
- process.exit(1);
24
- });
25
- child.on('exit', (code, signal) => {
26
- if (signal) {
27
- // Re-raise it on ourselves so a shell sees the same thing it would have
28
- // seen from the real program, rather than a plain exit.
29
- process.kill(process.pid, signal);
30
- return;
31
- }
32
- process.exit(code ?? 0);
33
- });
1
+ #!/usr/bin/env node
2
+ // The `inillucent-mcp` shim: resolve the platform binary and become it.
3
+ //
4
+ // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
+ // execve and this has to work on Windows. The child's exit code and its signal
6
+ // are both passed back up: a wrapper that always exited 0 would break every
7
+ // script that branches on the exit code, and inillucent's exit codes carry
8
+ // meaning - 3 is "the engine has not built that".
9
+ import { spawn } from 'node:child_process';
10
+ import { resolveBinary } from '../resolve.mjs';
11
+
12
+ let binary;
13
+ try {
14
+ binary = resolveBinary('inillucent-mcp');
15
+ } catch (why) {
16
+ process.stderr.write(String(why.message ?? why) + '\n');
17
+ process.exit(1);
18
+ }
19
+
20
+ const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
+ child.on('error', (why) => {
22
+ process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
+ process.exit(1);
24
+ });
25
+ child.on('exit', (code, signal) => {
26
+ if (signal) {
27
+ // Re-raise it on ourselves so a shell sees the same thing it would have
28
+ // seen from the real program, rather than a plain exit.
29
+ process.kill(process.pid, signal);
30
+ return;
31
+ }
32
+ process.exit(code ?? 0);
33
+ });
@@ -1,33 +1,33 @@
1
- #!/usr/bin/env node
2
- // The `inillucent-migrate` shim: resolve the platform binary and become it.
3
- //
4
- // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
- // execve and this has to work on Windows. The child's exit code and its signal
6
- // are both passed back up: a wrapper that always exited 0 would break every
7
- // script that branches on the exit code, and inillucent's exit codes carry
8
- // meaning - 3 is "the engine has not built that".
9
- import { spawn } from 'node:child_process';
10
- import { resolveBinary } from '../resolve.mjs';
11
-
12
- let binary;
13
- try {
14
- binary = resolveBinary('inillucent-migrate');
15
- } catch (why) {
16
- process.stderr.write(String(why.message ?? why) + '\n');
17
- process.exit(1);
18
- }
19
-
20
- const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
- child.on('error', (why) => {
22
- process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
- process.exit(1);
24
- });
25
- child.on('exit', (code, signal) => {
26
- if (signal) {
27
- // Re-raise it on ourselves so a shell sees the same thing it would have
28
- // seen from the real program, rather than a plain exit.
29
- process.kill(process.pid, signal);
30
- return;
31
- }
32
- process.exit(code ?? 0);
33
- });
1
+ #!/usr/bin/env node
2
+ // The `inillucent-migrate` shim: resolve the platform binary and become it.
3
+ //
4
+ // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
+ // execve and this has to work on Windows. The child's exit code and its signal
6
+ // are both passed back up: a wrapper that always exited 0 would break every
7
+ // script that branches on the exit code, and inillucent's exit codes carry
8
+ // meaning - 3 is "the engine has not built that".
9
+ import { spawn } from 'node:child_process';
10
+ import { resolveBinary } from '../resolve.mjs';
11
+
12
+ let binary;
13
+ try {
14
+ binary = resolveBinary('inillucent-migrate');
15
+ } catch (why) {
16
+ process.stderr.write(String(why.message ?? why) + '\n');
17
+ process.exit(1);
18
+ }
19
+
20
+ const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
+ child.on('error', (why) => {
22
+ process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
+ process.exit(1);
24
+ });
25
+ child.on('exit', (code, signal) => {
26
+ if (signal) {
27
+ // Re-raise it on ourselves so a shell sees the same thing it would have
28
+ // seen from the real program, rather than a plain exit.
29
+ process.kill(process.pid, signal);
30
+ return;
31
+ }
32
+ process.exit(code ?? 0);
33
+ });
@@ -1,33 +1,33 @@
1
- #!/usr/bin/env node
2
- // The `inillucent-shell` shim: resolve the platform binary and become it.
3
- //
4
- // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
- // execve and this has to work on Windows. The child's exit code and its signal
6
- // are both passed back up: a wrapper that always exited 0 would break every
7
- // script that branches on the exit code, and inillucent's exit codes carry
8
- // meaning - 3 is "the engine has not built that".
9
- import { spawn } from 'node:child_process';
10
- import { resolveBinary } from '../resolve.mjs';
11
-
12
- let binary;
13
- try {
14
- binary = resolveBinary('inillucent-shell');
15
- } catch (why) {
16
- process.stderr.write(String(why.message ?? why) + '\n');
17
- process.exit(1);
18
- }
19
-
20
- const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
- child.on('error', (why) => {
22
- process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
- process.exit(1);
24
- });
25
- child.on('exit', (code, signal) => {
26
- if (signal) {
27
- // Re-raise it on ourselves so a shell sees the same thing it would have
28
- // seen from the real program, rather than a plain exit.
29
- process.kill(process.pid, signal);
30
- return;
31
- }
32
- process.exit(code ?? 0);
33
- });
1
+ #!/usr/bin/env node
2
+ // The `inillucent-shell` shim: resolve the platform binary and become it.
3
+ //
4
+ // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
+ // execve and this has to work on Windows. The child's exit code and its signal
6
+ // are both passed back up: a wrapper that always exited 0 would break every
7
+ // script that branches on the exit code, and inillucent's exit codes carry
8
+ // meaning - 3 is "the engine has not built that".
9
+ import { spawn } from 'node:child_process';
10
+ import { resolveBinary } from '../resolve.mjs';
11
+
12
+ let binary;
13
+ try {
14
+ binary = resolveBinary('inillucent-shell');
15
+ } catch (why) {
16
+ process.stderr.write(String(why.message ?? why) + '\n');
17
+ process.exit(1);
18
+ }
19
+
20
+ const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
+ child.on('error', (why) => {
22
+ process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
+ process.exit(1);
24
+ });
25
+ child.on('exit', (code, signal) => {
26
+ if (signal) {
27
+ // Re-raise it on ourselves so a shell sees the same thing it would have
28
+ // seen from the real program, rather than a plain exit.
29
+ process.kill(process.pid, signal);
30
+ return;
31
+ }
32
+ process.exit(code ?? 0);
33
+ });
@@ -1,33 +1,33 @@
1
- #!/usr/bin/env node
2
- // The `inillucent` shim: resolve the platform binary and become it.
3
- //
4
- // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
- // execve and this has to work on Windows. The child's exit code and its signal
6
- // are both passed back up: a wrapper that always exited 0 would break every
7
- // script that branches on the exit code, and inillucent's exit codes carry
8
- // meaning - 3 is "the engine has not built that".
9
- import { spawn } from 'node:child_process';
10
- import { resolveBinary } from '../resolve.mjs';
11
-
12
- let binary;
13
- try {
14
- binary = resolveBinary('inillucent');
15
- } catch (why) {
16
- process.stderr.write(String(why.message ?? why) + '\n');
17
- process.exit(1);
18
- }
19
-
20
- const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
- child.on('error', (why) => {
22
- process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
- process.exit(1);
24
- });
25
- child.on('exit', (code, signal) => {
26
- if (signal) {
27
- // Re-raise it on ourselves so a shell sees the same thing it would have
28
- // seen from the real program, rather than a plain exit.
29
- process.kill(process.pid, signal);
30
- return;
31
- }
32
- process.exit(code ?? 0);
33
- });
1
+ #!/usr/bin/env node
2
+ // The `inillucent` shim: resolve the platform binary and become it.
3
+ //
4
+ // `spawn` with `stdio: 'inherit'` rather than an exec, because Node has no
5
+ // execve and this has to work on Windows. The child's exit code and its signal
6
+ // are both passed back up: a wrapper that always exited 0 would break every
7
+ // script that branches on the exit code, and inillucent's exit codes carry
8
+ // meaning - 3 is "the engine has not built that".
9
+ import { spawn } from 'node:child_process';
10
+ import { resolveBinary } from '../resolve.mjs';
11
+
12
+ let binary;
13
+ try {
14
+ binary = resolveBinary('inillucent');
15
+ } catch (why) {
16
+ process.stderr.write(String(why.message ?? why) + '\n');
17
+ process.exit(1);
18
+ }
19
+
20
+ const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
21
+ child.on('error', (why) => {
22
+ process.stderr.write(`inillucent: could not run ${binary}: ${why.message}\n`);
23
+ process.exit(1);
24
+ });
25
+ child.on('exit', (code, signal) => {
26
+ if (signal) {
27
+ // Re-raise it on ourselves so a shell sees the same thing it would have
28
+ // seen from the real program, rather than a plain exit.
29
+ process.kill(process.pid, signal);
30
+ return;
31
+ }
32
+ process.exit(code ?? 0);
33
+ });
package/index.mjs CHANGED
@@ -77,14 +77,15 @@ export { resolveBinary, PROGRAMS, platformPackage };
77
77
  * @param binary - the program to run
78
78
  * @param args - its command line
79
79
  * @param stdin - what to write to its standard input, or null
80
+ * @param env - the environment the program runs with
80
81
  */
81
- async function spawnWith(binary, args, stdin) {
82
+ async function spawnWith(binary, args, stdin, env) {
82
83
  if (stdin === null) {
83
- return run(binary, args, { maxBuffer: 256 * 1024 * 1024 });
84
+ return run(binary, args, { maxBuffer: 256 * 1024 * 1024, env });
84
85
  }
85
86
  const { spawn } = await import('node:child_process');
86
87
  return new Promise((resolve, reject) => {
87
- const child = spawn(binary, args, { stdio: ['pipe', 'pipe', 'pipe'] });
88
+ const child = spawn(binary, args, { stdio: ['pipe', 'pipe', 'pipe'], env });
88
89
  let stdout = '';
89
90
  let stderr = '';
90
91
  child.stdout.on('data', (chunk) => {
@@ -110,6 +111,22 @@ async function spawnWith(binary, args, stdin) {
110
111
  });
111
112
  }
112
113
 
114
+ /**
115
+ * Builds the environment a child program runs with.
116
+ *
117
+ * The key of an encrypted database travels in `INILLUCENT_KEY` and never on the
118
+ * command line, because a command line is visible in the process list. With no
119
+ * key the inherited environment is passed on untouched.
120
+ *
121
+ * @param key - the key text of an encrypted database, or undefined
122
+ */
123
+ function childEnvironment(key) {
124
+ if (key === undefined || key === null) {
125
+ return process.env;
126
+ }
127
+ return { ...process.env, INILLUCENT_KEY: String(key) };
128
+ }
129
+
113
130
  /**
114
131
  * Runs one inillucent command and returns its result object.
115
132
  *
@@ -124,10 +141,11 @@ async function spawnWith(binary, args, stdin) {
124
141
  * broken invocation throws.
125
142
  *
126
143
  * @param command - the verb, such as "query" or "describe"
127
- * @param options - the named arguments the verb takes, plus `db`
144
+ * @param options - the named arguments the verb takes, plus `db` and `key`
145
+ * (the key of an encrypted database, sent in `INILLUCENT_KEY`)
128
146
  */
129
147
  export async function inillucent(command, options = {}) {
130
- const { db, ...rest } = options;
148
+ const { db, key, ...rest } = options;
131
149
  const args = [command, '--output', 'json'];
132
150
  let stdin = null;
133
151
  if (db) {
@@ -160,7 +178,7 @@ export async function inillucent(command, options = {}) {
160
178
  }
161
179
  const binary = resolveBinary('inillucent');
162
180
  try {
163
- const { stdout } = await spawnWith(binary, args, stdin);
181
+ const { stdout } = await spawnWith(binary, args, stdin, childEnvironment(key));
164
182
  return JSON.parse(stdout);
165
183
  } catch (why) {
166
184
  // A non-zero exit still prints the result object on standard output when
@@ -182,7 +200,7 @@ export async function inillucent(command, options = {}) {
182
200
  * that need the counts, the timing or the failure class.
183
201
  *
184
202
  * @param sql - the statement
185
- * @param options - `db`, `params`, `limit`
203
+ * @param options - `db`, `key`, `params`, `limit`
186
204
  */
187
205
  export async function query(sql, options = {}) {
188
206
  const result = await inillucent('query', { sql, ...options });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inillucent",
3
- "version": "1.0.32",
3
+ "version": "2.0.1",
4
4
  "description": "Embedded SQL database with full-text and vector search, an MCP server, and a sqlite3-shaped shell. No native build step: the binaries ship as platform packages.",
5
5
  "keywords": [
6
6
  "database",
@@ -43,10 +43,10 @@
43
43
  "README.md"
44
44
  ],
45
45
  "optionalDependencies": {
46
- "@blackrainbowlabs/cli-win32-x64": "1.0.32",
47
- "@blackrainbowlabs/cli-darwin-arm64": "1.0.32",
48
- "@blackrainbowlabs/cli-darwin-x64": "1.0.32",
49
- "@blackrainbowlabs/cli-linux-x64": "1.0.32",
50
- "@blackrainbowlabs/cli-linux-arm64": "1.0.32"
46
+ "@blackrainbowlabs/cli-win32-x64": "2.0.1",
47
+ "@blackrainbowlabs/cli-darwin-arm64": "2.0.1",
48
+ "@blackrainbowlabs/cli-darwin-x64": "2.0.1",
49
+ "@blackrainbowlabs/cli-linux-x64": "2.0.1",
50
+ "@blackrainbowlabs/cli-linux-arm64": "2.0.1"
51
51
  }
52
52
  }