Skip to main content
On this page

Guest daemon

podbox-guest runs inside the container. It bridges notifications, URI opening, clipboard, and host execution to the host over a Unix socket.

Entry point

The container starts with podbox-guest --entry [<command>...]:

  • Symlinks /run/user/%U/flatpak-info → /.flatpak-info, so portal-aware toolkits route audio/video capture through portals.
  • fork()s: the child re-execs podbox-guest --daemon; the parent execs your command (or a login shell).
  • The daemon idles in the background with a 5-minute timeout.

Daemon startup

  • Creates /run/podbox/bin/ for interceptor symlinks.
  • Compares PODBOX_HOST_VERSION against its own version; warns on drift.
  • Connects to the host socket (3 tries, 500ms apart).
  • Handshakes — sends its capabilities, gets back the accepted subset.
  • Symlinks one interceptor per accepted capability.
  • Prepends /run/podbox/bin to PATH via /etc/profile.d/podbox.sh and /etc/fish/conf.d/podbox.fish.
  • Enters the event loop: poll() on the socket, 0% CPU when idle. With lifecycle.idle_timeout set, it exits once no user processes remain past the timeout.

Event loop

EventAction
ShutdownExit
PingNo-op (keepalive)
CheckIdleScan /proc; reply Busy or IdleTimeout
Disconnect / POLLHUP / POLLERRExit
Idle timeout expiredSend IdleTimeout, exit
EINTRRetry poll()

User processes are tracked via pidfds (Linux 5.3+). 0% CPU when idle.

Socket protocol

Length-prefixed JSON over $XDG_RUNTIME_DIR/podbox/<container>.sock. Full wire format: protocol.md.

bash
→ {"type":"hello","version":"0.1.0","container":"myenv","capabilities":["notify","xdg_open","clipboard","host_exec"]}
← {"type":"hello_ack","accepted":["notify","xdg_open"],"rejected":["clipboard","host_exec"]}

Interceptors

Symlinks in /run/podbox/bin/, one per accepted capability. The binary reads argv[0] to know which interceptor it is. They shadow system binaries via PATH (/etc/profile.d/podbox.sh, /etc/fish/conf.d/podbox.fish).

SymlinkCapabilityWhat it does
notify-sendnotifyForwards args; --action/-A buttons wait for the host's reply
xdg-openxdg_openSends the URI to the host
podbox-clipboardclipboardset reads stdin; get writes the host clipboard to stdout
host-exechost_execRuns the command on the host, relays output, exits with its code

Each opens its own short-lived socket connection, sends one message, waits for the reply, exits.

Host-exec security

Off by default. When enabled, the host validates every command:

CheckRejectedExample error
AllowlistAnything not in the map (guest $PATH ignored)Permission denied: 'ls' is not in the host-exec allowlist
Shell metacharacters;, |, &, $, ` in argshost-exec: failed to execute 'echo $HOME'
Dangerous flags--exec-path, --config, -o, …Security violation: argument "--exec-path=/tmp/x" …
Absolute-path bypass/usr/bin/git when the key is gitPermission denied: '/usr/bin/git' is not in the host-exec allowlist

See config.md for the allowlist shape — and prefer wrapper scripts over general-purpose tools.

podbox — declarative Linux container environments
Licensed under MIT. Open Source by bethropolis.