Exposed ports#
An agent that serves a chat UI, a dashboard, or an HTTP API needs a way for people to reach it. An exposed port publishes one container port at a stable URL derived from the sandbox's slug.
curl -fsS -X POST \
-H "Authorization: Bearer $LENS_AGENTS_TOKEN" \
-H "Content-Type: application/json" \
https://agents.example.com/v1/projects/$PROJECT_ID/sandboxes \
-d '{
"image": "ghcr.io/acme/agent:1.2",
"command": "./start.sh",
"cpu": "500m", "memory": "2Gi",
"exposedPorts": [{"name": "web", "port": 8080, "auth": "private"}]
}'
Create a sandbox in production from ghcr.io/acme/agent:1.2 running
"./start.sh" with 500m CPU and 2Gi memory. Publish port 8080 as
"web" with private auth, and give me the URL.
nexusctl sandbox create --project production \
--image ghcr.io/acme/agent:1.2 \
--command "./start.sh" \
--cpu 500m --memory 2Gi \
--port name=web,port=8080,auth=private
Or in a spec file:
exposedPorts:
- name: web
port: 8080
auth: private
A sandbox publishes at most four ports, and auth is decided per port. So one agent can serve an authenticated dashboard and an unauthenticated webhook receiver from the same container:
exposedPorts:
- name: web
port: 8080
auth: private
- name: hooks
port: 9000
auth: public
The URL#
Each port answers on its own host, built from the port name and the sandbox slug:
https://<port-name>--<sandbox-slug>.<sandboxIngress.host>
The first entry in exposedPorts also answers on the short host, and that is the URL the platform reports for it:
https://<sandbox-slug>.<sandboxIngress.host>
The slug is assigned at creation and never changes, so a URL is stable across restarts, image upgrades, and rollbacks. The port name is part of the host, so a rename changes the URL.
The short host carries the auth of the first port
The short host serves the first entry in exposedPorts, with that entry's auth. Put a public port first and the short host is open to anyone. Keep the private port first, and let the public port answer on its own named host.
Read the URLs back from the sandbox:
curl -fsS -H "Authorization: Bearer $LENS_AGENTS_TOKEN" \
https://agents.example.com/v1/projects/$PROJECT_ID/sandboxes/$SANDBOX_ID \
| jq -r '.exposedPorts[].url'
What URL is the nightly-refactor sandbox in production serving on?
nexusctl sandbox get nightly-refactor --project production -o json \
| jq -r '.exposedPorts[].url'
The URL is null without sandboxIngress.host
If the install was deployed without sandboxIngress.host, exposed ports still exist but resolve to no URL. Set the value and make sure *.<host> resolves to your ingress. See Install on Kubernetes.
Fields#
| Field | Rules |
|---|---|
name |
Lowercase DNS label fragment, ^[a-z][a-z0-9-]{0,30}$. Unique within the sandbox — it is what a URL resolves by. |
port |
TCP port 1–65535, unique within the sandbox. The supervisor dials it on 127.0.0.1 inside the container. |
auth |
private or public, decided per port. |
A duplicate name or a duplicate port number is refused, because either one makes an entry unreachable.
A sandbox created before platform 0.27.0 serves one port until it restarts
The data tunnel of an older sandbox cannot address a port by name. Rather than route a request to the listener behind a different port, the ingress answers 502 for a revision that declares more than one port. Restart the sandbox so it picks up the current loader image. A single-port revision is unaffected.
The agent binds the port on loopback inside its own container. Nothing else about the sandbox's network is opened by exposing a port — outbound traffic is still governed by policy, and no other port becomes reachable.
Authentication modes#
private#
Requests must carry a valid platform session. Anyone without one is sent to sign in, and access is checked against the project the sandbox belongs to.
This is the default and the right choice for anything beyond a demo.
public#
The URL opens with no platform session at all. Anyone who has the URL can reach the service.
Use public for a local trial, a demo, or a service that does its own authentication. Treat the URL as a secret, and remember that the agent behind it is running with real credentials and real access.
Health checks pair with exposed ports#
A sandbox that serves HTTP is usually worth probing. Point an HTTP health check at the same port so the platform can tell "running" from "running and answering":
exposedPorts:
- name: web
port: 8080
auth: private
healthCheck:
type: http
http:
path: /healthz
port: 8080
Related#
- Sandbox spec — declaring ports in a file
- Storage and health — probes, revisions, and rollback
- Install on Kubernetes — wildcard ingress for
*.<sandboxIngress.host> - Sandbox isolation — what an exposed port does not open up