CLI for Schlundtech DNS. Go, cobra. Read README.md first — it is the source of truth for what the tool does.
README.md must exist, with the flags and output described. If you change the CLI, update README.md in the same change. Verify with go run . <command> --help.gopls check <file> (or gopls definition / gopls references when you need to navigate). Fix what gopls reports; do not suppress.cmd/. Every command needs Short, Long, and a complete Example in its help text. No hand-rolled flag parsing.main.go entrypoint
cmd/ root.go, credentials.go, output.go, one file per command
internal/api/ the gateway client: XML types, tasks, record logic
internal/api is the only place that speaks HTTP to the gateway. Commands must
not build requests themselves. This is a personal tool — do not add packages
beyond this; shared helpers belong in cmd/.
go build # writes ./schlundtech-dns — run that, not `go build ./...`
go test ./...
gopls check ./... # must be clean before you stop
go build ./... deliberately writes nothing: with more than one package it only
compiles as a check. Use bare go build, or go run . <command> to skip the
build step entirely.
Read from .env in the working directory, falling back to the process
environment. godotenv parses the file, but the precedence is ours: a real
environment variable always wins, so a one-off override never needs a file
edit. Never commit .env; .env.example is the committed template. Never log
or echo a credential, and keep a test that proves the password cannot reach an
error message. Writes to DNS are public and propagate immediately — every
mutating command needs a --dry-run flag.
.opencode/skill/, .opencode/agent/, .opencode/command/ are generated from the maister submodule. Never edit them; run node tools/build-maister-opencode.mjs and node tools/verify-maister-opencode.mjs after git -C maister pull.
gateway.schlundtech.de is Schlundtech's hostname
for InterNetX's AutoDNS platform — same host as gateway.autodns.com (the IP
reverse-resolves to it). Use gateway.schlundtech.de as the default endpoint.cloud.schlundtech.com/services/backend/zone/... returns
the documented stid/status/object/data envelope with the same task
codes: S0205 zone inquire, S0210 axfr, S0225 zone list). Do not mistake that
for a public API: it authenticates by portal session cookie only — no
BasicAuth — so it is the frontend's private backend, versioned with
Schlundtech's release cycle, and unusable without driving a browser login
plus 2FA. api.autodns.com/v1 is the documented JSON API but rejects a
Schlundtech login with EF1322002 "Application could not be found.": the
BasicAuth username must be a registered application on InterNetX's side
(x-autodns-agent: PHOENIX does not change this). Build against the XML
gateway, which takes the credentials a Schlundtech customer actually has.10. Verified against the real account: zone responses
carry "owner":{"context":10,"user":"<id>"}. Context 1 is the demo system,
4 the PersonalAutoDNS live system — so a demo run needs SCHLUNDTECH_CONTEXT=1,
not 10.<token> in the auth block, XML only. The Authentication page
lists exactly two options for XML: credentials (user/context/password) and
2FA. The token is a six-digit TOTP per RFC 6238, not an opaque string, and it
expires roughly every 30 seconds. The JSON API has no token at all — it uses
BasicAuth plus an X-Domainrobot-Context header, which is a second reason not
to build on it here.HTTP 200 and this body:
response/result/msg/{code:EF00202, text:"User does not exist or password
incorrect.", object:{type:user, value:<user>}}, with the sibling
status/{code:E00000, type:error} staying generic. Never branch on the HTTP
status code — parse the body, treat result/msg/code as the real error and
result/status/type as the success flag. Non-2xx from this host is an nginx
404 (wrong path), never an application error. Whether the 3 req/s limit comes
back as an HTTP 429 or as an in-body error is still unverified.0205. Record replace = task 0202001 (rr_rem + rr_add
in one transaction, system_ns required). Zone's own NS is the first NS in
nameServers (a.k.a. virtualNameServer).{name, type, value} plus ttl, where the apex is the empty
name. nameServers is the NS set. Task 0210 returns a plain zone file.