diff --git a/documentation/provider/dnscale.md b/documentation/provider/dnscale.md index 8f207ad047..9774849da9 100644 --- a/documentation/provider/dnscale.md +++ b/documentation/provider/dnscale.md @@ -64,6 +64,33 @@ DNScale supports the following record types: - TLSA - TXT +## Nameservers and apex NS records + +DNScale automatically assigns nameservers (e.g. `ns1.dnscale.eu`, `ns2.dnscale.eu`) when a zone is created. These system-managed NS records at the zone apex are invisible to DNSControl — they cannot be modified or deleted. + +Third-party NS records at the apex **are** supported for multi-provider DNS setups. For example, if you use DNScale alongside another provider, you can add their nameservers as NS records and DNSControl will manage them normally. + +### Multi-provider DNS setup + +Because DNScale assigns nameservers server-side, `GetNameservers` returns an empty list. This means DNScale's nameservers are not automatically included in registrar delegation. For multi-provider setups, you must explicitly declare them using `NAMESERVER()`: + +{% code title="dnsconfig.js" %} +```javascript +var REG_NAMECHEAP = NewRegistrar("namecheap"); +var DSP_DNSCALE = NewDnsProvider("dnscale"); +var DSP_CLOUDFLARE = NewDnsProvider("cloudflare"); + +D("example.com", REG_NAMECHEAP, + DnsProvider(DSP_DNSCALE), + DnsProvider(DSP_CLOUDFLARE), + NAMESERVER("ns1.dnscale.eu"), + NAMESERVER("ns2.dnscale.eu"), + A("@", "192.0.2.1"), + A("www", "192.0.2.1"), +END); +``` +{% endcode %} + ## New domains If a domain does not exist in your DNScale account, DNSControl will automatically create it when you run `dnscontrol push`. diff --git a/providers/dnscale/auditrecords.go b/providers/dnscale/auditrecords.go index 12429dcf9c..942c5a5021 100644 --- a/providers/dnscale/auditrecords.go +++ b/providers/dnscale/auditrecords.go @@ -11,9 +11,6 @@ import ( func AuditRecords(records []*models.RecordConfig) []error { a := rejectif.Auditor{} - // DNScale automatically manages apex NS records - they cannot be modified via API - a.Add("NS", rejectif.NsAtApex) - a.Add("MX", rejectif.MxNull) // MX records must have a target a.Add("TXT", rejectif.TxtHasDoubleQuotes) // TXT records shouldn't contain unescaped double quotes diff --git a/providers/dnscale/dnscaleProvider.go b/providers/dnscale/dnscaleProvider.go index 07a3e5e394..3a554ab508 100644 --- a/providers/dnscale/dnscaleProvider.go +++ b/providers/dnscale/dnscaleProvider.go @@ -146,9 +146,13 @@ func (p *dnscaleProvider) GetZoneRecords(dc *models.DomainConfig) (models.Record curRecords := make(models.Records, 0, len(records)) for _, rec := range records { - // Skip NS records at apex - these are managed by DNScale + // Skip DNScale's own NS records at apex - these are system-managed. + // Third-party NS records at apex are kept for multi-provider DNS setups. if rec.Type == "NS" && (rec.Name == domain+"." || rec.Name == "@") { - continue + content := strings.TrimSuffix(rec.Content, ".") + if strings.HasSuffix(content, ".dnscale.eu") || strings.HasSuffix(content, ".dnscale.com") { + continue + } } // Skip SOA records if rec.Type == "SOA" { @@ -217,9 +221,20 @@ func (p *dnscaleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, cur return corrections, actualChangeCount, nil } -// GetNameservers returns an empty array. -// DNScale manages apex NS records automatically - they cannot be modified via API. -// Returning empty prevents DNSControl from trying to create NS records at apex. +// GetNameservers returns an empty array because DNScale assigns nameservers +// server-side when a zone is created. Returning empty means dnscontrol won't +// auto-generate NS records for DNScale at the apex. +// +// For multi-provider DNS setups, you must explicitly declare DNScale's +// nameservers so they are included in registrar delegation: +// +// D("example.com", REG_NAMECHEAP, +// DnsProvider(DSP_DNSCALE), +// DnsProvider(DSP_CLOUDFLARE), +// NAMESERVER("ns1.dnscale.eu"), +// NAMESERVER("ns2.dnscale.eu"), +// // ... +// ) func (p *dnscaleProvider) GetNameservers(domain string) ([]*models.Nameserver, error) { return []*models.Nameserver{}, nil }