feat: TTL enforcement and operational hardening (SAND-WP-0009)

Add TTL parser, expires_at on create, extend_ttl and expire/reap APIs,
activity-core integration doc, repo classification, registry refresh,
HTTP parity, and 69 tests.
This commit is contained in:
2026-06-24 12:44:04 +02:00
parent b58191b23e
commit df658e7ef9
20 changed files with 913 additions and 39 deletions

View File

@@ -0,0 +1,42 @@
# activity-core integration
activity-core schedules bounded work on Railiance01. sand-boxer provides
**sandbox venues** with TTL enforcement; activity-core owns **when** expire runs.
## Scheduled TTL reap
Run periodically (cron, Temporal activity, or CI):
```bash
sandboxer expire --apply
```
HTTP equivalent:
```http
POST /v1/sandboxes/expire?apply=true
```
Returns a list of `ExpireActionResult` entries (`dry-run`, `destroyed`, `failed`).
## Lifecycle events
Each expired sandbox emits a State Hub progress event:
- `state`: `expired` (`event_type`: `milestone`)
- Followed by `destroying``destroyed`
Event `detail` includes `ttl`, `expires_at`, and reachability fields.
## What sand-boxer does not do
- No Temporal workflows or activity-core code in this repo
- No push webhook to activity-core on expiry (poll/schedule only in v0)
- TTL parsing and destroy orchestration live in sand-boxer
## Consumer pattern
1. activity-core activity provisions via `sandboxer create` (or HTTP)
2. Work runs in the sandbox (glas-harness, wise-validator, etc.)
3. Scheduled `sandboxer expire --apply` reaps past-TTL sandboxes
4. State Hub records full lifecycle for audit

View File

@@ -82,7 +82,7 @@ Extends the `build-agent` self-register pattern: generic sandbox identities carr
| `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 |
| `extend_ttl` | Extend time-to-live | **Yes** |
| `recreate` | Destroy and reprovision from stored seed | **Yes** |
| `destroy` | Idempotent teardown | **Yes** |
| `snapshot` / `restore` | Checkpoint workspace | **Yes** (compose-ssh, saas-stub) |
@@ -97,6 +97,9 @@ HTTP surface (optional v0; CLI calls core library directly):
- `POST /v1/sandboxes/{id}/snapshot` — checkpoint
- `POST /v1/snapshots/{id}/restore` — restore
- `GET /v1/snapshots` — list checkpoints
- `POST /v1/sandboxes/{id}/recreate` — recreate
- `PATCH /v1/sandboxes/{id}/ttl` — extend TTL
- `POST /v1/sandboxes/expire` — TTL reap (query `apply=true`)
---

View File

@@ -46,4 +46,4 @@ Deferred: Packer orchestration from API, `make remote-build` shim.
| ~~SaaS extensions + payments v0~~ | SAND-WP-0006 — stub + routing + credits |
| E2B / Modal real adapters | Post SAND-WP-0006 |
| ~~Snapshot / restore~~ | SAND-WP-0007 — `docs/snapshots.md` |
| TTL enforcement + scheduled reap | **SAND-WP-0009** |
| ~~TTL enforcement + scheduled reap~~ | SAND-WP-0009`docs/ttl.md` |

23
docs/security.md Normal file
View File

@@ -0,0 +1,23 @@
# Security posture
sand-boxer limits **blast radius** — it does not enforce **intent**.
## What sandboxing provides
- Isolated compose projects and workspace directories on placement hosts
- Profile-declared network default-deny (declarative in v0; enforcement varies by extension)
- TTL-bound disposable venues with automated expire/reap
- Consumer attribution (`adm` / `agt` / `atm`) on lifecycle events
## What sandboxing does not provide
- Protection against a malicious or compromised agent *inside* the sandbox
- Guarantee that an agent follows instructions or policy
- Replacement for secrets management (use OpenBao / operator paths via `warden route`)
- Production isolation on Railiance01 (sandboxes run on sandboxer01 / CoulombCore)
Per INTENT: *"Honest security — sandboxing limits blast radius; it is not intent
enforcement."*
Operators should combine sand-boxer with flex-auth, credential routing, and
harness-level controls for end-to-end safety.

65
docs/ttl.md Normal file
View File

@@ -0,0 +1,65 @@
# Time-to-live (TTL)
Disposable-by-default sandboxes — SAND-WP-0009.
## Semantics
Each ready sandbox has:
| Field | Meaning |
|-------|---------|
| `ttl` | Active duration string (e.g. `4h`) |
| `expires_at` | UTC timestamp when the sandbox should be reaped |
On `create`, TTL comes from `SandboxCreateRequest.ttl` or the profile
`ttl.default`, capped at `ttl.max`. Anchor is `ready_at`.
Duration format: positive integer + unit — `s`, `m`, `h`, `d` (e.g. `30m`, `4h`).
## extend_ttl
Extend a live sandbox (`ready` or `active`):
```bash
sandboxer extend-ttl <sandbox_id> --duration 2h
```
HTTP: `PATCH /v1/sandboxes/{id}/ttl` with `{"duration": "2h"}`.
Extension adds to the current `expires_at` (or now if already past). Total lifetime
from `ready_at` cannot exceed profile `ttl.max`.
## expire / reap
TTL reap is distinct from host inventory `reap-stale`:
| Command | Purpose |
|---------|---------|
| `sandboxer expire` | Sandboxes past `expires_at` or profile `ttl.idle_reap` |
| `sandboxer reap-stale` | Orphan host resources vs store inventory |
```bash
sandboxer expire # dry-run (default)
sandboxer expire --apply # mark expired, destroy
```
HTTP: `POST /v1/sandboxes/expire?apply=true`
Flow on `--apply`:
1. Transition to `expired` (State Hub milestone)
2. `destroy` (idempotent teardown)
## Profile fields
```yaml
ttl:
default: 4h
max: 24h
idle_reap: null # optional; reap when updated_at + idle_reap elapsed
```
## activity-core
Scheduled jobs should invoke `sandboxer expire --apply` (or HTTP equivalent).
See `docs/integrations/activity-core.md`.