URL Format
A preview URL is built from three parts in the subdomain:port— the port the server listens on inside the workspace (1–65535)workspaceIdPrefix— the first 8 hex characters of the workspace IDtoken— the workspace preview token, exactly 32[a-z0-9]characters
abc12345:
Getting a Preview URL
The web app shows preview URLs automatically when it detects an open port. Each port in the workspace’s Preview popover has a Copy link button (and, on devices with a share sheet, a Share button), and the in-app preview panel’s address bar has the same copy control. To build a URL from the CLI:Authenticated previews
A repository can declare a per-portauth_command that mints a development session inside the workspace. There are two ways to apply it.
Zero-click from a plain preview link
Setauto: true beside auth_command. On the first visit, the ordinary preview URL—including the plain link in a pull-request preview comment—starts the hook and applies its result, so a hook that navigates or reloads lands the visitor authenticated without a decorated URL or an Aether login. The request is asynchronous and application initialization can continue before its result arrives; startup-only clients that return local_storage must also return reload: true.
The preview runtime records its attempt in sessionStorage before it calls the hook. It therefore tries silently at most once per tab session at that preview origin: if the hook, network, response, or session application fails, the visitor stays on the plain preview and later reloads in the same tab do not retry. During a mixed-fleet rollout, older workspaces simply do not bootstrap; their plain previews continue to work unchanged.
On demand with --authed
To run the hook explicitly from the CLI:
url shape. Open that URL to land in the application with the session the repository minted.
For a local_storage result, the CLI prints a clear explanation to stderr and the complete structured JSON response to stdout. This keeps the result script-consumable instead of dropping entries that require client-side application. Pass --json to suppress the explanation and emit only JSON.
In the web app, open a workspace preview and select Open logged-in preview (the key icon) in the preview toolbar. Aether mints on each click and opens a returned bootstrap URL in a new tab. If the hook returns local_storage, the web app explains that the current flow cannot apply it across origins and directs you to the CLI JSON instead.
Authenticated links require an authenticated Aether user who can read the workspace’s owning organization. They are never added to the sticky pull-request preview comment or any other GitHub-bound surface; PR comments continue to contain only ordinary preview links.
Supabase template
The Aether repository’sscripts/preview-auth.ts is the canonical Supabase example. It strictly parses the hook input, uses the Supabase password grant through the supplied or canonically derived 54321 sibling preview, validates the bounded response, and emits a 5173 URL fragment that supabase-js consumes:
.aether/environment.json
scripts/preview-auth.ts
54321 sibling by replacing only the port prefix of a canonical preview origin. This covers services that are reachable through the preview gateway but are not declared or discovered when the hook runs. Copy the complete canonical script rather than the excerpt: its strict origin checks, sibling-origin derivation, output bounds, and password-grant response validation are part of the security boundary. Replace only the seeded development credentials and publishable key for your local Supabase stack.
Sleeping Workspaces
Idle workspaces suspend to save resources, but preview links stay valid. Opening a link to a suspended workspace lands on a start page; once the workspace is ready, the page redirects to the preview automatically. Actively using a preview also keeps its workspace awake — preview traffic defers idle suspension in 15-minute windows (up to a hard ceiling, so an abandoned tab can’t keep a VM alive forever). Waking a workspace requires its owner. Anonymous access works only while the workspace is running: the start page asks visitors to sign in, and the start action is scoped to the workspace’s owner — someone you shared a link with cannot wake your workspace. If the workspace is already starting when the page loads, no click is needed; the page waits and redirects on its own.Preview Links on Pull Requests
Declare aport on a terminal in .aether/environment.json and Aether maintains a sticky Preview comment on the task’s pull request:
name → url and is edited in place as the task progresses — one comment per task, never a stream of new ones. Removing the declarations clears it. Reviewers can click straight through to the running app while the workspace is awake; if it has suspended, the link lands on the start page, and waking it is up to the workspace’s owner.
The declared port is the contract: the link is published whether or not the service is currently listening, the same way deploy tooling posts your deploy URL regardless of your app’s health. A link to a declared port with nothing listening yet returns the gateway’s connect error until the service is up.
How Routing Works
Previews are served by the gateway proxy, separate from the main API. For each request it:- Parses the subdomain into port, workspace ID prefix, and token. A malformed subdomain returns
400. - Looks up a ready workspace by its ID prefix. On a hit, the token is validated with a constant-time comparison — a mismatch returns
403— and the request is proxied to the workspace’s private IP on the requested port. Preview traffic also records activity, which keeps the workspace awake. - If no ready workspace matches, the gateway checks whether the workspace exists at all (matching both prefix and token). If it doesn’t, the request returns
404. - If the workspace exists but isn’t ready (suspended or starting), a browser navigation is redirected (
302) to the start page — where the workspace’s owner can wake it; non-navigation requests (assets, XHR, a reloading HMR client) return503instead of an HTML page.
Port Discovery
Aether watches for listening ports inside the workspace and emits open/close events to the web app, so a preview becomes reachable as soon as a server starts — no configuration needed. See the ports WebSocket channel.Host Header Rewriting
Before forwarding, the gateway rewrites theHost header to localhost:{port}. Dev servers that validate the host (Vite’s default config, for example) accept the request without extra configuration.
WebSocket Support
WebSocket upgrade requests are proxied through to the workspace, so:- Hot Module Replacement (HMR) works through the proxy for Vite, Next.js, and webpack dev servers
- WebSocket-based apps (chat, live dashboards) work without modification
CORS Headers
The gateway adds CORS headers to preview responses, which enables embedding previews in iframes and making cross-port requests between previews (a frontend preview calling a backend preview on another port).Preview Runtime
The gateway serves a small JavaScript runtime at/_aether/preview-runtime.js and injects it into proxied HTML responses. To keep injection deterministic, HTML responses are proxied uncompressed.