Customer & Operator Guide

A multi-tenant, policy-governed authoritative DNS control plane. Everything the console does is an API call; anything the console can do, you can script.

1 · Getting started

Console: /ops/ — sign in with your email address as the username. API: obtain a bearer token from POST /api/v1/auth/login; access tokens last 30 minutes, refresh at POST /api/v1/auth/refresh. Accounts with MFA receive a challenge token and complete it at /auth/mfa/verify.

Tenants

A deployment hosts many tenants (customers). Zones, records, users, roles, policies and audit history belong to exactly one tenant, and the API only ever returns your tenant's objects — isolation is enforced in the data layer, not hidden in the UI. A superuser administers any tenant through the API; the console dashboard stays scoped to the tenant it belongs to.

The three access levels

RoleCanCannot
ViewerRead zones, records, policiesChange anything — every write is 403
Record Editor (operator)Create/update records, create zones, load zone filesDelete records or zones
Zone AdminEverything above + delete records/zones, manage permissions

Roles are granted per zone, so one person can administer one zone and be read-only on another. Denials are explicit (403 with a reason) and audited.

Your first zone

Create example.com. (trailing dot) with a primary nameserver and hostmaster; SOA/NS are written for you and the zone is registered with the DNS server immediately. Add www → A → 203.0.113.10; it is live on port 53 when the call returns 201: dig @<host> www.example.com. Zone deletion requires the zone name as confirmation and the Zone Admin role.

2 · Zones & records

POST   /api/v1/zones                      {"name":"example.com.","primary_ns":"ns1.example.com.","admin_email":"hostmaster.example.com."}
GET    /api/v1/zones?limit=50&offset=0
DELETE /api/v1/zones/{id}?confirm=example.com.      # Zone Admin
POST   /api/v1/zones/{id}/records         {"name":"@","record_type":"MX","content":"mail.example.com.","priority":10,"ttl":3600}
PUT/DELETE /api/v1/zones/{id}/records/{record_id}
FieldNotes
nameRelative to the zone: www, _dmarc, _sip._tcp; @ for the apex
record_typeA, AAAA, CNAME, MX, NS, TXT, SRV, CAA, PTR …
contentTargets fully qualified (mail.example.com.)
ttl · priority · weight/port · flags/tagseconds · MX/SRV · SRV · CAA
Names are canonicalised (no accidental example.com.example.com.), CNAME exclusivity is enforced, long TXT strings are split correctly, duplicates are rejected, and policies run on every write. A write is committed to the control plane and pushed to the DNS server as one unit — if the DNS server rejects it, the transaction rolls back. There is no "publish" step and no drift.

3 · Loading zone files

Validate first: POST /zones/{id}/import/validate parses the file and evaluates every line against your policies without writing anything — {"summary":{"total":42,"valid":40,"invalid":1,"policy_denied":1}, "lines":[…]}.

curl -X POST https://<host>/api/v1/zones/$ZONE/import/bind -H "Authorization: Bearer $TOKEN" -F "file=@example.com.zone"
# -> {"imported": 40, "errors": [], "records": [...]}
$ORIGIN example.com.
$TTL 3600
@        IN  A      203.0.113.10
@        IN  MX 10  mail.example.com.
www      IN  CNAME  example.com.
@        IN  TXT    "v=spf1 mx -all"
_dmarc   IN  TXT    "v=DMARC1; p=reject"

Existing records are skipped (re-import is safe); malformed or policy-refused lines are reported with line numbers while the rest imports; the batch reaches the DNS server in chunked writes that roll back on partial failure; one audit entry summarises the import. SOA lines are ignored. CSV is accepted at /import/csv (name,type,content[,ttl,priority,weight,port]). Synchronous imports cap at 5,000 records; larger files go to /import/bind/async (202 + job_id, poll GET /jobs/{job_id}). Export any zone with GET /zones/{id}/export/bind or /export/csv. Importing needs Record Editor or above plus write permission on the zone.

4 · Users, roles & access

Four layers, all per tenant: usersroles (Zone Admin / Record Editor / Viewer) → groups that carry roles → zone permissions (READ/WRITE/ADMIN for a role, group or user on a specific zone). The console's Users tab creates users with a role attached, assigns or removes roles, and activates or deactivates accounts.

GET  /api/v1/roles                                   # find role ids
POST /api/v1/users   {"email":"jane@example.com","username":"jane","password":"…","full_name":"Jane","role_ids":["<record-editor>"]}
POST /api/v1/users/{user_id}/roles/{role_id}         # or attach later
POST /api/v1/users/{user_id}/groups/{group_id}
GET/POST /api/v1/zones/{zone_id}/permissions

Deactivate leavers (PUT /users/{id}is_active:false) to keep their audit history attributable. Deactivating a whole tenant refuses all of its logins immediately. Emails are unique platform-wide so a login is never ambiguous. Every denied write is audited with the user, zone and reason.

5 · Policies

Guardrails evaluated on every record create and update. A policy is global (all zones) or scoped to one zone, has a priority, and holds rules; a matching DENY refuses the operation with a reason naming the policy and rule.

rule_typeparametersmatches when…
hostname_regexpatternthe record name matches
value_regexpatternthe content matches
ip_rangecidrs, record_typesan A/AAAA address is inside a CIDR
allowed_types / record_type_filtertypesthe type is in the list
ttl_rangemin_ttl, max_ttlthe TTL is inside the range
max_recordsmax_per_name, max_per_typecounts are within limits
conditional_recordif_type, then_allow, require_existinga prerequisite record exists
Enforcing vs. advisory. Every rule type matches the compliant case. A DENY fires when it matches — natural for blacklists (wildcards, private addresses). To enforce a range or whitelist ("TTL ≥ 300", "only these types"), write DENY with "negate": true so it fires on the non-compliant case; the reason is prefixed negated:. A plain ALLOW never blocks. Negation is only applied to genuine verdicts — a rule that does not apply to the record cannot fire by accident.
TTL floor/cap:        {"rule_type":"ttl_range","action":"deny","parameters":{"min_ttl":300,"max_ttl":86400,"negate":true}}
No wildcards:         {"rule_type":"hostname_regex","action":"deny","parameters":{"pattern":"^\\*"}}
No private IPs:       {"rule_type":"ip_range","action":"deny","parameters":{"cidrs":["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16"],"record_types":["A","AAAA"]}}
Only prod types:      {"rule_type":"allowed_types","action":"deny","parameters":{"types":["A","AAAA","CNAME","MX","NS","TXT","SRV","CAA","PTR"],"negate":true}}
MX requires SPF:      {"rule_type":"conditional_record","action":"deny","parameters":{"if_type":"TXT","then_allow":["MX"],"require_existing":true,"negate":true}}

Policy denied: DENY by ttl_range in 'TTL Enforcement': negated: TTL 60 is below the minimum of 300

Zone-scoped example: a bank zone with no CNAMEs and a one-hour TTL floor — the same CNAME refused there is accepted in any other zone. The console's Policies tab lists every policy with its rules, activates or deactivates one without deleting it, and has a dry-run form that evaluates a hypothetical record (including its TTL) against your policies without writing anything — the same as POST /api/v1/policies/test.

6 · API quick start

TOKEN=$(curl -s -X POST https://<host>/api/v1/auth/login -H 'Content-Type: application/json' \
  -d '{"username":"you@example.com","password":"…"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s https://<host>/api/v1/zones?limit=50 -H "Authorization: Bearer $TOKEN"
curl -s https://<host>/api/v1/records/search?q=mail -H "Authorization: Bearer $TOKEN"
curl -s https://<host>/api/v1/audit/recent -H "Authorization: Bearer $TOKEN"

Errors

JSON envelope with a stable code, message and request_id. Policy refusals are 403 naming the policy.

API keys

POST /api/v1/api-keys with explicit scopes (zones.view, zones.manage, zones.delete, audit.view, …) — least-privilege by default, shown once, sent as X-API-Key: <key>, optional expiry, revocable individually.

Idempotency

Send Idempotency-Key on POSTs; a repeated key returns the original response instead of a duplicate.

Rate limits

Per user and category; x-ratelimit-* headers on every response, 429 + Retry-After when exceeded.

Pagination

{"items","total","limit","offset"}, limit capped at 50.

Swagger

/docs — click Authorize, paste the token, try any endpoint.

7 · Security & operations

Transport & edge

HTTPS only with auto-renewed certificates and HSTS; CSP, frame-deny, nosniff, referrer and permissions policies; host firewall exposes only 22 (key-only, rate-limited), 80/443 and 53.

Identity

Modern password hashing, TOTP MFA with encrypted secrets, short-lived tokens with server-side revocation that fails closed in production, per-account and per-source lockouts on every auth endpoint.

Integrity

Policy engine on every write; tamper-evident, hash-chained audit log that records denials too; control plane and DNS server written atomically with rollback; outbox + reconciler converge any gap.

DNSSEC

Per zone: enable, DS records for your registrar, DS-check, key rotation and monitored rollovers. Private keys wrapped with a dedicated key at rest.

Backups

Encrypted (age) database backups with the key held off-box, plus scheduled restore tests that prove each backup is usable. Zone and tenant exports on demand.

Automation

Health probe with a real DNS query, backup + verified restore, outbox drain (30 s), drift reconcile (15 min), zone integrity checks, audit-chain anchoring, SIEM export, scheduled DNSSEC rollover, retention, certificate renewal — all on timers, observable at /health/ready and /metrics.

Webhooks

Signed deliveries for zone.*, record.*, policy.* and user.* events, with delivery history, redelivery, a test endpoint and zero-downtime secret rotation.

Recommended

MFA for every human; API keys per integration; least-privilege roles; TTL / wildcard / private-IP / allowed-types policies on from day one; audit feed to your SIEM.

KNS DNS Platform · this guide ships with the product at /ops/guide.html · full guides in docs/customer-guide/.