Skip to content

feat(seatbelt): add system power access - #1066

Draft
Carlos Alexandro Becker (caarlos0) wants to merge 7 commits into
mainfrom
macos-bazel
Draft

feat(seatbelt): add system power access#1066
Carlos Alexandro Becker (caarlos0) wants to merge 7 commits into
mainfrom
macos-bazel

Conversation

@caarlos0

@caarlos0 Carlos Alexandro Becker (caarlos0) commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

📖 Description

Add opt-in seatbelt.systemPowerAccess support for macOS workloads that need
sleep/wake notifications or power assertions.

The capability is off by default. When enabled, the generated Seatbelt profile
allows only:

  • RootDomainUserClient
  • com.apple.PowerManagement.control
  • com.apple.iokit.powerdxpc

The new field is available in schema 0.9.0-alpha and later. This change wires
it through the versioned contract, parser, Rust and Node SDKs, generated
artifacts, tests, and documentation. Published 0.7 and 0.8 schemas remain
unchanged.

🔗 References

None.

🔍 Validation

  • Verified both macOS power APIs are denied with systemPowerAccess: false and
    allowed with systemPowerAccess: true through mxc-exec-mac.
  • Ran targeted Seatbelt, config contract, parser, and Rust SDK tests.
  • Built the Node SDK and ran its wire-conformance test.
  • Ran schema/type codegen, schema-version, and config-validation gates.
  • Ran Rust formatting and Clippy checks.
  • Merged the latest main and reran the full PR validation.

✅ 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.

Microsoft Reviewers: Open in CodeFlow

Add an opt-in capability for sleep/wake notifications and power assertions while keeping the default sandbox profile restricted.

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

Copilot-Session: 342a9bf7-a27a-457f-aaac-c910b8cd5535
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
@caarlos0
Carlos Alexandro Becker (caarlos0) requested review from a team and a balanced review from Copilot August 28, 2026 20:08
@azure-pipelines

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

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.

Copilot review overview

Review tier: Balanced
Findings: 2 Medium severity · 1 Low severity

New issues introduced by this change (3)
Severity Finding
Medium severity src/​core/​wxc_common/​src/​config_parser.rssystemPowerAccess is mapped without any schema-version check. The production loaders still…
Medium severity src/​core/​mxc_engine/​src/​policy.rs — This public setter bypasses schema validation because build_request has already parsed the…
Low severity docs/​schema.md — This example places systemPowerAccess under experimental.seatbelt, but the parser explicitly…
What changed in this PR

Adds opt-in macOS Seatbelt permissions for sleep/wake notifications and power assertions.

Changes:

  • Extends schema, parser, Rust SDK, and Node SDK contracts.
  • Emits narrowly scoped Seatbelt power-service rules.
  • Adds unit tests, generated artifacts, and documentation.
File Description
src/​core/​wxc_common/​src/​wire.rs Adds the wire field.
src/​core/​wxc_common/​src/​models.rs Adds the domain setting and default.
src/​core/​wxc_common/​src/​config_parser.rs Maps and tests the setting.
src/​core/​wxc_common/​src/​config_contract_adapters/​v0_8.rs Defaults the unavailable field.
src/​core/​wxc_common/​src/​config_contract_adapters/​v0_7.rs Defaults the unavailable field.
src/​core/​wxc_common/​src/​config_contract_adapters/​dev/​one_shot.rs Adapts the 0.9 contract field.
src/​core/​wxc_common/​src/​config_contract_adapters/​dev/​one_shot_tests/​stable_candidate.rs Tests adapter mappings.
src/​core/​mxc-sdk/​README.md Documents Rust SDK usage.
src/​core/​mxc_engine/​src/​policy.rs Adds the Rust SDK setter.
src/​core/​mxc_config_contract/​tests/​version_boundaries/​seatbelt.rs Tests 0.9 introduction.
src/​core/​mxc_config_contract/​tests/​v0_9_0_alpha/​seatbelt.rs Tests field typing.
src/​core/​mxc_config_contract/​tests/​v0_9_0_alpha/​optional_fields.rs Tests null rejection.
src/​core/​mxc_config_contract/​tests/​v0_9_0_alpha/​fixtures/​one_shot/​valid/​seatbelt_complete.json Extends the valid fixture.
src/​core/​mxc_config_contract/​src/​dev/​stable.rs Adds the exact contract field.
src/​backends/​seatbelt/​common/​src/​profile_builder.rs Generates and tests power rules.
sdk/​node/​src/​types.ts Exposes the Node SDK option.
sdk/​node/​src/​generated/​wire.ts Updates rolling generated types.
sdk/​node/​src/​generated/​v0_9_0_alpha/​wire.ts Updates exact generated types.
sdk/​node/​README.md Documents Node SDK usage.
schemas/​dev/​mxc-config.schema.0.9.0-dev.json Updates the rolling schema.
schemas/​dev/​mxc-config.schema.0.9.0-alpha.json Updates the exact schema.
docs/​seatbelt/​seatbelt-backend.md Documents backend behavior.
docs/​schema.md Updates the configuration example.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/core/wxc_common/src/config_parser.rs
Comment thread src/core/mxc_engine/src/policy.rs
Comment thread docs/schema.md Outdated
Copilot AI review requested due to automatic review settings August 28, 2026 20:13

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.

Copilot review overview

Review tier: Balanced
Findings: 2 Medium severity · 1 Low severity

Pre-existing issues (3)
Severity Finding
Medium severity src/​core/​mxc_engine/​src/​policy.rs — This public setter bypasses schema validation because build_request has already parsed the… View comment
Medium severity src/​core/​wxc_common/​src/​config_parser.rssystemPowerAccess is mapped without any schema-version check. The production loaders still… View comment
Low severity docs/​schema.md — This example places systemPowerAccess under experimental.seatbelt, but the parser explicitly… View comment
Suppressed comments (3)

src/core/mxc_engine/src/policy.rs:688

  • This mutator bypasses the advertised 0.9 schema boundary: callers can build and validate a 0.7/0.8 request, then set this model field after parsing, and the Seatbelt runner applies it without checking schema_version. The new test even exercises that path with 0.7.0-alpha. Make the setter reject unsupported versions (or add an execution-time invariant covering directly constructed requests) so the Rust SDK cannot enable a 0.9-only capability under an older contract.
    pub fn set_seatbelt_system_power_access(&mut self, allow: bool) -> &mut Self {
        self.inner
            .seatbelt
            .get_or_insert_default()
            .system_power_access = allow;

docs/schema.md:209

  • This example places the new option under experimental.seatbelt, but the parser explicitly rejects that section with “has moved to the stable section”; the supported path is top-level seatbelt.systemPowerAccess. Move the Seatbelt example out of the experimental object so readers can copy a valid 0.9 configuration.
            "systemPowerAccess": false      // Allow sleep/wake notifications and power assertions

src/core/wxc_common/src/config_parser.rs:623

  • systemPowerAccess is not version-gated in the production parser. Both production loaders still deserialize the rolling wire::MxcConfig directly, and the exact-contract adapters are explicitly not reachable from production, so a request declaring schema 0.7 or 0.8 can set this field to true and reach the profile builder. The contract boundary test alone does not enforce the runtime boundary. Add a raw field-version check to both loader paths (similar to directional networking) and a production parser regression test that rejects this field before 0.9.
        system_power_access: system_power_access.unwrap_or(false),

Gate systemPowerAccess at every one-shot loader and again before Seatbelt execution so post-parse SDK mutations cannot bypass schema 0.9.

Move the documented Seatbelt block to its supported top-level location.

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

Copilot-Session: 342a9bf7-a27a-457f-aaac-c910b8cd5535
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 29, 2026 16:39

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.

Copilot review overview

Review tier: Balanced
Findings: 1 Low severity

New issues introduced by this change (1)
Severity Finding
Low severity docs/​schema.md — This “Full Schema” example still declares &quot;version&quot;: &quot;0.6.0-alpha&quot; at line 87, but the parser…
Issues resolved since last review (3)
Severity Finding
Low severity docs/​schema.md — This example places systemPowerAccess under experimental.seatbelt, but the parser explicitly… View resolved comment
Medium severity src/​core/​mxc_engine/​src/​policy.rs — This public setter bypasses schema validation because build_request has already parsed the… View resolved comment
Medium severity src/​core/​wxc_common/​src/​config_parser.rssystemPowerAccess is mapped without any schema-version check. The production loaders still… View resolved comment

Comment thread docs/schema.md Outdated
Copilot AI review requested due to automatic review settings August 29, 2026 16:43

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.

Copilot review overview

Review tier: Balanced
Findings: 1 Low severity

Pre-existing issues (1)
Severity Finding
Low severity docs/​schema.md — This “Full Schema” example still declares &quot;version&quot;: &quot;0.6.0-alpha&quot; at line 87, but the parser… View comment

Keep the full schema example consistent with its systemPowerAccess field.

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

Copilot-Session: 342a9bf7-a27a-457f-aaac-c910b8cd5535
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 29, 2026 17:05

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.

Copilot review overview

Review tier: Balanced
Findings: None

Issues resolved since last review (1)
Severity Finding
Low severity docs/​schema.md — This “Full Schema” example still declares &quot;version&quot;: &quot;0.6.0-alpha&quot; at line 87, but the parser… View resolved comment

Resolve the Seatbelt policy move by keeping execution invariants in the backend and raw schema-version gates in wxc_common.

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

Copilot-Session: 342a9bf7-a27a-457f-aaac-c910b8cd5535
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 31, 2026 12:04
@microsoft-github-policy-service microsoft-github-policy-service Bot added the Copilot-Instructions PR modifies Copilot instruction files (.github/copilot-instructions.md or .github/instructions/) label Aug 31, 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.

Copilot review overview

Review tier: Balanced
Findings: None

Copilot AI review requested due to automatic review settings August 31, 2026 17:35

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.

Copilot review overview

Review tier: Balanced
Findings: None

Ok(())
}

/// Emit the narrow Seatbelt capability needed for system power notifications

@dhoehna Darren Hoehna (dhoehna) Sep 1, 2026

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.

What is a narrow capability?

/// Emit the narrow Seatbelt capability needed for system power notifications
/// and assertions. Off by default because opening `RootDomainUserClient` also
/// exposes host power-management operations beyond notification registration.
fn write_system_power_rules(out: &mut String, request: &ExecutionRequest) {

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.

Most methods have the out parameter as the last parameter. Can the out variable be moved to the end? Also, why have an out param instead of a return value?

fn system_power_access_default_off_omits_power_services() {
let r = req();
let p = build_profile(&r).unwrap();
assert!(!p.contains("systemPowerAccess"));

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.

Can these strings ever change? If they do will this test catch that change?

});
let p = build_profile(&r).unwrap();
assert!(p.contains("systemPowerAccess"));
assert!(p.contains("(global-name \"com.apple.PowerManagement.control\")"));

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.

Kinda odd that the names here have a quite around them but the previous test has no quotes. Is this delibrate?


#[test]
fn system_power_access_support_starts_at_v09() {
assert_eq!(system_power_access_support("0.8.0-alpha"), Some(false));

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.

Why only go down to 0.8.0?

}

#[test]
fn rejects_system_power_access_before_v09() {

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.

Looks like this is the same test as system_power_access_validation_rejects_pre_v09_requests?

pub keychain_access: OptionalField<bool>,
/// Whether system sleep/wake notifications and power assertions are allowed.
#[serde(default)]
pub system_power_access: OptionalField<bool>,

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.

The comment states power assertions but the variable is power_access


#[test]
fn rejects_non_boolean_system_power_access_values() {
for system_power_access in ["0", "1", "\"string\"", "[]", "{}"] {

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.

I think Copilot went a bit too in-depth with its unit testing.

Ok(())
}

fn validate_one_shot_field_versions(config: &serde_json::Value) -> Result<(), WxcError> {

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.

These tests belong in seatbelt not wxc. power access is a seatbelt specific functionality.

@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 should be careful with the features we're adding before 1.0 is locked down. We don't want to overly add features to the backends that we haven't discussed a need for. Let's chat to see where this requirement is coming from.

CC: Anis Mohammed Khaja Mohideen (@kanismohammed)

@caarlos0

Copy link
Copy Markdown
Collaborator Author

talked with Anis, will hold this for now. Will move this to draft.

@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 Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Copilot-Instructions PR modifies Copilot instruction files (.github/copilot-instructions.md or .github/instructions/) Needs-Attention Issue needs attention from Microsoft

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants