Files
sand-boxer/docs/extension-sdk.md
tegwick 1415e17230 Implement SAND-WP-0006: SaaS payments, routing, and ext.saas-stub
Add credits store, metering on create/destroy, extension routing resolver,
metered SaaS stub extension, burst/saas profiles, credits CLI, docs, and tests.
2026-06-24 07:52:20 +02:00

3.0 KiB

Extension SDK

Author guide for sand-boxer backend adapters. Version 0.1 — SAND-WP-0005.

Contract

Every extension implements three methods:

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

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:
id: ext.my-backend
title: My Backend
handler: sandboxer.extensions.my_backend:MyExtension
capabilities:
  isolation_levels: [container]
  pricing_model: self-hosted
config:
  key: value
  1. Add a profile binding in profiles/profile.<slug>.yaml with extension: ext.my-backend.
  2. 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:

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
E2B / Modal / Daytona cloud adapters Post SAND-WP-0006
fin-hub billing export Future
Snapshot / restore hooks SAND-WP-0007