Skip to content

feat(sandbox): Windows backend via Job Objects - #20

Merged
emretheus merged 3 commits into
masterfrom
feat/windows-sandbox-support
Jul 27, 2026
Merged

feat(sandbox): Windows backend via Job Objects#20
emretheus merged 3 commits into
masterfrom
feat/windows-sandbox-support

Conversation

@emretheus

@emretheus emretheus commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Summary

Adds Windows support to the sandbox without touching the macOS Seatbelt backend. execute_python now dispatches on sys.platform in api.py: darwin → the existing Seatbelt backend (byte-for-byte unchanged), win32 → a new executor_windows.py, anything else → a clear SandboxError. The CLI and desktop-bridge callers are unchanged.

Windows isolation (Job Objects, stdlib ctypes — no pywin32)

Resource isolation matching the macOS rlimit tier:

  • Process-memory cap (ProcessMemoryLimit)
  • ActiveProcessLimit=1 → fork-bomb protection
  • Per-process CPU-time limit
  • KILL_ON_JOB_CLOSE + TerminateJobObject on timeout → whole-tree wall-clock kill
  • The interpreter is launched CREATE_SUSPENDED, assigned to the job, then resumed, so limits apply before any code runs.

Honest scope

The Windows backend is a deliberately weaker tier than Seatbelt on filesystem/network isolation, documented in the README parity table; AppContainer is left for a future version. runtime_python() now resolves Scripts/python.exe on Windows.

Commits

  1. fix(sessions) — Repairs a regression introduced by the PR feat(sessions): add delete command and enrich sessions list #19 merge: the upper half of sessions.py (imports, Session, save_session, load_session) had been dropped, breaking test collection repo-wide with a NameError. Restored from the last good revision. delete_session also had a contradictory contract (returned Path and raised on a missing file, while the bridge handler and newer tests expect a bool); aligned the implementation to return bool (True if removed, False if absent) and dropped the stale test that asserted the raise behavior.
  2. feat(sandbox) — The Windows support described above.
  3. fix(bridge)list_sessions() now returns SessionSummary dataclasses, but the bridge sessions.list_ handler still unpacked them as (name, saved_at) tuples (a pre-existing master mypy failure). Reads the dataclass fields directly and drops the redundant per-session load_session call. Also applies ruff format to executor_windows.py so the CI format check passes.

Tests / CI gates (all green locally)

  • ruff check . — clean
  • ruff format --check . — clean
  • mypy packages shared0 errors (the pre-existing bridge failure is now fixed)
  • pytest --cov --cov-fail-under=75210 passed, coverage 83% (integration tests skip without Ollama, as in CI)
  • ✅ macOS executor.py and the Seatbelt profile are byte-for-byte unchanged
  • test_executor_windows.py is Windows-only (skips on macOS/Linux CI); test_dispatch.py covers the platform routing on every OS.

⚠️ Known / out of scope

  • The Windows code has not yet been run on a real Windows machine. The Job Object logic is statically correct but needs runtime validation — packages/sandbox/WINDOWS_TESTING.md has a step-by-step manual checklist.
  • A Windows CI runner job and the desktop sidecar .exe packaging are separate follow-ups.

emretheus added 3 commits June 8, 2026 13:46
The PR #19 merge dropped the upper half of sessions.py (imports, Session,
save_session, load_session), which broke test collection repo-wide with a
NameError. Restore it from the last good revision.

delete_session also had a contradictory contract: the implementation returned
Path and raised on a missing file, while the bridge handler and the newer tests
expect a bool (True if removed, False if absent). Align the implementation with
that contract and drop the stale test that asserted the raise behavior.
execute_python now dispatches on sys.platform in api.py: darwin keeps the
existing Seatbelt backend untouched, win32 routes to a new executor_windows.py,
anything else raises a clear SandboxError. Callers (CLI, desktop bridge) are
unchanged.

The Windows backend uses Job Objects (stdlib ctypes, no pywin32) for resource
isolation matching the macOS rlimit tier: process-memory cap, ActiveProcessLimit
fork-bomb protection, per-process CPU-time limit, and KILL_ON_JOB_CLOSE +
TerminateJobObject for whole-tree wall-clock kill. The interpreter is launched
CREATE_SUSPENDED, assigned to the job, then resumed so limits apply first.

This is a deliberately weaker tier than Seatbelt on filesystem/network isolation
(documented honestly in README's parity table); AppContainer is left for a
future version. runtime_python() now resolves Scripts/python.exe on Windows.

Tests: test_executor_windows.py mirrors the macOS cases (Windows-only, skips
elsewhere); test_dispatch.py covers the platform routing on every OS. macOS
executor.py and the Seatbelt profile are byte-for-byte unchanged. See
WINDOWS_TESTING.md for the manual checklist to validate on a real Windows host.
…ows backend

list_sessions() returns SessionSummary dataclasses, but the bridge handler
still unpacked them as (name, saved_at) tuples — a pre-existing master mypy
failure. Read the dataclass fields directly (and drop the now-redundant
per-session load_session call). Also apply ruff format to executor_windows.py
so the CI format check passes.
@emretheus
emretheus merged commit 3b5aff9 into master Jul 27, 2026
6 checks passed
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.

1 participant