Skip to content

feat(macos): port the native runtime + sidecar to macOS#111

Open
abcxff wants to merge 3 commits into
mainfrom
macos-support
Open

feat(macos): port the native runtime + sidecar to macOS#111
abcxff wants to merge 3 commits into
mainfrom
macos-support

Conversation

@abcxff

@abcxff abcxff commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Ports the native stack (previously Linux-only) to macOS (aarch64/x86_64). Every macOS path is cfg(target_os)-gated, so the Linux paths are untouched.

Why

cargo build -p agent-os-sidecar failed to compile on macOS at a series of Linux-only syscalls. This makes it build and pass its tests on macOS.

Changes

v8-runtime — per-thread CPU accounting

  • timeout.rs: pthread_getcpuclockid has no macOS equivalent. The CpuBudgetGuard watchdog now reads the execution thread's CPU time via the Mach thread port (pthread_mach_thread_np + thread_info(THREAD_BASIC_INFO)), which is readable cross-thread like the Linux clockid.
  • bridge.rs: RUSAGE_THREAD is Linux-only. process.cpuUsage()/resourceUsage() get per-thread CPU time from thread_info; remaining fields are best-effort from RUSAGE_SELF.

sidecar — host-mount filesystem confinement

  • macos_fs.rs (new): macOS has no openat2(RESOLVE_BENEATH). Resolve-beneath resolution now goes through cap-std (audited userspace walk; uses openat2 on Linux, an fd-relative O_NOFOLLOW walk elsewhere). fcntl(F_GETPATH) replaces readlink(/proc/self/fd/N); /dev/fd/N replaces /proc/self/fd/N.
  • host_dir.rs + filesystem.rs: the two resolve-beneath helpers use cap-std on macOS; O_PATH → read-only anchor, O_TMPFILE → empty. All nine mapped-runtime child ops (mkdir/rmdir/unlink/symlink/rename/readlink/lstat/lutimes) were converted from the Linux /proc/self/fd-join idiom to fd-relative *at calls, since /dev/fd/N cannot have children appended. lstat goes through a platform-neutral HostStat translator; rename uses renameat with a real-path EXDEV fallback. Also fixes a latent dangling-fd bug in the mapped utimes path (the resolved handle is now held across the operation on both platforms).
  • execution.rs: waitid/WNOWAIT is unavailable in nix on macOS; the child-status poll uses waitpid(WNOHANG) there.
  • Latent macOS type-width fixes (mode_t/dev_t/nlink_t).

Also a separate hygiene commit removes four accidentally-committed node_modules symlinks that pointed at /home/nathan/... and broke pnpm install everywhere.

Testing

All secure-exec-sidecar lib tests pass on macOS (aarch64-apple-darwin), including every host-mount escape/confinement test (.. traversal, symlinked-parent, pnpm-symlink layouts, cross-mount EXDEV). Linux is unchanged by construction (cfg-gated) — relying on CI to confirm.

🤖 Generated with Claude Code

abcxff and others added 2 commits June 22, 2026 16:24
These were absolute symlinks pointing at /home/nathan/secure-exec/... — a
local checkout — so they dangle on every other machine and break `pnpm
install` (it cannot create a real node_modules over a stale symlink).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The native stack previously compiled only on Linux. This adds macOS
(aarch64/x86_64) support, gated entirely behind cfg(target_os) so the Linux
paths are untouched.

v8-runtime (per-thread CPU accounting):
- timeout.rs: pthread_getcpuclockid has no macOS equivalent; the CpuBudgetGuard
  watchdog now reads the execution thread's CPU time via the Mach thread port
  (pthread_mach_thread_np + thread_info(THREAD_BASIC_INFO)), readable
  cross-thread like the Linux clockid.
- bridge.rs: RUSAGE_THREAD is Linux-only; process.cpuUsage/resourceUsage get
  per-thread CPU time from thread_info, the rest best-effort from RUSAGE_SELF.

sidecar (host-mount filesystem confinement):
- macos_fs.rs: macOS has no openat2(RESOLVE_BENEATH); resolve-beneath path
  resolution now goes through cap-std (audited userspace walk; openat2 on Linux,
  fd-relative O_NOFOLLOW walk elsewhere). F_GETPATH replaces readlink on
  /proc/self/fd; /dev/fd/N replaces /proc/self/fd/N.
- host_dir.rs + filesystem.rs: open_beneath / mapped-runtime helpers resolve via
  cap-std on macOS; O_PATH -> read-only anchor, O_TMPFILE -> empty. All nine
  mapped-runtime child ops (mkdir/rmdir/unlink/symlink/rename/readlink/lstat/
  lutimes) converted from the Linux /proc/self/fd-join idiom to fd-relative *at
  calls, since /dev/fd/N cannot have children appended. lstat goes through a
  platform-neutral HostStat translator; rename uses renameat with a real-path
  EXDEV fallback. Also fixes a latent dangling-fd bug in the mapped utimes path
  (the resolved handle is now held across the operation on both platforms).
- execution.rs: waitid/WNOWAIT is unavailable in nix on macOS; the child-status
  poll uses waitpid(WNOHANG) there.
- Plus latent macOS type-width fixes (mode_t/dev_t/nlink_t).

All sidecar lib tests pass on macOS, including every host-mount escape test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@railway-app railway-app Bot temporarily deployed to secure-exec / secure-exec-pr-111 June 22, 2026 20:27 Destroyed
@railway-app railway-app Bot temporarily deployed to rivet-frontend / secure-exec-pr-111 June 22, 2026 20:27 Destroyed
@railway-app

railway-app Bot commented Jun 22, 2026

Copy link
Copy Markdown

🚅 Deployed to the secure-exec-pr-111 environment in rivet-frontend

Service Status Web Updated (UTC)
secure-exec 😴 Sleeping (View Logs) Jun 22, 2026 at 9:31 pm

🚅 Deployed to the secure-exec-pr-111 environment in secure-exec

Service Status Web Updated (UTC)
secure-exec 😴 Sleeping (View Logs) Web Jun 22, 2026 at 9:31 pm

NathanFlurry added a commit that referenced this pull request Jun 23, 2026
…ebuilt matrix; drop windows

Target set is dictated by rusty_v8 prebuilts: x86_64/aarch64 linux-gnu +
x86_64/aarch64 apple-darwin. No musl prebuilt (would force a 30+ min V8 source
build). Windows is dropped entirely: removed from the matrix, SIDECAR_PLATFORMS,
the OpenSSL-for-windows step, the platform package, and the dead
`#[cfg(not(unix))]` resource-usage fallback in bridge.rs.

darwin portability:
- v8-runtime: narrowed Linux-only RUSAGE_THREAD / pthread_getcpuclockid to
  target_os=linux/android; macOS uses getrusage(RUSAGE_SELF). NOTE: this is a
  stopgap; PR #111 (macos-support) implements proper Mach-based per-thread CPU
  accounting + cap-std host-mount confinement and should supersede these edits
  on reconcile. The sidecar's openat2/O_PATH/O_TMPFILE/waitid darwin port lives
  in #111 and is NOT in this branch yet (darwin legs fail until integrated).
- openssl 'vendored' (both crates): static OpenSSL, no system dependency.

Also carries: /workspace cwd + /home/agentos home + binding facade + Node-22 CI
pins + pnpm website-filter install + libc stat casts + canonical 0.3.0-rc.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NathanFlurry added a commit that referenced this pull request Jun 23, 2026
…facade + 4-target publish

Combines the macos-support port (#111: openat2->cap-std confinement, Mach
per-thread CPU clock, waitid->waitpid, type-width fixes, core export restore)
with the publish-infra work:
- publish matrix = the 4 rusty_v8-prebuilt targets (x86_64/aarch64 linux-gnu +
  apple-darwin). musl/windows dropped (no v8 prebuilt; windows also a POSIX-kernel
  non-starter) incl. matrix/SIDECAR_PLATFORMS/platform-packages/dead cfg code.
- openssl 'vendored' (static; no system-openssl discovery on any runner).
- Node-22 CI pins; full installs use --filter='!@secure-exec/website' (pnpm
  symlink race); canonical 0.3.0-rc.1 + workspace:* versions; libc stat handled
  by #111's translator. My v8-runtime darwin stopgaps dropped in favor of #111.
- /workspace default cwd + /home/agentos home (user->agentos Alpine re-derive);
  binding rename + facade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NathanFlurry added a commit that referenced this pull request Jun 23, 2026
…facade + 4-target publish

Also fix publish vfs-bump bug (FALLING_OUT.md §4a): bumpCargoVersions only
matched agent-os-*/secure-exec-* path deps, silently skipping the unprefixed
`vfs` workspace crate -> dependent stayed pinned to ^0.3.0-rc.1 while the crate
moved to the preview version, failing cargo resolution in the publish build.
Regex now bumps every internal `path = "...", version = "..."` dep.
NathanFlurry added a commit that referenced this pull request Jun 23, 2026
…facade + 4-target publish

Also fix publish vfs-bump bug (FALLING_OUT.md §4a): bumpCargoVersions only
matched agent-os-*/secure-exec-* path deps, silently skipping the unprefixed
`vfs` workspace crate -> dependent stayed pinned to ^0.3.0-rc.1 while the crate
moved to the preview version, failing cargo resolution in the publish build.
Regex now bumps every internal `path = "...", version = "..."` dep.

Also gate publish-crates to release-only (FALLING_OUT.md §4b): on previews the
crates dry-run compiled/packaged and any error reddened the whole run even though
npm published fine. build-sidecar already compiles every crate per target.
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