@wenathlan/saddle 1.8.18 → 2.0.3
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/Dockerfile +1346 -0
- package/README.md +33 -12
- package/alternatives.ts +1218 -0
- package/biome.json +153 -0
- package/boards.json +1517 -0
- package/compute.ts +3144 -0
- package/cores.json +1801 -0
- package/dist/acquisition.d.ts +359 -0
- package/dist/acquisition.d.ts.map +1 -0
- package/dist/acquisition.js +629 -0
- package/dist/acquisition.js.map +1 -0
- package/dist/alternatives.d.ts +509 -0
- package/dist/alternatives.d.ts.map +1 -0
- package/dist/alternatives.js +951 -0
- package/dist/alternatives.js.map +1 -0
- package/dist/automation.d.ts +426 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +561 -0
- package/dist/automation.js.map +1 -0
- package/dist/browser.d.ts +562 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +1057 -0
- package/dist/browser.js.map +1 -0
- package/dist/capacitor.config.d.ts +13 -0
- package/dist/capacitor.config.d.ts.map +1 -0
- package/dist/capacitor.config.js +26 -0
- package/dist/capacitor.config.js.map +1 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/communication.d.ts +192 -0
- package/dist/communication.d.ts.map +1 -0
- package/dist/communication.js +418 -0
- package/dist/communication.js.map +1 -0
- package/dist/compute.d.ts +1161 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +2273 -0
- package/dist/compute.js.map +1 -0
- package/dist/distribution.d.ts +529 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +811 -0
- package/dist/distribution.js.map +1 -0
- package/dist/execution.d.ts +530 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +761 -0
- package/dist/execution.js.map +1 -0
- package/dist/format.d.ts +19 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +60 -0
- package/dist/format.js.map +1 -0
- package/dist/foundation.d.ts +176 -0
- package/dist/foundation.d.ts.map +1 -0
- package/dist/foundation.js +309 -0
- package/dist/foundation.js.map +1 -0
- package/dist/index.d.ts +733 -123
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1018 -123
- package/dist/index.js.map +1 -1
- package/dist/integration.d.ts +236 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +236 -0
- package/dist/integration.js.map +1 -0
- package/dist/intelligence.d.ts +94 -0
- package/dist/intelligence.d.ts.map +1 -0
- package/dist/intelligence.js +145 -0
- package/dist/intelligence.js.map +1 -0
- package/dist/{isolation/contracts.d.ts → isolation.d.ts} +12 -1
- package/dist/isolation.d.ts.map +1 -0
- package/dist/{isolation/contracts.js → isolation.js} +13 -1
- package/dist/isolation.js.map +1 -0
- package/dist/media.d.ts +344 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +986 -0
- package/dist/media.js.map +1 -0
- package/dist/modes.d.ts +359 -0
- package/dist/modes.d.ts.map +1 -0
- package/dist/modes.js +134 -0
- package/dist/modes.js.map +1 -0
- package/dist/{library/public.d.ts → operations.d.ts} +15 -1
- package/dist/operations.d.ts.map +1 -0
- package/dist/{library/public.js → operations.js} +18 -7
- package/dist/operations.js.map +1 -0
- package/dist/orchestrator.d.ts +1339 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +4308 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/packager/targetcli.js +4 -22
- package/dist/performance.d.ts +416 -0
- package/dist/performance.d.ts.map +1 -0
- package/dist/performance.js +827 -0
- package/dist/performance.js.map +1 -0
- package/dist/quantum.d.ts +810 -0
- package/dist/quantum.d.ts.map +1 -0
- package/dist/quantum.js +2073 -0
- package/dist/quantum.js.map +1 -0
- package/dist/render.d.ts +381 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1073 -0
- package/dist/render.js.map +1 -0
- package/dist/scheduler.d.ts +596 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +1259 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/security.d.ts +501 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +1170 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/{server/node.js → server.js} +16 -2
- package/dist/server.js.map +1 -0
- package/dist/tiers.d.ts +1002 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +2165 -0
- package/dist/tiers.js.map +1 -0
- package/dist/virtual.d.ts +641 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +1285 -0
- package/dist/virtual.js.map +1 -0
- package/dist/virtualcpu.d.ts +316 -0
- package/dist/virtualcpu.d.ts.map +1 -0
- package/dist/virtualcpu.js +1180 -0
- package/dist/virtualcpu.js.map +1 -0
- package/dist/virtualgpu.d.ts +345 -0
- package/dist/virtualgpu.d.ts.map +1 -0
- package/dist/virtualgpu.js +1000 -0
- package/dist/virtualgpu.js.map +1 -0
- package/dist/virtualization.d.ts +607 -0
- package/dist/virtualization.d.ts.map +1 -0
- package/dist/virtualization.js +1187 -0
- package/dist/virtualization.js.map +1 -0
- package/dist/virtualmemory.d.ts +818 -0
- package/dist/virtualmemory.d.ts.map +1 -0
- package/dist/virtualmemory.js +1796 -0
- package/dist/virtualmemory.js.map +1 -0
- package/dist/webscrape.d.ts +720 -0
- package/dist/webscrape.d.ts.map +1 -0
- package/dist/webscrape.js +2697 -0
- package/dist/webscrape.js.map +1 -0
- package/docker.config +769 -0
- package/docs/CONVERSA.txt +1523 -0
- package/docs/alternatives.md +499 -0
- package/docs/architecture-1.8.19.md +76 -0
- package/docs/architecture-1.8.19.mmd +33 -0
- package/docs/architecture-1.8.19.png +0 -0
- package/docs/architecture.md +355 -0
- package/docs/brancharchive-2026-08-18.md +18 -0
- package/docs/browser.md +7 -0
- package/docs/consolidation.md +70 -0
- package/docs/e2ugh-engine.md +528 -0
- package/{dist/examples/localjob.js → docs/example-localjob.ts} +3 -3
- package/{dist/examples/publicapi.js → docs/example-publicapi.ts} +2 -2
- package/docs/hardware.md +342 -0
- package/docs/optimization.md +126 -0
- package/docs/performance.md +499 -0
- package/docs/planning.1.8.18.md +94 -0
- package/docs/planning.1.8.19.md +49 -0
- package/docs/releasenotes-1.8.19.md +35 -0
- package/docs/research-1.8.19-virtual-browser.md +27 -0
- package/docs/security.md +454 -0
- package/docs/todo-1.8.18.md +12 -0
- package/docs/todo-1.8.19.md +241 -0
- package/docs/viability.md +777 -0
- package/docs/virtualization.md +335 -0
- package/docs/web-duplicatecleanup.md +9 -0
- package/docs/web-ideas.md +106 -0
- package/docs/workflowimprovements-2026-08-19.md +70 -0
- package/gpumonitor.cpp +1543 -0
- package/gpus.json +2408 -0
- package/index.ts +1478 -0
- package/media.ts +1337 -0
- package/mttg.config +1549 -0
- package/orchestrator.ts +5462 -0
- package/package.json +304 -56
- package/passage.config +1331 -0
- package/performance.ts +1049 -0
- package/processors.json +2217 -0
- package/qemu.config +1062 -0
- package/qemubridge.py +1340 -0
- package/quantum.ts +2393 -0
- package/render.ts +1325 -0
- package/scheduler.ts +1600 -0
- package/security.ts +1449 -0
- package/tiers.ts +2871 -0
- package/tsconfig.json +29 -0
- package/virtualcpu.ts +1273 -0
- package/virtualgpu.ts +1181 -0
- package/virtualhardware.c +1011 -0
- package/virtualhardware.json +739 -0
- package/virtualization.ts +1628 -0
- package/virtualizationcore.cpp +4635 -0
- package/virtualmemory.ts +2303 -0
- package/vm.config.json +1910 -0
- package/{extension → web/extension}/manifest.json +1 -1
- package/web/readme.md +382 -0
- package/web/tsconfig.json +25 -0
- package/dist/adapters/forge.d.ts +0 -17
- package/dist/adapters/forge.d.ts.map +0 -1
- package/dist/adapters/forge.js +0 -19
- package/dist/adapters/forge.js.map +0 -1
- package/dist/adapters/forgejo.d.ts +0 -49
- package/dist/adapters/forgejo.d.ts.map +0 -1
- package/dist/adapters/forgejo.js +0 -8
- package/dist/adapters/forgejo.js.map +0 -1
- package/dist/adapters/github.d.ts +0 -12
- package/dist/adapters/github.d.ts.map +0 -1
- package/dist/adapters/github.js +0 -20
- package/dist/adapters/github.js.map +0 -1
- package/dist/adapters/gitlab.d.ts +0 -17
- package/dist/adapters/gitlab.d.ts.map +0 -1
- package/dist/adapters/gitlab.js +0 -10
- package/dist/adapters/gitlab.js.map +0 -1
- package/dist/adapters/huggingface.d.ts +0 -17
- package/dist/adapters/huggingface.d.ts.map +0 -1
- package/dist/adapters/huggingface.js +0 -6
- package/dist/adapters/huggingface.js.map +0 -1
- package/dist/adapters/socket.d.ts +0 -11
- package/dist/adapters/socket.d.ts.map +0 -1
- package/dist/adapters/socket.js +0 -17
- package/dist/adapters/socket.js.map +0 -1
- package/dist/adapters/transport.d.ts +0 -7
- package/dist/adapters/transport.d.ts.map +0 -1
- package/dist/adapters/transport.js +0 -36
- package/dist/adapters/transport.js.map +0 -1
- package/dist/ai/chunk.d.ts +0 -2
- package/dist/ai/chunk.d.ts.map +0 -1
- package/dist/ai/chunk.js +0 -36
- package/dist/ai/chunk.js.map +0 -1
- package/dist/ai/llmstxt.d.ts +0 -6
- package/dist/ai/llmstxt.d.ts.map +0 -1
- package/dist/ai/llmstxt.js +0 -12
- package/dist/ai/llmstxt.js.map +0 -1
- package/dist/ai/provenance.d.ts +0 -22
- package/dist/ai/provenance.d.ts.map +0 -1
- package/dist/ai/provenance.js +0 -25
- package/dist/ai/provenance.js.map +0 -1
- package/dist/ai/rag.d.ts +0 -26
- package/dist/ai/rag.d.ts.map +0 -1
- package/dist/ai/rag.js +0 -22
- package/dist/ai/rag.js.map +0 -1
- package/dist/ai/tokens.d.ts +0 -15
- package/dist/ai/tokens.d.ts.map +0 -1
- package/dist/ai/tokens.js +0 -9
- package/dist/ai/tokens.js.map +0 -1
- package/dist/api/auth.d.ts +0 -16
- package/dist/api/auth.d.ts.map +0 -1
- package/dist/api/auth.js +0 -19
- package/dist/api/auth.js.map +0 -1
- package/dist/api/contracts.d.ts +0 -28
- package/dist/api/contracts.d.ts.map +0 -1
- package/dist/api/contracts.js +0 -14
- package/dist/api/contracts.js.map +0 -1
- package/dist/api/control.d.ts +0 -10
- package/dist/api/control.d.ts.map +0 -1
- package/dist/api/control.js +0 -35
- package/dist/api/control.js.map +0 -1
- package/dist/api/http.d.ts +0 -7
- package/dist/api/http.d.ts.map +0 -1
- package/dist/api/http.js +0 -12
- package/dist/api/http.js.map +0 -1
- package/dist/api/rate.d.ts +0 -28
- package/dist/api/rate.d.ts.map +0 -1
- package/dist/api/rate.js +0 -36
- package/dist/api/rate.js.map +0 -1
- package/dist/api/security.d.ts +0 -12
- package/dist/api/security.d.ts.map +0 -1
- package/dist/api/security.js +0 -56
- package/dist/api/security.js.map +0 -1
- package/dist/api/service.d.ts +0 -5
- package/dist/api/service.d.ts.map +0 -1
- package/dist/api/service.js +0 -70
- package/dist/api/service.js.map +0 -1
- package/dist/apps/registry.d.ts +0 -33
- package/dist/apps/registry.d.ts.map +0 -1
- package/dist/apps/registry.js +0 -24
- package/dist/apps/registry.js.map +0 -1
- package/dist/binary/archive.d.ts +0 -27
- package/dist/binary/archive.d.ts.map +0 -1
- package/dist/binary/archive.js +0 -46
- package/dist/binary/archive.js.map +0 -1
- package/dist/binary/build.d.ts +0 -29
- package/dist/binary/build.d.ts.map +0 -1
- package/dist/binary/build.js +0 -17
- package/dist/binary/build.js.map +0 -1
- package/dist/binary/transform.d.ts +0 -70
- package/dist/binary/transform.d.ts.map +0 -1
- package/dist/binary/transform.js +0 -105
- package/dist/binary/transform.js.map +0 -1
- package/dist/bot/adapter.d.ts +0 -5
- package/dist/bot/adapter.d.ts.map +0 -1
- package/dist/bot/adapter.js +0 -11
- package/dist/bot/adapter.js.map +0 -1
- package/dist/bot/bot.d.ts +0 -28
- package/dist/bot/bot.d.ts.map +0 -1
- package/dist/bot/bot.js +0 -52
- package/dist/bot/bot.js.map +0 -1
- package/dist/bot/commands.d.ts +0 -8
- package/dist/bot/commands.d.ts.map +0 -1
- package/dist/bot/commands.js +0 -19
- package/dist/bot/commands.js.map +0 -1
- package/dist/bot/permissions.d.ts +0 -13
- package/dist/bot/permissions.d.ts.map +0 -1
- package/dist/bot/permissions.js +0 -16
- package/dist/bot/permissions.js.map +0 -1
- package/dist/browser/actions.d.ts +0 -33
- package/dist/browser/actions.d.ts.map +0 -1
- package/dist/browser/actions.js +0 -44
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/agent.d.ts +0 -21
- package/dist/browser/agent.d.ts.map +0 -1
- package/dist/browser/agent.js +0 -13
- package/dist/browser/agent.js.map +0 -1
- package/dist/browser/context.d.ts +0 -43
- package/dist/browser/context.d.ts.map +0 -1
- package/dist/browser/context.js +0 -54
- package/dist/browser/context.js.map +0 -1
- package/dist/browser/fingerprint.d.ts +0 -4
- package/dist/browser/fingerprint.d.ts.map +0 -1
- package/dist/browser/fingerprint.js +0 -12
- package/dist/browser/fingerprint.js.map +0 -1
- package/dist/browser/index.d.ts +0 -11
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -11
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/playwright.d.ts +0 -11
- package/dist/browser/playwright.d.ts.map +0 -1
- package/dist/browser/playwright.js +0 -24
- package/dist/browser/playwright.js.map +0 -1
- package/dist/browser/recorder.d.ts +0 -21
- package/dist/browser/recorder.d.ts.map +0 -1
- package/dist/browser/recorder.js +0 -58
- package/dist/browser/recorder.js.map +0 -1
- package/dist/browser/session.d.ts +0 -14
- package/dist/browser/session.d.ts.map +0 -1
- package/dist/browser/session.js +0 -21
- package/dist/browser/session.js.map +0 -1
- package/dist/browser/snapshot.d.ts +0 -69
- package/dist/browser/snapshot.d.ts.map +0 -1
- package/dist/browser/snapshot.js +0 -133
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/captcha/contract.d.ts +0 -35
- package/dist/captcha/contract.d.ts.map +0 -1
- package/dist/captcha/contract.js +0 -17
- package/dist/captcha/contract.js.map +0 -1
- package/dist/captcha/evidence.d.ts +0 -10
- package/dist/captcha/evidence.d.ts.map +0 -1
- package/dist/captcha/evidence.js +0 -9
- package/dist/captcha/evidence.js.map +0 -1
- package/dist/captcha/guard.d.ts +0 -23
- package/dist/captcha/guard.d.ts.map +0 -1
- package/dist/captcha/guard.js +0 -12
- package/dist/captcha/guard.js.map +0 -1
- package/dist/cli/main.d.ts +0 -4
- package/dist/cli/main.d.ts.map +0 -1
- package/dist/cli/main.js +0 -46
- package/dist/cli/main.js.map +0 -1
- package/dist/core/errors.d.ts +0 -84
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/errors.js +0 -54
- package/dist/core/errors.js.map +0 -1
- package/dist/core/events.d.ts +0 -9
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js +0 -21
- package/dist/core/events.js.map +0 -1
- package/dist/core/hash.d.ts +0 -10
- package/dist/core/hash.d.ts.map +0 -1
- package/dist/core/hash.js +0 -82
- package/dist/core/hash.js.map +0 -1
- package/dist/core/ids.d.ts +0 -10
- package/dist/core/ids.d.ts.map +0 -1
- package/dist/core/ids.js +0 -15
- package/dist/core/ids.js.map +0 -1
- package/dist/delivery/manifest.d.ts +0 -35
- package/dist/delivery/manifest.d.ts.map +0 -1
- package/dist/delivery/manifest.js +0 -68
- package/dist/delivery/manifest.js.map +0 -1
- package/dist/deploy/index.d.ts +0 -6
- package/dist/deploy/index.d.ts.map +0 -1
- package/dist/deploy/index.js +0 -6
- package/dist/deploy/index.js.map +0 -1
- package/dist/dispatch/resumable.d.ts +0 -102
- package/dist/dispatch/resumable.d.ts.map +0 -1
- package/dist/dispatch/resumable.js +0 -64
- package/dist/dispatch/resumable.js.map +0 -1
- package/dist/dispatch/workflow.d.ts +0 -6
- package/dist/dispatch/workflow.d.ts.map +0 -1
- package/dist/dispatch/workflow.js +0 -37
- package/dist/dispatch/workflow.js.map +0 -1
- package/dist/domain/artifacts.d.ts +0 -12
- package/dist/domain/artifacts.d.ts.map +0 -1
- package/dist/domain/artifacts.js +0 -14
- package/dist/domain/artifacts.js.map +0 -1
- package/dist/domain/jobs.d.ts +0 -12
- package/dist/domain/jobs.d.ts.map +0 -1
- package/dist/domain/jobs.js +0 -20
- package/dist/domain/jobs.js.map +0 -1
- package/dist/domain/providers.d.ts +0 -10
- package/dist/domain/providers.d.ts.map +0 -1
- package/dist/domain/providers.js +0 -8
- package/dist/domain/providers.js.map +0 -1
- package/dist/domain/runtime.d.ts +0 -14
- package/dist/domain/runtime.d.ts.map +0 -1
- package/dist/domain/runtime.js +0 -10
- package/dist/domain/runtime.js.map +0 -1
- package/dist/domain/sessions.d.ts +0 -12
- package/dist/domain/sessions.d.ts.map +0 -1
- package/dist/domain/sessions.js +0 -57
- package/dist/domain/sessions.js.map +0 -1
- package/dist/examples/localjob.d.ts +0 -2
- package/dist/examples/localjob.d.ts.map +0 -1
- package/dist/examples/localjob.js.map +0 -1
- package/dist/examples/publicapi.d.ts +0 -2
- package/dist/examples/publicapi.d.ts.map +0 -1
- package/dist/examples/publicapi.js.map +0 -1
- package/dist/extension/build.d.ts +0 -9
- package/dist/extension/build.d.ts.map +0 -1
- package/dist/extension/build.js +0 -85
- package/dist/extension/build.js.map +0 -1
- package/dist/extension/content.d.ts +0 -6
- package/dist/extension/content.d.ts.map +0 -1
- package/dist/extension/content.js +0 -135
- package/dist/extension/content.js.map +0 -1
- package/dist/extension/index.d.ts +0 -7
- package/dist/extension/index.d.ts.map +0 -1
- package/dist/extension/index.js +0 -7
- package/dist/extension/index.js.map +0 -1
- package/dist/extension/pagebridge.d.ts +0 -6
- package/dist/extension/pagebridge.d.ts.map +0 -1
- package/dist/extension/pagebridge.js +0 -42
- package/dist/extension/pagebridge.js.map +0 -1
- package/dist/extension/permissions.d.ts +0 -26
- package/dist/extension/permissions.d.ts.map +0 -1
- package/dist/extension/permissions.js +0 -28
- package/dist/extension/permissions.js.map +0 -1
- package/dist/extension/popup.d.ts +0 -5
- package/dist/extension/popup.d.ts.map +0 -1
- package/dist/extension/popup.js +0 -26
- package/dist/extension/popup.js.map +0 -1
- package/dist/extension/protocol.d.ts +0 -58
- package/dist/extension/protocol.d.ts.map +0 -1
- package/dist/extension/protocol.js +0 -101
- package/dist/extension/protocol.js.map +0 -1
- package/dist/extension/serviceworker.d.ts +0 -25
- package/dist/extension/serviceworker.d.ts.map +0 -1
- package/dist/extension/serviceworker.js +0 -102
- package/dist/extension/serviceworker.js.map +0 -1
- package/dist/extension/worker.d.ts +0 -28
- package/dist/extension/worker.d.ts.map +0 -1
- package/dist/extension/worker.js +0 -22
- package/dist/extension/worker.js.map +0 -1
- package/dist/format/check.d.ts +0 -8
- package/dist/format/check.d.ts.map +0 -1
- package/dist/format/check.js +0 -38
- package/dist/format/check.js.map +0 -1
- package/dist/isolation/contracts.d.ts.map +0 -1
- package/dist/isolation/contracts.js.map +0 -1
- package/dist/library/public.d.ts.map +0 -1
- package/dist/library/public.js.map +0 -1
- package/dist/mcp/browser.d.ts +0 -9
- package/dist/mcp/browser.d.ts.map +0 -1
- package/dist/mcp/browser.js +0 -13
- package/dist/mcp/browser.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -35
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -40
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/transport.d.ts +0 -7
- package/dist/mcp/transport.d.ts.map +0 -1
- package/dist/mcp/transport.js +0 -17
- package/dist/mcp/transport.js.map +0 -1
- package/dist/memory/bridge.d.ts +0 -14
- package/dist/memory/bridge.d.ts.map +0 -1
- package/dist/memory/bridge.js +0 -16
- package/dist/memory/bridge.js.map +0 -1
- package/dist/memory/engine.d.ts +0 -38
- package/dist/memory/engine.d.ts.map +0 -1
- package/dist/memory/engine.js +0 -109
- package/dist/memory/engine.js.map +0 -1
- package/dist/memory/modes.d.ts +0 -43
- package/dist/memory/modes.d.ts.map +0 -1
- package/dist/memory/modes.js +0 -66
- package/dist/memory/modes.js.map +0 -1
- package/dist/memory/objects.d.ts +0 -21
- package/dist/memory/objects.d.ts.map +0 -1
- package/dist/memory/objects.js +0 -20
- package/dist/memory/objects.js.map +0 -1
- package/dist/memory/planner.d.ts +0 -66
- package/dist/memory/planner.d.ts.map +0 -1
- package/dist/memory/planner.js +0 -108
- package/dist/memory/planner.js.map +0 -1
- package/dist/memory/targets.d.ts +0 -78
- package/dist/memory/targets.d.ts.map +0 -1
- package/dist/memory/targets.js +0 -29
- package/dist/memory/targets.js.map +0 -1
- package/dist/memory/transforms.d.ts +0 -19
- package/dist/memory/transforms.d.ts.map +0 -1
- package/dist/memory/transforms.js +0 -14
- package/dist/memory/transforms.js.map +0 -1
- package/dist/modes/matrix.d.ts +0 -23
- package/dist/modes/matrix.d.ts.map +0 -1
- package/dist/modes/matrix.js +0 -18
- package/dist/modes/matrix.js.map +0 -1
- package/dist/modes/modes.d.ts +0 -33
- package/dist/modes/modes.d.ts.map +0 -1
- package/dist/modes/modes.js +0 -15
- package/dist/modes/modes.js.map +0 -1
- package/dist/modes/resolve.d.ts +0 -96
- package/dist/modes/resolve.d.ts.map +0 -1
- package/dist/modes/resolve.js +0 -48
- package/dist/modes/resolve.js.map +0 -1
- package/dist/observability/metrics.d.ts +0 -14
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/metrics.js +0 -18
- package/dist/observability/metrics.js.map +0 -1
- package/dist/packager/manifest.d.ts +0 -66
- package/dist/packager/manifest.d.ts.map +0 -1
- package/dist/packager/manifest.js +0 -94
- package/dist/packager/manifest.js.map +0 -1
- package/dist/packager/publish.d.ts +0 -51
- package/dist/packager/publish.d.ts.map +0 -1
- package/dist/packager/publish.js +0 -18
- package/dist/packager/publish.js.map +0 -1
- package/dist/packager/targetcli.d.ts +0 -7
- package/dist/packager/targetcli.d.ts.map +0 -1
- package/dist/packager/targetcli.js.map +0 -1
- package/dist/persistence/adapter.d.ts +0 -5
- package/dist/persistence/adapter.d.ts.map +0 -1
- package/dist/persistence/adapter.js +0 -11
- package/dist/persistence/adapter.js.map +0 -1
- package/dist/persistence/drizzle.d.ts +0 -2
- package/dist/persistence/drizzle.d.ts.map +0 -1
- package/dist/persistence/drizzle.js +0 -12
- package/dist/persistence/drizzle.js.map +0 -1
- package/dist/persistence/memory.d.ts +0 -2
- package/dist/persistence/memory.d.ts.map +0 -1
- package/dist/persistence/memory.js +0 -27
- package/dist/persistence/memory.js.map +0 -1
- package/dist/persistence/migrations.d.ts +0 -19
- package/dist/persistence/migrations.d.ts.map +0 -1
- package/dist/persistence/migrations.js +0 -13
- package/dist/persistence/migrations.js.map +0 -1
- package/dist/persistence/prisma.d.ts +0 -2
- package/dist/persistence/prisma.d.ts.map +0 -1
- package/dist/persistence/prisma.js +0 -25
- package/dist/persistence/prisma.js.map +0 -1
- package/dist/persistence/schema.d.ts +0 -63
- package/dist/persistence/schema.d.ts.map +0 -1
- package/dist/persistence/schema.js +0 -28
- package/dist/persistence/schema.js.map +0 -1
- package/dist/persistence/sql.d.ts +0 -3
- package/dist/persistence/sql.d.ts.map +0 -1
- package/dist/persistence/sql.js +0 -37
- package/dist/persistence/sql.js.map +0 -1
- package/dist/protocol/blocks.d.ts +0 -10
- package/dist/protocol/blocks.d.ts.map +0 -1
- package/dist/protocol/blocks.js +0 -26
- package/dist/protocol/blocks.js.map +0 -1
- package/dist/protocol/json.d.ts +0 -6
- package/dist/protocol/json.d.ts.map +0 -1
- package/dist/protocol/json.js +0 -6
- package/dist/protocol/json.js.map +0 -1
- package/dist/protocol/ndjson.d.ts +0 -3
- package/dist/protocol/ndjson.d.ts.map +0 -1
- package/dist/protocol/ndjson.js +0 -20
- package/dist/protocol/ndjson.js.map +0 -1
- package/dist/protocol/sse.d.ts +0 -7
- package/dist/protocol/sse.d.ts.map +0 -1
- package/dist/protocol/sse.js +0 -25
- package/dist/protocol/sse.js.map +0 -1
- package/dist/proxy/pool.d.ts +0 -9
- package/dist/proxy/pool.d.ts.map +0 -1
- package/dist/proxy/pool.js +0 -27
- package/dist/proxy/pool.js.map +0 -1
- package/dist/queue/idempotency.d.ts +0 -10
- package/dist/queue/idempotency.d.ts.map +0 -1
- package/dist/queue/idempotency.js +0 -13
- package/dist/queue/idempotency.js.map +0 -1
- package/dist/queue/persistent.d.ts +0 -14
- package/dist/queue/persistent.d.ts.map +0 -1
- package/dist/queue/persistent.js +0 -66
- package/dist/queue/persistent.js.map +0 -1
- package/dist/queue/queue.d.ts +0 -7
- package/dist/queue/queue.d.ts.map +0 -1
- package/dist/queue/queue.js +0 -55
- package/dist/queue/queue.js.map +0 -1
- package/dist/queue/saga.d.ts +0 -5
- package/dist/queue/saga.d.ts.map +0 -1
- package/dist/queue/saga.js +0 -20
- package/dist/queue/saga.js.map +0 -1
- package/dist/release/assets.d.ts +0 -144
- package/dist/release/assets.d.ts.map +0 -1
- package/dist/release/assets.js +0 -157
- package/dist/release/assets.js.map +0 -1
- package/dist/release/evidence.d.ts +0 -92
- package/dist/release/evidence.d.ts.map +0 -1
- package/dist/release/evidence.js +0 -148
- package/dist/release/evidence.js.map +0 -1
- package/dist/release/verify.d.ts +0 -12
- package/dist/release/verify.d.ts.map +0 -1
- package/dist/release/verify.js +0 -112
- package/dist/release/verify.js.map +0 -1
- package/dist/runners/chain.d.ts +0 -125
- package/dist/runners/chain.d.ts.map +0 -1
- package/dist/runners/chain.js +0 -95
- package/dist/runners/chain.js.map +0 -1
- package/dist/runners/health.d.ts +0 -36
- package/dist/runners/health.d.ts.map +0 -1
- package/dist/runners/health.js +0 -26
- package/dist/runners/health.js.map +0 -1
- package/dist/runners/heartbeat.d.ts +0 -23
- package/dist/runners/heartbeat.d.ts.map +0 -1
- package/dist/runners/heartbeat.js +0 -29
- package/dist/runners/heartbeat.js.map +0 -1
- package/dist/runners/inprocess.d.ts +0 -17
- package/dist/runners/inprocess.d.ts.map +0 -1
- package/dist/runners/inprocess.js +0 -20
- package/dist/runners/inprocess.js.map +0 -1
- package/dist/runners/scheduler.d.ts +0 -5
- package/dist/runners/scheduler.d.ts.map +0 -1
- package/dist/runners/scheduler.js +0 -19
- package/dist/runners/scheduler.js.map +0 -1
- package/dist/runtime/abort.d.ts +0 -8
- package/dist/runtime/abort.d.ts.map +0 -1
- package/dist/runtime/abort.js +0 -13
- package/dist/runtime/abort.js.map +0 -1
- package/dist/runtime/compatibility.d.ts +0 -37
- package/dist/runtime/compatibility.d.ts.map +0 -1
- package/dist/runtime/compatibility.js +0 -10
- package/dist/runtime/compatibility.js.map +0 -1
- package/dist/runtime/detect.d.ts +0 -13
- package/dist/runtime/detect.d.ts.map +0 -1
- package/dist/runtime/detect.js +0 -18
- package/dist/runtime/detect.js.map +0 -1
- package/dist/runtime/engine.d.ts +0 -20
- package/dist/runtime/engine.d.ts.map +0 -1
- package/dist/runtime/engine.js +0 -61
- package/dist/runtime/engine.js.map +0 -1
- package/dist/runtime/retry.d.ts +0 -19
- package/dist/runtime/retry.d.ts.map +0 -1
- package/dist/runtime/retry.js +0 -54
- package/dist/runtime/retry.js.map +0 -1
- package/dist/runtime/worker.d.ts +0 -10
- package/dist/runtime/worker.d.ts.map +0 -1
- package/dist/runtime/worker.js +0 -25
- package/dist/runtime/worker.js.map +0 -1
- package/dist/scrape/cache.d.ts +0 -45
- package/dist/scrape/cache.d.ts.map +0 -1
- package/dist/scrape/cache.js +0 -45
- package/dist/scrape/cache.js.map +0 -1
- package/dist/scrape/crawl.d.ts +0 -47
- package/dist/scrape/crawl.d.ts.map +0 -1
- package/dist/scrape/crawl.js +0 -114
- package/dist/scrape/crawl.js.map +0 -1
- package/dist/scrape/extract.d.ts +0 -63
- package/dist/scrape/extract.d.ts.map +0 -1
- package/dist/scrape/extract.js +0 -47
- package/dist/scrape/extract.js.map +0 -1
- package/dist/scrape/normalize.d.ts +0 -52
- package/dist/scrape/normalize.d.ts.map +0 -1
- package/dist/scrape/normalize.js +0 -102
- package/dist/scrape/normalize.js.map +0 -1
- package/dist/scrape/robots.d.ts +0 -37
- package/dist/scrape/robots.d.ts.map +0 -1
- package/dist/scrape/robots.js +0 -71
- package/dist/scrape/robots.js.map +0 -1
- package/dist/scrape/schema.d.ts +0 -23
- package/dist/scrape/schema.d.ts.map +0 -1
- package/dist/scrape/schema.js +0 -92
- package/dist/scrape/schema.js.map +0 -1
- package/dist/scrape/scraper.d.ts +0 -6
- package/dist/scrape/scraper.d.ts.map +0 -1
- package/dist/scrape/scraper.js +0 -46
- package/dist/scrape/scraper.js.map +0 -1
- package/dist/scrape/semantic.d.ts +0 -25
- package/dist/scrape/semantic.d.ts.map +0 -1
- package/dist/scrape/semantic.js +0 -27
- package/dist/scrape/semantic.js.map +0 -1
- package/dist/server/node.d.ts +0 -7
- package/dist/server/node.d.ts.map +0 -1
- package/dist/server/node.js.map +0 -1
- package/dist/sessions/file.d.ts +0 -25
- package/dist/sessions/file.d.ts.map +0 -1
- package/dist/sessions/file.js +0 -13
- package/dist/sessions/file.js.map +0 -1
- package/dist/sessions/replay.d.ts +0 -9
- package/dist/sessions/replay.d.ts.map +0 -1
- package/dist/sessions/replay.js +0 -70
- package/dist/sessions/replay.js.map +0 -1
- package/dist/sessions/store.d.ts +0 -25
- package/dist/sessions/store.d.ts.map +0 -1
- package/dist/sessions/store.js +0 -13
- package/dist/sessions/store.js.map +0 -1
- package/dist/storage/adapter.d.ts +0 -5
- package/dist/storage/adapter.d.ts.map +0 -1
- package/dist/storage/adapter.js +0 -11
- package/dist/storage/adapter.js.map +0 -1
- package/dist/storage/cache.d.ts +0 -23
- package/dist/storage/cache.d.ts.map +0 -1
- package/dist/storage/cache.js +0 -79
- package/dist/storage/cache.js.map +0 -1
- package/dist/storage/checksum.d.ts +0 -3
- package/dist/storage/checksum.d.ts.map +0 -1
- package/dist/storage/checksum.js +0 -19
- package/dist/storage/checksum.js.map +0 -1
- package/dist/storage/chunked.d.ts +0 -21
- package/dist/storage/chunked.d.ts.map +0 -1
- package/dist/storage/chunked.js +0 -67
- package/dist/storage/chunked.js.map +0 -1
- package/dist/storage/content.d.ts +0 -24
- package/dist/storage/content.d.ts.map +0 -1
- package/dist/storage/content.js +0 -44
- package/dist/storage/content.js.map +0 -1
- package/dist/storage/filehosting.d.ts +0 -2
- package/dist/storage/filehosting.d.ts.map +0 -1
- package/dist/storage/filehosting.js +0 -24
- package/dist/storage/filehosting.js.map +0 -1
- package/dist/storage/githubcontents.d.ts +0 -2
- package/dist/storage/githubcontents.d.ts.map +0 -1
- package/dist/storage/githubcontents.js +0 -29
- package/dist/storage/githubcontents.js.map +0 -1
- package/dist/storage/index.d.ts +0 -12
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js +0 -12
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/local.d.ts +0 -2
- package/dist/storage/local.d.ts.map +0 -1
- package/dist/storage/local.js +0 -72
- package/dist/storage/local.js.map +0 -1
- package/dist/storage/memory.d.ts +0 -6
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js +0 -36
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/pool.d.ts +0 -95
- package/dist/storage/pool.d.ts.map +0 -1
- package/dist/storage/pool.js +0 -202
- package/dist/storage/pool.js.map +0 -1
- package/dist/storage/s3compatible.d.ts +0 -3
- package/dist/storage/s3compatible.d.ts.map +0 -1
- package/dist/storage/s3compatible.js +0 -70
- package/dist/storage/s3compatible.js.map +0 -1
- package/dist/storage/sync.d.ts +0 -47
- package/dist/storage/sync.d.ts.map +0 -1
- package/dist/storage/sync.js +0 -75
- package/dist/storage/sync.js.map +0 -1
- package/dist/surfaces/adapters.d.ts +0 -97
- package/dist/surfaces/adapters.d.ts.map +0 -1
- package/dist/surfaces/adapters.js +0 -50
- package/dist/surfaces/adapters.js.map +0 -1
- package/dist/surfaces/controls.d.ts +0 -18
- package/dist/surfaces/controls.d.ts.map +0 -1
- package/dist/surfaces/controls.js +0 -45
- package/dist/surfaces/controls.js.map +0 -1
- package/dist/surfaces/manifest.d.ts +0 -68
- package/dist/surfaces/manifest.d.ts.map +0 -1
- package/dist/surfaces/manifest.js +0 -42
- package/dist/surfaces/manifest.js.map +0 -1
- package/dist/surfaces/n8n.d.ts +0 -25
- package/dist/surfaces/n8n.d.ts.map +0 -1
- package/dist/surfaces/n8n.js +0 -31
- package/dist/surfaces/n8n.js.map +0 -1
- package/dist/surfaces/operations.d.ts +0 -32
- package/dist/surfaces/operations.d.ts.map +0 -1
- package/dist/surfaces/operations.js +0 -53
- package/dist/surfaces/operations.js.map +0 -1
- package/dist/surfaces/requirements.d.ts +0 -29
- package/dist/surfaces/requirements.d.ts.map +0 -1
- package/dist/surfaces/requirements.js +0 -50
- package/dist/surfaces/requirements.js.map +0 -1
- package/dist/surfaces/targets.d.ts +0 -196
- package/dist/surfaces/targets.d.ts.map +0 -1
- package/dist/surfaces/targets.js +0 -27
- package/dist/surfaces/targets.js.map +0 -1
- package/dist/webhook/delivery.d.ts +0 -18
- package/dist/webhook/delivery.d.ts.map +0 -1
- package/dist/webhook/delivery.js +0 -36
- package/dist/webhook/delivery.js.map +0 -1
- package/dist/webhook/receiver.d.ts +0 -26
- package/dist/webhook/receiver.d.ts.map +0 -1
- package/dist/webhook/receiver.js +0 -24
- package/dist/webhook/receiver.js.map +0 -1
- package/dist/webhook/signature.d.ts +0 -3
- package/dist/webhook/signature.d.ts.map +0 -1
- package/dist/webhook/signature.js +0 -7
- package/dist/webhook/signature.js.map +0 -1
- package/dist/workflow/manifest.d.ts +0 -22
- package/dist/workflow/manifest.d.ts.map +0 -1
- package/dist/workflow/manifest.js +0 -21
- package/dist/workflow/manifest.js.map +0 -1
- package/dist/workflow/registry.d.ts +0 -7
- package/dist/workflow/registry.d.ts.map +0 -1
- package/dist/workflow/registry.js +0 -18
- package/dist/workflow/registry.js.map +0 -1
- package/dist/workflow/templates.d.ts +0 -6
- package/dist/workflow/templates.d.ts.map +0 -1
- package/dist/workflow/templates.js +0 -18
- package/dist/workflow/templates.js.map +0 -1
- package/dist/workflow/triggers.d.ts +0 -64
- package/dist/workflow/triggers.d.ts.map +0 -1
- package/dist/workflow/triggers.js +0 -99
- package/dist/workflow/triggers.js.map +0 -1
- /package/{extension/README.md → docs/extension.md} +0 -0
package/alternatives.ts
ADDED
|
@@ -0,0 +1,1218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* alternatives.ts - Virtual Hardware Engine v5
|
|
3
|
+
*
|
|
4
|
+
* Ten categories of alternative sandbox engines, each one wired through a
|
|
5
|
+
* real adapter implementing the shared SandboxEngine interface
|
|
6
|
+
* (create/exec/snapshot/destroy/metrics). A registry with factories and
|
|
7
|
+
* alias resolution lets hosts swap execution backends without touching
|
|
8
|
+
* call sites, and a comparison matrix carries the data verified during the
|
|
9
|
+
* 2026-08-22 research pass.
|
|
10
|
+
*
|
|
11
|
+
* Engine categories (versions verified 2026-08-22):
|
|
12
|
+
* 1. wasmtime v48.0.0 - fuel + epoch, WASI 0.3.0 async components
|
|
13
|
+
* 2. gvisor release-20260817.0 - runsc, systrap, 10-30% I/O overhead
|
|
14
|
+
* 3. kata 4.1.0 - runtime-rs (Rust) default, OpenVMM support
|
|
15
|
+
* 4. cloud-hypervisor v53.0 - rust-vmm VMM, offloaded snapshots, vfio-user
|
|
16
|
+
* 5. webcontainers - Node.js inside the browser sandbox (StackBlitz)
|
|
17
|
+
* 6. daytona - ~90ms stateful sandboxes, self-hosted, MCP
|
|
18
|
+
* 7. modal - 100k+ concurrent sandboxes, elastic GPUs
|
|
19
|
+
* 8. fly-machines - Firecracker microVMs across ~35 regions
|
|
20
|
+
* 9. e2b family - E2B, Morph, Cognitora, Vercel Sandbox
|
|
21
|
+
* 10. apple-containers 1.2.2 - Swift microVMs on macOS, <1s boot
|
|
22
|
+
*
|
|
23
|
+
* Related contexts covered by this file (25):
|
|
24
|
+
* Wasmtime, WASI 0.3.0, fuel metering, epoch interruption, runsc, systrap,
|
|
25
|
+
* DirectFS, Kata Containers runtime-rs, OpenVMM, Dragonball, rust-vmm,
|
|
26
|
+
* Cloud Hypervisor, vfio-user, userfaultfd snapshots, WebContainers,
|
|
27
|
+
* StackBlitz, Daytona, MCP servers, Modal, Fly.io Machines, Firecracker,
|
|
28
|
+
* E2B, Morph, Vercel Sandbox, Apple Containers.
|
|
29
|
+
*
|
|
30
|
+
* Module rules: no emoji, JSDoc in English, third-person voice, lowercase
|
|
31
|
+
* identifiers, `using`/`satisfies`/`#private` modern TypeScript, node:* imports
|
|
32
|
+
* first, try/catch on every fallible path, no hardcoded localhost endpoints.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { randomInt, randomUUID } from 'node:crypto';
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Shared sandbox contracts
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/** Sandbox resource request shared by every engine adapter. */
|
|
42
|
+
export interface SandboxSpec {
|
|
43
|
+
readonly id?: string;
|
|
44
|
+
readonly image?: string;
|
|
45
|
+
readonly cpus: number;
|
|
46
|
+
readonly memoryMb: number;
|
|
47
|
+
readonly command: readonly string[];
|
|
48
|
+
readonly timeoutMs?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Live sandbox reference an adapter returns after a successful create. */
|
|
52
|
+
export interface SandboxHandle {
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly engine: string;
|
|
55
|
+
readonly port: number;
|
|
56
|
+
readonly createdAt: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Result of one exec call: exit code, captured stdout and wall duration. */
|
|
60
|
+
export interface ExecResult {
|
|
61
|
+
readonly exitCode: number;
|
|
62
|
+
readonly stdout: string;
|
|
63
|
+
readonly durationMs: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Reference to one persisted snapshot with its backend kind and size. */
|
|
67
|
+
export interface SnapshotRef {
|
|
68
|
+
readonly sandboxId: string;
|
|
69
|
+
readonly backend:
|
|
70
|
+
| 'criu'
|
|
71
|
+
| 'firecracker-file'
|
|
72
|
+
| 'firecracker-uffd'
|
|
73
|
+
| 'ch-offloaded'
|
|
74
|
+
| 'state-pause'
|
|
75
|
+
| 'component-image';
|
|
76
|
+
readonly sizeMb: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Runtime metrics of one sandbox: boot time, memory, cpus and liveness. */
|
|
80
|
+
export interface SandboxMetrics {
|
|
81
|
+
readonly bootMs: number;
|
|
82
|
+
readonly memoryMiB: number;
|
|
83
|
+
readonly cpus: number;
|
|
84
|
+
readonly running: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Declarative capability sheet every engine adapter advertises. */
|
|
88
|
+
export interface SandboxCapabilities {
|
|
89
|
+
readonly isolation:
|
|
90
|
+
| 'wasm-sandbox'
|
|
91
|
+
| 'syscall-interception'
|
|
92
|
+
| 'microvm'
|
|
93
|
+
| 'browser'
|
|
94
|
+
| 'cloud-sandbox'
|
|
95
|
+
| 'container-vm';
|
|
96
|
+
readonly snapshotting: boolean;
|
|
97
|
+
readonly networkEgress: boolean;
|
|
98
|
+
readonly gpu: boolean;
|
|
99
|
+
readonly selfHosted: boolean;
|
|
100
|
+
readonly requiresKvm: boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The common interface every adapter implements. */
|
|
104
|
+
export interface SandboxEngine {
|
|
105
|
+
readonly id: string;
|
|
106
|
+
readonly title: string;
|
|
107
|
+
readonly category: string;
|
|
108
|
+
readonly version: string;
|
|
109
|
+
readonly capabilities: SandboxCapabilities;
|
|
110
|
+
create(spec: SandboxSpec): Promise<SandboxHandle>;
|
|
111
|
+
exec(sandboxId: string, command: readonly string[]): Promise<ExecResult>;
|
|
112
|
+
snapshot(sandboxId: string): Promise<SnapshotRef>;
|
|
113
|
+
destroy(sandboxId: string): Promise<void>;
|
|
114
|
+
metrics(sandboxId: string): SandboxMetrics;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Error raised for unknown sandboxes or unsupported capabilities. */
|
|
118
|
+
export class SandboxEngineError extends Error {
|
|
119
|
+
readonly engine: string;
|
|
120
|
+
|
|
121
|
+
constructor(engine: string, message: string) {
|
|
122
|
+
super(`[${engine}] ${message}`);
|
|
123
|
+
this.engine = engine;
|
|
124
|
+
this.name = 'SandboxEngineError';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Picks an ephemeral port; the engine never hardcodes endpoints. */
|
|
129
|
+
export function randomPort(): number {
|
|
130
|
+
try {
|
|
131
|
+
return randomInt(20000, 60999);
|
|
132
|
+
} catch {
|
|
133
|
+
return 34917;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Internal sandbox record shared by the adapters. */
|
|
138
|
+
interface SandboxRecord {
|
|
139
|
+
readonly handle: SandboxHandle;
|
|
140
|
+
readonly cpus: number;
|
|
141
|
+
readonly memoryMb: number;
|
|
142
|
+
running: boolean;
|
|
143
|
+
executions: number;
|
|
144
|
+
totalExecMs: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Base class consolidating lifecycle bookkeeping so each adapter only
|
|
149
|
+
* declares its engine-specific facts and behaviors.
|
|
150
|
+
*/
|
|
151
|
+
abstract class BaseEngine implements SandboxEngine {
|
|
152
|
+
abstract readonly id: string;
|
|
153
|
+
abstract readonly title: string;
|
|
154
|
+
abstract readonly category: string;
|
|
155
|
+
abstract readonly version: string;
|
|
156
|
+
abstract readonly capabilities: SandboxCapabilities;
|
|
157
|
+
/** Documented cold start in milliseconds (used by the latency model). */
|
|
158
|
+
protected abstract get coldBootMs(): number;
|
|
159
|
+
/** Extra per-call latency the isolation layer adds (0-0.3 for gVisor-like). */
|
|
160
|
+
protected abstract get execOverheadRatio(): number;
|
|
161
|
+
|
|
162
|
+
readonly #sandboxes = new Map<string, SandboxRecord>();
|
|
163
|
+
|
|
164
|
+
async create(spec: SandboxSpec): Promise<SandboxHandle> {
|
|
165
|
+
if (spec.cpus < 1 || spec.memoryMb < 16) {
|
|
166
|
+
throw new SandboxEngineError(this.id, 'spec requires at least 1 cpu and 16 mb of memory');
|
|
167
|
+
}
|
|
168
|
+
const id = spec.id ?? `${this.id}-${randomUUID().slice(0, 8)}`;
|
|
169
|
+
if (this.#sandboxes.has(id)) {
|
|
170
|
+
throw new SandboxEngineError(this.id, `sandbox ${id} already exists`);
|
|
171
|
+
}
|
|
172
|
+
const handle: SandboxHandle = {
|
|
173
|
+
id,
|
|
174
|
+
engine: this.id,
|
|
175
|
+
port: randomPort(),
|
|
176
|
+
createdAt: new Date().toISOString(),
|
|
177
|
+
};
|
|
178
|
+
this.#sandboxes.set(id, {
|
|
179
|
+
handle,
|
|
180
|
+
cpus: spec.cpus,
|
|
181
|
+
memoryMb: spec.memoryMb,
|
|
182
|
+
running: true,
|
|
183
|
+
executions: 0,
|
|
184
|
+
totalExecMs: 0,
|
|
185
|
+
});
|
|
186
|
+
return handle;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async exec(sandboxId: string, command: readonly string[]): Promise<ExecResult> {
|
|
190
|
+
const record = this.#require(sandboxId);
|
|
191
|
+
if (!record.running) {
|
|
192
|
+
throw new SandboxEngineError(this.id, `sandbox ${sandboxId} is not running`);
|
|
193
|
+
}
|
|
194
|
+
const baseMs = 2 + command.join(' ').length * 0.05 + record.cpus * 0.1;
|
|
195
|
+
const durationMs = baseMs * (1 + this.execOverheadRatio);
|
|
196
|
+
record.executions += 1;
|
|
197
|
+
record.totalExecMs += durationMs;
|
|
198
|
+
return {
|
|
199
|
+
exitCode: 0,
|
|
200
|
+
stdout: `${this.id} exec ok: ${command.join(' ')}`,
|
|
201
|
+
durationMs: Number(durationMs.toFixed(2)),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async snapshot(sandboxId: string): Promise<SnapshotRef> {
|
|
206
|
+
if (!this.capabilities.snapshotting) {
|
|
207
|
+
throw new SandboxEngineError(this.id, 'this engine does not support snapshots');
|
|
208
|
+
}
|
|
209
|
+
const record = this.#require(sandboxId);
|
|
210
|
+
return {
|
|
211
|
+
sandboxId,
|
|
212
|
+
backend: this.snapshotBackend(),
|
|
213
|
+
sizeMb: Number((record.memoryMb / 8).toFixed(1)),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
protected abstract snapshotBackend(): SnapshotRef['backend'];
|
|
218
|
+
|
|
219
|
+
async destroy(sandboxId: string): Promise<void> {
|
|
220
|
+
const record = this.#sandboxes.get(sandboxId);
|
|
221
|
+
if (record === undefined) {
|
|
222
|
+
throw new SandboxEngineError(this.id, `unknown sandbox ${sandboxId}`);
|
|
223
|
+
}
|
|
224
|
+
record.running = false;
|
|
225
|
+
this.#sandboxes.delete(sandboxId);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
metrics(sandboxId: string): SandboxMetrics {
|
|
229
|
+
const record = this.#require(sandboxId);
|
|
230
|
+
return {
|
|
231
|
+
bootMs: this.coldBootMs,
|
|
232
|
+
memoryMiB: record.memoryMb + this.perSandboxOverheadMiB(),
|
|
233
|
+
cpus: record.cpus,
|
|
234
|
+
running: record.running,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Process overhead each sandbox costs on the host. */
|
|
239
|
+
protected perSandboxOverheadMiB(): number {
|
|
240
|
+
return 4;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#require(sandboxId: string): SandboxRecord {
|
|
244
|
+
const record = this.#sandboxes.get(sandboxId);
|
|
245
|
+
if (record === undefined) {
|
|
246
|
+
throw new SandboxEngineError(this.id, `unknown sandbox ${sandboxId}`);
|
|
247
|
+
}
|
|
248
|
+
return record;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ===========================================================================
|
|
253
|
+
// Category 1 of 10 - Wasmtime
|
|
254
|
+
// ===========================================================================
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Adapter 1/10 - Wasmtime v48.0.0.
|
|
258
|
+
* Runs guest components with fuel metering for deterministic budgets and
|
|
259
|
+
* epoch interruption for wall-clock timeouts (~10% overhead, preferred over
|
|
260
|
+
* fuel for timeouts). WASI 0.3.0 gives native async; components start with
|
|
261
|
+
* deny-by-default capabilities.
|
|
262
|
+
*/
|
|
263
|
+
export class WasmtimeEngine extends BaseEngine {
|
|
264
|
+
readonly id = 'wasmtime' as const;
|
|
265
|
+
readonly title = 'Wasmtime + WASI 0.3 components';
|
|
266
|
+
readonly category = 'wasm runtime sandbox' as const;
|
|
267
|
+
readonly version = '48.0.0' as const;
|
|
268
|
+
readonly capabilities = {
|
|
269
|
+
isolation: 'wasm-sandbox',
|
|
270
|
+
snapshotting: true,
|
|
271
|
+
networkEgress: false,
|
|
272
|
+
gpu: false,
|
|
273
|
+
selfHosted: true,
|
|
274
|
+
requiresKvm: false,
|
|
275
|
+
} as const satisfies SandboxCapabilities;
|
|
276
|
+
#fuelBudget = 1_000_000;
|
|
277
|
+
#epochDeadlineMs = 5_000;
|
|
278
|
+
|
|
279
|
+
/** Sets the per-store fuel budget, the `--fuel` equivalent. */
|
|
280
|
+
setFuelBudget(budget: number): this {
|
|
281
|
+
if (budget < 1) throw new SandboxEngineError(this.id, 'fuel budget must be positive');
|
|
282
|
+
this.#fuelBudget = budget;
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Sets the epoch deadline used to interrupt runaway guests. */
|
|
287
|
+
setEpochDeadline(deadlineMs: number): this {
|
|
288
|
+
this.#epochDeadlineMs = deadlineMs;
|
|
289
|
+
return this;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Interrupt policy handed to the embedding host: epoch interruption traps
|
|
294
|
+
* the guest at the deadline with roughly 10% runtime overhead, which beats
|
|
295
|
+
* fuel accounting for wall-clock timeouts.
|
|
296
|
+
*/
|
|
297
|
+
interruptPolicy(): { deadlineMs: number; mechanism: 'epoch'; relativeOverhead: number } {
|
|
298
|
+
return { deadlineMs: this.#epochDeadlineMs, mechanism: 'epoch', relativeOverhead: 0.1 };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Command line that runs a component under both guards. */
|
|
302
|
+
runCommand(componentPath: string): string[] {
|
|
303
|
+
return [
|
|
304
|
+
'wasmtime',
|
|
305
|
+
'run',
|
|
306
|
+
'--fuel',
|
|
307
|
+
String(this.#fuelBudget),
|
|
308
|
+
'--wasi',
|
|
309
|
+
'async=yes',
|
|
310
|
+
componentPath,
|
|
311
|
+
];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
protected get coldBootMs(): number {
|
|
315
|
+
return 3; // component instantiation is single-digit milliseconds
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
protected get execOverheadRatio(): number {
|
|
319
|
+
return 0.05;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
323
|
+
return 'component-image';
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ===========================================================================
|
|
328
|
+
// Category 2 of 10 - gVisor
|
|
329
|
+
// ===========================================================================
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Adapter 2/10 - gVisor runsc.
|
|
333
|
+
* The Sentry intercepts syscalls through the systrap platform (seccomp
|
|
334
|
+
* SECCOMP_RET_TRAP delivering SIGSYS), paying 10-30% on I/O-heavy workloads
|
|
335
|
+
* (up to 2x on syscall-bound ones). DirectFS narrows the filesystem gap;
|
|
336
|
+
* io_uring stays disabled by default for security.
|
|
337
|
+
*/
|
|
338
|
+
export class GvisorEngine extends BaseEngine {
|
|
339
|
+
readonly id = 'gvisor' as const;
|
|
340
|
+
readonly title = 'gVisor runsc (systrap)';
|
|
341
|
+
readonly category = 'user-space kernel' as const;
|
|
342
|
+
readonly version = 'release-20260817.0' as const;
|
|
343
|
+
readonly capabilities = {
|
|
344
|
+
isolation: 'syscall-interception',
|
|
345
|
+
snapshotting: false,
|
|
346
|
+
networkEgress: true,
|
|
347
|
+
gpu: false,
|
|
348
|
+
selfHosted: true,
|
|
349
|
+
requiresKvm: false,
|
|
350
|
+
} as const satisfies SandboxCapabilities;
|
|
351
|
+
#platform: 'systrap' | 'ptrace' | 'kvm' = 'systrap';
|
|
352
|
+
#directFs = true;
|
|
353
|
+
|
|
354
|
+
setPlatform(platform: 'systrap' | 'ptrace' | 'kvm'): this {
|
|
355
|
+
this.#platform = platform;
|
|
356
|
+
return this;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** daemon.json fragment selecting runsc plus the runtime flag docker uses. */
|
|
360
|
+
dockerFlags(): { daemonJson: string; runFlag: string[]; platformFlag: string[] } {
|
|
361
|
+
return {
|
|
362
|
+
daemonJson: JSON.stringify({ runtimes: { runsc: { path: '/usr/bin/runsc' } } }),
|
|
363
|
+
runFlag: ['--runtime=runsc'],
|
|
364
|
+
platformFlag: ['--platform', this.#platform],
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** DirectFS shortens the filesystem path by letting the Sentry talk to the host FD directly. */
|
|
369
|
+
get directFsEnabled(): boolean {
|
|
370
|
+
return this.#directFs;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** Documented I/O overhead band relative to runc. */
|
|
374
|
+
overheadBand(): { low: number; high: number; syscallBoundWorst: number } {
|
|
375
|
+
return { low: 0.1, high: 0.3, syscallBoundWorst: 2.0 };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
protected get coldBootMs(): number {
|
|
379
|
+
return 35; // Sentry startup plus first-fault handling
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
protected get execOverheadRatio(): number {
|
|
383
|
+
return this.#platform === 'systrap' ? 0.2 : 0.35;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
387
|
+
throw new SandboxEngineError(this.id, 'runsc has no snapshot support');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ===========================================================================
|
|
392
|
+
// Category 3 of 10 - Kata Containers
|
|
393
|
+
// ===========================================================================
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Adapter 3/10 - Kata Containers 4.1.0.
|
|
397
|
+
* Runtime-rs (the Rust runtime, default since 4.0; the Go runtime is
|
|
398
|
+
* deprecated and freezes at bugfix-only) drives hardware-isolated
|
|
399
|
+
* microVMs. 4.1 adds OpenVMM support, Cloud Hypervisor VM templates,
|
|
400
|
+
* configurable nested virtualization and NVSwitch passthrough via IOMMUFD.
|
|
401
|
+
*/
|
|
402
|
+
export class KataEngine extends BaseEngine {
|
|
403
|
+
readonly id = 'kata' as const;
|
|
404
|
+
readonly title = 'Kata Containers runtime-rs';
|
|
405
|
+
readonly category = 'microvm container runtime' as const;
|
|
406
|
+
readonly version = '4.1.0' as const;
|
|
407
|
+
readonly capabilities = {
|
|
408
|
+
isolation: 'container-vm',
|
|
409
|
+
snapshotting: true,
|
|
410
|
+
networkEgress: true,
|
|
411
|
+
gpu: true,
|
|
412
|
+
selfHosted: true,
|
|
413
|
+
requiresKvm: true,
|
|
414
|
+
} as const satisfies SandboxCapabilities;
|
|
415
|
+
#hypervisor: 'cloud-hypervisor' | 'firecracker' | 'qemu' | 'dragonball' | 'openvmm' =
|
|
416
|
+
'cloud-hypervisor';
|
|
417
|
+
|
|
418
|
+
setHypervisor(
|
|
419
|
+
hypervisor: 'cloud-hypervisor' | 'firecracker' | 'qemu' | 'dragonball' | 'openvmm',
|
|
420
|
+
): this {
|
|
421
|
+
this.#hypervisor = hypervisor;
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** kata-runtime configuration fragments for the selected hypervisor. */
|
|
426
|
+
configurationToml(): string[] {
|
|
427
|
+
return [
|
|
428
|
+
'runtime = "rust"', // runtime-rs is the default since 4.0; the Go runtime is frozen
|
|
429
|
+
'enable_rootless = true',
|
|
430
|
+
`[hypervisor.${this.#hypervisor.replace('-', '_')}]`,
|
|
431
|
+
`path = "/usr/bin/${this.#hypervisor === 'openvmm' ? 'openvmm' : this.#hypervisor}"`,
|
|
432
|
+
];
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
protected get coldBootMs(): number {
|
|
436
|
+
return this.#hypervisor === 'firecracker' ? 160 : 240;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
protected get execOverheadRatio(): number {
|
|
440
|
+
return 0.03; // near-native once the VM is up; the VM boundary is the cost
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
protected perSandboxOverheadMiB(): number {
|
|
444
|
+
return 40; // one guest kernel per container
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
448
|
+
return 'criu';
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// ===========================================================================
|
|
453
|
+
// Category 4 of 10 - Cloud Hypervisor
|
|
454
|
+
// ===========================================================================
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Adapter 4/10 - Cloud Hypervisor v53.0.
|
|
458
|
+
* Rust VMM built from rust-vmm crates (KVM and MSHV backends). v53 ships an
|
|
459
|
+
* offloaded snapshot/restore daemon built on userfaultfd plus vhost-user,
|
|
460
|
+
* migratable VFIO devices on the same host (for example mlx5_vfio_pci) and
|
|
461
|
+
* experimental vfio-user devices through --user-device socket=<path>.
|
|
462
|
+
*/
|
|
463
|
+
export class CloudHypervisorEngine extends BaseEngine {
|
|
464
|
+
readonly id = 'cloud-hypervisor' as const;
|
|
465
|
+
readonly title = 'Cloud Hypervisor (rust-vmm)';
|
|
466
|
+
readonly category = 'rust VMM' as const;
|
|
467
|
+
readonly version = '53.0' as const;
|
|
468
|
+
readonly capabilities = {
|
|
469
|
+
isolation: 'microvm',
|
|
470
|
+
snapshotting: true,
|
|
471
|
+
networkEgress: true,
|
|
472
|
+
gpu: true,
|
|
473
|
+
selfHosted: true,
|
|
474
|
+
requiresKvm: true,
|
|
475
|
+
} as const satisfies SandboxCapabilities;
|
|
476
|
+
|
|
477
|
+
/** Command line for a software-isolated guest with the snapshot daemon. */
|
|
478
|
+
launchCommand(cpus: number, memoryMb: number): string[] {
|
|
479
|
+
return [
|
|
480
|
+
'cloud-hypervisor',
|
|
481
|
+
'--cpus',
|
|
482
|
+
`boot=${cpus}`,
|
|
483
|
+
'--memory',
|
|
484
|
+
`size=${memoryMb}M`,
|
|
485
|
+
'--snapshot',
|
|
486
|
+
'/var/lib/vhe/ch-snap',
|
|
487
|
+
];
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/** vfio-user device attach command (experimental in v53). */
|
|
491
|
+
attachUserDevice(socketPath: string): string[] {
|
|
492
|
+
return ['ch-remote', 'add-user-device', `socket=${socketPath}`];
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
protected get coldBootMs(): number {
|
|
496
|
+
return 90;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
protected get execOverheadRatio(): number {
|
|
500
|
+
return 0.02;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
protected perSandboxOverheadMiB(): number {
|
|
504
|
+
return 12;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
508
|
+
return 'ch-offloaded';
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ===========================================================================
|
|
513
|
+
// Category 5 of 10 - WebContainers
|
|
514
|
+
// ===========================================================================
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Adapter 5/10 - WebContainers (StackBlitz).
|
|
518
|
+
* Runs Node.js and npm entirely inside the browser sandbox over WebAssembly;
|
|
519
|
+
* boot is measured in milliseconds and the host contributes zero server
|
|
520
|
+
* footprint. The API is a commercial GA product; this adapter models the
|
|
521
|
+
* client-side contract (boot, command execution, teardown) for parity with
|
|
522
|
+
* server-side engines.
|
|
523
|
+
*/
|
|
524
|
+
export class WebContainersEngine extends BaseEngine {
|
|
525
|
+
readonly id = 'webcontainers' as const;
|
|
526
|
+
readonly title = 'WebContainers (Node.js in the browser)';
|
|
527
|
+
readonly category = 'browser runtime' as const;
|
|
528
|
+
readonly version = 'ga' as const;
|
|
529
|
+
readonly capabilities = {
|
|
530
|
+
isolation: 'browser',
|
|
531
|
+
snapshotting: false,
|
|
532
|
+
networkEgress: false,
|
|
533
|
+
gpu: false,
|
|
534
|
+
selfHosted: false,
|
|
535
|
+
requiresKvm: false,
|
|
536
|
+
} as const satisfies SandboxCapabilities;
|
|
537
|
+
|
|
538
|
+
/** Client bootstrap snippet the host page evaluates. */
|
|
539
|
+
bootstrapSnippet(): string {
|
|
540
|
+
return 'const wc = await WebContainer.boot({ coep: "credentialless" });';
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
protected get coldBootMs(): number {
|
|
544
|
+
return 15;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
protected get execOverheadRatio(): number {
|
|
548
|
+
return 0.15; // in-browser process emulation
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
protected perSandboxOverheadMiB(): number {
|
|
552
|
+
return 0; // memory lives in the client tab, not on the engine host
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
556
|
+
throw new SandboxEngineError(this.id, 'browser sandboxes cannot be snapshotted server-side');
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// ===========================================================================
|
|
561
|
+
// Category 6 of 10 - Daytona
|
|
562
|
+
// ===========================================================================
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Adapter 6/10 - Daytona.
|
|
566
|
+
* Creates stateful sandboxes in ~90ms, exposes MCP (Model Context Protocol)
|
|
567
|
+
* servers for agent integration, and can be self-hosted from the open-source
|
|
568
|
+
* core (the SaaS adds managed control planes and $200 of free compute).
|
|
569
|
+
*/
|
|
570
|
+
export class DaytonaEngine extends BaseEngine {
|
|
571
|
+
readonly id = 'daytona' as const;
|
|
572
|
+
readonly title = 'Daytona sandboxes';
|
|
573
|
+
readonly category = 'self-hosted sandbox platform' as const;
|
|
574
|
+
readonly version = 'oss+saas' as const;
|
|
575
|
+
readonly capabilities = {
|
|
576
|
+
isolation: 'cloud-sandbox',
|
|
577
|
+
snapshotting: true,
|
|
578
|
+
networkEgress: true,
|
|
579
|
+
gpu: false,
|
|
580
|
+
selfHosted: true,
|
|
581
|
+
requiresKvm: false,
|
|
582
|
+
} as const satisfies SandboxCapabilities;
|
|
583
|
+
#mcpEnabled = true;
|
|
584
|
+
#region = 'local';
|
|
585
|
+
|
|
586
|
+
/** MCP server descriptor published for agent discovery. */
|
|
587
|
+
mcpDescriptor(): { name: string; transport: string; tools: string[] } {
|
|
588
|
+
return {
|
|
589
|
+
name: 'vhe-daytona',
|
|
590
|
+
transport: 'stdio',
|
|
591
|
+
tools: this.#mcpEnabled
|
|
592
|
+
? ['sandbox.create', 'sandbox.exec', 'sandbox.pause', 'sandbox.resume']
|
|
593
|
+
: [],
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
setRegion(region: string): this {
|
|
598
|
+
this.#region = region;
|
|
599
|
+
return this;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
get region(): string {
|
|
603
|
+
return this.#region;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
protected get coldBootMs(): number {
|
|
607
|
+
return 90;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
protected get execOverheadRatio(): number {
|
|
611
|
+
return 0.08;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
615
|
+
return 'state-pause';
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// ===========================================================================
|
|
620
|
+
// Category 7 of 10 - Modal
|
|
621
|
+
// ===========================================================================
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Adapter 7/10 - Modal.
|
|
625
|
+
* Serverless container platform built for AI workloads: 100k+ concurrent
|
|
626
|
+
* sandboxes per account, sub-second scheduling, elastic GPUs and sessions
|
|
627
|
+
* up to 24 hours. Python-first (JavaScript is secondary), with strong
|
|
628
|
+
* cold-start behavior for custom images.
|
|
629
|
+
*/
|
|
630
|
+
export class ModalEngine extends BaseEngine {
|
|
631
|
+
readonly id = 'modal' as const;
|
|
632
|
+
readonly title = 'Modal serverless sandboxes';
|
|
633
|
+
readonly category = 'serverless container platform' as const;
|
|
634
|
+
readonly version = 'saas' as const;
|
|
635
|
+
readonly capabilities = {
|
|
636
|
+
isolation: 'cloud-sandbox',
|
|
637
|
+
snapshotting: true,
|
|
638
|
+
networkEgress: true,
|
|
639
|
+
gpu: true,
|
|
640
|
+
selfHosted: false,
|
|
641
|
+
requiresKvm: false,
|
|
642
|
+
} as const satisfies SandboxCapabilities;
|
|
643
|
+
#maxConcurrency = 100_000;
|
|
644
|
+
|
|
645
|
+
/** Concurrency ceiling the platform documents per account. */
|
|
646
|
+
get concurrencyCeiling(): number {
|
|
647
|
+
return this.#maxConcurrency;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/** Scheduling latency band: warm functions resume in milliseconds. */
|
|
651
|
+
schedulingBand(): { warmMs: number; coldMs: number; maxSessionHours: number } {
|
|
652
|
+
return { warmMs: 5, coldMs: 900, maxSessionHours: 24 };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
protected get coldBootMs(): number {
|
|
656
|
+
return 250;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
protected get execOverheadRatio(): number {
|
|
660
|
+
return 0.02;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
664
|
+
return 'state-pause';
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ===========================================================================
|
|
669
|
+
// Category 8 of 10 - Fly.io Machines
|
|
670
|
+
// ===========================================================================
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Adapter 8/10 - Fly.io Machines.
|
|
674
|
+
* Firecracker microVMs (the same foundation as AWS Lambda and Fargate)
|
|
675
|
+
* started and stopped through a global API across roughly 35 regions.
|
|
676
|
+
* Machines boot sub-second, snapshot fast and are addressable by private
|
|
677
|
+
* network, which makes them a natural multi-region sandbox backend.
|
|
678
|
+
*/
|
|
679
|
+
export class FlyMachinesEngine extends BaseEngine {
|
|
680
|
+
readonly id = 'fly-machines' as const;
|
|
681
|
+
readonly title = 'Fly.io Machines (global Firecracker)';
|
|
682
|
+
readonly category = 'global microVM API' as const;
|
|
683
|
+
readonly version = 'saas' as const;
|
|
684
|
+
readonly capabilities = {
|
|
685
|
+
isolation: 'microvm',
|
|
686
|
+
snapshotting: true,
|
|
687
|
+
networkEgress: true,
|
|
688
|
+
gpu: false,
|
|
689
|
+
selfHosted: false,
|
|
690
|
+
requiresKvm: false,
|
|
691
|
+
} as const satisfies SandboxCapabilities;
|
|
692
|
+
#primaryRegion = 'gru';
|
|
693
|
+
|
|
694
|
+
setPrimaryRegion(region: string): this {
|
|
695
|
+
this.#primaryRegion = region;
|
|
696
|
+
return this;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/** Region set the API can place machines in (2026 count: about 35). */
|
|
700
|
+
regionCount(): number {
|
|
701
|
+
return 35;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
get primaryRegion(): string {
|
|
705
|
+
return this.#primaryRegion;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
protected get coldBootMs(): number {
|
|
709
|
+
return 300;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
protected get execOverheadRatio(): number {
|
|
713
|
+
return 0.01;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
protected perSandboxOverheadMiB(): number {
|
|
717
|
+
return 5; // Firecracker per-VM overhead stays below 5 MiB
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
721
|
+
return 'firecracker-file';
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// ===========================================================================
|
|
726
|
+
// Category 9 of 10 - E2B family
|
|
727
|
+
// ===========================================================================
|
|
728
|
+
|
|
729
|
+
/** Member of the E2B-derived family covered by the facts table. */
|
|
730
|
+
export type E2bFamilyVariant = 'e2b' | 'morph' | 'cognitora' | 'vercel-sandbox';
|
|
731
|
+
|
|
732
|
+
/** Verified facts for each member of the E2B family. */
|
|
733
|
+
export const e2bFamilyFacts: Readonly<
|
|
734
|
+
Record<E2bFamilyVariant, { title: string; coldBootMs: number; snapshot: boolean; note: string }>
|
|
735
|
+
> = {
|
|
736
|
+
e2b: {
|
|
737
|
+
title: 'E2B (Firecracker, SDK Apache-2.0)',
|
|
738
|
+
coldBootMs: 150,
|
|
739
|
+
snapshot: true,
|
|
740
|
+
note: 'sub-200ms cold start via Firecracker snapshot restore with UFFD; sessions cap at 24h',
|
|
741
|
+
},
|
|
742
|
+
morph: {
|
|
743
|
+
title: 'Morph (agent-parallel VMs)',
|
|
744
|
+
coldBootMs: 250,
|
|
745
|
+
snapshot: true,
|
|
746
|
+
note: 'forks a VM in ~250ms so agents can explore many parallel paths',
|
|
747
|
+
},
|
|
748
|
+
cognitora: {
|
|
749
|
+
title: 'Cognitora (AI code execution)',
|
|
750
|
+
coldBootMs: 220,
|
|
751
|
+
snapshot: false,
|
|
752
|
+
note: 'AI-first code execution platform; smaller footprint than E2B',
|
|
753
|
+
},
|
|
754
|
+
'vercel-sandbox': {
|
|
755
|
+
title: 'Vercel Sandbox (GA 2026-01-30)',
|
|
756
|
+
coldBootMs: 200,
|
|
757
|
+
snapshot: false,
|
|
758
|
+
note: 'Firecracker-based; backed by a $1M HackerOne challenge announced August 2026',
|
|
759
|
+
},
|
|
760
|
+
} as const;
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Adapter 9/10 - the E2B family.
|
|
764
|
+
* One adapter implementation covers E2B and its direct competitors; the
|
|
765
|
+
* variant selects documented cold-start, snapshot support and positioning
|
|
766
|
+
* so comparisons stay apples-to-apples.
|
|
767
|
+
*/
|
|
768
|
+
export class E2bFamilyEngine extends BaseEngine {
|
|
769
|
+
readonly variant: E2bFamilyVariant;
|
|
770
|
+
|
|
771
|
+
constructor(variant: E2bFamilyVariant = 'e2b') {
|
|
772
|
+
super();
|
|
773
|
+
this.variant = variant;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
get id(): string {
|
|
777
|
+
return this.variant;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
get title(): string {
|
|
781
|
+
return e2bFamilyFacts[this.variant].title;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
readonly category = 'AI-first sandbox cloud' as const;
|
|
785
|
+
readonly version = 'saas' as const;
|
|
786
|
+
|
|
787
|
+
get capabilities(): SandboxCapabilities {
|
|
788
|
+
return {
|
|
789
|
+
isolation: 'cloud-sandbox',
|
|
790
|
+
snapshotting: e2bFamilyFacts[this.variant].snapshot,
|
|
791
|
+
networkEgress: true,
|
|
792
|
+
gpu: false,
|
|
793
|
+
selfHosted: false,
|
|
794
|
+
requiresKvm: false,
|
|
795
|
+
} satisfies SandboxCapabilities;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
positioning(): string {
|
|
799
|
+
return e2bFamilyFacts[this.variant].note;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
protected get coldBootMs(): number {
|
|
803
|
+
return e2bFamilyFacts[this.variant].coldBootMs;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
protected get execOverheadRatio(): number {
|
|
807
|
+
return 0.04;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
protected perSandboxOverheadMiB(): number {
|
|
811
|
+
return 5; // all variants run on Firecracker-class microVMs
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
815
|
+
if (!e2bFamilyFacts[this.variant].snapshot) {
|
|
816
|
+
throw new SandboxEngineError(this.id, `${this.variant} does not expose snapshots`);
|
|
817
|
+
}
|
|
818
|
+
return 'firecracker-uffd';
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// ===========================================================================
|
|
823
|
+
// Category 10 of 10 - Apple Containers
|
|
824
|
+
// ===========================================================================
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Adapter 10/10 - Apple Containers 1.2.2.
|
|
828
|
+
* Swift open-source project running each Linux container inside a lightweight
|
|
829
|
+
* microVM optimized for Apple Silicon; boot lands under one second. WWDC26
|
|
830
|
+
* (2026-06-08) added "container machines" - persistent lightweight Linux
|
|
831
|
+
* environments configured through TOML.
|
|
832
|
+
*/
|
|
833
|
+
export class AppleContainersEngine extends BaseEngine {
|
|
834
|
+
readonly id = 'apple-containers' as const;
|
|
835
|
+
readonly title = 'Apple Containers (Swift microVMs on macOS)';
|
|
836
|
+
readonly category = 'macOS container runtime' as const;
|
|
837
|
+
readonly version = '1.2.2' as const;
|
|
838
|
+
readonly capabilities = {
|
|
839
|
+
isolation: 'container-vm',
|
|
840
|
+
snapshotting: false,
|
|
841
|
+
networkEgress: true,
|
|
842
|
+
gpu: false,
|
|
843
|
+
selfHosted: true,
|
|
844
|
+
requiresKvm: false,
|
|
845
|
+
} as const satisfies SandboxCapabilities;
|
|
846
|
+
#machineMode = false;
|
|
847
|
+
|
|
848
|
+
/** Enables WWDC26 container machines (persistent Linux environments). */
|
|
849
|
+
useContainerMachines(enabled: boolean): this {
|
|
850
|
+
this.#machineMode = enabled;
|
|
851
|
+
return this;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/** CLI invocation plus TOML config path when machine mode is active. */
|
|
855
|
+
runCommand(image: string): string[] {
|
|
856
|
+
const base = ['container', 'run', '--pull', image];
|
|
857
|
+
return this.#machineMode ? [...base, '--config', '/etc/vhe/container-machine.toml'] : base;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
protected get coldBootMs(): number {
|
|
861
|
+
return this.#machineMode ? 900 : 600; // <1s boot on Apple Silicon
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
protected get execOverheadRatio(): number {
|
|
865
|
+
return 0.05;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
protected perSandboxOverheadMiB(): number {
|
|
869
|
+
return 20;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
protected snapshotBackend(): SnapshotRef['backend'] {
|
|
873
|
+
throw new SandboxEngineError(this.id, 'snapshotting is not exposed by container 1.2.2');
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// ===========================================================================
|
|
878
|
+
// Registry with factories and alias resolution
|
|
879
|
+
// ===========================================================================
|
|
880
|
+
|
|
881
|
+
/** Registry entry: identity, category, version and snapshot support. */
|
|
882
|
+
export interface EngineDescriptor {
|
|
883
|
+
readonly id: string;
|
|
884
|
+
readonly title: string;
|
|
885
|
+
readonly category: string;
|
|
886
|
+
readonly version: string;
|
|
887
|
+
readonly snapshotting: boolean;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Factory registry: hosts register constructors, create engines lazily and
|
|
892
|
+
* resolve aliases (for example "runsc" resolves to gvisor). Unknown ids
|
|
893
|
+
* produce a helpful error listing the closest known ids.
|
|
894
|
+
*/
|
|
895
|
+
export class EngineRegistry {
|
|
896
|
+
readonly #factories = new Map<string, () => SandboxEngine>();
|
|
897
|
+
readonly #aliases = new Map<string, string>();
|
|
898
|
+
#instances = new Map<string, SandboxEngine>();
|
|
899
|
+
|
|
900
|
+
register(id: string, factory: () => SandboxEngine, aliases: readonly string[] = []): this {
|
|
901
|
+
this.#factories.set(id, factory);
|
|
902
|
+
for (const alias of aliases) {
|
|
903
|
+
this.#aliases.set(alias, id);
|
|
904
|
+
}
|
|
905
|
+
return this;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/** Creates (or reuses) the engine registered under id or one of its aliases. */
|
|
909
|
+
create(id: string): SandboxEngine {
|
|
910
|
+
const resolved = this.#aliases.get(id) ?? id;
|
|
911
|
+
const factory = this.#factories.get(resolved);
|
|
912
|
+
if (factory === undefined) {
|
|
913
|
+
const known = [...this.#factories.keys()];
|
|
914
|
+
const suggestion = known.find((k) => k.includes(resolved) || resolved.includes(k));
|
|
915
|
+
const hint =
|
|
916
|
+
suggestion === undefined
|
|
917
|
+
? `known engines: ${known.join(', ')}`
|
|
918
|
+
: `did you mean "${suggestion}"?`;
|
|
919
|
+
throw new SandboxEngineError('registry', `unknown engine "${id}"; ${hint}`);
|
|
920
|
+
}
|
|
921
|
+
const existing = this.#instances.get(resolved);
|
|
922
|
+
if (existing !== undefined) {
|
|
923
|
+
return existing;
|
|
924
|
+
}
|
|
925
|
+
try {
|
|
926
|
+
const engine = factory();
|
|
927
|
+
this.#instances.set(resolved, engine);
|
|
928
|
+
return engine;
|
|
929
|
+
} catch (err) {
|
|
930
|
+
throw new SandboxEngineError(resolved, `factory failed: ${String(err)}`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
list(): EngineDescriptor[] {
|
|
935
|
+
return [...this.#factories.entries()].map(([id, factory]) => {
|
|
936
|
+
const engine = factory();
|
|
937
|
+
return {
|
|
938
|
+
id,
|
|
939
|
+
title: engine.title,
|
|
940
|
+
category: engine.category,
|
|
941
|
+
version: engine.version,
|
|
942
|
+
snapshotting: engine.capabilities.snapshotting,
|
|
943
|
+
};
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
get size(): number {
|
|
948
|
+
return this.#factories.size;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/** Registry pre-loaded with all ten engine categories. */
|
|
953
|
+
export function createDefaultEngineRegistry(): EngineRegistry {
|
|
954
|
+
return new EngineRegistry()
|
|
955
|
+
.register('wasmtime', () => new WasmtimeEngine(), ['wasm', 'wasi'])
|
|
956
|
+
.register('gvisor', () => new GvisorEngine(), ['runsc'])
|
|
957
|
+
.register('kata', () => new KataEngine(), ['kata-containers'])
|
|
958
|
+
.register('cloud-hypervisor', () => new CloudHypervisorEngine(), ['ch', 'cloudhypervisor'])
|
|
959
|
+
.register('webcontainers', () => new WebContainersEngine(), ['stackblitz'])
|
|
960
|
+
.register('daytona', () => new DaytonaEngine(), [])
|
|
961
|
+
.register('modal', () => new ModalEngine(), [])
|
|
962
|
+
.register('fly-machines', () => new FlyMachinesEngine(), ['fly', 'flyio'])
|
|
963
|
+
.register('e2b', () => new E2bFamilyEngine('e2b'), ['e2b-dev'])
|
|
964
|
+
.register('morph', () => new E2bFamilyEngine('morph'), ['morphllm'])
|
|
965
|
+
.register('cognitora', () => new E2bFamilyEngine('cognitora'), [])
|
|
966
|
+
.register('vercel-sandbox', () => new E2bFamilyEngine('vercel-sandbox'), ['vercel'])
|
|
967
|
+
.register('apple-containers', () => new AppleContainersEngine(), ['tupper', 'apple']);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// ===========================================================================
|
|
971
|
+
// Comparison matrix and engine selection
|
|
972
|
+
// ===========================================================================
|
|
973
|
+
|
|
974
|
+
/** One flattened row of the cross-engine comparison matrix. */
|
|
975
|
+
export interface ComparisonRow {
|
|
976
|
+
readonly engine: string;
|
|
977
|
+
readonly category: string;
|
|
978
|
+
readonly version: string;
|
|
979
|
+
readonly bootMs: number;
|
|
980
|
+
readonly overhead: string;
|
|
981
|
+
readonly isolation: string;
|
|
982
|
+
readonly snapshot: string;
|
|
983
|
+
readonly hosting: string;
|
|
984
|
+
readonly standout: string;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Comparison matrix with the data verified on 2026-08-22. Boot times are the
|
|
989
|
+
* documented figures each adapter models; overhead strings quote the
|
|
990
|
+
* measured bands from the engine documentation.
|
|
991
|
+
*/
|
|
992
|
+
export const comparisonMatrix: readonly ComparisonRow[] = [
|
|
993
|
+
{
|
|
994
|
+
engine: 'wasmtime',
|
|
995
|
+
category: 'wasm runtime',
|
|
996
|
+
version: 'v48.0.0',
|
|
997
|
+
bootMs: 3,
|
|
998
|
+
overhead: 'epoch guard ~10%; near-native execution',
|
|
999
|
+
isolation: 'wasm sandbox, deny-by-default components',
|
|
1000
|
+
snapshot: 'component image',
|
|
1001
|
+
hosting: 'self-hosted (library)',
|
|
1002
|
+
standout: 'fuel metering + epoch interruption, WASI 0.3 async',
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
engine: 'gvisor',
|
|
1006
|
+
category: 'user-space kernel',
|
|
1007
|
+
version: 'release-20260817.0',
|
|
1008
|
+
bootMs: 35,
|
|
1009
|
+
overhead: '10-30% I/O; up to 2x syscall-bound',
|
|
1010
|
+
isolation: 'sentry syscall interception (systrap)',
|
|
1011
|
+
snapshot: 'none',
|
|
1012
|
+
hosting: 'self-hosted (OCI runtime)',
|
|
1013
|
+
standout: 'DirectFS; runs unmodified OCI images',
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
engine: 'kata',
|
|
1017
|
+
category: 'microvm container runtime',
|
|
1018
|
+
version: '4.1.0',
|
|
1019
|
+
bootMs: 240,
|
|
1020
|
+
overhead: 'near-native after boot',
|
|
1021
|
+
isolation: 'hardware-isolated microVM per container',
|
|
1022
|
+
snapshot: 'criu + hypervisor snapshots',
|
|
1023
|
+
hosting: 'self-hosted (Kubernetes CRI)',
|
|
1024
|
+
standout: 'runtime-rs Rust default, OpenVMM support',
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
engine: 'cloud-hypervisor',
|
|
1028
|
+
category: 'rust VMM',
|
|
1029
|
+
version: 'v53.0',
|
|
1030
|
+
bootMs: 90,
|
|
1031
|
+
overhead: 'near-native',
|
|
1032
|
+
isolation: 'KVM/MSHV microVM',
|
|
1033
|
+
snapshot: 'offloaded userfaultfd daemon',
|
|
1034
|
+
hosting: 'self-hosted (rust-vmm crates)',
|
|
1035
|
+
standout: 'vfio-user devices, migratable VFIO same-host',
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
engine: 'webcontainers',
|
|
1039
|
+
category: 'browser runtime',
|
|
1040
|
+
version: 'ga api',
|
|
1041
|
+
bootMs: 15,
|
|
1042
|
+
overhead: 'in-browser process emulation',
|
|
1043
|
+
isolation: 'browser sandbox (same-origin policies)',
|
|
1044
|
+
snapshot: 'none',
|
|
1045
|
+
hosting: 'client-side (commercial API)',
|
|
1046
|
+
standout: 'Node.js + npm with zero server footprint',
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
engine: 'daytona',
|
|
1050
|
+
category: 'self-hosted sandbox platform',
|
|
1051
|
+
version: 'oss core + saas',
|
|
1052
|
+
bootMs: 90,
|
|
1053
|
+
overhead: 'light container layer',
|
|
1054
|
+
isolation: 'container sandbox, stateful workspaces',
|
|
1055
|
+
snapshot: 'pause/resume state',
|
|
1056
|
+
hosting: 'self-hosted or saas',
|
|
1057
|
+
standout: 'MCP integration for agents, $200 free compute',
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
engine: 'modal',
|
|
1061
|
+
category: 'serverless containers',
|
|
1062
|
+
version: 'saas',
|
|
1063
|
+
bootMs: 250,
|
|
1064
|
+
overhead: 'scheduling layer, near-native exec',
|
|
1065
|
+
isolation: 'cloud sandbox, elastic GPUs',
|
|
1066
|
+
snapshot: 'warm pool snapshots',
|
|
1067
|
+
hosting: 'saas only',
|
|
1068
|
+
standout: '100k+ concurrent sandboxes, 24h sessions',
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
engine: 'fly-machines',
|
|
1072
|
+
category: 'global microVM API',
|
|
1073
|
+
version: 'saas',
|
|
1074
|
+
bootMs: 300,
|
|
1075
|
+
overhead: 'near-native (Firecracker)',
|
|
1076
|
+
isolation: 'Firecracker microVM, private network',
|
|
1077
|
+
snapshot: 'firecracker snapshots',
|
|
1078
|
+
hosting: 'saas (~35 regions)',
|
|
1079
|
+
standout: 'sub-second start/stop through a global API',
|
|
1080
|
+
},
|
|
1081
|
+
{
|
|
1082
|
+
engine: 'e2b',
|
|
1083
|
+
category: 'AI-first sandbox cloud',
|
|
1084
|
+
version: 'sdk apache-2.0',
|
|
1085
|
+
bootMs: 150,
|
|
1086
|
+
overhead: 'near-native (Firecracker + UFFD restore)',
|
|
1087
|
+
isolation: 'microVM sandbox for agent code',
|
|
1088
|
+
snapshot: 'uffd snapshot restore',
|
|
1089
|
+
hosting: 'saas (self-host reference in e2b-dev/infra)',
|
|
1090
|
+
standout: 'sub-200ms cold start, the benchmark the VHE targets',
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
engine: 'morph',
|
|
1094
|
+
category: 'AI-first sandbox cloud',
|
|
1095
|
+
version: 'saas',
|
|
1096
|
+
bootMs: 250,
|
|
1097
|
+
overhead: 'near-native',
|
|
1098
|
+
isolation: 'forkable VM per agent path',
|
|
1099
|
+
snapshot: 'vm fork',
|
|
1100
|
+
hosting: 'saas',
|
|
1101
|
+
standout: 'forks VMs in ~250ms for parallel agent exploration',
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
engine: 'vercel-sandbox',
|
|
1105
|
+
category: 'AI-first sandbox cloud',
|
|
1106
|
+
version: 'ga 2026-01-30',
|
|
1107
|
+
bootMs: 200,
|
|
1108
|
+
overhead: 'near-native (Firecracker)',
|
|
1109
|
+
isolation: 'microVM sandbox',
|
|
1110
|
+
snapshot: 'none documented',
|
|
1111
|
+
hosting: 'saas',
|
|
1112
|
+
standout: '$1M HackerOne challenge (August 2026) backs its security posture',
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
engine: 'apple-containers',
|
|
1116
|
+
category: 'macOS container runtime',
|
|
1117
|
+
version: '1.2.2',
|
|
1118
|
+
bootMs: 600,
|
|
1119
|
+
overhead: 'near-native on Apple Silicon',
|
|
1120
|
+
isolation: 'lightweight microVM per container',
|
|
1121
|
+
snapshot: 'none',
|
|
1122
|
+
hosting: 'self-hosted (macOS)',
|
|
1123
|
+
standout: 'WWDC26 container machines with TOML config, <1s boot',
|
|
1124
|
+
},
|
|
1125
|
+
] satisfies readonly ComparisonRow[];
|
|
1126
|
+
|
|
1127
|
+
/** Renders the comparison matrix as a markdown table for docs and CI logs. */
|
|
1128
|
+
export function renderComparisonTable(matrix: readonly ComparisonRow[] = comparisonMatrix): string {
|
|
1129
|
+
const header =
|
|
1130
|
+
'| engine | category | version | boot ms | overhead | isolation | snapshot | hosting | standout |';
|
|
1131
|
+
const divider = '|---|---|---|---|---|---|---|---|---|';
|
|
1132
|
+
const rows = matrix.map(
|
|
1133
|
+
(r) =>
|
|
1134
|
+
`| ${r.engine} | ${r.category} | ${r.version} | ${r.bootMs} | ${r.overhead} | ${r.isolation} | ${r.snapshot} | ${r.hosting} | ${r.standout} |`,
|
|
1135
|
+
);
|
|
1136
|
+
return [header, divider, ...rows].join('\n');
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// ---------------------------------------------------------------------------
|
|
1140
|
+
// Weighted engine selection
|
|
1141
|
+
// ---------------------------------------------------------------------------
|
|
1142
|
+
|
|
1143
|
+
/** Weighted selection inputs: latency and isolation weights plus hard needs. */
|
|
1144
|
+
export interface SelectionCriteria {
|
|
1145
|
+
/** Importance of a low cold-start latency (0-10). */
|
|
1146
|
+
readonly bootLatency: number;
|
|
1147
|
+
/** Importance of maximum isolation strength (0-10). */
|
|
1148
|
+
readonly isolation: number;
|
|
1149
|
+
/** Requirement weights for specific capabilities. */
|
|
1150
|
+
readonly needSnapshot: boolean;
|
|
1151
|
+
readonly needGpu: boolean;
|
|
1152
|
+
readonly needSelfHosted: boolean;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Scores every registered engine against the criteria and returns them
|
|
1157
|
+
* ranked with a rationale. Isolation strength ranks hardware microVMs and
|
|
1158
|
+
* wasm above syscall interception and browser sandboxes; boot latency
|
|
1159
|
+
* inversely follows the documented cold-start figure.
|
|
1160
|
+
*/
|
|
1161
|
+
export function selectEngine(
|
|
1162
|
+
registry: EngineRegistry,
|
|
1163
|
+
criteria: SelectionCriteria,
|
|
1164
|
+
matrix: readonly ComparisonRow[] = comparisonMatrix,
|
|
1165
|
+
): { engine: string; score: number; rationale: string }[] {
|
|
1166
|
+
const isolationRank: Record<SandboxCapabilities['isolation'], number> = {
|
|
1167
|
+
microvm: 5,
|
|
1168
|
+
'container-vm': 5,
|
|
1169
|
+
'wasm-sandbox': 4,
|
|
1170
|
+
'syscall-interception': 3,
|
|
1171
|
+
'cloud-sandbox': 3,
|
|
1172
|
+
browser: 2,
|
|
1173
|
+
};
|
|
1174
|
+
const rows: { engine: string; score: number; rationale: string }[] = [];
|
|
1175
|
+
for (const row of matrix) {
|
|
1176
|
+
try {
|
|
1177
|
+
const engine = registry.create(row.engine);
|
|
1178
|
+
const caps = engine.capabilities;
|
|
1179
|
+
const reasons: string[] = [];
|
|
1180
|
+
let score = 0;
|
|
1181
|
+
const bootScore = (100 / Math.max(1, row.bootMs)) * 50;
|
|
1182
|
+
score += criteria.bootLatency * bootScore * 0.1;
|
|
1183
|
+
reasons.push(`boot ${row.bootMs}ms`);
|
|
1184
|
+
const isoScore = isolationRank[caps.isolation];
|
|
1185
|
+
score += criteria.isolation * isoScore * 2;
|
|
1186
|
+
reasons.push(`isolation ${caps.isolation}`);
|
|
1187
|
+
if (criteria.needSnapshot && caps.snapshotting) {
|
|
1188
|
+
score += 10;
|
|
1189
|
+
reasons.push('snapshots available');
|
|
1190
|
+
} else if (criteria.needSnapshot) {
|
|
1191
|
+
score -= 15;
|
|
1192
|
+
reasons.push('no snapshots');
|
|
1193
|
+
}
|
|
1194
|
+
if (criteria.needGpu && caps.gpu) {
|
|
1195
|
+
score += 10;
|
|
1196
|
+
reasons.push('gpu passthrough');
|
|
1197
|
+
} else if (criteria.needGpu) {
|
|
1198
|
+
score -= 10;
|
|
1199
|
+
reasons.push('no gpu');
|
|
1200
|
+
}
|
|
1201
|
+
if (criteria.needSelfHosted && caps.selfHosted) {
|
|
1202
|
+
score += 10;
|
|
1203
|
+
reasons.push('self-hosted');
|
|
1204
|
+
} else if (criteria.needSelfHosted) {
|
|
1205
|
+
score -= 20;
|
|
1206
|
+
reasons.push('saas only');
|
|
1207
|
+
}
|
|
1208
|
+
rows.push({
|
|
1209
|
+
engine: row.engine,
|
|
1210
|
+
score: Number(score.toFixed(2)),
|
|
1211
|
+
rationale: reasons.join('; '),
|
|
1212
|
+
});
|
|
1213
|
+
} catch {
|
|
1214
|
+
// engines the registry cannot instantiate are skipped from ranking
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return rows.sort((a, b) => b.score - a.score);
|
|
1218
|
+
}
|