The management API is served from /api/v1/* on the management listener (default: 127.0.0.1:8080). It is the shared control surface used by the web admin SPA, dnsctl, and operator automation.
The canonical REST contract lives in the homeDNS source checkout at docs/openapi.yaml. TypeScript types for the web admin are generated from it via make api-types.
Requests are gated in this order:
- Network gate —
management.allow_cidrs in YAML; requests outside these CIDRs are rejected before any auth check.
- Session cookie —
homedns_session, issued by POST /auth/login.
- Bearer token —
Authorization: Bearer <token> for tokens minted via POST /users/{id}/tokens.
| Code | Meaning |
|---|
401 | Missing or invalid credentials |
403 | Authenticated but role is insufficient |
404 | Resource does not exist (or hidden by feature flag) |
409 | Conflict — e.g. zone already exists |
400 | Validation failure |
| Role | Access |
|---|
admin | Full control, including user management |
operator | All DNS and config operations |
viewer | Read-only |
All paths below are relative to /api/v1.
| Path | Method | Notes |
|---|
/health | GET | Returns {"status":"ok"} when healthy |
/version | GET | Returns version, buildTime, debug |
| Path | Method | Notes |
|---|
/zones | GET, POST | List all zones; create a zone |
/zones/{zone} | GET, DELETE | Fetch or delete a zone |
/zones/{zone}/records | GET, POST | List or add records (JSON, zone-file presentation) |
/zones/{zone}/records | DELETE | Delete a record |
/zones/{zone}/import | POST | Import raw RFC 1035 zone file (text/plain body) |
/zones/{zone}/export | GET | Download zone as text/plain zone file |
/zones/{zone}/rrset | GET | Fetch a specific RRset (verify implementation) |
/zones/{zone}/serial/increment | POST | Bump SOA serial (verify implementation) |
| Path | Method | Notes |
|---|
/forwarders | GET, POST | List or create conditional forwarders |
/forwarders/{id} | GET, PUT, DELETE | Fetch, update, or delete a forwarder |
/forwarders/health/stream | GET (SSE) | Live health and latency updates — see SSE channels |
| Path | Method | Notes |
|---|
/filter/sources | GET, POST | List or add filter sources |
/filter/sources/{id} | GET, PUT, DELETE | Manage a specific source |
/filter/sources/{id}/refresh | POST | Force-refresh a specific list |
/filter/policy | GET, PUT | Get or update the global block policy |
/filter/decide | GET | Test a domain — ?name=ads.example.com |
/filter/refresh | POST | Trigger a bulk refresh of all sources |
| Path | Method | Notes |
|---|
/cache/stats | GET | Current cache statistics |
/cache/flush | POST | Flush all cached records |
| Path | Method | Notes |
|---|
/ddns/tsig | GET, POST | List or create TSIG keys |
/ddns/tsig/{name} | GET, DELETE | Fetch or delete a key |
/ddns/policies | GET | List all zone update policies |
/ddns/policies/{zone} | GET, PUT | Fetch or update policy for a zone |
| Path | Method | Notes |
|---|
/queries | GET | Recent query log (paginated) |
/queries/stream | GET (SSE) | Live query log — see SSE channels |
| Path | Method | Notes |
|---|
/stats | GET | Snapshot of cache/filter/health aggregates |
/stats/stream | GET (SSE) | Rolling stats stream — see SSE channels |
| Path | Method | Notes |
|---|
/config/effective | GET | Merged runtime config |
/config/file | GET | On-disk YAML config |
| Path | Method | Notes |
|---|
/server/listeners | GET | Active listener bindings and state |
/server/reload | POST | Trigger config reload |
/server/probe/{forwarder_id} | POST | Fire a manual health probe for a forwarder |
The following paths are defined in the source checkout’s docs/openapi.yaml but may be in varying states of implementation. Check internal/api/ before building automation against them:
/auth/login, /auth/logout, /auth/me, /sessions/*
/users/*, /tokens/*
/dnssec/* (trust anchors, validator status)
/acl (DNS ACL config)
/backup, /restore
/metrics (Prometheus — roadmap item; use /stats in v1)
All three streams use the text/event-stream content type. The client helper in the web admin is web-admin/src/lib/api/sse.ts.
| Path | Event names | Payload description |
|---|
/api/v1/forwarders/health/stream | snapshot, health | Full snapshot on connect, then delta health updates per upstream |
/api/v1/queries/stream | query | One QueryLogEntry per resolved query |
/api/v1/stats/stream | stats | Nested cache / filter / health aggregate map |
- Strategies and transports are serialized as strings (
"parallel", "dot", etc.). The Go enum values are integers internally; the JSON marshaling is frozen in the contracts package.
- TLS runtime objects (
*tls.Config) are not serialized. API-level TLS knobs use explicit fields: skip_tls_verify, sni_override.
- CA bundles and client certs are addressed by ID once the cert manager API lands (roadmap).
- Records use zone-file presentation format in both request and response bodies.