Custom Domains
Every service gets a platform hostname automatically on deploy (`{service}.tawa.pro`, `{service}.sandbox.tawa.pro`, `{service}.uat.tawa.pro`). To put your own…
Ruleconfigure
About
Every service gets a platform hostname automatically on deploy (`{service}.tawa.pro`, `{service}.sandbox.tawa.pro`, `{service}.uat.tawa.pro`). To put your own domain in front of a service, use `tawa domain`. There are **three DNS modes**, picked by how the domain's DNS is managed and whether Cloudflare can be used.
Skill Content
This is the raw markdown that gets installed as a Claude Code rule.
# Custom Domains
Every service gets a platform hostname automatically on deploy (`{service}.tawa.pro`,
`{service}.sandbox.tawa.pro`, `{service}.uat.tawa.pro`). To put your own domain in front of a
service, use `tawa domain`. There are **three DNS modes**, picked by how the domain's DNS is
managed and whether Cloudflare can be used.
## Mode comparison
| | `--cloudflare` | `--external` | `--direct` (BYO-DNS) |
|---|---|---|---|
| Customer DNS record | CF auto-creates CNAME | CNAME → platform host | **A record → ingress LB IP** |
| TLS terminated at | Cloudflare edge | Cloudflare edge | **In-cluster (cert-manager / Let's Encrypt)** |
| Apex/root domains | CF flattening | hard (apex CNAME) | **native (A record is legal at apex)** |
| CDN / WAF / DDoS | yes | yes | no (origin hit directly) |
| `verify` checks for | n/a (auto) | `CNAME → target` | `A → ingress LB IP` |
| Needs CF zone / NS on CF | yes | yes (for TLS) | **no** |
**Default to `--cloudflare`.** Use `--direct` only when the customer cannot move their
nameservers to Cloudflare (e.g. DNS stuck at HostGator/cPanel) but can still set an A record.
## Commands
```bash
# Cloudflare-managed (auto DNS + TLS) — preferred
tawa domain add portal.example.com --cloudflare
# External CNAME (you add the CNAME; TLS still at Cloudflare)
tawa domain add portal.example.com --external
# Direct A record (BYO-DNS, TLS issued in-cluster) — for when Cloudflare is not an option
tawa domain add example.com --direct
tawa domain verify example.com
tawa deploy --prod
tawa domain list
tawa domain status example.com
tawa domain remove example.com
```
## Direct mode (BYO-DNS) — how it works
1. `tawa domain add <domain> --direct` registers the domain on the service with
`dnsProvider: 'direct'`. The `expectedTarget` is the cluster **ingress LoadBalancer IP**
(`INGRESS_TARGET` on the builder). The CLI prints the A record to add.
2. The customer adds an **A record** for the domain (and `www`, registered separately) pointing
at that IP. Apex domains work natively — no CNAME flattening needed.
3. `tawa domain verify <domain>` checks that the **A record resolves to the ingress LB IP**
(not a CNAME). On success the record is marked `dnsVerified`.
4. `tawa deploy` injects the verified direct-mode hosts into the service's **builder-managed
ingress**, adds a `tls` block (`secretName: {service}-custom-tls`) and the annotation
`cert-manager.io/cluster-issuer: {CERT_MANAGER_CLUSTER_ISSUER}` (default `letsencrypt-prod`).
5. **cert-manager** solves a Let's Encrypt **HTTP-01** challenge over port 80 (which works
precisely because the A record already lands on the ingress), issues the cert into the TLS
secret, and **auto-renews** it (~30 days before expiry). No Cloudflare, no manual cert work.
Cloudflare/external domains are **not** given an in-cluster TLS block — Cloudflare terminates
their TLS. Only `direct` domains get cert-manager TLS.
## Platform requirements for direct mode
- **cert-manager** must be installed in the cluster with a `ClusterIssuer` named to match
`CERT_MANAGER_CLUSTER_ISSUER` (default `letsencrypt-prod`, HTTP-01 solver on `ingressClassName: nginx`).
- **`INGRESS_TARGET`** on the builder must be set to the ingress LB IP. `tawa domain add --direct`
fails with `DIRECT_MODE_UNAVAILABLE` if it is unset or not an IP.
## Trade-offs / operational notes
- **LB IP coupling.** Direct-mode customers hardcode the ingress LB IP in their DNS. If that IP
ever changes (cluster migration, LB recreation) every direct-mode domain breaks. Pin a
**reserved/floating IP** for the ingress LoadBalancer before relying on this at scale.
- **No edge protection.** Direct-mode traffic hits the origin raw — no Cloudflare CDN, WAF, or
DDoS shielding, and the origin IP is public. Fine for static marketing sites; weigh it for
anything sensitive.
- **Renewal dependency.** HTTP-01 re-validates every ~60 days, so the A record must stay pointed
at the platform and port 80 must remain reachable.
- TLS for non-direct hosts on the same ingress is unaffected — nginx only force-redirects /
serves certs for hosts present in a `tls` block.
## Apex + www
Direct mode registers one record per hostname. For apex + www, add both:
```bash
tawa domain add example.com --direct
tawa domain add www.example.com --direct
tawa domain verify example.com && tawa domain verify www.example.com
tawa deploy --prod
```
Both hosts are added to the same `{service}-custom-tls` secret, so a single Let's Encrypt cert
covers them.
Install
Copy the skill content and save it to:
~/.claude/rules/tawa-custom-domains.mdComing soon via CLI:
tawa chaac install tawa-custom-domainsDetails
- Format
- Rule
- Category
- configure
- Version
- 1.0.63091
- Tokens
- ~1,133
- Updated
- 2026-06-24
platformdomainsdnstlscloudflarecert-manageringress