Custom preview proxies let you serve a machine preview through your own domain, session model, headers, and application proxy while keeping Nullspace edge credentials out of browser-visible URLs.

Custom domains

The customer-run preview proxy is the supported custom-domain path for the current launch. Put your own domain, authentication, branding, and browser session policy in front of the marker-only upstream URL returned by Nullspace. No managed custom preview domain inventory, DNS verification or TLS issuance workflow is exposed by Nullspace yet. The API surface remains the preview proxy target plus durable preview grant inventory and revocation.

Create a proxy target

The TypeScript SDK does not expose a preview-proxy-target helper, so create targets from the Python SDK, CLI, or HTTP API:
from nullspace import Machine

machine = Machine.connect("mch_123")
target = machine.create_preview_proxy_target(
    8080,
    expires_in_seconds=900,
    transports=["http", "websocket"],
)

print(target.http_url)
print(target.websocket_url)
print(target.token_header_name)
print(target.required_forwarded_headers)
The returned http_url and websocket_url include __ns_preview_transport=header. That marker is not a secret. The secret is the transport-specific token value:
FieldUse
http_urlUpstream HTTP target for your proxy.
websocket_urlUpstream WebSocket target for your proxy.
http_tokenSend as x-nullspace-preview-proxy-token for HTTP upstream requests.
websocket_tokenSend as x-nullspace-preview-proxy-token for WebSocket upstream requests.
expires_atExpiration for the proxy grant and returned tokens.
traffic_access_requiredWhether private traffic access also requires a traffic token header.

Forward upstream

Your proxy should authenticate the browser request using your own app controls, then forward to the Nullspace upstream target with the preview proxy token:
curl -fsS \
  -H "x-nullspace-preview-proxy-token: ${NULLSPACE_PREVIEW_PROXY_HTTP_TOKEN}" \
  -H "x-forwarded-host: app.example.com" \
  -H "x-forwarded-proto: https" \
  -H "x-forwarded-for: 203.0.113.10" \
  "${NULLSPACE_PREVIEW_PROXY_HTTP_URL}"
Forward the headers listed in required_forwarded_headers. Use http_token only for HTTP upstream requests and websocket_token only for WebSocket upgrades; tokens are scoped to the machine, port, grant, principal, expiry, and transport. Nullspace preserves X-Forwarded-Host, X-Forwarded-Proto, and X-Forwarded-For exactly as your proxy sends them. It does not add missing forwarded headers or append to an existing X-Forwarded-For chain. If the machine service also needs a different upstream Host, configure network.mask_request_host; that setting changes only the Host header sent to the machine service and does not rewrite X-Forwarded-* metadata. HTTP responses are returned to the browser as machine services emit them, including redirects with relative Location values. For WebSocket routes, use the returned websocket_url and websocket_token; HTTP proxy tokens are not valid for WebSocket upgrades. See the custom preview proxy example for minimal Node/Express and Python/FastAPI proxy apps that forward HTTP, WebSocket, private traffic, and required forwarded headers.

Private traffic tokens

When a machine is created with network={"allow_public_traffic": False}, the proxy target response reports traffic_access_required: true and the traffic access header name. It does not return the traffic token value. Store the machine.traffic_access_token returned at machine create time and send it upstream as a separate secret:
-H "x-nullspace-traffic-access-token: ${NULLSPACE_TRAFFIC_ACCESS_TOKEN}"
Preview proxy tokens and traffic access tokens are not interchangeable. Edge validates them independently, and Nullspace strips both headers before the request reaches the machine service.

Rotation and revocation

Create a fresh proxy target before expires_at when your proxy needs continued access. Proxy targets use durable preview grants, so machine.list_preview_urls() includes them and machine.revoke_preview_url(grant_id) revokes them.