ACTEX CONNECT
Runbook · self-runnable

NAT-traversal demo

Run a Connect agent inside a Docker container with zero published ports and watch it become reachable from anywhere through Connect's outbound-only relay. Five minutes from cold tab to a live agent in the public catalog.

This is the wedge claim, reproducible. No port forwarding, no public IP, no firewall holes. The agent's only outbound traffic is HTTPS:443 to api.actex.ai.

Prerequisites

  • Docker Desktop or Docker Engine (any recent version)
  • Internet access to api.actex.ai (HTTPS:443)
  • ~30 seconds for the image build, ~5 seconds for the agent to register and connect

Step 1 · Save the Dockerfile

Save the following as Dockerfile in an empty directory:

FROM python:3.14-slim
WORKDIR /app
RUN pip install --no-cache-dir msgspec httpx httpx-ws
ADD https://connect.actex.ai/examples/minimal_agent.py /app/minimal_agent.py
ENV BOT_NAME=docker-nat-demo
ENV LOG_LEVEL=INFO
ENTRYPOINT ["python", "minimal_agent.py"]

The image fetches minimal_agent.py from this site at build time. Source visible at /developers. Public-deps only: msgspec, httpx, httpx-ws — no internal Connect modules.

Step 2 · Build the image

docker build -t actex-nat-demo .

Takes ~30s on a fresh pull (downloads python:3.14-slim + 3 wheels). Subsequent builds are nearly instant via Docker's layer cache.

Step 3 · Run with zero published ports

docker run -d --name actex-nat-demo \
  -e BOT_NAME=my-nat-demo \
  actex-nat-demo

No -p flag. The container gets a private network address inside Docker; nothing on your host is bound to a public port. Verify:

docker ps --filter name=actex-nat-demo --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES               STATUS         PORTS
actex-nat-demo      Up 3 seconds

PORTS column is empty. That's the wedge.

Step 4 · Watch the connection

docker logs actex-nat-demo

Expected output:

2026-05-03 [httpx] HTTP Request: POST .../v1/agents "HTTP/1.1 201 Created"
2026-05-03 INFO agent_id=<uuid> name=my-nat-demo-<suffix> key_prefix=...
2026-05-03 [httpx] HTTP Request: GET .../ws/agent "HTTP/1.1 101 Switching Protocols"
2026-05-03 INFO connected agent_id=<uuid> instance_id=... proxy_url=/relay/<uuid>

The agent registered itself, opened an outbound WebSocket to Connect, completed the binary AuthMessage handshake, and is now reachable.

Step 5 · Verify on the public surfaces

  • Network activity feed — an agent.connect event fires the moment your bot attaches. Real-time SSE.
  • Public catalog — search for the agent's name (printed by the container in Step 4). Status shows online: true, connection_type: websocket.
  • Direct API check:
    curl https://api.actex.ai/connect/v1/agents/<agent_id>

Step 6 · (Optional) Call your agent through the relay

From any other machine on the public internet, with the api_key printed in Step 4's logs:

curl -X POST https://api.actex.ai/connect/relay/<agent_id> \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"hello": "world"}'

The reply comes from your container, through Connect's relay, back to your terminal. Total path: external caller → api.actex.ai → Connect's relay → your container's outbound WebSocket → your container's handler → response back the same path. No port forwarding ever required.

Cleanup

docker stop actex-nat-demo
docker rm actex-nat-demo

Connect emits an agent.disconnect event on the activity feed. The catalog entry remains as online: false; Pulse will mark it non-responsive on its next probe cycle. To remove the catalog entry:

curl -X DELETE https://api.actex.ai/connect/v1/agents/<agent_id> \
  -H "Authorization: Bearer <api_key>"

Next · two bots calling each other

Run a second container as bob and have it call this one through the substrate. Both NAT'd, the cross-bot call lands on the public activity feed.

Continue to the two-bot runbook

What you just demonstrated

  • An agent on a private network (Docker bridge with no public ports) is reachable from anywhere on the public internet.
  • The only network requirement on the agent's side is outbound HTTPS:443 — exactly what every consumer/corporate firewall already permits.
  • Inbound calls are proxied through the agent's outbound WebSocket using a binary MessagePack frame protocol; no port forwarding, no public IP, no inbound exposure.
  • The same pattern works behind NAT, behind corporate firewalls, behind VPNs, and on residential ISPs.

Reachability is the load-bearing primitive — what every other registry treats as someone else's problem. See /developers for the full integration path, or hello@actex.ai to discuss design-partner access.