Ingress
A service running inside a sandbox can receive requests from outside it. Any TCP port a process binds is reachable through the gateway's ingress proxy, you build the URL with proxyUrl(port) and call it like any HTTP endpoint. This is how you send commands to a long-running agent or server living in the sandbox.
Start a server, then call it
Here we write a tiny HTTP server that receives a command and returns a result, start it with execStream so it keeps running, then reach it from the client through the proxy.
import { getOrCreateSandbox } from "@hiver.sh/client";
const sandbox = await getOrCreateSandbox("server", { image: "python" });
// A minimal server that echoes back whatever command it receives.
await sandbox.writeFile("/workspace/server.py", `
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
class H(BaseHTTPRequestHandler):
def do_POST(self):
n = int(self.headers["content-length"])
cmd = json.loads(self.rfile.read(n))["cmd"]
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps({"ran": cmd, "ok": True}).encode())
HTTPServer(("0.0.0.0", 8000), H).serve_forever()
`);
// Start it in the background, execStream returns immediately and keeps it alive.
const server = await sandbox.execStream(["python3", "/workspace/server.py"], { cwd: "/workspace" });
// Give it a moment to bind, then send it a command through the proxy.
await new Promise((r) => setTimeout(r, 500));
const res = await fetch(`${sandbox.proxyUrl(8000)}run`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ cmd: "build" }),
});
console.log(await res.json()); // { ran: "build", ok: true }Exposed ports
If your image declares ports with EXPOSE, list them with getPorts (get_ports / GetPorts); each is reachable via proxyUrl. A process that binds a port at runtime (like the server above) is reachable the same way without an EXPOSE.
console.log(await sandbox.getPorts()); // e.g. [8000]For a service baked into an image as its entrypoint, see OCI Image and the Claude Agent SDK example.
Observing inbound traffic
Every inbound call surfaces on the event stream as an ingress.request / ingress.response pair, the mirror image of the egress.* events for outbound traffic. The ingress.response fires as soon as the status and headers are known; the body then streams as ingress.chunk events (with label up/down for WebSocket frames), so you can follow a long-lived SSE or WebSocket connection live instead of waiting for it to close. Watch them to audit what's hitting your in-sandbox service:
for await (const event of sandbox.getEventsStream()) {
if (event.type === "ingress.request") {
console.log("in:", event.method, event.path, "→ port", event.port);
}
}Next: File Access