bulkhead-runtime 2026.4.5-beta.6 → 2026.4.5-beta.8

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.
Files changed (3) hide show
  1. package/README.md +86 -480
  2. package/dist/package.json +30 -1
  3. package/package.json +30 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  Each in its own OS namespace. Each with private memory, encrypted credentials, and an isolated filesystem.
7
7
  **No Docker. No cloud. One `npm install`.**
8
8
 
9
- ![Features](docs/assets/features.png)
9
+ Built on battle-tested subsystems extracted from [OpenClaw](https://github.com/nicepkg/openclaw) — model fallback, error classification, API key rotation, SSRF protection, embedding pipeline, and more.
10
10
 
11
11
  ---
12
12
 
@@ -68,11 +68,11 @@ app.post("/api/agent", async (req, res) => {
68
68
 
69
69
  ### Teams -- per-team agents, per-team secrets
70
70
 
71
- Different GitHub orgs, different databases, different cloud accounts. No credential leaks between teams.
71
+ Different teams, different databases, different cloud accounts. No credential leaks.
72
72
 
73
73
  ```typescript
74
- const eng = await platform.createWorkspace("engineering");
75
- const ops = await platform.createWorkspace("ops");
74
+ const eng = await platform.createWorkspace("engineering");
75
+ const ops = await platform.createWorkspace("ops");
76
76
 
77
77
  eng.skills.enable("github-pr");
78
78
  ops.skills.enable("pagerduty");
@@ -81,22 +81,9 @@ await eng.credentials.store("github", { token: "ghp_eng..." });
81
81
  await ops.credentials.store("pagerduty", { token: "pd_..." });
82
82
  ```
83
83
 
84
- ### Consulting -- client isolation, clean offboarding
85
-
86
- One `deleteWorkspace()` wipes everything -- memory, credentials, sessions. Gone.
87
-
88
- ```typescript
89
- const acme = await platform.createWorkspace("client-acme");
90
- await acme.credentials.store("aws", { key: "...", secret: "..." });
91
- await acme.run({ message: "Check staging and open a Jira ticket if it failed" });
92
-
93
- // Client offboarded -- clean wipe
94
- await platform.deleteWorkspace("client-acme");
95
- ```
96
-
97
84
  ### CI/CD -- ephemeral agents, zero state leaks
98
85
 
99
- Spin up per job, per PR, per deploy. The workspace is destroyed after. No state leaks between runs.
86
+ Spin up per job, per PR, per deploy. Destroyed after.
100
87
 
101
88
  ```typescript
102
89
  const ws = await platform.createWorkspace(`deploy-${Date.now()}`);
@@ -112,35 +99,15 @@ await platform.deleteWorkspace(ws.userId);
112
99
 
113
100
  ![Architecture](docs/assets/architecture.png)
114
101
 
115
- > Agent A cannot see Agent B's files, memory, credentials, or processes. Not by policy. **By kernel enforcement.**
116
-
117
- ---
118
-
119
- ## Why Bulkhead Over Alternatives
120
-
121
-
122
- | | Docker per user | E2B / Cloud | **Bulkhead Runtime** |
123
- | -------------------------------- | ------------------ | -------------------- | --------------------------------------------------------- |
124
- | **Isolation mechanism** | Container per user | Cloud VM per session | **Linux namespaces** |
125
- | **Credential security** | DIY | Not built-in | **AES-256-GCM, never exposed to agent** |
126
- | **Persistent memory** | DIY | DIY | **SQLite + vector embeddings per tenant** |
127
- | **Embedding cache + batch** | DIY | DIY | **SQLite cache, batch with retry** |
128
- | **SSRF protection** | DIY | DIY | **DNS-validated, private-IP blocking, fail-closed** |
129
- | **Model fallback** | DIY | DIY | **Automatic multi-model fallback chains** |
130
- | **API key rotation** | DIY | DIY | **Multi-key with rate-limit rotation** |
131
- | **Parallel subagents** | DIY | DIY | **Built-in with depth limiting** |
132
- | **Skills with secret injection** | DIY | DIY | **Credentials injected server-side** |
133
- | **Infrastructure** | Docker daemon | Cloud API + billing | **Single npm package** |
134
- | **Cold start** | ~2s | ~5-10s | **~50ms** |
135
- | **Embeddable in your app** | No | No | **Yes — it's a library** |
136
- | **License** | — | Proprietary | **MIT** |
102
+ When `workspace.run()` executes, Bulkhead spawns a **child process** with 5 layers of kernel isolation: user namespace, PID namespace, mount namespace (pivot_root), optional network namespace, and cgroups v2 resource limits. The agent **never runs in your application's process** and **cannot see anything outside its sandbox**.
137
103
 
104
+ > **[Read the full isolation architecture →](docs/isolation.md)**
138
105
 
139
106
  ---
140
107
 
141
108
  ## Single-User Mode
142
109
 
143
- The fastest path for prototyping or single-agent use. No platform needed.
110
+ For prototyping or single-agent use. No platform needed.
144
111
 
145
112
  ```typescript
146
113
  import { createRuntime } from "bulkhead-runtime";
@@ -155,448 +122,101 @@ const result = await runtime.run({
155
122
  });
156
123
  ```
157
124
 
158
- The agent runs inside a Linux namespace sandbox with full coding tools (read, write, edit, bash, grep, find, ls) and autonomous memory (`memory_store`, `memory_search`).
159
-
160
125
  ---
161
126
 
162
- ## Multi-Tenant Isolation
163
-
164
- This is the core of Bulkhead Runtime. Each user gets a **workspace** — a fully isolated environment with its own memory, encrypted credentials, enabled skills, and session history. Workspaces are physically separated at the OS level.
165
-
166
- ```typescript
167
- import { createPlatform } from "bulkhead-runtime";
168
-
169
- const platform = createPlatform({
170
- stateDir: "/var/bulkhead-runtime",
171
- credentialPassphrase: process.env.BULKHEAD_CREDENTIAL_KEY,
172
- });
173
-
174
- const alice = await platform.createWorkspace("alice", {
175
- provider: "anthropic",
176
- model: "claude-sonnet-4-20250514",
177
- });
178
-
179
- const bob = await platform.createWorkspace("bob", {
180
- provider: "google",
181
- model: "gemini-2.5-flash",
182
- });
183
- ```
184
-
185
- ### What "isolated" actually means
186
-
187
- When `workspace.run()` executes, Bulkhead spawns a **child process** with 5 layers of kernel isolation. The agent **never runs in your application's process**.
188
-
189
- ![Execution Flow](docs/assets/execution-flow.png)
190
-
191
- The agent gets coding tools (bash, file read/write/edit) because the mount namespace restricts its entire filesystem view. **It literally cannot see anything outside its sandbox.**
192
-
193
- ---
194
-
195
- ## Credential Security
196
-
197
- Credentials are **AES-256-GCM encrypted** at rest. PBKDF2 key derivation with 100k iterations (SHA-512). The agent **never** sees raw secrets — not through tools, not through IPC, not through environment variables.
198
-
199
- ```typescript
200
- await alice.credentials.store("github", { token: "ghp_alice_secret" });
201
- await alice.credentials.store("openai", { apiKey: "sk-..." });
202
- ```
203
-
204
- ![Credential Flow](docs/assets/credential-flow.png)
205
-
206
- System environment variables (`PATH`, `HOME`, `NODE_ENV`) are protected from credential key collision. Skill IDs are validated against prototype pollution.
127
+ ## Features
128
+
129
+ | Feature | What It Does | Docs |
130
+ |---------|-------------|------|
131
+ | **OS-level Isolation** | 5 layers: user, PID, mount, network namespaces + cgroups v2 | [docs/isolation.md](docs/isolation.md) |
132
+ | **Encrypted Credentials** | AES-256-GCM at rest, PBKDF2 key derivation, never exposed to agent | [docs/credentials.md](docs/credentials.md) |
133
+ | **Hybrid Memory** | Vector + FTS5 fusion, MMR diversity, temporal decay, 7-language query expansion | [docs/memory.md](docs/memory.md) |
134
+ | **Per-Workspace Skills** | Global registry, per-tenant enablement, credentials injected server-side | [docs/skills.md](docs/skills.md) |
135
+ | **Session Continuity** | Named sessions, JSONL transcripts, context pruning | [docs/sessions.md](docs/sessions.md) |
136
+ | **Model Fallback** | Automatic multi-model chains, error classification, cooldown tracking | [docs/fallback.md](docs/fallback.md) |
137
+ | **API Key Rotation** | Multi-key per provider, automatic rotation on rate limits | [docs/fallback.md](docs/fallback.md) |
138
+ | **Subagent Orchestration** | Parallel execution, depth limiting, registry | [docs/subagents.md](docs/subagents.md) |
139
+ | **Structured Logging** | JSON/pretty/compact, file output, subsystem tagging | [docs/logging.md](docs/logging.md) |
140
+ | **SSRF Protection** | DNS pinning, private IP blocking, hostname allowlists, fail-closed | [docs/memory.md](docs/memory.md) |
141
+ | **Embedding Pipeline** | Batch with retry, SQLite cache, 5 providers (including local Ollama) | [docs/memory.md](docs/memory.md) |
207
142
 
208
143
  ---
209
144
 
210
- ## Per-Workspace Skills
211
-
212
- Skills are registered globally and **enabled per workspace**. Each workspace gets exactly the capabilities it needs — nothing more.
213
-
214
- ```typescript
215
- const frontend = await platform.createWorkspace("team-frontend");
216
- const backend = await platform.createWorkspace("team-backend");
217
-
218
- frontend.skills.enable("github-issues");
219
- backend.skills.enable("github-issues");
220
- backend.skills.enable("db-migration");
221
-
222
- await frontend.credentials.store("github", { token: "ghp_frontend_token" });
223
- await backend.credentials.store("github", { token: "ghp_backend_token" });
224
- await backend.credentials.store("database", { url: "postgres://prod:5432/app" });
225
- ```
226
-
227
- ```javascript
228
- // skills/github-issues/execute.js
229
- const params = JSON.parse(await readStdin());
230
- const token = process.env.token;
231
- const res = await fetch(`https://api.github.com/repos/${params.repo}/issues`, {
232
- headers: { Authorization: `Bearer ${token}` },
233
- });
234
- console.log(JSON.stringify(await res.json()));
235
- ```
236
-
237
- Skills run with a minimal env (`PATH`, `HOME`, `NODE_ENV` + credentials), 30s timeout, and 10 MB stdout/stderr cap.
238
-
239
- ---
240
-
241
- ## Memory Isolation
242
-
243
- Each workspace has its own SQLite database. Memories never cross workspace boundaries — not by access control, **by physical separation**.
244
-
245
- ```typescript
246
- await alice.memory.store("Project uses React and TypeScript");
247
- await bob.memory.store("Project uses Vue and Python");
248
-
249
- const search = await alice.memory.search("framework");
250
- // → "React and TypeScript"
251
- // Bob's data doesn't exist in Alice's universe.
252
- ```
253
-
254
- ### Autonomous Agent Memory
255
-
256
- The agent decides what to remember. Memory persists across sessions, across restarts.
257
-
258
- ```typescript
259
- // Session 1
260
- await runtime.run({
261
- message: "My name is Juan, I work in fintech, I prefer TypeScript",
262
- sessionId: "onboarding",
263
- });
264
-
265
- // Session 2 — different session, memory persists
266
- await runtime.run({
267
- message: "Set up a new project for me",
268
- sessionId: "new-project",
269
- });
270
- // Agent searches memory → finds preferences → scaffolds TypeScript project
271
- ```
272
-
273
- **Hybrid search engine under the hood:**
274
-
275
-
276
- | Stage | Algorithm |
277
- | ------------------- | ---------------------------------------------------- |
278
- | **Vector search** | Cosine similarity against stored embeddings |
279
- | **Keyword search** | SQLite FTS5 with BM25 ranking |
280
- | **Fusion** | Weighted merge of vector + keyword scores |
281
- | **Temporal decay** | Exponential time-based score attenuation |
282
- | **Diversity** | MMR (Maximal Marginal Relevance) re-ranking |
283
- | **Query expansion** | 7-language keyword extraction (EN/ES/PT/ZH/JA/KO/AR) |
284
-
145
+ ## Why Bulkhead Over Alternatives
285
146
 
286
- Works without any embedding API key falls back to FTS5 keyword search.
147
+ | | Docker per user | E2B / Cloud | **Bulkhead Runtime** |
148
+ | -------------------------------- | ------------------ | -------------------- | -------------------------------------------- |
149
+ | **Isolation mechanism** | Container per user | Cloud VM per session | **Linux namespaces** |
150
+ | **Credential security** | DIY | Not built-in | **AES-256-GCM, never exposed to agent** |
151
+ | **Persistent memory** | DIY | DIY | **SQLite + vector embeddings per tenant** |
152
+ | **SSRF protection** | DIY | DIY | **DNS-validated, private-IP blocking** |
153
+ | **Model fallback** | DIY | DIY | **Automatic multi-model fallback chains** |
154
+ | **API key rotation** | DIY | DIY | **Multi-key with rate-limit rotation** |
155
+ | **Skills with secret injection** | DIY | DIY | **Credentials injected server-side** |
156
+ | **Infrastructure** | Docker daemon | Cloud API + billing | **Single npm package** |
157
+ | **Cold start** | ~2s | ~5-10s | **~50ms** |
158
+ | **Embeddable in your app** | No | No | **Yes — it's a library** |
159
+ | **License** | — | Proprietary | **MIT** |
287
160
 
288
161
  ---
289
162
 
290
- ## Session Continuity
291
-
292
- ```typescript
293
- await workspace.run({
294
- message: "Create a REST API for user management",
295
- sessionId: "api-project",
296
- });
297
-
298
- // Later — agent sees the full conversation history
299
- await workspace.run({
300
- message: "Add input validation to those endpoints",
301
- sessionId: "api-project",
302
- });
303
- ```
304
-
305
- Sessions are per-workspace, stored as JSONL transcripts with async locking.
306
-
307
- ---
163
+ ## Provider Support
308
164
 
309
- ## Subagent Orchestration
165
+ ### LLM Providers
310
166
 
311
- Agents can spawn sub-agents for parallel or complex work. Sub-agents run concurrently with controlled parallelism and depth limiting.
167
+ Any provider supported by [pi-ai](https://github.com/nicepkg/pi-ai):
312
168
 
313
- ```typescript
314
- const result = await runtime.run({
315
- message: "Review this PR for security and performance",
316
- enableSubagents: true,
317
- });
318
- ```
169
+ | Provider | Example Model |
170
+ |----------|---------------|
171
+ | **Anthropic** | `claude-sonnet-4-20250514` |
172
+ | **Google** | `gemini-2.5-flash` |
173
+ | **OpenAI** | `gpt-4o` |
174
+ | **Groq** | `llama-3.3-70b-versatile` |
175
+ | **Cerebras** | `llama-3.3-70b` |
176
+ | **Mistral** | `mistral-large-latest` |
177
+ | **xAI** | `grok-3` |
319
178
 
320
- Or programmatically:
179
+ ### Embedding Providers
321
180
 
322
- ```typescript
323
- import { spawnSubagentsParallel } from "bulkhead-runtime";
324
-
325
- const results = await spawnSubagentsParallel({
326
- tasks: [
327
- { id: "security", task: "Audit for SQL injection and XSS", label: "Security" },
328
- { id: "perf", task: "Profile hot paths and suggest optimizations", label: "Performance" },
329
- { id: "docs", task: "Check all public APIs have JSDoc", label: "Documentation" },
330
- ],
331
- maxConcurrent: 3,
332
- run: async (task) => {
333
- const r = await runtime.run({
334
- message: task.task,
335
- systemPrompt: `You are a ${task.label} expert.`,
336
- });
337
- return r.response;
338
- },
339
- });
340
- ```
181
+ | Provider | Default Model | Local |
182
+ |----------|--------------|-------|
183
+ | **OpenAI** | `text-embedding-3-small` | |
184
+ | **Gemini** | `gemini-embedding-001` | |
185
+ | **Voyage** | `voyage-3-lite` | |
186
+ | **Mistral** | `mistral-embed` | |
187
+ | **Ollama** | `nomic-embed-text` | **Yes** |
341
188
 
342
189
  ---
343
190
 
344
191
  ## Lifecycle Hooks
345
192
 
193
+ 6 hook points for audit logging, billing, and custom logic:
194
+
346
195
  ```typescript
347
196
  workspace.hooks.register("before_tool_call", async ({ toolName, input }) => {
348
197
  await auditLog.write({ tool: toolName, input, timestamp: Date.now() });
349
198
  });
350
199
 
351
200
  workspace.hooks.register("after_agent_end", async ({ sessionId, result }) => {
352
- await billing.recordUsage(workspace.id, sessionId);
353
- });
354
- ```
355
-
356
- 6 hook points: `session_start` · `session_end` · `before_agent_start` · `after_agent_end` · `before_tool_call` · `after_tool_call`
357
-
358
- ---
359
-
360
- ## Model Fallback & API Key Rotation
361
-
362
- When a model fails, Bulkhead automatically falls back to the next candidate. The error classification engine — ported from [OpenClaw](https://github.com/nicepkg/openclaw)'s battle-tested failover system — covers rate limits, billing, auth, overload, timeout, model not found, context overflow, and provider-specific patterns (Bedrock, Groq, Azure, Ollama, Mistral, etc.). Providers in cooldown are skipped automatically. When a key is rate-limited, it rotates to the next one.
363
-
364
- ```typescript
365
- const result = await runtime.run({
366
- message: "Analyze this codebase",
367
- provider: "anthropic",
368
- model: "claude-sonnet-4-20250514",
369
-
370
- fallbacks: ["openai/gpt-4o", "google/gemini-2.5-flash"],
371
-
372
- apiKeys: [process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!],
201
+ await billing.recordUsage(workspace.userId, sessionId);
373
202
  });
374
203
  ```
375
204
 
376
- Or set keys via environment variables:
377
-
378
- ```bash
379
- ANTHROPIC_API_KEY=sk-ant-...
380
- ANTHROPIC_API_KEYS=sk-ant-key1,sk-ant-key2,sk-ant-key3
381
- ANTHROPIC_API_KEY_1=sk-ant-...
382
- ANTHROPIC_API_KEY_2=sk-ant-...
383
- ```
205
+ `session_start` · `session_end` · `before_agent_start` · `after_agent_end` · `before_tool_call` · `after_tool_call`
384
206
 
385
207
  ---
386
208
 
387
- ## Context Window Guards
209
+ ## Demos
388
210
 
389
- Prevents silent failures from models with insufficient context windows.
211
+ 12 runnable demos covering every feature. 4 require an API key, 8 run fully local.
390
212
 
391
- ```typescript
392
- import { resolveContextWindowInfo, evaluateContextWindowGuard } from "bulkhead-runtime";
393
-
394
- const info = resolveContextWindowInfo({
395
- modelContextWindow: model.contextWindow,
396
- configContextTokens: 16_000,
397
- });
213
+ > **[See all demos →](docs/demos.md)**
398
214
 
399
- const guard = evaluateContextWindowGuard({ info });
400
- // guard.shouldWarn → true if below 32,000 tokens
401
- // guard.shouldBlock → true if below 16,000 tokens
402
- ```
403
-
404
- The runtime applies these guards automatically before every agent execution.
405
-
406
- ---
407
-
408
- ## Retry with Compaction
409
-
410
- Transient errors (rate limits, timeouts, context overflow) trigger automatic retry with exponential backoff.
411
-
412
- ```typescript
413
- const result = await runtime.run({
414
- message: "Refactor the auth module",
415
- maxRetries: 3,
416
- });
417
- // On context overflow → SDK compaction reduces history
418
- // On 429/5xx → exponential backoff + jitter
419
- ```
420
-
421
- ---
422
-
423
- ## Embedding Pipeline
424
-
425
- ### Embedding Cache
426
-
427
- Embeddings are cached in SQLite to avoid re-embedding unchanged content. Enabled by default.
428
-
429
- ```typescript
430
- const memory = createSimpleMemoryManager({
431
- dbDir: "/var/data/memory",
432
- embeddingProvider: createEmbeddingProvider({ provider: "openai", apiKey: "..." }),
433
- enableEmbeddingCache: true,
434
- maxCacheEntries: 50_000,
435
- });
436
- ```
437
-
438
- ### Batch Embedding with Retry
439
-
440
- ```typescript
441
- import { embedBatchWithRetry } from "bulkhead-runtime";
442
-
443
- const result = await embedBatchWithRetry(
444
- ["text 1", "text 2", "text 3"],
445
- {
446
- provider: embeddingProvider,
447
- cache: memory.embeddingCache ?? undefined,
448
- batchSize: 100,
449
- concurrency: 2,
450
- retryAttempts: 3,
451
- },
452
- );
453
- // result: { embeddings: [...], cached: 150, computed: 50, errors: 0 }
454
- ```
455
-
456
- ### SSRF Protection
457
-
458
- All embedding provider HTTP calls are protected against Server-Side Request Forgery by default. The SSRF engine — ported from [OpenClaw](https://github.com/nicepkg/openclaw) — resolves DNS and pins IPs before connecting, blocks private/link-local ranges, and enforces a hostname allowlist. Fail-closed by default.
459
-
460
- ```typescript
461
- import { validateUrl, buildBaseUrlPolicy } from "bulkhead-runtime";
462
-
463
- const provider = createEmbeddingProvider({
464
- provider: "openai",
465
- apiKey: "...",
466
- enableSsrf: true,
467
- });
468
-
469
- await validateUrl("https://api.openai.com/v1/embeddings"); // OK
470
- await validateUrl("http://169.254.1.1/steal"); // throws SSRF error
471
- ```
472
-
473
- ---
474
-
475
- ## File-based Memory Indexing
476
-
477
- Automatically watches `MEMORY.md` and `memory/` directory for changes and re-indexes them into the memory system.
478
-
479
- ```typescript
480
- import { createFileIndexer } from "bulkhead-runtime";
481
-
482
- const indexer = createFileIndexer({
483
- workspaceDir: "/path/to/workspace",
484
- memory,
485
- watchPaths: ["docs/"],
486
- debounceMs: 2000,
487
- });
488
-
489
- indexer.start();
490
- indexer.stop();
491
- ```
492
-
493
- ---
494
-
495
- ## Session Transcript Indexing
496
-
497
- Indexes session transcripts into memory for cross-session search. Supports post-compaction re-indexing. Ported from [OpenClaw](https://github.com/nicepkg/openclaw)'s memory-core extension.
498
-
499
- ```typescript
500
- import { createSessionIndexer } from "bulkhead-runtime";
501
-
502
- const indexer = createSessionIndexer({
503
- sessionsDir: path.join(stateDir, "sessions"),
504
- memory,
505
- deltaBytes: 4096,
506
- deltaMessages: 10,
507
- });
508
-
509
- await indexer.indexAllSessions();
510
- indexer.onTranscriptUpdate(sessionFile);
511
- ```
512
-
513
- ---
514
-
515
- ## Structured Logging
516
-
517
- Structured JSON logging with file output, rotation, and configurable levels.
518
-
519
- ```typescript
520
- import { configureLogger, createSubsystemLogger } from "bulkhead-runtime";
521
-
522
- configureLogger({
523
- level: "debug",
524
- file: "/var/log/bulkhead-runtime.log",
525
- maxFileBytes: 10 * 1024 * 1024,
526
- json: true,
527
- });
528
-
529
- const log = createSubsystemLogger("my-module");
530
- log.info("agent started", { userId: "alice", model: "claude-sonnet-4-20250514" });
531
- ```
532
-
533
- Or via environment:
534
-
535
- ```bash
536
- BULKHEAD_LOG_LEVEL=debug
537
- BULKHEAD_LOG_FILE=/var/log/bulkhead-runtime.log
538
- ```
539
-
540
- ---
541
-
542
- ## Security Architecture
543
-
544
- ### 5 Layers of Sandbox Isolation
545
-
546
- All layers are **fail-closed** — if any layer can't be applied, the sandbox refuses to start.
547
-
548
- ![Security Layers](docs/assets/security-layers.png)
549
-
550
- ### Defense in Depth
551
-
552
-
553
- | Defense | Mechanism |
554
- | ----------------------------- | ---------------------------------------------------------------------------------------------------- |
555
- | **Env allowlist** | Only `PATH`, `HOME`, `NODE_ENV` + the single API key the agent needs. Everything else dropped. |
556
- | **Credential proxy** | Secrets decrypted server-side, injected into skill execution. Never sent over IPC. |
557
- | **Path traversal blocklist** | `/proc`, `/sys`, `/home/`, `/etc/shadow`, `/run/docker.sock`, and more are blocked from bind mounts. |
558
- | **Symlink rejection** | `additionalBinds` sources must not be symlinks (prevents TOCTOU attacks). |
559
- | **IPC rate limiting** | 200 calls/sec per method. Prevents resource exhaustion from rogue agents. |
560
- | **IPC buffer limit** | 50 MB max. Peer stops on overflow to prevent memory exhaustion. |
561
- | **Prototype pollution guard** | `__proto__`, `constructor`, `prototype` rejected as skill/credential IDs. |
562
- | **Stdout interception** | IPC uses a dedicated fd. All other stdout is redirected to stderr. |
563
- | **Sensitive path validation** | `workspaceDir`, `projectDir`, `nodeExecutable`, `additionalBinds` all validated. |
564
- | **Atomic writes** | Config, credentials, sessions, skill state — all use tmp+rename pattern. |
565
-
566
-
567
- ---
568
-
569
- ## Provider Support
570
-
571
- ### LLM Providers
572
-
573
- Any provider supported by [pi-ai](https://github.com/nicepkg/pi-ai):
574
-
575
-
576
- | Provider | Example Model |
577
- | ------------- | -------------------------- |
578
- | **Anthropic** | `claude-sonnet-4-20250514` |
579
- | **Google** | `gemini-2.5-flash` |
580
- | **OpenAI** | `gpt-4o` |
581
- | **Groq** | `llama-3.3-70b-versatile` |
582
- | **Cerebras** | `llama-3.3-70b` |
583
- | **Mistral** | `mistral-large-latest` |
584
- | **xAI** | `grok-3` |
585
-
586
-
587
- ### Embedding Providers
588
-
589
- Optional — keyword search works without any API key.
590
-
591
-
592
- | Provider | Default Model | Local |
593
- | ----------- | ------------------------ | ------- |
594
- | **OpenAI** | `text-embedding-3-small` | |
595
- | **Gemini** | `gemini-embedding-001` | |
596
- | **Voyage** | `voyage-3-lite` | |
597
- | **Mistral** | `mistral-embed` | |
598
- | **Ollama** | `nomic-embed-text` | **Yes** |
215
+ Highlights:
599
216
 
217
+ - **[demo-workspace-real](demos/demo-workspace-real.ts)** — Full multi-tenant DevOps platform with 2 teams, skills, memory, credentials, and live agent execution
218
+ - **[demo-subagents](demos/demo-subagents.ts)** — Orchestrator delegates to specialist sub-agents
219
+ - **[demo-fallback](demos/demo-fallback.ts)** — Model fallback chains with simulated failures
600
220
 
601
221
  ---
602
222
 
@@ -613,7 +233,6 @@ src/
613
233
  │ ├── rootfs.ts Minimal rootfs with bind mounts
614
234
  │ ├── ipc.ts Bidirectional JSON-RPC 2.0 over stdio
615
235
  │ ├── seccomp.ts BPF syscall filter profiles
616
- │ ├── seccomp-apply.ts seccomp-BPF application via C helper
617
236
  │ ├── proxy-tools.ts memory/skill tools proxied to host via IPC
618
237
  │ └── worker.ts Agent entry point inside sandbox
619
238
  ├── credentials/ AES-256-GCM encrypted store + credential proxy
@@ -621,24 +240,15 @@ src/
621
240
  ├── runtime/ createRuntime() — single-user mode
622
241
  │ ├── failover-error.ts Error classification (ported from OpenClaw)
623
242
  │ ├── model-fallback.ts Fallback chains + cooldown tracking
624
- │ ├── context-guard.ts Context window guards (16K/32K thresholds)
625
- │ ├── retry.ts Exponential backoff with jitter + retryAfterMs
626
243
  │ ├── api-key-rotation.ts Per-provider key rotation
627
244
  │ ├── subagent.ts Parallel execution + lifecycle + registry
628
- ├── session-pruning.ts Trim old tool results in context
629
- │ ├── tool-result-truncation.ts Truncate oversized tool output
630
- │ ├── memory-flush.ts Pre-compaction memory save
631
- │ └── stream-adapters.ts Per-provider stream configuration
245
+ └── ... context-guard, retry, session-pruning, memory-flush
632
246
  ├── memory/ Hybrid search engine
633
247
  │ ├── hybrid.ts Vector + FTS5 fusion scoring
634
248
  │ ├── mmr.ts Maximal Marginal Relevance re-ranking
635
- │ ├── temporal-decay.ts Exponential time-based scoring
636
- │ ├── query-expansion.ts 7-language keyword expansion
637
- ├── embedding-cache.ts SQLite embedding cache
638
- │ ├── embedding-batch.ts Batch embedding with retry
639
- │ ├── file-indexer.ts File-based memory indexing with fs.watch
640
- │ ├── session-indexer.ts Session transcript indexing
641
- │ └── ssrf.ts SSRF protection for HTTP calls
249
+ │ ├── embeddings.ts 5-provider embedding support
250
+ │ ├── ssrf.ts SSRF protection for HTTP calls
251
+ └── ... temporal-decay, query-expansion, cache, indexers
642
252
  ├── hooks/ 6 lifecycle hook points
643
253
  ├── logging/ Structured JSON logging with file output
644
254
  ├── sessions/ File-based store with async locking
@@ -674,25 +284,21 @@ Every file is workspace-scoped. No shared state between tenants.
674
284
 
675
285
  ## Built on OpenClaw
676
286
 
677
- Bulkhead Runtime stands on the shoulders of [OpenClaw](https://github.com/nicepkg/openclaw). Several production-critical subsystems were ported directly from OpenClaw's codebase:
678
-
679
-
680
- | Subsystem | Origin |
681
- | ----------------------------------------- | ------------------------------------------------------------------------- |
682
- | **Model fallback & error classification** | `src/agents/model-fallback.ts`, `failover-error.ts`, `failover-policy.ts` |
683
- | **API key rotation** | `src/agents/api-key-rotation.ts`, `live-auth-keys.ts` |
684
- | **Context window guards** | `src/agents/context-window-guard.ts` |
685
- | **Retry with backoff** | `src/infra/retry.ts` |
686
- | **SSRF protection** | `src/infra/net/ssrf.ts`, `fetch-guard.ts` |
687
- | **Embedding cache & batch** | `extensions/memory-core/src/memory/manager-embedding-ops.ts` |
688
- | **File indexer** | `extensions/memory-core/src/memory/manager-sync-ops.ts` |
689
- | **Session transcript indexer** | `extensions/memory-core/src/memory/manager-sync-ops.ts` |
690
- | **Subagent orchestration** | `src/agents/subagent-*.ts` |
691
- | **Structured logging** | `src/logging/logger.ts`, `subsystem.ts`, `levels.ts` |
692
- | **Transcript events** | `src/sessions/transcript-events.ts` |
287
+ Bulkhead Runtime stands on the shoulders of [OpenClaw](https://github.com/nicepkg/openclaw). Several production-critical subsystems were ported directly:
693
288
 
289
+ | Subsystem | Origin |
290
+ |-----------|--------|
291
+ | **Model fallback & error classification** | `src/agents/model-fallback.ts`, `failover-error.ts` |
292
+ | **API key rotation** | `src/agents/api-key-rotation.ts`, `live-auth-keys.ts` |
293
+ | **Context window guards** | `src/agents/context-window-guard.ts` |
294
+ | **Retry with backoff** | `src/infra/retry.ts` |
295
+ | **SSRF protection** | `src/infra/net/ssrf.ts`, `fetch-guard.ts` |
296
+ | **Embedding cache & batch** | `extensions/memory-core/` |
297
+ | **File & session indexers** | `extensions/memory-core/` |
298
+ | **Subagent orchestration** | `src/agents/subagent-*.ts` |
299
+ | **Structured logging** | `src/logging/logger.ts`, `subsystem.ts` |
694
300
 
695
- OpenClaw solved these problems in production. We extracted, adapted, and integrated them into Bulkhead's multi-tenant architecture. Full credit where it's due.
301
+ OpenClaw solved these problems in production. We extracted, adapted, and integrated them into Bulkhead's multi-tenant architecture.
696
302
 
697
303
  ---
698
304
 
@@ -704,4 +310,4 @@ OpenClaw solved these problems in production. We extracted, adapted, and integra
704
310
 
705
311
  ## License
706
312
 
707
- [MIT](LICENSE)
313
+ [MIT](LICENSE)
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulkhead-runtime",
3
- "version": "2026.4.5-beta.5",
3
+ "version": "2026.4.5-beta.8",
4
4
  "description": "Multi-tenant AI agent runtime with OS-level isolation. Sandboxed execution, encrypted credentials, private memory per tenant — one server, no Docker.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,6 +29,35 @@
29
29
  "@mariozechner/pi-ai": "0.55.3",
30
30
  "@mariozechner/pi-coding-agent": "0.55.3"
31
31
  },
32
+ "keywords": [
33
+ "ai-agent",
34
+ "ai-agents",
35
+ "agent",
36
+ "agent-runtime",
37
+ "autonomous-agent",
38
+ "multi-agent",
39
+ "ai",
40
+ "llm",
41
+ "ai-infrastructure",
42
+ "ai-security",
43
+ "agent-security",
44
+ "mcp",
45
+ "model-context-protocol",
46
+ "tool-use",
47
+ "sandbox",
48
+ "secure-sandbox",
49
+ "sandboxing",
50
+ "runtime",
51
+ "isolation",
52
+ "process-isolation",
53
+ "tenant-isolation",
54
+ "multi-tenant",
55
+ "multitenant",
56
+ "security",
57
+ "credentials",
58
+ "encrypted-credentials",
59
+ "containerless"
60
+ ],
32
61
  "engines": {
33
62
  "node": ">=22.12.0"
34
63
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulkhead-runtime",
3
- "version": "2026.4.5-beta.6",
3
+ "version": "2026.4.5-beta.8",
4
4
  "description": "Multi-tenant AI agent runtime with OS-level isolation. Sandboxed execution, encrypted credentials, private memory per tenant — one server, no Docker.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -45,6 +45,35 @@
45
45
  "typescript": "^5.9.3",
46
46
  "vitest": "^4.1.2"
47
47
  },
48
+ "keywords": [
49
+ "ai-agent",
50
+ "ai-agents",
51
+ "agent",
52
+ "agent-runtime",
53
+ "autonomous-agent",
54
+ "multi-agent",
55
+ "ai",
56
+ "llm",
57
+ "ai-infrastructure",
58
+ "ai-security",
59
+ "agent-security",
60
+ "mcp",
61
+ "model-context-protocol",
62
+ "tool-use",
63
+ "sandbox",
64
+ "secure-sandbox",
65
+ "sandboxing",
66
+ "runtime",
67
+ "isolation",
68
+ "process-isolation",
69
+ "tenant-isolation",
70
+ "multi-tenant",
71
+ "multitenant",
72
+ "security",
73
+ "credentials",
74
+ "encrypted-credentials",
75
+ "containerless"
76
+ ],
48
77
  "engines": {
49
78
  "node": ">=22.12.0"
50
79
  },