A domain provider is an optional executable that takes over custom-domain routing for managed hosting, edge proxies, load balancers, or DNS control planes.
Pilot looks for bench-domain-provider on PATH. Any deployment can provide that binary to replace Pilot's naive local DNS/domain behavior without changing Pilot code.
- Not found: Pilot uses built-in DNS verification and writes local site config.
- Found: Pilot calls the executable and trusts its exit code and stdout.
The implementation contract lives in pilot/core/adapters/domain_provider.py.
Build one executable named exactly bench-domain-provider, make it executable, and put it on PATH for the user running Pilot and the Admin service.
install -m 0755 ./my-domain-provider /usr/local/bin/bench-domain-providerProvider config such as API URLs and credentials belongs to your binary. Read it from your own files or environment.
bench-domain-provider generate-dns-records <site> <domain>
bench-domain-provider register <domain>
bench-domain-provider deregister <domain>
bench-domain-provider wildcard-domains
bench-domain-provider proxy-servers
Only generate-dns-records receives the site name. The process inherits the caller environment; Pilot sets no provider-specific variables.
This is a read-only preflight. Validate the domain and print DNS records the user can set. Do not provision proxy or DNS state here.
Stdout is a JSON object with cname and a record sets, or blank/{} if no manual records are needed.
{
"cname": [
{ "type": "CNAME", "host": "app.example.com", "value": "site.bench.example.com" }
],
"a": [
{ "type": "A", "host": "app.example.com", "value": "203.0.113.10" }
]
}Each set is a complete recipe. Add extra records to the same set when the user must create them together, such as TXT ownership records.
Exit 0 to proceed. Exit non-zero to abort and show stderr. Prefer fail-open behavior here during provider outages because register is the real gate.
Claim and provision the route for <domain>. This runs before local site creation, so failure should leave no orphaned local site.
Exit 0 only when the route is live. Print one JSON route policy to stdout.
{
"public_scheme": "https",
"origin_scheme": "http",
"client_ip_source": "x_forwarded_for"
}public_scheme tells Pilot which scheme a client uses. origin_scheme tells Pilot which scheme the provider uses for nginx.
Both scheme values must be http or https. client_ip_source must be direct, x_forwarded_for, or proxy_protocol_v2.
Use origin_scheme = "https" when Pilot must hold the certificate. Pilot gets the certificate and creates a TLS virtual host.
Use origin_scheme = "http" when the provider ends TLS. Pilot still shows HTTPS when public_scheme is https.
Pilot rejects an empty or invalid policy. Pilot then calls deregister to remove the route.
register must be idempotent. A retry must apply the route again and return the same policy.
Fail closed when the control plane is unreachable.
Remove the route and release the name. This runs after site drop and as rollback when creation fails midway.
Stdout is ignored. Always exit 0; cleanup is best effort and should not break an otherwise successful local drop.
Print wildcard patterns this host may create subdomains under.
["*.region.example.com", "*.eu.example.com"]Return blank for none. Prefer fail-soft behavior: blank stdout with exit 0 on provider outage.
Print edge proxy or load balancer addresses in front of this bench. An entry can be an IPv4 address, an IPv6 address, or a CIDR network.
["203.0.113.10", "203.0.113.11"]When entries are returned, nginx accepts proxy traffic only from those addresses and networks. It trusts their client IP data.
Return blank for direct client behavior.
Prefer fail-soft behavior here. A non-zero exit breaks nginx setup.
Use one error mechanism: exit non-zero and write the message to stderr. Pilot shows stderr to the user, so keep it free of internal detail (API URLs, request IDs, account names) - write that to your own logs instead.
echo "subdomain 'app' is already taken" >&2
exit 2Suggested exit codes:
| Code | Meaning |
|---|---|
0 |
Success, fail-open, or fail-soft no-op |
1 |
Transport or provider config failure |
2 |
Declined: taken, reserved, invalid, or at limit |
64 |
Usage error |
#!/usr/bin/env python3
import json
import sys
def main(argv):
verb = argv[1] if len(argv) > 1 else ""
if verb == "generate-dns-records" and len(argv) == 4:
site, domain = argv[2], argv[3]
return 0
if verb == "register" and len(argv) == 3:
domain = argv[2]
print(json.dumps({
"public_scheme": "https",
"origin_scheme": "http",
"client_ip_source": "x_forwarded_for",
}))
return 0
if verb == "deregister" and len(argv) == 3:
return 0
if verb == "wildcard-domains":
print(json.dumps(["*.region.example.com"]))
return 0
if verb == "proxy-servers":
print(json.dumps(["203.0.113.10"]))
return 0
print("usage: bench-domain-provider <verb> ...", file=sys.stderr)
return 64
if __name__ == "__main__":
sys.exit(main(sys.argv))./bench-domain-provider generate-dns-records mysite app.example.com
./bench-domain-provider wildcard-domains
./bench-domain-provider register taken.example.comInstall it on PATH, then test pilot new-site with a wildcard-matching name and Add/Remove Domain in the Admin UI.