feat: add admin DNS record management API#222
Open
ZPascal wants to merge 13 commits into
Open
Conversation
- feature/dns-api-extended: admin API for full DNS record management - feature/ai-agent-support: OpenAPI 3.1 spec + MCP server binary - feature/security-ha: security hardening + active-active HA deployment guide
- dns-api-extended: 7 tasks, TDD, admin CRUD API + DNS integration - ai-agent-support: 7 tasks, OpenAPI spec + MCP binary - security-ha: 7 tasks, 5 security fixes + HA deployment guide
Implements adminBearerMiddleware, adminListRecords, adminCreateRecord, adminUpdateRecord, and adminDeleteRecord HTTP handlers behind Bearer token auth; adds corresponding tests with DB teardown via t.Cleanup.
6 tasks
There was a problem hiding this comment.
Pull request overview
Adds an authenticated admin HTTP surface and backing storage to manage DNS records, and wires those records into the DNS response path as a fallback when no static/ACME records match.
Changes:
- Introduce
/admin/recordsCRUD endpoints guarded by an admin bearer token, enabled only when[api.admin].tokenis set. - Add
dns_recordspersistence and integrate DB-backed records into DNS answers as a fallback path. - Add record type/TTL/value validators and related unit tests; extend CORS configuration for admin endpoints.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
main.go |
Registers admin routes conditionally; expands CORS methods/headers. |
api.go |
Implements admin bearer middleware and record CRUD handlers. |
db.go |
Adds dns_records DDL and CRUD methods. |
dns.go |
Falls through to DB-managed records when static/ACME records don’t match. |
types.go |
Adds [api.admin] config struct, DNSRecord, and DB interface extensions. |
validation.go |
Adds record type/value/TTL validation helpers. |
validation_test.go |
Adds unit tests for the new validation helpers. |
config.cfg |
Adds [api.admin] section and token configuration. |
docs/superpowers/specs/2026-05-30-dns-api-extended-design.md |
Design doc describing the admin DNS management API and DNS integration. |
docs/superpowers/specs/2026-05-30-security-ha-design.md |
Security/HA design doc (appears unrelated to this PR’s stated scope). |
docs/superpowers/specs/2026-05-30-ai-agent-support-design.md |
AI agent support design doc (appears unrelated to this PR’s stated scope). |
docs/superpowers/plans/2026-05-30-security-ha.md |
Security/HA implementation plan (appears unrelated to this PR’s stated scope). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| AllowedOrigins: config.API.CorsOrigins, | ||
| AllowedMethods: []string{"GET", "POST"}, | ||
| AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"}, | ||
| AllowedHeaders: []string{"Authorization", "Content-Type"}, |
Comment on lines
+201
to
+208
| rec := DNSRecord{ | ||
| ID: uuid.New().String(), | ||
| Name: strings.ToLower(req.Name), | ||
| Type: req.Type, | ||
| Value: req.Value, | ||
| TTL: ttl, | ||
| Created: time.Now().Unix(), | ||
| } |
Comment on lines
+150
to
+152
| filterType := r.URL.Query().Get("type") | ||
| filterName := r.URL.Query().Get("name") | ||
| records, err := DB.ListRecords(filterType, filterName) |
Comment on lines
+251
to
+269
| func (d *DNSServer) answerManaged(q dns.Question) []dns.RR { | ||
| name := strings.ToLower(q.Name) | ||
| records, err := d.DB.ListRecords(dns.TypeToString[q.Qtype], name) | ||
| if err != nil { | ||
| log.WithFields(log.Fields{"error": err.Error()}).Debug("Error querying managed records") | ||
| return nil | ||
| } | ||
| var rrs []dns.RR | ||
| for _, rec := range records { | ||
| rrStr := fmt.Sprintf("%s %d IN %s %s", rec.Name, rec.TTL, rec.Type, rec.Value) | ||
| rr, err := dns.NewRR(rrStr) | ||
| if err != nil { | ||
| log.WithFields(log.Fields{"error": err.Error(), "record": rrStr}).Warning("Could not parse managed RR") | ||
| continue | ||
| } | ||
| rrs = append(rrs, rr) | ||
| } | ||
| return rrs | ||
| } |
| AllowedOrigins: config.API.CorsOrigins, | ||
| AllowedMethods: []string{"GET", "POST"}, | ||
| AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"}, | ||
| AllowedHeaders: []string{"Authorization", "Content-Type"}, |
Comment on lines
+75
to
+76
| case "CNAME", "MX", "NS", "PTR", "SRV", "TXT", "CAA": | ||
| return true // non-empty check handled above; structural validation deferred to dns.NewRR |
Comment on lines
+1
to
+16
| # Design: feature/ai-agent-support | ||
|
|
||
| **Date:** 2026-05-30 | ||
| **Branch:** `feature/ai-agent-support` | ||
| **Status:** Approved | ||
|
|
||
| ## Overview | ||
|
|
||
| Add two AI agent integration surfaces to acme-dns: | ||
|
|
||
| 1. **OpenAPI 3.1 spec** — served at `GET /openapi.json`, enables any OpenAPI-aware agent or tool to discover and call the API via function calling | ||
| 2. **MCP server binary** — `cmd/acme-dns-mcp/`, a standalone stdio-based MCP server that wraps the acme-dns HTTP API as structured tools for Claude and other MCP-capable agents | ||
|
|
||
| The core acme-dns server is minimally changed. The MCP server is a separate binary that proxies to the HTTP API — it has no direct DB or DNS access. | ||
|
|
||
| **Dependency:** This branch builds on `feature/dns-api-extended`. The OpenAPI spec and MCP tools for admin record management (`list_dns_records`, `create_dns_record`, `update_dns_record`, `delete_dns_record`) require the admin API from that branch to be merged first, or this branch should be built on top of it. |
Comment on lines
+1
to
+9
| # Security Hardening and HA Implementation Plan | ||
|
|
||
| > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. | ||
|
|
||
| **Goal:** Fix five security issues in the acme-dns codebase (SQL injection, bcrypt cost, rate limiting, CORS default, JSON error encoding) and add an HA deployment guide. | ||
|
|
||
| **Architecture:** All changes are in existing files — no new packages, no new dependencies. The rate limiter uses stdlib `sync` + `time` only. HA support is purely documentation (active-active with shared PostgreSQL requires no code changes). | ||
|
|
||
| **Tech Stack:** Go 1.26, `encoding/json`, `sync`, `time` (all stdlib) |
Comment on lines
+1
to
+16
| # Design: feature/ai-agent-support | ||
|
|
||
| **Date:** 2026-05-30 | ||
| **Branch:** `feature/ai-agent-support` | ||
| **Status:** Approved | ||
|
|
||
| ## Overview | ||
|
|
||
| Add two AI agent integration surfaces to acme-dns: | ||
|
|
||
| 1. **OpenAPI 3.1 spec** — served at `GET /openapi.json`, enables any OpenAPI-aware agent or tool to discover and call the API via function calling | ||
| 2. **MCP server binary** — `cmd/acme-dns-mcp/`, a standalone stdio-based MCP server that wraps the acme-dns HTTP API as structured tools for Claude and other MCP-capable agents | ||
|
|
||
| The core acme-dns server is minimally changed. The MCP server is a separate binary that proxies to the HTTP API — it has no direct DB or DNS access. | ||
|
|
||
| **Dependency:** This branch builds on `feature/dns-api-extended`. The OpenAPI spec and MCP tools for admin record management (`list_dns_records`, `create_dns_record`, `update_dns_record`, `delete_dns_record`) require the admin API from that branch to be merged first, or this branch should be built on top of it. |
Comment on lines
+1
to
+9
| # Security Hardening and HA Implementation Plan | ||
|
|
||
| > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. | ||
|
|
||
| **Goal:** Fix five security issues in the acme-dns codebase (SQL injection, bcrypt cost, rate limiting, CORS default, JSON error encoding) and add an HA deployment guide. | ||
|
|
||
| **Architecture:** All changes are in existing files — no new packages, no new dependencies. The rate limiter uses stdlib `sync` + `time` only. HA support is purely documentation (active-active with shared PostgreSQL requires no code changes). | ||
|
|
||
| **Tech Stack:** Go 1.26, `encoding/json`, `sync`, `time` (all stdlib) |
d8fbc00 to
e929f6d
Compare
…g, RR validation, DB index - CORS: restore X-Api-User and X-Api-Key to AllowedHeaders (required by /update endpoint) - api: normalize record names to FQDN (trailing dot) on create/update/list-filter so DNS lookups against q.Name (which always ends with dot) match stored records - api: normalize type filter to uppercase for case-insensitive GET /admin/records?type= - api: add probeRR helper — validates record value parses as a real DNS RR before storing, preventing values that are silently dropped at serve time - dns: guard answerManaged against unknown QTYPEs (empty TypeToString result) - dns: quote TXT and CAA values in RR string so values with spaces parse correctly - db: add index on dns_records(name, type) to avoid full table scans on every DNS request
e929f6d to
c941f89
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
[api.admin]config section with bearer token authenticationdns_recordstable with full CRUD:GET/POST /admin/records,PUT/DELETE /admin/records/:iddns_recordstable when no static/ACME records match a query[api.admin].tokenis non-empty (disabled by default)PUTandDELETEmethods +AuthorizationheaderTest Plan
go test ./...passesgo build ./...builds cleanlyPOST /admin/recordswith valid bearer token creates a record (201)GET /admin/recordswithout token returns 401GET /admin/records?type=Afilters by record typetoken = ""in config