Skip to content

Contributing

provider/ Core contract: Provider, RRset, RRSetChange, Capabilities,
optional interfaces, registry, name canonicalization
provider/<name>/ One adapter per provider
core/ Models + event-sourced aggregates (ORM++), migrations
app/ Business functions (zones, Sync Groups, Templates,
sources, DynDNS, certificates, users, teams …)
httpapi/ Fiber v3 API + registration layer for EE/Cloud
server/ Reusable wiring (server.Run + options)
auth/ acme/ mail/ Passwords/TOTP, ACME issuer (lego), SMTP delivery
jobs/ session/ Scheduler (drift, certificates, reminders), sessions
demo/ In-memory provider + demo seed (DNSE_DEMO=on)
ui/ webui/ Lit/Shoelace SPA + go:embed of the build
docs/ This documentation
Terminal window
make test # Go tests (race) + gofmt + vet + UI tests
go test -race ./...
gofmt -l .
go vet ./...

The interface lives pre-built in the repo (webui/dist) so that go:embed works. After changes to ui/, the build belongs in the same commit — CI checks this:

Terminal window
cd ui && npm ci && npm run build
Terminal window
docker compose up -d
PDNS_TEST_ENDPOINT=http://localhost:8081 PDNS_TEST_API_KEY=test-secret \
go test -race -run Integration -v ./provider/powerdns/

Without the two variables, these tests are skipped. CI runs the same tests against a PowerDNS service container.

An adapter lives under provider/<name>/ and implements:

MethodTask
Zoneslist the account’s zones (for onboarding)
RRsetsread the actual state of a zone
ApplyChangeswrite and delete record sets
Healthverify credentials (connection test)

It also includes a Descriptor (display name, credential fields, portal and docs links, short guide) and the Capabilities. Errors are mapped to the package’s sentinel values (ErrUnauthorized, ErrZoneNotFound, ErrInvalidChange, ErrUnreachable) — the account states in the interface hang off these.

desec (atomic bulk update) and digitalocean (individual records with priority/weight) make good starting templates.

  • Default branch: develop
  • main: protected, only via merge request with a green pipeline
  • Conventional Commits in German

CI builds every module without go.work. Whoever adds a new dependency therefore runs GOWORK=off go mod tidy — otherwise the checksums only end up in the local go.work.sum and the pipeline fails.