From afbe268cb5fee631ac7fed07ae4e89abaea11ace Mon Sep 17 00:00:00 2001 From: Konrad Neitzel Date: Tue, 25 Aug 2026 14:00:17 +0200 Subject: [PATCH] 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. --- .cursor/rules/project-specification.mdc | 33 -- README.md | 6 +- docs/backlog.md | 40 --- .../00001-setup-project/impl-decisions.md | 41 --- docs/changes/00001-setup-project/plan.md | 210 ------------ docs/changes/00001-setup-project/spec.md | 305 ------------------ docs/project-specification.md | 26 +- 7 files changed, 11 insertions(+), 650 deletions(-) delete mode 100644 .cursor/rules/project-specification.mdc delete mode 100644 docs/backlog.md delete mode 100644 docs/changes/00001-setup-project/impl-decisions.md delete mode 100644 docs/changes/00001-setup-project/plan.md delete mode 100644 docs/changes/00001-setup-project/spec.md diff --git a/.cursor/rules/project-specification.mdc b/.cursor/rules/project-specification.mdc deleted file mode 100644 index 8a38314..0000000 --- a/.cursor/rules/project-specification.mdc +++ /dev/null @@ -1,33 +0,0 @@ ---- -description: go-dns — Go library for dynamic DNS updates (RFC 2136 / nsupdate-style) -alwaysApply: true ---- - -# go-dns — Project Rules - -Follow `docs/project-specification.md` for design and implementation. - -## Stack - -- **Language:** Go (see workspace `go.work` for version) -- **Module:** `gitea.neitzel.de/konrad/go-dns` -- **Package:** `godns` (module root) -- **Type:** Importable Go library (not a standalone service) - -## Design - -- Target **BIND** and RFC 2136–compatible authoritative nameservers -- **Dynamic update** (RFC 2136) and **full zone transfer** (AXFR) over DNS -- **TSIG** authentication with caller-supplied keys (no key generation in library) -- Typed **RR builders** per record type (not generic rdata strings) -- Domain names: **ASCII only** in v1 -- UPDATE: **automatic TCP fallback** on UDP truncation -- Integration tests: **BIND testcontainer** (`go test -tags=integration ./...`) -- **`docs/bind-tsig-configuration.md`** — BIND `allow-update` / `allow-transfer` guide -- Typed **zone data structures** (`Record`, `Zone`, etc.) for transfer/update results -- Project `.cursor` overrides workspace rules for this module - -## Tests - -- Unit: `go test ./...` -- Integration: `go test -tags=integration ./...` — **BIND testcontainer** (Docker required) diff --git a/README.md b/README.md index 5c58fb1..f792a20 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ Go library for authoritative DNS operations against BIND (and compatible) nameservers: - **Dynamic updates** — `nsupdate`-style changes (RFC 2136) -- **Zone transfer** — full zone read via **AXFR** (IXFR on [backlog](docs/backlog.md)) +- **Zone transfer** — full zone read via **AXFR** (IXFR out of scope for v1) - **TSIG key authentication** (callers provide keys; generation out of scope) ## Status -Early project setup. The supported operation surface, zone data model, and BIND configuration requirements are defined in [docs/changes/00001-setup-project/spec.md](docs/changes/00001-setup-project/spec.md). Implementation follows the change-driven workflow (spec → plan → implement). +Skeleton. Design is in [docs/project-specification.md](docs/project-specification.md). Implementation Issues live in the ExApp Workspace (`docs/issues/`), not in this repository. ## Module @@ -22,5 +22,3 @@ Module path: `gitea.neitzel.de/konrad/go-dns` — import as **`godns`**. - [Project specification](docs/project-specification.md) - [BIND TSIG configuration](docs/bind-tsig-configuration.md) -- [Product backlog](docs/backlog.md) -- [Change efforts](docs/changes/) diff --git a/docs/backlog.md b/docs/backlog.md deleted file mode 100644 index 57e0297..0000000 --- a/docs/backlog.md +++ /dev/null @@ -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) | diff --git a/docs/changes/00001-setup-project/impl-decisions.md b/docs/changes/00001-setup-project/impl-decisions.md deleted file mode 100644 index 041dd26..0000000 --- a/docs/changes/00001-setup-project/impl-decisions.md +++ /dev/null @@ -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`. diff --git a/docs/changes/00001-setup-project/plan.md b/docs/changes/00001-setup-project/plan.md deleted file mode 100644 index f5ff424..0000000 --- a/docs/changes/00001-setup-project/plan.md +++ /dev/null @@ -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. diff --git a/docs/changes/00001-setup-project/spec.md b/docs/changes/00001-setup-project/spec.md deleted file mode 100644 index c331679..0000000 --- a/docs/changes/00001-setup-project/spec.md +++ /dev/null @@ -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 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. diff --git a/docs/project-specification.md b/docs/project-specification.md index c2fcdd6..da78db5 100644 --- a/docs/project-specification.md +++ b/docs/project-specification.md @@ -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/-/` (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/-< | `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.