| title |
|
||||||||
|---|---|---|---|---|---|---|---|---|---|
| description |
|
||||||||
| keywords |
|
||||||||
| topics |
|
||||||||
| tags |
|
||||||||
| content |
|
||||||||
| status | published |
This is an optional path. Most readers running the example on a personal laptop or a clean cloud VM should skip it — point NEMOCLAW_ENDPOINT_URL directly at the HTTPS inference endpoint and bring-up.sh will work.
You need this when the inference endpoint can't be reached cleanly from inside the OpenShell sandbox. Two common triggers:
- Corporate VPN / split-horizon DNS. The host resolves the inference hostname to
127.0.0.1(or some VPN-internal address), but the Docker sandbox doesn't share that resolver — it tries to reach the public address and fails or hits TLS-validation errors. - Local CA / mkcert chain. The host's TLS terminator presents a cert signed by a CA installed only on the host. Containers don't have that CA in their trust store, so TLS handshakes fail with "unable to verify the first certificate".
The fix is to give the sandbox a plain-HTTP target on the host that does the TLS handshake on its behalf using the host's own trust store. That's what scripts/host-tls-proxy.py does.
sandbox ──HTTP──> host:18080 (host-tls-proxy.py) ──HTTPS──> upstream-inference-host
(uses host trust store)
The proxy is a thin reverse proxy:
- Listens on plain HTTP on a host port (default
0.0.0.0:18080) so the sandbox can reach it throughhost.openshell.internal(e.g.http://host.openshell.internal:18080). - Forwards every request to the configured
--upstreamover HTTPS, using Python's default SSL context — which loads the host's installed root CAs. - Streams responses back unmodified (minus hop-by-hop headers).
It's about 140 lines of stdlib Python — no third-party dependencies.
Run it on the host before bash scripts/bring-up.sh. Keep it running for as long as the sandbox is running.
$ mkdir -p .tmp
$ setsid -f python3 scripts/host-tls-proxy.py \
--upstream "https://your-inference-host" \
--listen 0.0.0.0 \
--port 18080 \
> .tmp/host-tls-proxy.log 2>&1 < /dev/nullNotes on the invocation:
setsid -fdetaches the process from the current shell session — you get your prompt back and the proxy survives logout.< /dev/nullcloses stdin so the process doesn't try to read from your terminal.> .tmp/host-tls-proxy.log 2>&1captures stdout/stderr for debugging. The directory is gitignored.--upstreamtakes the full HTTPS URL of the upstream inference host (no trailing path — the proxy preserves whatever path the sandbox sends).
To stop:
$ pkill -f host-tls-proxy.pyTwo changes:
# Point the agent at the local proxy instead of the upstream HTTPS URL.
NEMOCLAW_ENDPOINT_URL=http://host.openshell.internal:18080/v1host.openshell.internal is the stable host-routed address OpenShell exposes inside Docker-backed sandboxes for package-managed and snap-managed gateways.
Provider setup runs its inference preflight on the host before sandbox
creation. For this special URL, the preflight translates only the hostname to
127.0.0.1 and checks the same proxy listener and request path. The provider
configuration remains host.openshell.internal, so the created sandbox uses
the correct host-routed address.
COMPATIBLE_API_KEY (or OPENAI_API_KEY) stays unchanged — the proxy passes the Authorization header straight through.
Before running bring-up.sh, confirm the proxy is reachable from the host and forwards correctly:
$ curl -sf http://localhost:18080/v1/models -H "Authorization: Bearer $COMPATIBLE_API_KEY" | head -20A 200 OK with a model list means the proxy and upstream are both working. A 502 (or no response) means the proxy can't reach the upstream — check --upstream and .tmp/host-tls-proxy.log.
After bring-up.sh, confirm the sandbox can reach it through the Docker bridge:
$ openshell sandbox exec hermes-direct curl -sf http://host.openshell.internal:18080/v1/models | head -20bring-up.shsucceeds but the agent's first inference call hangs or errors with TLS verification. Either the proxy isn't running, orNEMOCLAW_ENDPOINT_URLstill points at the HTTPS upstream. Run the smoke tests above to isolate.502 Upstream inference proxy errorfrom the proxy. The proxy reached the host but couldn't complete the upstream HTTPS handshake. Check that the host's trust store has the upstream's CA — the proxy usesssl.create_default_context(), which honors/etc/ssl/certs(or the equivalent) andSSL_CERT_FILE/REQUESTS_CA_BUNDLEenv vars.- Sandbox can't reach
host.openshell.internal:18080. Verify the proxy's--listenis0.0.0.0(not127.0.0.1) —127.0.0.1only accepts connections from the host's loopback, which is not the same loopback as inside the sandbox.
