diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 0aaf16e..1c88973 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -53,7 +53,8 @@ jobs:
hooks/claude-code/target
plugins/coding-agent-plugin/target
tools/coding-agents-kit-ctl/target
- key: ${{ runner.os }}-${{ matrix.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+ tests/target
+ key: ${{ runner.os }}-${{ matrix.arch }}-cargo-${{ hashFiles('hooks/claude-code/Cargo.lock', 'plugins/coding-agent-plugin/Cargo.lock', 'tools/coding-agents-kit-ctl/Cargo.lock', 'tests/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-cargo-
@@ -74,6 +75,65 @@ jobs:
- name: Run e2e tests
run: make test-e2e
+ build-test-windows:
+ name: Build & Test (Windows ${{ matrix.arch }})
+ runs-on: ${{ matrix.runner }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - arch: x64
+ runner: windows-latest
+ vcpkg_triplet: x64-windows-static
+ - arch: arm64
+ runner: windows-11-arm
+ vcpkg_triplet: arm64-windows-static
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Install make
+ run: choco install make -y
+
+ - name: Install vcpkg curl
+ run: vcpkg install curl:${{ matrix.vcpkg_triplet }}
+
+ - name: Cache Cargo registry and build artifacts
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.cargo/registry
+ ~/.cargo/git
+ hooks/claude-code/target
+ plugins/coding-agent-plugin/target
+ tools/coding-agents-kit-ctl/target
+ tests/target
+ key: ${{ runner.os }}-${{ matrix.arch }}-cargo-${{ hashFiles('hooks/claude-code/Cargo.lock', 'plugins/coding-agent-plugin/Cargo.lock', 'tools/coding-agents-kit-ctl/Cargo.lock', 'tests/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ matrix.arch }}-cargo-
+
+ - name: Cache Falco build
+ uses: actions/cache@v4
+ with:
+ path: |
+ build/falco-*-windows-*
+ build/falco-src-*
+ key: falco-windows-${{ matrix.arch }}-${{ hashFiles('installers/windows/build-falco.ps1', 'installers/windows/falco-windows-*.patch') }}
+
+ - name: Build
+ run: make build
+
+ - name: Build Falco from source
+ run: make falco-windows-${{ matrix.arch }}
+
+ - name: Run interceptor tests
+ run: make test-interceptor
+
+ - name: Run e2e tests
+ run: make test-e2e
+
build-test-macos:
name: Build & Test (macOS ${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
@@ -103,7 +163,8 @@ jobs:
hooks/claude-code/target
plugins/coding-agent-plugin/target
tools/coding-agents-kit-ctl/target
- key: ${{ runner.os }}-${{ matrix.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+ tests/target
+ key: ${{ runner.os }}-${{ matrix.arch }}-cargo-${{ hashFiles('hooks/claude-code/Cargo.lock', 'plugins/coding-agent-plugin/Cargo.lock', 'tools/coding-agents-kit-ctl/Cargo.lock', 'tests/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-cargo-
diff --git a/CLAUDE.md b/CLAUDE.md
index ce12fdd..d3d1f6b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
**coding-agents-kit** is a runtime security layer for AI coding agents. It intercepts tool calls (shell commands, file writes, web requests, etc.) before execution, evaluates them against Falco security rules, and enforces allow/deny/ask verdicts in real time. It operates entirely in user space with no elevated privileges.
-The initial version targets **Claude Code** on **Linux and macOS**. The architecture is designed to accommodate other coding agents (e.g., Codex) in the future.
+The project targets **Claude Code** on **Linux, macOS, and Windows**. The architecture is designed to accommodate other coding agents (e.g., Codex) in the future.
## Architecture
@@ -37,9 +37,9 @@ The initial version targets **Claude Code** on **Linux and macOS**. The architec
| **Interceptor** | `hooks/claude-code/` | Rust | Thin passthrough: reads hook JSON from stdin, wraps in envelope, sends to broker, maps verdict to stdout. No content interpretation. |
| **Plugin** | `plugins/` | Rust (falco_plugin SDK) | Falco source+extract plugin with embedded broker. Parses events, extracts fields, feeds Falco, receives alerts, resolves verdicts. |
| **Rules** | `rules/` | YAML (Falco rule language) | Vendor and local security policies. |
-| **Installer** | `installers/linux/`, `installers/macos/` | Shell | Platform-specific packaging, installation, hook registration, mode switching. |
+| **Installer** | `installers/linux/`, `installers/macos/`, `installers/windows/` | Shell/PowerShell | Platform-specific packaging, installation, hook registration, mode switching. |
| **Skills** | `skills/` | Claude Code skill format | Coding agent skills for rule authoring, status, etc. |
-| **Tests** | `tests/` | TBD | Integration and E2E tests. |
+| **Tests** | `tests/` | Rust | Cross-platform interceptor and E2E integration tests. |
## Key Design Decisions
@@ -283,7 +283,7 @@ The macOS implementation includes `is_service_loaded()` for idempotent start/sto
- **Falco 0.43** — rule engine, running in `nodriver` mode (no kernel instrumentation)
- **Rust** — interceptor and plugin (using `falco_plugin` crate v0.5.0)
-- **Platforms** — Linux (official Falco builds), macOS (Falco built from source with http_output patch)
+- **Platforms** — Linux (official Falco builds), macOS (Falco built from source with http_output patch), Windows (Falco built from source with http_output patch, system curl via vcpkg)
## Build & Development
@@ -301,12 +301,14 @@ Requires latest stable Rust (the falco_plugin SDK tracks latest stable as MSRV).
### Tests
```bash
-make test # Run all tests
+make test # Run all tests (cross-platform)
make test-interceptor # Interceptor unit tests (mock broker, no Falco needed)
-make test-e2e # E2E tests (requires Falco in PATH, plugin, and interceptor built)
+make test-e2e # E2E tests (requires Falco built, plugin, and interceptor)
```
-On Linux, use `make download-falco-linux` to download pre-built Falco binaries and `make falco-linux-bin-dir` to get the binary path. On macOS, use `make falco-macos` to build from source.
+Tests are written in Rust (`tests/` crate) and work on all platforms with the same targets. E2E tests automatically find the Falco binary from the project's build output (not the system). They skip gracefully if Falco is not built.
+
+On Linux, use `make download-falco-linux` to download pre-built Falco binaries. On macOS, use `make falco-macos` to build from source. On Windows, use `make falco-windows` to build from source (requires vcpkg + MSVC).
### Packaging
@@ -320,6 +322,11 @@ make macos-aarch64 # Apple Silicon
make macos-x86_64 # Intel (must run on Intel Mac)
make macos-universal # Fat binary (requires Rosetta + x86_64 Homebrew)
make falco-macos # Build only Falco (convenience target)
+
+# Windows (builds Falco from source, requires vcpkg + MSVC + WiX)
+make windows-x64 # x64 MSI package
+make windows-arm64 # arm64 MSI package
+make falco-windows # Build only Falco (convenience target)
```
### Environment Variables
diff --git a/Makefile b/Makefile
index ebaf543..deb9fee 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,11 @@ ARCH := $(shell uname -m)
.PHONY: all build build-interceptor build-plugin build-ctl \
download-falco-linux falco-linux-bin-dir \
falco-macos falco-macos-bin-dir \
+ falco-windows falco-windows-x64 falco-windows-arm64 \
test test-interceptor test-e2e \
linux linux-x86_64 linux-aarch64 \
macos macos-aarch64 macos-x86_64 macos-universal \
+ windows windows-x64 windows-arm64 \
clean help
all: linux
@@ -44,13 +46,13 @@ falco-linux-bin-dir:
## Run all tests
test: test-interceptor test-e2e
-## Run interceptor unit tests
-test-interceptor:
- bash tests/test_interceptor.sh
+## Run interceptor unit tests (Rust, cross-platform)
+test-interceptor: build-interceptor
+ cd tests && cargo test --test interceptor -- --nocapture
-## Run end-to-end tests (requires Falco in PATH)
-test-e2e:
- bash tests/test_e2e.sh
+## Run end-to-end tests (Rust, cross-platform, requires Falco built)
+test-e2e: build
+ cd tests && cargo test --test e2e --test e2e_monitor -- --nocapture
## Build Linux packages for all architectures
linux: linux-x86_64 linux-aarch64
@@ -86,12 +88,35 @@ falco-macos:
falco-macos-bin-dir:
@echo "build/falco-$(FALCO_VERSION)-darwin-$(subst arm64,aarch64,$(ARCH))"
+## Build Windows packages (must run on Windows)
+windows: windows-x64
+
+## Build Windows x64 MSI package
+windows-x64:
+ powershell -NoProfile -ExecutionPolicy Bypass -File installers/windows/package.ps1 -Arch x64
+
+## Build Windows arm64 MSI package
+windows-arm64:
+ powershell -NoProfile -ExecutionPolicy Bypass -File installers/windows/package.ps1 -Arch arm64
+
+## Build Falco from source for Windows (default: x64; requires vcpkg + MSVC)
+falco-windows: falco-windows-x64
+
+## Build Falco from source for Windows x64
+falco-windows-x64:
+ powershell -NoProfile -ExecutionPolicy Bypass -File installers/windows/build-falco.ps1 -Arch x64
+
+## Build Falco from source for Windows arm64
+falco-windows-arm64:
+ powershell -NoProfile -ExecutionPolicy Bypass -File installers/windows/build-falco.ps1 -Arch arm64
+
## Remove build artifacts
clean:
rm -rf build/
-cd hooks/claude-code && cargo clean
-cd plugins/coding-agent-plugin && cargo clean
-cd tools/coding-agents-kit-ctl && cargo clean
+ -cd tests && cargo clean
## Show available targets
help:
@@ -107,12 +132,16 @@ help:
@echo " test Run all tests"
@echo " test-interceptor Run interceptor unit tests"
@echo " test-e2e Run end-to-end tests (requires Falco in PATH)"
+ @echo " (tests are cross-platform — same targets work on all platforms)"
@echo ""
@echo "Falco:"
@echo " download-falco-linux Download pre-built Falco binary (Linux only)"
@echo " falco-linux-bin-dir Print path to downloaded Falco binary directory"
@echo " falco-macos Build Falco from source (macOS only)"
@echo " falco-macos-bin-dir Print path to built Falco binary directory"
+ @echo " falco-windows Build Falco from source for Windows (default: x64)"
+ @echo " falco-windows-x64 Build Falco from source for Windows x64"
+ @echo " falco-windows-arm64 Build Falco from source for Windows arm64"
@echo ""
@echo "Package:"
@echo " linux Build Linux packages for all architectures (default)"
@@ -122,6 +151,9 @@ help:
@echo " macos-aarch64 Build macOS Apple Silicon package"
@echo " macos-x86_64 Build macOS Intel package (must run on Intel Mac)"
@echo " macos-universal Build macOS universal binary (requires Rosetta + x86_64 Homebrew)"
+ @echo " windows Build Windows x64 MSI package (default)"
+ @echo " windows-x64 Build Windows x64 MSI package"
+ @echo " windows-arm64 Build Windows arm64 MSI package"
@echo ""
@echo "Other:"
@echo " clean Remove all build artifacts"
diff --git a/README.md b/README.md
index 69a7bf1..f482ed3 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#sandbox)
[](LICENSE)
-
+

> **Experimental Preview** — This project is under active development and released as an early preview. Interfaces and behavior may change between releases. We welcome your [feedback](#feedback) to help shape its future.
@@ -74,6 +74,19 @@ bash install.sh
The installer copies all components to `~/.coding-agents-kit/`, starts a systemd user service, and registers the hook automatically.
+### Windows
+
+Download the `.msi` installer from the [latest release](https://github.com/leogr/coding-agents-kit/releases/latest) and run:
+
+```powershell
+powershell -File Install-CodingAgentsKit.ps1
+```
+
+The installer deploys all components to `%LOCALAPPDATA%\coding-agents-kit\`, registers the Claude Code hook, and sets up auto-start on login.
+
+> [!NOTE]
+> x86_64 builds work on both x86_64 and ARM64 Windows (via emulation). See [`installers/windows/`](installers/windows/) for build prerequisites and details.
+
### Verify
```bash
@@ -184,10 +197,10 @@ The skill guides Claude through writing the rule, placing it in the right direct
|-------|----------|--------|
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Linux (x86_64, aarch64) | Supported |
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | macOS (Apple Silicon, Intel) | Supported |
+| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Windows (x86_64, ARM64) | Supported |
| [Codex](https://openai.com/index/codex/) | Linux, macOS | Planned |
-| — | Windows | Planned |
-We are actively working on expanding agent and platform support. [Codex](https://openai.com/index/codex/) integration and Windows support are next on the roadmap.
+We are actively working on expanding agent and platform support. [Codex](https://openai.com/index/codex/) integration is next on the roadmap.
## Building from Source
@@ -233,6 +246,23 @@ See [`installers/macos/`](installers/macos/) for details.
+
+Windows
+
+Requires: Rust (latest stable), Visual Studio 2022+ with C++ workload, CMake 3.24+, vcpkg with curl, .NET Runtime 8+, WiX Toolset v4.
+
+```powershell
+powershell -File installers\windows\package.ps1 -Version 0.1.2
+```
+
+Output: `build/out/coding-agents-kit-0.1.2-windows-x64.msi`
+
+> Falco is built from source on the first run (~10 min). Subsequent builds use the cached binary.
+
+See [`installers/windows/`](installers/windows/) for detailed prerequisites and build options.
+
+
+
Individual Components
diff --git a/hooks/claude-code/Cargo.lock b/hooks/claude-code/Cargo.lock
index 2173a33..4c97fd6 100644
--- a/hooks/claude-code/Cargo.lock
+++ b/hooks/claude-code/Cargo.lock
@@ -2,12 +2,117 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "bitflags"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
[[package]]
name = "claude-interceptor"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
+ "uds_windows",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "getrandom"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasip3",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
+[[package]]
+name = "indexmap"
+version = "2.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.16.1",
+ "serde",
+ "serde_core",
]
[[package]]
@@ -16,12 +121,61 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
+[[package]]
+name = "libc"
+version = "0.2.183"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -40,6 +194,31 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
+
[[package]]
name = "serde"
version = "1.0.228"
@@ -94,12 +273,197 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom",
+ "once_cell",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "uds_windows"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "windows-sys",
+]
+
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "wasip2"
+version = "1.0.2+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags",
+ "hashbrown 0.15.5",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "prettyplease",
+ "syn",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
+
[[package]]
name = "zmij"
version = "1.0.21"
diff --git a/hooks/claude-code/Cargo.toml b/hooks/claude-code/Cargo.toml
index a41d157..6c1b44c 100644
--- a/hooks/claude-code/Cargo.toml
+++ b/hooks/claude-code/Cargo.toml
@@ -12,6 +12,9 @@ path = "src/main.rs"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["raw_value"] }
+[target.'cfg(windows)'.dependencies]
+uds_windows = "1.2"
+
[profile.release]
opt-level = "z"
lto = true
diff --git a/hooks/claude-code/src/main.rs b/hooks/claude-code/src/main.rs
index e36c83b..0af1816 100644
--- a/hooks/claude-code/src/main.rs
+++ b/hooks/claude-code/src/main.rs
@@ -25,8 +25,8 @@
use serde::{Deserialize, Serialize};
use std::env;
use std::io::{self, BufRead, Read, Write};
+#[cfg(unix)]
use std::net::Shutdown;
-use std::os::unix::net::UnixStream;
use std::process;
use std::time::{Duration, Instant};
@@ -84,6 +84,7 @@ struct HookSpecificOutput<'a> {
const DEFAULT_TIMEOUT_MS: u64 = 5000;
const TIMEOUT_MIN_MS: u64 = 100;
const TIMEOUT_MAX_MS: u64 = 30000;
+#[cfg(unix)]
const SOCKET_SUFFIX: &str = "/.coding-agents-kit/run/broker.sock";
const INPUT_MAX: usize = 64 * 1024;
const RESPONSE_MAX: u64 = 64 * 1024;
@@ -144,11 +145,24 @@ fn get_socket_path() -> String {
return v;
}
}
- let home = env::var("HOME").unwrap_or_default();
- if home.is_empty() {
- return String::new();
+ #[cfg(unix)]
+ {
+ let home = env::var("HOME").unwrap_or_default();
+ if home.is_empty() {
+ return String::new();
+ }
+ format!("{home}{SOCKET_SUFFIX}")
+ }
+ #[cfg(windows)]
+ {
+ // MSI installs to %LOCALAPPDATA%\coding-agents-kit (not %USERPROFILE%)
+ let base = env::var("LOCALAPPDATA").unwrap_or_default();
+ if base.is_empty() {
+ return String::new();
+ }
+ // Use forward slashes — YAML configs and AF_UNIX paths must match exactly.
+ format!("{}/coding-agents-kit/run/broker.sock", base.replace('\\', "/"))
}
- format!("{home}{SOCKET_SUFFIX}")
}
fn get_timeout() -> Duration {
@@ -176,8 +190,12 @@ fn remaining_timeout(start: Instant, timeout: Duration) -> Result Result {
let start = Instant::now();
- let stream =
- UnixStream::connect(socket_path).map_err(|e| format!("broker unavailable: {e}"))?;
+ #[cfg(unix)]
+ let stream = std::os::unix::net::UnixStream::connect(socket_path)
+ .map_err(|e| format!("broker unavailable: {e}"))?;
+ #[cfg(windows)]
+ let stream = uds_windows::UnixStream::connect(socket_path)
+ .map_err(|e| format!("broker unavailable: {e}"))?;
// Send request.
let remaining = remaining_timeout(start, timeout)?;
@@ -188,7 +206,11 @@ fn communicate(socket_path: &str, request: &[u8], timeout: Duration) -> Result Result<(), Error> {
// Step 4: Configuration.
let socket_path = get_socket_path();
if socket_path.is_empty() {
- return Err(Error::BrokerError(
- "HOME not set, cannot locate broker socket".into(),
- ));
+ #[cfg(unix)]
+ let msg = "HOME not set, cannot locate broker socket";
+ #[cfg(windows)]
+ let msg = "LOCALAPPDATA not set, cannot locate broker socket";
+ return Err(Error::BrokerError(msg.into()));
}
let timeout = get_timeout();
diff --git a/installers/windows/Package.wxs b/installers/windows/Package.wxs
new file mode 100644
index 0000000..6a974cd
--- /dev/null
+++ b/installers/windows/Package.wxs
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installers/windows/README.md b/installers/windows/README.md
new file mode 100644
index 0000000..785474d
--- /dev/null
+++ b/installers/windows/README.md
@@ -0,0 +1,276 @@
+# Windows Build & Installation Guide
+
+## Architecture
+
+### Platform
+
+Builds target the native architecture of the machine. The build scripts auto-detect x86_64 or ARM64 and compile accordingly.
+
+### Pipeline
+
+The Windows port uses the same Falco plugin architecture as Linux/macOS. Alert delivery uses Falco's native `http_output` on all platforms:
+
+```
+Claude Code
+ │ PreToolUse hook
+ ▼
+claude-interceptor.exe ──Unix socket──▶ Plugin broker (in Falco)
+ │
+ ▼
+ Falco rule engine
+ │
+ ▼
+ http_output (curl)
+ │
+ ▼
+ Plugin HTTP server
+ │
+ ▼
+ Verdict resolution
+ │
+ ▼
+ Response to interceptor
+```
+
+| Component | Linux/macOS | Windows |
+|-----------|------------|---------|
+| Interceptor → broker | Unix domain socket | Unix domain socket (via `uds_windows` crate) |
+| Broker | Embedded in plugin | Embedded in plugin |
+| Plugin | .so / .dylib loaded by Falco | .dll loaded by Falco |
+| Alert delivery | Falco `http_output` (curl) | Falco `http_output` (curl, patched in) |
+| Processes | 1 (Falco) | 1 (Falco) |
+
+### http_output on Windows
+
+Falco's upstream build excludes `http_output` on Windows (CMake and preprocessor guards). A single patch (`falco-windows-http-output.patch`) enables it using the same `HAS_HTTP_OUTPUT` pattern as the macOS patch. System curl is provided via vcpkg with SChannel backend (no OpenSSL dependency). The patch also handles SChannel-specific curl limitations:
+
+- **NOPROXY**: Adds `CURLOPT_NOPROXY="*"` to bypass system/environment proxy settings for localhost alert delivery. Tolerates `CURLE_NOT_BUILT_IN` because the SChannel curl backend may omit proxy support.
+- **CA path**: Wraps the CA certificate path/bundle options in a Windows-specific block that tolerates `CURLE_NOT_BUILT_IN`. SChannel uses the Windows Certificate Store automatically and does not support `CURLOPT_CAPATH` / `CURLOPT_CAINFO`.
+- **Plugin path resolution**: Replaces the POSIX-only `path[0] != '/'` check with `std::filesystem::path::has_root_path()` so that Windows absolute paths (`C:/...`) are recognized correctly and not prepended with the default plugins directory.
+
+## Prerequisites
+
+Install the following in order. All commands should be run from PowerShell.
+
+### 1. Visual Studio Build Tools
+
+Install [Visual Studio 2022+](https://visualstudio.microsoft.com/) (Community is free) with:
+- **Desktop development with C++** workload
+ - MSVC v143+ build tools
+ - Windows SDK (10.0 or later)
+ - C++ CMake tools for Windows
+
+Verify:
+```powershell
+# Open "Developer Command Prompt for VS" and run:
+cl
+cmake --version # requires 3.24+
+```
+
+### 2. Git
+
+Install [Git for Windows](https://git-scm.com/download/win).
+
+```powershell
+git --version
+```
+
+### 3. Rust
+
+```powershell
+Invoke-WebRequest -Uri https://win.rustup.rs/x86_64 -OutFile rustup-init.exe
+.\rustup-init.exe -y
+# Restart your shell, then:
+rustc --version # requires 1.80+
+cargo --version
+```
+
+On ARM64 Windows, rustup installs the native `stable-aarch64-pc-windows-msvc` toolchain. Add the x64 target only if you need cross-compilation:
+```powershell
+rustup target add x86_64-pc-windows-msvc
+```
+
+### 4. vcpkg + curl
+
+vcpkg provides the system curl library used by Falco's `http_output`.
+
+```powershell
+git clone https://github.com/microsoft/vcpkg
+.\vcpkg\bootstrap-vcpkg.bat
+
+# Install curl for your architecture:
+.\vcpkg\vcpkg install curl:x64-windows-static # x64 hosts
+.\vcpkg\vcpkg install curl:arm64-windows-static # ARM64 hosts
+
+# REQUIRED: set VCPKG_ROOT so the build scripts find it:
+$env:VCPKG_ROOT = (Resolve-Path .\vcpkg).Path
+# To persist across sessions, add to your PowerShell profile or system env vars.
+```
+
+Note: on recent vcpkg baselines, the `schannel` feature name is not required for this use case. The packaging/build scripts only require `libcurl.lib` to be present under the selected triplet.
+
+### 5. .NET Runtime + WiX Toolset (for MSI packaging)
+
+```powershell
+winget install Microsoft.DotNet.Runtime.9 --accept-package-agreements --accept-source-agreements
+dotnet tool install --global wix
+wix eula accept wix7
+wix --version
+```
+
+WiX v7 requires explicit OSMF EULA acceptance. Without this, packaging fails with `WIX7015`.
+
+## Building
+
+### Quick Build (all components + MSI)
+
+```powershell
+# From the repository root (VCPKG_ROOT must be set):
+$env:VCPKG_ROOT = 'C:\path\to\vcpkg'
+powershell -ExecutionPolicy Bypass -File installers\windows\package.ps1 -Version 0.1.2
+```
+
+This single command:
+1. Compiles all Rust crates for the native architecture (interceptor, plugin DLL, ctl)
+2. Clones and builds Falco 0.43.0 from source with patches (~10 min first time, cached after)
+3. Stages all files and produces the MSI installer
+
+Output: `build/out/coding-agents-kit--windows-.msi`
+
+### Step-by-Step Build
+
+```powershell
+$env:VCPKG_ROOT = 'C:\path\to\vcpkg'
+
+# 1. Build Rust crates (auto-detects native target)
+cd hooks\claude-code && cargo build --release && cd ..\..
+cd plugins\coding-agent-plugin && cargo build --release && cd ..\..
+cd tools\coding-agents-kit-ctl && cargo build --release && cd ..\..
+
+# 2. Build Falco from source (~10 minutes first time, cached after)
+powershell -ExecutionPolicy Bypass -File installers\windows\build-falco.ps1
+
+# 3. Package MSI (uses pre-built components)
+powershell -ExecutionPolicy Bypass -File installers\windows\package.ps1 -SkipRustBuild -SkipFalcoBuild
+```
+
+### Build Options
+
+| Flag | Description |
+|------|-------------|
+| `-Version 0.1.2` | Package version (semantic versioning) |
+| `-Arch x64` or `-Arch arm64` | Target architecture (default: auto-detected from hardware) |
+| `-SkipRustBuild` | Skip Rust compilation (use pre-built binaries) |
+| `-SkipFalcoBuild` | Skip Falco build (use cached build) |
+| `-FalcoExe ` | Use a specific pre-built `falco.exe` |
+
+## Installing
+
+### MSI install + post-install setup
+
+```powershell
+# 1. Install the MSI (shows wizard, or use /quiet for silent)
+msiexec /i build\out\coding-agents-kit-0.1.2-windows-arm64.msi
+
+# 2. Run post-install setup (generates config, registers hook, adds bin/ to PATH)
+powershell -ExecutionPolicy Bypass -File "$env:LOCALAPPDATA\coding-agents-kit\scripts\postinstall.ps1"
+
+# 3. Open a NEW terminal (so the updated PATH takes effect), then:
+coding-agents-kit-ctl start
+Start-Sleep 5
+coding-agents-kit-ctl health
+```
+
+Expected output: `OK: pipeline healthy (synthetic event → allow)`
+
+### Helper script (alternative)
+
+The `Install-CodingAgentsKit.ps1` helper script runs the MSI silently and then runs post-install setup:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File build\out\Install-CodingAgentsKit.ps1
+```
+
+If the product is already installed, it opens the MSI maintenance UI instead of forcing a silent reinstall.
+
+### What the installer does
+
+- Deploys binaries, plugin DLL, rules, and scripts to `%LOCALAPPDATA%\coding-agents-kit\`
+- Post-install generates Falco configuration with resolved Windows paths
+- Registers the Claude Code interceptor hook in `~/.claude/settings.json`
+- Adds `bin/` to the user PATH (persistent, so `coding-agents-kit-ctl` works without full path)
+- Sets up auto-start via Registry Run key
+
+### Uninstalling
+
+**Always use the uninstall helper script** — it runs the cleanup script, removes the Claude Code hook, removes the auto-start registry key, and then removes the MSI-managed files:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File Uninstall-CodingAgentsKit.ps1
+```
+
+> **Warning**: Do NOT uninstall via Apps & Features or `msiexec /x` directly. The MSI alone cannot run cleanup scripts (Windows limitation for per-user installs), so the Claude Code hook and auto-start registry key would be left behind, leaving Claude Code in a fail-closed state.
+
+## Running Tests
+
+Tests are written in Rust and located in the `tests/` directory:
+
+```powershell
+# Run all tests (requires Falco + plugin built)
+cargo test --manifest-path tests/Cargo.toml
+
+# Interceptor unit tests only (mock broker, no Falco needed)
+cargo test --manifest-path tests/Cargo.toml --test interceptor
+
+# End-to-end tests (requires Falco in PATH or FALCO_BIN set)
+cargo test --manifest-path tests/Cargo.toml --test e2e
+```
+
+## Directory Layout (installed)
+
+```
+%LOCALAPPDATA%\coding-agents-kit\
+├── bin\
+│ ├── falco.exe # Falco rule engine
+│ ├── claude-interceptor.exe # Claude Code hook
+│ ├── coding-agents-kit-ctl.exe # Service management CLI
+│ └── coding-agents-kit-launcher.ps1 # Service launcher
+├── share\
+│ └── coding_agent.dll # Falco plugin (source + extract)
+├── config\
+│ ├── falco.yaml # Base Falco config
+│ └── falco.coding_agents_plugin.yaml # Plugin + rules config
+├── rules\
+│ ├── default\coding_agents_rules.yaml # Default security rules
+│ ├── user\ # Custom user rules (preserved on upgrade)
+│ └── seen.yaml # Catch-all rule (required)
+├── scripts\
+│ ├── postinstall.ps1 # Post-install setup
+│ └── uninstall.ps1 # Pre-uninstall cleanup
+├── run\ # Runtime state
+└── log\ # Falco log files
+```
+
+## Known Caveats
+
+- **`VCPKG_ROOT` must be set**: The Falco build script requires `VCPKG_ROOT` pointing to your vcpkg installation. The build fails immediately without it. Set it in your environment or pass it before each build command.
+
+- **`-ExecutionPolicy Bypass`**: Windows default execution policy blocks PowerShell scripts. Always use `powershell -ExecutionPolicy Bypass -File