← Back to docs

DNS API User Guide

Language: EN | EN | SV

DNS Zone List API - User Guide

Overview

The DNS Zone List API provides access to your BIND DNS zones through authenticated web endpoints. Use this API to discover available zones, retrieve zone metadata, and view DNS records. This is designed for the upcoming DNS zone editor.

Base URL: https://tools.tornevall.net/dns

Authentication: Session-based (for web UI) or Token-based (for API integrations)


Authentication Methods

Session-Based (Web Interface)

For web applications and dashboard use:

  1. Login at /login with your credentials
  2. Browser maintains session automatically
  3. Use with credentials: 'include' in JavaScript fetch

Token-Based (API Integrations)

For programmatic access:

  1. Request an API token in the DNS Editor UI (/dns/tokens)
  2. Each zone has a unique Secret token
  3. Include the Secret as Bearer token in API requests:
    curl -H "Authorization: Bearer dns_secret_xxxxx" https://tools.tornevall.net/api/dns/zones
    

Token system is similar to Cloudflare's API tokens - each zone gets its own secret + zone key pair.


Quick Start

I'm a Web Developer Using the API

  1. Get a session - Login to https://tools.tornevall.net with your credentials
  2. Your browser maintains the session automatically - JavaScript with credentials: 'include' works
  3. Make requests - Use fetch or curl with your session

I'm a Zone Owner Requesting an API Token

  1. Navigate to /dns/editor in the web interface
  2. Click "Manage API Tokens"
  3. Select your zone and click "Create Token"
  4. Copy the Secret for use in API calls

I'm an Admin Setting Up Zone Access

See the Access Control section to grant zone permissions to users.


Key Features

  • Zone Discovery - List all available DNS zones
  • Zone Metadata - Get zone file information and TSIG key names
  • DNS Records - View all DNS records in a zone (A, AAAA, CNAME, MX, NS, SOA, TXT, etc.)
  • Access Control - Admins control who can access which zones
  • API Tokens - Each zone has a unique secret token for programmatic access
  • Multi-Environment - Works on both production and development setups

DNS Cache Model (2026-03-27)

  • Zone cache is now stored row-by-row instead of one large serialized payload:
    • dns_zone_cache (zone metadata)
    • dns_zone_cache_records (one row per DNS record)
    • dns_zone_cache_ip_extras (indexed extra IP values for forward/reverse mapping)
  • POST /api/dns/records/add, delete, update, and bulk now synchronize matching cached rows after the master DNS update is confirmed successful.
  • Full-zone invalidation is no longer the normal path for record mutations.
  • Per-zone invalidate policy is configurable in dns_zone_settings:
    • cache_invalidate_enabled default: false
    • cache_invalidate_interval_seconds default: 259200 (3 days) when enabled
  • Scheduled invalidation is handled by dns:cache:invalidate and only runs for zones where policy is enabled and due.
  • Manual POST /api/dns/zones/{zone}/cache/clear is now treated as a policy-controlled fallback operation: it stays disabled by default and only works when that zone has cache invalidation enabled.

Prerequisites

For API Users

You need:

  • A user account on tools.tornevall.net
  • Admin privileges OR explicit zone permissions
  • A valid session (login via web) OR an API token for your zone

For Token-Based Access

Request a token in the DNS Editor UI. Tokens include:

  • Secret - Bearer token for API authentication
  • Zone Key - Zone identifier (for reference)

Tokens are managed per-zone, similar to Cloudflare's zone API tokens.

Note: This API requires session-based or token-based authentication. It is designed for:

  • Web applications running on the same domain
  • Administrators managing zones through the web dashboard
  • Server-side integrations with session or token handling

Access Control & Roles

User Roles

Each user can have different roles for different zones:

  • Viewer (👁️) - Read-only access. Can list zones and view records.
  • Editor (✏️) - Read + write access. Can modify records (when editor feature launches).
  • Owner (👑) - Full control. Can manage records, users, and tokens for the zone.

Admin Users

  • Can access and manage all zones without permissions
  • Can delegate zones to users with specific roles
  • Can create, regenerate, and revoke API tokens
  • Can view all tokens and their usage

Regular Users

  • By default: No access to any zone
  • If granted permission: Can access specific zones with assigned role
  • Can request tokens for zones they own

Granting Zone Access to Users

As an admin, you can grant specific users access to specific zones through the web interface:

  1. Navigate to /admin/dns/zone/{zoneName}
  2. Select a user and assign a role (Viewer, Editor, or Owner)
  3. Click "Grant Access"
  4. The user can now access that zone via the API

Available Roles:

  • Viewer (👁️) - Read-only access
  • Editor (✏️) - Can modify records
  • Owner (👑) - Full control

Token Management

Each zone can have one active API token consisting of:

  • Secret - Bearer token for authentication (e.g., dns_secret_abc123...)
  • Zone Key - Zone identifier (e.g., zone_xyz789...)

Users with Owner or Editor roles can:

  1. Request an API token for their zone via /dns/tokens
  2. Use the token in API calls
  3. Regenerate tokens if compromised

Admins can:

  1. Create tokens for any zone via /admin/dns/zone/{zone}
  2. Regenerate or revoke tokens anytime
  3. View when tokens were last used

API Endpoints

List All Zones

Endpoint: GET /dns/zones

Description: Returns a list of all editable zones discovered from BIND configuration files.

Requirements:

  • Must be logged in as an authenticated user
  • Must have admin privileges (is_admin = true)

Request:

# In a browser (automatic session handling)
curl -X GET "https://tools.tornevall.net/dns/zones" \
  --cookie "path/to/cookie_jar.txt"

Response (Success):

{
  "ok": true,
  "count": 3,
  "zones": [
    {
      "zone": "tornevall.net",
      "file": "master/tornevall/tornevall.net",
      "key": "tornevall.net"
    },
    {
      "zone": "example.com",
      "file": "master/example/example.com",
      "key": "example.com"
    },
    {
      "zone": "10.10.10.in-addr.arpa",
      "file": "master/reverse/10.10.10.in-addr.arpa",
      "key": "10.10.10.in-addr.arpa"
    }
  ]
}

Response (Forbidden - No Permission):

{
  "ok": false,
  "reason": "forbidden"
}

Status Codes:

  • 200 OK - Request successful
  • 403 Forbidden - User lacks admin privileges or zone access
  • 404 Not Found - Required paths missing on server

Notes:

  • Includes both forward zones and reverse PTR zones
  • Only returns zones if both zones directory and keys directory are readable
  • Zone names are discovered from BIND .conf files

Get Zone Details

Endpoint: GET /dns/zones/{zoneName}

Description: Retrieves metadata and parsed records for a specific zone. Admin users can access any zone; non-admin users are currently denied (permission framework placeholder for future zone-level permissions).

Parameters:

  • zoneName (string, required) - The zone name (e.g., tornevall.net)

Requirements:

  • Must be logged in as an authenticated user
  • Must have admin privileges (is_admin = true)
  • Future: Zone-level permissions table (dns_zone_permissions) will allow granular per-user zone access

Request:

# In a browser (automatic session handling)
curl -X GET "https://tools.tornevall.net/dns/zones/tornevall.net" \
  --cookie "path/to/cookie_jar.txt"

Response (Success):

{
  "ok": true,
  "zone": "tornevall.net",
  "named": {
    "file": "master/tornevall/tornevall.net",
    "key": "tornevall.net"
  },
  "zoneData": [
    {
      "name": "tornevall.net",
      "ttl": "3600",
      "class": "IN",
      "type": "SOA",
      "rdata": "ns1.tornevall.net. admin.tornevall.net. 2026021301 10800 3600 604800 3600",
      "rawLine": "tornevall.net  3600  IN  SOA  ns1.tornevall.net. admin.tornevall.net. 2026021301 10800 3600 604800 3600"
    },
    {
      "name": "tornevall.net",
      "ttl": "3600",
      "class": "IN",
      "type": "NS",
      "rdata": "ns1.tornevall.net.",
      "rawLine": "tornevall.net  3600  IN  NS  ns1.tornevall.net."
    },
    {
      "name": "www.tornevall.net",
      "ttl": "3600",
      "class": "IN",
      "type": "A",
      "rdata": "192.168.1.100",
      "rawLine": "www.tornevall.net  3600  IN  A  192.168.1.100"
    },
    {
      "name": "mail.tornevall.net",
      "ttl": "3600",
      "class": "IN",
      "type": "A",
      "rdata": "192.168.1.101",
      "rawLine": "mail.tornevall.net  3600  IN  A  192.168.1.101"
    },
    {
      "name": "tornevall.net",
      "ttl": "3600",
      "class": "IN",
      "type": "MX",
      "rdata": "10 mail.tornevall.net.",
      "rawLine": "tornevall.net  3600  IN  MX  10 mail.tornevall.net."
    }
  ]
}

Response (Zone Not Found):

{
  "ok": false,
  "reason": "zone_not_found"
}

Response (Forbidden):

{
  "ok": false,
  "reason": "forbidden"
}

Response (Missing Required Paths):

{
  "ok": false,
  "reason": "required_paths_missing_or_unreadable"
}

Status Codes:

  • 200 OK - Zone found and details retrieved
  • 403 Forbidden - User lacks permission to access zone
  • 404 Not Found - Zone not found or required paths missing

Access Control:

  • Admins - Can access all zones
  • Non-Admins - Currently denied (placeholder for future zone permission table)

Notes:

  • zoneData field is optional and will be null if the zone file cannot be read
  • Zone file path is resolved from the BIND configuration file target (relative paths supported)
  • Records are parsed conservatively (no $INCLUDE or $GENERATE support)

Record Format

Each parsed DNS record in zoneData contains:

Field Type Description
name string Record owner name (@ expands to zone origin, empty inherits from previous)
ttl string | null Time-to-live in seconds (null if using default TTL)
class string DNS class: IN, CH, or HS (defaults to IN)
type string Record type: A, AAAA, CNAME, MX, NS, SOA, TXT, etc.
rdata string Record data (e.g., IP address, hostname, text)
rawLine string Original line from zone file (for reference/debugging)

Zone File Parsing

Supported Syntax

The API parses zone files with the following features:

  • Standard Records - A, AAAA, CNAME, MX, NS, SOA, TXT, SRV, CAA, PTR, etc.
  • TTL - Per-record or default via $TTL directive
  • Classes - IN, CH, HS (defaults to IN)
  • Comments - Lines starting with ; are stripped
  • Multi-line Records - Basic support for parentheses continuation
  • Name Inheritance - Empty name field inherits previous record name
  • Origin Support - $ORIGIN directive recognized

Unsupported Features

The parser conservatively excludes:

  • $INCLUDE directives
  • $GENERATE directives
  • Complex DNSSEC extensions
  • Non-standard directives

Note: If you need advanced parsing, consider requesting an enhanced version.


Error Handling

Common Error Responses

Reason Status Description
forbidden 403 User lacks permission to access zones or this zone
zone_not_found 404 Requested zone doesn't exist
required_paths_missing_or_unreadable 404 Server not properly configured (contact admin)

Examples

Example 1: List All Zones (Browser)

Simply navigate to:

https://tools.tornevall.net/dns/zones

You'll get JSON output of all available zones (if you're logged in as an admin).

Example 2: List All Zones (curl with cookies)

#!/bin/bash

# First, login via the login endpoint or use existing session cookies
COOKIE_JAR="/tmp/cookies.txt"

# Login
curl -X POST "https://tools.tornevall.net/login" \
  --cookie-jar "$COOKIE_JAR" \
  -d "email=admin@example.com&password=yourpassword"

# List zones
curl -X GET "https://tools.tornevall.net/dns/zones" \
  --cookie "$COOKIE_JAR" \
  -s | jq .

Output:

{
  "ok": true,
  "count": 2,
  "zones": [
    {
      "zone": "tornevall.net",
      "file": "master/tornevall/tornevall.net",
      "key": "tornevall.net"
    }
  ]
}

Example 3: Retrieve and Filter Zone Records (curl)

#!/bin/bash

COOKIE_JAR="/tmp/cookies.txt"
ZONE="tornevall.net"

# Get all records
RESPONSE=$(curl -X GET "https://tools.tornevall.net/dns/zones/${ZONE}" \
  --cookie "$COOKIE_JAR" \
  -s)

# Filter MX records
echo "$RESPONSE" | jq '.zoneData[] | select(.type == "MX")'

Output:

{
  "name": "tornevall.net",
  "ttl": "3600",
  "class": "IN",
  "type": "MX",
  "rdata": "10 mail.tornevall.net.",
  "rawLine": "tornevall.net  3600  IN  MX  10 mail.tornevall.net."
}

Example 4: JavaScript / Fetch API (SPA/Frontend)

const baseUrl = 'https://tools.tornevall.net';

// Fetch all zones
async function listZones() {
  const response = await fetch(`${baseUrl}/dns/zones`, {
    method: 'GET',
    credentials: 'include' // Include session cookie
  });
  const data = await response.json();
  
  if (data.ok) {
    console.log(`Found ${data.count} zones:`, data.zones);
  } else {
    console.error('Error:', data.reason);
  }
}

// Fetch specific zone with records
async function getZoneDetails(zoneName) {
  const response = await fetch(`${baseUrl}/dns/zones/${zoneName}`, {
    method: 'GET',
    credentials: 'include'
  });
  const data = await response.json();
  
  if (data.ok) {
    console.log(`Zone: ${data.zone}`);
    console.log(`File: ${data.named.file}`);
    console.log(`Records:`, data.zoneData);
  } else {
    console.error(`Error: ${data.reason}`);
  }
}

// Usage
listZones();
getZoneDetails('tornevall.net');

Example 5: Filter and Display Zone Records

async function displayZoneRecords(zoneName, recordType = null) {
  const response = await fetch(`https://tools.tornevall.net/dns/zones/${zoneName}`, {
    method: 'GET',
    credentials: 'include'
  });
  
  const data = await response.json();
  
  if (!data.ok) {
    console.error(`Cannot load zone: ${data.reason}`);
    return;
  }
  
  // Filter records if type specified
  let records = data.zoneData || [];
  if (recordType) {
    records = records.filter(r => r.type === recordType.toUpperCase());
  }
  
  // Display in table
  console.table(records);
}

// Get all A records for tornevall.net
displayZoneRecords('tornevall.net', 'A');

} }

// Fetch specific zone with records async function getZoneDetails(zoneName) { const response = await fetch(${apiBase}/dns/zones/${zoneName}, { method: 'GET', credentials: 'include' }); const data = await response.json();

if (data.ok) { console.log(Zone: ${data.zone}); console.log(File: ${data.named.file}); console.log(Records:, data.zoneData); } else { console.error(Error: ${data.reason}); } }

// Usage listZones(); getZoneDetails('tornevall.net');


---

## Security Considerations

### Authentication Required
- All endpoints require valid session authentication
- Anonymous requests are rejected with `401 Unauthorized`

### Authorization
- **Admin Users** - Full access to all zones
- **Regular Users** - Access only to zones they have explicit permission for
- Zone permissions are managed via the `dns_zone_permissions` table

### Best Practices
- Use HTTPS only (always use `https://` in your requests)
- Session cookies are `HttpOnly` and `Secure` flagged
- Never share your login credentials or session cookies
- Regularly review zone access permissions for inactive users

---

## Limitations & Roadmap

### Current Limitations
- ✅ Session-based auth only (no API tokens yet)
- ✅ Admin-only zone listing (future: per-zone permissions filtering)
- ✅ Read-only access (no zone creation/modification yet)
- ✅ Basic zone file parsing (no `$INCLUDE` or `$GENERATE`)

### Planned Features
1. **Zone Editor** - Create, modify, delete DNS records
2. **API Tokens** - Programmatic access for integrations
3. **Per-Zone Permissions** - Filter listed zones by user permissions
4. **DNSSEC Support** - Parse and validate DNSSEC records
5. **Audit Logging** - Track all zone access and modifications
6. **Real-time Validation** - Check records against live DNS

---

## Related Documentation

- [MCU API Documentation](/docs/mcu-api)
- [RSS Watch System](/docs/rsswatch)
- [OpenAI Documentation](/docs/openai)

---

## Support

For issues or questions:
1. Ensure you're logged in with the correct account
2. Verify you have admin privileges or zone-specific permissions
3. Check that DNS zones are properly configured on the server
4. Contact an administrator for permission grants or server issues

**Last Updated:** 2026-02-13