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
115 changes: 115 additions & 0 deletions .github/workflows/windows-process-host.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Windows process host

on:
push:
branches: [main, stream-*]
paths:
- 'crates/astrid-audit/**'
- 'crates/astrid-capsule/**'
- 'crates/astrid-core/**'
- 'crates/astrid-kernel/**'
- 'crates/astrid-mcp/**'
- 'crates/astrid-workspace/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/windows-process-host.yml'
pull_request:
branches: [main]
paths:
- 'crates/astrid-audit/**'
- 'crates/astrid-capsule/**'
- 'crates/astrid-core/**'
- 'crates/astrid-kernel/**'
- 'crates/astrid-mcp/**'
- 'crates/astrid-workspace/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/windows-process-host.yml'

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

jobs:
native:
name: Native ${{ matrix.arch }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: windows-2025
host: x86_64-pc-windows-msvc
- arch: aarch64
runner: windows-11-arm
host: aarch64-pc-windows-msvc
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
submodules: recursive

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
toolchain: '1.95.0'
components: clippy

- name: Cache Rust artifacts
uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2
with:
key: windows-process-${{ matrix.host }}
cache-on-failure: true

- name: Assert native architecture
shell: pwsh
env:
EXPECTED_HOST: ${{ matrix.host }}
run: |
$actual = (rustc -vV | Select-String '^host: ').Line.Substring(6)
if ($actual -ne $env:EXPECTED_HOST) {
throw "Expected native Rust host $env:EXPECTED_HOST, got $actual"
}

- name: Check process-host crates
run: >-
cargo check --locked
-p astrid-audit
-p astrid-capsule
-p astrid-core
-p astrid-kernel
-p astrid-mcp
-p astrid-workspace

- name: Clippy process-host crates
run: >-
cargo clippy --locked
-p astrid-audit
-p astrid-capsule
-p astrid-core
-p astrid-kernel
-p astrid-mcp
-p astrid-workspace
--all-targets
--all-features
--no-deps
--
-D warnings

- name: Test Windows process behavior
run: cargo test --locked -p astrid-capsule windows_ -- --nocapture

- name: Test Windows sandbox policy
run: cargo test --locked -p astrid-workspace windows_ -- --nocapture

- name: Test native MCP denial audit
run: cargo test --locked -p astrid-mcp windows_ -- --nocapture
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ Changelog tracking starts with 0.2.0. Prior versions were not tracked.
cancellable busy-instance retries support probe-then-connect and concurrent
clients. Native x86_64 and ARM64 Windows jobs exercise binding, concurrent
instances, shutdown, and reconnect behavior. Closes #1349.
- **The native process host has a deterministic Windows backend.** Capsule
arguments cross the OS boundary as distinct `CreateProcessW` arguments
without an implicit command shell; executable resolution, case-insensitive
environment handling, working directories, stdin/stdout/stderr, exit status,
cancellation, and descendant cleanup are pinned by native x86_64 and ARM64
tests. Each child tree is owned by a kill-on-close Windows Job Object, so
cancellation and root-first exit cannot strand descendants or chase a reused
PID. Explicit sandbox policy `off` permits trusted native execution while
`required` fails closed before exec; unsupported signals and untrusted native
MCP starts produce signed denial records. Host-local bind support follows the
local-transport backend from #1349 rather than adding a process-host-specific
transport. Closes #1351.
- **Linux amd64 now has a distro-neutral OCI build target.** The image packages
exact immutable GitHub release bytes only after their tagged release-workflow
signatures and manifest digests verify, runs the persistent daemon as a
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions crates/astrid-audit/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,17 @@ pub enum AuditAction {
#[serde(default, skip_serializing_if = "Option::is_none")]
device_key_id: Option<String>,
},

/// Signal requested for an existing child-process tree.
///
/// Keep new variants at the end of this public enum so existing implicit
/// discriminants remain stable for downstream consumers.
ProcessSignal {
/// Command or non-reversible process descriptor.
process: String,
/// Stable signal name.
signal: String,
},
}

impl AuditAction {
Expand Down Expand Up @@ -543,6 +554,9 @@ impl AuditAction {
Self::ProcessSpawn { command } => {
format!("Spawned process {command}")
},
Self::ProcessSignal { process, signal } => {
format!("Signalled process {process} with {signal}")
},
Self::CapabilityCreated { resource, .. } => {
format!("Created capability for {resource}")
},
Expand Down
9 changes: 9 additions & 0 deletions crates/astrid-capsule/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ uuid = { workspace = true }
[target.'cfg(unix)'.dependencies]
nix = { workspace = true }

[target.'cfg(windows)'.dependencies]
windows-sys = { workspace = true, features = [
"Win32_Foundation",
"Win32_Security",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_JobObjects",
"Win32_System_Threading",
] }

[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dependencies]
astrid-mcp = { workspace = true }
# `astrid-vfs` is native-only (it uses `cap-std` and tokio's filesystem
Expand Down
16 changes: 16 additions & 0 deletions crates/astrid-capsule/src/audit_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,20 @@ pub trait HostAuditSink: Send + Sync {
event: HostAuditEvent<'_>,
outcome: HostAuditOutcome<'_>,
);

/// Record a process-signal request without extending the public,
/// exhaustively matched [`HostAuditEvent`] enum.
///
/// The provided no-op keeps existing external sink implementations source
/// compatible. The kernel sink overrides this method and persists a typed,
/// signed process-signal action.
fn record_process_signal(
&self,
principal: &astrid_core::PrincipalId,
process: &str,
signal: &str,
outcome: HostAuditOutcome<'_>,
) {
let _ = (principal, process, signal, outcome);
}
}
Loading
Loading