# Custom Domains



Every function gets a default URL at `https://{function-name}.clusterbase.dev`. You can also bring your own domain — claim and verify ownership once, then link it to a function or a VM service. TLS is provisioned automatically.

Domains are top-level objects scoped to an organization: a verified domain belongs to the org and is visible to every member, independent of any function or VM. Linking attaches a domain to a target; unlinking detaches it without forgetting the domain or re-running ownership verification.

## The Three-Step Flow [#the-three-step-flow]

The common path is `add` → set DNS → `verify` → `link`:

```bash
ccp domain add example.com
# (add the printed TXT ownership record and A routing record at your DNS provider, wait for propagation)
ccp domain verify claim_abc123
ccp domain link example.com --function fn_abc123
```

Splitting the ownership claim, verification, and attachment lets you prove you control the domain and set DNS up front, avoiding a window where the platform routes a domain that doesn't yet resolve or isn't yours.

## Start an Ownership Claim [#start-an-ownership-claim]

```bash
ccp domain add example.com
# ✓ Ownership claim created for example.com
# Claim:   claim_abc123
# Expires: 2025-01-15T12:00:00Z
#
# DNS Add these records in your DNS provider:
#
#   Type:  TXT
#   Name:  _clusterbase-verify.example.com
#   Value: clusterbase-verify=8f14e45f-ea0f-4c2b-9c1a-3d5b7e2a6f01
#
#   Type:  A
#   Name:  example.com
#   Value: 34.xx.xx.xx
#
# › After the TXT record resolves, run `ccp domain verify claim_abc123 --org-id <org>`
```

`add` starts an expiring ownership claim against an organization; the domain is not yet in inventory and does not appear in `ccp domain ls` until it's verified. The org is resolved from `cluster.toml`, the `--org-id` flag, the `CCP_ORG_ID` env var, or — when ambiguous — an interactive picker:

```bash
ccp domain add example.com --org-id org_abc123
```

`add` is print-only — it never prompts for project setup or writes `.ccp/config.json`, even when invoked outside a linked project directory.

## Verify a Claim [#verify-a-claim]

Once the TXT record from `add` has propagated, complete the claim:

```bash
ccp domain verify claim_abc123 --org-id org_abc123
# ✓ Domain example.com verified
# › It is ready to link to an app or VM.
```

`verify` resolves the TXT proof for a claim and, on success, admits the domain into the organization's inventory — this is the point at which it shows up in `ccp domain ls`. Nothing is routed yet; run `link` next.

## Link a Domain [#link-a-domain]

Attach a verified domain to a target. Pass exactly one of `--function` or `--vm`:

```bash
# Attach to a serverless function
ccp domain link example.com --function fn_abc123

# Attach to a VM service (vm_id:port)
ccp domain link api.example.com --vm vm-xyz:3000

# Inside a linked project directory: target defaults to that function
ccp domain link example.com
```

On success:

```text
✓ Domain example.com binding accepted at revision rev_2
› Routing and TLS will converge automatically
```

Every mutation is fenced against the domain's current binding revision, so `link` always applies on top of the state you last observed. A domain can only be linked to one target at a time — linking an already-attached domain replaces its desired target on the next revision. Pass `--org-id` if the domain's org can't be resolved from `cluster.toml`.

## Unlink a Domain [#unlink-a-domain]

Detach a domain from its current target. The domain stays owned and verified, so a later `link` doesn't repeat ownership verification.

```bash
ccp domain unlink example.com
# ✓ Domain example.com withdrawal accepted at revision rev_3
```

`unlink` submits a withdrawal of the current binding; routing disappears once the withdrawal converges, not immediately.

Use `unlink` when you want to move a domain to a different function or VM:

```bash
ccp domain unlink example.com
ccp domain link example.com --vm vm-xyz:3000
```

## List Domains [#list-domains]

`ls` lists every verified domain owned by the resolved organization, including domains not yet linked to a target. Domains with a pending (unverified) claim don't appear until `verify` completes. The org is resolved the same way as `add` (`cluster.toml`, `--org-id`, or interactive picker). No `cluster.toml` or `.ccp/config.json` is required:

```bash
ccp domain ls
# DOMAIN              REVISION
# example.com         rev_2
# parked.example.com  rev_0
```

Each row shows the domain and its current binding revision — the same revision used to fence `link`, `unlink`, and `rm` calls. Pass `--org-id` to list a different org.

Each domain prints on one line, sized to fit the terminal. Pass `--json` for the domain's id, exact `binding_revision`, and complete URL with no styling — the format to script against.

## Remove a Domain [#remove-a-domain]

`rm` retires the domain's ownership record outright. Confirmation is required (auto-confirms in headless mode). Works outside any project directory. Wait for an existing binding to finish withdrawing before removing — `rm` does not implicitly unlink first.

```bash
ccp domain rm example.com
# Remove domain example.com? (y/N): y
# ✓ Domain example.com removed
```

Use `unlink` instead if you only want to detach the domain — `rm` deletes the ownership record entirely.

## Verb Cheatsheet [#verb-cheatsheet]

| Verb     | Effect                                                                |
| -------- | --------------------------------------------------------------------- |
| `add`    | Start an ownership claim against the resolved org.                    |
| `verify` | Complete a claim's TXT proof; admits the domain into inventory.       |
| `link`   | Attach a verified domain to a function or VM service.                 |
| `unlink` | Withdraw from the current target; ownership + verification preserved. |
| `rm`     | Retire the domain's ownership record entirely.                        |
| `ls`     | List every verified domain in the resolved org.                       |

## Compute Services [#compute-services]

Compute services don't have a `--function` flag — point a domain at one via the underlying instance id and the service's port. Get the id from `ccp compute status`:

```bash
ccp compute status
# Compute service my-api
#
#   ID       3fd25c…
#   Status   running
#   Image    ghcr.io/me/api:v3
#   Port     8080
#   Instance vm-7e21…
```

Then link the domain at `<instance>:<port>`:

```bash
ccp domain link api.example.com --vm vm-7e21:8080
```

The `<port>` is the same one you passed to `ccp compute deploy --port`. Routing, TLS, and revision semantics are identical to function targets — see [Compute services](/docs/ccp/compute) for the full lifecycle.

## Domain Requirements [#domain-requirements]

* Must include a TLD (e.g., `example.com`, not just `example`)
* No scheme (`https://`), path (`/`), port (`:`), or spaces
* Must not start or end with a dot
* Maximum 253 characters

## Aliases [#aliases]

| Command             | Alias           |
| ------------------- | --------------- |
| `ccp domain list`   | `ccp domain ls` |
| `ccp domain remove` | `ccp domain rm` |

## How It Works [#how-it-works]

* `add` opens an expiring ownership claim against the resolved organization and returns the TXT and A records to add at your DNS provider.
* `verify` resolves the TXT proof for a claim and admits the domain into the organization's inventory, unattached.
* `link` sets the domain's desired binding to the target (function or VM service); the mutation is fenced against the domain's current binding revision and converges asynchronously.
* `unlink` sets the desired binding to withdrawn; routing and the desired target are removed once the withdrawal converges, but the domain's ownership and verification state are preserved.
* TLS certificates live on the domain (not the attachment), so an `unlink` → `link` cycle preserves the cert and avoids a re-issue.
* Deleting the function behind a linked domain (`ccp remove`) is rejected while the domain is still attached — `unlink` (or `rm`) it first. This prevents a deleted function from orphaning a live route.
