lucifer-gate 0.8.3 → 0.8.6-alpha.2.9ddde04
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 +10 -0
- package/dist/server/domains/command-gateway/repository/api_key_store.js +30 -0
- package/dist/server/domains/command-gateway/repository/api_key_store.js.map +1 -1
- package/dist/server/domains/command-gateway/service/execute_command.js +110 -115
- package/dist/server/domains/command-gateway/service/execute_command.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,6 +100,16 @@ Per-feature contracts live under [docs/specs/](docs/specs/):
|
|
|
100
100
|
For agent-specific instructions (task lifecycle, review checklist, skills),
|
|
101
101
|
start at [AGENTS.md](AGENTS.md).
|
|
102
102
|
|
|
103
|
+
## Operational limits
|
|
104
|
+
|
|
105
|
+
Before you plan a deployment, note the current shape of the runtime:
|
|
106
|
+
|
|
107
|
+
- **Single-process state.** Lucifer Gate runs as one Node process. There is no cluster-aware approval fan-out and no leader election — running more than one instance against the same data directory is not supported.
|
|
108
|
+
- **In-memory pending approvals are lost on restart.** Requests that are waiting for Telegram or web-admin approval at the moment of a restart are dropped: callers see their in-flight `POST /api/v1/execute` fail, and the approval notification stays orphaned in the channel. Approvals that have already resolved are persisted; only *pending* ones are volatile.
|
|
109
|
+
- **SQLite is the sole persistence layer.** Approvals, audit log, and related state all live in `<dataDir>/lucifer.db` via `better-sqlite3`. There is no network database driver, no clustering, and no replication; back up the file directly. Per-domain grading for these trade-offs is tracked in [docs/quality/QUALITY-GRADES.md](docs/quality/QUALITY-GRADES.md).
|
|
110
|
+
|
|
111
|
+
These are conscious pre-1.0 trade-offs, not bugs. If any of them is a blocker for your environment, open an issue before building on top of the current shape.
|
|
112
|
+
|
|
103
113
|
## Stack
|
|
104
114
|
|
|
105
115
|
- Express 5 + TypeScript
|
|
@@ -28,21 +28,51 @@ function isApiKeysConfig(data) {
|
|
|
28
28
|
return false;
|
|
29
29
|
return d.keys.every(isValidKeyEntry);
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Derive a 64-byte scrypt hash of `key` with `salt`, hex-encoded.
|
|
33
|
+
*
|
|
34
|
+
* Defends against: offline brute-force of a leaked `keyHash` (scrypt is memory-hard).
|
|
35
|
+
* Does NOT defend against: leaking both `keyHash` AND `salt` together, online
|
|
36
|
+
* guessing (that is the job of rate-limiting at the HTTP boundary), or disclosure
|
|
37
|
+
* of the raw `key` — once the key is out, the hash is moot.
|
|
38
|
+
*/
|
|
31
39
|
export function hashApiKey(key, salt) {
|
|
32
40
|
return scryptSync(key, salt, 64).toString('hex');
|
|
33
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Generate a fresh API key + per-key salt + scrypt hash.
|
|
44
|
+
*
|
|
45
|
+
* The raw `key` exists only in memory at generation time and is never persisted;
|
|
46
|
+
* only `salt` and `keyHash` are stored. Callers are responsible for delivering the
|
|
47
|
+
* raw key to the operator over a secure channel (the CLI writes it once to stdout).
|
|
48
|
+
*/
|
|
34
49
|
export function generateApiKey() {
|
|
35
50
|
const key = 'luc_' + randomBytes(24).toString('hex');
|
|
36
51
|
const salt = randomBytes(16).toString('hex');
|
|
37
52
|
const keyHash = hashApiKey(key, salt);
|
|
38
53
|
return { key, salt, keyHash };
|
|
39
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Generate a fresh admin bearer secret + salt + scrypt hash.
|
|
57
|
+
*
|
|
58
|
+
* Same shape as {@link generateApiKey} but with an `luc_admin_` prefix. The admin
|
|
59
|
+
* secret gates the `/admin/*` surface (approval UI, SSE stream); it is NOT a valid
|
|
60
|
+
* API-key for `/api/v1/execute`. Do not interchange the two.
|
|
61
|
+
*/
|
|
40
62
|
export function generateAdminSecret() {
|
|
41
63
|
const secret = 'luc_admin_' + randomBytes(24).toString('hex');
|
|
42
64
|
const salt = randomBytes(16).toString('hex');
|
|
43
65
|
const secretHash = hashApiKey(secret, salt);
|
|
44
66
|
return { secret, salt, secretHash };
|
|
45
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Construct an {@link ApiKeyStore} reading from `configPath`.
|
|
70
|
+
*
|
|
71
|
+
* `findByKey` iterates only `active: true` entries and compares with `timingSafeEqual`
|
|
72
|
+
* over equal-length buffers, so lookup time does not leak which stored hash matched.
|
|
73
|
+
* Does NOT defend against: a compromised config file (the salt + keyHash are enough
|
|
74
|
+
* to run offline scrypt cracking) or a process-memory read during `hashApiKey`.
|
|
75
|
+
*/
|
|
46
76
|
export function createApiKeyStore(configPath) {
|
|
47
77
|
let config;
|
|
48
78
|
function load() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api_key_store.js","sourceRoot":"","sources":["../../../../../server/src/domains/command-gateway/repository/api_key_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,GAAG,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAE/C,SAAS,eAAe,CAAC,GAAY;IACnC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAClD,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAChD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,eAAe,CAAC,IAAa;IACpC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,IAAY;IAClD,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,GAAG,GAAG,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,MAAM,GAAG,YAAY,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AACtC,CAAC;
|
|
1
|
+
{"version":3,"file":"api_key_store.js","sourceRoot":"","sources":["../../../../../server/src/domains/command-gateway/repository/api_key_store.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { createChildLogger } from '../../../lib/logger.js';
|
|
|
4
4
|
const log = createChildLogger('executor');
|
|
5
5
|
let activeExecutions = 0;
|
|
6
6
|
export async function executeCommand(options) {
|
|
7
|
-
const { command, requestId, cwd,
|
|
7
|
+
const { command, requestId, cwd, maxConcurrent, aliases } = options;
|
|
8
8
|
if (activeExecutions >= maxConcurrent) {
|
|
9
9
|
log.warn({ requestId, active: activeExecutions, max: maxConcurrent }, 'Max concurrent executions reached');
|
|
10
10
|
return {
|
|
@@ -18,129 +18,124 @@ export async function executeCommand(options) {
|
|
|
18
18
|
const resolved = resolveAlias(command, aliases);
|
|
19
19
|
log.info({ requestId, command, cwd, alias: resolved ? { cwd: resolved.cwd, bin: resolved.spawnCommand } : undefined }, 'Executing command');
|
|
20
20
|
try {
|
|
21
|
-
return await
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
21
|
+
return await runChildProcess(options, resolved, startTime);
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
activeExecutions--;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function spawnChild(options, resolved) {
|
|
28
|
+
// This is a command gateway that intentionally executes user-supplied
|
|
29
|
+
// commands. Access is gated by API-key auth and configurable command
|
|
30
|
+
// rules (allow/deny lists). The spawn call below is by design.
|
|
31
|
+
if (resolved) {
|
|
32
|
+
return spawn(resolved.spawnCommand, resolved.spawnArgs, { cwd: resolved.cwd, detached: true });
|
|
33
|
+
}
|
|
34
|
+
return spawn(options.command, { shell: true, cwd: options.cwd ?? process.cwd(), detached: true }); // NOSONAR -- intentional: this gateway executes user-supplied commands gated by API-key auth and command rules
|
|
35
|
+
}
|
|
36
|
+
function killChildTree(child) {
|
|
37
|
+
try {
|
|
38
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
child.kill('SIGKILL');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function runChildProcess(options, resolved, startTime) {
|
|
45
|
+
const { requestId, timeoutMs, maxOutputBytes, abortSignal } = options;
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const child = spawnChild(options, resolved);
|
|
48
|
+
let stdout = '';
|
|
49
|
+
let stderr = '';
|
|
50
|
+
let outputBytes = 0;
|
|
51
|
+
let killed = false;
|
|
52
|
+
const timer = setTimeout(() => {
|
|
53
|
+
killed = true;
|
|
54
|
+
killChildTree(child);
|
|
55
|
+
log.warn({ requestId, timeoutMs }, 'Command timed out');
|
|
56
|
+
}, timeoutMs);
|
|
57
|
+
const onAbort = () => {
|
|
58
|
+
killed = true;
|
|
59
|
+
killChildTree(child);
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
log.info({ requestId }, 'Command aborted (client disconnected)');
|
|
62
|
+
};
|
|
63
|
+
if (abortSignal?.aborted) {
|
|
64
|
+
killChildTree(child);
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
resolve({ requestId, status: 'failed', error: 'Request aborted' });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
70
|
+
const handleStdoutChunk = (chunk) => {
|
|
71
|
+
outputBytes += chunk.length;
|
|
72
|
+
if (outputBytes <= maxOutputBytes) {
|
|
73
|
+
stdout += chunk.toString();
|
|
74
|
+
return;
|
|
66
75
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
outputBytes += chunk.length;
|
|
86
|
-
if (outputBytes <= maxOutputBytes) {
|
|
87
|
-
stderr += chunk.toString();
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
child.on('close', (code) => {
|
|
91
|
-
clearTimeout(timer);
|
|
92
|
-
if (abortSignal) {
|
|
93
|
-
abortSignal.removeEventListener('abort', onAbort);
|
|
94
|
-
}
|
|
95
|
-
const durationMs = Date.now() - startTime;
|
|
96
|
-
if (killed && outputBytes > maxOutputBytes) {
|
|
97
|
-
resolve({
|
|
98
|
-
requestId,
|
|
99
|
-
status: 'failed',
|
|
100
|
-
stdout: stdout.slice(0, maxOutputBytes),
|
|
101
|
-
stderr,
|
|
102
|
-
durationMs,
|
|
103
|
-
error: `Output exceeded ${maxOutputBytes} bytes limit`,
|
|
104
|
-
});
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (killed) {
|
|
108
|
-
resolve({
|
|
109
|
-
requestId,
|
|
110
|
-
status: 'timed_out',
|
|
111
|
-
stdout,
|
|
112
|
-
stderr,
|
|
113
|
-
durationMs,
|
|
114
|
-
error: `Command timed out after ${timeoutMs}ms`,
|
|
115
|
-
});
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
76
|
+
if (killed)
|
|
77
|
+
return;
|
|
78
|
+
killed = true;
|
|
79
|
+
killChildTree(child);
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
log.warn({ requestId, outputBytes, maxOutputBytes }, 'Output buffer exceeded');
|
|
82
|
+
};
|
|
83
|
+
const handleStderrChunk = (chunk) => {
|
|
84
|
+
outputBytes += chunk.length;
|
|
85
|
+
if (outputBytes <= maxOutputBytes) {
|
|
86
|
+
stderr += chunk.toString();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const handleClose = (code) => {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
abortSignal?.removeEventListener('abort', onAbort);
|
|
92
|
+
const durationMs = Date.now() - startTime;
|
|
93
|
+
if (killed && outputBytes > maxOutputBytes) {
|
|
118
94
|
resolve({
|
|
119
95
|
requestId,
|
|
120
|
-
status:
|
|
121
|
-
|
|
122
|
-
stdout,
|
|
96
|
+
status: 'failed',
|
|
97
|
+
stdout: stdout.slice(0, maxOutputBytes),
|
|
123
98
|
stderr,
|
|
124
99
|
durationMs,
|
|
100
|
+
error: `Output exceeded ${maxOutputBytes} bytes limit`,
|
|
125
101
|
});
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
clearTimeout(timer);
|
|
130
|
-
if (abortSignal) {
|
|
131
|
-
abortSignal.removeEventListener('abort', onAbort);
|
|
132
|
-
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (killed) {
|
|
133
105
|
resolve({
|
|
134
106
|
requestId,
|
|
135
|
-
status: '
|
|
136
|
-
|
|
137
|
-
|
|
107
|
+
status: 'timed_out',
|
|
108
|
+
stdout,
|
|
109
|
+
stderr,
|
|
110
|
+
durationMs,
|
|
111
|
+
error: `Command timed out after ${timeoutMs}ms`,
|
|
138
112
|
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
resolve({
|
|
116
|
+
requestId,
|
|
117
|
+
status: code === 0 ? 'completed' : 'failed',
|
|
118
|
+
exitCode: code ?? undefined,
|
|
119
|
+
stdout,
|
|
120
|
+
stderr,
|
|
121
|
+
durationMs,
|
|
139
122
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
123
|
+
log.info({ requestId, exitCode: code, durationMs }, 'Command completed');
|
|
124
|
+
};
|
|
125
|
+
const handleError = (err) => {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
abortSignal?.removeEventListener('abort', onAbort);
|
|
128
|
+
resolve({
|
|
129
|
+
requestId,
|
|
130
|
+
status: 'failed',
|
|
131
|
+
error: `Failed to execute: ${err.message}`,
|
|
132
|
+
durationMs: Date.now() - startTime,
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
child.stdout.on('data', handleStdoutChunk);
|
|
136
|
+
child.stderr.on('data', handleStderrChunk);
|
|
137
|
+
child.on('close', handleClose);
|
|
138
|
+
child.on('error', handleError);
|
|
139
|
+
});
|
|
145
140
|
}
|
|
146
141
|
//# sourceMappingURL=execute_command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute_command.js","sourceRoot":"","sources":["../../../../../server/src/domains/command-gateway/service/execute_command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"execute_command.js","sourceRoot":"","sources":["../../../../../server/src/domains/command-gateway/service/execute_command.ts"],"names":[],"mappings":"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"}
|