Documentation Index
Fetch the complete documentation index at: https://engineering.unkey.com/llms.txt
Use this file to discover all available pages before exploring further.
What is Domain Connect?
Domain Connect is an open protocol that lets service providers (us) configure DNS records on a user’s domain with one click, instead of asking them to copy-paste CNAME and TXT values manually. The user gets redirected to their DNS provider (e.g. Cloudflare), approves the changes, and the records are created automatically.
How it works in our stack
User adds custom domain in dashboard
↓
ctrl API: AddCustomDomain
1. Generates CNAME target + verification token
2. Looks up domain's nameservers
3. Checks if the DNS provider supports Domain Connect
(via _domainconnect.{provider} TXT lookup)
4. If yes: builds a signed redirect URL using our private key
5. Stores domain + DC provider/URL in custom_domains table
↓
Dashboard shows "Automatic setup available" card
→ User clicks "Connect"
→ Redirected to DNS provider's consent page
→ Provider creates CNAME + TXT records
→ Redirects back to app.unkey.com/{workspace}/projects/{project}/settings
↓
Existing verification worker picks up the records (polls every 1 min)
→ ACME certificate issued
→ Frontline route created
→ Domain is live
Components
Template
The Domain Connect template defines what DNS records we need. It lives in the public Domain-Connect/templates repo as unkey.com.custom-domain.json.
| Setting | Value | Why |
|---|
providerId | unkey.com | Our provider identifier |
serviceId | custom-domain | Service identifier |
hostRequired | true | Subdomains only (apex uses manual setup) |
syncBlock | false | Synchronous flow (required by Cloudflare) |
syncPubKeyDomain | domainconnect.unkey.com | Where providers fetch our public key |
syncRedirectDomain | app.unkey.com | Where providers redirect after approval |
Records created:
| Type | Host | Value |
|---|
| CNAME | @ | %target% (full CNAME target, e.g. abc123.unkey-dns.com) |
| TXT | _unkey | unkey-domain-verify=%verificationToken% |
To update the template, open a PR against Domain-Connect/templates. Use the dc-template-linter to validate: dc-template-linter -cloudflare unkey.com.custom-domain.json.
Signing keypair
Domain Connect requires all requests to be digitally signed (RS256). We have an RSA keypair:
- Public key: published as DNS TXT records at
_dcpubkeyv1.domainconnect.unkey.com, split into two parts (p=1 and p=2) due to TXT record size limits
- Private key: stored in AWS Secrets Manager under
unkey/control as UNKEY_DOMAIN_CONNECT_PRIVATE_KEY (PEM format)
The DNS records are managed via Pulumi in infra/pulumi/projects/dns/unkey-com/main.go.
Discovery library
We use railwayapp/domainconnect-go which handles:
- DNS provider discovery (NS lookup →
_domainconnect.{provider} TXT check)
- Sync URL construction with all required parameters
- RS256 signing with our private key
The wrapper is in pkg/dns/domainconnect/discover.go.
Code
Discovery and signing live in pkg/dns/domainconnect/. The ctrl service calls Discover() during AddCustomDomain and persists the result. If no private key is configured, Domain Connect is silently disabled.
Supported DNS providers
Any provider that publishes a _domainconnect.{provider-domain} TXT record is automatically supported. As of now:
| Provider | Notes |
|---|
| Cloudflare | Template onboarded via email to domain-connect@cloudflare.com |
| Vercel DNS | Auto-discovered |
| DigitalOcean, Name.com, Hostinger, Dynadot, Namesilo | Use Cloudflare under the hood |
| IONOS | Own Domain Connect endpoint |
To onboard with a new provider, they need to pull our template from the templates repo. Some providers (like Cloudflare and Vercel) require manual registration via email.
Key rotation
If you need to rotate the signing keypair:
- Generate a new keypair:
openssl genrsa -out domain-connect-private.pem 2048
openssl rsa -in domain-connect-private.pem -pubout -outform DER \
| base64 | tr -d '\n' > domain-connect-pubkey.b64
-
Update the DNS TXT records at
_dcpubkeyv1.domainconnect.unkey.com with the new public key chunks
-
Update
UNKEY_DOMAIN_CONNECT_PRIVATE_KEY in AWS Secrets Manager (unkey/control)
-
Restart ctrl service to pick up the new key
Existing signed URLs in the database will become invalid. Users will need to re-add their domain to get a new signed URL.
Verifying the setup
Check public key is published
dig TXT _dcpubkeyv1.domainconnect.unkey.com
Verify a signature
Go to exampleservice.domainconnect.org/sig, enter:
- Key:
_dcpubkeyv1
- Domain:
domainconnect.unkey.com
- Paste the query string and signature from a generated URL
Validate template
go install github.com/Domain-Connect/dc-template-linter@latest
dc-template-linter -cloudflare unkey.com.custom-domain.json