> ## Documentation Index
> Fetch the complete documentation index at: https://hyperspeed.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Domains

> How to configure a public hostname for your self-hosted Hyperspeed instance — bring your own domain or use a gifted *.hyperspeedapp.com subdomain.

Every Hyperspeed deployment runs on your own infrastructure. You choose how users reach it:

* **BYO domain** — use a hostname you already control (recommended when you manage DNS).
* **Gifted subdomain** — get a `*.hyperspeedapp.com` hostname from Hyperspeed when you have no domain of your own. DNS points at your server; TLS still runs on your machine.

<Note>
  In both cases, the Hyperspeed stack (API, web, Postgres, object storage) runs on your server. Hyperspeed does not host customer application stacks.
</Note>

## BYO domain

<Steps>
  <Step title="Create a DNS A record">
    Point your hostname at your server's public IP address. For example, to use `app.example.com`, create an **A record** (or **AAAA** for IPv6) that resolves to your server's IP. A **CNAME** to another hostname that ultimately resolves to that IP also works.

    Verify propagation with:

    ```bash theme={null}
    dig app.example.com
    ```
  </Step>

  <Step title="Set CADDY_PUBLIC_HOST">
    In your `.env`, set `CADDY_PUBLIC_HOST` to the hostname only — no scheme, no trailing slash:

    ```bash theme={null}
    CADDY_PUBLIC_HOST=app.example.com
    CADDY_EMAIL=you@example.com
    ```

    Caddy uses this to select the site block and automatically obtains a Let's Encrypt certificate via HTTP-01. DNS must already resolve to your server before you start the stack, or the challenge will fail.
  </Step>

  <Step title="Set CORS_ORIGIN">
    Set `CORS_ORIGIN` to the exact origin users type in the browser — scheme, host, and port if non-default:

    ```bash theme={null}
    CORS_ORIGIN=https://app.example.com
    ```

    The value must match precisely. A trailing slash, a different scheme, or a missing/extra port will cause CORS failures in the browser.
  </Step>

  <Step title="Set PUBLIC_API_BASE_URL">
    Set `PUBLIC_API_BASE_URL` to the same origin. The API uses this when constructing absolute URLs for features like IDE preview iframes:

    ```bash theme={null}
    PUBLIC_API_BASE_URL=https://app.example.com
    ```

    Do not include a trailing path. The API appends `/api/...` paths itself.
  </Step>

  <Step title="Restart the stack">
    Apply the new environment:

    ```bash theme={null}
    docker compose up --build -d
    ```

    Caddy will obtain the TLS certificate on first start. Check Caddy logs if the certificate request fails:

    ```bash theme={null}
    docker compose logs caddy
    ```
  </Step>
</Steps>

### Caddyfile example

The repository Caddyfile is minimal for local development. For production with a real hostname, Caddy handles TLS automatically using the site block format:

```caddy theme={null}
app.example.com {
    encode gzip
    handle /api/* {
        reverse_proxy api:8080
    }
    handle /health {
        reverse_proxy api:8080
    }
    handle {
        reverse_proxy web:80
    }
}
```

Adjust `api` and `web` to match your Compose service names. The `reverse_proxy` directives automatically handle WebSocket upgrades for realtime features.

***

## Gifted subdomain

If you don't have a domain, Hyperspeed can provision a subdomain under `hyperspeedapp.com` (e.g. `acme.hyperspeedapp.com`) that points to your server. Hyperspeed creates the DNS A record; TLS is obtained on your server by Caddy using the same HTTP-01 challenge.

<Note>
  This requires Hyperspeed to issue you install credentials. Contact Hyperspeed to receive `PROVISIONING_INSTALL_ID` and `PROVISIONING_INSTALL_SECRET`.
</Note>

### How it works

Hyperspeed operates a **provisioning gateway** (a public edge service) that holds the DNS credentials for `hyperspeedapp.com`. Your API never receives those credentials — only the install-scoped credentials below. The gateway validates your request using an HMAC signature, then creates the DNS record via the Hyperspeed control plane.

<Warning>
  Never add Cloudflare API tokens or the Hyperspeed control-plane bearer to your `.env`. Your install only uses the three provisioning variables listed below. Those other credentials live exclusively on infrastructure Hyperspeed operates.
</Warning>

### Configuration

Add these three variables to your `.env`:

```bash theme={null}
PROVISIONING_BASE_URL=https://provision.hyperspeedapp.com
PROVISIONING_INSTALL_ID=<issued by Hyperspeed>
PROVISIONING_INSTALL_SECRET=<issued by Hyperspeed>
```

When all three are set, the API reports `provisioning_enabled: true` in `GET /api/v1/public/instance`.

### Claiming a subdomain

After configuring the provisioning variables and restarting the stack, an authenticated user can claim a subdomain from the setup wizard or workspace settings. You can also call the endpoint directly:

```bash theme={null}
POST /api/v1/provisioning/claim
Content-Type: application/json
Authorization: Bearer <user-token>

{
  "slug": "acme",
  "ipv4": "203.0.113.42"
}
```

The API signs the request to the provisioning gateway on your behalf. Possible error codes returned by the gateway:

| Code | Meaning |
| - | - |
| `invalid_slug` | The requested subdomain slug is not valid. |
| `invalid_ipv4` | The provided IP address is not a valid public IPv4. |
| `slug_taken` | That subdomain is already in use. |
| `rate_limited` | Too many requests from this install. |
| `provisioning_unavailable` | The gateway could not reach the control plane. |

After a successful claim, set `CORS_ORIGIN` and `PUBLIC_API_BASE_URL` to `https://acme.hyperspeedapp.com` and restart the stack so Caddy can obtain the TLS certificate for that hostname.

***

## CORS\_ORIGIN must be exact

Regardless of whether you use a BYO domain or a gifted subdomain, `CORS_ORIGIN` must be the **exact** origin the browser uses:

| Scenario | Correct value |
| - | - |
| Local Docker + Caddy | `http://localhost` |
| Local Vite dev server against native API | `http://localhost:5173` |
| Production BYO domain | `https://app.example.com` |
| Gifted subdomain | `https://acme.hyperspeedapp.com` |

The SPA is built to call `/api/...` on the same origin as the page (no separate `VITE_API_URL` in Docker). The browser's address bar origin must match `CORS_ORIGIN` exactly.

***

## Validation checklist

Run this after pointing any public hostname at your instance and after any change to DNS, IP address, or TLS termination.

| Check | What to verify |
| - | - |
| **DNS** | `dig app.example.com` resolves to your server's public IP. |
| **TLS** | The browser shows a valid certificate for your hostname with no mixed-content warnings. |
| **App same-origin** | Log in and open DevTools Network: API calls go to `https://<hostname>/api/...` with no CORS errors. |
| **WebSocket** | Open a space with realtime features. The connection to `/api/v1/organizations/.../ws` succeeds (`wss://` behind HTTPS). |
| **Health** | `GET https://<hostname>/health` returns `{"status":"ok"}`. The sample Caddyfile routes `/health` to the API; add the route explicitly if you use a custom proxy config. |
| **Previews** | If you use IDE preview: confirm `PUBLIC_API_BASE_URL` is set and the preview iframe loads. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.