Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
db18050
feat(sandbox): validate configuration before workload activation
johntmyers Sep 10, 2026
f7bd1a9
fix(sandbox): bound startup failures and preserve activation history
johntmyers Sep 14, 2026
f7922bf
fix(sandbox): enforce deadlines on startup RPC attempts
johntmyers Sep 14, 2026
a7ee151
test(sandbox): isolate provider auto-create policy fixture
johntmyers Sep 14, 2026
003826d
test(sandbox): remove unnecessary fixture string delimiters
johntmyers Sep 14, 2026
3f3a955
test(sandbox): capture startup logs before ephemeral cleanup
johntmyers Sep 14, 2026
62802a9
fix(sandbox): preserve credential revocation after rebase
johntmyers Sep 15, 2026
766b6ec
fix(server): retain provider revision helper for endpoint reports
johntmyers Sep 15, 2026
58a72c5
fix(server): import provider object trait in production
johntmyers Sep 15, 2026
eb39c4e
fix(supervisor): preserve admission across boundary extraction
johntmyers Sep 16, 2026
53907d7
fix(sandbox): use existing boundary discovery imports
johntmyers Sep 16, 2026
bb65bad
test(sandbox): distinguish workload and supervisor containers
johntmyers Sep 16, 2026
1d651d6
test(server): reconcile admission schema with timestamp migration
johntmyers Sep 16, 2026
64c39c3
test(server): reconcile admission with typed deletion schema
johntmyers Sep 16, 2026
bc8575d
test(server): reconcile admission with mutation request IDs
johntmyers Sep 17, 2026
d6b9f7e
test(supervisor): adapt local startup fixture to admission state
johntmyers Sep 17, 2026
6cd13d1
fix(supervisor): deduplicate startup quarantine diagnostics
johntmyers Sep 17, 2026
9548260
fix(tui): show configuration blockers in sandbox notes
johntmyers Sep 17, 2026
fc1ea5f
feat(sandbox): expire provisioning repair attempts after five minutes
johntmyers Sep 17, 2026
a12528c
fix(supervisor): reconcile admission with provider readiness
johntmyers Sep 17, 2026
88a50ba
fix(tui): separate configuration summaries from full diagnostics
johntmyers Sep 17, 2026
f863181
fix(tui): shorten invalid configuration note
johntmyers Sep 17, 2026
8229868
test(server): refresh schema inventory after rebase
johntmyers Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/tui-development/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ Phase 1: GetSandboxLogs → 500 initial lines → send via Event::LogLines
Phase 2: WatchSandbox(follow_logs: true) → live tail → send via Event::LogLines
```

**Sandboxes**: Fetched via `ListSandboxes` in a background collection-refresh task scheduled from the 2-second tick, scoped to the current workspace (or all workspaces). Follow `next_page_token` until empty so the dashboard reflects the complete collection.
**Sandboxes**: Fetched via `ListSandboxes` in a background collection-refresh task scheduled from the 2-second tick, scoped to the current workspace (or all workspaces). Follow `next_page_token` until empty so the dashboard reflects the complete collection. The NOTES column summarizes active `ConfigurationInvalid` readiness conditions as `Invalid config` before port forwards and clears the note on refresh after repair. Full diagnostics remain available through `openshell sandbox get <name> -o json`. Timed-out provisioning attempts show `Provisioning timed out` with cleanup pending or compute reclaimed, preserving port forwards. The sandbox detail pane wraps the full configuration error in its Notes field.

**Providers**: Fetched via `ListProviders` in the background collection-refresh task. Provider profiles are fetched per-workspace via `ListProviderProfiles` and cached in a `ProviderProfileCache` keyed by `(workspace, profile_id)`. Follow each list RPC's `next_page_token` until empty.

Expand Down
23 changes: 23 additions & 0 deletions architecture/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,29 @@ and number; this coordinated pre-1.0 API change does not alter durable schemas.
The outcome alone does not provide request deduplication. Opted-in unary methods
require a request UUID for the admission contract.

Configuration admission adds `SandboxStatus.configuration_admission` at field
11 and optional `configuration_activated` at field 12, extending the public and
durable closures. New sandboxes explicitly store `false` until first acceptance;
acceptance stores `true` permanently, including across restart. Legacy rows
have neither field and conservatively retain static-policy restrictions. No
database rewrite is required. A pre-admission byte fixture verifies that legacy
phase and policy-version fields survive without fabricated admission or activation.
`SandboxStatus.provisioning` uses field 13 for gateway-owned attempt timing and
compute reclamation progress. Its timestamps survive supervisor reconnects and
ordinary driver status updates. Older records decode with no provisioning
record; timing must be adopted once and persisted, never reconstructed from the
object's frequently changing update timestamp. The additive message requires
no rewrite of existing payloads and leaves the frozen storage-v1 schema intact.
Stored settings JSON also carries per-key change IDs and commit timestamps,
including deletion tombstones. Legacy values acquire stable source identities
on read; a subsequent write preserves them. These clocks distinguish effective
edits from no-op writes without treating status updates as configuration edits.
With timestamp types, deletion outcomes, and optional mutation request IDs, the
admission contract brings the public closure to 298 messages and 21 enums, the
durable closure to 92 messages and 16 enums, and their overlap to 80 messages
and 16 enums. Mutation request IDs extend public request fields without adding
messages to these closures or changing the durable protobuf schema.

| Dual-purpose encoded root | Current decision |
|---|---|
| `Sandbox` | Defer a storage twin; govern its complete dependency closure as durable. |
Expand Down
87 changes: 81 additions & 6 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,82 @@ the structured 403 and authors the narrowest rule. Mechanistically mapping L7
would either over-broaden rules or require path-templating logic that rots
quickly.

## Configuration Admission

Gateway-managed supervisors reconcile configuration before launching the main
process or exposing workload services. Admission covers the effective policy,
provider layers, credential bindings, and gateway-derived provenance. Explicit
user and global policy precedence is unchanged; an image without a policy uses
the restrictive baseline. An invalid image policy does not become a launchable
default.

The gateway tracks configuration admission independently of compute health.
A blocked startup remains `Provisioning` with a `ConfigurationInvalid` readiness
condition, even when the container backend reports readiness. Gateway management
operations remain available. The TUI summarizes configuration rejection in sandbox
NOTES alongside active port forwards; the detail view wraps the full diagnostic,
which is also available through sandbox inspection.
Replacing the policy or repairing providers allows
the same supervisor to reconcile and launch; it does not recreate the sandbox.
Startup retries continue reporting readiness, but unchanged configuration rejections
produce only one log event. A changed configuration or diagnostic emits a new
rejection event; successful repair emits a recovery event.
The gateway gives each initial provisioning attempt and explicit restart a
300-second repair window. Persisted configuration-source clocks reset the window
from the latest effective stored change, including settings deletion and provider
attachment changes. The first accepted rejection for that generation grants one
full window; repeated reports and reconnects do not extend it. Ready disarms the
timer. Failed desired updates to a running sandbox do not arm it.

A leader-owned scan runs independently of driver inventory. Expiry records
`Error`/`ProvisioningTimedOut` before reclaiming compute; cleanup progress and
backoff survive restart. Late runtime reports cannot replace that result. The
record and restartable storage survive cleanup, including for ephemeral creates.
Explicit start is blocked while cleanup is pending, then creates a fresh attempt
using the latest configuration. Configuration edits alone never restart an
expired sandbox. Legacy provisioning records receive one persisted rollout
window. Cross-object configuration serialization uses the gateway's existing
single-writer guard; enabling concurrent configuration writers still requires
the database-backed invariant work tracked by #1255.

Docker startup health remains unready during policy quarantine. A failed probe
does not terminate a live provisioning supervisor; the gateway deadline owns
that decision. Cleanup cancels pending driver startup before stopping compute
so a late startup failure cannot remove retained workload storage.

Static policy fields can be replaced before the first accepted activation.
A durable first-activation marker closes this repair window permanently, including
across stop/start and later rejected configurations. Legacy records without the
marker retain static-field immutability.
Admission validates policy composition; image and host setup failures, such as
an unresolved OCI user or unavailable isolation facilities, retain their existing
startup error behavior.

Acceptance identifies the effective policy hash/version, configuration revision,
provider-environment revision, and reporting supervisor instance. Startup captures
the matching provider environment and constructs the runtime before reporting
acceptance. Live reconciliation begins only after the main process has spawned,
so it cannot replace the configuration captured for that launch. Restart resets
admission and requires a fresh accepted configuration. Permanent gateway errors
and exhausted transient retries terminate startup; each RPC attempt has a
10-second deadline, including acceptance reports; only acknowledged configuration
rejections wait for repair within the gateway's provisioning deadline. Image discovery uses the authenticated
sandbox boundary control request deadline.

Policy and provider refreshes are prepared before publication. Publication
invalidates prior policy guards before exposing new provider material and swaps
the policy under the same publication locks. Rejected candidates cannot install
their credentials alongside the previous policy. Existing runtime fail-closed
checks remain necessary for in-flight traffic and invalid live updates.

The supervisor reads the workload image policy through an authenticated,
read-only `DiscoverPolicy` boundary request before attaching or launching the
workload. The boundary reads only the well-known policy paths, bounds the response,
and distinguishes missing policy from unreadable or invalid content. The supervisor
validates this candidate with gateway provider composition and obtains admission
before `Attach`, `Confirm`, networking startup, and `StartAgent`. Workload image
environment variables cannot configure the isolated supervisor.

## Policy Revision Acknowledgement

When the supervisor loads a sandbox-scoped policy from the gateway, it retains
Expand Down Expand Up @@ -647,12 +723,11 @@ outages cannot block policy polling, enforcement, settings, or provider
refreshes and cannot permanently lose the initial acknowledgement.

Only sandbox-scoped revisions (`PolicySource::Sandbox`, version greater than
zero) are acknowledged. Global policies and local-file development policies do
not use the sandbox revision API and produce no acknowledgement. When explicit
local Rego and data files are provisioned into the supervisor, it continues
polling the gateway for settings and provider refreshes but never replaces the
local OPA engine with a gateway policy revision. Workload image files and
environment variables do not configure the separately isolated supervisor.
zero) use the policy revision acknowledgement API. Global policies use the
configuration admission contract without a sandbox policy revision acknowledgement.
Local Rego/data overrides remain available for standalone development; combining
them with a gateway-managed sandbox is rejected because the gateway cannot admit
the runtime policy it would enforce.

## Failure Behavior

Expand Down
8 changes: 8 additions & 0 deletions architecture/security-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ flag defaults to `false` and is security-flagged in policy approval flows.
Incremental merges only ever add the flag to a matching endpoint; clearing it
requires removing the endpoint or replacing the policy.

Image discovery may persist a desired policy for repair, but does not authorize
workload activation. The gateway applies the credential gate after full provider
composition and provenance derivation. A rejected effective configuration keeps
startup blocked with a bounded diagnostic; the supervisor waits for management
repair instead of launching with connection-time denials or a fallback policy.
Accepted runtime state includes the matching provider-environment revision, so
policy and credential updates cannot activate independently.

The network supervisor independently enforces the same boundary. Credentialed
WebSocket upgrades use the parsed relay, binary frames fail closed, and text
placeholders require rewrite. REST bodies continue streaming when body rewrite is disabled. The relay holds
Expand Down
Loading
Loading