sand-boxer/docs/extension-sdk.md

126 lines
4 KiB
Markdown
Raw Normal View History

# Extension SDK
Author guide for sand-boxer backend adapters. Version 0.1 — SAND-WP-0005.
## Contract
Every extension implements three methods:
```text
provision(profile, inputs, host) → handle dict
wait_ready(handle) → reachability dict
teardown(handle) → cleanup report dict
```
Optional (SaaS): `estimate_cost(profile, duration) → MeterQuote`
Optional (checkpoints): `supports_snapshots()`, `snapshot(handle)`,
`restore_from_snapshot(profile, snapshot_meta, inputs, host)`
Optional (owner-mediated execution): `supports_execution()`, then
`execute(handle, command, credential_route_refs, execution_context,
timeout_seconds, max_output_bytes, stdin_text)`. The default implementation fails closed.
`ext.bwrap` is the reference implementation. An executing extension must
validate that its workspace belongs to the exact sandbox handle, sanitize the
child environment, use an argument vector, enforce the requested bounds, and
must not fall back to a host workspace.
Bounded stdin is content-bearing and must never be included in lifecycle logs or
execution evidence.
### Base class
```python
from sandboxer.extensions.base import SandboxExtension
class MyExtension(SandboxExtension):
def provision(self, profile, inputs, host): ...
def wait_ready(self, handle): ...
def teardown(self, handle): ...
```
Reference implementations:
| Extension | Module | Mode |
|-----------|--------|------|
| `ext.compose-ssh` | `compose_ssh.py` | Remote compose stack + tar snapshots |
| `ext.vm-packer` | `vm_packer.py` | Attach workspace or Packer build mode |
| `ext.saas-stub` | `saas_stub.py` | Metered stub + metadata snapshots |
| `ext.e2b` | `e2b.py` | E2B cloud adapter |
| `ext.modal` | `modal.py` | Modal cloud adapter |
| `ext.bwrap` | `bwrap.py` | Local namespaces + owner-mediated execution |
## Registration
1. Add `extensions/ext.<name>.yaml`:
```yaml
id: ext.my-backend
title: My Backend
handler: sandboxer.extensions.my_backend:MyExtension
capabilities:
isolation_levels: [container]
pricing_model: self-hosted
config:
key: value
```
2. Add a profile binding in `profiles/profile.<slug>.yaml` with `extension: ext.my-backend`.
3. Register capability metadata in `registry/` when ready for reuse-surface.
Loader validates `capabilities.isolation_levels` and `capabilities.pricing_model`
at startup (`sandboxer.extensions.registry`).
## Handle and reachability
**Handle** (returned by `provision`, stored in manager): opaque dict passed to
`wait_ready` and `teardown`. Include at minimum:
- `sandbox_id`
- `host` (placement host)
- Fields your extension needs for SSH/API (e.g. `remote_dir`, `vm_target`)
**Reachability** (returned by `wait_ready`): exposed on `SandboxStatus.reachability`:
- `ssh` — SSH destination string
- `remote_dir` — workspace path on remote
- `host` — placement host
- `compose_project` — compose-ssh only
## Inputs convention
Profiles declare semantics; extensions validate required `inputs` keys:
| Extension | Required inputs | Optional |
|-----------|-----------------|----------|
| compose-ssh | `repo` | `sandbox_id` |
| vm-packer (attach) | `vm` or `ssh_target` | `repo`, `tunnel_port`, `ssh_port`, `workspace_dir` |
| vm-packer (build) | `packer_template`, `vm_name` | `mode=build`, `packer_var_*` |
Consumer attribution travels on `SandboxCreateRequest.consumer`, not extension inputs.
## Testing
Mock SSH/subprocess in unit tests. See `tests/test_compose_ssh.py`, `tests/test_vm_packer.py`.
Pattern:
```python
with patch.object(SSHConfig, "run", return_value=(0, "ready")):
ext = VMPackerExtension()
handle = ext.provision(profile, {"vm": "haskell-build"}, "localhost")
```
## Metered extensions (SAND-WP-0006)
Implement `estimate_cost` and `meter_actual` on `SandboxExtension`. Register with
`pricing_model: metered`. See `docs/payments.md` and `ext.saas-stub`.
## Deferred
| Feature | Workplan |
|---------|----------|
| Packer build orchestration from `create` | Future WP |
| Daytona OSS cloud adapter | Future WP |
| fin-hub billing export | Future |
| Cross-host snapshot transfer | Future |