generated from coulomb/repo-seed
Implement SAND-WP-0002 meta-framework foundation (T01–T09)
Add meta-framework spec, pydantic schemas, profile/extension YAML, extension registry, ext.compose-ssh backend, SandboxManager with State Hub events, CLI commands, integration docs, capability registry entry, and compose-e2e runbook. Nine unit tests pass. T10 remote smoke test remains for operator.
This commit is contained in:
38
docs/integrations/glas-harness.md
Normal file
38
docs/integrations/glas-harness.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# glas-harness integration
|
||||
|
||||
glas-harness owns the agent gateway, tools, memory, and channels. sand-boxer
|
||||
delivers an isolated execution environment; the harness executes tools inside it.
|
||||
|
||||
## Example request
|
||||
|
||||
```bash
|
||||
sandboxer create \
|
||||
--profile profile.agent-dev \
|
||||
--input repo=/path/to/workspace \
|
||||
--actor agt \
|
||||
--project glas-harness
|
||||
```
|
||||
|
||||
## Response fields (ready state)
|
||||
|
||||
| Field | Owner | Description |
|
||||
|-------|-------|-------------|
|
||||
| `sandbox_id` | sand-boxer | Stable instance identifier |
|
||||
| `reachability.ssh` | sand-boxer | SSH target for harness exec channel |
|
||||
| `reachability.remote_dir` | sand-boxer | Workspace root on remote host |
|
||||
| `state` | sand-boxer | Lifecycle state (`ready`, etc.) |
|
||||
|
||||
## Ownership
|
||||
|
||||
| Concern | Owner |
|
||||
|---------|-------|
|
||||
| Sandbox provision / teardown | sand-boxer |
|
||||
| Tool call parsing and policies | glas-harness |
|
||||
| SSH / tunnel reachability setup | glas-harness + ops-bridge |
|
||||
| Agent memory and session state | glas-harness |
|
||||
|
||||
## Out of scope for sand-boxer
|
||||
|
||||
- Tool schemas and approval flows
|
||||
- Channel bridges (Slack, email, etc.)
|
||||
- Subagent orchestration
|
||||
37
docs/integrations/snuggle-inventor.md
Normal file
37
docs/integrations/snuggle-inventor.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# snuggle-inventor integration
|
||||
|
||||
snuggle-inventor owns code generation and modernization workflows. sand-boxer
|
||||
provides a build sandbox with setup metadata resolved at the provision boundary.
|
||||
|
||||
## Example request
|
||||
|
||||
```bash
|
||||
sandboxer create \
|
||||
--profile profile.build \
|
||||
--input repo=/path/to/target \
|
||||
--actor agt \
|
||||
--project snuggle-inventor
|
||||
```
|
||||
|
||||
## Response fields (ready state)
|
||||
|
||||
| Field | Owner | Description |
|
||||
|-------|-------|-------------|
|
||||
| `sandbox_id` | sand-boxer | Build environment instance |
|
||||
| `reachability.ssh` | sand-boxer | Remote workspace access |
|
||||
| `profile.setup.secret_refs` | sand-boxer resolves | Secrets never returned to agent context |
|
||||
|
||||
## Ownership
|
||||
|
||||
| Concern | Owner |
|
||||
|---------|-------|
|
||||
| Sandbox provision and teardown | sand-boxer |
|
||||
| Setup instructions content (Blitzy-style) | snuggle-inventor |
|
||||
| Generated code and PR output | snuggle-inventor |
|
||||
| Secret resolution at boundary | sand-boxer (via ops-warden / OpenBao) |
|
||||
|
||||
## Out of scope for sand-boxer
|
||||
|
||||
- Code generation prompts and tech specs
|
||||
- AAP-style planning
|
||||
- PR creation and review loops
|
||||
40
docs/integrations/wise-validator.md
Normal file
40
docs/integrations/wise-validator.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# wise-validator integration
|
||||
|
||||
wise-validator owns e2e test orchestration, health check semantics, and pass/fail
|
||||
interpretation. sand-boxer delivers the compose environment; wise-validator runs
|
||||
the validation story on top.
|
||||
|
||||
## Example request
|
||||
|
||||
```bash
|
||||
sandboxer create \
|
||||
--profile profile.compose-e2e \
|
||||
--input repo=/path/to/repo \
|
||||
--actor atm \
|
||||
--project wise-validator
|
||||
```
|
||||
|
||||
## Response fields (ready state)
|
||||
|
||||
| Field | Owner | Description |
|
||||
|-------|-------|-------------|
|
||||
| `sandbox_id` | sand-boxer | Instance id for the validation run |
|
||||
| `reachability.ssh` | sand-boxer | SSH endpoint for test execution |
|
||||
| `reachability.compose_project` | sand-boxer | Docker compose project name |
|
||||
| `reachability.remote_dir` | sand-boxer | Synced repo path on remote host |
|
||||
|
||||
## Ownership
|
||||
|
||||
| Concern | Owner |
|
||||
|---------|-------|
|
||||
| Environment provision (rsync + compose up) | sand-boxer |
|
||||
| `e2e/e2e.yml` parsing and semantics | wise-validator |
|
||||
| HTTP health polling and timeouts | wise-validator |
|
||||
| Test command execution and reporting | wise-validator |
|
||||
| State Hub test result events | wise-validator |
|
||||
|
||||
## Out of scope for sand-boxer
|
||||
|
||||
- Running `test_command` from e2e.yml
|
||||
- Interpreting health check pass/fail
|
||||
- Posting validation results to State Hub
|
||||
187
docs/meta-framework.md
Normal file
187
docs/meta-framework.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# sand-boxer meta-framework specification
|
||||
|
||||
Version 0.1 — derived from `research/03-meta-framework-synthesis.md` and `INTENT.md`.
|
||||
|
||||
sand-boxer is the **sandbox establishment service**: one API for consumers, many
|
||||
extension backends. It provisions *where and how* code runs; sibling projects own
|
||||
agent harnessing, validation, and code generation.
|
||||
|
||||
---
|
||||
|
||||
## Resource model
|
||||
|
||||
| Resource | Description |
|
||||
|----------|-------------|
|
||||
| **Profile** | Named, versioned sandbox recipe: extension binding, isolation, network, TTL, placement |
|
||||
| **Extension** | Backend adapter implementing provision / wait_ready / teardown |
|
||||
| **Host** | Registered placement target for self-hosted extensions |
|
||||
| **Sandbox** | Running instance of a profile |
|
||||
| **Snapshot** | Point-in-time workspace checkpoint (deferred — SAND-WP-0003) |
|
||||
| **Route** | Extension selection policy when multiple backends qualify |
|
||||
| **Meter** | Usage record for payments layer (SaaS extensions — SAND-WP-0006) |
|
||||
|
||||
---
|
||||
|
||||
## Lifecycle states
|
||||
|
||||
```
|
||||
requested → provisioning → ready → active → { expired | failed } → destroying → destroyed
|
||||
```
|
||||
|
||||
| State | Meaning |
|
||||
|-------|---------|
|
||||
| `requested` | Create accepted; not yet handed to extension |
|
||||
| `provisioning` | Extension running provision + wait_ready |
|
||||
| `ready` | Reachability confirmed; consumer may attach |
|
||||
| `active` | Consumer has marked sandbox in use (optional transition) |
|
||||
| `expired` | TTL elapsed before explicit destroy |
|
||||
| `failed` | Provision or readiness failed |
|
||||
| `destroying` | Teardown in progress |
|
||||
| `destroyed` | Resources released; record retained for audit |
|
||||
|
||||
### State Hub event mapping
|
||||
|
||||
Each transition emits a State Hub progress event (or dedicated registration API
|
||||
when available):
|
||||
|
||||
| Transition | `event_type` | Required fields |
|
||||
|------------|--------------|-----------------|
|
||||
| → `requested` | `note` | `sandbox_id`, `profile_id`, `consumer` |
|
||||
| → `provisioning` | `note` | `extension_id`, `host` |
|
||||
| → `ready` | `milestone` | `reachability` descriptor |
|
||||
| → `active` | `note` | `actor_type`, timestamps |
|
||||
| → `failed` | `note` | `error` summary |
|
||||
| → `destroying` | `note` | — |
|
||||
| → `destroyed` | `milestone` | `duration_s`, cleanup report |
|
||||
|
||||
Event `detail` payload (JSON):
|
||||
|
||||
```json
|
||||
{
|
||||
"sandbox_id": "abc12345",
|
||||
"profile_id": "profile.compose-e2e",
|
||||
"extension_id": "ext.compose-ssh",
|
||||
"host": "coulombcore",
|
||||
"consumer": {"actor": "atm", "project": "wise-validator", "run_id": "..."},
|
||||
"actor_type": "atm",
|
||||
"state": "ready",
|
||||
"reachability": {"ssh": "root@coulombcore", "remote_dir": "/tmp/sandboxer/abc12345"},
|
||||
"timestamps": {"created_at": "...", "ready_at": "..."}
|
||||
}
|
||||
```
|
||||
|
||||
Extends the `build-agent` self-register pattern: generic sandbox identities carry
|
||||
`profile_id` + `extension_id` instead of build-machine metadata.
|
||||
|
||||
---
|
||||
|
||||
## Core API operations (v0)
|
||||
|
||||
| Operation | Description | v0 scope |
|
||||
|-----------|-------------|----------|
|
||||
| `create` | Provision from profile + inputs | **Yes** |
|
||||
| `get` | Inspect sandbox status | **Yes** |
|
||||
| `list` | List sandboxes (filter by consumer optional) | **Yes** |
|
||||
| `extend_ttl` | Extend time-to-live | Stub |
|
||||
| `recreate` | Destroy and reprovision from stored seed | **Yes** |
|
||||
| `destroy` | Idempotent teardown | **Yes** |
|
||||
| `snapshot` / `restore` | Checkpoint workspace | Deferred (SAND-WP-0003) |
|
||||
| `exec` | Run command in sandbox | Harness-owned via SSH (glas-harness) |
|
||||
|
||||
HTTP surface (optional v0; CLI calls core library directly):
|
||||
|
||||
- `POST /v1/sandboxes` — create
|
||||
- `GET /v1/sandboxes/{id}` — get
|
||||
- `GET /v1/sandboxes` — list
|
||||
- `DELETE /v1/sandboxes/{id}` — destroy
|
||||
|
||||
---
|
||||
|
||||
## Consumer attribution
|
||||
|
||||
Every create request carries a **consumer** block:
|
||||
|
||||
```yaml
|
||||
consumer:
|
||||
actor: adm | agt | atm
|
||||
project: <calling-repo-or-service> # e.g. wise-validator, glas-harness
|
||||
session_id: <optional>
|
||||
run_id: <optional>
|
||||
```
|
||||
|
||||
| Actor | Typical caller |
|
||||
|-------|----------------|
|
||||
| `adm` | Human operator via CLI |
|
||||
| `agt` | LLM agent session |
|
||||
| `atm` | Deterministic automation (CI, activity-core, wise-validator) |
|
||||
|
||||
sand-boxer records attribution on every lifecycle event. It does not interpret
|
||||
agent intent or authorize the caller — flex-auth owns authorization when enforced.
|
||||
|
||||
---
|
||||
|
||||
## Extension interface
|
||||
|
||||
Each extension implements:
|
||||
|
||||
```text
|
||||
provision(profile, inputs, placement) → SandboxHandle
|
||||
wait_ready(handle) → Reachability
|
||||
teardown(handle) → CleanupReport
|
||||
estimate_cost?(profile, duration) → MeterQuote # optional; SaaS only
|
||||
```
|
||||
|
||||
Registration requirements (validated at load time):
|
||||
|
||||
- `id` — unique extension identifier (`ext.<name>`)
|
||||
- `capabilities` — isolation levels, regions, persistence, pricing model
|
||||
- `handler` — Python entry point or built-in registry binding
|
||||
|
||||
Extensions are discovered from `extensions/*.yaml` at repo root and loaded via
|
||||
`sandboxer.extensions.registry`.
|
||||
|
||||
---
|
||||
|
||||
## Routing policy vocabulary
|
||||
|
||||
When multiple extensions satisfy a profile capability:
|
||||
|
||||
| Strategy | Behavior |
|
||||
|----------|----------|
|
||||
| `prefer-self-hosted` | Self-hosted extensions first; SaaS fallback (default Coulomb posture) |
|
||||
| `lowest-cost` | Cheapest `estimate_cost` quote wins |
|
||||
| `lowest-latency` | Closest region / host wins |
|
||||
| `explicit` | Profile names a single `extension`; no auto-routing |
|
||||
|
||||
v0 resolves `profile.extension` directly — routing engine deferred to SAND-WP-0006.
|
||||
|
||||
---
|
||||
|
||||
## Security limits
|
||||
|
||||
sand-boxer commits to:
|
||||
|
||||
1. **Default-deny network** unless profile explicitly allows egress
|
||||
2. **Secrets at provision boundary** — `secret_refs` resolved via ops-warden /
|
||||
OpenBao; never returned to agent context
|
||||
3. **Blast-radius isolation** — dedicated hosts (sandboxer01, CoulombCore) away
|
||||
from Railiance01 production
|
||||
4. **Observable lifecycle** — every transition attributed to `adm` / `agt` / `atm`
|
||||
5. **Honest limits** — allowed tool paths can be abused by compromised agents
|
||||
|
||||
sand-boxer does **not** provide intent-aware egress filtering in v1.
|
||||
|
||||
---
|
||||
|
||||
## Out of scope (sibling ownership)
|
||||
|
||||
| Concern | Owner |
|
||||
|---------|-------|
|
||||
| Agent gateway, tools, memory | glas-harness |
|
||||
| e2e.yml semantics, health checks, test pass/fail | wise-validator |
|
||||
| Code generation, setup instructions content | snuggle-inventor |
|
||||
| SSH tunnels | ops-bridge |
|
||||
| SSH certificates | ops-warden |
|
||||
| Workstream / task state | state-hub |
|
||||
|
||||
See `docs/integrations/` for per-sibling contracts.
|
||||
90
docs/runbooks/profile-compose-e2e.md
Normal file
90
docs/runbooks/profile-compose-e2e.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Runbook: profile.compose-e2e
|
||||
|
||||
Provision a compose-based e2e sandbox via `ext.compose-ssh` (e2e-framework lineage).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
**Workstation**
|
||||
|
||||
- `uv`, `ssh`, `rsync`
|
||||
- sand-boxer installed: `make setup`
|
||||
|
||||
**Remote host** (CoulombCore or sandboxer01)
|
||||
|
||||
- SSH access (`SANDBOXER_HOST` or `--host`)
|
||||
- Docker + compose plugin
|
||||
- Sufficient disk for images
|
||||
|
||||
```bash
|
||||
export SANDBOXER_HOST=<ip-or-hostname> # e.g. coulombcore IP
|
||||
export SANDBOXER_SSH_USER=root # optional
|
||||
export SANDBOXER_SSH_KEY=~/.ssh/id_rsa # optional
|
||||
```
|
||||
|
||||
## Create
|
||||
|
||||
Target repo must contain `e2e/e2e.yml` (for compose file path) or a
|
||||
`docker-compose*.yml` at repo root.
|
||||
|
||||
```bash
|
||||
sandboxer create \
|
||||
--profile profile.compose-e2e \
|
||||
--input repo=/path/to/repo \
|
||||
--actor adm \
|
||||
--project sand-boxer
|
||||
```
|
||||
|
||||
Confirm `state: ready` and note `reachability.ssh`, `reachability.remote_dir`,
|
||||
and `reachability.compose_project`.
|
||||
|
||||
## Manual readiness check
|
||||
|
||||
sand-boxer confirms compose services are running (not HTTP health — that is
|
||||
wise-validator's job):
|
||||
|
||||
```bash
|
||||
ssh $SANDBOXER_SSH_USER@$SANDBOXER_HOST \
|
||||
'docker compose -p <compose_project> -f <remote_dir>/<compose_file> ps'
|
||||
```
|
||||
|
||||
## Destroy
|
||||
|
||||
```bash
|
||||
sandboxer destroy <sandbox_id>
|
||||
```
|
||||
|
||||
Verify cleanup:
|
||||
|
||||
```bash
|
||||
ssh $SANDBOXER_SSH_USER@$SANDBOXER_HOST 'docker compose ls'
|
||||
ssh $SANDBOXER_SSH_USER@$SANDBOXER_HOST 'ls /tmp/sandboxer/'
|
||||
```
|
||||
|
||||
## Compatibility with legacy `make e2e`
|
||||
|
||||
Interim callers in `the-custodian` should migrate to sand-boxer for
|
||||
provision/teardown only. Test execution remains in wise-validator (SAND-WP-0003).
|
||||
|
||||
| Legacy | sand-boxer |
|
||||
|--------|------------|
|
||||
| `make e2e REPO=foo` (full pipeline) | `sandboxer create` + wise-validator run |
|
||||
| rsync + compose up | `ext.compose-ssh` provision |
|
||||
| compose down + dir removal | `sandboxer destroy` |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Provision fails — no compose file**
|
||||
|
||||
Ensure repo has `e2e/e2e.yml` with `compose_file` or a root `docker-compose*.yml`.
|
||||
|
||||
**Leftover compose projects**
|
||||
|
||||
```bash
|
||||
ssh $SANDBOXER_HOST 'docker compose -p sbx-e2e-<id> down -v; rm -rf /tmp/sandboxer/<id>'
|
||||
```
|
||||
|
||||
**Skip State Hub events (local debug)**
|
||||
|
||||
```bash
|
||||
export SANDBOXER_NO_STATE_HUB=1
|
||||
```
|
||||
Reference in New Issue
Block a user