# Governed workspace execution and Cursor readiness

These paths extend the existing Spring workspace authority. Neither the SDK nor
an agent receives a Docker API, a provider key, or authority from a caller-picked
tenant/company/workspace string.

## Workspace execution

`exec.run`, parsed execution and the existing session/stream path continue to
pass through runner authentication, workspace resolution, resource/network
policy and admission. With the broker overlay they dispatch to the narrow
`app.execution_broker` service. The runner has no Docker socket. Only this
separate service owns Docker access; it accepts no caller Docker specification.
A dedicated credential file is shared only by the runner and broker.

The broker requires an immutable sandbox image, Linux cgroup v2 CPU/memory/PID
controls, an internal sandbox network, and an exact match between its own
workspace named-volume mount and the volume selected for sandboxes. Every
command runs as uid/gid 10001, with all capabilities dropped, a read-only root,
no host mount or socket, and a single scoped source directory mounted read-only.
Network-off uses Docker's `none` network. Allowlist mode uses the existing scoped
proxy; external DNS forwarding is disabled inside the sandbox.

A writable tmpfs workspace receives a source snapshot. Its size and the container
memory are kernel bounded. After an uncancelled command exits, the broker freezes
remaining workspace processes and reads
a bounded tar export while a fixed supervisor keeps tmpfs mounted, rejects paths
escaping the workspace, hard links and special
files, and checks the canonical source commitment before replacing it. Tar
ownership is ignored; canonical workspace ownership is preserved. Timeouts,
output-limit termination and cancellation never publish the partial snapshot.
The current full-snapshot export must fit the declared artifact budget and
20,000 entries. The default broker caps are 1 CPU, 1 GiB memory, 64 processes,
10 GiB tmpfs storage, 250 MB exported artifacts, 2 MB command output and one hour.
Smaller Spring limits win. Tmpfs data also counts against the memory cap.

A failed or conflicting reconciliation retains a bounded quarantine directory
and a separate evidence record. Further execution of that workspace is refused
until an operator reconciles that evidence. The SQLite admission journal retains
execution/request/scope commitments and bounded results; ambiguous dispatches
are never automatically replayed. The broker must run as one replica. Startup
reaps only containers bearing its fixed ownership label. The journal refuses
new work at 10,000 retained entries until retention is performed.

Enable using `docker-compose.workspace-execution.yml` after base + production
configuration, with the `workspace-execution` profile. Provide a tested
`WORKSPACE_EXEC_BROKER_IMAGE`, digest-pinned `WORKSPACE_EXEC_SANDBOX_IMAGE`, the
actual `DOCKER_WORKSPACE_VOLUME_NAME`, and a dedicated
`WORKSPACE_EXEC_BROKER_KEY_FILE`. Optional allowlist egress requires the existing
scoped proxy secret/configuration. Production stays disabled without the overlay;
a missing broker never falls back to an in-process shell or a Docker socket.
The sandbox image must include `/bin/sh`, `/bin/test`, `/usr/bin/python3`,
`/usr/bin/tar`, `cp`, `touch` and `sleep` from a trusted immutable toolchain.
The runner artifact supplies these tools. The canonical-source check detects
conflicting file changes, but is not a global transaction across independent
filesystem clients; production validation must include concurrent file writes.

## Cursor cloud execution

The worker resolves a `code_workspace` grant whose target key is the selected
workspace. The response contains only an opaque execution token. Requests go to
`/api/internal/cursor`, where Spring decodes current custody, verifies the token
purpose and exact workspace, checks current operation permission, and derives
the repository from the workspace's connected SCM record in the same company.
Caller metadata, paths, repositories and provider resume IDs cannot override
that authority. General provider-operation tokens cannot authorize this path.

A model token alone cannot authorize execution. The bridge additionally redeems
the one-use `provider_execution_grant` issued by canonical authenticated Project
Builder admission, and verifies exact tenant/company/owner, harness/model,
workspace ID and repository URL against that approved durable binding.

Spring journals the exact scoped idempotency/request commitments before calling
the sidecar. It sends a bounded cloud request and the current provider credential
only to the trusted adapter. Every two seconds while the call is pending it
rechecks custody, workspace permission and SCM connection; revocation triggers
cancellation. The original opaque grant also authorizes cancellation of only its
own admitted dispatch after expiry/revocation, without reopening execution.
Duplicate delivery cannot start another VM through another Spring replica.
An ambiguous request remains blocked for reconciliation instead of replay.

The sidecar requires a dedicated credential **file**, explicit model and
repository allowlists, and current Spring dispatch authority. It has bounded
queueing, input, output and execution duration, and preserves cancellation that
races admission. Missing credentials never enable anonymous requests. A provider
approval request cancels the live run and reports the pending approval; it cannot
silently continue a consequential action. Local execution and arbitrary provider
resume IDs are refused in this adapter: running workspace code beside provider
custody would violate the isolation boundary. Local workspace commands use the
separate broker; the sidecar is specifically a cloud provider adapter.

**Cloud dispatch currently remains refused:** the canonical Project grant requires
hard token/cost ceilings, while the published Cursor SDK exposes usage reporting
and cancellation but no native pre-dispatch token/cost cap. Spring `/ready` and
dispatch reject this condition. A caller-supplied flag cannot waive it. The
sidecar tests prove the transport/authority protocol, not live hard-budget
admission. Local workspace coding commands are implemented by the separate
constrained broker above. Provider-side ceiling verification is required before
cloud Cursor can be activated; source: [Cursor TypeScript SDK](https://cursor.com/docs/sdk/typescript).

Configure `docker-compose.cursor-governed.yml` after base + production configuration
with the `cursor-sdk` profile. Configure `CURSOR_SDK_IMAGE`,
`CURSOR_SDK_CONTROL_KEY_FILE`, and explicit `CURSOR_SDK_MODEL_ALLOWLIST` and
`CURSOR_SDK_ALLOWED_REPOS`. The overlay projects the dedicated secret into Spring
and the sidecar, clears legacy Cursor credentials from workers, and provides a
separate control network and provider egress network. Per-request Cursor opt-in
is still required; `CODING_AGENT_USE_CURSOR_SDK` remains false by default.

## Validation and promotion criteria

Local tests execute the real broker/provider/storage paths with Docker transport
substituted, and exercise the real sidecar HTTP server with a substituted Cursor
SDK. Spring tests execute actual scope/admission/cancellation service functions.
These tests are not a live Docker or provider attestation.

Before production opt-in, retain evidence from the configured local/staging
stack for: successful scoped source/write/reconciliation; blocked cross-company
requests; storage/memory/PID/output/deadline ceilings; proxy allowlist and metadata
refusal; cancellation during execution/queueing; broker restart with an orphan;
conflicting source edits and quarantine; duplicate Cursor delivery across Spring
replicas; custody revocation during a run; and one configured-account cloud
journey against a permitted sandbox repository. Sidecar `/ready` deliberately
reports `live_provider_verified:false`. AWS need not be restored for these tests.
