sandbox-comm
Trigger host actions from inside a micro-VM sandbox or Docker container by sending HTTP requests to a daemon running on the host.
The guest sends a JSON POST request to http://host.docker.internal:PORT/. The host daemon receives it and executes the requested action (desktop notification, shell command, etc.).
Why HTTP?
Unix domain sockets require both ends to be in the same kernel — they don't work across virtiofs/9p filesystem boundaries used by micro-VM sandboxes. vsock requires explicit hypervisor support. HTTP over TCP works universally: the sandbox proxy routes connections to host.docker.internal to the host's localhost, and the only requirement is opening the port in the sandbox network policy.
Installation
On the host:
pip install docker-sandbox-guest-to-host-communicator
On the guest (sandbox or container), either install the same package or copy
docker_sandbox_guest_to_host_communicator/client.py — it has zero non-stdlib dependencies.
Quick start
-
Open the port in the sandbox network policy (run on the host):
sbx policy allow network -g "localhost:9999"
-
Optionally create a config file (the
SANDBOX_COMM_URLenv var works too):mkdir -p ~/.config/sandbox-comm cat > ~/.config/sandbox-comm/config.toml <<EOF port = 9999 allowed_commands = ["make", "git", "npm"] log_level = "INFO" EOF
-
Start the host daemon:
sandbox-comm-daemon # or: python -m docker_sandbox_guest_to_host_communicator.daemon -
From the guest:
sandbox-comm ping sandbox-comm notify --title "Build" --body "Tests passed" sandbox-comm play sandbox-comm exec make test # To use a different URL either set SANDBOX_COMM_URL or use the --url argument export SANDBOX_COMM_URL=http://host.docker.internal:9999 sandbox-comm --url http://host.docker.internal:9999 ping
Actions
| Action | Args | Description |
|---|---|---|
ping |
— | No-op, confirms the daemon is reachable |
notify |
--title TEXT --body TEXT |
Show a desktop notification via notify-send |
play |
— | Play an audio file on the host (file configured on the daemon) |
exec |
CMD [ARGS...] |
Run a command on the host (must be in allowed_commands) |
notify and exec also accept JSON piped on stdin (Claude Code hook format):
echo '{"title":"Permission needed","message":"Claude needs Bash access"}' \
| sandbox-comm notify
echo '{"session_id":"abc","cwd":"/tmp"}' \
| sandbox-comm exec make test # JSON fields become env vars: SESSION_ID, CWD, ...
play
The play action plays a pre-configured audio file on the host. The guest only triggers it — the file path is set on the daemon side via --sound-file or sound_file in the config:
# Start the daemon on the host with a sound file
sandbox-comm-daemon --sound-file /usr/share/sounds/freedesktop/stereo/complete.oga
# Trigger playback from the guest
sandbox-comm play
The daemon tries these players in order: paplay, aplay, mpg123, ffplay.
Docker usage
docker run -e SANDBOX_COMM_URL=http://host.docker.internal:9999 \
my-image sandbox-comm notify --body "done"
Running as a systemd service
User service (recommended for desktop use)
cp systemd/sandbox-comm.service ~/.config/systemd/user/sandbox-comm.service
# Edit the port if needed (default: 9999)
systemctl --user daemon-reload
systemctl --user enable --now sandbox-comm
systemctl --user status sandbox-comm
# Logs
journalctl --user -u sandbox-comm -f
System service (headless / multi-user)
useradd -r -s /sbin/nologin sandbox-comm
cp systemd/sandbox-comm-system.service /etc/systemd/system/sandbox-comm.service
# Edit /etc/systemd/system/sandbox-comm.service to change the port if needed
systemctl daemon-reload
systemctl enable --now sandbox-comm
systemctl status sandbox-comm
# Logs
journalctl -u sandbox-comm -f
UFW
If UFW is running on the host, allow the port (scope it to the virtual interface for tighter control):
sudo ufw allow 9999/tcp comment "sandbox-comm"
Adding custom actions
Register a new action in daemon.py using the @action decorator:
from docker_sandbox_guest_to_host_communicator.daemon import action
@action("open_url")
async def _open_url(args: dict) -> None:
import webbrowser
webbrowser.open(args["url"])
Config reference
~/.config/sandbox-comm/config.toml (or /etc/sandbox-comm/config.toml):
port = 9999 # daemon listens on this port (0.0.0.0:port)
host = "host.docker.internal" # client connects here
allowed_commands = ["make", "git"] # empty list = allow all exec commands
log_level = "INFO" # DEBUG, INFO, WARNING, ERROR
`sound_file = "/usr/share/sounds/freedesktop/stereo/complete.oga" # played by 'play' action
The SANDBOX_COMM_URL environment variable overrides both host and port:
export SANDBOX_COMM_URL=http://host.docker.internal:9999
The client URL is resolved in this order, with earlier sources taking precedence:
--url URLflag on thesandbox-commcommandSANDBOX_COMM_URLenvironment variablehostandportin the config filehttp://host.docker.internal:9999(built-in default)
The daemon always listens on 0.0.0.0:PORT and accepts --port PORT and --sound-file PATH to override config values.
Metadata
Release files for docker-sandbox-guest-to-host-communicator 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz | 34.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.4 kB
Release files / docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz
| Download URL | docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz |
|---|---|
| Size | 34.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
76f0a100daacacb0b0be04fb3881df2e72301b30818e6e3d5b1cd4c69c472283
|
|
BLAKE2b-256 checksum How to use checksums |
7e71adde2013e8730970a79172d062a07d7683ee45ac01f290600affa322f3f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 16, 2026.
Transparency logRelease files / docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl
| Download URL | docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl |
|---|---|
| Size | 33.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8cb770ad901f3cac6697ccc553a2f9d23830e8fc220bd9ffa8d66eacac49a825
|
|
BLAKE2b-256 checksum How to use checksums |
8ab4a571f9d710abb97fdb70c25aa65838af4134bfa5f1af3e0947db7e6bbbbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 16, 2026.
Transparency logSecurity for docker-sandbox-guest-to-host-communicator 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.