Skip to content

NEW PROVIDER: Tencent Cloud DNS (TENCENTDNS/DNSPOD) provider and registrar#4237

Open
cylonchau wants to merge 3 commits into
DNSControl:mainfrom
cylonchau:feat/tencentdns
Open

NEW PROVIDER: Tencent Cloud DNS (TENCENTDNS/DNSPOD) provider and registrar#4237
cylonchau wants to merge 3 commits into
DNSControl:mainfrom
cylonchau:feat/tencentdns

Conversation

@cylonchau
Copy link
Copy Markdown

@cylonchau cylonchau commented May 9, 2026

Summary

Hey! This PR adds support for Tencent Cloud DNS (tencentdns also known as dnspod)for DNS hosting and Tencent Domain for registrar/nameserver management.

Tencent Cloud is one of the major cloud providers in China, and DNSPod is widely used by Chinese users and companies. I also rely on DNSControl in my own projects to manage domain records for real business services, so having native Tencent Cloud support would make DNSControl much more useful for users who need to manage domains across Cloudflare, Tencent Cloud, Aliyun, and other providers in one workflow.

This provider supports both DNS and registrar use cases:

  • DNS record management for common record types: A, AAAA, CNAME, MX, NS, TXT, SRV, and CAA
  • Registrar-side nameserver delegation for domains registered with Tencent Cloud
  • ALIAS support for Tencent DNSPod's apex CNAME behavior while keeping DNSControl validation happy
  • Zone provisioning through EnsureZoneExists
  • Provider-specific validation through RecordAuditor
  • Automatic handling of DNSPod free-tier TTL limits, where the minimum TTL is 600 seconds
  • Other own files test files and documents

The implementation uses Tencent Cloud's official Go SDK with the modern API 3.0 endpoints:

  • DNSPod API: v20210323 (Tencent still continue maintain this sdk, date only a lable, this version is latest)
  • Domain API: v20180808 (Tencent still continue maintain this sdk, date only a lable, this version is latest)

Fixes #4171

Test record

$ go mod tidy
$ go build -o dnscontrol .
$ go test -v ./providers/tencentdns/...
=== RUN   TestNativeToRecord
=== RUN   TestNativeToRecord/Basic_A_record
=== RUN   TestNativeToRecord/CNAME_record
=== RUN   TestNativeToRecord/MX_record
--- PASS: TestNativeToRecord (0.00s)
    --- PASS: TestNativeToRecord/Basic_A_record (0.00s)
    --- PASS: TestNativeToRecord/CNAME_record (0.00s)
    --- PASS: TestNativeToRecord/MX_record (0.00s)
=== RUN   TestRecordToCreateRequest
--- PASS: TestRecordToCreateRequest (0.00s)
=== RUN   TestRecordToCreateRequest_MX
--- PASS: TestRecordToCreateRequest_MX (0.00s)
=== RUN   TestNewTencentDNS
--- PASS: TestNewTencentDNS (0.00s)
=== RUN   TestNewTencentDNS_MissingCreds
--- PASS: TestNewTencentDNS_MissingCreds (0.00s)
PASS
ok      github.com/DNSControl/dnscontrol/v4/providers/tencentdns        (cached) 

add record test

$ dnscontrol % ./dnscontrol push
INFO: In dnsconfig.js NewRegistrar("tencentdns", "TENCENTDNS") can be simplified to NewRegistrar("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
INFO: In dnsconfig.js NewDnsProvider("tencentdns", "TENCENTDNS") can be simplified to NewDnsProvider("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "oomkill.com"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "oomkill.com"
******************** Domain: oomkill.com
9 corrections (tencentdns)
INFO#1: 5 records not being deleted because of NO_PURGE:
    CNAME("www.oomkill.com.", "www.oomkill.com.cdn.dnsv1.com."),
    CNAME("k8s-origin.web.oomkill.com.", "lb-fhodvg4u-64alogb8n2k1vt79.clb.sg-tencentclb.com."),
    CNAME("oomkill.com.", "oomkill.com.cdn.dnsv1.com."),
    TXT("_dnsauth.oomkill.com.", "202605010943194753vkhn1adx9mtpvjc4blyg48wfpzs0b15zy3sf7oc9qrxqq2"),
    TXT("_dnsauth.oomkill.com.", "202605010944363rbshgzl45kt0jwze9ydfasfzgijk2jarurfzm57mnigmz63sj"),
#1: + CREATE oomkill.com NS a.dnspod.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=b88d8aa1-1061-47f0-9fc6-690d76e2488f
#2: + CREATE oomkill.com NS b.dnspod.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=dd59d7e8-53e1-429c-a2fb-78c065abc933
#3: + CREATE oomkill.com NS c.dnspod.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=98c2cd8e-392d-46ca-80a3-cb61982097f0
#4: + CREATE oomkill.com MX 10 mail.yourtestdomain.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=95e5677e-cda4-47d0-a62e-7621a67e6071
#5: + CREATE blog.oomkill.com CNAME yourtestdomain.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=5410d07a-9d4f-4fea-8475-efccd5d3149f
#6: + CREATE ksss.oomkill.com A 1.2.3.4 ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=82d30e87-041e-41d9-b605-971b53d05ef2
#7: + CREATE test.oomkill.com A 5.6.7.8 ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=7b75f910-4a2a-4847-b685-6a561202aa63
#8: + CREATE verif.oomkill.com TXT "dnscontrol-test-123" ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=64ff1cbe-3fe3-469d-bbba-417517a9f4bd
#9: Update nameservers A.DNSPOD.COM,B.DNSPOD.COM,C.DNSPOD.COM -> a.dnspod.com,b.dnspod.com,c.dnspod.com
SUCCESS!
Done. 9 corrections.
completed with errors

delete record test

$ % ./dnscontrol push       
INFO: In dnsconfig.js NewRegistrar("tencentdns", "TENCENTDNS") can be simplified to NewRegistrar("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
INFO: In dnsconfig.js NewDnsProvider("tencentdns", "TENCENTDNS") can be simplified to NewDnsProvider("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "oomkill.com"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "oomkill.com"
******************** Domain: oomkill.com
9 corrections (tencentdns)
INFO#1: 5 records not being deleted because of NO_PURGE:
    CNAME("www.oomkill.com.", "www.oomkill.com.cdn.dnsv1.com."),
    CNAME("k8s-origin.web.oomkill.com.", "lb-fhodvg4u-64alogb8n2k1vt79.clb.sg-tencentclb.com."),
    CNAME("oomkill.com.", "oomkill.com.cdn.dnsv1.com."),
    TXT("_dnsauth.oomkill.com.", "202605010943194753vkhn1adx9mtpvjc4blyg48wfpzs0b15zy3sf7oc9qrxqq2"),
    TXT("_dnsauth.oomkill.com.", "202605010944363rbshgzl45kt0jwze9ydfasfzgijk2jarurfzm57mnigmz63sj"),
#1: + CREATE oomkill.com NS a.dnspod.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=04552fc0-9af2-429b-bb97-d48b56fae223
#2: + CREATE oomkill.com NS b.dnspod.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=f2c383c9-4dda-4ac4-8499-9c0a2bc26456
#3: + CREATE oomkill.com NS c.dnspod.com. ttl=300
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=83aff47e-514b-4273-9e94-8fb1080333fb
#4: + CREATE oomkill.com MX 10 mail.yourtestdomain.com. ttl=600
SUCCESS!
#5: + CREATE blog.oomkill.com CNAME yourtestdomain.com. ttl=600
SUCCESS!
#6: + CREATE ksss.oomkill.com A 1.2.3.4 ttl=600
SUCCESS!
#7: + CREATE test.oomkill.com A 5.6.7.8 ttl=600
SUCCESS!
#8: + CREATE verif.oomkill.com TXT "dnscontrol-test-123" ttl=600
SUCCESS!
#9: Update nameservers A.DNSPOD.COM,B.DNSPOD.COM,C.DNSPOD.COM -> a.dnspod.com,b.dnspod.com,c.dnspod.com
SUCCESS!
Done. 9 corrections.
completed with errors

add zone test

$ dnscontrol % ./dnscontrol preview
INFO: In dnsconfig.js NewRegistrar("tencentdns", "TENCENTDNS") can be simplified to NewRegistrar("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
INFO: In dnsconfig.js NewDnsProvider("tencentdns", "TENCENTDNS") can be simplified to NewDnsProvider("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "oomkill.com"
******************** Domain: oomkill.com
1 correction (tencentdns)
#1: Ensuring zone "oomkill.com" exists in "tencentdns"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "oomkill.com"
******************** Domain: oomkill.com
1 correction (tencentdns)
#1: + CREATE oomkill.com A 127.0.0.1 ttl=600
INFO#1: Skipping registrar "tencentdns": No nameservers declared for domain "oomkill.com". Add {no_ns: 'true'} to force
Done. 2 corrections.

$ % ./dnscontrol push   
INFO: In dnsconfig.js NewRegistrar("tencentdns", "TENCENTDNS") can be simplified to NewRegistrar("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
INFO: In dnsconfig.js NewDnsProvider("tencentdns", "TENCENTDNS") can be simplified to NewDnsProvider("tencentdns") (See https://docs.dnscontrol.org/commands/creds-json#cleanup)
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "oomkill.com"
******************** Domain: oomkill.com
1 correction (tencentdns)
#1: Ensuring zone "oomkill.com" exists in "tencentdns"
SUCCESS!
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "oomkill.com"
******************** Domain: oomkill.com
5 corrections (tencentdns)
#1: ± MODIFY-TTL oomkill.com NS ttl=(86400->300) a.dnspod.com.
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=9aa40b9a-108e-4195-9cd5-a295b745cbc1
#2: ± MODIFY-TTL oomkill.com NS ttl=(86400->300) b.dnspod.com.
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=5d6f3be1-4243-408e-98fb-07771a104053
#3: ± MODIFY-TTL oomkill.com NS ttl=(86400->300) c.dnspod.com.
FAILURE! [TencentCloudSDKError] Code=LimitExceeded.RecordTtlLimit, Message=记录的TTL值超出了限制。, RequestId=d9695fcf-b48a-4845-ba05-2926530a4f70
#4: + CREATE oomkill.com A 127.0.0.1 ttl=600
SUCCESS!
#5: Update nameservers dns1.registrar-servers.com,dns2.registrar-servers.com -> a.dnspod.com,b.dnspod.com,c.dnspod.com
SUCCESS!
Done. 6 corrections.

@TomOnTime
Copy link
Copy Markdown
Collaborator

Greetings! Thank you for your patience while we migrated to the new GitHub org.

Now that the migration is complete, please rebase. Thank you.

@cafferata cafferata added the provider-TENCENTDNS Tencent Cloud DNS provider label May 13, 2026
@cylonchau
Copy link
Copy Markdown
Author

Greetings! Thank you for your patience while we migrated to the new GitHub org.

Now that the migration is complete, please rebase. Thank you.

Hi, thanks for the reply.

Could you please confirm which branch I should rebase onto? Should I rebase against the latest main branch or is there another target branch I should use?

@TomOnTime
Copy link
Copy Markdown
Collaborator

Greetings! Thank you for your patience while we migrated to the new GitHub org.
Now that the migration is complete, please rebase. Thank you.

Hi, thanks for the reply.

Could you please confirm which branch I should rebase onto? Should I rebase against the latest main branch or is there another target branch I should use?

Excellent question! Please rebase to main.

@SukkaW
Copy link
Copy Markdown
Contributor

SukkaW commented May 14, 2026

@cylonchau I noticed that there are many FAILURE! and 记录的TTL值超出了限制 (record TTL is invalid) error here.

This error should be properly handled, either with a validator rejecting invalid TTL, or silently rewritten the TTL to the valid value before feed them to diff2 (that's what I have done with VERCEL provider).

Copy link
Copy Markdown
Contributor

@SukkaW SukkaW left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle invalid TTL, either with a validator rejectIf to reject invalid TTL, or silently re-write TTL before feeding records to diff2.

@cafferata
Copy link
Copy Markdown
Member

Optionally: since #4208 landed before this PR, would you be open to implementing RegisterCredsMetadata()? That would make Tencent Cloud DNS available in dnscontrol init. The PR has examples for simple providers like BIND and more complex ones like TransIP.

@cylonchau
Copy link
Copy Markdown
Author

@cylonchau I noticed that there are many FAILURE! and 记录的TTL值超出了限制 (record TTL is invalid) error here.

This error should be properly handled, either with a validator rejecting invalid TTL, or silently rewritten the TTL to the valid value before feed them to diff2 (that's what I have done with VERCEL provider).

@cylonchau I noticed that there are many FAILURE! and 记录的TTL值超出了限制 (record TTL is invalid) error here.

This error should be properly handled, either with a validator rejecting invalid TTL, or silently rewritten the TTL to the valid value before feed them to diff2 (that's what I have done with VERCEL provider).

Thanks your suggestion.

dnspod free plan requires a minimum TTL of 600 seconds. if user fill a number less than 600, then notice this message 记录的TTL值超出了限制 provider will reset to 600. i will fix the issue

@TomOnTime
Copy link
Copy Markdown
Collaborator

dnspod free plan requires a minimum TTL of 600 seconds. if user fill a number less than 600, then notice this message 记录的TTL值超出了限制 provider will reset to 600. i will fix the issue

Hello friend!

I'd like to make a suggestion.

This is a common question. Many providers require a minimum TTL or only specific TTL values. The best way to fix this is when converting the RecordConfig to the native record type.

In this PR, that means updating GetZoneRecordsCorrections() to iterate over existingRecords and fix any invalid TTLs.

You can look at other providers as examples. Most have a function called fixTTL().

@cylonchau
Copy link
Copy Markdown
Author

cylonchau commented May 15, 2026

Hi all, thanks for the reviews and helpful suggestions.

new submit i am fix some issue and suggestion

  • Suggestion for @SukkaW and @TomOnTime (record TTL is invalid)
  • Implementing RegisterCredsMetadata for @cafferata
  • Supports both Tencent Cloud China (cn) and International (inpl) APIs for dns record and registar.
  • Updated the provider docs.

Tested with (intl) :

Add zone and some record

create-zone.js

var REG_TENCENT = NewRegistrar("tencentdns");
var DSP_TENCENT = NewDnsProvider("tencentdns");

D("oomkill.net", REG_TENCENT, DnsProvider(DSP_TENCENT),
  NAMESERVER("ns1.dnspod.net."),
  NAMESERVER("ns2.dnspod.net."),

  A("@", "1.2.3.4", TTL(300)),
  TXT("txt", "hello")
);

preview

goldstains@goldsaintsMacBook-Pro dnscontrol % ./dnscontrol preview --config create-zone.js --creds creds.json
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "oomkill.net"
******************** Domain: oomkill.net
1 correction (tencentdns)
#1: Ensuring zone "oomkill.net" exists in "tencentdns"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "oomkill.net"
******************** Domain: oomkill.net
1 correction (tencentdns)
INFO#1: Domain "oomkill.net" provider tencentdns Error: [TencentCloudSDKError] Code=InvalidParameterValue.DomainNotExists, Message=当前域名有误,请返回重新操作。, RequestId=0bbf6948-219e-4b74-bb8d-85ca4562826b
#1: Update nameservers fns1.42.pl,fns2.42.pl -> ns1.dnspod.net,ns2.dnspod.net
Done. 2 corrections.
completed with errors

push

goldstains@goldsaintsMacBook-Pro dnscontrol % ./dnscontrol push --config create-zone.js --creds creds.json
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "oomkill.net"
******************** Domain: oomkill.net
1 correction (tencentdns)
#1: Ensuring zone "oomkill.net" exists in "tencentdns"
SUCCESS!
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "oomkill.net"
******************** Domain: oomkill.net
8 corrections (tencentdns)
#1: ± MODIFY-TTL oomkill.net NS ttl=(86400->600) a.dnspod.com.
SUCCESS!
#2: ± MODIFY-TTL oomkill.net NS ttl=(86400->600) b.dnspod.com.
SUCCESS!
#3: ± MODIFY-TTL oomkill.net NS ttl=(86400->600) c.dnspod.com.
SUCCESS!
#4: + CREATE oomkill.net NS ns1.dnspod.net. ttl=600
SUCCESS!
#5: + CREATE oomkill.net NS ns2.dnspod.net. ttl=600
SUCCESS!
#6: + CREATE oomkill.net A 1.2.3.4 ttl=600
SUCCESS!
#7: + CREATE www.oomkill.net CNAME dnscontroltest.com. ttl=600
SUCCESS!
#8: Update nameservers fns1.42.pl,fns2.42.pl -> a.dnspod.com,b.dnspod.com,c.dnspod.com,ns1.dnspod.net,ns2.dnspod.net
SUCCESS!
Done. 9 corrections.
goldstains@goldsaintsMacBook-Pro dnscontrol % 
圖片

Delete record

goldstains@goldsaintsMacBook-Pro dnscontrol % ./dnscontrol push --config create-zone.js --creds creds.json
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "oomkill.net"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "oomkill.net"
******************** Domain: oomkill.net
3 corrections (tencentdns)
#1: + CREATE txt.oomkill.net TXT "hello" ttl=600
SUCCESS!
#2: - DELETE www.oomkill.net CNAME dnscontroltest.com. ttl=600
SUCCESS!
#3: Update nameservers fns1.42.pl,fns2.42.pl -> a.dnspod.com,b.dnspod.com,c.dnspod.com,ns1.dnspod.net,ns2.dnspod.net
SUCCESS!
Done. 3 corrections.
圖片

If user filling TTL less than 600 and is free plan, will get default TTL for tencent cloud API and set to default TTL value (600)

func (c *tencentCloudClient) getMinTTL(domainName string) (uint32, error) {
	request := dnspod.NewDescribeDomainRequest()
	request.Domain = common.StringPtr(domainName)

	response, err := c.dnspodClient.DescribeDomain(request)
	if err != nil {
		return 0, err
	}
	if response.Response == nil || response.Response.DomainInfo == nil || response.Response.DomainInfo.Grade == nil {
		return defaultTTL, nil
	}
	grade := *response.Response.DomainInfo.Grade

	packageRequest := dnspod.NewDescribePackageDetailRequest()
	packageResponse, err := c.dnspodClient.DescribePackageDetail(packageRequest)
	if err != nil {
		return 0, err
	}
	if packageResponse.Response == nil {
		return defaultTTL, nil
	}

	return minTTLForGrade(grade, packageResponse.Response.Info), nil
}

default TTL value

const defaultTTL = uint32(600)

Change NS

The test domain contains sensitive information, so I have hidden part of it.

var REG_TENCENT = NewRegistrar("tencentdns");

D("xxxx.net", REG_TENCENT,
  NAMESERVER("dns7.hichina.com."),
  NAMESERVER("dns8.hichina.com.")
);

preview

goldstains@goldsaintsMacBook-Pro dnscontrol % ./dnscontrol preview --config change-ns.js --creds creds.json
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "xxxx.net"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "xxxx.net"
******************** Domain: xxxx.net
1 correction (tencentdns)
#1: Update nameservers NANCY.NS.CLOUDFLARE.COM,RUDY.NS.CLOUDFLARE.COM -> dns7.hichina.com,dns8.hichina.com
Done. 1 corrections.

push

goldstains@goldsaintsMacBook-Pro dnscontrol % ./dnscontrol push --config change-ns-recover.js --creds creds.json --debug
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "xxx.net"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "xxx.net"
******************** Domain: xxx.net
1 correction (tencentdns)
#1: Update nameservers dns7.hichina.com,dns8.hichina.com -> a.dnspod.com,b.dnspod.com,c.dnspod.com
SUCCESS!
Done. 1 corrections.
圖片

Registrar operations require the correct Tencent Cloud site. Use site: "cn" for Tencent Cloud China and site: "intl" for Tencent Cloud International; otherwise, nameserver updates may fail because the wrong registrar API will be called.

For example, user will enconter.

goldstains@goldsaintsMacBook-Pro dnscontrol % ./dnscontrol push --config change-ns.js --creds creds.json --debug
CONCURRENTLY checking for 0 zone(s)
SERIALLY checking for 1 zone(s)
Serially checking for zone: "xxx.net"
CONCURRENTLY gathering records of 0 zone(s)
SERIALLY gathering records of 1 zone(s)
Serially Gathering: "xxx.net"
******************** Domain: xxx.net
1 correction (tencentdns)
#1: Update nameservers a.dnspod.com,b.dnspod.com,c.dnspod.com -> dns7.hichina.com,dns8.hichina.com
FAILURE! tencent domain batch operation 104311325 failed for xxx.net: 该域名不属于当前账号,无法执行操作
Done. 1 corrections.
completed with errors

…registrar

Add support for Tencent Cloud DNS (encentdns/dnspod) as both DNS provider
and registrar using the official Tencent Cloud API 3.0.
Features:
- DNS Provider: Full CRUD for A, AAAA, CNAME, MX, NS, TXT, CAA, SRV records
- Registrar: Nameserver delegation management at the registry level
- Apex CNAME support: Seamlessly maps ALIAS records to Tencent's apex CNAME
- Zone management: Supports automatic zone creation (EnsureZoneExists)
- Zone listing: Supports the get-zones command for easy migration
- Incremental updates: Full support for NO_PURGE and IGNORE via diff2 engine
Technical details:
- Based on tencentcloud-sdk-go (API 3.0)
- Implements RecordAuditor to comply with DNSControl v4 requirements
- Handles free-tier limitations (TTL minimum 600s) automatically
Documentation and CI/CD configuration (GitHub Actions profiles) included.
Copy link
Copy Markdown
Member

@cafferata cafferata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the feedback and adding the optional RegisterCredsMetadata()! Two small inline comments, otherwise LGTM from my side.

Comment thread .github/CODEOWNERS Outdated
providers/unifi @zupolgec
providers/vercel @SukkaW
providers/vultr @pgaskin
providers/tencentdns @cylonchau
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entry is appended at the bottom (after vultr), breaking the alphabetical sort. Running generate-all.sh should fix this automatically, or you can move it manually between softlayer and transip.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, has been run to refresh some generated files.

Comment thread documentation/provider/tencentdns.md
- Implement RegisterCredsMetadata for dnscontrol init
- Rewrite low TTLs before diff2 using dnspod package limits
- Add cn/intl site selection for dns and registrar API
- Poll registrar batch operations and surface failures
- Add docs and focused unit tests
@cylonchau
Copy link
Copy Markdown
Author

thanks for @cafferata, The new commit have been fixed, and generate-all.sh has been run to refresh some generated files.

Comment on lines +66 to +68
if rc.Type == "ALIAS" {
req.RecordType = new("CNAME")
}
Copy link
Copy Markdown
Contributor

@SukkaW SukkaW May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better add a code comment about why converting ALIAS to CNAME here (ALIAS('@') instead of CNAME('@') for CNAME flattening only).

DNSPod itself doesn't support ALIAS record type in any way, you need to document this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I updated the code comment.

const defaultTTL = uint32(600)

var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Can(),
Copy link
Copy Markdown
Contributor

@SukkaW SukkaW May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better document that canUseALIAs is Can only because this is required to work with CNAME Flattening feature (writing ALIAS('@') instead of CNAME('@')). DNSPods itself doesn't support ALIAS record type at all.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I updated the CanUseAlias note

Copy link
Copy Markdown
Contributor

@SukkaW SukkaW May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document how to use ALIAS('@') in the provider docs:

  • If you want to add CNAME at @
  • Configure CNAME flattening at the DNSPod dashboard manually
  • Write ALIAS('@') to dnsconfig.js
  • Explain why write ALIAS('@'). DNSControl requires this because it considers CNAME('@') invalid.
  • Under the hood, this provider overwrites ALIAS to CNAME
  • Explain that DNSPod doesn't support ALIAS at all, all CNAME flattening configurations must be done via the dashboard for now.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I updated the docs to clarify how to use ALIAS with the tencentdns provider.

- Add code comments explaining that DNSPod does not support a native ALIAS record
- Document how to use ALIAS for DNSPod CNAME flattening.
@cylonchau cylonchau requested a review from SukkaW May 16, 2026 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

provider-request provider-TENCENTDNS Tencent Cloud DNS provider

Development

Successfully merging this pull request may close these issues.

Add Tencent Cloud DNS

5 participants