ACME-only DNS provider for RcodeZero using the libdns interfaces.
This provider is intentionally minimal and only supports the DNS operations required for ACME DNS-01 challenges.
It uses the dedicated RcodeZero ACME endpoint and does not implement full DNS management.
- Implements libdns interfaces required for ACME DNS-01
- Creates and removes
_acme-challengeTXT records - Uses the dedicated RcodeZero ACME API endpoint
- Designed for predictable ACME automation
- Integration test suite included
The provider uses the ACME endpoint:
PATCH /api/v1/acme/zones/{zone}/rrsetsGET /api/v1/acme/zones/{zone}/rrsets
Official OpenAPI specification:
https://my.rcodezero.at/openapi/
Authentication is performed using an HTTP Bearer token:
Authorization: Bearer <TOKEN>
The token must have permission to manage ACME challenge records for the zone.
This provider is strictly ACME-only.
Supported:
- Record type:
TXT - Name must start with
_acme-challenge
Accepted names:
_acme-challenge_acme-challenge.servera_acme-challenge.subdomain
Rejected immediately:
A,AAAA,CNAME,MX, etc.wwwmail- Any non
_acme-challengename
Attempts to manage unsupported records will fail fast.
If different hosts request certificates:
_acme-challenge.servera.example.com_acme-challenge.serverb.example.com
These are different rrsets.
No collision occurs. Concurrent validations are safe.
This is the most common real-world scenario.
If multiple issuers request validation for:
example.com*.example.com- the same hostname simultaneously
They will all use:
_acme-challenge.example.com
The RcodeZero ACME endpoint does not support safe per-value deletion.
Current endpoint behavior:
updatewrites the TXT rrset (single value semantics)deleteremoves the whole rrset
Therefore:
- Multiple concurrent validations for the same
_acme-challenge.<name>are not safe - External locking or a single issuer is required
This matches the behavior of:
- lego’s rcodezero provider
- cert-manager webhook implementation
This provider does NOT support:
- A / AAAA / CNAME / MX records
- Arbitrary zone management
- Non-ACME DNS operations
For full DNS record management, use the RcodeZero v2 API instead.
If multiple ACME clients validate the same hostname simultaneously:
- One may overwrite the other's TXT record
- Cleanup may remove records still required by another client
Recommended approach:
- Use a single ACME issuer per domain
- Or ensure shared locking/storage between HA instances
package main
import (
"context"
"fmt"
"time"
"github.com/libdns/libdns"
rcodezero "github.com/nic-at/libdns-rcodezero-acme"
)
func main() {
provider := &rcodezero.Provider{
APIToken: "your-token-here",
BaseURL: "https://my.rcodezero.at", // optional
}
ctx := context.Background()
zone := "example.com."
record := libdns.TXT{
Name: "_acme-challenge",
Text: "challenge-token-value",
TTL: 60 * time.Second,
}
// Present challenge
_, err := provider.AppendRecords(ctx, zone, []libdns.Record{record})
if err != nil {
panic(err)
}
fmt.Println("TXT challenge record created")
// Cleanup
_, err = provider.DeleteRecords(ctx, zone, []libdns.Record{record})
if err != nil {
panic(err)
}
fmt.Println("TXT challenge record removed")
}go test ./...
go vet ./...
Integration tests require a real zone and API token.
export LIBDNSTEST_ZONE="example.com."
export LIBDNSTEST_API_TOKEN="YOUR_TOKEN"
export LIBDNSTEST_BASE_URL="https://my.rcodezero.at"
go test ./libdnstest -v
The integration suite verifies:
- TXT record creation under
_acme-challenge - Record cleanup
- Multiple FQDN validation without collision
- Correct behavior under ACME endpoint semantics
Minimal GitHub Actions workflow:
name: Go
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- run: go test ./...
- run: go vet ./...This provider is intentionally ACME-only and minimal.