Skip to content

Update Learning Mode to V2 API contracts - #739

Merged
shschaefer merged 16 commits into
mainfrom
user/saulg/learning-mode-api-contract-v2
Aug 6, 2026
Merged

Update Learning Mode to V2 API contracts#739
shschaefer merged 16 commits into
mainfrom
user/saulg/learning-mode-api-contract-v2

Conversation

@richiemsft

@richiemsft Richie Gomez (richiemsft) commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📖 Description

Updates captureDenials to the official V2 Windows Learning Mode and process
security-environment contracts.

  • Uses the V2 Learning Mode ABI:
    • HRESULT StartLearningModeTrace
    • retryable, non-consuming HRESULT StopLearningModeTrace
    • infallible CloseLearningModeTrace
  • Applies three bounded delivery attempts for transient Stop failures
    (sharing/lock contention, busy, or retry requested), while permanent errors
    remain single-attempt and the final HRESULT is preserved.
  • Uses the V2 process security-environment ABI:
    • HRESULT CreateProcessSecurityEnvironment
    • QueryProcessSecurityEnvironmentSupport
    • infallible, by-value CloseProcessSecurityEnvironment
  • Adds generated bindings for the official PSEC 1.0 FlatBuffer contract.
  • Preserves the supported CreateProcessW +
    PROC_THREAD_ATTRIBUTE_SECURITY_ENVIRONMENT launch path; no legacy
    CreateProcessAsUserInsideSecurityEnvironment dependency remains.
  • Routes BaseContainer by request and host capability: schema 0.8+ prefers PSEC,
    while ordinary requests retain SBOX/AppContainer fallback when PSEC is
    unavailable or cannot represent the requested policy. captureDenials
    remains PSEC-only and fails closed.
  • Uses IsApiSetImplemented("api-win-appmodel-processmodel~securityenvironment")
    before resolving the official security-environment exports.
  • Encodes explicit PSEC egress defaults and routes unsupported ordinary
    leastPrivilege, proxy, and deny-policy combinations through compatible
    lower tiers; capture and host-list combinations still fail closed.
  • Requires the complete official V2 export set, so older V1 hosts return
    backend_unavailable instead of invoking an incompatible ABI.
  • Memoizes successful and failed export resolution plus the process-wide PSEC
    deny-support query.
  • Adds host-independent lifecycle, export-completeness, V1-degradation, and
    PSEC fail-closed tests.
  • Records PSEC schema provenance (validated Windows build, schema SHA-256, and
    exact flatc release assets/checksums) and adds a CI gate that regenerates
    and byte-compares the bindings with the pinned compiler.
  • Retains the existing JSON denial document and structured output-metadata
    contract introduced by Emit captureDenials JSON output (replacement for #701) #710.

Proxy compatibility fallback

Windows OS PR 16153076 changed the capability-aware SBOX proxy contract. Proxy
requests now require an AppContainer-hosted proxy named through
allowed_appcontainer_peer, require privateNetworkClientServer, and reject
internetClient. MXC does not yet author that model-2 peer identity.

Until that integration is implemented, hosts exposing
Experimental_QuerySandboxSupport route proxy requests to the AppContainer
compatibility path instead of launching an invalid SBOX request. Query-less
hosts retain the legacy SBOX proxy behavior. This is intentionally a temporary
compatibility fallback; model-2 SBOX support should be implemented separately.

VM validation on build 26663.1000 confirmed minimal built-in WinHTTP proxy
E2Es pass for schema 0.7.0-alpha and 0.8.0-alpha. The full Node integration
policy additionally grants SDK tool paths such as C:\Debuggers; when the
fallback reaches AppContainer+DACL, the test account must have WRITE_DAC on
those paths. That host permission is not required once MXC supports model-2 T1.
The separate OS-side ETW provider-readiness improvement is intentionally out of
scope for this ABI update.

🔗 References

🔍 Validation

  • cargo fmt --all -- --check
  • cargo test -p learning_mode_core -p learning_mode_windows -p appcontainer_common --all-targets
  • cargo test -p wxc --no-run
  • cargo test -p wxc_e2e_tests --no-run
  • cargo clippy -p learning_mode_core -p learning_mode_windows -p appcontainer_common --all-targets -- -D warnings
  • cargo check --workspace --all-targets
  • node scripts/versioning/check-schema-codegen.js
  • node scripts/versioning/check-sdk-types-codegen.js
  • node scripts/versioning/check-psec-codegen.js
  • node scripts/versioning/validate-configs.js
  • Independent adversarial, performance, reliability, leak, API-contract,
    lifecycle/concurrency, data-validation, and error-handling reviews found no
    blocking MXC issues.
  • V1 host (26657.1002):
    • V2 probes reject the incomplete export set.
    • captureDenials returns FailurePhase::BackendUnavailable.
    • no output document is created; schema 0.7 ProcessContainer execution remains
      functional through the legacy SBOX path.
  • V2 host (26663.1000):
    • all official V2 exports resolve.
    • standalone Start → launch → Stop → Close produced a 3,302-byte ETL.
    • block and allow captures produced JSON documents and structured pointer
      metadata.
    • schema 0.8 ordinary and capture ProcessContainer requests use PSEC.

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task

GitHub Actions runs the PR validation build automatically. The ADO pipeline
(MXC-PR-Build) is the Azure version of the PR pipeline, kept in parity with
the GitHub Actions build; it runs on merge to main, and Microsoft reviewers
with write access can trigger it on a PR with /azp run. See
docs/pull-requests.md.

If the dependency-feed-check check fails on a new dependency, the crate must
be added to the feed before the PR can pass. See
docs/pull-requests.md
for the steps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3a8860a9-bb20-48ef-91a1-3da8e34b92fb
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3a8860a9-bb20-48ef-91a1-3da8e34b92fb
Document the official-only exports and attribute-based process launch after rebasing the V2 contract changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3a8860a9-bb20-48ef-91a1-3da8e34b92fb
Clarify that captureDenials now decodes ETLs and returns output metadata automatically, while lm_analyze remains a developer diagnostic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3a8860a9-bb20-48ef-91a1-3da8e34b92fb
@richiemsft
Richie Gomez (richiemsft) requested a review from a team as a code owner August 3, 2026 22:27
Copilot AI balanced review requested due to automatic review settings August 3, 2026 22:27
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@richiemsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates Windows captureDenials to the official V2 Learning Mode and PSEC contracts.

Changes:

  • Implements HRESULT-based Start/Stop and explicit Close lifecycles.
  • Adds generated PSEC 1.0 bindings and capture-specific policy encoding.
  • Bypasses legacy SBOX fallback and documents unsupported policy combinations.

Reviewed changes

Copilot reviewed 16 out of 32 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
.github/copilot-instructions.md Documents V2 lifecycle architecture.
docs/learning-mode/capabilities.md Documents capture restrictions.
docs/schema.md Updates schema guidance.
external/windows-sdk/ProcessSecurityEnvironment.fbs Adds official PSEC schema.
src/Cargo.lock Records PSEC crate dependencies.
src/Cargo.toml Registers the generated crate.
src/backends/appcontainer/common/Cargo.toml Adds PSEC dependency.
src/backends/appcontainer/common/src/base_container_runner.rs Builds and launches PSEC capture environments.
src/backends/appcontainer/common/src/dispatcher.rs Routes capture directly to V2.
src/backends/learning_mode/windows/Cargo.toml Updates example dependencies.
src/backends/learning_mode/windows/examples/lm_analyze.rs Clarifies diagnostic usage.
src/backends/learning_mode/windows/examples/lm_capture.rs Exercises PSEC V2 capture.
src/backends/learning_mode/windows/examples/lm_probe.rs Probes V2 exports.
src/backends/learning_mode/windows/src/ffi.rs Implements V2 trace ABI.
src/backends/learning_mode/windows/src/lib.rs Updates public contracts and errors.
src/backends/learning_mode/windows/src/lifecycle.rs Implements Stop/Close sequencing.
src/backends/learning_mode/windows/src/secenv.rs Implements V2 PSEC ABI.
src/core/generated/process_security_environment_specification/Cargo.toml Defines generated crate.
src/core/generated/process_security_environment_specification/README.md Documents regeneration.
src/core/generated/process_security_environment_specification/regenerate.ps1 Adds binding generator script.
src/core/generated/process_security_environment_specification/src/lib.rs Exports generated modules.
…/destination_rule_generated.rs Defines destination-rule bindings.
…/endpoint_policy_generated.rs Defines endpoint-policy bindings.
…/endpoint_rule_generated.rs Defines endpoint-rule bindings.
…/filter_action_generated.rs Defines filter-action bindings.
…/ip_protocol_generated.rs Defines protocol bindings.
…/ip_subnet_generated.rs Defines subnet bindings.
…/network_policy_generated.rs Defines network-policy bindings.
…/port_rule_generated.rs Defines port-rule bindings.
…/process_security_environment_generated.rs Defines root PSEC bindings.
…/proxy_info_generated.rs Defines proxy bindings.
…/schema_version_generated.rs Defines schema-version bindings.

Comment thread src/backends/appcontainer/common/src/base_container_runner.rs
Comment thread src/backends/learning_mode/windows/examples/lm_probe.rs
Use a V2-specific denied-path capability error and document the complete process security-environment probe surface.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3a8860a9-bb20-48ef-91a1-3da8e34b92fb
Copilot AI review requested due to automatic review settings August 3, 2026 22:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 32 changed files in this pull request and generated 1 comment.

Comment thread src/backends/learning_mode/windows/src/lifecycle.rs
Use the V2 non-consuming Stop contract for three bounded attempts on transient output contention while preserving permanent and exhausted HRESULT failures before exactly-once close.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3a8860a9-bb20-48ef-91a1-3da8e34b92fb
Copilot AI review requested due to automatic review settings August 3, 2026 23:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 32 changed files in this pull request and generated no new comments.

@MGudgin Gudge (MGudgin) left a comment

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.

Adversarial review — 13 axes

Ran a multi-axis adversarial review (security, reliability, performance, maintainability, correctness, testability, test coverage, documentation-drift, cross-platform-parity, concurrency, backward-compatibility, supply-chain, proportionality), each axis in an isolated context on a non-Claude model, then consolidated.

I then re-verified every finding that cited code outside the diff, to make sure this PR is not being blamed for pre-existing issues. That pass moved four findings out of scope and made one worse. Details below — I'd rather hand you a shorter, accurate list than a longer one.

What the review found clean, with receipts

The hardest parts of an ABI migration are right, and that's worth saying plainly:

  • ABI fidelity — all six V2 exports match the documented contract, including the by-value CloseProcessSecurityEnvironment (secenv.rs:78-79) and the void-returning infallible CloseLearningModeTrace (ffi.rs:46). The pointer-vs-by-value trap is handled correctly.
  • HRESULT handling — everything goes through HRESULT::is_err() (self.0 < 0), so S_FALSE is correctly treated as success — and there's an explicit regression test for it (successful_hresult_starts_retryable_trace, ffi.rs:381).
  • Security — System32-only DLL loading, exact+complete export matching, unpredictable 128-bit denial filenames with create-new semantics, and job-object/suspended-launch enforcement retained through the legacy removal.
  • Concurrency — exact-once close, correct trace-before-environment drop order on success/error/unwind, handles nulled before Drop, and the Ctrl-C handler shares no trace/PSEC state.
  • Proportionality — the hand-written churn maps to an externally forced ABI change; no scope creep found.
  • Performance of the feature-off path — executions without captureDenials pay zero; all new DLL loading, OS queries and PSEC construction are cleanly gated.

The one thing I'd fix first

CaptureSession::finish (lifecycle.rs:111) calls StopLearningModeTrace once, treats every HRESULT as terminal, and then closes the trace and environment unconditionally. Your ffi.rs layer implements the non-consuming/retryable contract correctlyfailed_hresult_keeps_trace_live_until_close proves it — and then finish takes self by value and throws that capability away. A transient delivery failure fails an already-completed sandbox execution and permanently loses the denial document.

The verification pass — findings I am NOT attributing to this PR

These were raised by agents but I confirmed against git show 80cd261c:<path> that they are pre-existing. Flagging them only as context / possible follow-ups, not as things to fix here:

  1. Unchecked ResumeThread (base_container_runner.rs:1614). Reported as a new hang risk. It is byte-identical between base and HEAD, and there is exactly one CreateProcessW call site in both revisions — so there is no new capture-specific launch. Pre-existing; the contrast with the AppContainer path (which does terminate and report) is a pre-existing inconsistency worth a separate issue.
  2. LearningModeError flattened into io::Error::other (combine_capture_and_cleanup_results, HEAD :2014). Reported as a High testability blocker. The function is unchanged by this PR (base :1896); it arrived with #710. Your new LearningModeError variants do flow through it and get stringified, so the diagnosability ceiling is real — but it isn't yours to fix in this PR.
  3. The capture E2E is #[ignore] and asserts vacuously (e2e_windows.rs:284). e2e_windows.rs is not modified by this PR and the test is identical at base — same cmd.exe /c echo workload producing an empty denials array. Pre-existing. The fair criticism is only that it wasn't extended to cover V2.
  4. --probe disagrees with the dispatcher. Reported as a High. The dispatcher already had a capture_denials override at base (dispatcher.rs:342), and probe.rs is unchanged, so the tier-misprediction divergence pre-dates this PR. However — see below, there is a genuinely new part.

Out-of-diff findings that ARE attributable

Medium — --probe doesn't reflect the new V2 export requirement. probe.rs is unchanged and still calls fallback_detector::detect directly (probe.rs:131), with no reference to capture_denials or Learning Mode anywhere in the file. The tier divergence is pre-existing, but this PR changes the availability threshold to the complete V2 export set, so --probe now reports success on a V1 host for a request that will fail backend_unavailable at validate(). Worth teaching run_probe about the V2 check and exposing it in ProbeFacts.

Medium — wire.rs / generated dev schema are now stale. src/core/wxc_common/src/wire.rs is unchanged, and I confirmed the captureDenials + leastPrivilege and captureDenials + network.proxy rejections (base_container_runner.rs:1679, :1686) have no equivalent at base — they are new here. Since wire.rs drives schemas/dev/, the generated schema and IDE hovers still describe the old contract. docs/schema.md got the note; its generator input didn't.

Medium — no Learning Mode row in docs/process-container/os-version-support.md. That file is unchanged. Given this PR establishes a hard OS build floor (validated V1 26657.1002 fails, V2 26663.1000 works), the per-release support matrix is where a user would look.


What I'd want before approving

  1. Retry Stop rather than discarding it (lifecycle.rs:111) — bounded, transient-HRESULT-only.
  2. Restore lifecycle test coverage — see the inline comment; lifecycle.rs went from 2 tests to 0 while gaining the V2 state machine.
  3. Add an FFI seam so export-set gating and the PSEC fail-closed paths can be tested without a V2 host.
  4. Update wire.rs and regenerate schemas/dev/, and record the V2 build floor in os-version-support.md.
  5. Record .fbs provenance and pin flatc — the repo already has the pattern in the WSLC bindings runbook.

The OnceLock memoisation, the workspace-member gating, and the spawn_base extraction all read as follow-ups. Nothing here suggests the migration is wrong — the implementation is in good shape; it's the verification that's thin relative to the blast radius. Happy to be pushed back on any of it, particularly the parity/build-cost point which is a judgement call.

Comment thread src/backends/learning_mode/windows/src/lifecycle.rs
Comment thread src/backends/learning_mode/windows/src/lifecycle.rs
Comment thread src/backends/learning_mode/windows/src/ffi.rs
Comment thread src/backends/learning_mode/windows/src/ffi.rs
Comment thread src/backends/learning_mode/windows/src/secenv.rs
Comment thread src/backends/appcontainer/common/src/base_container_runner.rs Outdated
Comment thread docs/learning-mode/capabilities.md
Comment thread src/Cargo.toml
Comment thread external/windows-sdk/ProcessSecurityEnvironment.fbs Outdated
Comment thread src/core/generated/process_security_environment_specification/regenerate.ps1 Outdated
Add host-independent lifecycle and fail-closed tests, memoize V2 capability discovery, document host requirements, regenerate schema surfaces, and pin/provenance-gate the generated PSEC contract.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3a8860a9-bb20-48ef-91a1-3da8e34b92fb
Comment thread scripts/versioning/check-psec-codegen.js Fixed
@microsoft-github-policy-service microsoft-github-policy-service Bot added Needs-Attention Issue needs attention from Microsoft and removed Needs-Author-Feedback Issue needs attention from issue or PR author labels Aug 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 39 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/backends/learning_mode/windows/src/ffi.rs:411

  • This public probe now bypasses the memoized loader and calls LoadLibraryExW on every invocation. Because probe_learning_mode_exports intentionally never calls FreeLibrary, repeated availability checks keep incrementing the DLL reference count and contradict the PR's process-wide export-resolution memoization. The memoized loader already requires all three V2 exports, so use it here.
    probe_learning_mode_exports().is_complete()

scripts/versioning/check-psec-codegen.js:252

  • The regenerate-and-diff gate formats the temporary crate from the repository root, so rustup does not see src/rust-toolchain.toml and instead uses the workflow's moving stable toolchain. The documented regeneration script formats from src/ with pinned Rust 1.93; once rustfmt output changes, unchanged generated bindings can fail this byte comparison. Set cwd to src for this invocation so both paths use the pinned formatter.
  fs.writeFileSync(join(genCrate, "Cargo.toml"), FMT_MANIFEST);
  execFileSync("cargo", ["fmt", "--manifest-path", join(genCrate, "Cargo.toml")], {
    stdio: ["ignore", "ignore", "inherit"],
  });

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 52e48fee-23e1-4b05-803c-522812fcdda2
Copilot AI review requested due to automatic review settings August 5, 2026 19:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 39 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/backends/learning_mode/windows/src/lifecycle.rs:107

  • finish(None) now calls StopLearningModeTrace (including its retry policy), but the production early-failure paths still use finish(None) to discard sessions (base_container_runner.rs:1522-1525, 1627-1629, and 1741-1743). Under the V2 contract documented here, early-exit discard is close-without-Stop, so those paths can now sleep and report a spurious teardown failure for an operation that discard does not require. Give callers an explicit infallible discard path (or drop the session) and reserve finish/Stop for ETL delivery.
    /// Stop the trace and deliver it to `output_path` (or skip delivery when
    /// `None`), retry transient delivery failures, close the trace, then close
    /// the security environment. Call **after** the child has exited.

scripts/versioning/check-psec-codegen.js:252

  • This regeneration runs cargo fmt from the repository root against a manifest under the temp directory, so rustup does not discover src/rust-toolchain.toml and uses the runner's default stable toolchain. The checked-in regeneration script formats from src with pinned Rust 1.93, meaning a rustfmt change in a newer stable release can make this byte-comparison gate fail despite correct generated output. Run this command with cwd set to the repository's src directory so both paths honor the same pin.
  execFileSync("cargo", ["fmt", "--manifest-path", join(genCrate, "Cargo.toml")], {
    stdio: ["ignore", "ignore", "inherit"],
  });

docs/schema.md:76

  • The complete example declares schema 0.6.0-alpha at line 26, but the new runner validation rejects captureDenials before schema 0.8. It also rejects leastPrivilege and network.proxy for every schema 0.8+ PSEC request, not only when capture is enabled. As written, this example is guaranteed to fail and the new comments understate the schema-wide restrictions; update the example/version structure so it presents a valid configuration and documents both constraints.
                                           // captureDenials cannot be combined with leastPrivilege.
                                           // captureDenials cannot currently be combined with network.proxy.

@richiemsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

// flatc --conform ProcessSecurityEnvironment.previous.fbs ProcessSecurityEnvironment.fbs
//
// --conform verifies that every field/enum/table in the old schema still exists
// at the same vtable slot, type, and default in the new schema.

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.

remove

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This schema is copied from the official OS PSEC schema, including the FlatBuffer evolution guidance, so I retained it to keep the checked-in contract aligned with its source. If you intended a different specific section on line 32 to be removed, could you clarify which portion?

}
}

impl SecurityEnvironmentApi {

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.

SecurityEnvironmentApi

why is the secenv code in learning_mode folder?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it's the only place that uses it at the moment. I introduced it since it was needed to call Brian's API.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good question. secenv is now used by both ordinary PSEC execution and Learning Mode capture, so it is broader than Learning Mode. Moving it directly into appcontainer_common would create a dependency cycle because the Learning Mode lifecycle also consumes it; the clean separation would be a dedicated Windows process-security-environment support crate. I kept that structural extraction out of this ABI/fallback fix, but agree it should be tracked separately.

Comment thread src/backends/learning_mode/windows/src/secenv.rs
Comment thread src/backends/appcontainer/common/src/base_container_runner.rs Outdated
@jsidewhite

Copy link
Copy Markdown
Member
        job.terminate(u32::MAX);

sergeyshilov does this work?


Refers to: src/backends/appcontainer/common/src/base_container_runner.rs:2125 in b9368ff. [](commit_id = b9368ff, deletion_comment = False)

Route proxy requests away from capability-aware SBOX contracts until MXC can author the required AppContainer peer identity. Add the official security-environment API-set probe and retain legacy SBOX proxy behavior on query-less hosts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 52e48fee-23e1-4b05-803c-522812fcdda2
Copilot AI review requested due to automatic review settings August 6, 2026 02:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 40 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/backends/appcontainer/common/src/dispatcher.rs:345

  • This reintroduces the schema-0.8 legacy fallback that the PR contract explicitly excludes. is_usable_for_request can return true via SBOX when PSEC is unavailable/incompatible, so ordinary 0.8 requests may run through SBOX (or AppContainer) rather than failing backend_unavailable; it also makes dry_run with captureDenials depend on host probes before runner validation. Select BaseContainer/PSEC directly for every schema 0.8+ request and let its validation report unsupported PSEC capabilities without entering legacy tier detection.
    // Keep the established tier fallback behavior for every schema version.
    // For schema 0.8+, BaseContainerRunner prefers PSEC when available and
    // otherwise uses the transitional SBOX contract. If neither BaseContainer
    // contract is usable, detection continues to the AppContainer tiers.
    let prefer_base_container = BaseContainerRunner::is_usable_for_request(request);

@richiemsft

Copy link
Copy Markdown
Contributor Author

Branden Bonaby (@bbonaby) FYI: PR #739 now includes a temporary MXC compatibility fallback for the SBOX model-2 proxy contract introduced by OS PR 16153076.

The updated OS contract requires allowed_appcontainer_peer, an AppContainer-hosted proxy, and privateNetworkClientServer, while rejecting the current internetClient shape. MXC does not yet author that peer identity, so capability-aware hosts now route proxy requests to the AppContainer compatibility path instead of failing with ERROR_INVALID_PARAMETER (87). Query-less hosts retain legacy SBOX proxy behavior.

Validated on build 26663.1000: minimal built-in WinHTTP proxy E2Es pass for schema 0.7 and 0.8. A separate MXC change is still needed to implement the model-2 T1 proxy architecture and remove this fallback.

@richiemsft

Copy link
Copy Markdown
Contributor Author

Jeff Whiteside (@jsidewhite) On the job.terminate(u32::MAX) question: UiJobObject::terminate is a best-effort wrapper around TerminateJobObject, so it terminates the sandbox child and all descendants assigned to that job. In the BaseContainer teardown path it is followed by an infinite wait on the root process to reap it before capture/proxy teardown continues. The AppContainer streaming path uses the same tree-kill behavior.

@richiemsft

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@bbonaby Branden Bonaby (bbonaby) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We can follow up with Sergey about jeffs comment, and the refactoring.

@shschaefer
shschaefer dismissed Gudge (MGudgin)’s stale review August 6, 2026 16:08

There are multiple requests for additional work to be done in follow on PRs. We will need to track that work and ensure it is complete.

@shschaefer
shschaefer merged commit 18d73ec into main Aug 6, 2026
41 checks passed
@shschaefer
shschaefer deleted the user/saulg/learning-mode-api-contract-v2 branch August 6, 2026 16:08
@microsoft-github-policy-service microsoft-github-policy-service Bot removed the Needs-Attention Issue needs attention from Microsoft label Aug 6, 2026
Darren Hoehna (dhoehna) added a commit to dhoehna/mxc that referenced this pull request Aug 8, 2026
The README said the vendored schema "is not publicly redistributable",
which reads as a restriction on the schema itself.  The restriction is on
the internal OS tree it was taken from: the schema text carries an MIT
header and has been committed to this public repository since microsoft#739.

Add the repository field so the published crate carries a source link.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ab22cd6f-f3d9-4325-8397-9e998aa058c3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants