commit ecbc43f0d9708d2ddb4708e27b3f24cdb3902da7 from: mtmn date: Thu Aug 27 23:13:00 2026 UTC docs commit - 77abbd7c60f1b86567878155667ad2806ff24d1c commit + ecbc43f0d9708d2ddb4708e27b3f24cdb3902da7 blob - e5bb910dee32022ed917f75383a5771c5e2704ec blob + 68a69f134860e2eb21be7320c03677c6055f71b9 --- Makefile +++ Makefile @@ -1,80 +1,54 @@ -LIBDIR = src +PREFIX ?= /usr/local +BINDIR ?= $(PREFIX)/bin +LIBEXECDIR ?= $(PREFIX)/libexec/pisol +MANDIR ?= $(PREFIX)/share/man +DESTDIR ?= + +INSTALL ?= install +RM ?= rm -f SCHEME ?= scheme - -.PHONY: build tui test copy-config clean run fmt install uninstall help - +.PHONY: build tui test copy-config clean run install uninstall help default: help - build: build/runtime/pisol.so - build/runtime/pisol.so: bin/pisol.ss src/pisol/core.sls src/pisol/tui.sls tools/build.ss @mkdir -p build/runtime/lib/pisol - $(SCHEME) --libdirs $(LIBDIR) --compile-imported-libraries --program tools/build.ss + $(SCHEME) --libdirs src --compile-imported-libraries --program tools/build.ss cp src/pisol/core.so src/pisol/tui.so build/runtime/lib/pisol/ - tui: build - $(SCHEME) --libdirs $(LIBDIR) --program bin/pisol.ss - + $(SCHEME) --libdirs src --program bin/pisol.ss test: build - shellcheck bin/pisol test/integration.sh tools/fmt.sh - $(SCHEME) --libdirs $(LIBDIR) --program test/test.ss + shellcheck bin/pisol test/integration.sh + $(SCHEME) --libdirs src --program test/test.ss sh test/integration.sh - copy-config: - @config_home="$${XDG_CONFIG_HOME:-$$HOME/.config}"; \ - target="$$config_home/pisol/init"; \ - s=; if [ -e "$$config_home" ] && [ ! -w "$$config_home" ]; then s=sudo; fi; \ - if [ -f "$$target" ]; then \ - printf 'pisol: %s already exists\n' "$$target" >&2; \ - exit 1; \ - fi; \ - $$s install -d "$$config_home/pisol" && \ - $$s install -m 644 config/pisol/init "$$target" - + @target="$${XDG_CONFIG_HOME:-$$HOME/.config}/pisol/init"; \ + [ ! -e "$$target" ] || { printf 'pisol: %s already exists\n' "$$target" >&2; exit 1; }; \ + $(INSTALL) -d "$${target%/*}" && $(INSTALL) -m 644 config/pisol/init "$$target" clean: rm -rf build src/pisol/core.so src/pisol/tui.so - run: install - bash /usr/local/bin/pisol - -fmt: - sh tools/fmt.sh src/pisol/core.sls src/pisol/tui.sls bin/pisol.ss test/test.ss tools/build.ss - + $(DESTDIR)$(BINDIR)/pisol install: build - @prefix="$${PREFIX:-/usr/local}"; \ - bindir="$${BINDIR:-$$prefix/bin}"; \ - libexecdir="$${LIBEXECDIR:-$$prefix/libexec/pisol}"; \ - mandir="$${MANDIR:-$$prefix/share/man}"; \ - destdir="$${DESTDIR:-}"; \ - can_create() { d="$$1"; while [ -n "$$d" ] && [ ! -e "$$d" ]; do d=$$(dirname "$$d"); done; [ -d "$$d" ] && [ -w "$$d" ]; }; \ - s=; if ! can_create "$$destdir$$bindir"; then s=sudo; fi; \ - $$s install -d "$$destdir$$bindir" "$$destdir$$libexecdir/lib/pisol" "$$destdir$$mandir/man1" && \ - $$s install -m 755 bin/pisol "$$destdir$$bindir/pisol" && \ - $$s install -m 755 build/runtime/pisol.so "$$destdir$$libexecdir/pisol.so" && \ - $$s install -m 644 build/runtime/lib/pisol/core.so build/runtime/lib/pisol/tui.so "$$destdir$$libexecdir/lib/pisol/" && \ - $$s install -m 644 doc/pisol.1 "$$destdir$$mandir/man1/pisol.1" - + $(INSTALL) -d $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBEXECDIR)/lib/pisol $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -m 755 bin/pisol $(DESTDIR)$(BINDIR)/pisol + $(INSTALL) -m 755 build/runtime/pisol.so $(DESTDIR)$(LIBEXECDIR)/pisol.so + $(INSTALL) -m 644 build/runtime/lib/pisol/core.so build/runtime/lib/pisol/tui.so $(DESTDIR)$(LIBEXECDIR)/lib/pisol/ + $(INSTALL) -m 644 doc/pisol.1 $(DESTDIR)$(MANDIR)/man1/pisol.1 uninstall: - @prefix="$${PREFIX:-/usr/local}"; \ - bindir="$${BINDIR:-$$prefix/bin}"; \ - libexecdir="$${LIBEXECDIR:-$$prefix/libexec/pisol}"; \ - mandir="$${MANDIR:-$$prefix/share/man}"; \ - destdir="$${DESTDIR:-}"; \ - can_create() { d="$$1"; while [ -n "$$d" ] && [ ! -e "$$d" ]; do d=$$(dirname "$$d"); done; [ -d "$$d" ] && [ -w "$$d" ]; }; \ - s=; if ! can_create "$$destdir$$bindir"; then s=sudo; fi; \ - $$s rm -f "$$destdir$$bindir/pisol" "$$destdir$$libexecdir/pisol.so" "$$destdir$$libexecdir/lib/pisol/core.so" "$$destdir$$libexecdir/lib/pisol/tui.so" "$$destdir$$mandir/man1/pisol.1" - + $(RM) $(DESTDIR)$(BINDIR)/pisol $(DESTDIR)$(LIBEXECDIR)/pisol.so + $(RM) $(DESTDIR)$(LIBEXECDIR)/lib/pisol/core.so $(DESTDIR)$(LIBEXECDIR)/lib/pisol/tui.so + $(RM) $(DESTDIR)$(MANDIR)/man1/pisol.1 help: @printf '%s\n' \ - 'make build Compile the Chez Scheme program' \ - 'make tui Run the configuration TUI' \ - 'make test Run specification, static, Chez, shell, and TUI tests' \ - 'make run Install and run pisol' \ - 'make fmt Format Scheme sources with emacs scheme-mode' \ + 'make build Compile pisol' \ + 'make tui Run pisol program' \ + 'make test Run pisol tests' \ 'make copy-config Copy the default global init to ~/.config/pisol/init' \ 'make clean Remove generated artifacts' \ 'make install Install pisol and its manual' \ 'make uninstall Remove installed pisol files' \ 'make help Show this help message' \ '' \ - 'Variables: SCHEME=scheme' + 'Variables: PREFIX=/usr/local DESTDIR= BINDIR=PREFIX/bin' \ + ' LIBEXECDIR=PREFIX/libexec/pisol MANDIR=PREFIX/share/man' \ + ' SCHEME=scheme INSTALL=install' blob - ea1eb8902b18e4e1e8e1880e09dcef553b61fd1e blob + e9e5036cf087d5343b0cbd5a121280f5c1b858f3 --- README.md +++ README.md @@ -1,19 +1,20 @@ # pisol -Working-directory Landrun isolation and configuration, with a terminal interface -written in Chez Scheme. +Run commands in a Landrun sandbox and manage each project's isolation settings. +The terminal interface is written in Chez Scheme. -`pisol` combines command isolation with an editor for the local trusted -`.isolate` shell fragment. Outbound TCP is denied by default. The editor manages -extra writable/executable directories and the destination TCP ports commands -may connect to. +`pisol` uses a trusted local `.isolate` Bash file. It denies outbound TCP by +default. Use the editor to add writable and executable directories, and allow +connections to specific TCP ports. ## Run -You need Bash, [Landrun](https://github.com/Zouuup/landrun), Chez Scheme 10.4 or -newer, and Make. Install `fzf` for fuzzy directory selection. Without it, the -editor uses direct path entry. Install `emacs` to run `make fmt`. +You need Bash, [Landrun](https://github.com/Zouuup/landrun), Chez Scheme 10.4 +or later, and Make. +Install `fzf` to search for directories. Without it, enter a path directly. +Install `emacs` to run `make fmt`. + ```sh make tui make test @@ -31,14 +32,14 @@ Run a command in that directory sandbox: pisol your-command --with arguments ``` -Use `pisol -- COMMAND` when a command name conflicts with a pisol option. The -command receives the caller environment, `PROMPT_ENV_INDICATOR=isolated`, and -`ISOLATE_ENV` set to the canonical working directory. Calling `pisol COMMAND` -inside an existing pisol sandbox prints a warning and directly executes the -command rather than creating a nested Landlock domain. +Use `pisol -- COMMAND` when a command name conflicts with a pisol option. -## TUI commands +The command receives your environment, `PROMPT_ENV_INDICATOR=isolated` and +`ISOLATE_ENV`, set to the resolved working directory. If you run `pisol COMMAND` +inside an existing pisol sandbox, it warns you and runs the command directly. +## Terminal interface commands + ```text a fuzzy-find or enter a writable directory path r choose and remove a writable directory @@ -51,20 +52,21 @@ sx save and exit immediately (xs also works) x exit (discards any unsaved changes) ``` -After selecting `a`, enter a base path such as `/home/miro`, or press Enter to -open the picker in your home directory. `fzf` then lists that path and its -immediate child directories only. Press Enter to drill down into a directory, -Alt+Enter or Ctrl-S to select it, or Escape to use the typed path directly. -The baseline writable tree cannot be added redundantly. +After you select `a`, enter a base path such as `/home/miro`. Press Enter to +start in your home directory. `fzf` shows the path and its immediate child +directories. Press Enter to open a directory. Press Alt+Enter or Ctrl-S to +select it. Press Escape to use the path you entered. +You cannot add the baseline writable tree again. + Set `NO_COLOR=1` to disable colour. `COLUMNS` and `LINES` are respected by the terminal environment. Set `ISOLATE_EXTRA_CONFIG` to use a config path other than `CURRENT_DIRECTORY/.isolate`. ## Configuration and trust -`.isolate` is sourced as trusted Bash by the launcher. A generated file looks -like this: +`.isolate` is trusted Bash code. The launcher sources it. A generated file +looks like this: ```bash args+=( @@ -73,26 +75,29 @@ args+=( ) ``` -A global init file at `~/.config/pisol/init` is sourced before the local -`.isolate` when it exists. Put shared grants there; the project-specific file -builds on top of it. Override the path with `PISOL_INIT_CONFIG`. +A global init file at `~/.config/pisol/init` runs before the local `.isolate` +file. Put shared grants there. Project settings build on those grants. Set +`PISOL_INIT_CONFIG` to use a different global init file. -A permissive example init based on the legacy `isolate` script is included at -`config/pisol/init`. Copy it to `~/.config/pisol/init` and edit to taste; -remove `--unrestricted-network` to keep outbound TCP denied by default. +The source tree includes a permissive example init at `config/pisol/init`. +Copy it to `~/.config/pisol/init` and change it for your needs. Remove +`--unrestricted-network` to deny outbound TCP by default. -The TUI also evaluates existing custom `.isolate` logic. It extracts the -resulting `--rwx` and `--connect-tcp` entries and preserves other resulting -Landrun arguments. Saving custom logic requires confirmation because the source -is replaced by a canonical argument array; comments and dynamic logic are not -retained. Default grants from the global init are shown but not edited in the -TUI; add or remove project-specific entries instead. +The editor evaluates existing custom `.isolate` code. It manages the resulting +`--rwx` and `--connect-tcp` values and keeps the other Landrun arguments. -The minimal default policy grants the working tree read/write/execute access, -temporary storage write access, read access to system configuration/data, and -read/execute access to standard runtime and absolute `PATH` directories. It -also grants the standard null, zero, random, and controlling-terminal devices, -but does not grant unrestricted networking. A Wayland socket is added when +Saving custom code needs confirmation. It replaces the code with a static +argument array, so it removes comments and dynamic logic. The editor shows +global grants but does not let you change them. Add or remove project grants +instead. + +The default policy gives the working tree read, write and execute access. It +also gives write access to temporary storage. System configuration and shared +data are read-only. Standard runtime and absolute `PATH` directories are +readable and executable. + +The policy allows the standard null, zero, random and terminal devices. It does +not allow unrestricted networking. It adds a Wayland socket when `WAYLAND_DISPLAY` and `XDG_RUNTIME_DIR` are set. ## Build and install @@ -105,9 +110,10 @@ make install make copy-config ``` -`make fmt` reindents the Scheme sources in place with `emacs` scheme-mode. Run -it after editing Scheme files; it is a no-op on already-formatted files. +`make fmt` formats Scheme files in place with `emacs` scheme mode. Run it after +you edit Scheme files. It does not change files that are already formatted. -`make install` defaults to `/usr/local` and asks for `sudo` only when needed. +`make install` defaults to `/usr/local`; use `sudo make install` only when your +chosen destination requires it. Set `PREFIX`, `DESTDIR`, `BINDIR`, `LIBEXECDIR`, `MANDIR`, or `SCHEME` to override the defaults. See `pisol(1)` after installation. blob - 02b2e3b98611be14fa0f53f80d5647f81aa32e41 blob + 1c6a2cb10b3921fbcddd56b0df72f9f5daa4cf4d --- config/pisol/init +++ config/pisol/init @@ -1,7 +1,6 @@ #!/usr/bin/env bash -# Default global init for pisol. -# Sourced before the project-specific .isolate file. Put shared grants here -# and keep per-project additions in `.isolate`. +# Default global init for pisol that is sourced before +# the project-specific .isolate file. args+=(--unrestricted-network) blob - 89789314f9085e56e28497232eb4c24a28d07602 blob + ce3f2aec5603520c0caa998b0008cc9b32a17d3d --- doc/pisol.1 +++ doc/pisol.1 @@ -13,60 +13,60 @@ pisol \- configure and run working-directory Landrun i .RI [ ARG ...] .SH DESCRIPTION .B pisol -opens a Chez Scheme terminal editor for the current directory isolation -configuration. With a command, it replaces itself with +opens a Chez Scheme terminal editor for the current directory's isolation +settings. With a command, it replaces itself with .BR landrun (1) -and executes the command in a working-directory sandbox. Use +and runs the command in a sandbox for the current directory. Use .B -- before a command whose name conflicts with a pisol option. .PP -The canonical current directory defines the writable tree. The configuration +The resolved current directory defines the writable tree. The configuration path is .I CURRENT_DIRECTORY/.isolate unless .B ISOLATE_EXTRA_CONFIG is set. .SH ISOLATION POLICY -The working tree is readable, writable, and executable. Temporary storage is +The working tree is readable, writable and executable. Temporary storage is writable. Standard runtime and absolute PATH directories are readable and -executable, while system configuration and shared data are read-only. The -standard null, zero, random, and controlling-terminal devices are writable. +executable. System configuration and shared data are read-only. The standard +null, zero, random and controlling-terminal devices are writable. .PP Outbound TCP is denied by default. Only destination ports configured with .B --connect-tcp are granted. Scoped IPC restrictions are relaxed, and the current Wayland socket is granted when its environment variables are available. .PP -The caller environment is forwarded. The command additionally receives +The command receives the caller environment. It also receives .B PROMPT_ENV_INDICATOR=isolated and .B ISOLATE_ENV -set to the canonical working directory. If +set to the resolved working directory. If .B ISOLATE_ENV -is already non-empty, pisol warns and directly executes the command without -starting a nested Landlock domain. -.SH TUI COMMANDS +is already non-empty, pisol warns and runs the command directly. It does not +start a nested Landlock domain. +.SH TERMINAL INTERFACE COMMANDS .TP .B a -Prompt for a base directory path and add it as an additional -read/write/execute grant. With no input, the picker opens in the user's home -directory. When +Prompt for a base directory path and add a read/write/execute grant. With no +input, the picker opens in the user's home directory. When .BR fzf (1) -is available, the picker lists only the typed path and its immediate child -directories. Press Enter to drill down into a directory, Alt+Enter or -Ctrl-S to select it, or Escape to use the typed path directly. +is available, the picker lists the typed path and its immediate child +directories. Press Enter to open a directory. Press Alt+Enter or Ctrl-S to +select it. Press Escape to use the typed path directly. .TP .B r Prompt for the number of a writable directory and remove it. .TP .BI p " PORT" -Allow outbound TCP connections to PORT. Valid ports are 1 through 65535. +Allow outbound TCP connections to PORT. Valid ports are 1 to 65535. .TP .B d Prompt for a configured outbound TCP port and remove it. .TP .B c -Show the resolved global init file and the local configuration file, including their raw contents. A missing file is labelled absent. +Show the global init file and local configuration file, including their raw +contents. A missing file is labelled absent. .TP .B s Save atomically with mode 0600. The combined commands @@ -79,7 +79,7 @@ save and then exit immediately. Show help or exit. Exit discards any unsaved changes. .SH CONFIGURATION .I .isolate -is trusted Bash code sourced by the launcher. It modifies the Bash array +is trusted Bash code. The launcher sources it. It changes the Bash array .IR args , normally with an .B args+=(...) @@ -87,10 +87,10 @@ fragment. .PP If .I ~/.config/pisol/init -exists, it is sourced before the local +exists, the launcher sources it before the local .I .isolate . -Put shared grants in the global init file; per-project configuration builds on -top of it. The path can be overridden with +Put shared grants in the global init file. Per-project configuration builds on +it. Set .BR PISOL_INIT_CONFIG . .PP A permissive example global init based on the legacy @@ -99,18 +99,18 @@ script is included in the source tree at .IR config/pisol/init . Copy it to .I ~/.config/pisol/init -and edit to customize. Remove +and change it for your needs. Remove .B --unrestricted-network from the copy to deny outbound TCP by default. .PP -The editor evaluates an existing file, manages resulting +The editor evaluates an existing file. It manages the resulting .B --rwx and .B --connect-tcp -pairs, and preserves other resulting arguments. Saving custom shell logic -requires confirmation and replaces it with a canonical static array; source -comments and dynamic logic are not preserved. Default grants from the global -init file are shown but not editable in the TUI. +pairs and keeps other resulting arguments. Saving custom shell code requires +confirmation. It replaces the code with a static array, so it removes source +comments and dynamic logic. The terminal interface shows global init grants. +It does not let you change them. .SH ENVIRONMENT .TP .B ISOLATE_EXTRA_CONFIG @@ -132,7 +132,7 @@ Supply runtime paths and grants. .BR WAYLAND_DISPLAY , " XDG_RUNTIME_DIR" Identify an optional Wayland Unix socket grant. .SH EXIT STATUS -The TUI returns zero on normal exit and non-zero on load or save errors. In +The terminal interface returns zero on normal exit and non-zero on load or save errors. In command mode, pisol propagates the executed command status. Usage errors return 2 and a missing Landrun executable returns 127. .SH SEE ALSO