From da3055d37d59fd59bd9c6075004fb445a9b6e419 Mon Sep 17 00:00:00 2001 From: Josh Nichols Date: Fri, 16 Oct 2026 16:47:40 -0400 Subject: [PATCH 1/3] safe: enable launch-services so open works under safehouse open failed with kLSUnknownErr (-10810) via RunningBoard: the sandbox blocked the Launch Services mach lookups. safehouse ships an opt-in feature for it; launched apps run unsandboxed, noted in ADR 0060. Co-Authored-By: Claude Sonnet 5.5 --- bin/safe | 2 +- doc/adr/0060-agent-safehouse-wrapper.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/safe b/bin/safe index 65ac8a3..007afdb 100755 --- a/bin/safe +++ b/bin/safe @@ -38,7 +38,7 @@ if [ -f "$profiles/role-$role.sb" ]; then fi exec safehouse \ - --enable=clipboard \ + --enable=clipboard,launch-services \ --env-pass=SSH_AUTH_SOCK,GIT_CONFIG_GLOBAL \ --append-profile="$profiles/common.sb" \ ${role_profile:+"$role_profile"} \ diff --git a/doc/adr/0060-agent-safehouse-wrapper.md b/doc/adr/0060-agent-safehouse-wrapper.md index 4449a14..ca83616 100644 --- a/doc/adr/0060-agent-safehouse-wrapper.md +++ b/doc/adr/0060-agent-safehouse-wrapper.md @@ -38,7 +38,7 @@ it (`safe pi`). It: - appends `config/agent-safehouse/common.sb`, then `role-$DOTPICKLES_ROLE.sb` when one exists. Role rules come last so their denies win. -- enables `clipboard`. +- enables `clipboard` and `launch-services`. - starts a **dedicated ssh-agent** at `~/.ssh/agent/safehouse.sock`, fills it with `bin/load-agent-ssh-keys` (Keychain passphrase per [ADR 0030](0030-ssh-keychain-loading-at-login.md), skipping keys Automic Vault @@ -77,4 +77,5 @@ The split between files follows role, not convenience: dedicated agent has nothing to sign with. Signing inside the sandbox on an AV machine would need `av-gpg` reachable from the sandbox. Not done. - `--enable=clipboard` lets sandboxed code read the clipboard. +- `--enable=launch-services` lets `open` work (MCP OAuth sign-in, plannotator), but anything it launches runs outside the sandbox with full privileges. Accepted tradeoff: this is a sandbox escape hatch (e.g. `open -a Terminal`), and the escape guard in `role-work.sb` does not cover it. - No Linux story; safehouse is Seatbelt-only. From fa021bc8a26116d8fc3f7c31636b9af67afaf1c3 Mon Sep 17 00:00:00 2001 From: Josh Nichols Date: Fri, 16 Oct 2026 16:48:57 -0400 Subject: [PATCH 2/3] safe: enable keychain so gh can read its token gh stores its token in the Keychain; without the keychain feature it reports the token as invalid under safehouse. Tradeoff noted in ADR 0060. Co-Authored-By: Claude Sonnet 5.5 --- bin/safe | 2 +- doc/adr/0060-agent-safehouse-wrapper.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/safe b/bin/safe index 007afdb..4260b22 100755 --- a/bin/safe +++ b/bin/safe @@ -38,7 +38,7 @@ if [ -f "$profiles/role-$role.sb" ]; then fi exec safehouse \ - --enable=clipboard,launch-services \ + --enable=clipboard,keychain,launch-services \ --env-pass=SSH_AUTH_SOCK,GIT_CONFIG_GLOBAL \ --append-profile="$profiles/common.sb" \ ${role_profile:+"$role_profile"} \ diff --git a/doc/adr/0060-agent-safehouse-wrapper.md b/doc/adr/0060-agent-safehouse-wrapper.md index ca83616..40c79e2 100644 --- a/doc/adr/0060-agent-safehouse-wrapper.md +++ b/doc/adr/0060-agent-safehouse-wrapper.md @@ -38,7 +38,7 @@ it (`safe pi`). It: - appends `config/agent-safehouse/common.sb`, then `role-$DOTPICKLES_ROLE.sb` when one exists. Role rules come last so their denies win. -- enables `clipboard` and `launch-services`. +- enables `clipboard`, `keychain`, and `launch-services`. - starts a **dedicated ssh-agent** at `~/.ssh/agent/safehouse.sock`, fills it with `bin/load-agent-ssh-keys` (Keychain passphrase per [ADR 0030](0030-ssh-keychain-loading-at-login.md), skipping keys Automic Vault @@ -77,5 +77,6 @@ The split between files follows role, not convenience: dedicated agent has nothing to sign with. Signing inside the sandbox on an AV machine would need `av-gpg` reachable from the sandbox. Not done. - `--enable=clipboard` lets sandboxed code read the clipboard. +- `--enable=keychain` lets `gh` read its token from the Keychain (`gh auth status` reports it invalid without this), but sandboxed code can read every Keychain item the user owns. Accepted tradeoff; a narrower option is a dedicated token passed via `--env-pass`. - `--enable=launch-services` lets `open` work (MCP OAuth sign-in, plannotator), but anything it launches runs outside the sandbox with full privileges. Accepted tradeoff: this is a sandbox escape hatch (e.g. `open -a Terminal`), and the escape guard in `role-work.sb` does not cover it. - No Linux story; safehouse is Seatbelt-only. From 01a490dfa79d300ec4546879b74fdfdfb3f27529 Mon Sep 17 00:00:00 2001 From: Josh Nichols Date: Fri, 16 Oct 2026 16:52:20 -0400 Subject: [PATCH 3/3] safe: document what each enabled feature unlocks and exposes Co-Authored-By: Claude Sonnet 5.5 --- bin/safe | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bin/safe b/bin/safe index 4260b22..bf77a76 100755 --- a/bin/safe +++ b/bin/safe @@ -37,6 +37,20 @@ if [ -f "$profiles/role-$role.sb" ]; then role_profile="--append-profile=$profiles/role-$role.sb" fi +# Optional safehouse features, each a deliberate hole in the sandbox: +# clipboard pbcopy/pbpaste work. Sandboxed code can also read the +# clipboard. +# keychain Keychain reads. Needed because gh keeps its token there; +# without it `gh auth status` says the token is invalid. +# Sandboxed code can read EVERY Keychain item you own. A +# narrower option is a dedicated token via --env-pass. +# launch-services `open` works (MCP OAuth sign-in, plannotator opening a +# browser). Without it: kLSUnknownErr -10810 from +# RunningBoard. Anything it launches runs OUTSIDE the +# sandbox with your full privileges, so it is an escape +# hatch (`open -a Terminal ...`) the role escape guards don't +# cover. +# Rationale and tradeoffs: doc/adr/0060-agent-safehouse-wrapper.md. exec safehouse \ --enable=clipboard,keychain,launch-services \ --env-pass=SSH_AUTH_SOCK,GIT_CONFIG_GLOBAL \