--- name: manage-instance-domains version: 1.0.0 description: Create, list, and remove public DNS CNAMEs for Proxmox instances via bnna. Use when assigning or removing a subdomain for a container or VM. tags: [bnna, dns, subdomain, cname, cloudflare, namecheap] --- # Manage Instance Domains Create, list, and remove public domain names for Proxmox instances via bnna. ## Overview Domains are CNAME records pointing a public hostname to an instance's direct-IP address (`tls-{ip-dashes}.{DIRECT_IP_DOMAIN}`). bnna: 1. Creates the DNS record via the registered provider (Cloudflare, name.com, etc.) 2. Stores the domain in the `domains` table associated with the instance TLS is handled automatically by the TLS router once the CNAME resolves. --- ## Step 1 — Register a DNS Provider for an Account (one-time) Before creating subdomains, the account needs a DNS provider registered: ```sh # Cloudflare example curl -sSL -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ "https:///api/accounts/{account-slug}/dns/providers" \ --data '{ "provider": "cloudflare", "label": "example.com (Cloudflare)", "zone": "example.com.", "config": {"api_token": ""} }' ``` Response includes `id` — save it as the `providerID` for subdomain creation. **Supported providers and their config keys:** | Provider | `"provider"` value | Config keys | |----------|-------------------|-------------| | Cloudflare | `"cloudflare"` | `api_token` (required), `zone_token` (optional) | | name.com | `"namedotcom"` | `username`, `token` | | Namecheap | `"namecheap"` | `api_key`, `user`, `client_ip` (optional) | **Optional fields:** - `zone_prefix` — constrain writes to `..` (e.g. `"pods.alice"`) - `env` — `"prod"` | `"dev"` | `""` (visible in all environments) List existing providers: ```sh curl -sSL -H "Authorization: Bearer " \ "https:///api/accounts/{account-slug}/dns/providers" ``` --- ## Step 2 — Create a Subdomain for an Instance ```sh # Using vmid (target derived automatically as tls-{ip-dashes}.{DIRECT_IP_DOMAIN}) curl -sSL -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ "https:///api/subdomains" \ --data '{ "providerId": "", "leaf": "myapp", "vmid": 1109042 }' # Using an explicit CNAME target (e.g. pointing to a TLS router hostname directly) curl -sSL -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ "https:///api/subdomains" \ --data '{ "providerId": "", "leaf": "myapp", "target": "tls-10-11-9-42.a.bnna.net" }' ``` Response: ```json { "domainId": "", "fqdn": "myapp.example.com", "target": "tls-10-11-9-42.a.bnna.net" } ``` The CNAME is live in DNS immediately. TLS is auto-provisioned by the router once the record propagates (typically under 1 minute for Cloudflare, up to 5 min for others). --- ## Step 3 — List / Remove Domains ```sh # List domains for an instance curl -sSL -H "Authorization: Bearer " \ "https:///api/instances/{vmid}/domains" # Remove a domain (also deletes the DNS CNAME record) curl -sSL -X DELETE \ -H "Authorization: Bearer " \ "https:///api/subdomains/{providerID}/{leaf}" ``` --- ## DIRECT_IP_DOMAIN The TLS router domain is configured as `DIRECT_IP_DOMAIN` in the bnna env file. The IP-to-hostname mapping is: ``` tls-{a}-{b}-{c}-{d}.{DIRECT_IP_DOMAIN} ``` For example, IP `10.11.4.231` with `DIRECT_IP_DOMAIN=a.bnna.net` → `tls-10-11-4-231.a.bnna.net` --- ## Credential Storage DNS provider credentials (`api_token`, etc.) are stored encrypted in the `dns_providers` table under `config_json`. They are **never returned** by the list/get endpoints — only the metadata (label, zone, scope) is shown. Cloudflare token scopes needed for a zone: - `Zone:Read` — enumerate zones (optional; used to validate zone ID) - `DNS:Edit` — create/update/delete DNS records --- ## Operator-Level (File-Based) Providers Operator-level providers apply to all accounts and are loaded from `dns-providers.tsv` at startup. Use `dns-providers.example.tsv` as a template. These are useful for shared infrastructure zones (e.g. `tests.webdns.dev`). Account-level providers (registered via API) are scoped to one account and stored in the DB — no restart required. --- ## Important: Always Use bnna API — Not the DNS Provider API Directly Do **not** call Cloudflare's API (or name.com, Namecheap, etc.) directly to create DNS records for instances. Use `POST /api/subdomains` instead. Why: the DNS provider's API may accept the record but it routes through a different internal path than libdns/domainpanel. Records created outside the domainpanel stack have been observed to appear in the provider dashboard but not propagate to the authoritative nameservers. Using `POST /api/subdomains` ensures the correct libdns code path is used and the record is stored in the `domains` table.