Skip to main content

Exposed-port WebSockets

info = sandbox.get_host_info(8080)
print(info.websocket_url)
Use this for WebSocket servers that your sandbox exposes through the preview URL router. The returned URL is signed and temporary. Direct HTTP preview continuation cookies do not authorize WebSocket upgrades; use the signed websocket_url. Default SSH access does not use an exposed-port WebSocket. Use nullspace ssh for certificate-backed relay access. Only older port-22 fallback deployments use a signed websocket_url with OpenSSH and websocat. For the self-hosted single-host appliance, owned-domain mode returns signed wss://{PORT}-{SANDBOX_ID}.<domain>/... preview WebSocket URLs through API-compatible Caddy ingress. Localhost/no-domain mode leaves NULLSPACE_PUBLIC_HOSTNAME unset, so exposed-port helpers use direct local host mappings instead of signed public WebSocket URLs.

Sandbox control WebSocket

The sandbox control channel powers streaming exec, PTY sessions, and file watch events. Prefer SDK helpers unless you are implementing a custom client. The server sends WebSocket Ping frames every 15 seconds and closes after two missed Pong responses, so custom clients need normal Ping/Pong handling.
sandbox.commands.run(
    "for i in 1 2 3; do echo $i; sleep 1; done",
    shell=True,
    on_stdout=lambda data: print(data, end=""),
)

Lifecycle and monitor streams

from nullspace import Lifecycle, Monitor

lifecycle = Lifecycle()
sub = lifecycle.subscribe(on_event=lambda event: print(event.operation, event.status))
sub.stop()

monitor = Monitor()
monitor_sub = monitor.subscribe(
    on_metrics=lambda event: print(event.metrics),
    on_processes=lambda event: print(event.processes),
)
monitor_sub.stop()

Protocol reference

For raw message shapes, see WebSocket protocol.