Skip to content
Merged
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
31 changes: 31 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Validate kit

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install Docker Sandboxes CLI
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release download \
--repo docker/sbx-releases \
--pattern DockerSandboxes-linux-amd64.tar.gz \
--dir /tmp
tar -xzf /tmp/DockerSandboxes-linux-amd64.tar.gz -C /tmp
sudo env PREFIX="$HOME/.local/docker-sbx" /tmp/docker-sbx/install.sh
echo "$HOME/.local/docker-sbx/bin" >> "$GITHUB_PATH"

- name: Validate kit
run: scripts/smoke.sh
109 changes: 37 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,63 @@
# Kernel Docker Sandbox Mixin
# Kernel kit for Docker Sandboxes

This repo contains a [Docker Sandboxes](https://docs.docker.com/ai/sandboxes/) (`sbx`) mixin kit for [Kernel](https://www.kernel.sh). It gives an agent sandbox Kernel tooling, Kernel skills for Claude Code, and proxy-managed Kernel API authentication without putting your real `KERNEL_API_KEY` inside the sandbox.
This mixin gives any [Docker Sandbox](https://docs.docker.com/ai/sandboxes/) agent access to [Kernel](https://www.kernel.sh/) cloud browsers. It installs the Kernel CLI, adds a quick-reference guide, permits the required network destinations, and keeps the Kernel API key outside the sandbox.

## Quickstart
Docker publishes the kit at [`docker.io/sbx/kernel-kit`](https://hub.docker.com/r/sbx/kernel-kit) from the [`docker/sbx-kits-contrib`](https://github.com/docker/sbx-kits-contrib/tree/main/kernel) repository.

### 1. Install Docker Sandboxes
## Use the published kit

Install and sign in to `sbx` using the [Docker Sandboxes getting started guide](https://docs.docker.com/ai/sandboxes/get-started/).
1. Install Docker Sandboxes and sign in by following Docker's [getting started guide](https://docs.docker.com/ai/sandboxes/get-started/).
2. Create a Kernel API key, then store it in Docker Sandboxes' host-side secret store:

### 2. Set Up Kernel
```console
sbx secret set kernel
```

Create or use a Kernel account at [kernel.sh](https://www.kernel.sh), then create an API key.
3. Launch an agent with the kit:

Export the key in the host shell where you run `sbx`:
```console
sbx run claude --kit docker.io/sbx/kernel-kit:latest
```

```bash
export KERNEL_API_KEY=...
```

The real key stays on the host. This kit configures the `sbx` proxy so Kernel API requests from inside the sandbox receive the right auth header.
On first use, `sbx` asks you to approve injecting the `kernel` credential into requests to `api.onkernel.com`. The sandbox receives only a `proxy-managed` sentinel; the host proxy replaces it with the real key when the request leaves the sandbox.

### 3. Set Up Claude
## Develop locally

The built-in Claude sandbox needs Anthropic credentials. Export your API key in the same host shell:
Validate and inspect the kit before creating a sandbox:

```bash
export ANTHROPIC_API_KEY=...
```console
sbx kit validate .
sbx kit inspect .
```

### 4. Launch Claude With Kernel

Start Claude with this mixin:
Run the non-destructive checks with:

```bash
sbx run --name kernel-demo --kit . claude -- "Using the Kernel CLI, create a browser and navigate to news.ycombinator.com. Tell me the top five articles."
```console
scripts/smoke.sh
```

The agent should be able to call `kernel` and efficiently complete the task using the installed Kernel skills inside the sandbox without seeing the real `KERNEL_API_KEY`.

## What The Mixin Does

The mixin installs Kernel's CLI:
Run the full smoke test with a disposable Claude sandbox after storing the Kernel credential:

```yaml
commands:
install:
- command: "npm install -g @onkernel/cli"
```console
scripts/smoke.sh --create
```

It also installs all agent skills from [`kernel/skills`](https://github.com/kernel/skills):
The full test verifies the CLI, bundled quick-reference guide, proxy-managed environment variable, and an authenticated Kernel API request. Set `KEEP_SANDBOX=1` to retain the sandbox for debugging.

```yaml
commands:
install:
- command: "DISABLE_TELEMETRY=1 npm_config_update_notifier=false npx -y skills add kernel/skills --skill '*' --agent claude-code --global --copy --yes && rm -rf \"$HOME/.agents/skills\" && mkdir -p \"$HOME/.agents\" && cp -a \"$HOME/.claude/skills\" \"$HOME/.agents/skills\""
user: "1000"
```

Those flags make the `skills` install noninteractive: select all skills, target Claude Code, install globally into the sandbox agent user's home, copy files instead of symlinking, and accept prompts. After the CLI install, the command copies the resulting `~/.claude/skills` tree to `~/.agents/skills` so agents that read the generic skills location can use the same Kernel skills.

It allows the package registry, GitHub, skills metadata, and Kernel API:

```yaml
network:
allowedDomains:
- "registry.npmjs.org:443"
- "github.com:443"
- "api.github.com:443"
- "raw.githubusercontent.com:443"
- "release-assets.githubusercontent.com:443"
- "add-skill.vercel.sh:443"
- "skills.sh:443"
- "api.onkernel.com:443"
```
## Publish an organization-owned copy

It maps `api.onkernel.com` to a host-side credential source named `kernel`:
Docker Hub publication uses an OCI artifact rather than a container image:

```yaml
credentials:
sources:
kernel:
env:
- KERNEL_API_KEY
```console
sbx login
sbx kit validate .
sbx kit push . docker.io/onkernel/kernel-kit:latest --sign
```

The proxy injects the API key as an authorization header for Kernel API requests:
The Docker Verified Publisher badge is granted at the Docker Hub namespace level. Pushing a kit does not grant the badge; the `onkernel` namespace must complete Docker's [Verified Publisher application](https://hub.docker.com/publisher-program/apply) separately.

```yaml
network:
serviceDomains:
api.onkernel.com: kernel
serviceAuth:
kernel:
headerName: Authorization
valueFormat: "Bearer %s"
```
## Kit contents

- `spec.yaml` — schema v2 mixin definition
- `files/home/.kernel/quickstart.md` — examples installed into the agent's home directory
- `scripts/smoke.sh` — local validation and optional end-to-end test
112 changes: 112 additions & 0 deletions files/home/.kernel/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Kernel Browser — Quick Reference

`KERNEL_API_KEY` is proxy-managed: the sandbox holds a placeholder and the
proxy injects the real credential on outbound requests to `api.onkernel.com`.
The real key never enters the VM.

---

## TypeScript / JavaScript

Add the SDK as a project dependency:

npm install @onkernel/sdk playwright-core

Use `playwright-core` (not `playwright`) — it provides `connectOverCDP`
without downloading local Chromium binaries that you won't use.

import Kernel from '@onkernel/sdk';
import { chromium } from 'playwright-core';

const kernel = new Kernel();
const session = await kernel.browsers.create({ headless: true });

const browser = await chromium.connectOverCDP(session.cdp_ws_url);
const page = browser.contexts()[0].pages()[0];

await page.goto('https://example.com');
console.log(await page.title());

await browser.close();
await kernel.browsers.deleteByID(session.session_id);

---

## Python

Add the SDK as a project dependency:

PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 pip install kernel playwright

The env var prevents Playwright from downloading local Chromium binaries.

import asyncio
from kernel import Kernel
from playwright.async_api import async_playwright

kernel = Kernel()
session = kernel.browsers.create()

async def run():
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(session.cdp_ws_url)
page = browser.contexts[0].pages[0]
await page.goto('https://example.com')
print(await page.title())
await browser.close()
kernel.browsers.delete_by_id(session.session_id)

asyncio.run(run())

---

## Stealth mode (residential proxy + CAPTCHA bypass)

const session = await kernel.browsers.create({ stealth: true });

---

## GPU acceleration (vision-based / computer-use agents)

const session = await kernel.browsers.create({ headless: false, gpu: true });

Requires Start-Up or Enterprise plan.

---

## Session replays (record as MP4)

const { replay_id: replayId } = await kernel.browsers.replays.start(session.session_id);
// ... do work ...
await kernel.browsers.replays.stop(replayId, { id_or_name: session.session_id });

Download recordings from the Kernel dashboard or via API.

---

## Browser pools (pre-warmed for sub-30ms acquisition)

const pool = await kernel.browserPools.create({ size: 5, headless: true });
const session = await kernel.browserPools.acquire(pool.id, {});
// ... use session ...
await kernel.browserPools.release(pool.id, { session_id: session.session_id });

---

## Browser profiles (persist login state across sessions)

const profile = await kernel.profiles.create({ name: 'my-account' });
const session = await kernel.browsers.create({
profile: { name: 'my-account', save_changes: true },
});
// Log in once — subsequent sessions load the saved profile automatically.

---

## CLI

kernel browsers create --headless
kernel browsers create --stealth
kernel browsers list
kernel browsers delete <session-id>
kernel auth # show active user + token expiry
28 changes: 21 additions & 7 deletions scripts/install-sbx-linux.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail

DEB_URL="${SBX_DEB_URL:-https://github.com/docker/sbx-releases/releases/latest/download/DockerSandboxes-linux-amd64-ubuntu2404.deb}"

if command -v sbx >/dev/null 2>&1; then
echo "sbx is already installed: $(command -v sbx)"
sbx version || true
Expand All @@ -15,17 +13,33 @@ if [[ "$(uname -s)" != "Linux" ]]; then
fi

case "$(uname -m)" in
x86_64 | amd64) ;;
x86_64 | amd64) ARCH=amd64 ;;
aarch64 | arm64) ARCH=arm64 ;;
*)
echo "Unsupported architecture: $(uname -m)" >&2
exit 1
;;
esac

if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
source /etc/os-release
fi

case "${ID:-}:${VERSION_ID:-}" in
ubuntu:24.04) UBUNTU_VERSION=2404 ;;
ubuntu:26.04) UBUNTU_VERSION=2604 ;;
*)
echo "Docker Sandboxes currently publishes Linux amd64 packages for this path." >&2
echo "This helper supports Ubuntu 24.04 and 26.04. Follow Docker's installation guide for this distribution." >&2
exit 1
;;
esac

DEB_URL="${SBX_DEB_URL:-https://github.com/docker/sbx-releases/releases/latest/download/DockerSandboxes-linux-${ARCH}-ubuntu${UBUNTU_VERSION}.deb}"
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

echo "Adding Docker apt repository"
echo "Adding Docker's apt repository"
curl -fsSL https://get.docker.com | sudo REPO_ONLY=1 sh

echo "Installing docker-sbx from apt"
Expand All @@ -36,8 +50,8 @@ if sudo apt-get install -y docker-sbx; then
exit 0
fi

echo "docker-sbx is not available from this apt repository."
echo "Falling back to the Ubuntu 24.04 release .deb: $DEB_URL"
echo "docker-sbx is not available from the configured apt repository."
echo "Falling back to $DEB_URL"

deb="$tmpdir/docker-sbx.deb"
curl -fsSL "$DEB_URL" -o "$deb"
Expand Down
Loading
Loading