# Previews

> Run a web server inside a sandbox and reach it from your browser, via a private owner-only proxy or a shareable secret link with automatic TLS.

Run a web server (or any HTTP service) inside a sandbox and reach it from your browser.
Whatever you're running must bind `0.0.0.0` (not `127.0.0.1`) on a port, and the sandbox must
have networking (`network_mode: "bridge"`, the default).

There are two ways to preview: a **private** proxy (only you) and a **shareable** secret link
(anyone with the URL).

> **Make previews survive a stop/resume.** Stopping a sandbox keeps your files but kills running
> processes, so after a resume the preview shows nothing until the server is started again. Set a
> **startup command** (e.g. `python3 -m http.server 8000 --bind 0.0.0.0`) and the platform
> relaunches it automatically on every resume. Open a preview whose server isn't running and
> you'll get a friendly page explaining how to start it.

## Private preview (owner only)

```http
ANY /v1/sandboxes/{sandbox_id}/preview/{port}/{path}
```

Reverse-proxies your request into `http://<sandbox>:{port}/{path}`. It's **owner-scoped** -
authenticated with your API key or dashboard session - so only you can open it. All HTTP
methods, query strings, and bodies are forwarded.

```bash
curl -H "Authorization: Bearer $FLAX_API_KEY" \
  https://flaxcloud.com/v1/sandboxes/$SID/preview/8000/
```

Good for quickly checking your own work. Apps that emit absolute URLs may not render perfectly
under the `/preview/{port}/` path prefix - for that, use a shareable link (served at the
domain root).

## Shareable preview link (secret URL)

A shareable link gives you a clean public URL at the domain root:

```
https://<token>.preview.flaxcloud.com
```

The long random **token is the secret** - the link is unguessable and can't be enumerated.
Anyone with the URL can view the port; no login required. This is ideal for sharing a running
app with a teammate, a client, or for letting an external service hit your sandbox.

### Create or regenerate a link

```http
POST /v1/sandboxes/{sandbox_id}/preview-link
```

```json
{ "port": 8000 }
```

Response (`201`) includes the full `url` - **shown only once**:

```json
{ "sandbox_id":"sbx_…", "port":8000, "enabled":true, "url":"https://3cbf…87.preview.flaxcloud.com" }
```

Calling it again **rotates** the token: a new URL is issued and the previous one stops working
immediately.

### Inspect a link

```http
GET /v1/sandboxes/{sandbox_id}/preview-link
```

Returns status (port, enabled, created, last viewed). It never returns the token/URL again -
that's only shown at creation.

### Disable sharing

```http
DELETE /v1/sandboxes/{sandbox_id}/preview-link
```

Revokes the link; the URL immediately returns `404`. Regenerate with `POST` to share again.

### How security works

- Only a **SHA-256 hash** of the token is stored, so a database leak can't reveal live links.
- There is **no endpoint that lists links** - they can't be discovered, only known.
- Every access is **logged** (sandbox, port, path, client IP).
- TLS certificates are issued per subdomain automatically, gated so only live links get one.
- A **stopped, destroyed, or failed** sandbox stops serving its link (returns `404`). Unlike
  the private proxy, a shareable link does **not** auto-resume a stopped sandbox.

The dashboard's **Preview → Shareable link** panel does all of this with copy/regenerate/
disable buttons (the URL is shown once in a copy dialog).

## Common errors

| Status | Code | Meaning |
|--------|------|---------|
| `409` | `preview_unavailable` | The sandbox has `network_mode: "none"`. Recreate with `bridge`. |
| `502` | `preview_unreachable` | Nothing is listening on that port. Start a server bound to `0.0.0.0`. |
| `404` | `preview_not_found` | Shareable link is unknown, disabled, or the sandbox isn't live. |
| `503` | `preview_disabled` | Shareable links aren't enabled on this deployment. |

## Limits & roadmap

Previews currently proxy standard HTTP request/response. WebSocket and streaming (SSE)
passthrough are **coming soon**.
