Skip to main content

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

  1. Open the port in the sandbox network policy (run on the host):

    sbx policy allow network -g "localhost:9999"
    
  2. Optionally create a config file (the SANDBOX_COMM_URL env 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
    
  3. Start the host daemon:

    sandbox-comm-daemon
    # or: python -m docker_sandbox_guest_to_host_communicator.daemon
    
  4. 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:

  1. --url URL flag on the sandbox-comm command
  2. SANDBOX_COMM_URL environment variable
  3. host and port in the config file
  4. http://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


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

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz.

File metadata

File hashes

Hashes for docker_sandbox_guest_to_host_communicator-1.0.0.tar.gz
Algorithm Hash digest
SHA256 76f0a100daacacb0b0be04fb3881df2e72301b30818e6e3d5b1cd4c69c472283
MD5 8db099b24ee9006d95b45869fd4dc842
BLAKE2b-256 7e71adde2013e8730970a79172d062a07d7683ee45ac01f290600affa322f3f7

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for docker_sandbox_guest_to_host_communicator-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8cb770ad901f3cac6697ccc553a2f9d23830e8fc220bd9ffa8d66eacac49a825
MD5 34fcd57a08ba3f30cb1f6078e6b050d4
BLAKE2b-256 8ab4a571f9d710abb97fdb70c25aa65838af4134bfa5f1af3e0947db7e6bbbbc

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page