Skip to content
6 changes: 6 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ Update documentation in the same change when behavior changes:

Do not duplicate detailed backend behavior here. Keep the canonical explanation in the subsystem documentation.

### Terminology

- In consumer docs and public Rustdoc, replace "state-aware" with "container lifecycle operations" ("manual lifecycle operations" for caller responsibility), "one-shot" with "create-and-run execution", and "exact contract" with "contract".
- Use plain English and explicit type, namespace, field, and format names (e.g. "MXC request JSON"). Avoid "wire type", unqualified "raw JSON", "native request", "closed exclusion", "closes over policy", "operation-neutral", and "pinned logging".
- Preserve code identifiers. Follow the [developer wording guide](../docs/development/glossary.md); define retained jargon in the [consumer glossary](../docs/glossary.md) or developer glossary.

## SDK API consistency

- Publish every SDK operation, type, probe, discovery API, telemetry API, and helper only through a supported versioned (V*) namespace/module/entrypoint.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ behind a unified containment model and typed SDKs.
- **Network policy**: Proxy support, outbound controls, and backend-dependent
host filtering
- **UI policy**: Clipboard, display, and GUI access controls
- **State-aware lifecycle**: Provision, start, execute, stop, and deprovision
- **Container lifecycle operations**: Provision, start, execute, stop, and deprovision
persistent containers
- **Rust, .NET, and Node SDKs**: Versioned APIs for one-shot and state-aware
execution
- **Rust, .NET, and Node SDKs**: Versioned APIs for create-and-run execution and
container lifecycle operations
- **Diagnostics**: Tools to understand access-denied failures in a container

## What is MXC?
Expand Down Expand Up @@ -156,7 +156,7 @@ used by the Node SDK.

Build prerequisites are:

- [Rust](https://rustup.rs/), pinned to version **1.93** by
- [Rust](https://rustup.rs/), required version **1.93** as specified by
`src/rust-toolchain.toml`
- Node.js 24 or later and npm
- The platform toolchain and prerequisites described by the selected backend
Expand Down Expand Up @@ -187,6 +187,7 @@ build.bat --all # Release build for current architecture

| Document | Repository location | Purpose |
|---|---|---|
| Terminology | [`docs/glossary.md`](docs/glossary.md) | Plain-English definitions of consumer terms |
| SDK samples | [`samples/`](samples/README.md) | Runnable Rust, .NET, and Node scenarios |
| SDK API reference | [`docs/api-reference/`](docs/api-reference/README.md) | Supported V1 operations and types |
| Container lifecycle | [`docs/container-lifecycle.md`](docs/container-lifecycle.md) | Persistent container lifecycle overview |
Expand Down
9 changes: 6 additions & 3 deletions docs/api-reference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

> **Audience:** MXC consumers

These references describe the supported V1 SDK authoring surfaces: operation
These references describe the supported V1 SDK APIs: operation
signatures, public types, fields, and containment choices. They complement the
SDK READMEs and backend guides; they do not replace native policy validation or
certify backend availability.

See the [consumer glossary](../glossary.md) for terminology.

| SDK | Reference | Public entrypoint |
|---|---|---|
| Rust | [V1](rust/v1/README.md) | `mxc_sdk::v1` |
Expand Down Expand Up @@ -36,8 +38,9 @@ Results and live handles expose warnings and optional execution metadata.
Provision metadata is backend-specific; each language's type reference lists
the supported fields.

Containment selects the backend. Shared restrictions live on the request;
backend-specific authoring uses `*Config` types. Omitted environment input
`ContainerRequest` selects containment and carries shared restrictions;
backend-specific settings use `*Config` types listed in each language's type
reference. Omitted environment input
uses backend defaults, whereas an explicitly empty environment is preserved.
Filesystem discovery helpers follow the same distinction for their host
environment input. Native validation decides which policies the backend can
Expand Down
4 changes: 2 additions & 2 deletions docs/api-reference/dotnet/v1/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ Callers do not supply JSON or a schema version.

Creation takes `ContainerRequest` and operation options. Existing-container
execution takes the `ContainerId` returned by provision, `ExecutionRequest`,
and operation options. One-shot PTY support covers IsolationSession, Bubblewrap,
and operation options. Create-and-run PTY support covers IsolationSession, Bubblewrap,
LXC, and Seatbelt direct execution. Existing-container PTY support remains
IsolationSession-only. Seatbelt PTY rejects `guiAccess` and legacy
`launchMethod: "open"`. Terminal handles
give the caller explicit input, output, resize, and process ownership; there
is no separate public attached-console or raw-JSON launch API.
is no separate public attached-console launch API or API accepting ["MXC request JSON"](../../../schema.md#mxc-request-json).

## Discovery and validation

Expand Down
6 changes: 3 additions & 3 deletions docs/api-reference/dotnet/v1/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public sealed class ContainerRequest

## `Microsoft.Mxc.Sdk.V1.Containment`

Choose one of the SDK-supported containment configurations.
Choose one of the SDK-defined `Containment` subclasses listed below.

```csharp
public abstract class Containment
Expand Down Expand Up @@ -1111,7 +1111,7 @@ public sealed class ProvisionOptions

## `Microsoft.Mxc.Sdk.V1.ProvisionRequest`

Closed request for provisioning a supported containment.
SDK-defined base type for backend-specific provision requests.

```csharp
public abstract class ProvisionRequest
Expand All @@ -1123,7 +1123,7 @@ public abstract class ProvisionRequest

## `Microsoft.Mxc.Sdk.V1.ProvisionMetadata`

Closed backend-specific metadata returned by provisioning.
SDK-defined base type for backend-specific metadata returned by provisioning.

```csharp
public abstract class ProvisionMetadata
Expand Down
8 changes: 4 additions & 4 deletions docs/api-reference/node/v1/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ Callers do not supply JSON or a schema version.

Creation takes `ContainerRequest` and operation options. Existing-container
execution takes the `ContainerId` returned by provision, `ExecutionRequest`,
and operation options. PTY operations are asynchronous. One-shot PTY support
and operation options. PTY operations are asynchronous. Create-and-run PTY support
covers IsolationSession, Bubblewrap, LXC, and Seatbelt direct execution.
Existing-container PTY support remains IsolationSession-only. Seatbelt PTY
rejects `guiAccess` and legacy `launchMethod: "open"`.
Terminal handles give the caller explicit input, output, resize, and process
ownership; there is no separate attached-console or raw-JSON launch API.
ownership; there is no separate attached-console launch API or API accepting ["MXC request JSON"](../../../schema.md#mxc-request-json).

## Discovery and validation

Expand Down Expand Up @@ -98,7 +98,7 @@ export function getUserProfilePolicy(environment?: { [key: string]: string | und

## `@microsoft/mxc-sdk/v1::mxcErrorFromCode`

Constructs an MxcError from a wire-format error code.
Constructs an `MxcError` from a native runtime error code.

```typescript
export function mxcErrorFromCode(code: string, message: string, details?: Record<string, unknown>): MxcError;
Expand All @@ -116,7 +116,7 @@ export function probe(request?: ContainerRequest): ProbeOutput;

## `@microsoft/mxc-sdk/v1::provisionContainer`

Provision a container from a closed backend-specific request.
Provision a container from the backend-specific `ProvisionRequest` type.

```typescript
export async function provisionContainer<C extends LifecycleContainmentKind>(request: ProvisionRequest<C> & {
Expand Down
14 changes: 7 additions & 7 deletions docs/api-reference/node/v1/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export interface ContainerRequest {

## `@microsoft/mxc-sdk/v1::Containment`

Closed union and SDK-owned containment choices for container creation requests.
Union of the SDK-defined containment choices for `ContainerRequest`.

```typescript
export type Containment = Containment.Process | Containment.ProcessContainer | Containment.Wslc | Containment.Lxc | Containment.Seatbelt | Containment.IsolationSession | Containment.Bubblewrap;
Expand Down Expand Up @@ -237,7 +237,7 @@ export interface DirectionalNetworkConfig {

## `@microsoft/mxc-sdk/v1::ErrorCode`

Closed set of MXC wire-format error codes.
The SDK-defined set of MXC native runtime error codes.

```typescript
export type ErrorCode = 'malformed_request' | 'unsupported_containment' | 'unsupported_phase' | 'backend_unavailable' | 'malformed_id' | 'stale_id' | 'not_provisioned' | 'not_started' | 'already_started' | 'already_stopped' | 'policy_validation' | 'backend_error';
Expand Down Expand Up @@ -343,7 +343,7 @@ export interface FilesystemPolicyResult {

## `@microsoft/mxc-sdk/v1::IsolationSessionNetworkConfig`

The only network posture IsolationSession can truthfully provide.
Unrestricted networking, the only network permissions IsolationSession supports.

```typescript
export interface IsolationSessionNetworkConfig {
Expand Down Expand Up @@ -407,7 +407,7 @@ export interface LxcConfig {

## `@microsoft/mxc-sdk/v1::MxcError`

Typed error thrown by the MXC SDK in response to a wire-format error envelope.
Typed error thrown by the MXC SDK in response to a native JSON error object.

```typescript
export class MxcError extends Error {
Expand All @@ -425,7 +425,7 @@ export class MxcError extends Error {

## `@microsoft/mxc-sdk/v1::MxcErrorFields`

Every field an MxcError can carry, in the same flat shape as the wire error envelope — operation, nativeCode and remediation sit alongside code and message, not nested inside details.
Every field an `MxcError` can carry, matching the native JSON error object: `operation`, `nativeCode`, and `remediation` sit alongside `code` and `message`, not nested inside `details`.

```typescript
export interface MxcErrorFields {
Expand Down Expand Up @@ -678,7 +678,7 @@ export interface ProvisionOptions {

## `@microsoft/mxc-sdk/v1::ProvisionRequest`

Closed backend-specific request for provisioning a container.
Backend-specific input for provisioning a container, selected by `containment`.

```typescript
export type ProvisionRequest<C extends LifecycleContainmentKind = LifecycleContainmentKind> = C extends LifecycleContainmentKind ? ProvisionConfigFor<C> & {
Expand Down Expand Up @@ -735,7 +735,7 @@ export interface NetworkRuntimeConfig {

## `@microsoft/mxc-sdk/v1::SDK_CONTRACT_VERSION`

SDK-owned exact container-request contract used by the V1 high-level API.
The contract version selected internally by the V1 SDK API.

```typescript
const SDK_CONTRACT_VERSION: "1.0.0" = '1.0.0' as const;
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/rust/v1/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Attached execution is not exposed by the V1 SDK.

Creation takes `ContainerRequest` and operation options. Existing-container
execution takes the `ContainerId` returned by provision, `ExecutionRequest`,
and operation options. One-shot PTY support covers IsolationSession, Bubblewrap,
and operation options. Create-and-run PTY support covers IsolationSession, Bubblewrap,
LXC, and Seatbelt direct execution. Existing-container PTY support remains
IsolationSession-only. Seatbelt PTY rejects `guiAccess` and legacy
`launchMethod: "open"`. A PTY gives the
Expand Down
4 changes: 2 additions & 2 deletions docs/api-reference/rust/v1/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ impl ContainerRequest {

## `mxc_sdk::v1::Containment`, `mxc_sdk::v1::policy::Containment`

The closed backend choice carried by `ContainerRequest`. Select a variant with
The SDK-defined backend choices carried by `ContainerRequest`. Select a variant with
its typed configuration; the native engine validates backend and policy support.

```rust
Expand Down Expand Up @@ -200,7 +200,7 @@ impl From<MxcError> for Error {

## `mxc_sdk::v1::ErrorCode`

Closed set of error codes the SDK can return.
The predefined error codes the SDK can return.

```rust
pub enum ErrorCode {
Expand Down
24 changes: 12 additions & 12 deletions docs/backends/bwrap/bubblewrap-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ requiring root privileges or a container runtime.

> **Status:** Stable — the default Linux backend.

> **Supported exact contracts (v0.9+):** author `network.egress` /
> **Supported contracts (v0.9+):** use `network.egress` /
> `network.ingress` and, for proxy requests, `runtimeConfig.networkProxy`.
> Versions before `0.9.0-alpha` are rejected; changing only the version of an
> old config does not migrate its policy. Legacy `defaultPolicy`,
> `enforcementMode`, host lists, `allowLocalNetwork`, and `network.proxy`
> are rejected by supported exact contracts. Typed Rust requests no longer
> are rejected by supported contracts. Typed Rust requests no longer
> contain these fields. See [schema migration](../../schema.md).
> Bubblewrap still rejects enforceable-looking requests it cannot honor
> (such as `ingress.default: "allow"` or direct egress rules combined with a
Expand Down Expand Up @@ -87,7 +87,7 @@ requiring root privileges or a container runtime.
installed by an unprivileged supervisor that keeps the caller's uid, so on
a stock host with root-owned `/run` it cannot take that lock. `validate`
refuses such a host with a message naming the backend rather than letting
the supervisor die at the first rule. If pinned to `iptables-legacy`, switch it:
the supervisor die at the first rule. If configured to use `iptables-legacy`, switch it:
```bash
sudo update-alternatives --set iptables /usr/sbin/iptables-nft
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
Expand Down Expand Up @@ -314,7 +314,7 @@ regardless of the rule. MXC emits a warning naming those allowed destinations
rather than refusing them, since the posture fails closed.

An IPv4-mapped address such as `::ffff:203.0.113.5` is programmed as IPv4:
Linux puts a genuine IPv4 packet on the wire for one, so an `ip6tables` rule
Linux sends a genuine IPv4 packet for one, so an `ip6tables` rule
naming it would never match and a directional deny rule under
`egress.default: "allow"` would fail open. A mapped CIDR is translated the
same way — the mapped range is the last 32 bits of `::ffff:0:0/96`, so a
Expand Down Expand Up @@ -408,8 +408,8 @@ added in the same change that enables it.

Schema `0.9.0-alpha` uses an explicit `egress` and `ingress` section instead
of the retired `defaultPolicy` / `allowedHosts` / `blockedHosts` fields. An
exact v0.9 config carrying a legacy field fails parsing; a config declaring
an earlier version is refused as an unsupported contract before its network
`0.9.0-alpha` config carrying a legacy field fails parsing; a config declaring
an earlier version is refused as an unsupported contract version before its network
section is read.

```json
Expand Down Expand Up @@ -510,7 +510,7 @@ is opened. A proxy config that states — or defaults to — `deny` therefore ge
the posture it writes, since the deny still covers every host-loopback path but
that endpoint. `ingress.hostLoopback` is not consulted there — `EgressPlan::for_proxy`
builds that chain, not the directional builder that lowers the drop — but the
observed result matches the contract regardless.
observed result still matches the requested network policy.

The declaration and these refusals must ship together — declaring the inbound
features without them would be a fail-open. A unit test asserts exactly that
Expand All @@ -530,7 +530,7 @@ is over-declared and fails there. Reverting the host-loopback drop reproduces
the original bug as a test failure.

`runtimeProxy` is declared. The parser normalizes
`runtimeConfig.networkProxy` into `policy.network_proxy`, pinned to a loopback
`runtimeConfig.networkProxy` into `policy.network_proxy`, restricted to a loopback
endpoint and accepted only alongside `egress.default: "deny"` with no direct
rules. That is the proxy-only posture this backend enforces. The end-to-end
test runs the supported proxy configuration with ingress denial both omitted
Expand Down Expand Up @@ -690,7 +690,7 @@ an exit is detected; see [Limitations](#limitations).
A proxy request is the proxy-only posture, so `egress.default` must be `deny`
with no `allow` / `deny` rules; the chain opens the proxy endpoint alone.

Exact contracts before v0.9 are retired. Legacy `network.proxy` and host-list
Contract versions before `0.9.0-alpha` are retired. Legacy `network.proxy` and host-list
configurations cannot be expressed by declaring v0.9; use
`runtimeConfig.networkProxy` with a ruleless, deny-default directional egress
policy as shown above. The external proxy enforces any host filtering itself.
Expand Down Expand Up @@ -803,7 +803,7 @@ blocks, not DNS names. Do not put a hostname in a `cidr` field: Bubblewrap
cannot enforce an IP rule against the workload's changing DNS answers and
rejects the request. If policy must inspect requested hostnames, use
`runtimeConfig.networkProxy` with a separately configured external proxy.
The exact v0.9 contract provides no MXC-managed host-list policy for that proxy.
Contract `0.9.0-alpha` provides no MXC-managed host-list policy for that proxy.

## Comparison with LXC

Expand Down Expand Up @@ -852,8 +852,8 @@ Test configs are in `tests/configs/bubblewrap_*.json`.
IPv6 destinations remain unreachable until slirp supports IPv6 here.
`runtimeConfig.networkProxy` restricts direct egress to the configured
loopback proxy endpoint; hostname policy belongs to that external proxy.
- **No state-aware lifecycle** — Bubblewrap implements `ScriptRunner` only
(one-shot), not `StatefulSandboxBackend`
- **No container lifecycle operations**: Bubblewrap's `ScriptRunner`
supports create-and-run execution, not `StatefulSandboxBackend` lifecycle calls.
- **Provider-loss detection is exit-based** — in the private-namespace modes a
`slirp4netns` that exits mid-run fails the run; one that is alive but wedged
is not detected, and reaches the workload as an unreachable network
2 changes: 1 addition & 1 deletion docs/backends/hyperlight/hyperlight-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ three things:

| Entry | Purpose |
|---|---|
| `initrd.cpio` | The guest rootfs, pulled from `ghcr.io/hyperlight-dev/hyperlight-unikraft/<runtime>` at the release the crate is pinned to |
| `initrd.cpio` | The guest rootfs, pulled from `ghcr.io/hyperlight-dev/hyperlight-unikraft/<runtime>` at the release specified by the crate |
| `snapshot/` | The guest, booted once and captured with the runtime warm; every run restores it |
| `VERSION` | The rootfs release the directory holds |

Expand Down
10 changes: 5 additions & 5 deletions docs/backends/lxc/lxc-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

The LXC backend provides Linux container isolation using [LXC (Linux Containers)](https://linuxcontainers.org/lxc/).

For every registered contract from `0.9.0-alpha` onward, networking is
For every supported contract from `0.9.0-alpha` onward, networking is
directional-only: use `network.egress` and `network.ingress`. LXC rejects
`runtimeConfig.networkProxy`, so a supported LXC request has no proxy surface
at all — see [Proxy](#proxy) below.
Expand Down Expand Up @@ -167,7 +167,7 @@ The field must name a loopback endpoint, and an LXC container has its own
network namespace, so `127.0.0.1` there is the container rather than the host.
MXC ships no component that relays a host loopback proxy into that namespace.

The legacy `network.proxy` fields also have no supported exact contract.
The legacy `network.proxy` fields are also absent from every supported contract.
Choose a backend that supports a loopback proxy if the workload needs one;
relabeling a retired LXC proxy request as v0.9 cannot make its policy enforceable.

Expand Down Expand Up @@ -252,7 +252,7 @@ LXC reads a run's network section only when the container starts. Omit

**Teardown is owed on every terminal path,** including a drop without `wait`:
the network chains come down, and the container is released. A reused container
also has any stale proxy hosts-file pin from an earlier run cleared before its
also has any stale proxy address mapping in its hosts file cleared before its
new workload launches. MXC checks for marked lines inside the running
container using `lxc-attach` and removes them if present. A failed check
refuses to run the workload.
Expand Down Expand Up @@ -358,10 +358,10 @@ The zone query should answer the zone you assigned.
Stock `lxcbr0` gives the container no IPv6 address, so this surfaces only on a
host that provides one.
- **No proxied egress.** See [Proxy](#proxy).
- **No state-aware lifecycle.** LXC implements `ScriptRunner` (one-shot) and
- **No container lifecycle operations.** LXC implements `ScriptRunner` (create-and-run execution) and
`SandboxBackend` (streaming over pipes or a caller-controlled PTY), not
`StatefulSandboxBackend`. A
state-aware request is rejected.
request for a container lifecycle operation is rejected.
- **Inherited stdio is unavailable.** Use ordinary pipe streaming or
`spawn_with_pty`; the in-process API never takes over the host application's
own terminal. See [Streaming](#streaming).
Loading
Loading