Cua Docs

Forward a port from a sandbox

Expose a sandbox port on localhost so local tools can connect to it.

sb.tunnel.forward() exposes a port (or Android abstract socket) running inside the sandbox on the local host.

Forward a single port

Start the service inside the sandbox, then forward its port.

from cua import Sandbox, Image
 
async with Sandbox.ephemeral(Image.linux()) as sb:
    await sb.shell.run('python3 -m http.server 8080 &')
 
    async with sb.tunnel.forward(8080) as t:
        print(t.url)   # http://localhost:49823
        print(t.host, t.port)  # localhost, 49823

The tunnel closes automatically when the async with block exits.

Forward multiple ports at once

Pass multiple ports to get a dict[port, TunnelInfo].

async with sb.tunnel.forward(8080, 9222) as tunnels:
    app_url      = tunnels[8080].url
    devtools_url = tunnels[9222].url

Close a tunnel manually

If you need the tunnel outside an async with block, close it yourself.

t = await sb.tunnel.forward(8080)
print(t.url)
# ... do work ...
await t.close()

Android abstract sockets

Pass a string instead of a port number.

async with sb.tunnel.forward('chrome_devtools_remote') as t:
    print(t.url)  # http://localhost:<random>

Abstract socket names are Linux-only (Android). On Linux and macOS sandboxes use integer port numbers.

TunnelInfo fields

FieldTypeDescription
hoststrAlways 'localhost'
portintThe host-side port assigned
sandbox_portint or strThe original port/socket inside the sandbox
urlstrShorthand http://{host}:{port}

Example: Chrome DevTools Protocol

Start Chrome with remote debugging enabled, then read the DevTools target list through the forwarded port.

from cua import Sandbox, Image
 
async with Sandbox.ephemeral(Image.linux()) as sb:
    await sb.shell.run('google-chrome --remote-debugging-port=9222 --headless &')
 
    async with sb.tunnel.forward(9222) as t:
        import httpx
        targets = httpx.get(f'{t.url}/json').json()
        print(targets)