isol8 0.8.3 → 0.9.0-alpha.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.
@@ -71,6 +71,10 @@ if [ "$method" = "CONNECT" ]; then
71
71
 
72
72
  if ! is_allowed "$host"; then
73
73
  msg="isol8: CONNECT to ${host} blocked by network filter"
74
+ # Log security event
75
+ if [ -d "/tmp/isol8-proxy" ]; then
76
+ printf '{"type":"network_blocked","timestamp":"%s","details":{"method":"CONNECT","host":"%s","reason":"filter_mismatch"}}\n' "$(date -Iseconds)" "$host" >> /tmp/isol8-proxy/security-events.jsonl
77
+ fi
74
78
  printf "HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s" \
75
79
  "${#msg}" "$msg"
76
80
  exit 0
@@ -96,6 +100,10 @@ port="${hostport##*:}"
96
100
 
97
101
  if ! is_allowed "$host"; then
98
102
  msg="isol8: request to ${host} blocked by network filter"
103
+ # Log security event
104
+ if [ -d "/tmp/isol8-proxy" ]; then
105
+ printf '{"type":"network_blocked","timestamp":"%s","details":{"method":"%s","host":"%s","reason":"filter_mismatch"}}\n' "$(date -Iseconds)" "$method" "$host" >> /tmp/isol8-proxy/security-events.jsonl
106
+ fi
99
107
  printf "HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s" \
100
108
  "${#msg}" "$msg"
101
109
  exit 0
@@ -17,6 +17,9 @@ PORT="${ISOL8_PROXY_PORT:-8118}"
17
17
  PROXY_DIR="/tmp/isol8-proxy"
18
18
  mkdir -p "$PROXY_DIR"
19
19
 
20
+ # Create security events log file
21
+ touch "$PROXY_DIR/security-events.jsonl"
22
+
20
23
  WL_FILE="$PROXY_DIR/whitelist"
21
24
  BL_FILE="$PROXY_DIR/blacklist"
22
25
 
package/dist/index.js CHANGED
@@ -198,6 +198,144 @@ var init_logger = __esm(() => {
198
198
  logger = new Logger;
199
199
  });
200
200
 
201
+ // src/engine/audit.ts
202
+ import { spawn } from "node:child_process";
203
+ import { appendFileSync, existsSync as existsSync2, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
204
+ import { join as join2 } from "node:path";
205
+
206
+ class AuditLogger {
207
+ config;
208
+ auditFile;
209
+ constructor(config) {
210
+ this.config = config;
211
+ const auditDir = config.logDir ?? process.env.ISOL8_AUDIT_DIR ?? join2(process.cwd(), "./.isol8_audit");
212
+ this.auditFile = join2(auditDir, "executions.log");
213
+ if (!existsSync2(auditDir)) {
214
+ try {
215
+ mkdirSync(auditDir, { recursive: true });
216
+ } catch (err) {
217
+ logger.error("Failed to create audit dir:", err);
218
+ }
219
+ }
220
+ this.cleanupOldLogs();
221
+ }
222
+ cleanupOldLogs() {
223
+ if (!this.config.enabled || this.config.retentionDays <= 0) {
224
+ return;
225
+ }
226
+ try {
227
+ const auditDir = join2(this.auditFile, "..");
228
+ if (!existsSync2(auditDir)) {
229
+ return;
230
+ }
231
+ const cutoffTime = Date.now() - this.config.retentionDays * 24 * 60 * 60 * 1000;
232
+ const files = readdirSync(auditDir);
233
+ let cleanedCount = 0;
234
+ for (const file of files) {
235
+ if (file.endsWith(".log") || file.endsWith(".jsonl")) {
236
+ const filePath = join2(auditDir, file);
237
+ try {
238
+ const stats = statSync(filePath);
239
+ if (stats.mtimeMs < cutoffTime) {
240
+ unlinkSync(filePath);
241
+ cleanedCount++;
242
+ logger.debug(`Cleaned up old audit log: ${file}`);
243
+ }
244
+ } catch (err) {
245
+ logger.debug(`Failed to check/remove old log file ${file}:`, err);
246
+ }
247
+ }
248
+ }
249
+ if (cleanedCount > 0) {
250
+ logger.info(`Audit log cleanup: removed ${cleanedCount} old log files`);
251
+ }
252
+ } catch (err) {
253
+ logger.error("Failed to cleanup old audit logs:", err);
254
+ }
255
+ }
256
+ record(audit) {
257
+ if (!this.config.enabled) {
258
+ return;
259
+ }
260
+ try {
261
+ const filteredAudit = this.filterAuditData(audit);
262
+ const line = `${JSON.stringify(filteredAudit)}
263
+ `;
264
+ switch (this.config.destination) {
265
+ case "file":
266
+ case "filesystem":
267
+ appendFileSync(this.auditFile, line, { encoding: "utf-8" });
268
+ break;
269
+ case "stdout":
270
+ console.log("AUDIT_LOG:", filteredAudit);
271
+ break;
272
+ default:
273
+ logger.error(`Unsupported audit destination: ${this.config.destination}`);
274
+ return;
275
+ }
276
+ logger.debug("Audit record written:", audit.executionId);
277
+ if (this.config.postLogScript) {
278
+ this.runPostLogScript();
279
+ }
280
+ } catch (err) {
281
+ logger.error("Failed to write audit record:", err);
282
+ }
283
+ }
284
+ runPostLogScript() {
285
+ if (!this.config.postLogScript) {
286
+ return;
287
+ }
288
+ try {
289
+ const child = spawn(this.config.postLogScript, [this.auditFile], {
290
+ detached: true,
291
+ stdio: "ignore"
292
+ });
293
+ child.on("error", (err) => {
294
+ logger.error("Failed to run post-log script:", err);
295
+ });
296
+ child.unref();
297
+ } catch (err) {
298
+ logger.error("Failed to spawn post-log script:", err);
299
+ }
300
+ }
301
+ filterAuditData(audit) {
302
+ const result = {
303
+ executionId: audit.executionId,
304
+ userId: audit.userId,
305
+ timestamp: audit.timestamp,
306
+ runtime: audit.runtime,
307
+ codeHash: audit.codeHash,
308
+ containerId: audit.containerId,
309
+ exitCode: audit.exitCode,
310
+ durationMs: audit.durationMs
311
+ };
312
+ if (audit.resourceUsage !== undefined) {
313
+ result.resourceUsage = audit.resourceUsage;
314
+ }
315
+ if (audit.securityEvents !== undefined) {
316
+ result.securityEvents = audit.securityEvents;
317
+ }
318
+ if (audit.metadata !== undefined) {
319
+ result.metadata = audit.metadata;
320
+ }
321
+ if (this.config.includeCode && audit.code !== undefined) {
322
+ result.code = audit.code;
323
+ }
324
+ if (this.config.includeOutput) {
325
+ if (audit.stdout !== undefined) {
326
+ result.stdout = audit.stdout;
327
+ }
328
+ if (audit.stderr !== undefined) {
329
+ result.stderr = audit.stderr;
330
+ }
331
+ }
332
+ return result;
333
+ }
334
+ }
335
+ var init_audit = __esm(() => {
336
+ init_logger();
337
+ });
338
+
201
339
  // src/engine/concurrency.ts
202
340
  class Semaphore {
203
341
  max;
@@ -356,6 +494,51 @@ var init_pool = __esm(() => {
356
494
  init_logger();
357
495
  });
358
496
 
497
+ // src/engine/stats.ts
498
+ function calculateCPUPercent(stats) {
499
+ const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage;
500
+ const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage;
501
+ if (systemDelta === 0 || cpuDelta === 0) {
502
+ return 0;
503
+ }
504
+ const numCores = stats.cpu_stats.online_cpus ?? stats.cpu_stats.cpu_usage.percpu_usage?.length ?? 1;
505
+ return cpuDelta / systemDelta * numCores * 100;
506
+ }
507
+ function calculateNetworkStats(stats) {
508
+ if (!stats.networks) {
509
+ return { in: 0, out: 0 };
510
+ }
511
+ let rxBytes = 0;
512
+ let txBytes = 0;
513
+ for (const iface of Object.values(stats.networks)) {
514
+ rxBytes += iface.rx_bytes;
515
+ txBytes += iface.tx_bytes;
516
+ }
517
+ return { in: rxBytes, out: txBytes };
518
+ }
519
+ async function getContainerStats(container) {
520
+ const stats = await container.stats({
521
+ stream: false
522
+ });
523
+ const cpuPercent = calculateCPUPercent(stats);
524
+ const memoryBytes = stats.memory_stats.usage;
525
+ const network = calculateNetworkStats(stats);
526
+ return {
527
+ cpuPercent: Math.round(cpuPercent * 100) / 100,
528
+ memoryMB: Math.round(memoryBytes / (1024 * 1024)),
529
+ networkBytesIn: network.in,
530
+ networkBytesOut: network.out
531
+ };
532
+ }
533
+ function calculateResourceDelta(before, after) {
534
+ return {
535
+ cpuPercent: after.cpuPercent,
536
+ memoryMB: after.memoryMB,
537
+ networkBytesIn: after.networkBytesIn - before.networkBytesIn,
538
+ networkBytesOut: after.networkBytesOut - before.networkBytesOut
539
+ };
540
+ }
541
+
359
542
  // src/engine/utils.ts
360
543
  function parseMemoryLimit(limit) {
361
544
  const match = limit.match(/^(\d+(?:\.\d+)?)\s*([kmgt]?)b?$/i);
@@ -451,15 +634,15 @@ var exports_docker = {};
451
634
  __export(exports_docker, {
452
635
  DockerIsol8: () => DockerIsol8
453
636
  });
454
- import { spawn } from "node:child_process";
637
+ import { spawn as spawn2 } from "node:child_process";
455
638
  import { randomUUID } from "node:crypto";
456
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
639
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
457
640
  import { PassThrough } from "node:stream";
458
641
  import Docker from "dockerode";
459
642
  async function writeFileViaExec(container, filePath, content) {
460
643
  const data = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
461
644
  return new Promise((resolve2, reject) => {
462
- const child = spawn("docker", ["exec", "-i", "-u", "sandbox", container.id, "sh", "-c", `cat > ${filePath}`], {
645
+ const child = spawn2("docker", ["exec", "-i", "-u", "sandbox", container.id, "sh", "-c", `cat > ${filePath}`], {
463
646
  stdio: ["pipe", "ignore", "pipe"]
464
647
  });
465
648
  child.on("error", (err) => {
@@ -645,6 +828,7 @@ class DockerIsol8 {
645
828
  tmpSize;
646
829
  security;
647
830
  persist;
831
+ auditLogger;
648
832
  container = null;
649
833
  persistentRuntime = null;
650
834
  pool = null;
@@ -666,6 +850,9 @@ class DockerIsol8 {
666
850
  this.tmpSize = options.tmpSize ?? "256m";
667
851
  this.persist = options.persist ?? false;
668
852
  this.security = options.security ?? { seccomp: "strict" };
853
+ if (options.audit) {
854
+ this.auditLogger = new AuditLogger(options.audit);
855
+ }
669
856
  if (options.debug) {
670
857
  logger.setDebug(true);
671
858
  }
@@ -689,12 +876,79 @@ class DockerIsol8 {
689
876
  }
690
877
  async execute(req) {
691
878
  await this.semaphore.acquire();
879
+ const startTime = Date.now();
692
880
  try {
693
- return this.mode === "persistent" ? await this.executePersistent(req) : await this.executeEphemeral(req);
881
+ const result = this.mode === "persistent" ? await this.executePersistent(req, startTime) : await this.executeEphemeral(req, startTime);
882
+ return result;
694
883
  } finally {
695
884
  this.semaphore.release();
696
885
  }
697
886
  }
887
+ async recordAudit(req, result, startTime, container) {
888
+ try {
889
+ const enc = new TextEncoder;
890
+ const data = enc.encode(req.code);
891
+ const digest = await crypto.subtle.digest("SHA-256", data);
892
+ const codeHash = Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
893
+ let securityEvents;
894
+ if (container && this.network === "filtered") {
895
+ securityEvents = await this.collectSecurityEvents(container);
896
+ if (securityEvents.length === 0) {
897
+ securityEvents = undefined;
898
+ }
899
+ }
900
+ const audit = {
901
+ executionId: result.executionId,
902
+ userId: req.metadata?.userId || "",
903
+ timestamp: new Date(startTime).toISOString(),
904
+ runtime: result.runtime,
905
+ codeHash,
906
+ containerId: result.containerId || "",
907
+ exitCode: result.exitCode,
908
+ durationMs: result.durationMs,
909
+ resourceUsage: result.resourceUsage,
910
+ securityEvents,
911
+ metadata: req.metadata
912
+ };
913
+ this.auditLogger.record(audit);
914
+ } catch (err) {
915
+ logger.error("Failed to record audit log:", err);
916
+ }
917
+ }
918
+ async collectSecurityEvents(container) {
919
+ const events = [];
920
+ try {
921
+ const exec = await container.exec({
922
+ Cmd: ["cat", "/tmp/isol8-proxy/security-events.jsonl"],
923
+ AttachStdout: true,
924
+ AttachStderr: false,
925
+ User: "root"
926
+ });
927
+ const stream = await exec.start({ Tty: false });
928
+ const chunks = [];
929
+ for await (const chunk of stream) {
930
+ chunks.push(chunk);
931
+ }
932
+ const output = Buffer.concat(chunks).toString("utf-8").trim();
933
+ if (output) {
934
+ for (const line of output.split(`
935
+ `)) {
936
+ if (line.trim()) {
937
+ try {
938
+ const event = JSON.parse(line);
939
+ events.push({
940
+ type: event.type || "unknown",
941
+ message: `Security event: ${event.type}`,
942
+ details: event.details || {},
943
+ timestamp: event.timestamp || new Date().toISOString()
944
+ });
945
+ } catch {}
946
+ }
947
+ }
948
+ }
949
+ } catch {}
950
+ return events;
951
+ }
698
952
  async putFile(path, content) {
699
953
  if (!this.container) {
700
954
  throw new Error("No active container. Call execute() first in persistent mode.");
@@ -802,7 +1056,7 @@ class DockerIsol8 {
802
1056
  return adapter.image;
803
1057
  }
804
1058
  }
805
- async executeEphemeral(req) {
1059
+ async executeEphemeral(req, startTime) {
806
1060
  const adapter = this.getAdapter(req.runtime);
807
1061
  const timeoutMs = req.timeoutMs ?? this.defaultTimeoutMs;
808
1062
  const image = await this.resolveImage(adapter);
@@ -821,6 +1075,14 @@ class DockerIsol8 {
821
1075
  });
822
1076
  }
823
1077
  const container = await this.pool.acquire(image);
1078
+ let startStats;
1079
+ if (this.auditLogger) {
1080
+ try {
1081
+ startStats = await getContainerStats(container);
1082
+ } catch (err) {
1083
+ logger.debug("Failed to collect baseline stats:", err);
1084
+ }
1085
+ }
824
1086
  try {
825
1087
  if (this.network === "filtered") {
826
1088
  await startProxy(container, this.networkFilter);
@@ -861,7 +1123,16 @@ class DockerIsol8 {
861
1123
  const { stdout, stderr, truncated } = await this.collectExecOutput(execStream, container, timeoutMs);
862
1124
  const durationMs = Math.round(performance.now() - start);
863
1125
  const inspectResult = await exec.inspect();
864
- return {
1126
+ let resourceUsage;
1127
+ if (startStats) {
1128
+ try {
1129
+ const endStats = await getContainerStats(container);
1130
+ resourceUsage = calculateResourceDelta(startStats, endStats);
1131
+ } catch (err) {
1132
+ logger.debug("Failed to collect final stats:", err);
1133
+ }
1134
+ }
1135
+ const result = {
865
1136
  stdout: this.postProcessOutput(stdout, truncated),
866
1137
  stderr: this.postProcessOutput(stderr, false),
867
1138
  exitCode: inspectResult.ExitCode ?? 1,
@@ -871,8 +1142,13 @@ class DockerIsol8 {
871
1142
  runtime: req.runtime,
872
1143
  timestamp: new Date().toISOString(),
873
1144
  containerId: container.id,
1145
+ ...resourceUsage ? { resourceUsage } : {},
874
1146
  ...req.outputPaths ? { files: await this.retrieveFiles(container, req.outputPaths) } : {}
875
1147
  };
1148
+ if (this.auditLogger) {
1149
+ await this.recordAudit(req, result, startTime, container);
1150
+ }
1151
+ return result;
876
1152
  } finally {
877
1153
  if (this.persist) {
878
1154
  logger.debug(`[Persist] Leaving container running for inspection: ${container.id}`);
@@ -881,7 +1157,7 @@ class DockerIsol8 {
881
1157
  }
882
1158
  }
883
1159
  }
884
- async executePersistent(req) {
1160
+ async executePersistent(req, startTime) {
885
1161
  const adapter = this.getAdapter(req.runtime);
886
1162
  const timeoutMs = req.timeoutMs ?? this.defaultTimeoutMs;
887
1163
  if (!this.container) {
@@ -935,7 +1211,21 @@ class DockerIsol8 {
935
1211
  const { stdout, stderr, truncated } = await this.collectExecOutput(execStream, this.container, timeoutMs);
936
1212
  const durationMs = Math.round(performance.now() - start);
937
1213
  const inspectResult = await exec.inspect();
938
- return {
1214
+ let resourceUsage;
1215
+ if (this.auditLogger) {
1216
+ try {
1217
+ const endStats = await getContainerStats(this.container);
1218
+ resourceUsage = {
1219
+ cpuPercent: endStats.cpuPercent,
1220
+ memoryMB: endStats.memoryMB,
1221
+ networkBytesIn: endStats.networkBytesIn,
1222
+ networkBytesOut: endStats.networkBytesOut
1223
+ };
1224
+ } catch (err) {
1225
+ logger.debug("Failed to collect resource stats:", err);
1226
+ }
1227
+ }
1228
+ const result = {
939
1229
  stdout: this.postProcessOutput(stdout, truncated),
940
1230
  stderr: this.postProcessOutput(stderr, false),
941
1231
  exitCode: inspectResult.ExitCode ?? 1,
@@ -945,8 +1235,13 @@ class DockerIsol8 {
945
1235
  runtime: req.runtime,
946
1236
  timestamp: new Date().toISOString(),
947
1237
  containerId: this.container?.id,
1238
+ ...resourceUsage ? { resourceUsage } : {},
948
1239
  ...req.outputPaths ? { files: await this.retrieveFiles(this.container, req.outputPaths) } : {}
949
1240
  };
1241
+ if (this.auditLogger) {
1242
+ await this.recordAudit(req, result, startTime, this.container);
1243
+ }
1244
+ return result;
950
1245
  }
951
1246
  async retrieveFiles(container, paths) {
952
1247
  const files = {};
@@ -1038,11 +1333,11 @@ class DockerIsol8 {
1038
1333
  }
1039
1334
  loadDefaultSeccompProfile() {
1040
1335
  const devPath = new URL("../../docker/seccomp-profile.json", import.meta.url);
1041
- if (existsSync2(devPath)) {
1336
+ if (existsSync3(devPath)) {
1042
1337
  return readFileSync2(devPath, "utf-8");
1043
1338
  }
1044
1339
  const prodPath = new URL("./docker/seccomp-profile.json", import.meta.url);
1045
- if (existsSync2(prodPath)) {
1340
+ if (existsSync3(prodPath)) {
1046
1341
  return readFileSync2(prodPath, "utf-8");
1047
1342
  }
1048
1343
  logger.warn("Could not locate default seccomp profile. Running without seccomp filter.");
@@ -1248,6 +1543,7 @@ var SANDBOX_WORKDIR = "/sandbox", MAX_OUTPUT_BYTES, PROXY_PORT = 8118, PROXY_STA
1248
1543
  var init_docker = __esm(() => {
1249
1544
  init_runtime();
1250
1545
  init_logger();
1546
+ init_audit();
1251
1547
  init_pool();
1252
1548
  MAX_OUTPUT_BYTES = 1024 * 1024;
1253
1549
  });
@@ -1404,6 +1700,16 @@ var DEFAULT_CONFIG = {
1404
1700
  security: {
1405
1701
  seccomp: "strict"
1406
1702
  },
1703
+ audit: {
1704
+ enabled: false,
1705
+ destination: "filesystem",
1706
+ logDir: undefined,
1707
+ postLogScript: undefined,
1708
+ trackResources: true,
1709
+ retentionDays: 90,
1710
+ includeCode: false,
1711
+ includeOutput: false
1712
+ },
1407
1713
  debug: false
1408
1714
  };
1409
1715
  function loadConfig(cwd) {
@@ -1443,6 +1749,10 @@ function mergeConfig(defaults, overrides) {
1443
1749
  seccomp: overrides.security?.seccomp ?? defaults.security.seccomp,
1444
1750
  customProfilePath: overrides.security?.customProfilePath ?? defaults.security.customProfilePath
1445
1751
  },
1752
+ audit: {
1753
+ ...defaults.audit,
1754
+ ...overrides.audit
1755
+ },
1446
1756
  debug: overrides.debug ?? defaults.debug
1447
1757
  };
1448
1758
  }
@@ -1457,7 +1767,7 @@ init_logger();
1457
1767
  // package.json
1458
1768
  var package_default = {
1459
1769
  name: "isol8",
1460
- version: "0.8.2",
1770
+ version: "0.9.0",
1461
1771
  description: "Secure code execution engine for AI agents",
1462
1772
  author: "Illusion47586",
1463
1773
  license: "MIT",
@@ -1498,6 +1808,7 @@ var package_default = {
1498
1808
  "build:server": "bun run scripts/build-server.ts",
1499
1809
  "build:server:all": "bun run scripts/build-server.ts --all",
1500
1810
  test: "bun test",
1811
+ "test:prod": "bun test tests/production/",
1501
1812
  "lint:check": "ultracite check",
1502
1813
  "lint:fix": "ultracite fix",
1503
1814
  bench: "bunx tsx benchmarks/spawn.ts",
@@ -1619,7 +1930,8 @@ async function createServer(options) {
1619
1930
  sandboxSize: config.defaults.sandboxSize,
1620
1931
  tmpSize: config.defaults.tmpSize,
1621
1932
  ...body.options,
1622
- mode: body.sessionId ? "persistent" : "ephemeral"
1933
+ mode: body.sessionId ? "persistent" : "ephemeral",
1934
+ audit: config.audit
1623
1935
  };
1624
1936
  let engine;
1625
1937
  if (body.sessionId) {
@@ -1792,4 +2104,4 @@ export {
1792
2104
  BunAdapter
1793
2105
  };
1794
2106
 
1795
- //# debugId=D81C2764EAFDC14264756E2164756E21
2107
+ //# debugId=79CD8BB7E6C1C64B64756E2164756E21
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;;GAGG;AACH,QAAA,MAAM,cAAc,EAAE,WAuBrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAepD;AAmCD,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;;GAGG;AACH,QAAA,MAAM,cAAc,EAAE,WAiCrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAepD;AAuCD,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Pluggable audit logger for execution provenance.
3
+ *
4
+ * Records ExecutionAudit objects to various destinations based on configuration.
5
+ * Supports filesystem and stdout logging, with extensibility for cloud services.
6
+ */
7
+ import type { AuditConfig, ExecutionAudit } from "../types";
8
+ export declare class AuditLogger {
9
+ private readonly config;
10
+ private readonly auditFile;
11
+ constructor(config: AuditConfig);
12
+ /**
13
+ * Clean up audit log files older than retentionDays.
14
+ * Checks both the main executions.log and any rotated/archived logs.
15
+ */
16
+ private cleanupOldLogs;
17
+ /**
18
+ * Record an audit entry based on the current configuration.
19
+ */
20
+ record(audit: ExecutionAudit): void;
21
+ /**
22
+ * Run the configured post-log script.
23
+ * The script receives the audit file path as its first argument.
24
+ */
25
+ private runPostLogScript;
26
+ /**
27
+ * Apply privacy filtering to audit data based on configuration.
28
+ */
29
+ private filterAuditData;
30
+ }
31
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/engine/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG5D,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,MAAM,EAAE,WAAW;IAqB/B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAwCtB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAoCnC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAuBxB;;OAEG;IACH,OAAO,CAAC,eAAe;CAuCxB"}
@@ -45,6 +45,7 @@ export declare class DockerIsol8 implements Isol8Engine {
45
45
  private readonly tmpSize;
46
46
  private readonly security;
47
47
  private readonly persist;
48
+ private readonly auditLogger?;
48
49
  private container;
49
50
  private persistentRuntime;
50
51
  private pool;
@@ -65,6 +66,14 @@ export declare class DockerIsol8 implements Isol8Engine {
65
66
  * the concurrency limit, then delegates to ephemeral or persistent execution.
66
67
  */
67
68
  execute(req: ExecutionRequest): Promise<ExecutionResult>;
69
+ /**
70
+ * Record an audit entry for the execution.
71
+ */
72
+ private recordAudit;
73
+ /**
74
+ * Collect security events from the container (e.g., network filter blocks).
75
+ */
76
+ private collectSecurityEvents;
68
77
  /**
69
78
  * Upload a file into the running container via a tar archive.
70
79
  * Only available in persistent mode after at least one `execute()` call.
@@ -1 +1 @@
1
- {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/engine/docker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,MAAM,MAAM,WAAW,CAAC;AAG/B,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,WAAW,EAEX,YAAY,EAIZ,WAAW,EACZ,MAAM,UAAU,CAAC;AAiTlB,2HAA2H;AAC3H,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,WAAY,YAAW,WAAW;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAsB;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAElC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,IAAI,CAA8B;IAE1C;;;OAGG;gBACS,OAAO,GAAE,kBAAuB,EAAE,aAAa,SAAK;IAwBhE;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,kFAAkF;IAC5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B;;;OAGG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAW9D;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpE;;;;;;OAMG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB5C,6GAA6G;IAC7G,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED;;;OAGG;IACI,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,aAAa,CAAC,WAAW,CAAC;YAsFzD,YAAY;YAcZ,gBAAgB;YA0GhB,iBAAiB;YA8FjB,aAAa;YAkBb,oBAAoB;YASpB,wBAAwB;IA4BtC,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,QAAQ;YAwCD,gBAAgB;YA8EjB,iBAAiB;IAiG/B,OAAO,CAAC,iBAAiB;IAYzB;;;;;;;;;;;;;;;;;;;;OAoBG;WACU,OAAO,CAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CA2BlE"}
1
+ {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/engine/docker.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"image-builder.d.ts","sourceRoot":"","sources":["../../../src/engine/image-builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,MAAM,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAmB5C,mDAAmD;AACnD,UAAU,aAAa;IACrB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACtC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,gBAAgB,GAAG,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAmCf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,EACnB,UAAU,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAkBf;AAgED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOrF;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAa/F"}
1
+ {"version":3,"file":"image-builder.d.ts","sourceRoot":"","sources":["../../../src/engine/image-builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,MAAM,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAsB5C,mDAAmD;AACnD,UAAU,aAAa;IACrB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACtC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,gBAAgB,GAAG,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAmCf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,EACnB,UAAU,CAAC,EAAE,gBAAgB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAkBf;AAgED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOrF;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAa/F"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @module engine/stats
3
+ *
4
+ * Resource usage statistics collection from Docker containers.
5
+ * Uses dockerode's container.stats() API to get CPU, memory, and network metrics.
6
+ */
7
+ import type Docker from "dockerode";
8
+ /**
9
+ * Resource usage metrics for a container execution.
10
+ */
11
+ export interface ContainerResourceUsage {
12
+ /** CPU usage as percentage (0-100 * num_cores) */
13
+ cpuPercent: number;
14
+ /** Current memory usage in megabytes */
15
+ memoryMB: number;
16
+ /** Peak memory usage in megabytes (if tracked) */
17
+ peakMemoryMB?: number;
18
+ /** Bytes received during execution */
19
+ networkBytesIn: number;
20
+ /** Bytes sent during execution */
21
+ networkBytesOut: number;
22
+ }
23
+ /**
24
+ * Get resource usage snapshot for a container.
25
+ *
26
+ * @param container - Docker container instance
27
+ * @returns Resource usage metrics
28
+ */
29
+ export declare function getContainerStats(container: Docker.Container): Promise<ContainerResourceUsage>;
30
+ /**
31
+ * Calculate resource usage delta between two stat snapshots.
32
+ * Useful for getting per-execution metrics.
33
+ */
34
+ export declare function calculateResourceDelta(before: ContainerResourceUsage, after: ContainerResourceUsage): ContainerResourceUsage;
35
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../src/engine/stats.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,WAAW,CAAC;AAEpC;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,eAAe,EAAE,MAAM,CAAC;CACzB;AA+ED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,CAAC,SAAS,GAC1B,OAAO,CAAC,sBAAsB,CAAC,CAgBjC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,EAC9B,KAAK,EAAE,sBAAsB,GAC5B,sBAAsB,CAUxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAWD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa;;;;GA+OxD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAWD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa;;;;GAgPxD"}