Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 266 additions & 0 deletions browsers/process-execution.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
---
title: "Process Execution"
description: "Run commands and manage processes alongside the browser"
---

every KERNEL browser runs in a full linux environment. playwright controls what happens in the page; process execution lets you run commands, scripts, and binaries alongside the browser.

use process execution when your browser automation needs software or system access outside a playwright callback. code running alongside the browser can access the same files and local browser services, so you can process downloads without transferring them to your application first, run existing command-line tools, or keep a high-frequency agent loop close to the browser.

use `process.exec` for bounded commands where you need the result before continuing. use `process.spawn` for agents, servers, watchers, interactive shells, and other long-running processes. after spawning a process, you can inspect its status, stream its output, send input, resize its pty, or terminate it.

if your task only needs the `page`, `context`, and `browser` objects, use [playwright execution](/browsers/playwright-execution). use process execution when you need an executable, operating-system tools, a long-running process, or direct filesystem access. for workloads that need their own deployment and invocation lifecycle, use [KERNEL apps](/apps).

## common production patterns

- **co-locate an agent with its browser.** browser agents often make many small tool calls. upload an agent binary with [file i/o](/browsers/file-io), run it alongside chromium, and point it at the local playwright endpoint to remove a round trip through KERNEL's public api from each call. see the [fx co-located agent cookbook](https://github.com/kernel/cookbooks/tree/main/integrations/fx-colocated-agent) for an end-to-end example.
- **process downloads before retrieving them.** unpack archives, extract text from documents, resize images, or compress a directory while the files are still alongside the browser, then retrieve only the final artifacts with file i/o.
- **run existing command-line tools.** upload a pinned binary or script and call it from the same workflow instead of rewriting it as browser automation code.
- **start a session-local helper.** run a local server, callback handler, or file watcher for as long as the browser task needs it.
- **inspect failed browser tasks.** run tools such as `curl`, `ps`, and `ls` to inspect network responses, running processes, logs, and downloaded files before the browser is deleted.

## Run a command synchronously

`process.exec` runs a command and blocks until it exits or times out. `stdout_b64` and `stderr_b64` are base64-encoded, and `exit_code` tells you whether it succeeded.

<CodeGroup>
```typescript Typescript/Javascript
import Kernel from '@onkernel/sdk';

const kernel = new Kernel();

const browser = await kernel.browsers.create({});

const result = await kernel.browsers.process.exec(browser.session_id, {
command: 'ls',
args: ['-la', '/tmp'],
});

console.log(Buffer.from(result.stdout_b64 ?? '', 'base64').toString());
console.log('exit code', result.exit_code);
```

```python Python
import base64

from kernel import Kernel

kernel = Kernel()

browser = kernel.browsers.create()

result = kernel.browsers.process.exec(
browser.session_id,
command="ls",
args=["-la", "/tmp"],
)

print(base64.b64decode(result.stdout_b64 or "").decode())
print("exit code", result.exit_code)
```
</CodeGroup>

Use `cwd` to set a working directory, `env` to pass environment variables, `as_user`/`as_root` to control privileges, and `timeout_sec` to cap execution time.

## Run a command in the background

`process.spawn` starts a command without waiting for it to finish, returning a `process_id` you use to manage it afterward. This is the right call for long-running processes — a server, a watcher script, an interactive shell.

<Warning>
If your long-running process is a server, pick a port yourself and don't assume it's free — the VM's own infrastructure (live view, CDP, the playwright daemon) already listens on several, including `8080`, `9222`–`9225`, `8888`, `10001`, and `10002`.
</Warning>

<CodeGroup>
```typescript Typescript/Javascript
const spawned = await kernel.browsers.process.spawn(browser.session_id, {
command: 'sh',
args: ['-c', 'while true; do echo "tick $(date +%s)"; sleep 5; done'],
});

console.log('process_id', spawned.process_id);
```

```python Python
spawned = kernel.browsers.process.spawn(
browser.session_id,
command="sh",
args=["-c", 'while true; do echo "tick $(date +%s)"; sleep 5; done'],
)

print("process_id", spawned.process_id)
```
</CodeGroup>

Pass `allocate_tty: true` to attach a pseudo-terminal for interactive shells, with `cols`/`rows` to set its initial size.

## Manage a running process

### Check status

Poll for whether a spawned process is still running, and its resource usage:

<CodeGroup>
```typescript Typescript/Javascript
const status = await kernel.browsers.process.status(spawned.process_id, {
id_or_name: browser.session_id,
});

console.log(status.state, status.exit_code);
```

```python Python
status = kernel.browsers.process.status(
spawned.process_id,
id_or_name=browser.session_id,
)

print(status.state, status.exit_code)
```
</CodeGroup>

### Stream stdout and stderr

Read output from a spawned process as it happens over server-sent events:

<CodeGroup>
```typescript Typescript/Javascript
const stream = await kernel.browsers.process.stdoutStream(spawned.process_id, {
id_or_name: browser.session_id,
});

for await (const chunk of stream) {
if (chunk.event === 'exit') {
console.log('exited with', chunk.exit_code);
break;
}
console.log(chunk.stream, Buffer.from(chunk.data_b64 ?? '', 'base64').toString());
}
```

```python Python
import base64

with kernel.browsers.process.stdout_stream(
spawned.process_id, id_or_name=browser.session_id
) as stream:
for chunk in stream:
if chunk.event == "exit":
print("exited with", chunk.exit_code)
break
print(chunk.stream, base64.b64decode(chunk.data_b64 or "").decode())
```
</CodeGroup>

### Write to stdin

Send base64-encoded input to a running process, e.g. to answer an interactive prompt:

<CodeGroup>
```typescript Typescript/Javascript
await kernel.browsers.process.stdin(spawned.process_id, {
id_or_name: browser.session_id,
data_b64: Buffer.from('y\n').toString('base64'),
});
```

```python Python
import base64

kernel.browsers.process.stdin(
spawned.process_id,
id_or_name=browser.session_id,
data_b64=base64.b64encode(b"y\n").decode(),
)
```
</CodeGroup>

### Resize a PTY

Resizing only works on a process spawned with `allocate_tty: true` — calling it on a plain process returns a 400. Match the terminal to a live view or client window:

<CodeGroup>
```typescript Typescript/Javascript
const shell = await kernel.browsers.process.spawn(browser.session_id, {
command: 'sh',
allocate_tty: true,
cols: 80,
rows: 24,
});

await kernel.browsers.process.resize(shell.process_id, {
id_or_name: browser.session_id,
cols: 120,
rows: 40,
});
```

```python Python
shell = kernel.browsers.process.spawn(
browser.session_id,
command="sh",
allocate_tty=True,
cols=80,
rows=24,
)

kernel.browsers.process.resize(
shell.process_id,
id_or_name=browser.session_id,
cols=120,
rows=40,
)
```
</CodeGroup>

### Kill a process

<CodeGroup>
```typescript Typescript/Javascript
await kernel.browsers.process.kill(spawned.process_id, {
id_or_name: browser.session_id,
signal: 'TERM',
});
```

```python Python
kernel.browsers.process.kill(
spawned.process_id,
id_or_name=browser.session_id,
signal="TERM",
)
```
</CodeGroup>

`signal` accepts `TERM`, `KILL`, `INT`, or `HUP`.

## Root and per-user execution

Pass `as_root: true` or `as_user: "<name>"` on `exec` or `spawn` to control which user the command runs as. This is safe because a Kernel browser is a [unikernel VM](/security#2-4-security-features) with no shared host kernel — root inside your session has no path to other customers or platform infrastructure.

## Via CLI

The [CLI](/reference/cli/browsers#process-control) exposes the same operations:

```bash
# Synchronous
kernel browsers process exec <session-id> --command ls --args -la

# Background, then inspect
kernel browsers process spawn <session-id> --command python3 --args -m --args http.server
kernel browsers process status <session-id> <process-id>
kernel browsers process kill <session-id> <process-id>
```

## Related

<CardGroup cols={3}>
<Card title="File I/O" icon="folder" href="/browsers/file-io">
Upload and download files from a browser VM
</Card>
<Card title="SSH Access" icon="terminal" href="/browsers/ssh">
Open an interactive SSH session for debugging
</Card>
<Card title="CLI reference" icon="square-terminal" href="/reference/cli/browsers#process-control">
All `kernel browsers process` subcommands and flags
</Card>
</CardGroup>
4 changes: 4 additions & 0 deletions cookbooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,8 @@ import { CookbookSearch } from '/snippets/cookbook-search.jsx';
<div className="cookbook-tag">harnesses & models</div>
Connect e2b sandboxes with KERNEL browsers.
</Card>
<Card title="fx, Co-located" img="/images/cookbooks/vercel.png" href="https://github.com/kernel/cookbooks/tree/main/integrations/fx-colocated-agent">
<div className="cookbook-tag">harnesses & models</div>
Run Vercel's fx agent inside a KERNEL browser VM, co-located with the browser it drives.
</Card>
</Columns>
4 changes: 4 additions & 0 deletions cookbooks/fx-colocated-agent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "fx, Co-located"
url: "https://github.com/kernel/cookbooks/tree/main/integrations/fx-colocated-agent"
---
2 changes: 2 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
"info/api-keys",
"info/audit-logs",
"browsers/file-io",
"browsers/process-execution",
"browsers/curl",
"browsers/ssh",
"browsers/computer-controls",
Expand Down Expand Up @@ -327,6 +328,7 @@
"cookbooks/e2b",
"cookbooks/eve-foreman",
"cookbooks/eve-managed-auth",
"cookbooks/fx-colocated-agent",
"cookbooks/mastra-web-task-assistant",
"cookbooks/modal-web-scraper",
"cookbooks/modal-pr-qa-agent",
Expand Down
Loading