Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions docs/api-reference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ See the [consumer glossary](../glossary.md) for terminology.
| .NET | [V1](dotnet/v1/README.md) | `Microsoft.Mxc.Sdk.V1` |
| Node | [V1](node/v1/README.md) | `@microsoft/mxc-sdk/v1` |

V1.Dev exact-JSON entry points are listed in [Rust](rust/v1/dev.md),
[.NET](dotnet/v1/dev.md), and [Node](node/v1/dev.md). Each accepts a complete
caller-authored exact-version document. Use V1.Dev when the typed V1 API
cannot express the request, such as a supported historical contract or
fields in the mutable development contract. Prefer typed V1 for published
functionality; development-contract fields and experimental backends are
for development and testing, not production dependencies. The selected
contract must be registered in the MXC build, and experimental backend
authorization is an independent invocation option.

## Choosing an operation

| Task | Input | Result |
Expand Down
1 change: 1 addition & 0 deletions docs/api-reference/dotnet/v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Filesystem discovery helpers use `Microsoft.Mxc.Sdk.V1.Policy.Filesystem`.
Discovery, probes, telemetry, and policy helpers use the same versioned boundary.

- [Operation signatures](api.md)
- [V1.Dev exact-JSON API](dev.md)
- [Types and fields](types.md)
- [Cross-SDK requirements](../../README.md)

Expand Down
6 changes: 5 additions & 1 deletion docs/api-reference/dotnet/v1/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ 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 launch API or API accepting ["MXC request JSON"](../../../schema.md#mxc-request-json).
is no separate public attached-console launch API. Typed V1 does not accept
["MXC request JSON"](../../../schema.md#mxc-request-json); the
[V1.Dev exact-JSON API](dev.md) accepts caller-authored requests separately.
Captured existing-container execution closes its output readers on timeout
and returns the text collected so far with `TimedOut = true`.

## Discovery and validation

Expand Down
67 changes: 67 additions & 0 deletions docs/api-reference/dotnet/v1/dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# .NET V1.Dev exact-JSON API

Public namespace: `Microsoft.Mxc.Sdk.V1.Dev`. All methods accept one complete
caller-authored exact-version JSON string; they do not stamp a version, parse
through typed V1, or take a duplicate container ID. Native exact parsing
rejects unregistered versions and unknown fields. Phase-specific methods
check the JSON phase before calling native code.

`JsonOptions.Experimental` authorizes an experimental backend independently
of the JSON version. `PtyJsonOptions` is a separate option type for PTY
operations with its own `Experimental` and optional `MxcPtySize? Size`
(24 rows by 80 columns by default). Non-PTY operations accept only
`JsonOptions`. Return types are the existing V1 execution and process types;
lifecycle and validation results are complete native response JSON strings,
without narrowing through typed V1 metadata. When importing both typed and
dev namespaces, qualify the dev facade (for example,
`using Dev = Microsoft.Mxc.Sdk.V1.Dev;`).

```csharp
// Microsoft.Mxc.Sdk.V1.Dev.MxcContainer
ExecutionResult RunJson(string json, JsonOptions? options = null);
Task<ExecutionResult> RunJsonAsync(string json, JsonOptions? options = null,
CancellationToken cancellationToken = default);
MxcProcess SpawnJson(string json, JsonOptions? options = null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to reason over the overall design.

Is it a normal SDK pattern to have a complete a ::dev:: namespace that duplicates the API surface for active development?

Could it be something like,

  1. "dev" namespace just contains a request object (new ContainerRequestDev that just holds the json string)
  2. Change the real Spawn API to accept an interface, IContainerRequest

?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that way there's only one Spawn function and it's easier for consumer to migrate code from "dev" to stable

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for raising the migration ergonomics. The separate V1.Dev surface is intentional: typed V1 Spawn(ContainerRequest) emits the SDK-owned published contract with no experimental opt-in, while SpawnJson preserves the caller-authored exact version and takes backend authorization separately. An IContainerRequest overload would mix those contracts in the stable facade, and it would not address lifecycle calls whose JSON owns the phase/id and whose response can contain evolving metadata. The one-shot result and process-handle types are already shared. Moving a promoted feature to typed V1 still requires converting the request fields; sharing the Spawn name would not remove that step. I would keep explicit *Json operations in this PR and consider a cross-SDK convenience surface separately if migration friction warrants it.

Task<MxcProcess> SpawnJsonAsync(string json, JsonOptions? options = null,
CancellationToken cancellationToken = default);
MxcPtyProcess SpawnWithPtyJson(string json, PtyJsonOptions? options = null);

// Microsoft.Mxc.Sdk.V1.Dev.MxcLifecycle
ExecutionResult RunInContainerJson(string json, JsonOptions? options = null);
Task<ExecutionResult> RunInContainerJsonAsync(string json, JsonOptions? options = null,
CancellationToken cancellationToken = default);
MxcProcess SpawnInContainerJson(string json, JsonOptions? options = null);
Task<MxcProcess> SpawnInContainerJsonAsync(string json, JsonOptions? options = null,
CancellationToken cancellationToken = default);
MxcPtyProcess SpawnInContainerWithPtyJson(string json, PtyJsonOptions? options = null);

string ProvisionContainerJson(string json, JsonOptions? options = null);
Task<string> ProvisionContainerJsonAsync(string json, JsonOptions? options = null);
string StartContainerJson(string json, JsonOptions? options = null);
Task<string> StartContainerJsonAsync(string json, JsonOptions? options = null);
string StopContainerJson(string json, JsonOptions? options = null);
Task<string> StopContainerJsonAsync(string json, JsonOptions? options = null);
string DeprovisionContainerJson(string json, JsonOptions? options = null);
Task<string> DeprovisionContainerJsonAsync(string json, JsonOptions? options = null);

string ValidateProvisionJson(string json, JsonOptions? options = null);
Task<string> ValidateProvisionJsonAsync(string json, JsonOptions? options = null);
string ValidateStartJson(string json, JsonOptions? options = null);
Task<string> ValidateStartJsonAsync(string json, JsonOptions? options = null);
string ValidateStopJson(string json, JsonOptions? options = null);
Task<string> ValidateStopJsonAsync(string json, JsonOptions? options = null);
string ValidateDeprovisionJson(string json, JsonOptions? options = null);
Task<string> ValidateDeprovisionJsonAsync(string json, JsonOptions? options = null);
string ValidateProcessJson(string json, JsonOptions? options = null);
Task<string> ValidateProcessJsonAsync(string json, JsonOptions? options = null);
```

PTY entry points are synchronous only. Only the four async run/spawn methods
take cancellation tokens, matching their existing typed V1 counterparts.
One-shot `RunJsonAsync` cancellation stops awaiting, not native execution;
`RunInContainerJsonAsync` disposes its owned process on cancellation. Async
lifecycle operations run off the caller's thread but have no cancellation
token. Captured output is returned as lossy UTF-8 text; live handles retain
the existing V1 stream and disposal contract.
Timed-out in-container capture closes its readers and returns partial text
without deprovisioning the caller-owned container.
1 change: 1 addition & 0 deletions docs/api-reference/node/v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Filesystem discovery helpers and their result/options types use `policy.filesyst
Discovery, probes, telemetry, and policy helpers use the same versioned boundary.

- [Operation signatures](api.md)
- [V1.Dev exact-JSON API](dev.md)
- [Types and fields](types.md)
- [Cross-SDK requirements](../../README.md)

Expand Down
10 changes: 9 additions & 1 deletion docs/api-reference/node/v1/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ 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 launch API or API accepting ["MXC request JSON"](../../../schema.md#mxc-request-json).
ownership; there is no separate attached-console launch API. Typed V1 does
not accept ["MXC request JSON"](../../../schema.md#mxc-request-json); the
[V1.Dev exact-JSON API](dev.md) accepts caller-authored requests separately.
Captured existing-container execution closes its output streams on timeout
and returns the text collected so far with `timedOut: true`.
Output read failures reject capture without waiting for the workload to exit.
For executor-backed ProcessContainer PTY, `timeoutMs` reaches `wxc-exec`,
but its exit channel does not distinguish a timeout from an ordinary exit:
`wait().timedOut` is `false` when the executor exits in either case.

## Discovery and validation

Expand Down
54 changes: 54 additions & 0 deletions docs/api-reference/node/v1/dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Node V1.Dev exact-JSON API

Public subpath: `@microsoft/mxc-sdk/v1/dev`. All operations accept one
complete caller-authored exact-version JSON string and return Promises.
The SDK never stamps a version or reserializes the document through the V1
stable mapper. Native exact parsing remains authoritative for unknown fields,
unsupported versions, policy, and backend availability. There is no
package-root alias.

`JsonOptions` contains optional `experimental?: boolean` authorization,
independent of the authored JSON version. `PtyJsonOptions` additionally
contains optional `size?: MxcPtySize` (24 rows by 80 columns by default).
Execution returns the existing V1 result or process types. Lifecycle and
validation return the complete native response JSON string. Phase-specific
methods reject a different phase before native dispatch.

```typescript
export function runJson(json: string, options?: JsonOptions): Promise<ExecutionResult>;
export function spawnJson(json: string, options?: JsonOptions): Promise<MxcProcess>;
export function spawnWithPtyJson(json: string, options?: PtyJsonOptions): Promise<MxcPtyProcess>;

export function runInContainerJson(json: string, options?: JsonOptions): Promise<ExecutionResult>;
export function spawnInContainerJson(json: string, options?: JsonOptions): Promise<MxcProcess>;
export function spawnInContainerWithPtyJson(
json: string, options?: PtyJsonOptions
): Promise<MxcPtyProcess>;

export function provisionContainerJson(json: string, options?: JsonOptions): Promise<string>;
export function startContainerJson(json: string, options?: JsonOptions): Promise<string>;
export function stopContainerJson(json: string, options?: JsonOptions): Promise<string>;
export function deprovisionContainerJson(json: string, options?: JsonOptions): Promise<string>;
export function validateProvisionJson(json: string, options?: JsonOptions): Promise<string>;
export function validateStartJson(json: string, options?: JsonOptions): Promise<string>;
export function validateStopJson(json: string, options?: JsonOptions): Promise<string>;
export function validateDeprovisionJson(json: string, options?: JsonOptions): Promise<string>;
export function validateProcessJson(json: string, options?: JsonOptions): Promise<string>;
```

Captured stdout and stderr are returned as lossy UTF-8 text. Pipe and PTY
operations resolve with owning V1 handles after startup. Existing-container
execution does not deprovision its caller-owned container. On timeout,
in-container capture closes its output streams and returns the text collected
so far. If capture fails, disposal cannot replace the
original read or wait error; read failures reject without waiting for the
workload to exit. The Node ProcessContainer one-shot PTY route
passes the original UTF-8 JSON through `wxc-exec --config-base64`, including
Comment on lines +45 to +46
the historical `0.9.0-alpha` `appcontainer` alias, rather than the stable
typed mapper. Its Promise resolves when the executor starts, before the executor
finishes policy validation; subsequent failures appear in combined terminal
output and its exit code. This route cannot expose structured native warnings
or output metadata through its current terminal channel. It forwards
`process.timeout` to `wxc-exec`, but `wait().timedOut` remains `false` on an
executor exit even if the executor enforced that timeout; the terminal
channel supplies no distinct timeout outcome.
1 change: 1 addition & 0 deletions docs/api-reference/rust/v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Filesystem discovery helpers and their result/options types use `v1::policy::fil
Discovery, probes, telemetry, and policy helpers use the same versioned boundary.

- [Operation signatures](api.md)
- [V1.Dev exact-JSON API](dev.md)
- [Types and fields](types.md)
- [Cross-SDK requirements](../../README.md)

Expand Down
3 changes: 2 additions & 1 deletion docs/api-reference/rust/v1/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Public entrypoint: `mxc_sdk::v1`. [Types](types.md) | [Overview](README.md)

Signatures describe the typed consumer API and omit implementation bodies.
Callers do not supply JSON or a schema version.
JSON request adapters are not part of the supported V1 API.
The separate [V1.Dev exact-JSON API](dev.md) accepts caller-authored requests
for one-shot execution and state-aware lifecycle operations.
Comment on lines +9 to +10
Attached execution is not exposed by the V1 SDK.

## Choosing a launch operation
Expand Down
51 changes: 51 additions & 0 deletions docs/api-reference/rust/v1/dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Rust V1.Dev exact-JSON API

Public entrypoint: `mxc_sdk::v1::dev`. All methods accept one complete,
caller-authored exact-version JSON document and execute through the
in-process engine. No method stamps a version, reserializes the request, or
takes a second container ID. Invalid versions, unknown fields, and malformed
JSON are rejected by the exact contract parser.
Comment on lines +6 to +7

`JsonOptions { experimental: bool }` authorizes an experimental backend
independently of the authored version. `PtyJsonOptions` also carries
`size: MxcPtySize` (24 rows by 80 columns by default). The full request,
including its version, phase, and any container ID, is a single JSON string.
Result and process types are the existing `mxc_sdk::v1` types.

`run_json` and `run_in_container_json` return byte-preserving
`ExecutionResult` values; a nonzero workload exit or timeout is an outcome,
not an API error. Pipe and PTY spawns return owned `MxcProcess` and
`MxcPtyProcess` handles. Existing-container execution does not deprovision
the caller-owned container. The phase-specific lifecycle and validation
methods require the matching `phase` in the JSON before dispatch; validation
uses native dry-run behavior. They return the complete native response
envelope as a `String`, including backend-specific metadata.

```rust
pub fn run_json(json: &str, options: JsonOptions) -> Result<ExecutionResult, Error>;
pub fn spawn_json(json: &str, options: JsonOptions) -> Result<MxcProcess, Error>;
pub fn spawn_with_pty_json(json: &str, options: PtyJsonOptions)
-> Result<MxcPtyProcess, Error>;

pub fn run_in_container_json(json: &str, options: JsonOptions)
-> Result<ExecutionResult, Error>;
pub fn spawn_in_container_json(json: &str, options: JsonOptions) -> Result<MxcProcess, Error>;
pub fn spawn_in_container_with_pty_json(json: &str, options: PtyJsonOptions)
-> Result<MxcPtyProcess, Error>;

pub fn provision_container_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn start_container_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn stop_container_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn deprovision_container_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn validate_provision_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn validate_start_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn validate_stop_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn validate_deprovision_json(json: &str, options: JsonOptions) -> Result<String, Error>;
pub fn validate_process_json(json: &str, options: JsonOptions) -> Result<String, Error>;
```

Capture uses pipe-backed execution: a backend without piped execution
rejects this mode with `UnsupportedContainment` even when experimental
access is authorized. PTY and state-aware support also depend on the
selected backend and host. Unsupported modes fail rather than switching to
another I/O mode.
5 changes: 5 additions & 0 deletions docs/development/architecture/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ take a caller-supplied schema version. Each v1 SDK targets the published exact

Raw configuration APIs require the caller to declare a registered `version`
to select an immutable published or mutable development contract.
Rust `mxc_sdk::v1::dev`, .NET `Microsoft.Mxc.Sdk.V1.Dev`, and Node
`@microsoft/mxc-sdk/v1/dev` accept complete caller-authored one-shot or
state-aware JSON strings using the native exact-version parsing path.
Experimental backend authorization is supplied separately in invocation
options, not inferred from the document's version.

```typescript
// sdk/node/src/types.ts
Expand Down
8 changes: 8 additions & 0 deletions sdk/dotnet/Microsoft.Mxc.Sdk.AotSmokeTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Text.Json.Nodes;
using Microsoft.Mxc.Sdk;
using Microsoft.Mxc.Sdk.V1;
using Dev = Microsoft.Mxc.Sdk.V1.Dev;

static void Check(bool condition, string what)
{
Expand All @@ -34,6 +35,13 @@ static void CheckRoundTrip<T>(T value, Func<T, bool> check, string name)
}

var initialPtySize = new MxcPtySize(40, 120);
CheckRoundTrip(new Dev.JsonOptions { Experimental = true },
x => x.Experimental, "dev JSON options experimental authorization");
CheckRoundTrip(new Dev.PtyJsonOptions(),
x => !x.Experimental && x.Size is null, "dev PTY options without initial dimensions");
CheckRoundTrip(new Dev.PtyJsonOptions { Experimental = true, Size = initialPtySize },
x => x.Experimental && x.Size == initialPtySize,
"dev PTY options experimental authorization and initial dimensions");
CheckRoundTrip(new TelemetryConfig(), x => x.Enabled is null, "omitted telemetry enabled");
CheckRoundTrip(new TelemetryConfig { Enabled = false }, x => x.Enabled == false, "explicit disabled telemetry");
foreach (var enabled in new bool?[] { null, true, false })
Expand Down
Loading
Loading