Drop the local Cursor rules and point docs at the Workspace.

Issues live in the ExApp Workspace; this repo keeps the Library spec and BIND guide.
This commit is contained in:
Konrad Neitzel
2026-08-25 14:00:17 +02:00
parent eff7637f86
commit afbe268cb5
7 changed files with 11 additions and 650 deletions
-40
View File
@@ -1,40 +0,0 @@
# go-dns — Product Backlog
Potential features for future change efforts. **Not a commitment** — items move into `docs/changes/` when scoped and prioritized.
Align with [project-specification.md](project-specification.md): **`godns`** package, TSIG auth, typed RR builders, ASCII-only names, BIND testcontainer integration tests.
**Priority legend:** P0 = core library, P1 = soon after core, P2 = enhancement, P3 = later / optional
---
## Zone transfer
| Priority | Item |
|----------|------|
| P2 | **IXFR (incremental zone transfer)** — RFC 1995; apply deltas to a prior `Zone` snapshot; SOA serial baseline; fallback to AXFR when IXFR unavailable; extend `TransferResult` and BIND guide |
---
## Internationalization
| Priority | Item |
|----------|------|
| P3 | **IDNA / punycode** — Unicode domain labels; normalization beyond ASCII-only validation |
---
## Record types & API
| Priority | Item |
|----------|------|
| P2 | Additional typed RR builders beyond initial set (section 4 of setup spec) |
| P3 | **Zone diff** helper — compare two `Zone` values for automation |
---
## Infrastructure
| Priority | Item |
|----------|------|
| P3 | Example CLI or small tool wrapping `godns` (library remains primary deliverable) |
@@ -1,41 +0,0 @@
# Implementation decisions: Initial project setup and DNS operation catalog
## Context
- **Change folder:** `docs/changes/00001-setup-project/`
- **Branch:** `feature/C-00001-setup-project`
- **Scope:** Documentation and scaffolding only (no Go source)
## Decisions
| # | Area | Decision | Rationale |
|---|------|----------|-----------|
| 1 | Git baseline | Baseline commit pushed before implementation edits | Per `implement-change` skill and plan step 0 |
| 2 | BIND guide | Single `docs/bind-tsig-configuration.md` with all spec section 9 topics | Primary deliverable of this change |
| 3 | Example secret | Placeholder Base64 in guide (not a real key) | Illustration only; guide warns against committing secrets |
| 4 | `.gitignore` | Added `.env` and `.env.*` | Spec requirement 10; was missing from specify phase |
| 5 | Library code | No `doc.go` or placeholder `godns` package | Plan explicitly excludes `*.go`; first API change adds packages |
## Deviations from plan
- None.
## Follow-ups / risks
- **`go work sync` at workspace root** may fail if sibling module `log-analyzer` lacks `go.mod` (pre-existing workspace issue). AC-1 verification should use `go-dns/go.mod` directly or fix workspace separately.
- **Gitea repo** was available; baseline and feature branch push succeeded.
- **verify-agent:** Run full AC-1 through AC-10 checklist from `plan.md`.
- **Next change:** Implement `package godns` wire protocol (RFC 2136 UPDATE, AXFR, TSIG) per backlog/spec.
## Files touched
| Path | Action |
|------|--------|
| `docs/bind-tsig-configuration.md` | created |
| `docs/project-specification.md` | modified — live BIND guide links |
| `README.md` | modified — BIND guide link |
| `.gitignore` | modified — `.env` patterns |
| `.cursor/rules/project-specification.mdc` | modified — guide path wording |
| `docs/changes/00001-setup-project/impl-decisions.md` | created |
Git: `git init`, remote `origin`, branch `feature/C-00001-setup-project`, baseline commit `47f1744`.
-210
View File
@@ -1,210 +0,0 @@
# Plan: Initial project setup and DNS operation catalog
## Summary
Complete **go-dns** project scaffolding and documentation alignment for change **00001-setup-project**: git repository and feature branch, verify workspace registration and existing specify-phase artifacts (`go.mod`, Cursor rule, project spec, README, backlog, `.gitignore`), and **create** `docs/bind-tsig-configuration.md` per spec section 9. Preserve the full DNS operation catalog in `spec.md` (sections 1–10) without rewriting it.
This change is **setup + documentation only**. It does **not** implement RFC 2136, AXFR, TSIG signing, typed RR builders, zone data types in Go, unit tests, BIND testcontainer integration tests, CLI, or Docker.
## Prerequisites
- Approved `spec.md` in this change folder.
- Workspace root `go.work` already lists `./go-dns` and declares `go 1.26.4` — verify only; no edit expected unless sync fails.
- Specify-phase artifacts already exist — **verify and preserve**; apply **minimal fixes** only when misaligned with `spec.md` (broken links, missing topics, `.env` in `.gitignore`).
- **Git (implement-agent, before other changes):** Per `implement-change` skill and go-storywriter **00001** pattern:
- `git init` in `go-dns/` if `.git` is missing.
- Add remote `origin` → `ssh://git@gitea.neitzel.de:2222/konrad/go-dns.git` if absent.
- Checkout branch `feature/C-00001-setup-project`.
- Baseline commit (`chore: baseline before change C-00001-setup-project`) and `git push -u origin feature/C-00001-setup-project`.
- **Risk:** Gitea repository `konrad/go-dns` may need to be created on the server before push succeeds; if push fails, stop and report — do not proceed until resolved.
- Go toolchain **1.26.4** available locally.
- No third-party Go dependencies for this change (no application `.go` files).
## Implementation steps
### Step 0 — Git workspace setup
Run from `go-dns/` per Prerequisites. Establish the feature branch and baseline commit **before** creating or modifying scaffolding files in steps 1–11.
### Step 1 — Verify Go module
1. Confirm `go.mod` exists with:
- `module gitea.neitzel.de/konrad/go-dns`
- `go 1.26.4`
2. No `require` block expected (no dependencies until a future library change).
3. Fix only if module path or Go version diverges from spec.
### Step 2 — Verify workspace registration
1. From workspace root: confirm `grep -q go-dns go.work`.
2. Run `go work sync` from workspace root.
3. If sync fails, resolve `go.mod` issues from step 1 and retry.
### Step 3 — Verify Cursor project rule
Confirm `.cursor/rules/project-specification.mdc`:
- Front matter: `alwaysApply: true`, short `description` for go-dns.
- Body: pointer to `docs/project-specification.md` as authoritative spec.
- Brief stack summary: Go library, module path, **`godns`** package at module root, BIND target, TSIG, AXFR-only transfer, typed RR builders (future), BIND testcontainer (future integration tests).
- Note that project `.cursor` overrides workspace defaults when they conflict.
Follow patterns from `go-ollama/.cursor/rules/project-specification.mdc` and `log-analyzer/.cursor/rules/project-specification.mdc`. **Minimal edits only** if topics are missing or contradict resolved decisions.
### Step 4 — Verify project specification document
Confirm `docs/project-specification.md` (English) covers all required topics:
| Section | Content |
|---------|---------|
| **Overview** | Library for RFC 2136 dynamic updates and **AXFR** zone transfer against BIND; TSIG auth; typed zone data and RR builders (future). |
| **Module metadata** | Path `gitea.neitzel.de/konrad/go-dns`, public package **`godns`**, Go 1.26.4, Gitea remote URL. |
| **Target environment** | BIND 9.x; UPDATE with **automatic TCP fallback**; AXFR over TCP; TSIG required; **ASCII-only** names in v1; **no IXFR** in v1. |
| **Operation surface** | Pointer to change **00001** `spec.md` as authoritative catalog (sections 1–10). |
| **Zone data model** | Logical types `Record`, `RRSet`, `Zone`, `UpdateResult`, `TransferResult`. |
| **BIND configuration** | Pointer to `docs/bind-tsig-configuration.md` (after step 8). |
| **API style** | Typed builders per RR type; no generic rdata string as primary API. |
| **Change workflow** | `docs/changes/` efforts; backlog at `docs/backlog.md`. |
| **Tests (future)** | Unit `go test ./...`; integration `go test -tags=integration ./...` with **BIND testcontainer** — not implemented in this change. |
| **Package layout (planned)** | `package godns` at module root; `internal/` for wire protocol (future). |
After step 8, ensure the BIND section links to the live `docs/bind-tsig-configuration.md` path (not only “written during implementation”).
### Step 5 — Preserve backlog
1. Confirm `docs/backlog.md` exists with a prioritized **IXFR** entry under Zone transfer (P2 per specify phase).
2. **Do not** recreate or substantially rewrite; minimal link fixes only if needed.
### Step 6 — Verify `.gitignore`
Confirm exclusions for:
- Go binaries and test artifacts (`*.test`, `*.out`, project binary names)
- IDE/OS junk (`.idea/`, `.vscode/`, `.DS_Store`)
- Local secrets: `testconfig/local.toml` (already present); add `.env` / `.env.*` if missing per spec requirement 10
### Step 7 — Verify project README
Confirm `README.md` (English):
- Describes library intent: dynamic updates (RFC 2136) and **AXFR** zone transfer
- States early setup status and links to `docs/changes/00001-setup-project/spec.md`
- Documents module path and **`godns`** import name
- Links to `docs/project-specification.md` and `docs/backlog.md`
- After step 8, add link to `docs/bind-tsig-configuration.md`
No build/run instructions for a binary (library-only; no `cmd/` in this change).
### Step 8 — Create BIND TSIG configuration guide
Create `docs/bind-tsig-configuration.md` (English). This is the **primary new deliverable** of this change. Content MUST satisfy spec section 9:
| # | Requirement | Guide content |
|---|-------------|---------------|
| 1 | **Define the key** | `key "name" { algorithm ...; secret "..."; };` in `named.conf` or included file. State explicitly that **key generation is out of library scope**; operators use `tsig-keygen`, `rndc tsig-keygen`, or secret management. |
| 2 | **Zone ACLs** | For `zone "example.com" { ... }`: **`allow-update { key "name"; };`** and **`allow-transfer { key "name"; };`** for the same TSIG key. |
| 3 | **Stricter policy** | Mention **`update-policy`** as optional supplement or alternative to broad `allow-update`. |
| 4 | **Reload / validation** | `named-checkconf`, `rndc reload`, verify with `nsupdate -y` and TSIG-signed AXFR (`dig -y` or equivalent). |
| 5 | **Security notes** | Restrict key to needed zones; do not expose AXFR publicly; separate read vs write keys when policy requires (library may use different keys per client). |
| 6 | **Minimal example** | Complete `named.conf` fragment: `key` block + `zone` block with `allow-update` and `allow-transfer` for one key (expand the illustrative fragment in spec section 9). |
Suggested structure: introduction → key definition → zone configuration → optional `update-policy` → reload and verification → security → full example → related docs (link to `spec.md`, `project-specification.md`).
### Step 9 — Cross-document alignment pass
After the BIND guide exists:
1. Update `docs/project-specification.md` BIND section with a direct link to `bind-tsig-configuration.md`.
2. Update `README.md` documentation links if not done in step 7.
3. Confirm `.cursor/rules/project-specification.mdc` still accurately references the guide path.
4. Re-read resolved decisions in `spec.md` — no contradictions (godns, typed builders deferred, AXFR-only, BIND testcontainer deferred, TCP fallback deferred, ASCII-only deferred, no key generation).
### Step 10 — Change documentation layout
1. Confirm `docs/changes/` exists.
2. This folder `docs/changes/00001-setup-project/` contains `spec.md` (specify phase) and `plan.md` (plan phase).
3. `verification.md` and `changes.md` are **out of scope** for implement phase (verify-agent / document-agent).
### Step 11 — Local verification (implement-agent quick check)
From workspace root and `go-dns/`:
```bash
cd /Users/konrad/Projects/go && go work sync
test -f go-dns/docs/bind-tsig-configuration.md
```
No `go test` or `go build` — no Go source in this change.
## Files to create or modify
| Path | Action | Purpose |
|------|--------|---------|
| `.git/` + branch `feature/C-00001-setup-project` | create (implement-agent, step 0) | Git repo, remote, baseline push |
| `go.mod` | verify/preserve | Module identity and Go version (specify phase) |
| `go.work` (workspace root) | verify only | Already lists `./go-dns` |
| `.cursor/rules/project-specification.mdc` | verify/preserve | Cursor always-on project rule |
| `docs/project-specification.md` | verify/modify | Authoritative project spec; link BIND guide after step 8 |
| `docs/backlog.md` | verify/preserve | IXFR backlog entry |
| `.gitignore` | verify/modify | Go artifacts + secret paths (add `.env` if missing) |
| `README.md` | verify/modify | Project overview; link BIND guide after step 8 |
| `docs/bind-tsig-configuration.md` | **create** | BIND `allow-update` / `allow-transfer` TSIG guide |
| `docs/changes/00001-setup-project/spec.md` | verify/preserve | Full operation catalog — **do not rewrite** |
| `docs/changes/00001-setup-project/plan.md` | create | This plan (plan phase) |
**Not created in this change:** any `*.go` files, `internal/`, `cmd/`, unit tests, integration tests (`*_integration_test.go`), `docker-compose.yml`, `frontend/`, `verification.md`, `changes.md`.
## Dependencies and risks
| Risk | Mitigation |
|------|------------|
| Gitea remote repo does not exist | Create `konrad/go-dns` on Gitea before baseline push; implement-agent stops on push failure |
| `go work sync` fails | Ensure valid `go.mod` before sync |
| Scope creep (RFC 2136, AXFR, TSIG code, testcontainers) | Strictly follow spec out-of-scope; documentation and scaffolding only |
| Rewriting `spec.md` operation catalog | Preserve specify-phase content; verify AC-6 only |
| BIND guide incomplete | Use spec section 9 checklist as implement-agent acceptance gate before handoff |
| Doc drift between README, project-spec, cursor rule | Step 9 alignment pass |
**External dependencies:** None (no Go packages, no Docker for this change).
**Integration tests:** Not applicable in this change. Future library changes will use **BIND testcontainer** via testcontainers-go with build tag `integration` and command `go test -tags=integration ./...` (Docker required in CI) per workspace `integration-testing.mdc` and spec resolved decisions. Document in project-spec and cursor rule only — **do not implement** testcontainers or BIND image wiring here.
**Future changes (out of scope, for traceability):**
| Topic | When |
|-------|------|
| `package godns` public API and `internal/` wire protocol | After setup verified |
| Typed RR builders (A, AAAA, MX, …) | Dedicated change |
| Zone data types in Go (`Record`, `Zone`, …) | With or after first protocol change |
| RFC 2136 UPDATE + automatic TCP fallback | Library implementation change |
| AXFR + TSIG signing | Library implementation change |
| BIND testcontainer integration tests | After minimal library surface exists |
| IXFR | Backlog P2 — `docs/backlog.md` |
## Test strategy
| Criterion | Verification method |
|-----------|----------------------|
| **AC-1** | From workspace root: `grep -q go-dns /Users/konrad/Projects/go/go.work` exits 0; `cd /Users/konrad/Projects/go && go work sync` exits 0 |
| **AC-2** | `grep -E '^module gitea.neitzel.de/konrad/go-dns$' /Users/konrad/Projects/go/go-dns/go.mod` and `grep -E '^go 1.26.4$' /Users/konrad/Projects/go/go-dns/go.mod` |
| **AC-3** | `test -f go-dns/.cursor/rules/project-specification.mdc`; `grep -q 'alwaysApply: true' go-dns/.cursor/rules/project-specification.mdc`; `grep -q 'docs/project-specification.md' go-dns/.cursor/rules/project-specification.mdc` |
| **AC-4** | `test -f go-dns/docs/project-specification.md`; `rg -i 'gitea.neitzel.de/konrad/go-dns|godns|BIND|RFC 2136|AXFR|TSIG|Record|RRSet|Zone|UpdateResult|TransferResult|bind-tsig-configuration' go-dns/docs/project-specification.md` returns matches for module path, BIND target, protocols, TSIG, zone model, and BIND guide reference |
| **AC-5** | `test -f go-dns/README.md`; `rg -i 'update|AXFR|gitea.neitzel.de/konrad/go-dns|godns|00001-setup-project/spec' go-dns/README.md` |
| **AC-6** | `test -f go-dns/docs/changes/00001-setup-project/spec.md`; manual/doc review — sections 1–10 present: session/auth, updates, prerequisites, RR types, update submission, AXFR (no IXFR as v1 feature), zone data model, auxiliary, BIND guide requirements, non-goals. Command aid: `rg -n '^### [0-9]+\.' go-dns/docs/changes/00001-setup-project/spec.md` shows numbered sections 1–10 under “DNS operations” and section 9 “BIND configuration” / section 10 “Explicit non-goals” |
| **AC-7** | **(a)** Spec requirement: `rg -i 'bind-tsig-configuration|allow-update|allow-transfer' go-dns/docs/changes/00001-setup-project/spec.md` matches. **(b)** After implement: `test -f go-dns/docs/bind-tsig-configuration.md`; content checklist — `rg -i 'allow-update|allow-transfer|tsig-keygen|named-checkconf|rndc reload|update-policy|hmac-sha256' go-dns/docs/bind-tsig-configuration.md`; manual review confirms minimal `key` + `zone` `named.conf` example and security notes |
| **AC-8** | `test -f go-dns/docs/backlog.md`; `rg -i 'IXFR' go-dns/docs/backlog.md` |
| **AC-9** | `test -f go-dns/docs/changes/00001-setup-project/spec.md`; `test -f go-dns/docs/changes/00001-setup-project/plan.md`; after verify/document phases: `verification.md` and `changes.md` in same folder |
| **AC-10** | `rg -i 'godns|typed builder|AXFR|BIND testcontainer|TCP fallback|ASCII|git init' go-dns/docs/changes/00001-setup-project/spec.md` matches [Resolved decisions](#resolved-decisions) table |
**Integration test command:** N/A for this change (BIND testcontainer deferred to a future change).
**Unit test command:** N/A — no Go source files; `go test ./...` has nothing to run until a later change adds packages.
**Docker required:** No (for this change).
## Rollback notes
- **Code/docs:** Revert commits on `feature/C-00001-setup-project` or remove files created in this change; preserve `docs/changes/` history if useful.
- **Workspace:** `go.work` entry for `./go-dns` can remain (harmless) or be removed manually if fully abandoning the project.
- **Remote:** No destructive git operations; branch can be deleted on Gitea manually if the effort is cancelled after push.
- **Runtime:** No deployed services or containers for this change.
-305
View File
@@ -1,305 +0,0 @@
# Initial project setup and DNS operation catalog
## Goal
Bootstrap **go-dns** as a new Go module in the workspace and **define the operation surface** the library will support:
- **Dynamic DNS updates** against BIND-style authoritative nameservers (RFC 2136, `nsupdate` semantics)
- **Zone transfer** — full zone read via **AXFR** only (IXFR deferred — see [backlog.md](../../backlog.md))
- **TSIG key authentication** for all of the above (callers supply existing keys; key generation is out of scope)
- **Zone data structures** to parse transfer/update responses and hold complete zone contents in memory
- **BIND configuration guide** describing how a TSIG key must be configured on the server for updates and zone transfers
This change establishes project identity, documentation layout, and an agreed **catalog of supported operations and data models**. It does **not** implement the library API or wire protocol yet.
## Background
Authoritative DNS on BIND is commonly manipulated in two ways over the wire:
1. **Dynamic update** — add, change, or remove resource records without editing zone files (`nsupdate`, RFC 2136).
2. **Zone transfer** — read the full zone via **AXFR** (RFC 5936). Incremental transfer (**IXFR**) is **out of scope** for the initial library; see [backlog.md](../../backlog.md).
Both paths are authenticated on production servers with **TSIG** shared secrets (RFC 2845, RFC 8945): the client signs requests; BIND verifies the signature and applies ACLs (`allow-update`, `allow-transfer`) for that key.
**go-dns** will be an importable Go library so other projects (automation, controllers, internal tools) can perform updates and zone transfers programmatically — with typed data structures, validation, and testability — instead of shelling out to `nsupdate` or `dig axfr`.
Sibling workspace projects (`go-ollama`, etc.) follow: module path `gitea.neitzel.de/konrad/<project>`, project spec under `docs/`, and `.cursor/rules/project-specification.mdc`.
## DNS operations — supported interface
The following catalog describes **what** the library interface must be able to express. **How** (exact Go type names, function signatures, error shapes) is deferred to later changes. Wording follows RFC 2136, RFC 5936, and common BIND 9 / `nsupdate` usage.
### 1. Session, authentication, and transport
Operations that establish **where**, **how**, and **as whom** requests are sent.
| Operation | Description |
|-----------|-------------|
| **Set server** | Target authoritative nameserver (hostname or IP, optional port; default 53). |
| **Set zone** | Origin zone name (UPDATE question section; AXFR QNAME). |
| **Configure TSIG key** | Authenticate with an existing shared secret: key name, algorithm (e.g. hmac-sha256), and secret (Base64). Callers **provide** keys; the library does **not** generate keys. |
| **Choose transport** | **UDP** first for UPDATE where applicable; **automatic TCP fallback** on truncation (TC bit) or oversize; **AXFR always uses TCP**. |
| **Timeout / context** | Caller-controlled deadline and cancellation (maps to Go `context.Context`). |
Authentication model:
- **TSIG is mandatory** for the documented happy path (updates and zone transfers).
- The same key may be used for both UPDATE and AXFR **when BIND ACLs permit** (see [BIND configuration](#bind-configuration-for-tsig-keys) below).
- **Out of scope:** key generation (`tsig-keygen`, `rndc tsig-keygen`, etc.), GSS-TSIG, SIG(0), unauthenticated UPDATE or AXFR.
### 2. Update actions (zone mutations)
Operations that modify RRsets in the zone — the **Update** section of RFC 2136.
| Operation | Description | nsupdate analogue |
|-----------|-------------|-------------------|
| **Add RR** | Insert one or more RRs at a name. If the RRset exists, new RRs are merged in; duplicates may be rejected or ignored per server policy. | `add name [ttl] [class] type rdata` |
| **Delete RR (exact)** | Remove RRs matching name, class, type, and **exact rdata**. Other RRs at the same name/type remain. | `delete name [ttl] [class] type rdata` |
| **Delete RRset** | Remove **all** RRs with the given name, class, and type (rdata wildcard). | `delete name [ttl] [class] type` |
| **Delete all at name** | Remove **all** RRs at a name (any type), optionally scoped by class. | `delete name` / `update delete name` |
| **Replace RRset** | Convenience composition: delete an RRset (or exact match) then add new RRs — same net effect as delete-then-add in one UPDATE. | two-step `delete` + `add` in one `send` |
| **Set TTL on add** | Specify TTL when adding; omit to use server default where applicable. | TTL field on `add` |
| **Set class** | Default **IN** (Internet); allow other classes only if explicitly required later. | `[class]` on `add`/`delete` |
Each row may appear **multiple times** in a single UPDATE message before submission.
### 3. Prerequisites (conditional updates)
Operations in the **Prerequisite** section — the update **fails entirely** if any prerequisite is not met (no partial apply).
| Operation | Description | nsupdate analogue |
|-----------|-------------|-------------------|
| **Name in use** | The name must already have at least one RR (any type). | `prereq nxdomain` inverse / `requires` patterns |
| **Name not in use** | The name must have **no** RRs (NXDOMAIN or empty owner). | `prereq nxdomain` |
| **RRset exists** | An RRset with given name, class, and type must exist. Optional: full set of rdata values must match. | `prereq yxrrset` |
| **RRset not exists** | No RRset for name/class/type, or no RRs matching given rdata set. | `prereq nxrrset` |
| **RR exists** | A specific RR (name, class, type, rdata) must exist. | `prereq yxrrset` with rdata |
| **RR not exists** | That specific RR must not exist. | `prereq nxrrset` with rdata |
Prerequisites are optional but recommended for idempotent automation (compare-and-swap style updates).
### 4. Resource record types and typed builders
The interface MUST use **typed builders per RR type** — not a generic `(type, rdata string)` API. Each supported type has a dedicated Go type (or builder) with validated fields (e.g. `ARecord` with `net.IP`, `MXRecord` with priority + host, `SRVRecord` with priority/weight/port/target).
Initial target set:
| Category | Types |
|----------|-------|
| Address | **A**, **AAAA** |
| Names | **CNAME**, **PTR**, **NS** |
| Text / metadata | **TXT**, **CAA** |
| Mail / service | **MX**, **SRV** |
| DNSSEC-related (dynamic) | **CDS**, **CDNSKEY** (where policy allows) |
| Other | **HINFO**, **NAPTR**, **TLSA**, **SSHFP** |
For RR types returned in **AXFR** but not yet given a typed builder, `Record` may hold a typed **unknown** wrapper (type number + validated wire rdata bytes or presentation parse) until a builder is added — callers must not rely on unparsed strings for updates.
Per-type field validation (e.g. hostname vs IP) is implemented with the typed builders in a later change; this catalog requires **typed expression only**, not free-form rdata strings.
SOA serial manipulation is **not** a direct client operation; BIND increments the SOA serial automatically on successful updates.
### 5. Update submission and batching
| Operation | Description |
|-----------|-------------|
| **Build update message** | Collect prerequisites and update actions into one RFC 2136 UPDATE. |
| **Send update** | Transmit the TSIG-signed message and receive the response. |
| **Batch multiple changes** | One send applies many add/delete/prerequisite operations atomically (all succeed or none). |
| **Interpret update response** | Map server RCODE to success or typed errors (e.g. REFUSED, NOTAUTH, NXDOMAIN, YXDOMAIN, NOTZONE, BADSIG). |
There is no “show” or local-only preview in the protocol; local validation before send is a library concern, not a server operation.
### 6. Zone transfer (AXFR)
Operations to **read** the full zone from the authoritative server (distinct from RFC 2136 UPDATE).
| Operation | Description | Typical use |
|-----------|-------------|-------------|
| **AXFR (full transfer)** | Download **all** RRs in the zone in one TSIG-signed transfer session over **TCP**. | Sync, backup, inspect zone contents |
| **Parse transfer stream** | Decode multi-message AXFR response into structured zone data (see [Zone data model](#zone-data-model)). | Populate in-memory zone |
| **Interpret transfer errors** | REFUSED (ACL), NOTAUTH (TSIG/ACL), BADSIG, FORMERR, etc. | Caller error handling |
Constraints:
- AXFR **always** uses TCP.
- Transfers require **`allow-transfer`** (or equivalent policy) for the TSIG key on the server — see [BIND configuration](#bind-configuration-for-tsig-keys).
Record types in transferred zones include **SOA**, **NS**, and all RR types present in the zone — not limited to the update-target set in section 4.
### 7. Zone data model
The library MUST define **concrete data types** (implemented in Go in a later change) to hold parsed zone contents and operation results. Logical model:
#### `Record` (single RR)
| Field | Description |
|-------|-------------|
| **Owner** | Domain name (FQDN, normalized) |
| **Class** | DNS class (default IN) |
| **Type** | RR type (A, AAAA, SOA, … or generic numeric type) |
| **TTL** | Time to live in seconds |
| **RData** | Type-specific value matching the RR type (typed builder / sum type — not a raw presentation string) |
#### `RRSet` (optional grouping)
| Field | Description |
|-------|-------------|
| **Owner, Class, Type** | RRset key |
| **Records** | One or more `Record` values sharing owner/class/type (multiple RData for same RRset) |
| **TTL** | RRset TTL (consistent across members in a well-formed zone) |
#### `Zone`
| Field | Description |
|-------|-------------|
| **Origin** | Zone apex name (e.g. `example.com.`) |
| **Records** | All RRs from transfer or constructed locally — **complete zone contents** |
| **SOA** | SOA RR extracted for convenience (serial, refresh, retry, expire, minimum TTL, MNAME, RNAME) |
| **Serial** | SOA serial number (from SOA RR after transfer) |
Access patterns the types must support (behavioral requirements, not API names):
- Iterate all records; lookup by owner name; lookup by owner + type.
- After **AXFR**, populate `Zone` with the **full current zone**.
- After **UPDATE**, optionally reflect mutations in an in-memory `Zone` if the caller maintains one (library may offer helpers).
- Export to a stable sorted text representation (zone-file-like) for debugging/tests (exact format in plan).
#### Transfer / update result wrappers
| Type (logical) | Holds |
|----------------|-------|
| **UpdateResult** | Success/failure, RCODE, optional server message |
| **TransferResult** | Resulting `Zone`, transfer kind (AXFR), ending serial (SOA after transfer) |
### 8. Read-only / auxiliary (library-side)
| Operation | Description |
|-----------|-------------|
| **Validate rdata** | Client-side syntax check before send (optional helper). |
| **Normalize name** | FQDN vs relative-to-zone, trailing dot; **ASCII hostnames only** in v1 (reject non-ASCII / Unicode labels; IDNA/punycode deferred). |
| **Dry run / encode only** | Produce wire-format or human-readable message without sending (testing and debugging). |
| **Zone diff** | Compare two `Zone` values (optional helper for automation; detail in plan). |
Out of scope: recursive resolver lookups (A/AAAA for unrelated names), catalog zones, BIND `rndc` control channel.
### 9. BIND configuration for TSIG keys
The project MUST ship a **BIND configuration guide** (English) describing how to configure BIND 9 so a **single TSIG key** can perform **dynamic updates** and **full zone transfers (AXFR)**. Target location: `docs/bind-tsig-configuration.md` (created in implementation plan; content requirements defined here).
The guide MUST cover at minimum:
1. **Define the key** — `key "name" { algorithm ...; secret "..."; };` in `named.conf` or an included file. State explicitly that **key generation is out of library scope**; operators use `tsig-keygen` or `rndc tsig-keygen` (or copy from existing secret management).
2. **Reference the key in the zone** — for the target `zone "example.com" { ... }` block:
- **`allow-update { key "name"; };`** — permits RFC 2136 UPDATE signed with that key.
- **`allow-transfer { key "name"; };`** — permits AXFR to clients signing with that key.
3. **Optional stricter policy** — mention **`update-policy`** (grant rules per name/type) as an alternative or supplement to broad `allow-update`.
4. **Reload / validation** — `named-checkconf`, `rndc reload`, and verifying with `nsupdate -y` and `dig -y` / TSIG-signed AXFR.
5. **Security notes** — restrict key to needed zones; do not expose AXFR to the public Internet; separate keys for read vs write if policy requires (library supports different keys per client instance even if the guide shows one key for simplicity).
6. **Minimal complete example** — one `named.conf` fragment: `key` + `zone` with `allow-update` and `allow-transfer` for the same key.
Example fragment (illustrative; the guide expands this):
```bind
key "go-dns-key" {
algorithm hmac-sha256;
secret "base64secret==";
};
zone "example.com" {
type master;
file "/var/lib/bind/db.example.com";
allow-update { key "go-dns-key"; };
allow-transfer { key "go-dns-key"; };
};
```
### 10. Explicit non-goals (initial version)
| Excluded | Reason |
|----------|--------|
| TSIG key generation | Operator/tooling concern (`tsig-keygen`, secret store) |
| DNSSEC inline signing workflow | Complex policy; may revisit later |
| Unauthenticated UPDATE or AXFR | Security risk; TSIG is the default path |
| subprocess wrapper around `nsupdate` / `dig` | Library implements the protocol directly |
| Non-BIND servers | Best-effort compatibility possible, but BIND 9 is the reference |
| IDNA / Unicode domain names | ASCII-only in v1; punycode support deferred |
| IXFR (incremental zone transfer) | Full AXFR only in v1; see [backlog.md](../../backlog.md) |
## Scope
- Register the project in workspace `go.work`
- Initialize Go module `gitea.neitzel.de/konrad/go-dns`
- Add project Cursor rule and `docs/project-specification.md` (summary + pointer to this catalog)
- Add `README.md`, `.gitignore`, `docs/backlog.md`, and `docs/changes/` layout
- Document the full **DNS operation catalog** (update, transfer, data model, BIND guide requirements) as the agreed interface surface
- Ensure `go work sync` succeeds with the new module (no application code required in this phase)
## Out of scope
- Go API implementation (types, interfaces, function bodies)
- RFC 2136 / AXFR wire encoding and TSIG signing implementation
- Unit or integration tests for update or transfer logic
- CLI binary, HTTP service, or Docker deployment (production compose — not required for library; BIND **testcontainer** is used for integration tests only)
- `git init`, Gitea remote, and first push in **this specify phase** — deferred to **implementation plan** (see [Resolved decisions](#resolved-decisions))
- Detailed per-record-type rdata validator **implementation** (typed builder shapes are decided here; code in a later change)
- **TSIG key generation** — operators create keys with BIND tooling or secret management; library accepts key name, algorithm, secret only
- Writing `docs/bind-tsig-configuration.md` in this phase (requirements for it are in this spec; file is created during implementation)
## Requirements
1. Module path MUST be `gitea.neitzel.de/konrad/go-dns` per workspace infrastructure rules.
2. Go version in `go.mod` MUST match workspace `go.work` (currently `1.26.4`).
3. Project MUST be listed in root `go.work` under `use (...)`.
4. `.cursor/rules/project-specification.mdc` MUST exist with `alwaysApply: true` and reference `docs/project-specification.md`.
5. `docs/project-specification.md` MUST summarize purpose, BIND target, RFC 2136 + AXFR, TSIG authentication, zone data model, and point to this operation catalog.
6. This `spec.md` MUST contain the full operation catalog (sections 1–10): session/auth, updates, prerequisites, RR types, update submission, **AXFR**, **zone data model**, auxiliary helpers, **BIND configuration guide requirements**, and non-goals.
7. This `spec.md` MUST require **TSIG key authentication** for updates and transfers; key generation MUST be listed as out of scope.
8. This `spec.md` MUST define logical types **`Record`**, **`RRSet`**, **`Zone`**, **`UpdateResult`**, and **`TransferResult`** for holding zone data and responses.
9. `README.md` MUST be in English and describe the project intent (updates + zone transfer) and link to this spec.
10. `.gitignore` MUST exclude common Go artifacts and local secret config paths.
11. `docs/changes/` MUST exist for future change efforts.
12. Public Go package name MUST be **`godns`** (import `gitea.neitzel.de/konrad/go-dns`, `package godns` at module root).
13. RR construction MUST use **typed builders per type**; a generic rdata string API MUST NOT be the primary interface.
14. Integration tests (future change) MUST use a **BIND testcontainer** via testcontainers-go; tag `integration` per workspace rules.
15. UPDATE transport MUST **automatically fall back to TCP** when UDP response is truncated or unsuitable.
16. Domain names MUST be **ASCII-only** in v1; reject Unicode labels at validation time.
17. `docs/backlog.md` MUST exist with **IXFR support** listed as a future backlog item.
## Resolved decisions
| Question | Decision |
|----------|----------|
| Root package name | **`godns`** — `package godns` at module root |
| RData API | **Typed builders** per RR type (A, AAAA, MX, …); no generic rdata string as primary API |
| Zone transfer | **AXFR only** in v1; IXFR tracked in [backlog.md](../../backlog.md) |
| Integration testing | **BIND testcontainer** (testcontainers-go); `go test -tags=integration ./...` |
| TCP fallback | **Automatic** on UDP truncation / oversize for UPDATE |
| IDNA / Unicode | **ASCII only** initially; IDNA deferred |
| Git repository | **`git init` + Gitea remote** during **implementation** of this change (plan phase), not in specify phase |
Remote URL per workspace infrastructure: `ssh://git@gitea.neitzel.de:2222/konrad/go-dns.git`
## Constraints
- Follow workspace change-driven development: library implementation happens only after this spec and an approved `plan.md`.
- Documentation and comments in English.
- Library-first design (importable package), not a mandatory daemon or CLI.
- TSIG is the primary authentication mechanism; do not design for unsigned updates or transfers as the happy path.
- Zone transfers use TCP; UPDATE uses UDP with **mandatory automatic TCP fallback** on truncation.
- Domain names: **ASCII-only** validation in v1.
- Integration tests: **BIND testcontainer** only (no optional external nameserver config required for CI).
- Match conventions of existing workspace Go libraries (e.g. `go-ollama`) where applicable.
## Acceptance criteria
1. **AC-1:** `grep -q go-dns /Users/konrad/Projects/go/go.work` and `go work sync` from the workspace root completes without error.
2. **AC-2:** `go-dns/go.mod` declares `module gitea.neitzel.de/konrad/go-dns` and `go 1.26.4`.
3. **AC-3:** `go-dns/.cursor/rules/project-specification.mdc` exists, has `alwaysApply: true`, and references `docs/project-specification.md`.
4. **AC-4:** `go-dns/docs/project-specification.md` documents module path, BIND target, RFC 2136 + AXFR, TSIG auth, zone data model, and references this spec.
5. **AC-5:** `go-dns/README.md` exists, is in English, mentions updates and AXFR zone transfer, and links to this spec.
6. **AC-6:** This `spec.md` includes sections 1–10: updates, **AXFR** (no IXFR), **zone data model** (`Record`, `RRSet`, `Zone`, result types), **BIND configuration guide requirements**, and non-goals including **no key generation** and **no IXFR**.
7. **AC-7:** This `spec.md` specifies that `docs/bind-tsig-configuration.md` will document `allow-update` and `allow-transfer` for a TSIG key with a minimal `named.conf` example.
8. **AC-8:** `go-dns/docs/backlog.md` exists with an **IXFR** backlog entry.
9. **AC-9:** `go-dns/docs/changes/00001-setup-project/` contains this `spec.md` (and later `plan.md`, `verification.md`, `changes.md` per workflow).
10. **AC-10:** [Resolved decisions](#resolved-decisions) documents `godns`, typed builders, AXFR-only transfer, BIND testcontainer, automatic TCP fallback, ASCII-only names, and git setup in implementation.
+9 -17
View File
@@ -5,7 +5,7 @@
**go-dns** is a Go library for authoritative DNS operations against **BIND** (and compatible servers):
- **Dynamic updates** — same semantics as `nsupdate` ([RFC 2136](https://www.rfc-editor.org/rfc/rfc2136))
- **Zone transfer** — full **AXFR** only ([RFC 5936](https://www.rfc-editor.org/rfc/rfc5936)); IXFR is backlog
- **Zone transfer** — full **AXFR** only ([RFC 5936](https://www.rfc-editor.org/rfc/rfc5936)); IXFR is out of scope for v1
- **TSIG authentication** — callers supply existing shared keys; key generation is out of scope
Callers perform updates and full zone reads in Go with typed **zone data structures** and **typed RR builders** instead of shelling out to `nsupdate` or `dig axfr`.
@@ -15,9 +15,11 @@ Callers perform updates and full zone reads in Go with typed **zone data structu
| Item | Value |
|------|-------|
| Module path | `gitea.neitzel.de/konrad/go-dns` |
| Go version | 1.26.4 (workspace `go.work`) |
| Go version | Workspace `go.work` (currently 1.26.4) |
| Public package | **`godns`** (`package godns` at module root) |
| Git remote (implementation) | `ssh://git@gitea.neitzel.de:2222/konrad/go-dns.git` |
| Git remote | `ssh://git@gitea.neitzel.de:2222/konrad/go-dns.git` |
This is a **Library** in the ExApp Workspace. Workspace Go rules live in `.cursor/rules/` (`go.mdc`, `go-testing.mdc`). Specs and Issues for this Library live in the Workspace under `docs/issues/`, not in this repository.
## Target environment
@@ -27,24 +29,20 @@ Callers perform updates and full zone reads in Go with typed **zone data structu
| Protocols | RFC 2136 UPDATE (UDP + **automatic TCP fallback**); **AXFR over TCP** |
| Authentication | TSIG (RFC 2845 / RFC 8945) — required for documented happy path |
| Domain names | **ASCII only** in v1 (no IDNA) |
| Out of scope (initially) | IXFR, full DNS server, recursive resolver, TSIG key generation, IDNA |
| Out of scope (v1) | IXFR, full DNS server, recursive resolver, TSIG key generation, IDNA |
## Supported operation surface
The authoritative list lives in change **00001-setup-project** ([spec.md](changes/00001-setup-project/spec.md)). Summary:
1. **Session & TSIG key** — server, zone, transport, existing key (name, algorithm, secret)
2. **Dynamic update** — add, delete, prerequisites, batch UPDATE; typed RR builders
3. **Zone transfer** — **AXFR** only; parse stream into zone data
4. **Zone data model** — `Record`, `RRSet`, `Zone`, `UpdateResult`, `TransferResult`
5. **BIND guide** — [bind-tsig-configuration.md](bind-tsig-configuration.md) (`allow-update` + `allow-transfer` for a TSIG key)
Future work: [backlog.md](backlog.md) (includes **IXFR**).
## API style
- **Typed builders** per RR type (e.g. A, AAAA, MX, SRV) — not a generic `(type, rdata string)` API
- Exact method names follow in subsequent changes after the catalog is agreed
- Exact method names follow in later Workspace Issues after the catalog is agreed
## Zone data model (logical)
@@ -62,10 +60,6 @@ Operators configure BIND so a TSIG key may **update** and **transfer** a zone (`
**[docs/bind-tsig-configuration.md](bind-tsig-configuration.md)**
## Change workflow
Features are delivered through documented changes under `docs/changes/<Number>-<short-description>/` (spec → plan → implement → verify → changes). The product backlog lives at [backlog.md](backlog.md).
## Tests
| Command | Purpose |
@@ -73,14 +67,12 @@ Features are delivered through documented changes under `docs/changes/<Number>-<
| `go test ./...` | Unit tests (encoding, parsing, zone model, typed builders) |
| `go test -tags=integration ./...` | **BIND testcontainer** (Docker required) |
Integration tests use testcontainers-go with a BIND image per workspace `integration-testing.mdc`.
Follow the Workspace `.cursor/rules/go-testing.mdc`. This Library's integration image is BIND.
## Package layout (planned)
```
package godns # public API at module root
internal/ # RFC 2136, AXFR, TSIG, wire format
docs/ # Project docs incl. bind-tsig-configuration.md, backlog.md
docs/ # Library spec and BIND TSIG guide
```
Layout details are finalized in the setup implementation plan.