Terminal, iTerm und die gesandboxte Shell
Terminal-Apps sind der eine Fall, in dem ScopeShelf nicht die App sandboxt, sondern die Shell darin. Diese Seite erklärt, warum das so ist, geht jede Einstellung durch und listet die Fehlermeldungen auf, die dir begegnen können, samt Abhilfe.
- Warum ein Terminal nicht in der Sandbox laufen kann
- Was ScopeShelf stattdessen macht
- Terminal-Launcher in der App anlegen
- Der Abschnitt „Shell“, Einstellung für Einstellung
- Die gesandboxte Shell starten
- Was drinnen geht und was nicht
- Dasselbe auf der Kommandozeile
- Was die Sandbox durchsetzt und was nicht
- Fehlermeldungen und was zu tun ist
1. Warum ein Terminal nicht in der Sandbox laufen kann
Terminal.app, iTerm2, Alacritty, kitty und Co. starten deine Shell nicht direkt. Sie gehen über /usr/bin/login, ein kleines Programm mit gesetztem setuid-Bit. macOS weigert sich, setuid-Programme in einer Seatbelt-Sandbox zu starten, egal was im Profil steht. Das kannst du selbst nachprüfen:
$ sandbox-exec -p '(version 1)(allow default)' /usr/bin/login -fpl $USER /bin/echo hi sandbox-exec: execvp() of '/usr/bin/login' failed: Operation not permitted
Ein Terminal, das in der Sandbox startet, öffnet also sein Fenster, versucht eine Session aufzumachen und scheitert. Es sieht aus, als wäre die App kaputt. Daran lässt sich in ScopeShelf nichts einstellen; es ist eine Regel des Betriebssystems.
ScopeShelf erkennt solche Apps beim Auswählen. Auf der Karte steht Terminal — die Sandbox gilt für die Shell, die du hier startest, der Start-Button heißt Shell starten, und es gibt für sie weder „Beenden“ noch „Geschützt neu starten“: das würde dein Terminal mit allen offenen Shells schließen, für einen Start, der nicht funktionieren kann.
2. Was ScopeShelf stattdessen macht
Dein Terminal läuft ganz normal, ohne Sandbox. Darin startet ScopeShelf eine gesandboxte Shell: eine zsh (oder welche Shell du benutzt), eingepackt in das Seatbelt-Profil des Launchers. Alles, was du aus dieser Shell startest, erbt die Sandbox: Claude Code, Codex, npm, Build-Skripte, git. Die Shell sieht deinen Projektordner und die freigegebenen Ordner, und sonst nichts aus deinem Benutzerordner.
Es gelten dieselben drei Zonen wie bei jedem Launcher:
- Schreibbar: der Arbeitsordner der Shell (dein Projekt) und jeder Ordner, den du als schreibbar freigegeben hast.
- Nur lesbar: Referenzordner, deine Shell-Startdateien, Programm-Ordner wie
~/.local/bin. - Unsichtbar: alles andere in deinem Benutzerordner.
~/.ssh,~/.gnupg,~/.aws,~/.config/gcloudund deine Schlüsselbunde bleiben gesperrt, auch wenn ein freigegebener Ordner sie enthält.
Alles außerhalb deines Benutzerordners (/usr, /opt/homebrew, /Applications, /tmp) bleibt erreichbar, wie bei jedem ScopeShelf-Launcher.
3. Terminal-Launcher in der App anlegen
- Neuer Launcher. Unter Schritt 1 klickst du in der Zeile Oder eine installierte App direkt auf Terminal oder iTerm. Den Datei-Dialog brauchst du nicht. Wenn du ihn trotzdem nimmst: Terminal.app liegt unter
/System/Applications/Utilities/; der Dialog zeigt versteckte Ordner, du kannst also dorthin navigieren oder Cmd+Shift+G drücken und den Pfad einfügen. - Name. Der daraus abgeleitete Kurzname ist das, was du später auf der Kommandozeile tippst (
scopeshelf shell mein-name). - Freigaben. Die Vorlagen für Terminal und iTerm geben von sich aus nichts frei. Nach dem Projektordner fragt ScopeShelf beim ersten „Shell starten“ (siehe unten), den musst du hier also nicht eintragen. Trag hier ein, was die Tools drinnen über das Projekt hinaus brauchen. Typisch für Claude Code mit dem nativen Installer:
# Schreibbar ~/.claude # Einstellungen, Sitzungen ~/.local/share/claude # die Programmdateien # Dateien (als einzelne Dateien freigegeben) ~/.claude.json ~/.gitconfig
Referenzmaterial, das die KI nur lesen soll, kommt als nur lesbar dazu. - Abschnitt „Shell“ (Schritt 4). Hier wird die Shell selbst eingestellt. Alle Listen sind auf deinem Mac gemessen, nicht geraten. Die Einzelheiten stehen im nächsten Abschnitt.
- Sichern.
4. Der Abschnitt „Shell“, Einstellung für Einstellung
Du findest ihn unter Launcher bearbeiten → Schritt 4, Shell. Dieselben Einstellungen gibt es noch einmal unter Einstellungen → Shell als Vorgabe für alle Launcher; ein Launcher überschreibt nur, was du in ihm setzt.
| Einstellung | Was sie bewirkt |
|---|---|
| Programm | Welche Shell läuft. Vorgabe ist deine Login-Shell ($SHELL). Die Liste kommt aus /etc/shells. ScopeShelf kennt zsh, bash, fish, nushell, ksh, sh, tcsh, elvish, xonsh und PowerShell (pwsh). Andere Shells starten auch, aber ScopeShelf kennt ihre Startdateien nicht. |
| Login-Shell | Startet die Shell mit -l, sodass sie ihre Profil-Dateien liest (.zprofile), wie ein frisches Terminalfenster. Standardmäßig an. |
| Arbeitsordner | Der Arbeitsbereich der Shell: hier startet sie und hier darf sie schreiben, auch wenn der Ordner nicht in den Freigaben oben steht. Lässt du ihn leer, fragt die App beim ersten „Shell starten“ nach einem Ordner und merkt ihn sich. Auf der Kommandozeile heißt ein leerer Arbeitsordner „starte dort, wo ich den Befehl aufgerufen habe“ (siehe Abschnitt 7). Der Benutzerordner selbst wird als Arbeitsordner abgelehnt, weil eine Sandbox, die deinen ganzen Benutzerordner beschreiben darf, nichts schützt. |
| Startdateien | Die Startdateien der gewählten Shell, die es auf deinem Mac wirklich gibt (~/.zshenv, ~/.zprofile, ~/.zshrc bei zsh). Hak an, welche die Shell lesen darf. Ohne sie bekommst du eine nackte Shell: kein Prompt-Thema, keine Aliase, keine PATH-Ergänzungen. |
| Dateien, die deine Startdateien nachladen | ScopeShelf liest die angehakten Startdateien und listet auf, was sie selbst per source laden, zum Beispiel ~/.cargo/env oder ~/.nvm/nvm.sh. Hak die auch an, sonst meldet die Shell bei jedem Start operation not permitted für diese Zeile. |
| Programm-Ordner | Ordner aus deinem $PATH, die im Benutzerordner liegen: ~/.local/bin, ~/.bun/bin, ~/.cargo/bin, die node-Ordner von nvm und so weiter. Alles im Benutzerordner ist in der Sandbox unsichtbar, solange es nicht freigegeben ist; ein dort installiertes Tool verschwindet also ohne Häkchen. Ein bin-Ordner bringt seine Nachbarn lib, libexec und share mit, sonst findet npm zwar node, aber nicht seine Module. |
| Eigene Konfiguration und History im Sandkasten | Die Shell bekommt ein eigenes Konfigurationsverzeichnis (ZDOTDIR bei zsh, XDG_CONFIG_HOME bei fish und nushell) unter ~/Library/Application Support/ScopeShelf/shells/<kurzname>. ScopeShelf legt dort kleine Brückendateien ab, die deine echten Startdateien laden, und die History der Shell landet ebenfalls dort. Deine eigene ~/.zsh_history fasst eine gesandboxte Sitzung nie an. Shells ohne verschiebbares Konfigurationsverzeichnis (bash, sh) laufen ohne eigene History. |
5. Die gesandboxte Shell starten
Aus der App
Klick auf Shell starten, auf der Launcher-Karte, in der Detailansicht oder im Menü der Menüleiste. Dann passiert zweierlei:
- Dialog „Arbeitsordner“ (nur beim ersten Mal, wenn kein Arbeitsordner eingestellt ist): wähl den Projektordner, in dem die Shell arbeiten soll. ScopeShelf speichert ihn im Launcher. Den Benutzerordner und gesperrte Ordner wie
~/.sshlehnt der Dialog ab. - Dein Terminal kommt nach vorn, mit einem neuen Fenster. ScopeShelf tippt den Shell-Befehl hinein. iTerm und Terminal.app werden direkt gesteuert; jedes andere Terminal bekommt ein Fenster in Terminal.app.
Beim ersten Mal fragt macOS, ob ScopeShelf dein Terminal steuern darf (Systemeinstellungen → Datenschutz & Sicherheit → Automation). Erlaub es, sonst geht nichts auf.
Ein Dock-Starter (in der Detailansicht anzulegen) macht dasselbe, inklusive Ordner-Dialog.
Was der Banner dir sagt
Gesandboxte Shell (tttt, zsh) — beenden mit `exit`, Details mit `scopeshelf scope`. Schreibbar: ~/repos/mein-projekt ~/.claude ~/.local/share/claude Nur lesbar: ~/.zshenv ~/.zshrc ~/.local/bin Arbeitsordner ~/repos/mein-projekt ist fuer diese Shell schreibbar freigegeben (zusaetzlich zum Profil).
- Die erste Zeile nennt Launcher und Shell.
- Schreibbar und Nur lesbar listen deine Freigaben; der Arbeitsordner steht vorn.
- Eine Zeile, die mit Nicht freigegeben, aber von einer Startdatei geladen beginnt, nennt Dateien, die du unter Dateien, die deine Startdateien nachladen anhaken solltest.
- Netzwerk: blockiert oder Netzwerk nur zu: … erscheint, wenn der Launcher das Netz einschränkt.
Nach dem Banner bist du in deinem normalen Prompt, im Arbeitsordner. Starte Claude Code, Codex oder was du willst. exit beendet die gesandboxte Shell und bringt dich zurück in die ungeschützte Shell dieses Fensters.
6. Was drinnen geht und was nicht
scopeshelf scopezeigt das vollständige Profil der Shell, in der du gerade bist: schreibbar, nur lesbar, gesperrt, Netzwerk.cd ~geht, aberlsdort antwortet mit Operation not permitted: der Benutzerordner ist unsichtbar. Genau das soll die Sandbox tun. Nimm die Ordner aus dem Banner.- Schreiben außerhalb der schreibbaren Ordner scheitert mit Operation not permitted. Lesen von
~/.sshoder dem Schlüsselbund scheitert genauso, immer. - Alles, was du startest, erbt die Regeln: ein Build-Skript kann nicht schreiben, wo die Shell es nicht kann.
- Mit Netzwerk blockieren oder einem Domain-Filter am Launcher sind ausgehende Verbindungen aus dieser Shell gesperrt oder auf die gelisteten Domains begrenzt. Ein gesperrter Host wird im Terminal als
scopeshelf: Netzwerk blockiert: hostgemeldet. - Deine History liegt pro Launcher im Konfigurationsverzeichnis des Sandkastens, nicht in
~/.zsh_history.
7. Dasselbe auf der Kommandozeile
Installier den Befehl einmal unter Einstellungen → Kommandozeilen-Werkzeug. Das legt einen Link auf das scopeshelf-Programm aus der App in ~/.local/bin (oder /usr/local/bin, falls dir der Ordner gehört). Danach in jedem Terminalfenster:
scopeshelf shell mein-name # Shell im Profil des Launchers, in diesem Fenster scopeshelf shell mein-name --cd ~/repos/anderes # anderes Projekt, nur für diesen Start scopeshelf shell mein-name --shell /bin/bash # andere Shell, nur für diesen Start scopeshelf shell mein-name --no-network # ausgehendes Netz für diesen Start sperren scopeshelf shell mein-name -e FOO=bar # Umgebungsvariable setzen scopeshelf shell --app /Applications/iTerm.app ~/repos/x ~/docs:ro # ad hoc, ohne gespeicherten Launcher: x schreibbar, docs nur lesbar scopeshelf dry mein-name # Profil anzeigen, ohne etwas zu starten scopeshelf scope # in einer gesandboxten Shell: was gilt hier?
Regeln für den Startordner auf der Kommandozeile:
- Ein im Launcher eingestellter oder mit
--cdübergebener Arbeitsordner ist der Arbeitsbereich und wird für diese Shell schreibbar. - Ohne beides startet die Shell dort, wo du den Befehl aufgerufen hast, aber nur, wenn dieser Ordner im Profil sichtbar ist. Sonst nennt sie dir die freigegebenen Ordner und bricht ab. Wo du zufällig warst, wird nie stillschweigend freigegeben;
--cdist die ausdrückliche Entscheidung. - Dein Benutzerordner und gesperrte Ordner werden als Arbeitsordner abgelehnt.
Für ein Projekt im Team legst du eine Datei .scopeshelf ins Repository und rufst in dem Ordner einfach scopeshelf shell auf:
{
"launcher": "mein-name",
"shell": { "workingDirectory": "." }
}
Die Projekt-Einstellungen aus der Datei liegen über denen des Launchers; ein relativer Arbeitsordner wird gegen das Projekt aufgelöst.
8. Was die Sandbox durchsetzt und was nicht
Jede Aussage unten prüft ein Skript im Repository (Scripts/check-sandbox-escapes.sh), das jeden Ausbruchsweg aus einer gesandboxten Shell heraus wirklich versucht, mit Stand 1.1.2 sind es 21 Versuche; die vollständige Tabelle, was wo durchgesetzt wird, steht in docs/sandbox-enforcement-map.md. Regeln mit dem Vermerk Profil stecken in jeder ScopeShelf-Sandbox und lassen sich von innen nicht abschalten.
Durchgesetzt vom Profil oder vom Kernel
- Dein Benutzerordner ist unsichtbar, bis auf die Freigaben. Gesperrte Ordner (
~/.ssh,~/.gnupg,~/.aws,~/.config/gcloud, Schlüsselbunde) bleiben gesperrt, auch innerhalb eines freigegebenen Ordners. - Kein Lockern von innen. Ein verschachteltes
sandbox-execkann nur weiter einschränken; setuid-Programme wiesudo,loginundcrontabstarten gar nicht. - Keine andere App lässt sich starten.
open -a Terminal skript.commandoderopen https://…wird abgelehnt (Seatbelt-Regellsopen): eine so gestartete App liefe ohne Sandbox. Das heißt auch: Links, die du in einer gesandboxten App anklickst, öffnen keinen Browser. - Keine Signale an Prozesse außerhalb der Sandbox. Ein gesandboxter Prozess kann seine eigenen Kinder beenden, aber nicht den ScopeShelf-Prozess mit dem Domain-Filter und nicht deine anderen Apps.
- Kein Schreiben außerhalb deines Benutzerordners, außer in Temp-Ordner (
/tmp,$TMPDIR) und in Freigaben. Sonst könnte ein Agent/opt/homebrew/bin/gitaustauschen, und dein nächstes ungeschütztesgitführt seinen Code aus. Werkzeuge aus der Sandbox heraus in einen freigegebenen Ordner installieren. - ScopeShelf selbst ist nicht veränderbar: das App-Bundle und der Launcher-Speicher sind schreibgeschützt, auch wenn ein freigegebener Ordner sie enthält.
- Die Sandbox überlebt ScopeShelf. Der Kernel hängt das Profil an den Prozess. Töte den ScopeShelf-Prozess (auch mit SIGKILL von außen), beende die App, lass sie abstürzen: jeder gesandboxte Prozess bleibt gesandboxt, und ein Domain-Filter fällt ganz zu, weil sein Proxy weg ist. Ein Prozess von innen kann dieses Signal gar nicht erst senden (siehe oben).
- Dein ssh-agent ist unerreichbar: der Socket des Agenten ist gesperrt, geladene Schlüssel lassen sich von innen nicht benutzen.
- Kein ungesandboxter Helfer von dir führt für sie Befehle aus. Ein screen- oder tmux-Server, ein Neovim- oder Emacs-Server, Docker oder Kurzbefehle, die schon außerhalb der Sandbox laufen, würden Befehle ohne Sandbox ausführen. Ihre Sockets, FIFOs und Dienste sind gesperrt; die Shell bekommt einen eigenen Temp-Ordner statt des geteilten. tmux und screen, die du in der Sandbox startest, laufen über private Ordner weiter.
- Hardlinks und Umbenennen helfen nicht. Eine geschützte oder nur lesbare Datei lässt sich nicht in einen beschreibbaren Ordner verlinken und dort ändern, und geschützte Ordner lassen sich nicht unter ihren Regeln wegbewegen.
- Bei „Netz aus“ lauscht auch nichts. Ein gesandboxter Prozess kann keinen Port öffnen, zu dem ein Prozess von außen verbinden könnte.
- Eine
.scopeshelf-Datei kann ihre eigene Sandbox nicht weiten. Sie darf nur Ordner im Projekt freigeben; ein Agent, der sie umschreibt, gewinnt für den nächsten Start nichts.
Durchgesetzt von macOS, nicht vom Profil
- Apple Events (
osascript -e 'tell application "Terminal" to do script …') hängen an der Automation-Freigabe in Systemeinstellungen → Datenschutz & Sicherheit, und die gesandboxte Shell zählt als Teil des Terminals, in dem sie läuft. Das Profil kann sie nicht sperren: wir haben gemessen, dass Seatbelt die Regelappleevent-sendannimmt, sie aber nichts bewirkt. Erlaub deinem Terminal nicht, andere Terminals zu steuern, wenn darin KI-Agenten laufen. - launchd lehnt Aufträge aus gesandboxten Prozessen ab (
launchctl submit,bootstrap,loadscheitern). Das ist Verhalten von launchd; ändert es sich, wird der Test rot.
Gar nicht geschützt
- Die Terminal-App selbst. iTerm oder Terminal.app läuft mit deinen vollen Rechten. Gesandboxt ist nur die Shell, die ScopeShelf gestartet hat, und was darin läuft.
- Andere Tabs und Fenster. Ein neuer Tab in iTerm ist eine normale, ungeschützte Shell. Lass KI-Agenten in dem Fenster, das den Banner zeigt.
- Ordner, die du als schreibbar freigegeben hast. Darin kann ein Agent weiterhin löschen und ändern. Dafür ist die Freigabe da; halt die Liste kurz.
- Terminals, die ScopeShelf nicht steuern kann. Alacritty, kitty, Warp und andere bekommen ihre gesandboxte Shell in einem Terminal.app-Fenster, oder du rufst
scopeshelf shellselbst darin auf.
9. Fehlermeldungen und was zu tun ist
| Du siehst | Bedeutung und Abhilfe |
|---|---|
/Users/du/.zshenv:.:1: operation not permitted: /Users/du/.cargo/env | Eine Startdatei lädt eine weitere Datei, die nicht freigegeben ist. Launcher bearbeiten → Schritt 4, Shell → unter Dateien, die deine Startdateien nachladen anhaken. Der Banner nennt die Datei, bevor die Shell sich beschwert. |
… kann kein Arbeitsordner sein: eine Freigabe auf den Benutzerordner (oder darueber) wuerde nichts mehr sperren | Du hast ~ oder einen Ordner darüber gewählt. Wähl einen Projektordner. |
… liegt im gesperrten Pfad … | Der Arbeitsordner liegt unter ~/.ssh, ~/.aws oder einem anderen gesperrten Ordner. Sperren gewinnen immer gegen Freigaben. |
… ist im Profil nicht sichtbar, und es ist kein Arbeitsordner eingestellt | Du hast scopeshelf shell in einem Ordner außerhalb der Freigaben aufgerufen. Entweder per cd in einen freigegebenen Ordner wechseln, oder --cd <ordner> mitgeben, dann wird dieser Ordner der Arbeitsbereich. |
Arbeitsordner … ist im Profil nur lesbar | Der Ordner steht in den Nur-lesen-Freigaben, die Shell kann dort also nicht schreiben. Ändere die Freigabe im Launcher, wenn sie es soll. |
zsh: command not found: scopeshelf | Das Kommandozeilen-Werkzeug ist nicht installiert oder sein Ordner steht nicht in deinem PATH. Einstellungen → Kommandozeilen-Werkzeug zeigt die genaue Zeile zum Ergänzen. In einer gesandboxten Shell ist der Befehl immer da; das betrifft nur deine normale Shell. |
command not found: node (oder claude, bun, cargo …) in der Shell | Das Tool liegt in einem Ordner in deinem Benutzerordner, der nicht freigegeben ist. Launcher bearbeiten → Schritt 4, Shell → unter Programm-Ordner anhaken. |
| Nackter Prompt, keine Aliase, kein Thema | Die Startdateien sind nicht angehakt, oder Login-Shell ist aus. ~/.zshrc und Co. anhaken. |
| „Shell starten“ tut nichts, oder Not authorized to send Apple events | ScopeShelf darf dein Terminal noch nicht steuern. Systemeinstellungen → Datenschutz & Sicherheit → Automation → ScopeShelf → iTerm / Terminal erlauben. |
Shell ist setuid und kann in der Sandbox nicht starten | Das gewählte Shell-Programm ist selbst setuid. Wähl unter „Programm“ ein anderes. |
Fehlt etwas auf dieser Seite? Sag es uns, dann ergänzen wir es.