Njalla for libdns
Manage Njalla DNS records through the libdns interfaces.
Implements RecordGetter, RecordAppender, RecordSetter, RecordDeleter,
and ZoneLister.
go get github.com/libdns/njallaCreate a token under Settings → API:
provider := njalla.Provider{APIToken: "your-njalla-api-token"}Grant the token the methods you call:
| libdns method | Njalla API methods |
|---|---|
GetRecords |
list-records |
AppendRecords |
add-record |
SetRecords |
list-records, add-record, edit-record, remove-record |
DeleteRecords |
remove-record |
ListZones |
list-domains |
Njalla's token editor offers an ACME option for the DNS-01 challenge. It
grants the four record methods, scoped to TXT records under _acme-challenge.
That scope also narrows list-records to the same prefix, so GetRecords
returns nothing outside it. DeleteRecords never lists, so challenge cleanup
keeps working.
package main
import (
"context"
"fmt"
"net/netip"
"time"
"github.com/libdns/libdns"
"github.com/libdns/njalla"
)
func main() {
provider := njalla.Provider{APIToken: "your-njalla-api-token"}
ctx := context.Background()
zone := "example.com."
added, err := provider.AppendRecords(ctx, zone, []libdns.Record{
libdns.Address{
Name: "test",
IP: netip.MustParseAddr("192.0.2.1"),
TTL: time.Hour,
},
})
if err != nil {
fmt.Printf("adding record: %v\n", err)
return
}
records, err := provider.GetRecords(ctx, zone)
if err != nil {
fmt.Printf("getting records: %v\n", err)
return
}
fmt.Printf("%d records in %s\n", len(records), zone)
if _, err := provider.DeleteRecords(ctx, zone, added); err != nil {
fmt.Printf("deleting records: %v\n", err)
}
}A, AAAA, CAA, CNAME, MX, NS, TXT, and HTTPS map to their libdns types. Anything
else, including Njalla's Redirect and Dynamic pseudo-types, arrives as
libdns.RR. You can pass any type as input.
Njalla refuses these records. The provider checks each one before sending the request, so the error names the cause.
| Record | Constraint |
|---|---|
| SRV | Zone apex only |
| SVCB | Unsupported |
| MX | Preference 0 |
| TXT | Empty values, and values containing " |
SRV. Njalla validates the last two labels of the name, so it takes
_sip._tcp and rejects the RFC 2782 form _sip._tcp.voice. Reordering to
voice._sip._tcp passes validation and publishes an invalid SRV location, so
the provider sends the name unchanged.
SVCB. libdns names a SVCB record _scheme.name, and Njalla rejects
underscore labels for this type. HTTPS carries no such prefix and works.
Trailing dots. Njalla stores hostname values as you send them and matches them byte for byte, so the provider passes them through untouched in both directions. Deleting a record tries both spellings.
TXT encoding. Values round trip as sent, including spaces, semicolons, non-ASCII text, and lengths past 255 bytes. The provider adds no escaping or chunking.
TTL. A TTL of 0 leaves the field unset, and Njalla applies 10800 seconds.
Pass a sub-second duration to ask for 0, as libdns.RR
describes.
Underscore names. Njalla refuses them for A, AAAA, and MX. TXT, CNAME, and SRV accept them.
Atomicity. Njalla has no batch endpoint, so AppendRecords, SetRecords,
and DeleteRecords apply one change at a time. When one returns an error, it
also returns the records it already changed.
DNSSEC. GetRecords omits DNSSEC records, and SetRecords does not handle
them.
Unit tests need no credentials:
go test ./...libdnstest/ is a separate module running the shared libdns conformance suite
against a real zone. It creates and deletes records, so point it at a zone you
can afford to modify.
cd libdnstest
NJALLA_API_TOKEN=your-token NJALLA_TEST_ZONE=example.com. go test -v ./...The suite skips SRV and SVCB for the reasons under Limitations.