HTTP bridge for triggering host actions from a container/VM guest
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz.
File metadata
- Download URL: docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz
- Upload date:
- Size: 34.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76f0a100daacacb0b0be04fb3881df2e72301b30818e6e3d5b1cd4c69c472283
|
|
| MD5 |
8db099b24ee9006d95b45869fd4dc842
|
|
| BLAKE2b-256 |
7e71adde2013e8730970a79172d062a07d7683ee45ac01f290600affa322f3f7
|
Provenance
The following attestation bundles were made for docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz:
Publisher:
publish.yml on gene1wood/docker-sandbox-guest-to-host-communicator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz -
Subject digest:
76f0a100daacacb0b0be04fb3881df2e72301b30818e6e3d5b1cd4c69c472283 - Sigstore transparency entry: 1552232934
- Sigstore integration time:
-
Permalink:
gene1wood/docker-sandbox-guest-to-host-communicator@e0de7a1ee576600f0aa6fea81a64e963c15c5350 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/gene1wood
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e0de7a1ee576600f0aa6fea81a64e963c15c5350 -
Trigger Event:
push
-
Statement type:
File details
Details for the file docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl.
File metadata
- Download URL: docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl
- Upload date:
- Size: 33.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cb770ad901f3cac6697ccc553a2f9d23830e8fc220bd9ffa8d66eacac49a825
|
|
| MD5 |
34fcd57a08ba3f30cb1f6078e6b050d4
|
|
| BLAKE2b-256 |
8ab4a571f9d710abb97fdb70c25aa65838af4134bfa5f1af3e0947db7e6bbbbc
|
Provenance
The following attestation bundles were made for docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on gene1wood/docker-sandbox-guest-to-host-communicator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl -
Subject digest:
8cb770ad901f3cac6697ccc553a2f9d23830e8fc220bd9ffa8d66eacac49a825 - Sigstore transparency entry: 1552232950
- Sigstore integration time:
-
Permalink:
gene1wood/docker-sandbox-guest-to-host-communicator@e0de7a1ee576600f0aa6fea81a64e963c15c5350 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/gene1wood
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e0de7a1ee576600f0aa6fea81a64e963c15c5350 -
Trigger Event:
push
-
Statement type: