Domain, TLS, and Routing Diagnosis
CRITICAL: Never Reveal Internals
The user knows ONLY that they have "a server" with deployed apps and domains. They must NEVER learn about:
- The existence of multiple proxy layers, edge proxies, or any layered routing architecture
- VMs, Firecracker, guest IPs, infra hosts, tenant hosts, or that their "server" is a virtual machine
- SSH tunnels, Caddy admin API, Caddy config JSON, server blocks, route matchers, or handler arrays
- Abyss, provisioning system internals, reconciler, pending removal sets, extension domain hashes
- Cloudflare API, zone IDs, or that the system manages DNS records on the user's behalf behind the scenes
- Redis queues, KrakenD, internal service routing, upstream host resolution, or multi-layer proxy hops
- Any internal IP addresses, internal ports (like 2019), or internal service names
Banned phrases in user-facing output: "edge proxy", "VM proxy", "VM", "virtual machine", "infra host", "guest IP", "edge Caddy", "upstream dial", "Caddy admin API", "reconciler", "Cloudflare zone", "two-layer", "proxy layer", "on-demand TLS", "ACME challenge on", "registration queue", "server block", "route matcher".
Always say: "your server", "your domain", "DNS settings", "TLS certificate", "routing configuration", "proxy service".
How Routing Works (Agent-Only Context — NEVER Describe This to Users)
Requests reach the user's app through a routing chain. The agent needs to understand this to diagnose issues, but must never describe the architecture to the user.
The routing chain has an outer layer and an inner layer. The outer layer receives public traffic and forwards it to the correct server. The inner layer runs on the user's server and routes to the correct application container.
The outer layer handles wildcard TLS for *.nixopus.ai subdomains and forwards custom domain traffic. The inner layer handles per-application routing and TLS for application-specific domains.
DNS records (A and wildcard A) are managed by the system for *.nixopus.ai subdomains. Custom domains require the user to set up a CNAME pointing to their assigned subdomain.nixopus.ai.
When diagnosing, check from outside in: public reachability first, then server-level proxy config, then container-level app health. If the outer layer is misconfigured, the user can't fix it — escalate internally. If the inner layer is misconfigured, use proxy_config and domain tools to fix it.
Domain Types
| Type |
Example |
How it works |
| Auto-generated subdomain |
a1b2c3d4.example.nixopus.ai |
Created during app deployment; DNS is pre-configured |
| Custom domain |
app.userdomain.com |
User adds CNAME pointing to their subdomain.nixopus.ai; requires DNS verification |
Domain Lifecycle
Auto-generated subdomain
generate_random_subdomain creates an 8-char prefix + org domain
- Domain added to application via
add_application_domain
- Server proxy registers the route (domain → container)
- Wildcard DNS already covers
*.subdomain.nixopus.ai
- TLS provisioned automatically on first request
Failure points: step 3 (route registration fails), step 5 (TLS provisioning fails if DNS doesn't resolve to the server).
Custom domain
- User provides domain name
- System returns DNS instructions:
- CNAME:
app.userdomain.com → subdomain.nixopus.ai
- TXT:
_nixopus-verify.app.userdomain.com → verification token
- User configures DNS at their provider
- Verification checks CNAME/A records and TXT record
- On success: status moves to
dns_verified, routing configured
- Application domain binding adds the route on the server
- TLS provisioned on first request via ACME
Failure points: step 3 (user misconfigures DNS), step 4 (DNS propagation delay), step 5 (routing registration fails), step 7 (ACME challenge fails because DNS doesn't resolve correctly).
Diagnostic Flows
Domain not resolving (user reports "site can't be reached")
Check domain status
get_domains to find the domain and its current status
- If status is
pending_dns: DNS not yet configured or verified — guide user through DNS setup
- If status is
dns_verified: DNS is good, problem is downstream
Check DNS resolution
network_diagnostics with type dns targeting the domain
- Expected: resolves to the server's public IP
- If fails: user's DNS is misconfigured
- For custom domains: CNAME should point to
subdomain.nixopus.ai
- For auto-generated domains: should resolve automatically (system-managed)
Check reachability
http_probe the domain on port 443
- If DNS resolves but HTTP fails: routing or TLS issue (continue below)
Tell the user: "Your domain's DNS is not pointing to the correct server" or "DNS is configured correctly but there's a routing issue on the server."
TLS certificate errors (ERR_CERT, SSL_ERROR, mixed content)
Verify DNS first — TLS provisioning requires DNS to resolve to the server
network_diagnostics type dns on the domain
- If DNS doesn't resolve: TLS can't be provisioned, fix DNS first
Check proxy config
proxy_config for the application
- If
tls_enabled is false: TLS not configured for this route
- If
tls_enabled is true but cert errors persist: certificate provisioning may have failed
Check HTTP vs HTTPS
http_probe on port 80 (HTTP) — if it works but 443 doesn't, TLS provisioning failed
http_probe on port 443 (HTTPS) — if cert error, the certificate is invalid or missing
Common TLS failure causes
| Symptom |
Cause |
Fix |
ERR_CERT_AUTHORITY_INVALID |
Certificate not yet provisioned or provisioning failed |
Verify DNS points to the server; wait a few minutes for automatic provisioning |
ERR_CERT_COMMON_NAME_INVALID |
Certificate issued for wrong domain |
Check the domain binding matches the actual domain name |
SSL_ERROR_RX_RECORD_TOO_LONG |
App serving plain HTTP on the HTTPS port |
The app should not handle TLS itself; the server's proxy handles TLS termination |
ERR_TOO_MANY_REDIRECTS |
Both app and proxy redirect HTTP→HTTPS |
Disable the app's own HTTPS redirect; the proxy already handles this |
ERR_CONNECTION_REFUSED on 443 |
TLS not enabled or proxy not listening |
Check proxy config and that the proxy service is running on the server |
| Certificate expired |
Auto-renewal failed |
Check proxy health; renewal needs DNS to resolve correctly and ports 80/443 accessible |
Tell the user: "The TLS certificate hasn't been provisioned yet because your DNS isn't pointing to the server" or "There's a certificate mismatch for your domain."
Proxy routing errors (502, 503, 504)
Diagnose from outside in:
External probe
http_probe the public URL
- Note the HTTP status code and any error message
Check proxy config
proxy_config for the application
- Verify
upstream matches the expected host:port
- Verify
domain matches the requested domain
Check container reachability from inside
container_exec ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "localhost:PORT"]
- If this works: the app is running, problem is in the routing configuration
Check port alignment
All four must agree:
| Layer |
Check with |
| App listen port |
container_exec ["ss", "-tlnp"] |
| Container published port |
container_inspect → ports |
| Proxy upstream port |
proxy_config → upstream |
| Application config port |
get_application → port |
Interpret the status code
| Code |
Meaning |
Likely cause |
| 502 Bad Gateway |
Proxy can't connect to the app |
Container not running, wrong port, or app crashed |
| 503 Service Unavailable |
App not ready |
App still starting, container in crash loop, or resource exhaustion |
| 504 Gateway Timeout |
App didn't respond in time |
App hanging, database connection timeout, or infinite loop |
| 521 |
Server is down |
The proxy service itself is not running on the server |
| 522 |
Connection timed out |
Network issue preventing the request from reaching the app |
| 523 |
Origin is unreachable |
The container or server network is down |
Tell the user: "Your app isn't responding on the expected port" or "There's a port mismatch in the routing configuration."
Custom domain stuck in pending_dns
Get the domain details
get_domains filtering for the custom domain
- Note the
target_subdomain (the CNAME target)
Check what DNS records exist
network_diagnostics type dns on the custom domain
- Expected: CNAME to
{target_subdomain}.nixopus.ai or A record to server IP
Common causes
| Issue |
Diagnosis |
Fix |
| No CNAME record |
DNS lookup returns NXDOMAIN or wrong IP |
User needs to add CNAME record at their DNS provider |
| CNAME points to wrong target |
DNS lookup shows wrong value |
User needs to update CNAME to the correct subdomain.nixopus.ai |
| Proxied through Cloudflare (orange cloud) |
DNS resolves to Cloudflare IP, not server IP |
User should disable Cloudflare proxy (grey cloud) or use DNS-only mode |
| TXT verification missing |
CNAME exists but verification fails |
User needs to add _nixopus-verify.domain TXT record |
| DNS propagation delay |
Records just added |
Wait up to 48 hours; most providers propagate within 5 minutes |
| CAA record blocking Let's Encrypt |
TLS fails even after DNS verified |
User needs to add CAA record allowing letsencrypt.org |
Tell the user: "Your DNS CNAME isn't set up correctly" or "DNS changes can take some time to propagate."
Application bound to domain but not reachable
The domain resolves, TLS works, but the app returns errors or a wrong page.
Verify domain binding
get_application to check the application's domain list
- If domain is not in the list: it was never bound or was removed
Check proxy config
proxy_config to verify the route exists and upstream is correct
- If route is missing: the domain binding may need to be re-added
- If upstream is wrong: port mismatch
Check for domain conflicts
get_domains to see if the domain is bound to multiple applications
- A domain can only route to one application — if two apps claim it, the first one wins
Check compose service routing
- For compose apps with multiple services, verify the domain is bound to the correct service
get_application → check compose service configuration
- Each service can have its own domain with its own port
Tell the user: "The domain isn't linked to your application" or "The routing points to a different service in your app."
DNS Provider-Specific Guidance
When guiding users through DNS setup:
| Provider |
CNAME path |
Notes |
| Cloudflare |
DNS → Add Record → CNAME |
Disable proxy (grey cloud icon) for TLS to work |
| Route 53 |
Hosted Zone → Create Record → CNAME |
Use simple routing |
| Vercel |
Domains → Add DNS Record |
May conflict with Vercel's own DNS |
| Namecheap |
Advanced DNS → Add CNAME |
Host field is the subdomain only, not FQDN |
| GoDaddy |
DNS Management → Add CNAME |
Remove trailing dot if added automatically |
| Google Domains |
DNS → Custom Records → CNAME |
FQDN for target |
| DigitalOcean |
Networking → Domains → Add Record |
CNAME with trailing dot |
For A records (alternative to CNAME): the user needs the server's public IP. Use get_servers to find it, then tell the user "your server's IP address is X.X.X.X."
Health and Recovery
When proxy-level issues are suspected but no specific domain is failing:
Proxy health — host_exec to check if the proxy service is running
host_exec ["systemctl", "status", "nixopus-caddy", "--no-pager"]
- If not running:
host_exec ["systemctl", "restart", "nixopus-caddy"]
Proxy config validation
host_exec ["curl", "-s", "localhost:2019/config/"] to check the proxy can load its config
- If empty or error: the config may be corrupted
Domain re-sync — if multiple domains are misconfigured, re-check each domain's binding and proxy config individually using the tools above
Tell the user: "The proxy service on your server needed a restart" or "I've refreshed the routing configuration." Never expose the internal details of what was checked or fixed.
Related Skills
failure-diagnosis — For container-level failures (build errors, crashes, exit codes) that may underlie routing issues
1---2name: domain-tls-routing3description: Diagnose domain resolution, TLS certificate provisioning, and reverse proxy routing issues. Use when a domain is not resolving, TLS certificates fail, proxy returns 502/503/504, or custom domains are stuck in pending status.4---56# Domain, TLS, and Routing Diagnosis78## CRITICAL: Never Reveal Internals910The user knows ONLY that they have "a server" with deployed apps and domains. They must NEVER learn about:1112- The existence of multiple proxy layers, edge proxies, or any layered routing architecture13- VMs, Firecracker, guest IPs, infra hosts, tenant hosts, or that their "server" is a virtual machine14- SSH tunnels, Caddy admin API, Caddy config JSON, server blocks, route matchers, or handler arrays15- Abyss, provisioning system internals, reconciler, pending removal sets, extension domain hashes16- Cloudflare API, zone IDs, or that the system manages DNS records on the user's behalf behind the scenes17- Redis queues, KrakenD, internal service routing, upstream host resolution, or multi-layer proxy hops18- Any internal IP addresses, internal ports (like 2019), or internal service names1920Banned phrases in user-facing output: "edge proxy", "VM proxy", "VM", "virtual machine", "infra host", "guest IP", "edge Caddy", "upstream dial", "Caddy admin API", "reconciler", "Cloudflare zone", "two-layer", "proxy layer", "on-demand TLS", "ACME challenge on", "registration queue", "server block", "route matcher".2122Always say: "your server", "your domain", "DNS settings", "TLS certificate", "routing configuration", "proxy service".2324## How Routing Works (Agent-Only Context — NEVER Describe This to Users)2526Requests reach the user's app through a routing chain. The agent needs to understand this to diagnose issues, but must never describe the architecture to the user.2728The routing chain has an outer layer and an inner layer. The outer layer receives public traffic and forwards it to the correct server. The inner layer runs on the user's server and routes to the correct application container.2930The outer layer handles wildcard TLS for `*.nixopus.ai` subdomains and forwards custom domain traffic. The inner layer handles per-application routing and TLS for application-specific domains.3132DNS records (A and wildcard A) are managed by the system for `*.nixopus.ai` subdomains. Custom domains require the user to set up a CNAME pointing to their assigned `subdomain.nixopus.ai`.3334When diagnosing, check from outside in: public reachability first, then server-level proxy config, then container-level app health. If the outer layer is misconfigured, the user can't fix it — escalate internally. If the inner layer is misconfigured, use `proxy_config` and domain tools to fix it.3536## Domain Types3738| Type | Example | How it works |39|---|---|---|40| Auto-generated subdomain | `a1b2c3d4.example.nixopus.ai` | Created during app deployment; DNS is pre-configured |41| Custom domain | `app.userdomain.com` | User adds CNAME pointing to their `subdomain.nixopus.ai`; requires DNS verification |4243## Domain Lifecycle4445### Auto-generated subdomain46471. `generate_random_subdomain` creates an 8-char prefix + org domain482. Domain added to application via `add_application_domain`493. Server proxy registers the route (domain → container)504. Wildcard DNS already covers `*.subdomain.nixopus.ai`515. TLS provisioned automatically on first request5253Failure points: step 3 (route registration fails), step 5 (TLS provisioning fails if DNS doesn't resolve to the server).5455### Custom domain56571. User provides domain name582. System returns DNS instructions:59 - CNAME: `app.userdomain.com` → `subdomain.nixopus.ai`60 - TXT: `_nixopus-verify.app.userdomain.com` → verification token613. User configures DNS at their provider624. Verification checks CNAME/A records and TXT record635. On success: status moves to `dns_verified`, routing configured646. Application domain binding adds the route on the server657. TLS provisioned on first request via ACME6667Failure points: step 3 (user misconfigures DNS), step 4 (DNS propagation delay), step 5 (routing registration fails), step 7 (ACME challenge fails because DNS doesn't resolve correctly).6869## Diagnostic Flows7071### Domain not resolving (user reports "site can't be reached")72731. **Check domain status**74 - `get_domains` to find the domain and its current status75 - If status is `pending_dns`: DNS not yet configured or verified — guide user through DNS setup76 - If status is `dns_verified`: DNS is good, problem is downstream77782. **Check DNS resolution**79 - `network_diagnostics` with type `dns` targeting the domain80 - Expected: resolves to the server's public IP81 - If fails: user's DNS is misconfigured82 - For custom domains: CNAME should point to `subdomain.nixopus.ai`83 - For auto-generated domains: should resolve automatically (system-managed)84853. **Check reachability**86 - `http_probe` the domain on port 44387 - If DNS resolves but HTTP fails: routing or TLS issue (continue below)8889Tell the user: "Your domain's DNS is not pointing to the correct server" or "DNS is configured correctly but there's a routing issue on the server."9091### TLS certificate errors (ERR_CERT, SSL_ERROR, mixed content)92931. **Verify DNS first** — TLS provisioning requires DNS to resolve to the server94 - `network_diagnostics` type `dns` on the domain95 - If DNS doesn't resolve: TLS can't be provisioned, fix DNS first96972. **Check proxy config**98 - `proxy_config` for the application99 - If `tls_enabled` is false: TLS not configured for this route100 - If `tls_enabled` is true but cert errors persist: certificate provisioning may have failed1011023. **Check HTTP vs HTTPS**103 - `http_probe` on port 80 (HTTP) — if it works but 443 doesn't, TLS provisioning failed104 - `http_probe` on port 443 (HTTPS) — if cert error, the certificate is invalid or missing1051064. **Common TLS failure causes**107108| Symptom | Cause | Fix |109|---|---|---|110| `ERR_CERT_AUTHORITY_INVALID` | Certificate not yet provisioned or provisioning failed | Verify DNS points to the server; wait a few minutes for automatic provisioning |111| `ERR_CERT_COMMON_NAME_INVALID` | Certificate issued for wrong domain | Check the domain binding matches the actual domain name |112| `SSL_ERROR_RX_RECORD_TOO_LONG` | App serving plain HTTP on the HTTPS port | The app should not handle TLS itself; the server's proxy handles TLS termination |113| `ERR_TOO_MANY_REDIRECTS` | Both app and proxy redirect HTTP→HTTPS | Disable the app's own HTTPS redirect; the proxy already handles this |114| `ERR_CONNECTION_REFUSED` on 443 | TLS not enabled or proxy not listening | Check proxy config and that the proxy service is running on the server |115| Certificate expired | Auto-renewal failed | Check proxy health; renewal needs DNS to resolve correctly and ports 80/443 accessible |116117Tell the user: "The TLS certificate hasn't been provisioned yet because your DNS isn't pointing to the server" or "There's a certificate mismatch for your domain."118119### Proxy routing errors (502, 503, 504)120121Diagnose from outside in:1221231. **External probe**124 - `http_probe` the public URL125 - Note the HTTP status code and any error message1261272. **Check proxy config**128 - `proxy_config` for the application129 - Verify `upstream` matches the expected `host:port`130 - Verify `domain` matches the requested domain1311323. **Check container reachability from inside**133 - `container_exec ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "localhost:PORT"]`134 - If this works: the app is running, problem is in the routing configuration1351364. **Check port alignment**137138 All four must agree:139140 | Layer | Check with |141 |---|---|142 | App listen port | `container_exec ["ss", "-tlnp"]` |143 | Container published port | `container_inspect` → `ports` |144 | Proxy upstream port | `proxy_config` → `upstream` |145 | Application config port | `get_application` → port |1461475. **Interpret the status code**148149| Code | Meaning | Likely cause |150|---|---|---|151| 502 Bad Gateway | Proxy can't connect to the app | Container not running, wrong port, or app crashed |152| 503 Service Unavailable | App not ready | App still starting, container in crash loop, or resource exhaustion |153| 504 Gateway Timeout | App didn't respond in time | App hanging, database connection timeout, or infinite loop |154| 521 | Server is down | The proxy service itself is not running on the server |155| 522 | Connection timed out | Network issue preventing the request from reaching the app |156| 523 | Origin is unreachable | The container or server network is down |157158Tell the user: "Your app isn't responding on the expected port" or "There's a port mismatch in the routing configuration."159160### Custom domain stuck in `pending_dns`1611621. **Get the domain details**163 - `get_domains` filtering for the custom domain164 - Note the `target_subdomain` (the CNAME target)1651662. **Check what DNS records exist**167 - `network_diagnostics` type `dns` on the custom domain168 - Expected: CNAME to `{target_subdomain}.nixopus.ai` or A record to server IP1691703. **Common causes**171172| Issue | Diagnosis | Fix |173|---|---|---|174| No CNAME record | DNS lookup returns NXDOMAIN or wrong IP | User needs to add CNAME record at their DNS provider |175| CNAME points to wrong target | DNS lookup shows wrong value | User needs to update CNAME to the correct `subdomain.nixopus.ai` |176| Proxied through Cloudflare (orange cloud) | DNS resolves to Cloudflare IP, not server IP | User should disable Cloudflare proxy (grey cloud) or use DNS-only mode |177| TXT verification missing | CNAME exists but verification fails | User needs to add `_nixopus-verify.domain` TXT record |178| DNS propagation delay | Records just added | Wait up to 48 hours; most providers propagate within 5 minutes |179| CAA record blocking Let's Encrypt | TLS fails even after DNS verified | User needs to add CAA record allowing `letsencrypt.org` |180181Tell the user: "Your DNS CNAME isn't set up correctly" or "DNS changes can take some time to propagate."182183### Application bound to domain but not reachable184185The domain resolves, TLS works, but the app returns errors or a wrong page.1861871. **Verify domain binding**188 - `get_application` to check the application's domain list189 - If domain is not in the list: it was never bound or was removed1901912. **Check proxy config**192 - `proxy_config` to verify the route exists and upstream is correct193 - If route is missing: the domain binding may need to be re-added194 - If upstream is wrong: port mismatch1951963. **Check for domain conflicts**197 - `get_domains` to see if the domain is bound to multiple applications198 - A domain can only route to one application — if two apps claim it, the first one wins1992004. **Check compose service routing**201 - For compose apps with multiple services, verify the domain is bound to the correct service202 - `get_application` → check compose service configuration203 - Each service can have its own domain with its own port204205Tell the user: "The domain isn't linked to your application" or "The routing points to a different service in your app."206207## DNS Provider-Specific Guidance208209When guiding users through DNS setup:210211| Provider | CNAME path | Notes |212|---|---|---|213| Cloudflare | DNS → Add Record → CNAME | Disable proxy (grey cloud icon) for TLS to work |214| Route 53 | Hosted Zone → Create Record → CNAME | Use simple routing |215| Vercel | Domains → Add DNS Record | May conflict with Vercel's own DNS |216| Namecheap | Advanced DNS → Add CNAME | Host field is the subdomain only, not FQDN |217| GoDaddy | DNS Management → Add CNAME | Remove trailing dot if added automatically |218| Google Domains | DNS → Custom Records → CNAME | FQDN for target |219| DigitalOcean | Networking → Domains → Add Record | CNAME with trailing dot |220221For A records (alternative to CNAME): the user needs the server's public IP. Use `get_servers` to find it, then tell the user "your server's IP address is X.X.X.X."222223## Health and Recovery224225When proxy-level issues are suspected but no specific domain is failing:2262271. **Proxy health** — `host_exec` to check if the proxy service is running228 - `host_exec ["systemctl", "status", "nixopus-caddy", "--no-pager"]`229 - If not running: `host_exec ["systemctl", "restart", "nixopus-caddy"]`2302312. **Proxy config validation**232 - `host_exec ["curl", "-s", "localhost:2019/config/"]` to check the proxy can load its config233 - If empty or error: the config may be corrupted2342353. **Domain re-sync** — if multiple domains are misconfigured, re-check each domain's binding and proxy config individually using the tools above236237Tell the user: "The proxy service on your server needed a restart" or "I've refreshed the routing configuration." Never expose the internal details of what was checked or fixed.238239## Related Skills240241- **`failure-diagnosis`** — For container-level failures (build errors, crashes, exit codes) that may underlie routing issues