Some changes are easier to judge by using them: a new checkout flow, a layout on a small screen, a chart with real data. An agent can start your app’s dev server inside its run and share it as a preview. You click Open preview, and the app opens in a tab of yours, served live from the agent’s sandbox while it keeps working.
Run r-7f3a
Ada, fixing prices for EU customers
bun run dev :5173
Exposed a preview: Checkout Open preview
Posted the link in #checkout
Edited OrderSummary.svelte
Opens in your browser
Order summary
- Ergo chair
- €429.00
- Shipping to Berlin
- Free
- Includes VAT (19%)
- €68.50
- Total
- €429.00
Ask for one in plain words, wherever you talk to the agent:
@ryph start the dev server and send me a preview of the new checkout An agent with a browser can click through the same dev server itself. A preview is how you get to click through it too.
Hot reload, streaming responses and WebSockets all work, so when the agent saves a change, your tab updates as it would on your own machine.
How a preview comes up
-
The agent starts the dev server
It runs the server with
ryph__background_runas a service, so the server keeps running while the agent works on, and the agent hears about it if the server stops. -
It exposes the port
It calls
ryph__expose_portwith the port and a label, such as{ "port": 5173, "label": "Checkout" }. Ryph registers the preview and hands back its link, which the agent shares in the thread, the task or the pull request. -
You open it
Ryph checks you can see the run, then lets your browser through to the dev server. The run opened the connection from inside, so nothing in it had to be published.
-
It lives as long as the run
When the run ends, its previews close. If the dev server crashes, the preview answers as unavailable until the agent starts it again on the same port.
Where to open it
- Open the task in the web app.
- In the transcript, the agent’s Exposed a preview step has an Open preview button.
- Or switch to the run’s Commands tab. Every preview of the run is listed under Previews, with its label and port, and its own Open preview button.
Next to the list, Ryph says who can open the run’s previews: People who can see this run, or This machine only when Ryph runs on your machine.
A link the agent posts in a chat thread or a pull request works too. On a hosted deployment, opening it takes you through Ryph’s sign-in first if you need it, then the same check.
Who can open a preview
On Kubernetes with a preview domain, anyone who can see the run can open its previews, and nobody else. Each time you open one, Ryph checks you can see the run and gives your browser a one-time ticket, good for 60 seconds. The preview trades it for a cookie that works for that one preview, for up to eight hours. Networked Compose runs the same check, for a browser on the host.
On your machine and with Docker Compose, previews open at http://<id>.localhost, which only a browser on that same machine can reach. Anyone on the machine who has the link can use the app.
How Ryph contains a preview
The agent wrote the code a preview serves, and an agent can be talked into things by what it reads. So on a hosted deployment, Ryph treats every preview as a hostile page and fences it in:
- Every preview gets its own origin,
<id>.<your preview domain>, with an id nobody can guess. The preview domain is separate from your Ryph app, so a page in a preview can’t read or use your Ryph session. - The viewer cookie works for one preview only. It’s set for that exact host name, so another preview can’t read it or overwrite it. Ryph removes it from every request before it reaches the dev server.
- The page reaches what the run could. Every response carries a content security policy built from the agent’s network allowlist, which holds the page’s requests, WebSockets, scripts and forms to the hosts the run may reach.
- The dev server learns little about you. Ryph removes your IP address from every request, and drops response headers that would reach past the preview, such as
Clear-Site-Dataand reporting endpoints. - Nothing outlives the run. Ryph refuses to install service workers, so a page can’t leave code running in your browser after its run ends.
- Only ports the agent registers are reachable, between 1024 and 65535, up to four per run. Ryph’s own services inside the run, such as its control API, tool server and browser, can’t be exposed.
What to keep in mind
Some things are out of Ryph’s reach once the page is in your browser:
- A page can send data out by navigating. A link it opens, a window it pops up, WebRTC and DNS prefetching all get past a content security policy. Assume anything you type into a preview can leave it.
- A page can look like anything, including your company’s sign-in. Sign in to Ryph only on your Ryph app’s address, and don’t enter a password into a preview.
- Access is checked when you open a preview. Someone who loses access to a run keeps a preview they already opened until their cookie expires, after up to eight hours, or the run ends.
- Previews can toss cookies at each other. Page code can set a cookie for the whole preview domain, which every other preview then receives. The worst it can do is confuse another preview, or fill your browser’s cookies for the domain until previews stop loading for you, which clearing them fixes. Browsers only keep subdomains apart like this for domains on the Public Suffix List, which is meant for domains that host many unrelated sites, so a preview domain will usually have this gap.
- Old Safari is looser. Safari before 16.4 lacks the request headers Ryph uses to tell a page load from an embedded script, so there one preview can load another’s scripts.
On the ryph binary and Docker Compose, a preview is code the agent wrote, running in your browser with your machine’s network. It can reach other services on the machine and your local network, and the internet, even when the run itself is confined. Open previews of runs you trust.
Getting your dev server ready
Most dev servers work as they are. A few settings trip them up:
- Listen on localhost inside the run. The tunnel connects to
127.0.0.1or::1on the exposed port. - Allow the preview’s host name. Dev servers that check the
Hostheader, such as Vite withserver.allowedHosts, need the preview’s domain on the list:.localhoston your machine, or your preview domain on a hosted deployment. - Use relative URLs. An app that hardcodes
localhost:5173, an OAuth callback URL or a fixed hot reload address has to be configured with the preview’s URL. The tunnel doesn’t rewrite your code. - Call a separate API through the dev server’s proxy. Requests from one preview to another are refused, so put the API behind a same-origin proxy, such as Vite’s
server.proxy.
If your app needs a database or a cache to start, give the repo an environment with those services, and the dev server finds them as it would anywhere else.
Previews on your deployment
Previews are most useful when your whole team can open them, which takes a preview domain. Today that’s possible on Kubernetes. Everywhere else, a preview is local-only: it opens only in a browser on the machine Ryph runs on.
| Deployment | Who can open previews | Where they open |
|---|---|---|
| On your machine | Local-only. A browser on the same machine | http://<id>.localhost:<port> |
| Docker Compose | Local-only. A browser on the host | http://<id>.localhost:7490 |
| Networked Compose | Local-only. A browser on the host, for people who can see the run | http://<id>.preview.localhost:7490 |
| Kubernetes | Your team: everyone who can see the run, once you set a preview domain | https://<id>.<your preview domain> |
On your machine, RYPH_LOCAL_PREVIEWS=off turns previews off. They’re also off when RYPH_APP_URL points somewhere other than localhost, since a .localhost link means nothing to a browser on another machine. The same goes for Docker Compose, where RYPH_PREVIEW_PORT moves the published port.
On Kubernetes, previews are off until you give them a domain, set up as below.
Set up a preview domain
On Kubernetes, Ryph serves previews from a dedicated domain: every preview at https://<id>.<preview domain>, open to everyone who can see its run. That domain is the boundary between code an agent wrote and everything else your people are signed in to, so choose it with care.
Register a new domain for previews, such as acme-previews.dev, and use it for nothing else. No app, no sign-in, no marketing site, no other subdomains, and no cookies set by anything but Ryph.
Don’t use a subdomain of a domain you already use, such as previews.acme.com. Every subdomain of acme.com is a separate origin, but cookies don’t follow origins: a cookie can be set for all of acme.com, and browsers treat its subdomains as the same site, so SameSite doesn’t keep them apart. Page code in a preview could then set cookies that your other acme.com apps receive, which can log people out or fix their session to one an attacker chose. And any cookie those apps set for all of acme.com, sessions included, would be sent to the agent’s dev server with every request.
To set it up:
Register the domain and keep it for previews alone. It must not be the domain your Ryph app is on, at any level.
Point
*.<domain>at the cluster with a wildcard DNS record and a wildcard certificate, throughingress.previewsorhttpRoute.previewsin the Helm chart.Turn previews on in the chart’s values:
previews: enabled: true domain: acme-previews.devRYPH_PREVIEWS_TOKENis generated by the chart. To bring your own, set it undersecrets.internal, 32 random characters or more. Ryph signs preview addresses and viewer cookies with it, so anyone holding it can let themselves into any preview. Changing it gives every preview a new address and signs everyone out of open ones.
The service’s internal port, 7491, carries runs’ tunnels and Ryph’s own requests. Route only the public port, 7490, to the internet. The chart’s network policies already keep 7491 to the control plane and runs.