generated from coulomb/repo-seed
Implement SAND-WP-0005: extension SDK and ext.vm-packer
Add SandboxExtension base class, extension SDK docs, vm-packer attach mode for build-machines VMs, profile.vm-haskell-build, SSH port support, tests, and migration docs.
This commit is contained in:
102
docs/extension-sdk.md
Normal file
102
docs/extension-sdk.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# 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, deferred): `estimate_cost(profile, duration) → MeterQuote`
|
||||
|
||||
### 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 |
|
||||
| `ext.vm-packer` | `vm_packer.py` | Attach workspace on pre-built VM |
|
||||
|
||||
## 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 | `vm` or `ssh_target` | `repo`, `tunnel_port`, `ssh_port`, `workspace_dir` |
|
||||
|
||||
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")
|
||||
```
|
||||
|
||||
## Deferred
|
||||
|
||||
| Feature | Workplan |
|
||||
|---------|----------|
|
||||
| Packer build orchestration from `create` | Future WP |
|
||||
| SaaS adapters + `estimate_cost` | SAND-WP-0006 |
|
||||
| Multi-backend routing engine | SAND-WP-0006 |
|
||||
| Snapshot / restore hooks | SAND-WP-0007 |
|
||||
62
docs/migration-build-machines.md
Normal file
62
docs/migration-build-machines.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Migration — build-machines → ext.vm-packer
|
||||
|
||||
Maps `the-custodian/infra/build-machines/` to sand-boxer `profile.vm-haskell-build`.
|
||||
|
||||
## What moved
|
||||
|
||||
| Legacy (build-machines) | sand-boxer v0 |
|
||||
|-------------------------|---------------|
|
||||
| Packer OVA build | **Unchanged** — operator runs Packer in the-custodian |
|
||||
| VM boot + build-agent registration | **Unchanged** — systemd agent on VM |
|
||||
| `make remote-build PROJECT=` | `sandboxer create` + SSH into `reachability.remote_dir` |
|
||||
| Isolated workspace `/build/<project>` | `/build/sbx-<sandbox_id>/` per create |
|
||||
| `make bridge-status` | `ssh -p 12222 build@localhost` or `sandboxer inspect` (future) |
|
||||
|
||||
## v0 attach workflow
|
||||
|
||||
1. Build/import VM per [build-machines README](~/the-custodian/infra/build-machines/README.md).
|
||||
2. Ensure tunnel is up (`make bridge-status` in build-machines).
|
||||
3. Create sand-boxer workspace:
|
||||
|
||||
```bash
|
||||
export SANDBOXER_VM_TUNNEL_PORT=12222 # or use SSH alias
|
||||
|
||||
sandboxer create \
|
||||
--profile profile.vm-haskell-build \
|
||||
--input vm=haskell-build \
|
||||
--input repo=~/projects/my-haskell-app \
|
||||
--host localhost
|
||||
```
|
||||
|
||||
4. Run builds on VM:
|
||||
|
||||
```bash
|
||||
ssh haskell-build "cd <remote_dir> && source ~/.ghcup/env && cabal build all"
|
||||
```
|
||||
|
||||
5. Destroy workspace (VM stays running):
|
||||
|
||||
```bash
|
||||
sandboxer destroy <sandbox_id>
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
| Input | Purpose |
|
||||
|-------|---------|
|
||||
| `vm` | SSH config alias (e.g. `haskell-build`) |
|
||||
| `ssh_target` | Alias for `vm` |
|
||||
| `tunnel_port` | Local reverse-tunnel port (default via `SANDBOXER_VM_TUNNEL_PORT`) |
|
||||
| `repo` | Optional rsync source to workspace |
|
||||
| `workspace_dir` | Override workspace path on VM |
|
||||
|
||||
## Not migrated yet
|
||||
|
||||
- Automated Packer `create` trigger from sand-boxer API
|
||||
- State Hub capability-catalog sync from build-agent (agent unchanged)
|
||||
- Port registry automation (`port-registry.yml`)
|
||||
- `make remote-build` Makefile targets in the-custodian (add shim in follow-on if needed)
|
||||
|
||||
## Runbook
|
||||
|
||||
`docs/runbooks/profile-vm-haskell-build.md`
|
||||
@@ -28,9 +28,21 @@ Recorded after SAND-WP-0002-T10 remote verification on CoulombCore (`92.205.130.
|
||||
**e2e-framework migration arc complete** (provision: sand-boxer, validation:
|
||||
wise-validator, operator entry: `make e2e`).
|
||||
|
||||
## build-machines (SAND-WP-0005) — attach mode delivered
|
||||
|
||||
| Legacy (`build-machines`) | sand-boxer today | Notes |
|
||||
|---------------------------|------------------|-------|
|
||||
| Packer OVA build | Operator-driven (unchanged) | Not triggered by `create` |
|
||||
| `make remote-build` rsync + SSH | `sandboxer create --profile profile.vm-haskell-build` | Workspace `/build/sbx-<id>/` |
|
||||
| VM teardown | N/A | `destroy` removes workspace only; VM persists |
|
||||
| Extension author contract | `docs/extension-sdk.md` | `SandboxExtension` base class |
|
||||
|
||||
Deferred: Packer orchestration from API, `make remote-build` shim.
|
||||
|
||||
## sand-boxer follow-ons
|
||||
|
||||
| Item | Workplan |
|
||||
|------|----------|
|
||||
| Self-canary + host telemetry | SAND-WP-0008 |
|
||||
| Default `sandboxer create` without repo | SAND-WP-0008-T06 |
|
||||
| SaaS extensions + payments | SAND-WP-0006 |
|
||||
| Snapshot / restore | SAND-WP-0007 |
|
||||
| TTL enforcement + scheduled reap | TBD |
|
||||
50
docs/runbooks/profile-vm-haskell-build.md
Normal file
50
docs/runbooks/profile-vm-haskell-build.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# profile.vm-haskell-build — Runbook
|
||||
|
||||
Attach an isolated Haskell build workspace on a pre-built VM (build-machines lineage).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- VM built/imported per `the-custodian/infra/build-machines/`
|
||||
- SSH tunnel up (`make bridge-status` in build-machines)
|
||||
- `~/.ssh/config` entry for `haskell-build` (or `tunnel_port` / `SANDBOXER_VM_TUNNEL_PORT`)
|
||||
- `sandboxer` on PATH
|
||||
|
||||
## Create workspace
|
||||
|
||||
```bash
|
||||
# Via SSH alias (recommended):
|
||||
sandboxer create \
|
||||
--profile profile.vm-haskell-build \
|
||||
--input vm=haskell-build \
|
||||
--input repo=~/projects/my-app \
|
||||
--host localhost
|
||||
|
||||
# Via tunnel port:
|
||||
export SANDBOXER_VM_TUNNEL_PORT=12222
|
||||
sandboxer create \
|
||||
--profile profile.vm-haskell-build \
|
||||
--input vm=build@localhost \
|
||||
--input tunnel_port=12222 \
|
||||
--input repo=~/projects/my-app \
|
||||
--host localhost
|
||||
```
|
||||
|
||||
## Build on VM
|
||||
|
||||
Use `reachability.remote_dir` from create output:
|
||||
|
||||
```bash
|
||||
ssh haskell-build "cd /build/sbx-<id> && source ~/.ghcup/env && cabal build all"
|
||||
```
|
||||
|
||||
## Destroy
|
||||
|
||||
```bash
|
||||
sandboxer destroy <sandbox_id>
|
||||
```
|
||||
|
||||
Removes workspace only; the VM keeps running.
|
||||
|
||||
## Migration reference
|
||||
|
||||
`docs/migration-build-machines.md`
|
||||
Reference in New Issue
Block a user