[iris]
Concepts

Preview URLs

Signed, publicly-routable URLs for ports on a running sandbox

A preview URL is a signed, publicly-routable URL for one (sandbox, port) pair. The HMAC token is baked into the hostname — any fetch() or httpx.get() reaches the sandbox with no extra headers.

URL shape: https://{port}-{hash}-{exp}-{hmac}.irisrun.io

Each call also returns a headerJwt / header_jwt carrying the same expiry, for contexts that need the auth in a header instead of the URL.

Getting a preview URL

import { IrisClient } from '@iris/sdk'

const client = new IrisClient()
const sandbox = await client.sandboxes.create({ name: 'web' })

// start a server on 0.0.0.0:8000 inside the sandbox...

const preview = await sandbox.getPreviewUrl(8000, { ttlSeconds: 600 })
console.log(preview.url)       // https://8000-<hash>-<exp>-<hmac>.irisrun.io
console.log(preview.headerJwt) // RS256 JWT, same expiry
console.log(preview.expiresAt) // Unix seconds
from iris import IrisClient

client = IrisClient()  # reads IRIS_API_KEY
sandbox = client.sandboxes.create(name="web")

# start a server on 0.0.0.0:8000 inside the sandbox...

preview = sandbox.get_preview_url(8000, ttl_seconds=600)
print(preview.url)          # https://8000-<hash>-<exp>-<hmac>.irisrun.io
print(preview.header_jwt)   # RS256 JWT, same expiry
print(preview.expires_at)   # Unix seconds

The signed URL works with a plain fetch — no credentials needed in the request:

const res = await fetch(preview.url)
const body = await res.text()
import httpx

res = httpx.get(preview.url)
print(res.text)

VM listener binding

Bind your server on 0.0.0.0 (IPv4). The in-VM data plane republishes IPv4 ports on the VM's IPv6 address, which the edge dials — no special bind flag is needed.

# plain IPv4 bind is enough
python -m http.server 8000 --bind 0.0.0.0

TTL and expiry

Omitting ttlSeconds / ttl_seconds uses the server default of 3600 s. Values are clamped to [60, 86400] server-side.

// default TTL (3600 s)
const preview = await sandbox.getPreviewUrl(8000)

// custom TTL — server clamps to [60, 86400]
const short = await sandbox.getPreviewUrl(8000, { ttlSeconds: 300 })
# default TTL (3600 s)
preview = sandbox.get_preview_url(8000)

# custom TTL — server clamps to [60, 86400]
short = sandbox.get_preview_url(8000, ttl_seconds=300)

Public vs private sandboxes

The public flag on a sandbox controls access to its bare machine domain ({name}-{hash}.irisrun.io). It does not gate the preview URL — a valid signed URL fetches anonymously for both public and private sandboxes, because the embedded HMAC is the auth. You do not need public: true to use preview URLs.

Using the JWT header

When the calling context can't carry a signed hostname — for example a cross-origin fetch where the browser rewrites the Host, or a server-to-server call behind a header-only auth policy — pass headerJwt / header_jwt as a bearer token instead:

const res = await fetch(endpointUrl, {
  headers: { 'Authorization': `Bearer ${preview.headerJwt}` },
})
import httpx

res = httpx.get(
    endpoint_url,
    headers={"Authorization": f"Bearer {preview.header_jwt}"},
)

Async Python

AsyncIrisClient exposes the same surface with async/await:

from iris import AsyncIrisClient

async def main():
    client = AsyncIrisClient()
    sandbox = await client.sandboxes.create(name="web")
    preview = await sandbox.get_preview_url(8000, ttl_seconds=600)
    print(preview.url)

On this page