tdns

package module
v2.0.0-...-e5c0ba0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2026 License: BSD-2-Clause Imports: 65 Imported by: 3

Documentation

Overview

* Copyright (c) 2025 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected] * * Agent distribution management API endpoints

* Copyright (c) 2025 Johan Stenstam, [email protected] * * API handlers for tdns-auth peer management (multi-provider DNSSEC). * /auth/peer: peer-ping, status commands. * /auth/distrib: peer listing (same pattern as agent/combiner distrib).

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected] * * Combiner distribution management API endpoints

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected] * * Transaction diagnostic API endpoints for agents and combiners. * Provides visibility into open outgoing/incoming transactions and error history.

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected] * * DBDelegationBackend stores child delegation data in the ChildDelegationData * SQLite table. Extracts and replaces the existing ApplyChildUpdateToDB logic.

* Copyright (c) 2026 Johan Stenstam, [email protected] * * DirectDelegationBackend applies child UPDATEs directly to in-memory zone data. * This is the original behavior for Primary zones.

* Copyright (c) 2026 Johan Stenstam, [email protected] * * ZonefileDelegationBackend writes per-child delegation data as DNS zone file * fragments. Each child zone gets its own file that can be $INCLUDEd into the * parent zone file. Files are written atomically (write-to-temp + rename).

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected] * * In-memory error journal for the combiner/receiver. Records errors that occur * during HandleChunkNotify processing, indexed by distribution ID for targeted * diagnostic queries.

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, <[email protected]>

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2025 Johan Stenstam, [email protected]

* Copyright (c) 2025 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Transport signal synthesis (SVCB / TSYNC)

* Copyright (c) 2025 Johan Stenstam

* Copyright (c) DNS TAPIR

* Copyright (c) 2024 Johan Stenstam, [email protected]

* Copyright (c) 2026 Johan Stenstam, [email protected] * * Wrappers for unexported functions/methods needed by tdns-mp. * These export functionality that tdns-mp cannot access directly * because it is a different package.

* Copyright (c) 2024 Johan Stenstam, [email protected]

Index

Constants

View Source
const (
	MsgAccept               dns.MsgAcceptAction = iota // Accept the message
	MsgReject                                          // Reject the message with a RcodeFormatError
	MsgIgnore                                          // Ignore the error and send nothing back.
	MsgRejectNotImplemented                            // Reject the message with a RcodeNotImplemented
)

Allowed returned values from a MsgAcceptFunc.

View Source
const (
	MaxOperationsPerUpdate = 1000 // Maximum operations in a single update
	MaxRecordsPerOwner     = 500  // Maximum records per owner per RRtype in a single operation
)

Resource limits to prevent abuse via oversized updates

View Source
const (
	AgentMsgHello  = core.AgentMsgHello
	AgentMsgBeat   = core.AgentMsgBeat
	AgentMsgNotify = core.AgentMsgNotify
	AgentMsgRfi    = core.AgentMsgRfi
	AgentMsgStatus = core.AgentMsgStatus
	AgentMsgPing   = core.AgentMsgPing
	AgentMsgEdits  = core.AgentMsgEdits
)
View Source
const (
	// DNS limitations
	MaxDomainLength = 253 // Maximum length of a domain name (RFC 1035)
	MaxLabelLength  = 63  // Maximum length of a single label (RFC 1035)

	// Default cookie for chunk identification
	DefaultCookie = "c0"

	// Sequence number format (e.g., "00-")
	SequenceFormat = "%02d-"
	SequenceLength = 3 // XX- is 3 characters
)
View Source
const (
	DogCfgFile = "/etc/axfr.net/dog.yaml"

	// Legacy constants for backward compatibility
	// New code should use GetDefaultConfigFile() instead
	DefaultCliCfgFile = "/etc/tdns/tdns-cli.yaml"
	DefaultImrCfgFile = "/etc/tdns/tdns-imr.yaml"

	DefaultAuthCfgFile = "/etc/tdns/tdns-auth.yaml"

	DefaultAgentCfgFile    = "/etc/tdns/tdns-agent.yaml"
	DefaultCombinerCfgFile = "/etc/tdns/tdns-combiner.yaml"
	DefaultReporterCfgFile = "/etc/tdns/tdns-reporter.yaml"
	DefaultScannerCfgFile  = "/etc/tdns/tdns-scanner.yaml"
	DefaultKdcCfgFile      = "/etc/tdns/tdns-kdc.yaml"
	DefaultKrsCfgFile      = "/etc/tdns/tdns-krs.yaml"
)
View Source
const (
	UpdateModeReplace = "replace" // Replace all existing delegation data with new data
	UpdateModeDelta   = "delta"   // Use incremental adds/removes (delta updates)
)

Update mode constants for parent delegation updates

View Source
const (
	Sig0StateCreated     string = "created"
	Sig0StatePublished   string = "published"
	Sig0StateActive      string = "active"
	Sig0StateRetired     string = "retired"
	DnskeyStateCreated   string = "created"
	DnskeyStateMpdist    string = "mpdist"   // multi-provider distribution: awaiting confirmation from all providers
	DnskeyStateMpremove  string = "mpremove" // multi-provider removal: awaiting confirmation from all providers
	DnskeyStatePublished string = "published"
	DnskeyStateStandby   string = "standby"
	DnskeyStateActive    string = "active"
	DnskeyStateRetired   string = "retired"
	DnskeyStateRemoved   string = "removed"
	DnskeyStateForeign   string = "foreign"
)
View Source
const (
	SvcbTransportKey uint16 = 65280
	SvcbTLSAKey      uint16 = 65281
	SvcbBootstrapKey uint16 = 65282 // "bootstrap" SvcParamKey per draft-ietf-dnsop-delegation-mgmt-via-ddns-01
)

Private-use SVCB key codes for TDNS. RFC 9460 reserves 65280–65534 for local assignments.

View Source
const DefaultDnskeyTTL = 3600 * time.Second

DefaultDnskeyTTL is the assumed DNSKEY RRset TTL for multi-provider gating. Used when the actual TTL is not readily available. 3600 seconds (1 hour) is a common DNSKEY RRset TTL.

View Source
const (
	TimeLayout = "2006-01-02 15:04:05"
)

Variables

View Source
var AgentMsgToString = core.AgentMsgToString
View Source
var AgentOptionToString = map[AgentOption]string{}
View Source
var AgentStateToString = map[AgentState]string{
	AgentStateNeeded:      "NEEDED",
	AgentStateKnown:       "KNOWN",
	AgentStateIntroduced:  "INTRODUCED",
	AgentStateOperational: "OPERATIONAL",
	AgentStateLegacy:      "LEGACY",
	AgentStateDegraded:    "DEGRADED",
	AgentStateInterrupted: "INTERRUPTED",
	AgentStateError:       "ERROR",
}
View Source
var AllowedLocalRRtypes = AllowedRRtypePresets["apex-combiner"]

AllowedLocalRRtypes is the active preset. Default: "apex-combiner".

View Source
var AllowedRRtypePresets = map[string]map[uint16]bool{
	"apex-combiner": {
		dns.TypeDNSKEY: true,
		dns.TypeCDS:    true,
		dns.TypeCSYNC:  true,
		dns.TypeNS:     true,
		dns.TypeKEY:    true,
	},
}

Named presets for allowed RRtypes. Hardcoded for safety. "apex-combiner": manages DNSKEY, CDS, CSYNC, NS, KEY at the zone apex. "delegation-combiner": (future) manages NS, DS, GLUE at delegation points.

View Source
var AppTypeToString = map[AppType]string{
	AppTypeAuth:       "auth",
	AppTypeAgent:      "agent",
	AppTypeCombiner:   "combiner",
	AppTypeImr:        "imr",
	AppTypeCli:        "cli",
	AppTypeReporter:   "reporter",
	AppTypeScanner:    "scanner",
	AppTypeKdc:        "kdc",
	AppTypeKrs:        "krs",
	AppTypeEdgeSigner: "edgeSigner",
	AppTypeMPSigner:   "mpsigner",
	AppTypeMPAgent:    "mpagent",
	AppTypeMPCombiner: "mpcombiner",
}
View Source
var AuthOptionToString = map[AuthOption]string{
	AuthOptParentUpdate:          "parent-update",
	AuthOptPersistOutboundSerial: "persist-outbound-serial",
}
View Source
var CombinerOptionToString = map[CombinerOption]string{
	CombinerOptAddSignature: "add-signature",
}
View Source
var DefaultTables = map[string]string{

	"ChildDnskeys": `CREATE TABLE IF NOT EXISTS 'ChildDnskeys' (
id		  INTEGER PRIMARY KEY,
parent	  TEXT,
child	  TEXT,
keyid	  INTEGER,
trusted	  INTEGER,
keyrr	  TEXT,
comment	  TEXT,
UNIQUE (parent, child, keyid)
)`,

	"ChildDelegationData": `CREATE TABLE IF NOT EXISTS 'ChildDelegationData' (
id		  INTEGER PRIMARY KEY,
parent	  TEXT,
child	  TEXT,
owner	  TEXT,
rrtype	  TEXT,
rr		  TEXT,
UNIQUE (owner,rr)
)`,

	"Sig0TrustStore": `CREATE TABLE IF NOT EXISTS 'Sig0TrustStore' (
id		  		  INTEGER PRIMARY KEY,
zonename	  	  TEXT,
keyid		      INTEGER,
validated	      INTEGER DEFAULT 0,
trusted		      INTEGER DEFAULT 0,
dnssecvalidated	  INTEGER DEFAULT 0,
source		      TEXT,
keyrr		      TEXT,
comment		      TEXT,
UNIQUE (zonename, keyid)
)`,

	"Sig0KeyStore": `CREATE TABLE IF NOT EXISTS 'Sig0KeyStore' (
id		  INTEGER PRIMARY KEY,
zonename	  TEXT,
state		  TEXT,
keyid		  INTEGER,
algorithm	  TEXT,
creator	  	  TEXT,
privatekey	  TEXT,
keyrr		  TEXT,
comment		  TEXT,
parent_state	  INTEGER DEFAULT 0,
UNIQUE (zonename, keyid)
)`,

	"DnssecKeyStore": `CREATE TABLE IF NOT EXISTS 'DnssecKeyStore' (
id		  INTEGER PRIMARY KEY,
zonename	  TEXT,
state		  TEXT,
keyid		  INTEGER,
flags		  INTEGER,
algorithm	  TEXT,
creator	  	  TEXT,
privatekey	  TEXT,
keyrr		  TEXT,
comment		  TEXT,
propagation_confirmed     INTEGER DEFAULT 0,
propagation_confirmed_at  TEXT DEFAULT '',
published_at              TEXT DEFAULT '',
retired_at                TEXT DEFAULT '',
UNIQUE (zonename, keyid)
)`,
}
View Source
var ErrNotHandled = errors.New("query not handled by this handler")

ErrNotHandled is returned by query/notify handlers to indicate they don't handle this request. TDNS will try the next handler or fall back to the default handler.

View Source
var ErrZoneNotReady = errors.New("zone data is not yet ready")

ErrZoneNotReady is returned by GetOwner/GetRRset when the zone data has not been loaded yet (zd.Ready == false). Callers that need to handle initial-load gracefully can check with errors.Is.

View Source
var ErrorTypeToString = map[ErrorType]string{
	ConfigError:  "config",
	RefreshError: "refresh",
	AgentError:   "agent",
	DnssecError:  "DNSSEC",
}
View Source
var Globals = GlobalStuff{

	Verbose:    false,
	Debug:      false,
	ApiClients: map[string]*ApiClient{},
}
View Source
var HsyncIndexes = []string{

	`CREATE INDEX IF NOT EXISTS idx_peer_registry_state ON PeerRegistry(state)`,
	`CREATE INDEX IF NOT EXISTS idx_peer_registry_last_contact ON PeerRegistry(last_contact_at)`,

	`CREATE INDEX IF NOT EXISTS idx_peer_zones_zone ON PeerZones(zone_name)`,
	`CREATE INDEX IF NOT EXISTS idx_peer_zones_peer ON PeerZones(peer_id)`,

	`CREATE INDEX IF NOT EXISTS idx_sync_ops_zone ON SyncOperations(zone_name)`,
	`CREATE INDEX IF NOT EXISTS idx_sync_ops_status ON SyncOperations(status)`,
	`CREATE INDEX IF NOT EXISTS idx_sync_ops_created ON SyncOperations(created_at)`,
	`CREATE INDEX IF NOT EXISTS idx_sync_ops_sender ON SyncOperations(sender_id)`,
	`CREATE INDEX IF NOT EXISTS idx_sync_ops_receiver ON SyncOperations(receiver_id)`,

	`CREATE INDEX IF NOT EXISTS idx_sync_confirm_distribution ON SyncConfirmations(distribution_id)`,
	`CREATE INDEX IF NOT EXISTS idx_sync_confirm_status ON SyncConfirmations(status)`,

	`CREATE INDEX IF NOT EXISTS idx_metrics_time ON OperationalMetrics(metric_time)`,
	`CREATE INDEX IF NOT EXISTS idx_metrics_peer ON OperationalMetrics(peer_id)`,

	`CREATE INDEX IF NOT EXISTS idx_events_time ON TransportEvents(event_time)`,
	`CREATE INDEX IF NOT EXISTS idx_events_peer ON TransportEvents(peer_id)`,
	`CREATE INDEX IF NOT EXISTS idx_events_type ON TransportEvents(event_type)`,
	`CREATE INDEX IF NOT EXISTS idx_events_expires ON TransportEvents(expires_at)`,

	`CREATE INDEX IF NOT EXISTS idx_pending_edits_zone ON CombinerPendingEdits(zone)`,

	`CREATE INDEX IF NOT EXISTS idx_approved_edits_zone ON CombinerApprovedEdits(zone)`,

	`CREATE INDEX IF NOT EXISTS idx_rejected_edits_zone ON CombinerRejectedEdits(zone)`,

	`CREATE INDEX IF NOT EXISTS idx_publish_instr_zone ON CombinerPublishInstructions(zone)`,

	`CREATE INDEX IF NOT EXISTS idx_contributions_zone ON CombinerContributions(zone)`,
	`CREATE INDEX IF NOT EXISTS idx_contributions_zone_sender ON CombinerContributions(zone, sender_id)`,
}

HsyncIndexes defines indexes for the HSYNC tables.

View Source
var HsyncTables = map[string]string{

	"PeerRegistry": `CREATE TABLE IF NOT EXISTS 'PeerRegistry' (
		id                    INTEGER PRIMARY KEY AUTOINCREMENT,
		peer_id               TEXT NOT NULL UNIQUE,

		-- Discovery information
		discovery_time        INTEGER NOT NULL,          -- Unix timestamp when first discovered
		discovery_source      TEXT,                       -- "hsync", "manual", "dns"

		-- API transport details
		api_endpoint          TEXT,                       -- Full URL for API transport
		api_host              TEXT,                       -- Hostname for API transport
		api_port              INTEGER,                    -- Port for API transport
		api_tlsa_record       TEXT,                       -- TLSA record (wire format, base64)
		api_available         INTEGER DEFAULT 0,          -- 1 if API transport is available

		-- DNS transport details
		dns_host              TEXT,                       -- Hostname for DNS transport
		dns_port              INTEGER DEFAULT 53,         -- Port for DNS transport
		dns_key_record        TEXT,                       -- KEY record (wire format, base64)
		dns_available         INTEGER DEFAULT 0,          -- 1 if DNS transport is available

		-- Operational address (may differ from discovery address for DDoS mitigation)
		operational_host      TEXT,
		operational_port      INTEGER,
		operational_transport TEXT,                       -- "udp", "tcp", "https"

		-- Public keys for encryption/verification
		encryption_pubkey     TEXT,                       -- JWK format public key for encryption
		verification_pubkey   TEXT,                       -- JWK format public key for signature verification

		-- State and preferences
		state                 TEXT DEFAULT 'needed',      -- needed, known, introducing, operational, degraded, interrupted, error
		state_reason          TEXT,                       -- Reason for current state
		state_changed_at      INTEGER,                    -- Unix timestamp of last state change
		preferred_transport   TEXT DEFAULT 'api',         -- api, dns

		-- Metrics
		last_contact_at       INTEGER,                    -- Unix timestamp of last successful contact
		last_hello_at         INTEGER,                    -- Unix timestamp of last hello handshake
		last_beat_at          INTEGER,                    -- Unix timestamp of last heartbeat received
		beat_interval         INTEGER DEFAULT 30,         -- Expected heartbeat interval in seconds
		beats_sent            INTEGER DEFAULT 0,          -- Total heartbeats sent
		beats_received        INTEGER DEFAULT 0,          -- Total heartbeats received
		failed_contacts       INTEGER DEFAULT 0,          -- Consecutive failed contact attempts

		-- Metadata
		created_at            INTEGER NOT NULL,
		updated_at            INTEGER NOT NULL,

		UNIQUE(peer_id)
	)`,

	"PeerZones": `CREATE TABLE IF NOT EXISTS 'PeerZones' (
		id              INTEGER PRIMARY KEY AUTOINCREMENT,
		peer_id         TEXT NOT NULL,
		zone_name       TEXT NOT NULL,

		-- Relationship details
		relationship    TEXT DEFAULT 'peer',          -- peer, upstream, downstream
		role            TEXT,                          -- provider, owner, backup
		hsync_identity  TEXT,                          -- Identity from HSYNC record

		-- Sync state per zone
		last_sync_at    INTEGER,                       -- Unix timestamp of last successful sync
		sync_serial     INTEGER,                       -- Last synced SOA serial
		sync_state      TEXT DEFAULT 'pending',        -- pending, synced, conflict, error

		-- Metadata
		added_at        INTEGER NOT NULL,
		updated_at      INTEGER NOT NULL,

		UNIQUE(peer_id, zone_name),
		FOREIGN KEY(peer_id) REFERENCES PeerRegistry(peer_id) ON DELETE CASCADE
	)`,

	"SyncOperations": `CREATE TABLE IF NOT EXISTS 'SyncOperations' (
		id                INTEGER PRIMARY KEY AUTOINCREMENT,
		distribution_id   TEXT NOT NULL UNIQUE,

		-- Operation details
		zone_name         TEXT NOT NULL,
		sync_type         TEXT NOT NULL,              -- NS, DNSKEY, GLUE, CDS, CSYNC
		direction         TEXT NOT NULL,              -- outbound, inbound

		-- Participants
		sender_id         TEXT NOT NULL,
		receiver_id       TEXT NOT NULL,

		-- Payload
		records           TEXT,                        -- JSON array of RR strings
		serial            INTEGER,                     -- SOA serial at time of sync

		-- Transport used
		transport         TEXT,                        -- api, dns
		encrypted         INTEGER DEFAULT 0,           -- 1 if payload was encrypted

		-- Status tracking
		status            TEXT DEFAULT 'pending',      -- pending, sent, received, confirmed, failed, rejected
		status_message    TEXT,

		-- Timestamps
		created_at        INTEGER NOT NULL,
		sent_at           INTEGER,
		received_at       INTEGER,
		confirmed_at      INTEGER,
		expires_at        INTEGER,                     -- For replay protection

		-- Error tracking
		retry_count       INTEGER DEFAULT 0,
		last_error        TEXT,
		last_error_at     INTEGER
	)`,

	"SyncConfirmations": `CREATE TABLE IF NOT EXISTS 'SyncConfirmations' (
		id                INTEGER PRIMARY KEY AUTOINCREMENT,
		distribution_id   TEXT NOT NULL,

		-- Confirmation source
		confirmer_id      TEXT NOT NULL,              -- Peer that sent the confirmation

		-- Status
		status            TEXT NOT NULL,              -- success, partial, failed, rejected
		message           TEXT,

		-- Detailed results (JSON)
		items_processed   TEXT,                        -- JSON: [{record_type, zone, status, details}]

		-- Proof (optional)
		signed_proof      TEXT,                        -- DNSSEC signatures from signer
		confirmer_signature TEXT,                      -- JWS signature from confirmer

		-- Timestamps
		confirmed_at      INTEGER NOT NULL,
		received_at       INTEGER NOT NULL,

		FOREIGN KEY(distribution_id) REFERENCES SyncOperations(distribution_id) ON DELETE CASCADE
	)`,

	"OperationalMetrics": `CREATE TABLE IF NOT EXISTS 'OperationalMetrics' (
		id              INTEGER PRIMARY KEY AUTOINCREMENT,
		metric_time     INTEGER NOT NULL,             -- Unix timestamp (rounded to minute)
		peer_id         TEXT,                          -- NULL for aggregate metrics
		zone_name       TEXT,                          -- NULL for aggregate metrics

		-- Communication metrics
		syncs_sent      INTEGER DEFAULT 0,
		syncs_received  INTEGER DEFAULT 0,
		syncs_confirmed INTEGER DEFAULT 0,
		syncs_failed    INTEGER DEFAULT 0,

		-- Heartbeat metrics
		beats_sent      INTEGER DEFAULT 0,
		beats_received  INTEGER DEFAULT 0,
		beats_missed    INTEGER DEFAULT 0,

		-- Latency (milliseconds)
		avg_latency     INTEGER,
		max_latency     INTEGER,

		-- Transport breakdown
		api_operations  INTEGER DEFAULT 0,
		dns_operations  INTEGER DEFAULT 0,

		UNIQUE(metric_time, peer_id, zone_name)
	)`,

	"TransportEvents": `CREATE TABLE IF NOT EXISTS 'TransportEvents' (
		id              INTEGER PRIMARY KEY AUTOINCREMENT,
		event_time      INTEGER NOT NULL,
		peer_id         TEXT,
		zone_name       TEXT,

		-- Event details
		event_type      TEXT NOT NULL,                -- hello, beat, sync, relocate, confirm, error, state_change
		transport       TEXT,                          -- api, dns
		direction       TEXT,                          -- outbound, inbound

		-- Result
		success         INTEGER,                       -- 1 for success, 0 for failure
		error_code      TEXT,
		error_message   TEXT,

		-- Additional context (JSON)
		context         TEXT,

		-- Auto-cleanup: events older than 7 days can be purged
		expires_at      INTEGER
	)`,

	"CombinerPendingEdits": `CREATE TABLE IF NOT EXISTS 'CombinerPendingEdits' (
		id              INTEGER PRIMARY KEY AUTOINCREMENT,
		edit_id         INTEGER NOT NULL UNIQUE,
		zone            TEXT NOT NULL,
		sender_id       TEXT NOT NULL,
		delivered_by    TEXT NOT NULL DEFAULT '',
		distribution_id TEXT NOT NULL,
		records_json    TEXT NOT NULL,
		received_at     INTEGER NOT NULL
	)`,

	"CombinerApprovedEdits": `CREATE TABLE IF NOT EXISTS 'CombinerApprovedEdits' (
		id              INTEGER PRIMARY KEY AUTOINCREMENT,
		edit_id         INTEGER NOT NULL UNIQUE,
		zone            TEXT NOT NULL,
		sender_id       TEXT NOT NULL,
		distribution_id TEXT NOT NULL,
		records_json    TEXT NOT NULL,
		received_at     INTEGER NOT NULL,
		approved_at     INTEGER NOT NULL
	)`,

	"CombinerRejectedEdits": `CREATE TABLE IF NOT EXISTS 'CombinerRejectedEdits' (
		id              INTEGER PRIMARY KEY AUTOINCREMENT,
		edit_id         INTEGER NOT NULL UNIQUE,
		zone            TEXT NOT NULL,
		sender_id       TEXT NOT NULL,
		distribution_id TEXT NOT NULL,
		records_json    TEXT NOT NULL,
		received_at     INTEGER NOT NULL,
		rejected_at     INTEGER NOT NULL,
		reason          TEXT NOT NULL
	)`,

	"CombinerPublishInstructions": `CREATE TABLE IF NOT EXISTS 'CombinerPublishInstructions' (
		id                INTEGER PRIMARY KEY AUTOINCREMENT,
		zone              TEXT NOT NULL,
		sender_id         TEXT NOT NULL,
		key_rrs_json      TEXT NOT NULL DEFAULT '[]',
		cds_rrs_json      TEXT NOT NULL DEFAULT '[]',
		locations_json    TEXT NOT NULL DEFAULT '[]',
		published_ns_json TEXT NOT NULL DEFAULT '[]',
		updated_at        INTEGER NOT NULL,
		UNIQUE(zone, sender_id)
	)`,

	"CombinerContributions": `CREATE TABLE IF NOT EXISTS 'CombinerContributions' (
		id          INTEGER PRIMARY KEY AUTOINCREMENT,
		zone        TEXT NOT NULL,
		sender_id   TEXT NOT NULL,
		owner       TEXT NOT NULL,
		rrtype      INTEGER NOT NULL,
		rr          TEXT NOT NULL,
		updated_at  INTEGER NOT NULL,
		UNIQUE(zone, sender_id, owner, rrtype, rr)
	)`,

	"OutgoingSerials": `CREATE TABLE IF NOT EXISTS 'OutgoingSerials' (
		zone       TEXT NOT NULL PRIMARY KEY,
		serial     INTEGER NOT NULL,
		updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
	)`,
}

HsyncTables defines the database tables for HSYNC functionality. These are added to the KeyDB during initialization.

View Source
var ImrOptionToString = map[ImrOption]string{
	ImrOptRevalidateNS:            "revalidate-ns",
	ImrOptQueryForTransport:       "query-for-transport",
	ImrOptAlwaysQueryForTransport: "always-query-for-transport",
	ImrOptTransportSignalType:     "transport-signal-type",
	ImrOptQueryForTransportTLSA:   "query-for-transport-tlsa",
	ImrOptUseTransportSignals:     "use-transport-signals",
}
View Source
var KnownCsyncMinSOAs = map[string]uint32{}

Check the minsoa in this CSYNC against the minsoa in the possible already stored CSYNC in the CsyncStatus table. If not found or old min_soa is lower, then update table.

View Source
var ScanTypeToString = map[ScanType]string{
	ScanRRtype: "rrtype",
	ScanCDS:    "cds",
	ScanCSYNC:  "csync",
	ScanDNSKEY: "dnskey",
}
View Source
var ServerName string = "PLACEHOLDER"
View Source
var SignerOptionToString = map[SignerOption]string{}
View Source
var StringToAgentOption = map[string]AgentOption{}
View Source
var StringToAppType = map[string]AppType{
	"auth":       AppTypeAuth,
	"agent":      AppTypeAgent,
	"combiner":   AppTypeCombiner,
	"imr":        AppTypeImr,
	"cli":        AppTypeCli,
	"reporter":   AppTypeReporter,
	"scanner":    AppTypeScanner,
	"kdc":        AppTypeKdc,
	"krs":        AppTypeKrs,
	"edgeSigner": AppTypeEdgeSigner,
	"mpsigner":   AppTypeMPSigner,
	"mpagent":    AppTypeMPAgent,
	"mpcombiner": AppTypeMPCombiner,
}
View Source
var StringToAuthOption = map[string]AuthOption{
	"parent-update":           AuthOptParentUpdate,
	"persist-outbound-serial": AuthOptPersistOutboundSerial,
}
View Source
var StringToCombinerOption = map[string]CombinerOption{
	"add-signature": CombinerOptAddSignature,
}
View Source
var StringToImrOption = map[string]ImrOption{
	"revalidate-ns":              ImrOptRevalidateNS,
	"query-for-transport":        ImrOptQueryForTransport,
	"always-query-for-transport": ImrOptAlwaysQueryForTransport,
	"transport-signal-type":      ImrOptTransportSignalType,
	"query-for-transport-tlsa":   ImrOptQueryForTransportTLSA,
	"use-transport-signals":      ImrOptUseTransportSignals,
}
View Source
var StringToScanType = map[string]ScanType{
	"rrtype": ScanRRtype,
	"cds":    ScanCDS,
	"csync":  ScanCSYNC,
	"dnskey": ScanDNSKEY,
}
View Source
var StringToSignerOption = map[string]SignerOption{}
View Source
var StringToZoneOption = map[string]ZoneOption{
	"delegation-sync-parent":     OptDelSyncParent,
	"delegation-sync-child":      OptDelSyncChild,
	"allow-updates":              OptAllowUpdates,
	"allow-child-updates":        OptAllowChildUpdates,
	"allow-edits":                OptAllowEdits,
	"fold-case":                  OptFoldCase,
	"black-lies":                 OptBlackLies,
	"dont-publish-key":           OptDontPublishKey,
	"dont-publish-jwk":           OptDontPublishJWK,
	"online-signing":             OptOnlineSigning,
	"inline-signing":             OptInlineSigning,
	"multi-provider":             OptMultiProvider,
	"dirty":                      OptDirty,
	"frozen":                     OptFrozen,
	"automatic-zone":             OptAutomaticZone,
	"add-transport-signal":       OptAddTransportSignal,
	"catalog-zone":               OptCatalogZone,
	"catalog-member-auto-create": OptCatalogMemberAutoCreate,
	"catalog-member-auto-delete": OptCatalogMemberAutoDelete,
	"mp-manual-approval":         OptMPManualApproval,
	"multi-signer":               OptMultiSigner,
	"mp-not-listed-error":        OptMPNotListedErr,
	"mp-disallow-edits":          OptMPDisallowEdits,
}
View Source
var Templates = make(map[string]ZoneConf)

Add near the top of the file with other vars

View Source
var ZoneOptionToString = map[ZoneOption]string{
	OptDelSyncParent:     "delegation-sync-parent",
	OptDelSyncChild:      "delegation-sync-child",
	OptAllowUpdates:      "allow-updates",
	OptAllowChildUpdates: "allow-child-updates",
	OptAllowEdits:        "allow-edits",
	OptFoldCase:          "fold-case",
	OptBlackLies:         "black-lies",
	OptDontPublishKey:    "dont-publish-key",
	OptDontPublishJWK:    "dont-publish-jwk",
	OptOnlineSigning:     "online-signing",
	OptInlineSigning:     "inline-signing",
	OptMultiProvider:     "multi-provider",
	OptDirty:             "dirty",
	OptFrozen:            "frozen",
	OptAutomaticZone:     "automatic-zone",

	OptAddTransportSignal:      "add-transport-signal",
	OptCatalogZone:             "catalog-zone",
	OptCatalogMemberAutoCreate: "catalog-member-auto-create",
	OptCatalogMemberAutoDelete: "catalog-member-auto-delete",
	OptMPManualApproval:        "mp-manual-approval",
	OptMultiSigner:             "multi-signer",
	OptMPNotListedErr:          "mp-not-listed-error",
	OptMPDisallowEdits:         "mp-disallow-edits",
}
View Source
var ZoneStoreToString = map[ZoneStore]string{
	XfrZone:   "XfrZone",
	MapZone:   "MapZone",
	SliceZone: "SliceZone",
}
View Source
var ZoneTypeToString = map[ZoneType]string{
	Primary:   "primary",
	Secondary: "secondary",
}
View Source
var Zones = core.NewCmap[*ZoneData]()

Functions

func APICatalog

func APICatalog(app *AppDetails) func(w http.ResponseWriter, r *http.Request)

APICatalog handles catalog zone management operations

func APIauthDistrib

func APIauthDistrib(conf *Config) func(w http.ResponseWriter, r *http.Request)

APIauthDistrib handles /auth/distrib requests — peer listing for the signer. Uses the same JSON response format as agent/combiner distrib so that listDistribPeers/displayPeers works identically.

func APIauthPeer

func APIauthPeer(conf *Config) func(w http.ResponseWriter, r *http.Request)

APIauthPeer handles /auth/peer requests for multi-provider peer management.

func APIcombiner

func APIcombiner(app *AppDetails, refreshZoneCh chan<- ZoneRefresher, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func APIcombinerDebug

func APIcombinerDebug(conf *Config) func(w http.ResponseWriter, r *http.Request)

func APIcombinerEdits

func APIcombinerEdits(conf *Config) func(w http.ResponseWriter, r *http.Request)

APIcombinerEdits handles /combiner/edits requests for managing pending, approved and rejected edits.

func APIcommand

func APIcommand(conf *Config, rtr *mux.Router) func(w http.ResponseWriter, r *http.Request)

func APIcommand(stopCh chan struct{}) func(w http.ResponseWriter, r *http.Request) {

func APIconfig

func APIconfig(conf *Config) func(w http.ResponseWriter, r *http.Request)

func APIdebug

func APIdebug(conf *Config) func(w http.ResponseWriter, r *http.Request)

func APIdelegation

func APIdelegation(delsyncq chan DelegationSyncRequest) func(w http.ResponseWriter, r *http.Request)

func APIdispatcher

func APIdispatcher(conf *Config, router *mux.Router, done <-chan struct{}) error

func APIdispatcherNG

func APIdispatcherNG(conf *Config, router *mux.Router, addrs []string, certFile string, keyFile string, done <-chan struct{}) error

APIdispatcherNG differs from APIdispatcher in that it allows for a different set of addresses and certificate files to be used for the API server.

func APImultisigner

func APImultisigner(kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func APIping

func APIping(conf *Config) func(w http.ResponseWriter, r *http.Request)

func APIscanner

func APIscanner(conf *Config, app *AppDetails, scannerq chan ScanRequest, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func APIscannerDelete

func APIscannerDelete(conf *Config) func(w http.ResponseWriter, r *http.Request)

APIscannerDelete handles DELETE requests for scan job deletion

func APIscannerStatus

func APIscannerStatus(conf *Config) func(w http.ResponseWriter, r *http.Request)

APIscannerStatus handles GET requests for scan job status

func APIzone

func APIzone(app *AppDetails, refreshq chan ZoneRefresher, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func APIzoneDsync

func APIzoneDsync(ctx context.Context, app *AppDetails, refreshq chan ZoneRefresher, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func AddRouterCommandsToSwitch

func AddRouterCommandsToSwitch()

AddRouterCommandsToSwitch adds router command handling to the agent API switch statement. This should be called from APIagent handler to add the router command cases.

Example usage in apihandler_agent.go:

switch amp.Command {
// ... existing cases ...
case "router-list":
	if conf.Internal.TransportManager == nil || conf.Internal.TransportManager.Router == nil {
		resp.Error = true
		resp.ErrorMsg = "Router not available (DNS transport not configured)"
		return
	}
	routerResp := handleRouterList(conf.Internal.TransportManager.Router)
	resp = *routerResp
// ... add other router cases ...
}

func AgentToString

func AgentToString(a *Agent) string

func AuthDNSQuery

func AuthDNSQuery(qname string, lg *log.Logger, nameservers []string,
	rrtype uint16, verbose bool) (*core.RRset, int, error)

XXX: Do we still use this anywhere?

func AuthQuery

func AuthQuery(qname, ns string, rrtype uint16) ([]dns.RR, error)

func AuthQueryEngine

func AuthQueryEngine(ctx context.Context, requests chan AuthQueryRequest)

func AutoConfigureZonesFromCatalog

func AutoConfigureZonesFromCatalog(ctx context.Context, update *CatalogZoneUpdate, conf *Config) error

AutoConfigureZonesFromCatalog auto-configures zones based on catalog and meta groups

func BailiwickNS

func BailiwickNS(zonename string, nsrrs []dns.RR) ([]string, error)

Return the names of NS RRs that are in bailiwick for the zone.

func Base32Decode

func Base32Decode(data string) ([]byte, error)

Base32Decode decodes base32 data

func Base32Encode

func Base32Encode(data []byte) string

Base32Encode encodes data to base32

func CaseFoldContains

func CaseFoldContains(slice []string, str string) bool

func ChildDelegationDataUnsynched

func ChildDelegationDataUnsynched(zone, pzone, childpri, parpri string) (bool, []dns.RR, []dns.RR, error)

func ChildGlueRRsToAddrs

func ChildGlueRRsToAddrs(v4glue, v6glue []dns.RR) ([]string, error)

func ChildGlueRRsetsToAddrs

func ChildGlueRRsetsToAddrs(v4glue, v6glue []*core.RRset) ([]string, error)

func Chomp

func Chomp(s string) string

func ChunkBase32Data

func ChunkBase32Data(base32Data string, cookie string) []string

ChunkBase32Data splits base32 data into chunks of maximum size and adds cookie and sequence prefixes

func ChunksToDomains

func ChunksToDomains(chunks []string, domainSuffix string) []string

ChunksToDomains combines chunks into domain names It dynamically calculates how many chunks can fit in each domain

func CombinerMsgHandler

func CombinerMsgHandler(ctx context.Context, conf *Config, msgQs *MsgQs,
	protectedNamespaces []string, errorJournal *ErrorJournal)

CombinerMsgHandler consumes beat, hello, ping, and sync messages from MsgQs. Updates PeerRegistry liveness on beats and logs hello/ping messages. Processes sync messages asynchronously: applies zone updates via CombinerProcessUpdate and sends detailed confirmation back to the agent via DNSTransport.Confirm().

func CombinerStateSetChunkHandler

func CombinerStateSetChunkHandler(cs *CombinerState, handler *transport.ChunkNotifyHandler)

CombinerStateSetChunkHandler wraps setting the unexported chunkHandler field on CombinerState.

func ComputeBailiwickNS

func ComputeBailiwickNS(childpri, parpri, owner string) ([]string, []string, error)

XXX: Should be replaced by four calls: one per child and parent primary to get

the NS RRsets and one to new ComputeBailiwickNS() that takes a []dns.RR + zone name

func ComputeDo53Remainder

func ComputeDo53Remainder(pct map[string]uint8) uint8

ComputeDo53Remainder returns the implicit remainder for do53 (clip at 0).

func ComputeGroupHash

func ComputeGroupHash(identities []string) string

ComputeGroupHash computes a deterministic hash from a sorted, deduplicated list of provider identities. Uses length-prefixed encoding to prevent collisions (e.g., ["a","bb"] vs ["ab","b"]).

func ComputeRRDiff

func ComputeRRDiff(childpri, parpri, owner string, rrtype uint16) (bool, []dns.RR, []dns.RR, error)

Only used in the CLI version

func CopyFile

func CopyFile(src, dst string) (int64, error)

func CreateChildReplaceUpdate

func CreateChildReplaceUpdate(parent, child string, newNS, newA, newAAAA, newDS []dns.RR) (*dns.Msg, error)

CreateChildReplaceUpdate creates a DNS UPDATE message that replaces all delegation data CreateChildReplaceUpdate creates a DNS UPDATE message for parent that replaces the delegation for child. It removes all existing NS records for the child and deletes A/AAAA glue for any in-bailiwick nameservers discovered among the provided new NS, A, and AAAA records, then inserts the new NS and glue RRs. Returns an error if parent or child is empty or equal to ".".

func CreateChildUpdate

func CreateChildUpdate(parent, child string, adds, removes []dns.RR) (*dns.Msg, error)

Parent is the zone to apply the update to. XXX: This is to focused on creating updates for child delegation info. Need a more general CreateChildUpdate constructs a DNS UPDATE message for the given parent zone that applies the provided additions and removals for a child delegation.

If any removed RR is an NS whose target name is within the child zone, the function also removes A and AAAA glue RRsets for that NS name. It validates that parent and child are non-empty and not ".", returning an error when validation fails. When Globals.Debug is set, the resulting message is printed.

It returns the constructed DNS UPDATE message, or an error if validation fails.

func CreateUpdate

func CreateUpdate(zone string, adds, removes []dns.RR) (*dns.Msg, error)

CreateUpdate creates a DNS UPDATE message for the given zone, applies the provided removals and additions, and enables EDNS0 (payload 1232 with the DO bit set) so that EDNS0 Extended DNS Error (EDE) information can be returned. It returns the constructed *dns.Msg, or an error if the zone is empty or ".".

func DefaultQueryHandler

func DefaultQueryHandler(ctx context.Context, req *DnsQueryRequest) error

DefaultQueryHandler handles all other queries using zone-based query handling. This is registered with qtype=0 to catch all query types that aren't handled by other handlers. Exported so apps (like KDC) can register it even when no zones are in config.

func DiscoverAgentAPI

func DiscoverAgentAPI(ctx context.Context, imr *Imr, identity string, result *AgentDiscoveryResult)

DiscoverAgentAPI performs DNS-based discovery of an agent's API transport.

  1. URI record at _https._tcp.<identity> → get API endpoint URI and port
  2. SVCB record at api.<identity> → get ipv4hint/ipv6hint addresses
  3. TLSA record at _<port>._tcp.api.<identity> → get TLS certificate for verification

func DiscoverAgentDNS

func DiscoverAgentDNS(ctx context.Context, imr *Imr, identity string, result *AgentDiscoveryResult)

DiscoverAgentDNS performs DNS-based discovery of an agent's DNS transport.

  1. URI record at _dns._tcp.<identity> → get DNS endpoint URI and port
  2. SVCB record at dns.<identity> → get ipv4hint/ipv6hint addresses
  3. JWK record at dns.<identity> → get JOSE/HPKE public key (preferred)
  4. KEY record at dns.<identity> → get SIG(0) public key (legacy fallback if no JWK)

func DnsDoHEngine

func DnsDoHEngine(ctx context.Context, conf *Config, dohaddrs []string, certFile, keyFile string,
	ourDNSHandler func(w dns.ResponseWriter, r *dns.Msg)) error

func DnsDoQEngine

func DnsDoQEngine(ctx context.Context, conf *Config, doqaddrs []string, cert *tls.Certificate,
	ourDNSHandler func(w dns.ResponseWriter, r *dns.Msg)) error

func DnsDoTEngine

func DnsDoTEngine(ctx context.Context, conf *Config, dotaddrs []string, cert *tls.Certificate,
	ourDNSHandler func(w dns.ResponseWriter, r *dns.Msg)) error

func DnsEngine

func DnsEngine(ctx context.Context, conf *Config) error

func DomainsToBase32

func DomainsToBase32(domains []string, cookie string) (string, error)

DomainsToBase32 extracts and combines base32 data from domain names

func DomainsToJson

func DomainsToJson(domains []string, cookie string) ([]byte, error)

DomainsToJson converts domain names back to JSON

func DomainsToStruct

func DomainsToStruct(domains []string, cookie string, result interface{}) error

DomainsToStruct converts domain names back to a struct

func DotServerQnameResponse

func DotServerQnameResponse(qname string, w dns.ResponseWriter, r *dns.Msg)

func DsyncUpdateTargetName

func DsyncUpdateTargetName(zonename string) string

DsyncUpdateTargetName computes the DSYNC UPDATE target name for a parent zone from the global config. Returns empty string if not configured.

func ExpirationFromTtl

func ExpirationFromTtl(addedAt time.Time, ttl uint32) time.Time

ExpirationFromTtl converts an insertion time and TTL seconds to an expiration time. This is only used for formatting and display, not for cache logic.

func ExportDelegationData

func ExportDelegationData(backend DelegationBackend, parentZone, outfile string, defaultTTL uint32) error

ExportDelegationData writes all delegation data for a parent zone to a file in DNS zone file format. Called from the /delegation API handler. defaultTTL is applied to any RR with TTL=0.

func Fatal

func Fatal(msg string, args ...any)

Fatal logs at Error level and exits. slog has no built-in Fatal.

func FetchSVCB

func FetchSVCB(baseurl string, resolvers []string, timeout time.Duration,
	retries int) (*dns.SVCB, []string, uint16, string, error)

func FindSoaRefresh

func FindSoaRefresh(zd *ZoneData) (uint32, error)

func GenerateJobID

func GenerateJobID() (string, error)

GenerateJobID generates a unique job ID using crypto/rand.

func GetAlpn

func GetAlpn(svcb *dns.SVCB) []string

GetAlpn extracts the ALPN protocols from an SVCB RR (from SVCBAlpn param).

func GetDefaultConfigFile

func GetDefaultConfigFile() string

GetDefaultConfigFile returns the default config file path based on Globals.App.Name. The path is constructed as /etc/tdns/{app-name}.yaml. If Globals.App.Name is empty, it returns an empty string.

func GetDynamicZoneFilePath

func GetDynamicZoneFilePath(zoneName, zoneDirectory string) string

GetDynamicZoneFilePath returns the expected file path for a dynamic zone file This is useful for checking if a file exists before attempting to load it

func GetNameservers

func GetNameservers(KeyName string, zd *ZoneData) ([]string, error)

func GetProviderZoneRRtypes

func GetProviderZoneRRtypes(zone string) map[uint16]bool

GetProviderZoneRRtypes returns the allowed RRtype map for a provider zone, or nil if the zone is not configured as a provider zone.

func GetTransportParam

func GetTransportParam(svcb *dns.SVCB) (map[string]uint8, bool, error)

GetTransportParam fetches and parses the transport parameter from the SVCB RR, if present.

func HsyncEngine

func HsyncEngine(ctx context.Context, conf *Config, msgQs *MsgQs)

func InBailiwick

func InBailiwick(zone string, ns *dns.NS) bool

func IsIxfr

func IsIxfr(rrs []dns.RR) bool

func IsPEMFormat

func IsPEMFormat(keyData string) bool

IsPEMFormat detects if a stored private key is in PKCS#8 PEM format (new format). It returns true if the data decodes to a PEM block of type "PRIVATE KEY"; otherwise false.

func JsonToBase32Domains

func JsonToBase32Domains(jsonData []byte, domainSuffix string, cookie string) ([]string, error)

JsonToBase32Domains converts JSON data to a set of domain names Each domain name contains chunks of base32-encoded data with maximized label sizes

func KeyStateWorker

func KeyStateWorker(ctx context.Context, conf *Config) error

KeyStateWorker runs periodic checks on DNSSEC key states and performs automatic transitions and standby key maintenance.

func Logger

func Logger(subsystem string) *slog.Logger

Logger returns a *slog.Logger for the given subsystem. The subsystem gets its own level (defaulting to the global level). Call it freely — if the subsystem doesn't exist yet, it's created on first use.

func LookupChildKeyAtApex

func LookupChildKeyAtApex(ctx context.Context, childZone string, imr *Imr) ([]dns.RR, bool, error)

LookupChildKeyAtApex queries the child zone apex for KEY records via the IMR engine. Returns the KEY RRs found, whether the response was DNSSEC- validated, and any error.

func LookupChildKeyAtSignal

func LookupChildKeyAtSignal(ctx context.Context, childZone string, imr *Imr) ([]dns.RR, bool, error)

LookupChildKeyAtSignal queries _sig0key.<childzone>._signal.<ns>. for KEY records for each NS serving the child zone. Returns the union of KEY RRs found, whether all responses were DNSSEC-validated, and any error.

func LookupSVCB

func LookupSVCB(name string) (*core.RRset, error)

func LookupTlsaRR

func LookupTlsaRR(name string) (*core.RRset, error)

func MarshalTLSAToString

func MarshalTLSAToString(tlsa *dns.TLSA) (string, error)

MarshalTLSAToString renders a TLSA record to the "usage selector matching data" representation.

func MarshalTransport

func MarshalTransport(transports map[string]uint8) string

MarshalTransport converts a transport map back to a canonical string (sorted by key).

func MsgAcceptFunc

func MsgAcceptFunc(dh dns.Header) dns.MsgAcceptAction

func MsgPrint

func MsgPrint(m *dns.Msg, server string, elapsed time.Duration, short bool, options map[string]string)

func NSInBailiwick

func NSInBailiwick(zone string, ns *dns.NS) bool

func NeedsResigning

func NeedsResigning(rrsig *dns.RRSIG) bool

XXX: Perhaps a working algorithm woul be to test for the remaining signature lifetime to be something like

less than 3 x resigning interval?

func NewCombinerSyncHandler

func NewCombinerSyncHandler() transport.MessageHandlerFunc

NewCombinerSyncHandler creates a transport.MessageHandlerFunc for combiner UPDATE processing. The handler returns an immediate "pending" ACK in the DNS response and routes the update to MsgQs for async processing by CombinerMsgHandler. The actual CombinerProcessUpdate() runs asynchronously, and the detailed confirmation is sent back as a separate CONFIRM NOTIFY.

func NormalizeAddress

func NormalizeAddress(addr string) string

NormalizeAddress ensures an address has a port number. If the address doesn't have a port, ":53" is appended. This allows users to specify addresses as either "IP" or "IP:port" in config. Returns empty string if input is empty.

func NormalizeAddresses

func NormalizeAddresses(addresses []string) []string

NormalizeAddresses ensures all addresses have a port number. If an address doesn't have a port, ":53" is appended. This allows users to specify addresses as either "IP" or "IP:port" in config.

func Notifier

func Notifier(ctx context.Context, notifyreqQ chan NotifyRequest) error

XXX: The whole point with the NotifierEngine is to be able to control the max rate of send notifications per zone. This is not yet implemented, but this is where to do it.

func NotifyCatalogZoneUpdate

func NotifyCatalogZoneUpdate(update *CatalogZoneUpdate) error

NotifyCatalogZoneUpdate invokes all registered callbacks

func NotifyHandler

func NotifyHandler(ctx context.Context, conf *Config) error

func NotifyHandlerWithCallback

func NotifyHandlerWithCallback(ctx context.Context, conf *Config, handlerFunc func(context.Context, *DnsNotifyRequest) error) error

NotifyHandlerWithCallback consumes DnsNotifyRequest messages from the DnsNotifyQ channel and calls the provided handler function. This allows custom NOTIFY handlers (like KDC for confirmation NOTIFYs) to process NOTIFYs via channels. handlerFunc: Function that processes a DnsNotifyRequest

func NotifyReporter

func NotifyReporter(conf *Config, tsigSecrets map[string]string, addr string) (stop func(context.Context) error, err error)

func NotifyResponder

func NotifyResponder(ctx context.Context, dnr *DnsNotifyRequest, zonech chan ZoneRefresher, scannerq chan ScanRequest) error

TODO: Add per-source rate limiting for NOTIFY messages. An attacker could flood the server with NOTIFY messages to trigger excessive zone refreshes and scanner scans. Consider a token bucket or sliding window rate limiter keyed by source IP.

func PEMToPrivateKey

func PEMToPrivateKey(pemData string) (crypto.PrivateKey, error)

PEMToPrivateKey parses pemData as a PKCS#8 PEM-encoded private key and returns it as a crypto.PrivateKey.

The input must contain a PEM block of type "PRIVATE KEY" encoded in PKCS#8. Returns an error if the input PEMToPrivateKey parses a PKCS#8 PEM-encoded private key and returns the corresponding crypto.PrivateKey. It returns an error if pemData is empty, if no PEM block can be decoded, if the PEM block type is not "PRIVATE KEY", or if PKCS#8 parsing fails.

func ParseLogLevel

func ParseLogLevel(s string) slog.Level

ParseLogLevel converts a string like "debug", "info", "warn", "error" to an slog.Level. Defaults to Info for unrecognized strings.

func ParsePrivateKeyFromDB

func ParsePrivateKeyFromDB(privatekey, algorithm, keyrrstr string) (crypto.PrivateKey, uint8, string, error)

ParsePrivateKeyFromDB parses a private key from the database, detecting whether it's in old BIND format or new PEM format, and returns a crypto.PrivateKey. ParsePrivateKeyFromDB parses a private key stored in the database, accepting either PKCS#8 PEM (new) or legacy BIND private-key formats.

It returns the parsed crypto.PrivateKey, the DNSSEC algorithm numeric code, and a BIND-format private-key string suitable for backward compatibility. An error is ParsePrivateKeyFromDB parses a stored private key (either PKCS#8 PEM or legacy BIND format) and returns the crypto.PrivateKey, the DNSSEC algorithm numeric code, and a BIND-format private-key string suitable for use with PrepareKeyCache.

If the input `privatekey` is detected as a PKCS#8 PEM, the function parses it and derives a BIND-format private-key string from `keyrrstr` (a DNSKEY/KEY RR string) for compatibility. If `privatekey` is not PEM, it is treated as the legacy BIND private-key material and is wrapped into a full BIND-format string. The `algorithm` parameter is validated and converted to the corresponding DNSSEC algorithm code.

Returns an error if the algorithm is unknown, PEM decoding/parsing fails, the public key RR cannot be parsed, conversion to BIND format fails, or the legacy BIND parsing logic fails.

func ParseTLSAFromSvcbLocal

func ParseTLSAFromSvcbLocal(local *dns.SVCBLocal) (*dns.TLSA, error)

ParseTLSAFromSvcbLocal parses a private SVCB TLSA key into a dns.TLSA.

func ParseTLSAString

func ParseTLSAString(s string) (*dns.TLSA, error)

ParseTLSAString parses a TLSA RDATA string of the form "usage selector matching data".

func ParseTsigKeys

func ParseTsigKeys(keyconf *KeyConf) (int, map[string]string)

ParseTsigKeys parses the TSIG keys from the configuration and returns the number of keys and the secrets in the format expected by the dns.Server and the dns.Client. It also stores the keys with more details in the tdns.Globals struct.

func PrintDsRR

func PrintDsRR(rr dns.RR, leftpad, rightmargin int)

func PrintGenericRR

func PrintGenericRR(rr dns.RR, leftpad, rightmargin int)

func PrintJwkRR

func PrintJwkRR(rr dns.RR, leftpad, rightmargin int)

func PrintKeyRR

func PrintKeyRR(rr dns.RR, rrtype, ktype string, keyid uint16, leftpad, rightmargin int)

leftpad = amount of white space instead of the domain name on continuation lines during multiline output

func PrintMsgFull

func PrintMsgFull(m *dns.Msg, width int) string

func PrintMsgSection

func PrintMsgSection(header string, section []dns.RR, width int) string

func PrintRR

func PrintRR(rr dns.RR, leftpad int, options map[string]string) error

func PrintRrsigRR

func PrintRrsigRR(rr dns.RR, leftpad, rightmargin int)

func PrintSoaRR

func PrintSoaRR(rr dns.RR, leftpad, rightmargin int)

func PrintSvcbRR

func PrintSvcbRR(rr dns.RR, leftpad, rightmargin int)

func PrivKeyToBindFormat

func PrivKeyToBindFormat(privkey, algorithm string) (string, error)

func PrivateKeyToPEM

func PrivateKeyToPEM(privkey crypto.PrivateKey) (string, error)

PrivateKeyToPEM converts a crypto.PrivateKey to PKCS#8 PEM format. PrivateKeyToPEM converts a crypto.PrivateKey to a PKCS#8 PEM-encoded string. PrivateKeyToPEM converts a crypto.PrivateKey into a PKCS#8 PEM-encoded string.

It returns the PEM-formatted private key. An error is returned if the provided private key is nil or if marshaling the key to PKCS#8 DER fails.

func PublishKeyToCombiner

func PublishKeyToCombiner(zone ZoneName, keyRR dns.RR, tm *MPTransportBridge) (string, error)

PublishKeyToCombiner sends a KEY RR to the combiner as a REPLACE operation. Used by MP zones where the combiner manages the zone apex.

func QueryHandler

func QueryHandler(ctx context.Context, conf *Config, handlerFunc func(context.Context, *DnsQueryRequest) error) error

QueryHandler consumes DnsQueryRequest messages from the DnsQueryQ channel and calls the provided handler function. This allows custom query handlers (like KDC for KMREQ queries) to process queries via channels. handlerFunc: Function that processes a DnsQueryRequest

func RRsetToString

func RRsetToString(rrset *core.RRset) string

func ReadPubKey

func ReadPubKey(filename string) (dns.RR, uint16, uint8, error)

func ReadPubKeys

func ReadPubKeys(keydir string) (map[string]dns.KEY, error)

ReadPubKeys reads all ".key" public key files in the given directory and returns a mapping from each key's owner name to its dns.KEY record.

If keydir is empty, the function returns an error. Only files with the ".key" suffix are processed; other files are ignored. Each processed file is parsed as a DNS RR and must be of type KEY; parse failures, unexpected RR ReadPubKeys reads all files with the ".key" suffix in the provided directory, parses each as a DNS KEY RR, and returns a map from the RR owner name to the dns.KEY value.

The keydir parameter is the path to the directory containing public key files. Non-".key" files are ignored. If any filesystem operation fails or a file cannot be parsed as a DNS KEY RR, an error is returned.

func RecursiveDNSQuery

func RecursiveDNSQuery(server, qname string, qtype uint16, timeout time.Duration, retries int) (*core.RRset, error)

func RecursiveDNSQueryWithConfig

func RecursiveDNSQueryWithConfig(qname string, qtype uint16, timeout time.Duration, retries int) (*core.RRset, error)

func RecursiveDNSQueryWithResolvConf

func RecursiveDNSQueryWithResolvConf(qname string, qtype uint16, timeout time.Duration, retries int) (*core.RRset, error)

func RecursiveDNSQueryWithServers

func RecursiveDNSQueryWithServers(qname string, qtype uint16, timeout time.Duration,
	retries int, resolvers []string) (*core.RRset, error)

func RefreshEngine

func RefreshEngine(ctx context.Context, conf *Config)

func RegisterAPIRoute

func RegisterAPIRoute(routeFunc APIRouteFunc) error

RegisterAPIRoute registers a function that will add API routes to the router. IMPORTANT: The route registration function is called DURING SetupAPIRouter(), so routes must be registered BEFORE calling SetupAPIRouter(). For apps that call SetupAPIRouter() before initializing their subsystems, routes should be registered directly on the router after SetupAPIRouter() returns, rather than using RegisterAPIRoute().

Example usage:

tdns.RegisterAPIRoute(func(router *mux.Router) error {
    router.PathPrefix("/api/v1/kdc").HandlerFunc(kdc.APIKdcZone)
    return nil
})

func RegisterCatalogZoneCallback

func RegisterCatalogZoneCallback(callback CatalogZoneCallback)

RegisterCatalogZoneCallback registers a callback for catalog zone updates

func RegisterChunkQueryHandler

func RegisterChunkQueryHandler(store ChunkPayloadStore) error

RegisterChunkQueryHandler registers a query handler for CHUNK that serves payloads from the store. Should be called when running as agent with chunk_mode "query". store must not be nil.

func RegisterDebugNotifyHandler

func RegisterDebugNotifyHandler() error

RegisterDebugNotifyHandler registers a debug handler that logs all DNS NOTIFY messages. The handler logs NOTIFY details and always returns ErrNotHandled to pass through to the next handler. This is useful for debugging and monitoring.

The handler is registered with qtype=0, meaning it will be called for ALL NOTIFYs before any specific qtype handlers. It should be registered first (before other handlers).

Example usage:

tdns.RegisterDebugNotifyHandler()
tdns.RegisterNotifyHandler(core.TypeCHUNK, myHandler)

func RegisterDebugQueryHandler

func RegisterDebugQueryHandler() error

RegisterDebugQueryHandler registers a debug handler that logs all DNS queries. The handler logs query details and always returns ErrNotHandled to pass through to the next handler. This is useful for debugging and monitoring.

The handler is registered with qtype=0, meaning it will be called for ALL queries before any specific qtype handlers. It should be registered first (before other handlers).

Example usage:

tdns.RegisterDebugQueryHandler()
tdns.RegisterQueryHandler(hpke.TypeKMREQ, myHandler)

func RegisterDefaultQueryHandlers

func RegisterDefaultQueryHandlers(conf *Config) error

RegisterDefaultQueryHandlers registers the default zone-based query handler. This is called automatically during TDNS initialization. The default handler is registered if (a) zones are configured in the config, or (b) app type is Agent (agent gets an autozone from SetupAgent, needed for SOA/AXFR). Apps that need .server. query support should register ServerQueryHandler themselves.

func RegisterEngine

func RegisterEngine(name string, engine EngineFunc) error

RegisterEngine registers a long-running engine that will be started by TDNS. Engines are started as goroutines and run until the context is cancelled. They are started after TDNS initialization is complete.

Example usage:

tdns.RegisterEngine("KeyStateWorker", func(ctx context.Context) error {
    return kdc.KeyStateWorker(ctx, kdcDB, &kdcConf)
})

func RegisterImrClientQueryHook

func RegisterImrClientQueryHook(hook ImrClientQueryHookFunc) error

RegisterImrClientQueryHook registers a hook that is called when an external client query arrives at the IMR listener. Multiple hooks can be registered and are called in registration order.

func RegisterImrOutboundQueryHook

func RegisterImrOutboundQueryHook(hook ImrOutboundQueryHookFunc) error

RegisterImrOutboundQueryHook registers a hook that is called before the IMR sends an iterative query to an authoritative server. Multiple hooks can be registered and are called in registration order.

func RegisterImrResponseHook

func RegisterImrResponseHook(hook ImrResponseHookFunc) error

RegisterImrResponseHook registers a hook that is called after the IMR receives a response from an authoritative server. Multiple hooks can be registered and are called in registration order.

func RegisterNotifyHandler

func RegisterNotifyHandler(qtype uint16, handler NotifyHandlerFunc) error

RegisterNotifyHandler registers a handler for DNS NOTIFY messages. Multiple handlers can be registered for the same qtype - they will be called in registration order. If a handler returns ErrNotHandled, TDNS will try the next handler or fall back to default. If qtype is 0, handler is called for ALL NOTIFYs (use with caution, e.g., for debug handlers). Handlers registered with qtype=0 are called before handlers registered for specific qtypes.

This function can be called before TDNS is initialized (uses global storage), or after initialization (uses conf.Internal.NotifyHandlers). During NOTIFY processing, TDNS checks both locations.

func RegisterProviderZoneRRtypes

func RegisterProviderZoneRRtypes(pz ProviderZoneConf)

RegisterProviderZoneRRtypes parses a ProviderZoneConf and registers its allowed RRtype map for use by the combiner policy engine.

func RegisterQueryHandler

func RegisterQueryHandler(qtype uint16, handler QueryHandlerFunc) error

RegisterQueryHandler registers a handler for a specific query type. Multiple handlers can be registered for the same qtype - they will be called in registration order. If a handler returns ErrNotHandled, TDNS will try the next handler or fall back to default. If qtype is 0, handler is called for ALL query types (use with caution, e.g., for debug handlers). Handlers registered with qtype=0 are called before handlers registered for specific qtypes.

This function can be called before TDNS is initialized (uses global storage), or after initialization (uses conf.Internal.QueryHandlers). During query processing, TDNS checks both locations.

func RegisterUpdateHandler

func RegisterUpdateHandler(matcher UpdateMatcherFunc, handler UpdateHandlerFunc) error

RegisterUpdateHandler registers a handler for DNS UPDATE messages. The matcher function determines which UPDATEs should be handled by this handler. Multiple handlers can be registered - they will be called in registration order. If a handler returns ErrNotHandled, TDNS will try the next handler or fall back to default.

This function can be called before TDNS is initialized (uses global storage), or after initialization (uses conf.Internal.UpdateHandlers). During UPDATE processing, TDNS checks both locations.

Example usage:

// Match bootstrap UPDATEs (name pattern _bootstrap.*)
tdns.RegisterUpdateHandler(
	func(req *tdns.DnsUpdateRequest) bool {
		for _, rr := range req.Msg.Ns {
			if strings.HasPrefix(rr.Header().Name, "_bootstrap.") {
				return true
			}
		}
		return false
	},
	func(ctx context.Context, req *tdns.DnsUpdateRequest) error {
		return kdc.HandleBootstrapUpdate(ctx, req, kdcDB, &kdcConf)
	},
)

func RegisterZoneOptionHandler

func RegisterZoneOptionHandler(opt ZoneOption, handler ZoneOptionHandler)

RegisterZoneOptionHandler registers a callback for a zone option. Multiple handlers can be registered for the same option. Handlers fire synchronously during ParseZones, in registration order.

func ResignerEngine

func ResignerEngine(ctx context.Context, zoneresignch chan *ZoneData)

func ResignerEngine(zoneresignch chan ZoneRefresher, stopch chan struct{}) {

func RunKeysCmd

func RunKeysCmd(conf *Config, appType AppType, args []string) error

RunKeysCmd runs the "keys" subcommand (generate | show). Used by tdns-cli agent keys / tdns-cli combiner keys. conf must be loaded from the server's config file.

func SanitizeForJSON

func SanitizeForJSON(v interface{}) interface{}

SanitizeForJSON is a wrapper function that sanitizes a struct for JSON serialization

func ScannerEngine

func ScannerEngine(ctx context.Context, conf *Config) error

func SendUnixPing

func SendUnixPing(target string, dieOnError bool) (bool, error)

func ServerQueryHandler

func ServerQueryHandler(ctx context.Context, req *DnsQueryRequest) error

ServerQueryHandler handles queries for qnames ending in ".server." with ClassCHAOS. NOTE: This function is now optional. .server. queries are automatically handled by createAuthDnsHandler() in do53.go as a fallback before returning REFUSED. This exported function is kept for backward compatibility or for apps that want to handle .server. queries earlier in the handler chain.

func SetSubsystemLevel

func SetSubsystemLevel(name string, level slog.Level)

SetSubsystemLevel sets or updates the log level for a subsystem. Goroutine-safe (uses atomic LevelVar).

func SetupCliLogging

func SetupCliLogging()

SetupCliLogging sets up logging for CLI commands. Default: no source info, no timestamps. Verbose/Debug: adds source info.

func SetupLogging

func SetupLogging(logfile string, logConf LogConf) error

SetupLogging configures the slog-based logging system with lumberjack rotation. It also bridges the old log package so that existing log.Printf calls flow through slog to the same output.

func ShowAPI

func ShowAPI(rtr *mux.Router) ([]string, error)

Stolen from labstuff:apihandler_funcs.go

func Shutdowner

func Shutdowner(conf *Config, msg string)

func Sig0KeyOwnerName

func Sig0KeyOwnerName(zone, nameserver string) string

Sig0KeyOwnerName computes the RFC 9615-style owner name for a child SIG(0) KEY RR. Format: _sig0key.<zone>._signal.<nameserver>

func SignMsg

func SignMsg(m dns.Msg, signer string, sak *Sig0ActiveKeys) (*dns.Msg, error)

func SignerMsgHandler

func SignerMsgHandler(ctx context.Context, conf *Config, msgQs *MsgQs)

SignerMsgHandler consumes beat, hello, ping, and RFI messages from MsgQs. Updates PeerRegistry liveness on beats and logs hello/ping messages. Processes RFI KEYSTATE requests by querying KeyDB and pushing inventory.

func SprintUpdates

func SprintUpdates(actions []dns.RR) string

func StartDistributionGC

func StartDistributionGC(cache *DistributionCache, interval time.Duration, stopCh chan struct{})

StartDistributionGC starts a background goroutine that periodically purges: 1. Completed distributions older than 5 minutes 2. Expired distributions past their ExpiresAt time (based on message type retention) Incomplete distributions are never purged by GC (only "purge --force" removes them).

func StartEngine

func StartEngine(app *AppDetails, name string, engineFunc func() error)

startEngine wraps engine functions in a goroutine with error handling. It logs errors if the engine function returns an error, preventing silent failures.

func StartEngineNoError

func StartEngineNoError(app *AppDetails, name string, engineFunc func())

startEngineNoError wraps engine functions that don't return errors. This is for engines that handle errors internally or never fail during startup.

func StartRegisteredEngines

func StartRegisteredEngines(ctx context.Context)

StartRegisteredEngines starts all registered engines as goroutines. This should be called after TDNS initialization is complete. Engines run until the context is cancelled.

func StripKeyFileComments

func StripKeyFileComments(data []byte) []byte

StripKeyFileComments removes lines that are empty or start with '#' (after trim), so JWK/key files with comment headers (e.g. KDC/KRS-style) parse as valid JSON. Preserves line breaks and indentation of kept lines.

func StructToBase32Domains

func StructToBase32Domains(data interface{}, domainSuffix string, cookie string) ([]string, error)

StructToBase32Domains converts a Go struct to a set of domain names Each domain name contains chunks of base32-encoded data with maximized label sizes

func TLSAToSvcbLocal

func TLSAToSvcbLocal(tlsa *dns.TLSA) (*dns.SVCBLocal, error)

TLSAToSvcbLocal marshals a TLSA record into a private SVCB key/value pair.

func TtlPrint

func TtlPrint(expiration time.Time) string

TtlPrint returns a human-friendly TTL remaining until expiration. If the expiration time has passed, it returns "expired".

func TtyIntQuestion

func TtyIntQuestion(query string, oldval int, force bool) int

func TtyQuestion

func TtyQuestion(query, oldval string, force bool) string

func TtyRadioButtonQ

func TtyRadioButtonQ(query, defval string, choices []string) string

func TtyYesNo

func TtyYesNo(query, defval string) string

func TypeBitMapToString

func TypeBitMapToString(tbm []uint16) string

func UpdateHandler

func UpdateHandler(ctx context.Context, conf *Config) error

func UpdateResponder

func UpdateResponder(dur *DnsUpdateRequest, updateq chan UpdateRequest) error

func ValidateAgentNameservers

func ValidateAgentNameservers(config *Config) error

ValidateAgentNameservers ensures agent.local.nameservers are non-empty and outside the agent autozone (no glue). Each entry is normalized to FQDN (dns.Fqdn) in place so the config never carries non-FQDN names.

func ValidateAgentSupportedMechanisms

func ValidateAgentSupportedMechanisms(config *Config) error

ValidateAgentSupportedMechanisms validates agent.supported_mechanisms configuration. Requirements: - Must be non-empty (agent needs at least one communication mechanism) - Can only contain "api" and/or "dns" (case-insensitive) - Default if omitted: ["api", "dns"]

func ValidateBySection

func ValidateBySection(config *Config, configsections map[string]interface{}, cfgfile string) (string, error)

func ValidateCertAndKeyFiles

func ValidateCertAndKeyFiles(fl validator.FieldLevel) bool

validateCertAndKeyFiles is the custom validation function

func ValidateConfig

func ValidateConfig(v *viper.Viper, cfgfile string) error

func ValidateConfigWithCustomValidator

func ValidateConfigWithCustomValidator(v *viper.Viper, cfgfile string) error

ValidateConfigWithCustomValidator validates the config using the custom validator XXX: Not used at the moment.

func ValidateCryptoFiles

func ValidateCryptoFiles(config *Config) error

ValidateCryptoFiles validates that configured crypto key files exist and are readable. This is called during config validation to provide early feedback about missing files.

func ValidateDatabaseFile

func ValidateDatabaseFile(config *Config) error

ValidateDatabaseFile validates that the database file path is set for apps that require it. If db.file is unset or empty, this function returns an error (hard fail).

func ValidateExplicitServerSVCB

func ValidateExplicitServerSVCB(svcb *dns.SVCB) error

ValidateExplicitServerSVCB validates an explicit SVCB RR for server use. It checks transport weights against the ALPN list, if present. If transport is absent, the record is accepted as-is. If transport is present but alpn is missing, it's invalid.

func ValidateZones

func ValidateZones(c *Config, cfgfile string) error

func ValidatorEngine

func ValidatorEngine(ctx context.Context, conf *Config)

func VerifyCertAgainstTlsaRR

func VerifyCertAgainstTlsaRR(tlsarr *dns.TLSA, rawcert []byte) error

func VerifyChildKey

func VerifyChildKey(ctx context.Context, childZone string, keyRR string, imr *Imr) (verified bool, dnssecValidated bool)

VerifyChildKey checks whether a child's KEY (identified by keyRR string) can be found via the configured verification mechanisms (at-apex, at-ns). Returns true if any mechanism succeeds (key found + optionally DNSSEC-validated).

func VerifyKey

func VerifyKey(KeyName string, key string, keyid uint16, zd *ZoneData, updatetrustq chan<- KeyBootstrapperRequest)

func WalkRoutes

func WalkRoutes(router *mux.Router, address string)

func WildcardReplace

func WildcardReplace(rrs []dns.RR, qname, origqname string) []dns.RR

func ZoneDataWeAreASigner

func ZoneDataWeAreASigner(zd *ZoneData) (bool, error)

ZoneDataWeAreASigner wraps the unexported weAreASigner method.

func ZoneIsReady

func ZoneIsReady(zonename string) func() bool

ZoneIsReady returns a function that can be used as a PreCondition for a DeferredUpdate. The returned function will return true if the zone exists and is ready, otherwise false.

func ZoneTransferPrint

func ZoneTransferPrint(zname, upstream string, serial uint32, ttype uint16, options map[string]string) error

Types

type APIRouteFunc

type APIRouteFunc func(router *mux.Router) error

APIRouteFunc is the function signature for API route registration. The function receives the API router and should register routes on it.

type APIRouteRegistration

type APIRouteRegistration struct {
	RouteFunc APIRouteFunc
}

APIRouteRegistration stores an API route registration

type Agent

type Agent struct {
	Identity AgentId

	InitialZone   ZoneName
	ApiDetails    *AgentDetails
	DnsDetails    *AgentDetails
	ApiMethod     bool
	DnsMethod     bool
	IsInfraPeer   bool // true for combiner/signer — handled by StartInfraBeatLoop, not SendHeartbeats
	Zones         map[ZoneName]bool
	Api           *AgentApi
	State         AgentState // Agent states: needed, known, hello-done, operational, error
	LastState     time.Time  // When state last changed
	ErrorMsg      string     // Error message if state is error
	DeferredTasks []DeferredAgentTask
	// contains filtered or unexported fields
}

func (*Agent) AddDeferredAgentTask

func (agent *Agent) AddDeferredAgentTask(task *DeferredAgentTask)

XXX: The DeferredAgentTask functions are not yet fully thought out (and not in use yet).

func (*Agent) CheckState

func (agent *Agent) CheckState(ourBeatInterval uint32)

func (*Agent) CreateAgentUpstreamRFI

func (agent *Agent) CreateAgentUpstreamRFI() *DeferredAgentTask

func (*Agent) CreateOperationalAgentTask

func (agent *Agent) CreateOperationalAgentTask(action func() (bool, error), desc string) *DeferredAgentTask

func (*Agent) EffectiveState

func (a *Agent) EffectiveState() AgentState

EffectiveState returns the most relevant transport-layer state. DNS is checked first as it is the primary transport. Falls back to the top-level aggregate state if no transport is operational.

func (*Agent) IsAnyTransportOperational

func (a *Agent) IsAnyTransportOperational() bool

IsAnyTransportOperational returns true if at least one transport layer (DNS or API) is in the OPERATIONAL state. DNS is checked first as it is the primary (and currently only fully implemented) transport.

func (*Agent) MarshalJSON

func (agent *Agent) MarshalJSON() ([]byte, error)

func (*Agent) NewAgentSyncApiClient

func (agent *Agent) NewAgentSyncApiClient(localagent *MultiProviderConf) error

func (*Agent) SendApiBeat

func (agent *Agent) SendApiBeat(msg *AgentBeatPost) (*AgentBeatResponse, error)

func (*Agent) SendApiHello

func (agent *Agent) SendApiHello(msg *AgentHelloPost) (*AgentHelloResponse, error)

func (*Agent) SendApiMsg

func (agent *Agent) SendApiMsg(msg *AgentMsgPost) (*AgentMsgResponse, error)

Helper methods for SendBeat

func (*Agent) SendDnsMsg

func (agent *Agent) SendDnsMsg(msg *AgentMsgPost) (int, []byte, error)

type AgentApi

type AgentApi struct {
	Name       string
	Client     *http.Client
	BaseUrl    string
	ApiKey     string // TODO: to remove, but we still need it for a while
	Authmethod string

	// normal TDNS API client, we're using most of the tdns API client,
	ApiClient *ApiClient
}

type AgentBeatPost

type AgentBeatPost struct {
	MessageType    AgentMsg
	MyIdentity     AgentId
	YourIdentity   AgentId
	MyBeatInterval uint32   // intended, in seconds
	Zones          []string // Zones that we share with the remote agent
	Time           time.Time
	Gossip         []GossipMessage `json:"Gossip,omitempty"`
}

AgentBeatPost is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId instead of string for backward compatibility.

type AgentBeatReport

type AgentBeatReport struct {
	Time time.Time
	Beat AgentBeatPost
}

type AgentBeatResponse

type AgentBeatResponse struct {
	Status       string // ok | error | ...
	MyIdentity   AgentId
	YourIdentity AgentId
	Time         time.Time
	Client       string
	Msg          string
	Error        bool
	ErrorMsg     string
}

AgentBeatResponse is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId instead of string for backward compatibility.

type AgentDebugPost

type AgentDebugPost struct {
	Command string   `json:"command"`
	Zone    ZoneName `json:"zone"`
	AgentId AgentId  `json:"agent_id"`
	RRType  uint16
	RR      string
	Data    ZoneUpdate
}

also mgmt API, same response struct

type AgentDetails

type AgentDetails struct {
	Addrs   []string
	Port    uint16
	BaseUri string
	UriRR   *dns.URI
	//	SvcbRR  *dns.SVCB
	Host         string    // the host part of the BaseUri
	KeyRR        *dns.KEY  // for DNS transport (legacy)
	JWKData      string    // JWK data (preferred for DNS transport)
	KeyAlgorithm string    // Key algorithm (e.g., "ES256")
	TlsaRR       *dns.TLSA // for HTTPS transport
	//	LastHB      time.Time
	Endpoint    string
	ContactInfo string // "none", "partial", "complete"
	//	Zones           map[ZoneName]bool // zones we share with this agent
	State             AgentState // "discovered", "contact_attempted", "connected", "failed"
	LatestError       string
	LatestErrorTime   time.Time
	DiscoveryFailures uint32 // consecutive discovery failures (for IMR cache flush)
	HelloTime         time.Time
	LastContactTime   time.Time // Last contact of any type (Hello, Beat, Ping, Sync, etc.)
	BeatInterval      uint32
	SentBeats         uint32
	ReceivedBeats     uint32
	LatestSBeat       time.Time
	LatestRBeat       time.Time
}

type AgentDiscoveryResult

type AgentDiscoveryResult struct {
	Identity     string
	APIUri       string           // Base URI from URI record (e.g., https://agent.example.com:8443/api)
	DNSUri       string           // DNS endpoint if discovered
	JWKData      string           // Base64url-encoded JWK (preferred)
	PublicKey    crypto.PublicKey // Decoded public key from JWK
	KeyAlgorithm string           // Algorithm from JWK (e.g., "ES256")
	LegacyKeyRR  *dns.KEY         // Legacy KEY record (fallback if no JWK)
	TLSA         *dns.TLSA        // TLSA record for TLS verification
	APIAddresses []string         // IP addresses for API service from SVCB
	DNSAddresses []string         // IP addresses for DNS service from SVCB
	Port         uint16           // Port from URI record
	Error        error            // Any error during discovery
	Partial      bool             // True if some records were found but discovery incomplete
}

AgentDiscoveryResult holds the result of discovering an agent.

func DiscoverAgent

func DiscoverAgent(ctx context.Context, imr *Imr, identity string) *AgentDiscoveryResult

DiscoverAgent performs full DNS-based discovery of an agent's contact information for both API and DNS transports. Convenience wrapper around DiscoverAgentAPI + DiscoverAgentDNS.

type AgentDistribPost

type AgentDistribPost struct {
	Command       string `json:"command"`                  // "list", "purge", "peer-list", "peer-zones", "zone-agents", "op", "discover"
	Force         bool   `json:"force,omitempty"`          // for purge
	Op            string `json:"op,omitempty"`             // for op: operation name (e.g. "ping")
	To            string `json:"to,omitempty"`             // for op: recipient identity (e.g. "combiner", "agent.delta.dnslab.")
	PingTransport string `json:"ping_transport,omitempty"` // for op ping: "dns" (default) or "api"
	AgentId       string `json:"agent_id,omitempty"`       // for discover: agent identity to discover
	Zone          string `json:"zone,omitempty"`           // for zone-agents: zone name to list agents for
}

AgentDistribPost represents a request to the agent distrib API

type AgentDistribResponse

type AgentDistribResponse struct {
	Time          time.Time              `json:"time"`
	Error         bool                   `json:"error,omitempty"`
	ErrorMsg      string                 `json:"error_msg,omitempty"`
	Msg           string                 `json:"msg,omitempty"`
	Summaries     []*DistributionSummary `json:"summaries,omitempty"`
	Distributions []string               `json:"distributions,omitempty"` // For backward compatibility
	Data          []interface{}          `json:"data,omitempty"`          // For peer-zones command
	Agents        []string               `json:"agents,omitempty"`        // For zone-agents command
}

AgentDistribResponse represents a response from the agent distrib API

type AgentHelloPost

type AgentHelloPost struct {
	MessageType  AgentMsg
	Name         string `json:"name,omitempty"` // DEPRECATED: Unused field
	MyIdentity   AgentId
	YourIdentity AgentId
	Addresses    []string  `json:"addresses,omitempty"` // DEPRECATED: Use DNS discovery (SVCB records) instead
	Port         uint16    `json:"port,omitempty"`      // DEPRECATED: Use DNS discovery (URI scheme) instead
	TLSA         dns.TLSA  `json:"tlsa,omitempty"`      // DEPRECATED: Use DNS discovery (TLSA query) instead
	Zone         ZoneName  // in the /hello we only send one zone, the one that triggered the /hello
	Time         time.Time // message timestamp
}

AgentHelloPost is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId/ZoneName instead of string for backward compatibility.

type AgentHelloResponse

type AgentHelloResponse struct {
	Status       string // ok | error | ...
	MyIdentity   AgentId
	YourIdentity AgentId
	Time         time.Time
	// Client       string
	Msg      string
	Error    bool
	ErrorMsg string
}

AgentHelloResponse is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId instead of string for backward compatibility.

type AgentId

type AgentId string

func (AgentId) String

func (id AgentId) String() string

type AgentMgmtPost

type AgentMgmtPost struct {
	Command     string `json:"command"`
	MessageType AgentMsg
	Zone        ZoneName `json:"zone"`
	AgentId     AgentId  `json:"agent_id"`
	RRType      uint16
	RR          string
	RRs         []string
	AddedRRs    []string // for update-local-zonedata
	RemovedRRs  []string // for update-local-zonedata
	Upstream    AgentId
	Downstream  AgentId
	RfiType     string
	RfiSubtype  string
	Data        map[string]interface{} `json:"data,omitempty"` // Generic data field for custom parameters

}

AgentMgmt{Post,Response} are used in the mgmt API

type AgentMgmtPostPlus

type AgentMgmtPostPlus struct {
	AgentMgmtPost
	Response chan *AgentMgmtResponse
}

The ...Plus structs are always the original struct + a response channel

type AgentMgmtResponse

type AgentMgmtResponse struct {
	Identity       AgentId
	Status         string
	Time           time.Time
	Agents         []*Agent // used for hsync-agentstatus
	ZoneAgentData  *ZoneAgentData
	HsyncRRs       []string
	AgentConfig    MultiProviderConf
	RfiType        string
	RfiResponse    map[AgentId]*RfiData
	AgentRegistry  *AgentRegistry
	ZoneDataRepo   map[ZoneName]map[AgentId]map[uint16][]TrackedRRInfo
	KeystateStatus map[ZoneName]KeystateInfo `json:"keystate_status,omitempty"`
	Msg            string
	Error          bool
	ErrorMsg       string
	Data           interface{} `json:"data,omitempty"` // Generic data field for custom responses

	// HSYNC debug data (Phase 5)
	HsyncPeers         []*HsyncPeerInfo         `json:"hsync_peers,omitempty"`
	HsyncSyncOps       []*HsyncSyncOpInfo       `json:"hsync_sync_ops,omitempty"`
	HsyncConfirmations []*HsyncConfirmationInfo `json:"hsync_confirmations,omitempty"`
	HsyncEvents        []*HsyncTransportEvent   `json:"hsync_events,omitempty"`
	HsyncMetrics       *HsyncMetricsInfo        `json:"hsync_metrics,omitempty"`
}

type AgentMsg

type AgentMsg = core.AgentMsg

AgentMsg and related constants are defined in core package to avoid circular dependencies

type AgentMsgPost

type AgentMsgPost struct {
	MessageType    AgentMsg // "sync", "update", "rfi", "status"
	OriginatorID   AgentId  // Original author of the update
	DeliveredBy    AgentId  // Transport-level sender (who delivered this message to us)
	YourIdentity   AgentId
	Addresses      []string            `json:"addresses,omitempty"` // DEPRECATED: Use DNS discovery (SVCB records) instead
	Port           uint16              `json:"port,omitempty"`      // DEPRECATED: Use DNS discovery (URI scheme) instead
	TLSA           dns.TLSA            `json:"tlsa,omitempty"`      // DEPRECATED: Use DNS discovery (TLSA query) instead
	Zone           ZoneName            // An AgentMsgPost should always only refer to one zone.
	Records        map[string][]string // Resource records grouped by owner name (legacy: Class-overloaded)
	Operations     []core.RROperation  // Explicit operations (takes precedence over Records)
	Time           time.Time
	RfiType        string
	RfiSubtype     string
	DistributionID string                   // Originating distribution ID from the sending agent
	Nonce          string                   // Nonce from the incoming sync/update message (for confirmation echo)
	ZoneClass      string                   // "mp" (default) or "provider"
	Publish        *core.PublishInstruction // KEY/CDS publication instruction for combiner
}

AgentMsgPost is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId/ZoneName instead of string for backward compatibility. AgentMsg{Post,Response} are intended for agent-to-agent messaging

type AgentMsgPostPlus

type AgentMsgPostPlus struct {
	AgentMsgPost
	Response chan *AgentMsgResponse
}

type AgentMsgReport

type AgentMsgReport struct {
	Transport      string
	MessageType    AgentMsg
	Zone           ZoneName
	Identity       AgentId
	BeatInterval   uint32
	Msg            interface{}
	RfiType        string
	DistributionID string
	Response       chan *SynchedDataResponse
}

type AgentMsgResponse

type AgentMsgResponse struct {
	Status string // ok | error | ...
	Time   time.Time
	// Client      string
	AgentId     AgentId
	Msg         string
	Zone        ZoneName
	RfiResponse map[AgentId]*RfiData
	Error       bool
	ErrorMsg    string
}

AgentMsgResponse is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId/ZoneName instead of string for backward compatibility.

type AgentOption

type AgentOption uint8

type AgentPingPost

type AgentPingPost struct {
	MessageType  AgentMsg  // AgentMsgPing
	MyIdentity   AgentId   // sender's identity
	YourIdentity AgentId   // recipient's identity
	Nonce        string    // for round-trip verification
	Time         time.Time // message timestamp
}

AgentPingPost is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId instead of string for backward compatibility. AgentPingPost is used for ping operations (connectivity testing)

type AgentPingResponse

type AgentPingResponse struct {
	Status       string  // "ok" | "error"
	MyIdentity   AgentId // responder's identity
	YourIdentity AgentId // original sender
	Nonce        string  // echo from request
	Time         time.Time
	Msg          string
	Error        bool
	ErrorMsg     string
}

AgentPingResponse is defined in core package to avoid circular dependencies. We keep a wrapper type here that uses AgentId instead of string for backward compatibility. AgentPingResponse is the response to a ping operation

type AgentRegistry

type AgentRegistry struct {
	S            core.ConcurrentMap[AgentId, *Agent]
	RegularS     map[AgentId]*Agent
	RemoteAgents map[ZoneName][]AgentId

	LocalAgent     *MultiProviderConf // our own identity
	LocateInterval int                // seconds to wait between locating agents (until success)

	TransportManager      *transport.TransportManager // Generic transport (Router, PeerRegistry, etc.)
	MPTransport           *MPTransportBridge          // MP transport bridge (authorization, discovery, enqueue, beats, hellos)
	LeaderElectionManager *LeaderElectionManager      // optional; when set, election messages are processed
	ProviderGroupManager  *ProviderGroupManager       // optional; manages provider group computation
	GossipStateTable      *GossipStateTable           // optional; gossip protocol state
	// contains filtered or unexported fields
}

func (*AgentRegistry) AddRemoteAgent

func (ar *AgentRegistry) AddRemoteAgent(zonename ZoneName, agent *Agent)

AddRemoteAgent adds an agent to the list of remote agents for a zone

func (*AgentRegistry) AddZoneToAgent

func (ar *AgentRegistry) AddZoneToAgent(identity AgentId, zone ZoneName)

func (*AgentRegistry) CleanupZoneRelationships

func (ar *AgentRegistry) CleanupZoneRelationships(zonename ZoneName)

CleanupZoneRelationships handles the complex cleanup when we're no longer involved in a zone's management

func (*AgentRegistry) CommandHandler

func (ar *AgentRegistry) CommandHandler(msg *AgentMgmtPostPlus, synchedDataUpdateQ chan *SynchedDataUpdate)

Handler for local commands from CLI or other components in the same organization

func (*AgentRegistry) DiscoverAgentAsync

func (ar *AgentRegistry) DiscoverAgentAsync(remoteid AgentId, zonename ZoneName, deferredTask *DeferredAgentTask)

DiscoverAgentAsync marks an agent as NEEDED for discovery by DiscoveryRetrierNG.

DEPRECATED: This function is now a thin wrapper around MarkAgentAsNeeded() for backward compatibility. New code should call MarkAgentAsNeeded() directly instead.

The old immediate discovery behavior has been replaced with a retry-based approach: - Agents are marked as NEEDED - DiscoveryRetrierNG continuously retries discovery until success - Eliminates IMR race conditions and handles transient failures - Provides infinite retry with backoff (consistent with Hello/Beat mechanisms)

Parameters:

  • remoteid: The agent identity to discover
  • zonename: Optional zone name to associate with the agent
  • deferredTask: Optional task to execute when agent becomes operational

func (*AgentRegistry) DiscoveryRetrierNG

func (ar *AgentRegistry) DiscoveryRetrierNG(ctx context.Context)

DiscoveryRetrierNG continuously attempts to discover agents in NEEDED state. It runs in its own goroutine with a configurable retry interval and only attempts discovery when the IMR engine is available.

func (*AgentRegistry) EvaluateHello

func (ar *AgentRegistry) EvaluateHello(ahp *AgentHelloPost) (bool, string, error)

func (*AgentRegistry) FastBeatAttempts

func (ar *AgentRegistry) FastBeatAttempts(ctx context.Context, agent *Agent)

FastBeatAttempts sends up to 3 beats with 5s spacing after HELLO succeeds. If any beat succeeds (agent becomes OPERATIONAL), returns immediately. If all 3 fail, returns — the normal heartbeat ticker will take over.

func (*AgentRegistry) GetAgentInfo

func (ar *AgentRegistry) GetAgentInfo(identity AgentId) (*Agent, error)

Create a new synchronous function for code that needs immediate results

func (*AgentRegistry) GetAgentsForZone

func (ar *AgentRegistry) GetAgentsForZone(zone ZoneName) []*Agent

func (*AgentRegistry) GetZoneAgentData

func (ar *AgentRegistry) GetZoneAgentData(zonename ZoneName) (*ZoneAgentData, error)

GetRemoteAgents returns a list of remote agents for a zone. It does not check if the agents are operational, or try to get missing information. func (ar *AgentRegistry) GetRemoteAgents(zonename ZoneName) ([]*Agent, error) {

func (*AgentRegistry) HandleStatusRequest

func (ar *AgentRegistry) HandleStatusRequest(req SyncStatus)

XXX: Not used at the moment.

func (*AgentRegistry) HeartbeatHandler

func (ar *AgentRegistry) HeartbeatHandler(report *AgentMsgReport)

func (*AgentRegistry) HelloHandler

func (ar *AgentRegistry) HelloHandler(report *AgentMsgReport)

func (*AgentRegistry) HelloRetrier

func (ar *AgentRegistry) HelloRetrier()

func (*AgentRegistry) HelloRetrierNG

func (ar *AgentRegistry) HelloRetrierNG(ctx context.Context, agent *Agent)

HelloRetrierNG manages Hello retries for an agent. Handles both API and DNS transports independently. Continues retrying while EITHER transport is in KNOWN state.

Fast-start: configurable number of immediate attempts with configurable spacing. If all fail, falls back to the normal helloretry ticker. On HELLO success (INTRODUCED), triggers fast beat attempts.

func (*AgentRegistry) InitializeCombinerAsPeer

func (ar *AgentRegistry) InitializeCombinerAsPeer(conf *Config) error

InitializeCombinerAsPeer registers the combiner as a virtual peer in the AgentRegistry so that the HsyncEngine's Beat mechanism will automatically send heartbeats to it. This ensures we verify combiner connectivity and get early warning of communication issues.

func (*AgentRegistry) InitializeSignerAsPeer

func (ar *AgentRegistry) InitializeSignerAsPeer(conf *Config) error

InitializeSignerAsPeer registers the signer as a virtual peer in the AgentRegistry so that it shows up in "agent peer list" and can be pinged via "agent peer ping". Mirrors InitializeCombinerAsPeer for the signer role.

func (*AgentRegistry) LocateAgent

func (ar *AgentRegistry) LocateAgent(remoteid AgentId, zonename ZoneName, deferredTask *DeferredAgentTask)

LocateAgent is completely asynchronous with no return values

DEPRECATED: This function has critical concurrency issues (see docs/locateagent-review-findings.md). It will be replaced by the refactored discovery mechanism using common helpers from agent_discovery_common.go. Keep this implementation for backward compatibility until the migration is complete.

func (*AgentRegistry) MarkAgentAsNeeded

func (ar *AgentRegistry) MarkAgentAsNeeded(remoteid AgentId, zonename ZoneName, deferredTask *DeferredAgentTask)

MarkAgentAsNeeded creates a placeholder agent in NEEDED state. The agent will be discovered asynchronously by DiscoveryRetrierNG in HsyncEngine. This is the new recommended pattern for agent discovery triggered by HSYNC updates.

Parameters:

  • remoteid: The agent identity to mark as needed
  • zonename: Optional zone name to associate with the agent
  • deferredTask: Optional task to execute when agent becomes OPERATIONAL

func (*AgentRegistry) MsgHandler

func (ar *AgentRegistry) MsgHandler(ampp *AgentMsgPostPlus, synchedDataUpdateQ chan *SynchedDataUpdate, synchedDataCmdQ chan *SynchedDataCmd)

Handler for messages received from other agents

func (*AgentRegistry) RecomputeSharedZonesAndSyncState

func (ar *AgentRegistry) RecomputeSharedZonesAndSyncState(agent *Agent)

RecomputeSharedZonesAndSyncState updates an agent's shared zones and transitions between OPERATIONAL and LEGACY states based on zone count. This should be called after HSYNC changes to keep agent state synchronized with zone membership.

func (*AgentRegistry) RemoveRemoteAgent

func (ar *AgentRegistry) RemoveRemoteAgent(zonename ZoneName, identity AgentId)

RemoveRemoteAgent removes an agent from the list of remote agents for a zone

func (*AgentRegistry) SendHeartbeats

func (ar *AgentRegistry) SendHeartbeats()

func (*AgentRegistry) SingleHello

func (ar *AgentRegistry) SingleHello(agent *Agent, zone ZoneName)

func (*AgentRegistry) StartInfraBeatLoop

func (ar *AgentRegistry) StartInfraBeatLoop(ctx context.Context)

StartInfraBeatLoop sends periodic beats from the agent to infrastructure peers (combiner and signer). Runs at a lower frequency than the agent↔agent beat loop (default 10 minutes, vs 15–1800s for agent peers).

Combiner and signer are excluded from SendHeartbeats() via IsInfraPeer=true. This loop handles them exclusively.

func (*AgentRegistry) SyncRequestHandler

func (ar *AgentRegistry) SyncRequestHandler(ourId AgentId, req SyncRequest, synchedDataUpdateQ chan *SynchedDataUpdate)

func (*AgentRegistry) UpdateAgents

func (ar *AgentRegistry) UpdateAgents(ourId AgentId, req SyncRequest, zonename ZoneName, synchedDataUpdateQ chan *SynchedDataUpdate) error

XXX: This is likely not sufficient, we must also be able to deal with HSYNC3 RRs that simply "change" (i.e. the same identity, but now roles). ADD+REMOVE doesn't deal with that.

type AgentRepo

type AgentRepo struct {
	// Data map[AgentId]OwnerData // map[agentid]data
	Data core.ConcurrentMap[AgentId, *OwnerData] // map[agentid]data
}

func NewAgentRepo

func NewAgentRepo() (*AgentRepo, error)

func (*AgentRepo) Get

func (ar *AgentRepo) Get(agentId AgentId) (*OwnerData, bool)

func (*AgentRepo) Set

func (ar *AgentRepo) Set(agentId AgentId, ownerData *OwnerData)

type AgentState

type AgentState uint8
const (
	AgentStateNeeded      AgentState = iota + 1 // Agent is required but we don't have complete information
	AgentStateKnown                             // We have complete information but haven't established communication
	AgentStateIntroduced                        // We got a nice reply to our HELLO
	AgentStateOperational                       // We got a nice reply to our (secure) BEAT
	AgentStateLegacy                            // Established relationship but no shared zones (previously OPERATIONAL)
	AgentStateDegraded                          // Last successful heartbeat (in either direction) was more than 2x normal interval ago
	AgentStateInterrupted                       // Last successful heartbeat (in either direction) was more than 10x normal interval ago
	AgentStateError                             // We have tried to establish communication but failed
)

type ApiClient

type ApiClient struct {
	Name      string
	Client    *http.Client
	BaseUrl   string
	Addresses []string // if non-empty, replace the host part of the BaseUrl with each of these

	AuthMethod string
	UseTLS     bool
	Verbose    bool
	Debug      bool

	// deSEC stuff (from MUSIC)
	Email    string
	Password string
	TokViper *viper.Viper
	// contains filtered or unexported fields
}

func NewClient

func NewClient(name, baseurl, apikey, authmethod, rootcafile string) *ApiClient

func (*ApiClient) Delete

func (api *ApiClient) Delete(endpoint string) (int, []byte, error)

api Delete (not tested)

func (*ApiClient) Get

func (api *ApiClient) Get(endpoint string) (int, []byte, error)

api Get (not tested)

func (*ApiClient) Post

func (api *ApiClient) Post(endpoint string, data []byte) (int, []byte, error)

func (*ApiClient) Put

func (api *ApiClient) Put(endpoint string, data []byte) (int, []byte, error)

api Put

func (*ApiClient) RequestNG

func (api *ApiClient) RequestNG(method, endpoint string, data interface{}, dieOnError bool) (int, []byte, error)

func (*ApiClient) RequestNGWithContext

func (api *ApiClient) RequestNGWithContext(ctx context.Context, method, endpoint string, data interface{}, dieOnError bool) (int, []byte, error)

func (*ApiClient) SendPing

func (api *ApiClient) SendPing(pingcount int, dieOnError bool) (PingResponse, error)

func (*ApiClient) ShowApi

func (api *ApiClient) ShowApi()

func (*ApiClient) StartDaemon

func (api *ApiClient) StartDaemon(maxwait int, slurp bool, command string, daemonFlags []string)

slurp means that we'll connect to stdout and stderr on the underlying daemon to check for possible error messages (if it dies somehow). The problem is that connecting to stdout doesn't work well, it kills the daemon after a while. So we only want to slurp when explicitly checking for errors. command is an optional parameter specifying the daemon binary path. If empty, it falls back to viper.GetString("common.command") for backward compatibility. daemonFlags are additional command-line flags to pass to the daemon (e.g., --config, --debug, -v)

func (*ApiClient) StopDaemon

func (api *ApiClient) StopDaemon()

func (*ApiClient) UpdateDaemon

func (api *ApiClient) UpdateDaemon(data CommandPost, dieOnError bool) (int, CommandResponse, error)

func (*ApiClient) UrlReport

func (api *ApiClient) UrlReport(method, endpoint string, data []byte)

func (*ApiClient) UrlReportNG

func (api *ApiClient) UrlReportNG(method, fullurl string, data []byte)

type ApiServerAppConf

type ApiServerAppConf struct {
	Addresses []string
	ApiKey    SensitiveString
}

type ApiServerConf

type ApiServerConf struct {
	Addresses []string        `validate:"required"` // Must be in addr:port format
	ApiKey    SensitiveString `validate:"required"`
	CertFile  string          `validate:"required,file,certkey"`
	KeyFile   string          `validate:"required,file"`
	UseTLS    bool
	Server    ApiServerAppConf
	Agent     ApiServerAppConf
	// MSA       ApiServerAppConf
	Combiner ApiServerAppConf
}

type AppDetails

type AppDetails struct {
	Name             string
	Version          string
	Type             AppType
	Date             string
	ServerBootTime   time.Time
	ServerConfigTime time.Time
}

type AppType

type AppType uint8
const (
	AppTypeAuth AppType = iota + 1
	AppTypeAgent
	AppTypeCombiner
	AppTypeImr // simplified recursor
	AppTypeCli
	AppTypeReporter
	AppTypeScanner
	AppTypeKdc        // Key Distribution Center
	AppTypeKrs        // Key Receiving Service (edge receiver)
	AppTypeEdgeSigner // NYI
	AppTypeMPSigner   // MP signer (tdns-mp): DNS infra from tdns, MP wiring from tdns-mp
	AppTypeMPAgent    // MP agent (tdns-mp): DNS infra from tdns, MP wiring from tdns-mp
	AppTypeMPCombiner // MP combiner (tdns-mp): DNS infra from tdns, MP wiring from tdns-mp
)

type ApprovedEditRecord

type ApprovedEditRecord struct {
	EditID         int                 `json:"edit_id"`
	Zone           string              `json:"zone"`
	SenderID       string              `json:"sender_id"`
	DistributionID string              `json:"distribution_id"`
	Records        map[string][]string `json:"records"`
	ReceivedAt     time.Time           `json:"received_at"`
	ApprovedAt     time.Time           `json:"approved_at"`
}

ApprovedEditRecord represents a row in the CombinerApprovedEdits table.

type AuditResponseMsg

type AuditResponseMsg struct {
	SenderID  string
	Zone      string
	AuditData interface{} // Zone data repo snapshot (placeholder)
}

AuditResponseMsg carries audit data from a peer agent back to the requester. Delivered via MsgQs.AuditResponse channel.

func RequestAndWaitForAudit

func RequestAndWaitForAudit(ar *AgentRegistry, agent *Agent, zone string) *AuditResponseMsg

RequestAndWaitForAudit sends an RFI AUDIT to a peer agent and waits for the audit response on MsgQs.AuditResponse. Returns the audit data or nil on timeout/error.

type AuthDistribPost

type AuthDistribPost struct {
	Command string `json:"command"`
}

AuthDistribPost is the request body for /auth/distrib.

type AuthOption

type AuthOption uint8
const (
	AuthOptParentUpdate AuthOption = iota + 1
	AuthOptPersistOutboundSerial
)

type AuthPeerPost

type AuthPeerPost struct {
	Command string `json:"command"`
	PeerID  string `json:"peer_id,omitempty"` // Target peer for ping (default: configured agent)
}

AuthPeerPost is the request body for /auth/peer.

type AuthPeerResponse

type AuthPeerResponse struct {
	Time     time.Time `json:"time"`
	Error    bool      `json:"error"`
	ErrorMsg string    `json:"error_msg,omitempty"`
	Msg      string    `json:"msg,omitempty"`
}

AuthPeerResponse is the response body for /auth/peer.

type AuthQueryRequest

type AuthQueryRequest struct {
	// contains filtered or unexported fields
}

AuthQueryNG is the same as AuthQuery, but returns an RRset instead of a []dns.RR to be able to keep any RRSIGs. AuthQuery should be phased out. ns must be in addr:port format

type AuthQueryResponse

type AuthQueryResponse struct {
	// contains filtered or unexported fields
}

type BindPrivateKey

type BindPrivateKey struct {
	Private_Key_Format string `yaml:"Private-key-format"`
	Algorithm          string `yaml:"Algorithm"`
	PrivateKey         string `yaml:"PrivateKey"`
}

type BumperData

type BumperData struct {
	Zone   string
	Result chan BumperResponse
}

type BumperResponse

type BumperResponse struct {
	Time      time.Time
	Zone      string
	Msg       string
	OldSerial uint32
	NewSerial uint32
	Error     bool
	ErrorMsg  string
	Status    bool
}

type CatalogConf

type CatalogConf struct {
	GroupPrefixes GroupPrefixesConf             `yaml:"group_prefixes" mapstructure:"group_prefixes"`
	Policy        CatalogPolicy                 `yaml:"policy" mapstructure:"policy"` // Deprecated, kept for backward compatibility
	ConfigGroups  map[string]*ConfigGroupConfig `yaml:"config_groups" mapstructure:"config_groups"`
	MetaGroups    map[string]*ConfigGroupConfig `yaml:"meta_groups" mapstructure:"meta_groups"` // Deprecated, kept for backward compatibility
	SigningGroups map[string]*SigningGroupInfo  `yaml:"signing_groups" mapstructure:"signing_groups"`
}

CatalogConf defines configuration for catalog zone support (RFC 9432)

type CatalogMemberZone

type CatalogMemberZone struct {
	ZoneName     string
	Hash         string    // SHA256 hash of zone name (first 16 chars)
	Groups       []string  // List of group names associated with this zone (RFC 9432 terminology)
	DiscoveredAt time.Time // Timestamp when the zone was first added to the catalog
}

CatalogMemberZone represents a member zone in the catalog

type CatalogMembership

type CatalogMembership struct {
	CatalogZoneName string
	MemberZones     map[string]*CatalogMemberZone // zonename -> member data
	AvailableGroups []string                      // List of all defined groups (RFC 9432 terminology)
	// contains filtered or unexported fields
}

CatalogMembership manages the membership data for a catalog zone

func GetOrCreateCatalogMembership

func GetOrCreateCatalogMembership(catalogZoneName string) *CatalogMembership

GetOrCreateCatalogMembership returns the membership for a catalog zone, creating if needed

func (*CatalogMembership) AddGroup

func (cm *CatalogMembership) AddGroup(group string) error

AddGroup adds a group to the available groups list

func (*CatalogMembership) AddMemberZone

func (cm *CatalogMembership) AddMemberZone(zoneName string) error

AddMemberZone adds a zone to the catalog

func (*CatalogMembership) AddZoneGroup

func (cm *CatalogMembership) AddZoneGroup(zoneName, group string) error

AddZoneGroup associates a group with a zone

func (*CatalogMembership) GetGroups

func (cm *CatalogMembership) GetGroups() []string

GetGroups returns all available groups (thread-safe copy)

func (*CatalogMembership) GetMemberZones

func (cm *CatalogMembership) GetMemberZones() map[string]*MemberZone

GetMemberZones returns all member zones (thread-safe copy)

func (*CatalogMembership) RemoveGroup

func (cm *CatalogMembership) RemoveGroup(group string) error

RemoveGroup removes a group from the available groups list

func (*CatalogMembership) RemoveMemberZone

func (cm *CatalogMembership) RemoveMemberZone(zoneName string) error

RemoveMemberZone removes a zone from the catalog

func (*CatalogMembership) RemoveZoneGroup

func (cm *CatalogMembership) RemoveZoneGroup(zoneName, group string) error

RemoveZoneGroup disassociates a group from a zone

type CatalogPolicy

type CatalogPolicy struct {
	Zones struct {
		Add    string `yaml:"add" mapstructure:"add" validate:"omitempty,oneof=auto manual"`       // "auto" or "manual"
		Remove string `yaml:"remove" mapstructure:"remove" validate:"omitempty,oneof=auto manual"` // "auto" or "manual"
	} `yaml:"zones" mapstructure:"zones"`
}

CatalogPolicy defines policy for how catalog zones are processed

type CatalogPost

type CatalogPost struct {
	Command     string   `json:"command"`      // "create" | "zone-add" | "zone-delete" | "zone-list" | "group-add" | "group-delete" | "group-list" | "zone-group-add" | "zone-group-delete" | "notify-add" | "notify-remove" | "notify-list"
	CatalogZone string   `json:"catalog_zone"` // Name of the catalog zone
	Zone        string   `json:"zone"`         // Member zone name
	Group       string   `json:"group"`        // Group name (RFC 9432 terminology)
	Groups      []string `json:"groups"`       // Multiple groups (for zone-add with --groups flag)
	Address     string   `json:"address"`      // Notify address (IP:port) for notify-add/notify-remove
}

CatalogPost represents a request to manage catalog zones

type CatalogResponse

type CatalogResponse struct {
	Time            time.Time              `json:"time"`
	Error           bool                   `json:"error"`
	ErrorMsg        string                 `json:"error_msg,omitempty"`
	Msg             string                 `json:"msg,omitempty"`
	Zones           map[string]*MemberZone `json:"zones,omitempty"`            // For zone-list command
	Groups          []string               `json:"groups,omitempty"`           // For group-list command
	NotifyAddresses []string               `json:"notify_addresses,omitempty"` // For notify-list command
}

CatalogResponse represents the response from catalog operations

type CatalogZoneCallback

type CatalogZoneCallback func(update *CatalogZoneUpdate) error

CatalogZoneCallback is called when a catalog zone is updated

type CatalogZoneUpdate

type CatalogZoneUpdate struct {
	CatalogZone string                 `json:"catalog_zone"`
	MemberZones map[string]*MemberZone `json:"member_zones"`
	Serial      uint32                 `json:"serial"`
	UpdateTime  time.Time              `json:"update_time"`
}

CatalogZoneUpdate contains information about catalog zone changes

func ParseCatalogZone

func ParseCatalogZone(zd *ZoneData) (*CatalogZoneUpdate, error)

ParseCatalogZone parses a catalog zone and extracts member zones and groups

type ChildDelegationData

type ChildDelegationData struct {
	DelHasChanged    bool      // When returned from a scanner, this indicates that a change has been detected
	ParentSerial     uint32    // The parent serial that this data was correct for
	Timestamp        time.Time // Time at which this data was fetched
	ChildName        string
	RRsets           map[string]map[uint16]core.RRset // map[ownername]map[rrtype]RRset
	NS_rrs           []dns.RR
	A_glue           []dns.RR
	A_glue_rrsigs    []dns.RR
	AAAA_glue        []dns.RR
	AAAA_glue_rrsigs []dns.RR
	NS_rrset         *core.RRset
	DS_rrset         *core.RRset
	A_rrsets         []*core.RRset
	AAAA_rrsets      []*core.RRset
}

type ChunkPayloadStore

type ChunkPayloadStore interface {
	Get(qname string) (payload []byte, format uint8, ok bool)
	Set(qname string, payload []byte, format uint8)
	GetChunk(qname string, sequence uint16) (chunk *core.CHUNK, ok bool)
	SetChunks(qname string, chunks []*core.CHUNK)
}

ChunkPayloadStore stores payloads keyed by NOTIFY qname for query-mode CHUNK. Agent: Set before sending NOTIFY; CHUNK query handler Get() returns payload. Entries expire after TTL to avoid unbounded growth. Format is stored alongside payload (FormatJSON=1, FormatJWT=2).

type CombinerDebugPost

type CombinerDebugPost struct {
	Command string `json:"command"`
	Zone    string `json:"zone,omitempty"`
	AgentID string `json:"agent_id,omitempty"` // For agent-targeted commands (e.g. agent-ping)
}

type CombinerDebugResponse

type CombinerDebugResponse struct {
	Time               time.Time                                            `json:"time"`
	Error              bool                                                 `json:"error"`
	ErrorMsg           string                                               `json:"error_msg,omitempty"`
	Msg                string                                               `json:"msg,omitempty"`
	CombinerData       map[string]map[string]map[string][]string            `json:"combiner_data,omitempty"`       // zone → owner → rrtype → []rr
	AgentContributions map[string]map[string]map[string]map[string][]string `json:"agent_contributions,omitempty"` // zone → agent → owner → rrtype → []rr
}

CombinerDebugResponse returns both the merged CombinerData and the per-agent AgentContributions breakdown.

type CombinerDistribPost

type CombinerDistribPost struct {
	Command string `json:"command"` // "list", "purge"
	Force   bool   `json:"force,omitempty"`
}

CombinerDistribPost represents a request to the combiner distrib API

type CombinerDistribResponse

type CombinerDistribResponse struct {
	Time          time.Time              `json:"time"`
	Error         bool                   `json:"error,omitempty"`
	ErrorMsg      string                 `json:"error_msg,omitempty"`
	Msg           string                 `json:"msg,omitempty"`
	Summaries     []*DistributionSummary `json:"summaries,omitempty"`
	Distributions []string               `json:"distributions,omitempty"` // For backward compatibility
}

CombinerDistribResponse represents a response from the combiner distrib API

type CombinerEditPost

type CombinerEditPost struct {
	Command string   `json:"command"` // "list", "list-approved", "list-rejected", "approve", "reject", "clear"
	Zone    string   `json:"zone"`
	EditID  int      `json:"edit_id,omitempty"`
	Reason  string   `json:"reason,omitempty"`
	Tables  []string `json:"tables,omitempty"` // for "clear": which tables to clear; empty = all
}

CombinerEditPost represents a CLI request for managing pending/rejected edits.

type CombinerEditResponse

type CombinerEditResponse struct {
	Time     time.Time                      `json:"time"`
	Error    bool                           `json:"error"`
	ErrorMsg string                         `json:"error_msg,omitempty"`
	Msg      string                         `json:"msg,omitempty"`
	Pending  []*PendingEditRecord           `json:"pending,omitempty"`
	Approved []*ApprovedEditRecord          `json:"approved,omitempty"`
	Rejected []*RejectedEditRecord          `json:"rejected,omitempty"`
	Current  map[string]map[string][]string `json:"current,omitempty"` // agent → rrtype → []rr
}

CombinerEditResponse is the response for edit management commands.

type CombinerOption

type CombinerOption uint8
const (
	CombinerOptAddSignature CombinerOption = iota + 1
)

type CombinerPost

type CombinerPost struct {
	Command string              `json:"command"` // add, list, remove
	Zone    string              `json:"zone"`    // zone name
	Data    map[string][]string `json:"data"`    // The RRs as strings, indexed by owner name
}

type CombinerResponse

type CombinerResponse struct {
	Time     time.Time                `json:"time"`
	Error    bool                     `json:"error"`
	ErrorMsg string                   `json:"error_msg,omitempty"`
	Msg      string                   `json:"msg,omitempty"`
	Data     map[string][]RRsetString `json:"data,omitempty"`
}

type CombinerState

type CombinerState struct {
	// ErrorJournal records errors during CHUNK NOTIFY processing for operational diagnostics.
	// Queried via "transaction errors" CLI commands. If nil, errors are only logged.
	ErrorJournal *ErrorJournal

	// ProtectedNamespaces: domain suffixes belonging to this provider.
	// NS records from remote agents whose targets fall within these namespaces are rejected.
	ProtectedNamespaces []string
	// contains filtered or unexported fields
}

CombinerState holds combiner-specific state that outlives individual CHUNK messages. Used by CLI commands (error journal queries) and in-process SendToCombiner. Transport routing is handled by the unified ChunkNotifyHandler.

func RegisterCombinerChunkHandler

func RegisterCombinerChunkHandler(localID string, secureWrapper *transport.SecurePayloadWrapper) (*CombinerState, error)

RegisterCombinerChunkHandler registers the combiner's CHUNK handler using ChunkNotifyHandler. Creates a ChunkNotifyHandler with combiner-appropriate settings and registers it as a NotifyHandlerFunc. Returns CombinerState for error journal access and in-process updates.

func RegisterSignerChunkHandler

func RegisterSignerChunkHandler(localID string, secureWrapper *transport.SecurePayloadWrapper) (*CombinerState, error)

RegisterSignerChunkHandler registers a CHUNK NOTIFY handler for the signer (tdns-auth). Uses ChunkNotifyHandler — the signer only routes messages through the signer router which handles ping and KEYSTATE.

func (*CombinerState) ChunkHandler

func (cs *CombinerState) ChunkHandler() *transport.ChunkNotifyHandler

ChunkHandler returns the underlying ChunkNotifyHandler for wiring into TransportManager.

func (*CombinerState) ProcessUpdate

func (cs *CombinerState) ProcessUpdate(req *CombinerSyncRequest, localAgents map[string]bool, kdb *KeyDB, tm *MPTransportBridge) *CombinerSyncResponse

ProcessUpdate delegates to the standalone CombinerProcessUpdate.

func (*CombinerState) SetGetPeerAddress

func (cs *CombinerState) SetGetPeerAddress(fn func(senderID string) (address string, ok bool))

SetGetPeerAddress sets the GetPeerAddress callback on the underlying ChunkNotifyHandler.

func (*CombinerState) SetRouter

func (cs *CombinerState) SetRouter(router *transport.DNSMessageRouter)

SetRouter sets the router on the underlying ChunkNotifyHandler. Called from main_initfuncs.go after the router is initialized.

func (*CombinerState) SetSecureWrapper

func (cs *CombinerState) SetSecureWrapper(sw *transport.SecurePayloadWrapper)

SetSecureWrapper sets the secure wrapper on the underlying ChunkNotifyHandler.

type CombinerSyncRequest

type CombinerSyncRequest struct {
	SenderID       string                   // Identity of the sending agent
	DeliveredBy    string                   // Identity of the agent that delivered this to the combiner
	Zone           string                   // Zone being updated
	ZoneClass      string                   // "mp" (default) or "provider"
	SyncType       string                   // Type of sync: "NS", "DNSKEY", "CDS", "CSYNC", "GLUE"
	Records        map[string][]string      // RR strings grouped by owner name (same as CombinerPost.Data)
	Operations     []core.RROperation       // Explicit operations (takes precedence over Records)
	Publish        *core.PublishInstruction // KEY/CDS publication instruction
	Serial         uint32                   // Zone serial (optional)
	DistributionID string                   // Distribution ID for tracking
	Timestamp      time.Time                // When the request was created
}

CombinerSyncRequest represents a sync request to the combiner. Uses the same data structure as CombinerPost.Data for transport neutrality.

func ConvertZoneUpdateToSyncRequest

func ConvertZoneUpdateToSyncRequest(update *ZoneUpdate, senderID string, distributionID string) *CombinerSyncRequest

ConvertZoneUpdateToSyncRequest converts a ZoneUpdate to a CombinerSyncRequest. Groups records by owner for transport neutrality (same structure as CombinerPost).

func ParseAgentMsgNotify

func ParseAgentMsgNotify(data []byte, distributionID string) (*CombinerSyncRequest, error)

ParseAgentMsgNotify parses a sync payload into a CombinerSyncRequest. Expects the standard AgentMsgPost format (OriginatorID/Zone/Records).

type CombinerSyncRequestPlus

type CombinerSyncRequestPlus struct {
	Request  *CombinerSyncRequest
	Response chan *CombinerSyncResponse
}

CombinerSyncRequestPlus includes a response channel for async processing.

type CombinerSyncResponse

type CombinerSyncResponse struct {
	DistributionID string         // Echoed from request
	Zone           string         // Zone that was updated
	Nonce          string         // Echoed nonce from the incoming sync/update message
	Status         string         // "ok", "partial", "error"
	Message        string         // Human-readable message
	AppliedRecords []string       // RRs that were successfully applied (additions)
	RemovedRecords []string       // RRs that were successfully removed (deletions)
	RejectedItems  []RejectedItem // Items that were rejected with reasons
	Timestamp      time.Time      // When the response was created
	DataChanged    bool           // True when zone data was actually mutated (not idempotent re-apply)
}

CombinerSyncResponse represents a confirmation from the combiner.

func CombinerProcessUpdate

func CombinerProcessUpdate(req *CombinerSyncRequest, protectedNamespaces []string, localAgents map[string]bool, kdb *KeyDB, tm *MPTransportBridge) *CombinerSyncResponse

func SendToCombiner

func SendToCombiner(state *CombinerState, req *CombinerSyncRequest) *CombinerSyncResponse

SendToCombiner is a helper function that sends a sync request to the combiner and waits for a response. This is called from SynchedDataEngine. For in-process communication, this calls CombinerProcessUpdate directly.

type CommandPost

type CommandPost struct {
	Command    string
	SubCommand string
	Zone       string
	Force      bool
}

type CommandResponse

type CommandResponse struct {
	AppName      string
	Time         time.Time
	Status       string
	Zone         string
	Names        []string
	Zones        map[string]ZoneConf
	Msg          string
	ApiEndpoints []string
	Error        bool
	ErrorMsg     string
}

type Config

type Config struct {
	Service        ServiceConf
	DnsEngine      DnsEngineConf
	Imr            ImrEngineConf `yaml:"imrengine" mapstructure:"imrengine"`
	ApiServer      ApiServerConf
	DnssecPolicies map[string]DnssecPolicyConf
	MultiSigner    map[string]MultiSignerConf `yaml:"multisigner"`
	MultiProvider  *MultiProviderConf         `yaml:"multi-provider" mapstructure:"multi-provider"`
	Catalog        *CatalogConf               `yaml:"catalog" mapstructure:"catalog"`
	DynamicZones   DynamicZonesConf           `yaml:"dynamiczones" mapstructure:"dynamiczones"`
	Zones          []ZoneConf                 `yaml:"zones"`
	Templates      []ZoneConf                 `yaml:"templates"`
	Kasp           KaspConf                   `yaml:"kasp" mapstructure:"kasp"`
	Keys           KeyConf
	Db             DbConf
	Registrars     map[string][]string
	Log            LogConf
	Internal       InternalConf
}
var Conf Config

func LoadConfigForKeys

func LoadConfigForKeys(path string) (*Config, error)

LoadConfigForKeys reads the given YAML config file and decodes it into Config. Used by tdns-cli agent/combiner keys to get long_term_jose_priv_key path. Does not process includes or run full ParseConfig.

func (*Config) APIagent

func (conf *Config) APIagent(refreshZoneCh chan<- ZoneRefresher, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func (*Config) APIagentDebug

func (conf *Config) APIagentDebug() func(w http.ResponseWriter, r *http.Request)

func (*Config) APIagentDistrib

func (conf *Config) APIagentDistrib(cache *DistributionCache) func(w http.ResponseWriter, r *http.Request)

func (*Config) APIagentTransaction

func (conf *Config) APIagentTransaction(cache *DistributionCache) func(w http.ResponseWriter, r *http.Request)

APIagentTransaction handles transaction diagnostic requests for agents

func (*Config) APIbeat

func (conf *Config) APIbeat() func(w http.ResponseWriter, r *http.Request)

func (*Config) APIcombinerDistrib

func (conf *Config) APIcombinerDistrib(cache *DistributionCache) func(w http.ResponseWriter, r *http.Request)

func (*Config) APIcombinerTransaction

func (conf *Config) APIcombinerTransaction() func(w http.ResponseWriter, r *http.Request)

APIcombinerTransaction handles transaction diagnostic requests for combiners

func (*Config) APIhello

func (conf *Config) APIhello() func(w http.ResponseWriter, r *http.Request)

This is the agent-to-agent sync API hello handler.

func (*Config) APImsg

func (conf *Config) APImsg() func(w http.ResponseWriter, r *http.Request)

func (*Config) APIsyncPing

func (conf *Config) APIsyncPing() func(w http.ResponseWriter, r *http.Request)

APIsyncPing is the HSYNC peer ping handler on the sync API router (/sync/ping). This is separate from the management /ping (APIping in api_utils.go) which returns boot time and version info. This handler echoes the nonce for round-trip verification and routes the ping to MsgQs.Ping for state tracking.

func (*Config) AddDynamicZoneToConfig

func (conf *Config) AddDynamicZoneToConfig(zd *ZoneData) error

AddDynamicZoneToConfig adds or updates a zone in the dynamic config file

func (*Config) CheckDynamicConfigFileIncluded

func (conf *Config) CheckDynamicConfigFileIncluded(includedFiles []string) bool

CheckDynamicConfigFileIncluded checks if the dynamic config file is included in the main config Returns true if included, false otherwise (logs warning if not included)

func (*Config) FindDnsEngineAddrs

func (conf *Config) FindDnsEngineAddrs() ([]string, error)

Extract the addresses we listen on from the dnsengine configuration. Exclude localhost and non-standard ports.

func (*Config) ImrEngine

func (conf *Config) ImrEngine(ctx context.Context, quiet bool) error

func (*Config) InitializeKeyDB

func (conf *Config) InitializeKeyDB() error

func (*Config) LoadDynamicZoneFiles

func (conf *Config) LoadDynamicZoneFiles(ctx context.Context) error

LoadDynamicZoneFiles loads dynamic zones from the dynamic config file on startup This should be called after ParseZones() but before engines start It loads zones that were persisted in previous runs

func (*Config) LocalIdentity

func (conf *Config) LocalIdentity() string

LocalIdentity returns the local node's identity string, regardless of role. Used by sync API handlers (APIhello, APIbeat, APIsyncPing) so they work on agent, combiner, and signer without referencing conf.Agent.Identity directly.

func (*Config) MainInit

func (conf *Config) MainInit(ctx context.Context, defaultcfg string) error

func (*Config) MainLoop

func (conf *Config) MainLoop(ctx context.Context, cancel context.CancelFunc)

func (*Config) NewAgentRegistry

func (conf *Config) NewAgentRegistry() *AgentRegistry

func (*Config) ParseConfig

func (conf *Config) ParseConfig(reload bool) error

func (*Config) ParseZones

func (conf *Config) ParseZones(ctx context.Context, reload bool) ([]string, error)

func ParseZones(zones map[string]tdns.ZoneConf, zrch chan tdns.ZoneRefresher) error {

func (*Config) ReloadConfig

func (conf *Config) ReloadConfig() (string, error)

func (*Config) ReloadZoneConfig

func (conf *Config) ReloadZoneConfig(ctx context.Context) (string, error)

func (*Config) RemoveDynamicZoneFromConfig

func (conf *Config) RemoveDynamicZoneFromConfig(zoneName string) error

RemoveDynamicZoneFromConfig removes a zone from the dynamic config file

func (*Config) SetupAPIRouter

func (conf *Config) SetupAPIRouter(ctx context.Context) (*mux.Router, error)

func (*Config) SetupAgent

func (conf *Config) SetupAgent(all_zones []string) error

func (*Config) SetupAgentAutoZone

func (conf *Config) SetupAgentAutoZone(zonename string) (*ZoneData, error)

func (*Config) SetupAgentSyncRouter

func (conf *Config) SetupAgentSyncRouter(ctx context.Context) (*mux.Router, error)

This is the agent-to-agent sync API router.

func (*Config) SetupCombinerSyncRouter

func (conf *Config) SetupCombinerSyncRouter(ctx context.Context) (*mux.Router, error)

SetupCombinerSyncRouter sets up the HTTPS sync API for the combiner role. Agents can send HELLO, BEAT, PING, and MSG to the combiner over this router. Runs on a dedicated port (combiner.api.addresses.listen) separate from the management API, using mutual TLS with client cert verification against AgentRegistry.

func (*Config) SetupSignerSyncRouter

func (conf *Config) SetupSignerSyncRouter(ctx context.Context) (*mux.Router, error)

SetupSignerSyncRouter sets up the HTTPS sync API for the signer (tdns-auth) role. Agents can send HELLO, BEAT, PING, and MSG to the signer over this router.

func (*Config) SetupSimpleAPIRouter

func (conf *Config) SetupSimpleAPIRouter(ctx context.Context) (*mux.Router, error)

The simple API router is sufficient for tdns-imr, tdns-scanner and tdns-reporter.

func (*Config) ShouldPersistZone

func (conf *Config) ShouldPersistZone(zd *ZoneData) bool

ShouldPersistZone checks if a zone should be persisted based on configuration Returns true if the zone should be written to disk

func (*Config) StartAgent

func (conf *Config) StartAgent(ctx context.Context, apirouter *mux.Router) error

StartAgent starts subsystems for tdns-agent

func (*Config) StartAuth

func (conf *Config) StartAuth(ctx context.Context, apirouter *mux.Router) error

StartAuth starts subsystems for tdns-auth

func (*Config) StartCombiner

func (conf *Config) StartCombiner(ctx context.Context, apirouter *mux.Router) error

StartCombiner starts subsystems for tdns-combiner

func (*Config) StartImr

func (conf *Config) StartImr(ctx context.Context, apirouter *mux.Router) error

StartImr starts subsystems for tdns-imr

func (*Config) StartScanner

func (conf *Config) StartScanner(ctx context.Context, apirouter *mux.Router) error

StartScanner starts subsystems for tdns-scanner

func (*Config) SynchedDataEngine

func (conf *Config) SynchedDataEngine(ctx context.Context, msgQs *MsgQs)

SynchedDataEngine is a component that updates the combiner with new information received from the agents that are sharing zones with us.

func (*Config) WriteDynamicConfigFile

func (conf *Config) WriteDynamicConfigFile() error

WriteDynamicConfigFile writes the current dynamic zones to the config file This should be called whenever a dynamic zone is created, updated, or deleted

type ConfigEntry

type ConfigEntry struct {
	Key   string
	Value interface{}
}

OrderedConfig preserves the order of configuration entries

type ConfigGroupConfig

type ConfigGroupConfig struct {
	Name     string   `yaml:"-" mapstructure:"-"` // Populated from map key
	Upstream string   `yaml:"upstream" mapstructure:"upstream"`
	TsigKey  string   `yaml:"tsig_key" mapstructure:"tsig_key"`
	Store    string   `yaml:"store" mapstructure:"store"`
	Options  []string `yaml:"options" mapstructure:"options"`
}

ConfigGroupConfig provides configuration for zone transfers from catalog config groups (RFC 9432 terminology)

type ConfigPost

type ConfigPost struct {
	Command string // status | sync | ...
}

type ConfigResponse

type ConfigResponse struct {
	AppName         string
	Time            time.Time
	DnsEngine       DnsEngineConf
	ApiServer       ApiServerConf
	Identities      []string
	CombinerOptions map[CombinerOption]bool
	Msg             string
	Error           bool
	ErrorMsg        string
}

type ConfigResponseMsg

type ConfigResponseMsg struct {
	SenderID   string
	Zone       string
	Subtype    string            // "upstream", "downstream", "sig0key"
	ConfigData map[string]string // Key-value config data
}

ConfigResponseMsg carries config data from a peer agent back to the requester. Delivered via MsgQs.ConfigResponse channel.

func RequestAndWaitForConfig

func RequestAndWaitForConfig(ar *AgentRegistry, agent *Agent, zone string, subtype string) *ConfigResponseMsg

RequestAndWaitForConfig sends an RFI CONFIG to a peer agent and waits for the config response on MsgQs.ConfigResponse. Returns the config data or nil on timeout/error.

type ConfirmationDetail

type ConfirmationDetail struct {
	DistributionID string
	Zone           ZoneName
	Source         string // Identifies the confirming peer (combiner ID or agent ID)
	Status         string // "ok", "partial", "error"
	Message        string
	AppliedRecords []string
	RemovedRecords []string // RR strings confirmed as removed by combiner
	RejectedItems  []RejectedItemInfo
	Truncated      bool
	Timestamp      time.Time
}

ConfirmationDetail carries per-RR confirmation feedback from the combiner through to the SynchedDataEngine.

type ConfirmationItem

type ConfirmationItem struct {
	RecordType string `json:"record_type"`
	Zone       string `json:"zone"`
	Status     string `json:"status"`
	Details    string `json:"details,omitempty"`
}

ConfirmationItem represents a single item in a confirmation.

type CurrentScanData

type CurrentScanData struct {
	RRset  *core.RRset `json:"-"` // Current RRset for ScanRRtype test, nil if no data exists (not JSON serialized)
	CDS    *core.RRset `json:"-"` // Current CDS RRset for ScanCDS test, nil if no data exists (not JSON serialized)
	DS     *core.RRset `json:"-"` // Current DS RRset from delegation backend (not JSON serialized)
	CSYNC  *core.RRset `json:"-"` // Current CSYNC RRset for ScanCSYNC test, nil if no data exists (not JSON serialized)
	DNSKEY *core.RRset `json:"-"` // Current DNSKEY RRset for ScanDNSKEY test, nil if no data exists (not JSON serialized)
}

ScanCurrentData holds the current data for a scan test For ScanRRtype tests, it contains the current RRset for the queried RRtype at the name Note: This struct is not JSON-serializable directly. Use ToJSON() to convert to CurrentScanDataJSON.

func (*CurrentScanData) ToJSON

func (csd *CurrentScanData) ToJSON() CurrentScanDataJSON

ToJSON converts CurrentScanData to a JSON-serializable format

type CurrentScanDataJSON

type CurrentScanDataJSON struct {
	RRset  *core.RRsetString `json:"rrset,omitempty"`
	CDS    *core.RRsetString `json:"cds,omitempty"`
	DS     *core.RRsetString `json:"ds,omitempty"`
	CSYNC  *core.RRsetString `json:"csync,omitempty"`
	DNSKEY *core.RRsetString `json:"dnskey,omitempty"`
}

CurrentScanDataJSON is the JSON-serializable version of CurrentScanData

type CustomValidator

type CustomValidator struct {
	*validator.Validate
}

CustomValidator is a struct that embeds the validator.Validate type

func NewCustomValidator

func NewCustomValidator() (*CustomValidator, error)

NewCustomValidator creates a new instance of CustomValidator

type DBDelegationBackend

type DBDelegationBackend struct {
	// contains filtered or unexported fields
}

func (*DBDelegationBackend) ApplyChildUpdate

func (b *DBDelegationBackend) ApplyChildUpdate(parentZone string, ur UpdateRequest) error

func (*DBDelegationBackend) GetDelegationData

func (b *DBDelegationBackend) GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

func (*DBDelegationBackend) ListChildren

func (b *DBDelegationBackend) ListChildren(parentZone string) ([]string, error)

func (*DBDelegationBackend) Name

func (b *DBDelegationBackend) Name() string

type DbConf

type DbConf struct {
	File string // `validate:"required"`
}

type DebugPost

type DebugPost struct {
	Command string
	Zone    string
	Qname   string
	Qtype   uint16
	Verbose bool
}

type DebugResponse

type DebugResponse struct {
	AppName    string
	Time       time.Time
	Status     string
	Zone       string
	OwnerIndex map[string]int
	RRset      core.RRset
	//	TrustedDnskeys	map[string]dns.DNSKEY
	//	TrustedSig0keys	map[string]dns.KEY
	TrustedDnskeys  []cache.CachedDnskeyRRset
	TrustedSig0keys map[string]Sig0Key
	CachedRRsets    []cache.CachedRRset
	Validated       bool
	Msg             string
	Error           bool
	ErrorMsg        string
}

type DeferredAgentTask

type DeferredAgentTask struct {
	Precondition func() bool
	Action       func() (bool, error)
	Desc         string
}

AgentTask is a task that needs to be executed once the Precondition is met. A typical case is when we need to talk to a remote agent regarding zone transfer provisioning, but cannot do that until the remote agent is operational. The Precondition is checked every time a heartbeat is received from the remote agent.

type DeferredTask

type DeferredTask struct {
	Action      string
	Target      string
	ZoneName    string
	RetryCount  int
	MaxRetries  int
	LastAttempt time.Time
}

Define task struct for deferred operations

type DeferredUpdate

type DeferredUpdate struct {
	Cmd          string
	ZoneName     string
	AddTime      time.Time
	Description  string
	PreCondition func() bool
	Action       func() error
}

type DelegationBackend

type DelegationBackend interface {
	// ApplyChildUpdate processes an approved child UPDATE.
	// Actions are ClassINET (add), ClassNONE (delete-RR), ClassANY (delete-RRset).
	ApplyChildUpdate(parentZone string, ur UpdateRequest) error

	// GetDelegationData returns current delegation RRs for a child zone,
	// grouped by owner name and RR type.
	GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

	// ListChildren returns all child zones with stored delegation data.
	ListChildren(parentZone string) ([]string, error)

	// Name returns the backend name for logging.
	Name() string
}

DelegationBackend is the interface for storing delegation data received from child zones via DNS UPDATE. Implementations persist the data in different ways (in-memory zone, database, zone files, etc.).

func LookupDelegationBackend

func LookupDelegationBackend(name string, kdb *KeyDB, zd *ZoneData) (DelegationBackend, error)

LookupDelegationBackend resolves a backend name to a DelegationBackend.

Predefined names:

  • "db" → DBDelegationBackend (uses the zone's existing KeyDB)
  • "direct" → DirectDelegationBackend (modifies in-memory zone data)

Any other name is looked up in the "delegation-backends" config list.

type DelegationBackendConf

type DelegationBackendConf struct {
	Name          string `yaml:"name"`
	Type          string `yaml:"type"`
	Directory     string `yaml:"directory"`      // zonefile backend
	NotifyCommand string `yaml:"notify-command"` // zonefile backend
}

DelegationBackendConf is a named backend definition from the config file. Predefined backends ("db", "direct") need no config entry. Named backends are only needed for types that require parameters (e.g. "zonefile").

type DelegationData

type DelegationData struct {
	CurrentNS *core.RRset
	AddedNS   *core.RRset
	RemovedNS *core.RRset

	BailiwickNS []string
	A_glue      map[string]*core.RRset // map[nsname]
	AAAA_glue   map[string]*core.RRset // map[nsname]
	Actions     []dns.RR               // actions are DNS UPDATE actions that modify delegation data
	Time        time.Time
}

type DelegationPost

type DelegationPost struct {
	Command string // status | sync | export | ...
	Scheme  uint8  // 1=notify | 2=update
	Zone    string
	Force   bool
	Outfile string `json:"outfile,omitempty"` // for "export": destination file path
}

type DelegationResponse

type DelegationResponse struct {
	AppName    string
	Time       time.Time
	Zone       string
	SyncStatus DelegationSyncStatus
	Msg        string
	Error      bool
	ErrorMsg   string
}

type DelegationSyncRequest

type DelegationSyncRequest struct {
	Command      string
	ZoneName     string
	ZoneData     *ZoneData
	SyncStatus   DelegationSyncStatus
	OldDnskeys   *core.RRset
	NewDnskeys   *core.RRset
	MsignerGroup *core.RRset
	Response     chan DelegationSyncStatus // used for API-based requests
}

type DelegationSyncStatus

type DelegationSyncStatus struct {
	ZoneName      string
	Parent        string // use zd.Parent instead
	Time          time.Time
	InSync        bool
	Status        string
	Msg           string
	Rcode         uint8
	Adds          []dns.RR `json:"-"`
	Removes       []dns.RR `json:"-"`
	NsAdds        []dns.RR `json:"-"`
	NsRemoves     []dns.RR `json:"-"`
	AAdds         []dns.RR `json:"-"`
	ARemoves      []dns.RR `json:"-"`
	AAAAAdds      []dns.RR `json:"-"`
	AAAARemoves   []dns.RR `json:"-"`
	DNSKEYAdds    []dns.RR `json:"-"`
	DNSKEYRemoves []dns.RR `json:"-"`
	DSAdds        []dns.RR `json:"-"`
	DSRemoves     []dns.RR `json:"-"`
	Error         bool
	ErrorMsg      string
	UpdateResult  UpdateResult // Experimental
	// Complete new delegation data for replace mode
	NewNS   []dns.RR `json:"-"`
	NewA    []dns.RR `json:"-"`
	NewAAAA []dns.RR `json:"-"`
	NewDS   []dns.RR `json:"-"`
	// String representations for JSON serialization (populated by ToStrings())
	NsAddsStr      []string `json:"NsAdds,omitempty"`
	NsRemovesStr   []string `json:"NsRemoves,omitempty"`
	AAddsStr       []string `json:"AAdds,omitempty"`
	ARemovesStr    []string `json:"ARemoves,omitempty"`
	AAAAAddsStr    []string `json:"AAAAAdds,omitempty"`
	AAAARemovesStr []string `json:"AAAARemoves,omitempty"`
	DSAddsStr      []string `json:"DSAdds,omitempty"`
	DSRemovesStr   []string `json:"DSRemoves,omitempty"`
}

func (*DelegationSyncStatus) ToStrings

func (dss *DelegationSyncStatus) ToStrings()

type DirectDelegationBackend

type DirectDelegationBackend struct {
	// contains filtered or unexported fields
}

func (*DirectDelegationBackend) ApplyChildUpdate

func (b *DirectDelegationBackend) ApplyChildUpdate(parentZone string, ur UpdateRequest) error

func (*DirectDelegationBackend) GetDelegationData

func (b *DirectDelegationBackend) GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

func (*DirectDelegationBackend) ListChildren

func (b *DirectDelegationBackend) ListChildren(parentZone string) ([]string, error)

func (*DirectDelegationBackend) Name

func (b *DirectDelegationBackend) Name() string

type DistributionCache

type DistributionCache struct {
	// contains filtered or unexported fields
}

DistributionCache is an in-memory cache of distributions keyed by QNAME

func NewDistributionCache

func NewDistributionCache() *DistributionCache

NewDistributionCache creates a new distribution cache

func (*DistributionCache) Add

func (dc *DistributionCache) Add(qname string, info *DistributionInfo)

Add adds a distribution to the cache

func (*DistributionCache) Get

func (dc *DistributionCache) Get(qname string) (*DistributionInfo, bool)

Get retrieves a distribution by QNAME

func (*DistributionCache) List

func (dc *DistributionCache) List(senderID string) []*DistributionInfo

List returns all distributions for a given sender

func (*DistributionCache) MarkCompleted

func (dc *DistributionCache) MarkCompleted(qname string)

MarkCompleted marks a distribution as completed

func (*DistributionCache) PurgeAll

func (dc *DistributionCache) PurgeAll() int

PurgeAll removes all distributions

func (*DistributionCache) PurgeCompleted

func (dc *DistributionCache) PurgeCompleted(olderThan time.Duration) int

PurgeCompleted removes completed distributions older than the given duration. Incomplete distributions (CompletedAt == nil) are never purged; only explicit "purge --force" removes them.

func (*DistributionCache) PurgeExpired

func (dc *DistributionCache) PurgeExpired() int

PurgeExpired removes distributions that have passed their ExpiresAt time. This implements fast expiration for beat/ping messages to reduce clutter. Returns the number of distributions removed.

func (*DistributionCache) StartCleanupGoroutine

func (dc *DistributionCache) StartCleanupGoroutine(ctx context.Context)

StartCleanupGoroutine starts a background goroutine that periodically removes expired distributions. The cleanup runs every minute to keep the distribution list clean without excessive overhead.

type DistributionInfo

type DistributionInfo struct {
	DistributionID string
	SenderID       string
	ReceiverID     string
	Operation      string
	ContentType    string
	State          string
	PayloadSize    int // Size of the final payload in bytes (after encryption, before chunking)
	CreatedAt      time.Time
	CompletedAt    *time.Time
	ExpiresAt      *time.Time // When this distribution should be cleaned up (nil = no expiration)
	QNAME          string     // The DNS QNAME used to retrieve this distribution
}

DistributionInfo holds information about a distribution

type DistributionSummary

type DistributionSummary struct {
	DistributionID string `json:"distribution_id"`
	SenderID       string `json:"sender_id"`
	ReceiverID     string `json:"receiver_id"`
	Operation      string `json:"operation"`
	ContentType    string `json:"content_type"`
	State          string `json:"state"`
	PayloadSize    int    `json:"payload_size"`
	CreatedAt      string `json:"created_at"`
	CompletedAt    string `json:"completed_at,omitempty"`
}

DistributionSummary contains summary information about a distribution

type DnsEngineConf

type DnsEngineConf struct {
	Addresses   []string              `yaml:"addresses" validate:"required"`
	CertFile    string                `yaml:"certfile,omitempty"`
	KeyFile     string                `yaml:"keyfile,omitempty"`
	Transports  []string              `yaml:"transports" validate:"required,min=1,dive,oneof=do53 dot doh doq"` // "do53", "dot", "doh", "doq"
	OptionsStrs []string              `yaml:"options" mapstructure:"options"`
	Options     map[AuthOption]string `yaml:"-" mapstructure:"-"`
}

type DnsHandlerRequest

type DnsHandlerRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
}

type DnsNotifyRequest

type DnsNotifyRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
	Options        *edns0.MsgOptions
	Status         *NotifyStatus
}

type DnsQueryRequest

type DnsQueryRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
	Qtype          uint16
	Options        *edns0.MsgOptions
}

type DnsUpdateRequest

type DnsUpdateRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
	Options        *edns0.MsgOptions
	Status         *UpdateStatus
}

type DnskeyStatus

type DnskeyStatus struct {
	Time             time.Time
	ZoneName         string
	LocalAdds        []dns.RR // Local DNSKEYs added since last check
	LocalRemoves     []dns.RR // Local DNSKEYs removed since last check
	CurrentLocalKeys []dns.RR // Complete current set of local DNSKEYs (for replace operations)
}

DnskeyStatus holds the result of DNSKEY change detection (local keys only).

type DnssecKey

type DnssecKey struct {
	Name                   string
	State                  string
	Keyid                  uint16
	Flags                  uint16
	Algorithm              string
	Creator                string
	PrivateKey             string //
	Key                    dns.DNSKEY
	Keystr                 string
	PropagationConfirmed   bool      // True when all remote providers confirmed this key
	PropagationConfirmedAt time.Time // When propagation was confirmed (zero if not confirmed)
}

type DnssecKeyWithTimestamps

type DnssecKeyWithTimestamps struct {
	ZoneName    string
	KeyTag      uint16
	Algorithm   uint8
	Flags       uint16
	State       string
	KeyRR       string
	PublishedAt *time.Time
	RetiredAt   *time.Time
}

DnssecKeyWithTimestamps extends KeyInventoryItem with lifecycle timestamps. Used by the KeyStateWorker for time-based state transitions.

type DnssecKeys

type DnssecKeys struct {
	KSKs []*PrivateKeyCache
	ZSKs []*PrivateKeyCache
}

type DnssecPolicy

type DnssecPolicy struct {
	Name      string
	Algorithm uint8

	KSK KeyLifetime
	ZSK KeyLifetime
	CSK KeyLifetime
}

DnssecPolicy is what is actually used; it is created from the corresponding DnssecPolicyConf

type DnssecPolicyConf

type DnssecPolicyConf struct {
	Name      string
	Algorithm string

	KSK struct {
		Lifetime    string
		SigValidity string
	}
	ZSK struct {
		Lifetime    string
		SigValidity string
	}
	CSK struct {
		Lifetime    string
		SigValidity string
	}
}

DnssecPolicyConf should match the configuration

type DsyncResult

type DsyncResult struct {
	Qname  string
	Rdata  []*core.DSYNC
	Parent string
	Error  error
}

type DsyncSchemeInfo

type DsyncSchemeInfo struct {
	Scheme string `json:"scheme"` // "UPDATE", "NOTIFY", etc.
	Type   string `json:"type"`   // "CDS", "CSYNC", "ANY", etc.
	Target string `json:"target"` // target host
	Port   uint16 `json:"port"`
}

DsyncSchemeInfo describes a parent DSYNC sync scheme.

type DsyncTarget

type DsyncTarget struct {
	Name      string
	Scheme    core.DsyncScheme
	Port      uint16
	Addresses []string // in addr:port format
	RR        *core.DSYNC
}

type DynamicCatalogMemberConf

type DynamicCatalogMemberConf struct {
	Allowed bool   `yaml:"allowed" mapstructure:"allowed"`                                              // Whether catalog member zones are allowed
	Storage string `yaml:"storage" mapstructure:"storage" validate:"omitempty,oneof=memory persistent"` // "memory" or "persistent"
	Add     string `yaml:"add" mapstructure:"add" validate:"omitempty,oneof=auto manual"`               // "auto" or "manual" - Enable auto-configuration from catalog
	Remove  string `yaml:"remove" mapstructure:"remove" validate:"omitempty,oneof=auto manual"`         // "auto" or "manual" - Whether to remove zones when deleted from catalog
}

DynamicCatalogMemberConf defines configuration for catalog member zones (includes add/remove policy)

type DynamicConfigFile

type DynamicConfigFile struct {
	Zones []ZoneConf `yaml:"zones"`
}

DynamicConfigFile represents the structure of the dynamic zones config file

type DynamicZoneTypeConf

type DynamicZoneTypeConf struct {
	Allowed bool   `yaml:"allowed" mapstructure:"allowed"`                                              // Whether this type of zone is allowed
	Storage string `yaml:"storage" mapstructure:"storage" validate:"omitempty,oneof=memory persistent"` // "memory" or "persistent"
}

DynamicZoneTypeConf defines configuration for a type of dynamic zone

type DynamicZonesConf

type DynamicZonesConf struct {
	ConfigFile     string                   `yaml:"configfile" mapstructure:"configfile"`           // Absolute path to dynamic config file
	ZoneDirectory  string                   `yaml:"zonedirectory" mapstructure:"zonedirectory"`     // Absolute path to zone file directory
	CatalogZones   DynamicZoneTypeConf      `yaml:"catalog_zones" mapstructure:"catalog_zones"`     // Configuration for catalog zones
	CatalogMembers DynamicCatalogMemberConf `yaml:"catalog_members" mapstructure:"catalog_members"` // Configuration for catalog member zones
	Dynamic        DynamicZoneTypeConf      `yaml:"dynamic" mapstructure:"dynamic"`                 // Configuration for direct API-created zones (future)
}

DynamicZonesConf defines configuration for dynamically created zones (catalog zones, catalog members, etc.)

type EditsResponseMsg

type EditsResponseMsg struct {
	SenderID     string
	Zone         string
	AgentRecords map[string]map[string][]string // agentID → owner → []RR strings
}

EditsResponseMsg carries an agent's contributions from combiner back to the agent. Delivered via MsgQs.EditsResponse channel. Modeled on KeystateInventoryMsg.

type EngineFunc

type EngineFunc func(ctx context.Context) error

EngineFunc is the function signature for registered engines. Engines are long-running goroutines that run until the context is cancelled. They should return nil when the context is cancelled, or an error if they fail.

type EngineRegistration

type EngineRegistration struct {
	Name   string
	Engine EngineFunc
}

EngineRegistration stores an engine registration

type ErrorJournal

type ErrorJournal struct {
	// contains filtered or unexported fields
}

ErrorJournal is a bounded, time-windowed in-memory ring buffer of errors. Thread-safe. No persistence — this is for operational debugging.

func NewErrorJournal

func NewErrorJournal(maxCount int, maxAge time.Duration) *ErrorJournal

NewErrorJournal creates a new error journal with the given retention limits.

func (*ErrorJournal) ListSince

func (ej *ErrorJournal) ListSince(duration time.Duration) []ErrorJournalEntry

ListSince returns all errors within the given duration from now.

func (*ErrorJournal) LookupByDistID

func (ej *ErrorJournal) LookupByDistID(distID string) (*ErrorJournalEntry, bool)

LookupByDistID returns the error entry for a specific distribution ID, if any.

func (*ErrorJournal) Record

func (ej *ErrorJournal) Record(entry ErrorJournalEntry)

Record adds a new error entry to the journal, evicting old entries as needed.

type ErrorJournalEntry

type ErrorJournalEntry struct {
	DistributionID string    `json:"distribution_id"`
	Sender         string    `json:"sender"`       // Sender identity (extracted from QNAME control zone)
	MessageType    string    `json:"message_type"` // "ping", "beat", "sync", "update", or "unknown"
	ErrorMsg       string    `json:"error_msg"`
	QNAME          string    `json:"qname"` // Original NOTIFY qname
	Timestamp      time.Time `json:"timestamp"`
}

ErrorJournalEntry records a single error that occurred during CHUNK NOTIFY processing.

type ErrorType

type ErrorType uint8
const (
	NoError ErrorType = iota
	ConfigError
	RefreshError
	AgentError
	DnssecError
)

type GlobalStuff

type GlobalStuff struct {
	// IMR         string  // trying to get rid of this, use Imr instead
	ImrEngine   *Imr
	Verbose     bool
	Debug       bool
	Zonename    string
	AgentId     AgentId
	ParentZone  string
	Sig0Keyfile string
	Api         *ApiClient
	ApiClients  map[string]*ApiClient // tdns-cli has multiple clients
	PingCount   int
	Slurp       bool
	Algorithm   string
	Rrtype      string
	ShowHeaders bool // -H in various CLI commands
	BaseUri     string
	Port        uint16
	Address     string
	App         AppDetails
	ServerSVCB  *dns.SVCB // ALPN for DoH/DoQ
	TsigKeys    map[string]*TsigDetails
}

func (*GlobalStuff) Validate

func (gs *GlobalStuff) Validate() error

type GossipMessage

type GossipMessage struct {
	GroupHash string                  `json:"group_hash"`
	GroupName GroupNameProposal       `json:"group_name"`
	Members   map[string]*MemberState `json:"members"` // key: provider identity
	Election  GroupElectionState      `json:"election"`
}

GossipMessage carries gossip state for one provider group. Included in beats between agents that share group membership.

type GossipStateTable

type GossipStateTable struct {

	// key: group hash → member identity → MemberState
	States map[string]map[string]*MemberState
	// key: group hash → GroupElectionState
	Elections map[string]*GroupElectionState
	// key: group hash → GroupNameProposal (best proposal seen)
	Names map[string]*GroupNameProposal
	// Our identity
	LocalID string
	// contains filtered or unexported fields
}

GossipStateTable manages the NxN state matrix for all provider groups. Each entry is a MemberState keyed by (groupHash, memberIdentity).

func NewGossipStateTable

func NewGossipStateTable(localID string) *GossipStateTable

NewGossipStateTable creates a new gossip state table.

func (*GossipStateTable) BuildGossipForPeer

func (gst *GossipStateTable) BuildGossipForPeer(peerID string, pgm *ProviderGroupManager, lem ...*LeaderElectionManager) []GossipMessage

BuildGossipForPeer builds gossip messages for all groups shared with a peer. If a LeaderElectionManager is provided, group election state is included.

func (*GossipStateTable) CheckGroupState

func (gst *GossipStateTable) CheckGroupState(groupHash string, expectedMembers []string)

CheckGroupState checks if all cells in the NxN matrix for a group are OPERATIONAL. Fires OnGroupOperational when the group first reaches full agreement. Fires OnGroupDegraded when a previously operational group loses agreement.

func (*GossipStateTable) GetGroupState

func (gst *GossipStateTable) GetGroupState(groupHash string) (map[string]*MemberState, *GroupElectionState, *GroupNameProposal)

GetGroupState returns a deep copy of the state matrix for a group.

func (*GossipStateTable) MergeGossip

func (gst *GossipStateTable) MergeGossip(msg *GossipMessage)

MergeGossip merges received gossip into the local state table. For each member's state entry, keep the one with the latest timestamp. The member's PeerStates map is replaced atomically (never cherry-pick individual peer entries from different timestamps).

func (*GossipStateTable) RefreshLocalStates

func (gst *GossipStateTable) RefreshLocalStates(ar *AgentRegistry, pgm *ProviderGroupManager)

RefreshLocalStates updates our local state entries for all groups based on current agent registry state.

func (*GossipStateTable) SetOnElectionUpdate

func (gst *GossipStateTable) SetOnElectionUpdate(fn func(groupHash string, state GroupElectionState))

func (*GossipStateTable) SetOnGroupDegraded

func (gst *GossipStateTable) SetOnGroupDegraded(fn func(groupHash string))

func (*GossipStateTable) SetOnGroupOperational

func (gst *GossipStateTable) SetOnGroupOperational(fn func(groupHash string))

func (*GossipStateTable) UpdateLocalState

func (gst *GossipStateTable) UpdateLocalState(groupHash string, peerStates map[string]string, zones []string)

UpdateLocalState updates our own state entry for a group. This sets the Timestamp to now — only we update our own state.

type GroupElectionState

type GroupElectionState struct {
	Leader       string    `json:"leader,omitempty"` // identity of current leader
	Term         uint32    `json:"term,omitempty"`
	LeaderExpiry time.Time `json:"leader_expiry,omitempty"`
}

GroupElectionState carries election state for a provider group.

type GroupNameProposal

type GroupNameProposal struct {
	GroupHash  string    `json:"group_hash"`
	Name       string    `json:"name"`
	Proposer   string    `json:"proposer"`    // provider identity that chose the name
	ProposedAt time.Time `json:"proposed_at"` // when the name was chosen
}

GroupNameProposal is a name proposed by a provider for a group.

type GroupPrefixesConf

type GroupPrefixesConf struct {
	Config  string `yaml:"config" mapstructure:"config" validate:"required"`   // Prefix for config/transfer groups, or "none" to disable
	Signing string `yaml:"signing" mapstructure:"signing" validate:"required"` // Prefix for signing groups, or "none" to disable
}

GroupPrefixesConf defines prefixes that identify special group types in catalog zones

type HsyncAgentStatus

type HsyncAgentStatus struct {
	Identity    string
	LastContact time.Time
	State       string   // "discovered", "contact_attempted", "connected", "failed"
	LastError   string   // If state is "failed"
	Endpoints   []string // Discovered API/DNS endpoints
}

HsyncAgentStatus represents the current status of a remote agent

type HsyncConfirmationInfo

type HsyncConfirmationInfo struct {
	DistributionID string    `json:"distribution_id"`
	ConfirmerID    string    `json:"confirmer_id"`
	Status         string    `json:"status"`
	Message        string    `json:"message,omitempty"`
	ConfirmedAt    time.Time `json:"confirmed_at"`
	ReceivedAt     time.Time `json:"received_at"`
}

HsyncConfirmationInfo contains confirmation information for CLI display

func ConfirmRecordToInfo

func ConfirmRecordToInfo(conf *SyncConfirmationRecord) *HsyncConfirmationInfo

ConfirmRecordToInfo converts a SyncConfirmationRecord to HsyncConfirmationInfo for CLI display.

type HsyncMetricsInfo

type HsyncMetricsInfo struct {
	SyncsSent      int64 `json:"syncs_sent"`
	SyncsReceived  int64 `json:"syncs_received"`
	SyncsConfirmed int64 `json:"syncs_confirmed"`
	SyncsFailed    int64 `json:"syncs_failed"`
	BeatsSent      int64 `json:"beats_sent"`
	BeatsReceived  int64 `json:"beats_received"`
	BeatsMissed    int64 `json:"beats_missed"`
	AvgLatency     int64 `json:"avg_latency"`
	MaxLatency     int64 `json:"max_latency"`
	APIOperations  int64 `json:"api_operations"`
	DNSOperations  int64 `json:"dns_operations"`
}

HsyncMetricsInfo contains aggregated metrics for CLI display

type HsyncPeerInfo

type HsyncPeerInfo struct {
	PeerID             string    `json:"peer_id"`
	State              string    `json:"state"`
	StateReason        string    `json:"state_reason,omitempty"`
	DiscoverySource    string    `json:"discovery_source,omitempty"`
	DiscoveryTime      time.Time `json:"discovery_time,omitempty"`
	PreferredTransport string    `json:"preferred_transport"`
	APIHost            string    `json:"api_host,omitempty"`
	APIPort            int       `json:"api_port,omitempty"`
	APIAvailable       bool      `json:"api_available"`
	DNSHost            string    `json:"dns_host,omitempty"`
	DNSPort            int       `json:"dns_port,omitempty"`
	DNSAvailable       bool      `json:"dns_available"`
	LastContactAt      time.Time `json:"last_contact_at,omitempty"`
	LastHelloAt        time.Time `json:"last_hello_at,omitempty"`
	LastBeatAt         time.Time `json:"last_beat_at,omitempty"`
	BeatInterval       int       `json:"beat_interval"`
	BeatsSent          int64     `json:"beats_sent"`
	BeatsReceived      int64     `json:"beats_received"`
	FailedContacts     int       `json:"failed_contacts"`
}

HsyncPeerInfo contains peer information for CLI display

func PeerRecordToInfo

func PeerRecordToInfo(peer *PeerRecord) *HsyncPeerInfo

PeerRecordToInfo converts a PeerRecord to HsyncPeerInfo for CLI display.

type HsyncStatus

type HsyncStatus struct {
	Time         time.Time
	ZoneName     string
	Command      string
	Status       bool
	Error        bool
	ErrorMsg     string
	Msg          string
	HsyncAdds    []dns.RR // Changed from Adds
	HsyncRemoves []dns.RR // Changed from Removes
}

type HsyncSyncOpInfo

type HsyncSyncOpInfo struct {
	DistributionID string    `json:"distribution_id"`
	ZoneName       string    `json:"zone_name"`
	SyncType       string    `json:"sync_type"`
	Direction      string    `json:"direction"`
	SenderID       string    `json:"sender_id"`
	ReceiverID     string    `json:"receiver_id"`
	Status         string    `json:"status"`
	StatusMessage  string    `json:"status_message,omitempty"`
	Transport      string    `json:"transport,omitempty"`
	CreatedAt      time.Time `json:"created_at"`
	SentAt         time.Time `json:"sent_at,omitempty"`
	ReceivedAt     time.Time `json:"received_at,omitempty"`
	ConfirmedAt    time.Time `json:"confirmed_at,omitempty"`
	RetryCount     int       `json:"retry_count"`
}

HsyncSyncOpInfo contains sync operation information for CLI display

func SyncOpRecordToInfo

func SyncOpRecordToInfo(op *SyncOperationRecord) *HsyncSyncOpInfo

SyncOpRecordToInfo converts a SyncOperationRecord to HsyncSyncOpInfo for CLI display.

type HsyncTransportEvent

type HsyncTransportEvent struct {
	EventTime    time.Time `json:"event_time"`
	PeerID       string    `json:"peer_id,omitempty"`
	ZoneName     string    `json:"zone_name,omitempty"`
	EventType    string    `json:"event_type"`
	Transport    string    `json:"transport,omitempty"`
	Direction    string    `json:"direction,omitempty"`
	Success      bool      `json:"success"`
	ErrorCode    string    `json:"error_code,omitempty"`
	ErrorMessage string    `json:"error_message,omitempty"`
}

HsyncTransportEvent contains transport event information for CLI display

type Imr

type Imr struct {
	Cache       *cache.RRsetCacheT
	DnskeyCache *cache.DnskeyCacheT
	Options     map[ImrOption]string
	LineWidth   int // used to truncate long lines in logging and output (eg. DNSKEYs and RRSIGs)
	Verbose     bool
	Debug       bool
	Quiet       bool        // if true, suppress informational logging (useful for CLI tools)
	DebugLog    *log.Logger // non-nil when imr debug logging is enabled
	// RequireDnssecValidation: when true, security-sensitive lookups (TLSA, etc.) require
	// a secure DNSSEC validation state. Default true; set false in lab environments where
	// the full DNSSEC chain is not yet established.
	RequireDnssecValidation bool
}

func (*Imr) AuthDNSQuery

func (imr *Imr) AuthDNSQuery(ctx context.Context, qname string, qtype uint16, nameservers []string,
	lg *log.Logger, verbose bool) (*core.RRset, int, cache.CacheContext, error)

func (*Imr) CollectNSAddresses

func (imr *Imr) CollectNSAddresses(ctx context.Context, rrset *core.RRset, respch chan *ImrResponse) error

CollectNSAddresses - given an NS RRset, chase down the A and AAAA records corresponding to each nsname

func (*Imr) DefaultDNSKEYFetcher

func (imr *Imr) DefaultDNSKEYFetcher(ctx context.Context, name string) (*core.RRset, error)

func (*Imr) DefaultRRsetFetcher

func (imr *Imr) DefaultRRsetFetcher(ctx context.Context, qname string, qtype uint16) (*core.RRset, error)

func (*Imr) DsyncDiscovery

func (imr *Imr) DsyncDiscovery(ctx context.Context, child string, verbose bool) (DsyncResult, error)

func (*Imr) ImrQuery

func (imr *Imr) ImrQuery(ctx context.Context, qname string, qtype uint16, qclass uint16, respch chan *ImrResponse) (*ImrResponse, error)

func (*Imr) ImrResponder

func (imr *Imr) ImrResponder(ctx context.Context, w dns.ResponseWriter, r *dns.Msg, qname string, qtype uint16, msgoptions *edns0.MsgOptions)

func (*Imr) IterativeDNSQuery

func (imr *Imr) IterativeDNSQuery(ctx context.Context, qname string, qtype uint16, serverMap map[string]*cache.AuthServer, force bool, requireEncrypted bool) (*core.RRset, int, cache.CacheContext, core.Transport, error)

force is true if we should force a lookup even if the answer is in the cache visitedZones tracks which zones we've been referred to for this qname to prevent referral loops requireEncrypted is true if PR flag is set and only encrypted transports should be used Returns: rrset, rcode, context, transport, error

func (*Imr) IterativeDNSQueryFetcher

func (imr *Imr) IterativeDNSQueryFetcher() cache.RRsetFetcher

IterativeDNSQueryFetcher adapts IterativeDNSQuery to the RRsetFetcher interface. It discards the rcode and CacheContext return values, only returning the RRset and error.

func (*Imr) IterativeDNSQueryWithLoopDetection

func (imr *Imr) IterativeDNSQueryWithLoopDetection(ctx context.Context, qname string, qtype uint16, serverMap map[string]*cache.AuthServer, force bool, visitedZones map[string]bool, requireEncrypted bool) (*core.RRset, int, cache.CacheContext, core.Transport, error)

IterativeDNSQueryWithLoopDetection is the internal implementation with loop detection visitedZones tracks which zones we've been referred to for this qname (format: "qname:zone") requireEncrypted is true if PR flag is set and only encrypted transports should be used Returns: rrset, rcode, context, transport, error

func (*Imr) LookupDSYNCTarget

func (imr *Imr) LookupDSYNCTarget(ctx context.Context, childzone string, dtype uint16, scheme core.DsyncScheme) (*DsyncTarget, error)

dtype = the type of DSYNC RR to look for (dns.TypeCDS, dns.TypeCSYNC, dns.TypeANY, ...) scheme = the DSYNC scheme (SchemeNotify | SchemeUpdate)

func (*Imr) ParentZone

func (imr *Imr) ParentZone(z string) (string, error)

func (*Imr) ParseAdditionalForNSAddrs

func (imr *Imr) ParseAdditionalForNSAddrs(ctx context.Context, src string, nsrrset *core.RRset, zonename string,
	nsMap map[string]bool, r *dns.Msg) (map[string]*cache.AuthServer, error)

func (*Imr) ProcessAuthDNSResponse

func (imr *Imr) ProcessAuthDNSResponse(ctx context.Context, qname string, qtype uint16, rrset *core.RRset, rcode int, context cache.CacheContext, msgoptions *edns0.MsgOptions, m *dns.Msg, w dns.ResponseWriter, r *dns.Msg, transport core.Transport) (bool, error)

returns true if we have a response (i.e. we're done), false if we have an error all errors are treated as "done"

func (*Imr) SendRfc9567ErrorReport

func (imr *Imr) SendRfc9567ErrorReport(ctx context.Context, qname string, qtype uint16, ede_code uint16, msgoptions *edns0.MsgOptions) error

func (*Imr) StartImrEngineListeners

func (imr *Imr) StartImrEngineListeners(ctx context.Context, conf *Config) error

func (*Imr) TransportSignalCached

func (imr *Imr) TransportSignalCached(owner string) bool

func (*Imr) TransportSignalRRType

func (imr *Imr) TransportSignalRRType() uint16

type ImrClientQueryHookFunc

type ImrClientQueryHookFunc func(ctx context.Context, w dns.ResponseWriter,
	r *dns.Msg, qname string, qtype uint16,
	msgoptions *edns0.MsgOptions) (context.Context, *dns.Msg)

ImrClientQueryHookFunc is called when an external client query arrives at the IMR listener. Return nil ctx to keep the original context, or a new context to enrich it (e.g. to carry a parent query ID through the resolution chain). Return nil *dns.Msg to proceed with normal resolution. Return a non-nil *dns.Msg to short-circuit: the msg is sent as the response and resolution is skipped.

type ImrEngineConf

type ImrEngineConf struct {
	Active      *bool                `yaml:"active" mapstructure:"active"`         // If nil or true, IMR is active. Only false explicitly disables it.
	RootHints   string               `yaml:"root-hints" mapstructure:"root-hints"` // Path to root hints file. If empty, uses compiled-in hints.
	Addresses   []string             `yaml:"addresses" mapstructure:"addresses" validate:"required"`
	CertFile    string               `yaml:"certfile" mapstructure:"certfile"`
	KeyFile     string               `yaml:"keyfile" mapstructure:"keyfile"`
	Transports  []string             `yaml:"transports" mapstructure:"transports" validate:"required"` // "do53", "dot", "doh", "doq"
	Stubs       []ImrStubConf        `yaml:"stubs"`
	OptionsStrs []string             `yaml:"options" mapstructure:"options"`
	Options     map[ImrOption]string `yaml:"-" mapstructure:"-"`
	// Trust anchors for recursive validation. Provide either DS or DNSKEY as
	// full RR text (zonefile format). DS is preferred as it is more convenient.
	TrustAnchorDS     string `yaml:"trust_anchor_ds"`
	TrustAnchorDNSKEY string `yaml:"trust_anchor_dnskey"`
	// Unbound-style file with one RR per line (DNSKEY and/or DS). Absolute path.
	TrustAnchorFile string `yaml:"trust-anchor-file"`
	Verbose         bool
	Debug           bool
	Logging         ImrLoggingConf `yaml:"logging" mapstructure:"logging"`
	// RequireDnssecValidation: when true (default), TLSA and other security-sensitive
	// records must have a secure DNSSEC validation state. Set to false to allow
	// indeterminate/insecure records during lab/development when the full DNSSEC
	// chain is not yet established.
	RequireDnssecValidation *bool `yaml:"require_dnssec_validation" mapstructure:"require_dnssec_validation"`
}

type ImrLoggingConf

type ImrLoggingConf struct {
	Enabled bool   `yaml:"enabled" mapstructure:"enabled"`
	File    string `yaml:"file" mapstructure:"file"`
}

type ImrOption

type ImrOption uint8
const (
	ImrOptRevalidateNS ImrOption = iota + 1
	ImrOptQueryForTransport
	ImrOptAlwaysQueryForTransport
	ImrOptTransportSignalType
	ImrOptQueryForTransportTLSA
	ImrOptUseTransportSignals
)

type ImrOutboundQueryHookFunc

type ImrOutboundQueryHookFunc func(ctx context.Context, qname string,
	qtype uint16, serverName string, serverAddr string,
	transport core.Transport) error

ImrOutboundQueryHookFunc is called before the IMR sends an iterative query to an authoritative server. Return nil to proceed with the query. Return a non-nil error to skip this server (behaves as if the server didn't respond).

type ImrRequest

type ImrRequest struct {
	Qname      string
	Qtype      uint16
	Qclass     uint16
	ResponseCh chan ImrResponse
}

type ImrResponse

type ImrResponse struct {
	RRset     *core.RRset
	Validated bool
	Error     bool
	ErrorMsg  string
	Msg       string
}

type ImrResponseHookFunc

type ImrResponseHookFunc func(ctx context.Context, qname string, qtype uint16,
	serverName string, serverAddr string, transport core.Transport,
	response *dns.Msg, rcode int)

ImrResponseHookFunc is called after the IMR receives a response from an authoritative server. Observe-only — return value is ignored.

type ImrStubConf

type ImrStubConf struct {
	Zone string `validate:"required"`
	// Servers []StubServerConf `validate:"required"`
	Servers []cache.AuthServer `validate:"required"`
}

type InternalConf

type InternalConf struct {
	InternalDnsConf
	InternalMpConf
}

InternalConf embeds both DNS and MP internal configuration. Field promotion means all existing access sites continue to work unchanged.

type InternalDnsConf

type InternalDnsConf struct {
	CfgFile             string //
	DebugMode           bool   // if true, may activate dangerous tests
	ZonesCfgFile        string //
	CertData            string // PEM encoded certificate
	KeyData             string // PEM encoded key
	KeyDB               *KeyDB
	AllZones            []string
	DnssecPolicies      map[string]DnssecPolicy
	StopCh              chan struct{}
	APIStopCh           chan struct{}
	StopOnce            sync.Once
	RefreshZoneCh       chan ZoneRefresher
	BumpZoneCh          chan BumperData
	ValidatorCh         chan ValidatorRequest
	RecursorCh          chan ImrRequest
	ScannerQ            chan ScanRequest
	UpdateQ             chan UpdateRequest
	DeferredUpdateQ     chan DeferredUpdate
	DnsUpdateQ          chan DnsUpdateRequest
	DnsNotifyQ          chan DnsNotifyRequest
	DnsQueryQ           chan DnsQueryRequest           // Optional: if nil, queries use direct call to QueryResponder
	QueryHandlers       map[uint16][]QueryHandlerFunc  // qtype -> list of handlers (registered via RegisterQueryHandler)
	QueryHandlersMutex  sync.RWMutex                   // protects QueryHandlers map
	NotifyHandlers      map[uint16][]NotifyHandlerFunc // qtype -> list of handlers (registered via RegisterNotifyHandler, 0 = all NOTIFYs)
	NotifyHandlersMutex sync.RWMutex                   // protects NotifyHandlers map
	UpdateHandlers      []UpdateHandlerRegistration    // UPDATE handlers (registered via RegisterUpdateHandler)
	UpdateHandlersMutex sync.RWMutex                   // protects UpdateHandlers slice
	DelegationSyncQ     chan DelegationSyncRequest
	MusicSyncQ          chan MusicSyncRequest
	NotifyQ             chan NotifyRequest
	AuthQueryQ          chan AuthQueryRequest
	ResignQ             chan *ZoneData     // the names of zones that should be kept re-signed should be sent into this channel
	RRsetCache          *cache.RRsetCacheT // ConcurrentMap of cached RRsets from queries
	ImrEngine           *Imr
	Scanner             *Scanner // Scanner instance for async job tracking
}

InternalDnsConf holds DNS-specific internal state: channels, handlers, caches, and engine references. Stays in tdns after repo split.

type InternalMpConf

type InternalMpConf struct {
	SyncQ                 chan SyncRequest
	MsgQs                 *MsgQs // aggregated channels for agent communication
	SyncStatusQ           chan SyncStatus
	AgentRegistry         *AgentRegistry
	ZoneDataRepo          *ZoneDataRepo
	CombinerState         *CombinerState              // Combiner business logic state (error journal, protected namespaces)
	TransportManager      *transport.TransportManager // Generic transport (Router, PeerRegistry, DNS/API transports, RMQ)
	MPTransport           *MPTransportBridge          // MP-specific transport bridge (authorization, discovery, enqueue, DNSKEY tracking)
	LeaderElectionManager *LeaderElectionManager      // Per-zone leader election for delegation sync; nil if not agent
	ChunkPayloadStore     ChunkPayloadStore           // Optional: for query-mode CHUNK (agent); keyed by qname; set when agent chunk_mode is "query"
	MPZoneNames           []string                    // Zone names with OptMultiProvider, collected at parse time for SDE hydration
	DistributionCache     *DistributionCache          // In-memory cache of distributions (agent/combiner)
	KdcDB                 interface{}                 // *kdc.KdcDB - using interface{} to avoid circular import
	KdcConf               interface{}                 // *kdc.KdcConf - using interface{} to avoid circular import
	KrsDB                 interface{}                 // *krs.KrsDB - using interface{} to avoid circular import
	KrsConf               interface{}                 // *krs.KrsConf - using interface{} to avoid circular import
}

InternalMpConf holds multi-provider internal state: transport, agent registry, combiner state, message channels. Moves to tdns-mp after repo split.

type Ixfr

type Ixfr struct {
	FromSerial uint32
	ToSerial   uint32
	Removed    []core.RRset
	Added      []core.RRset
}

type KaspConf

type KaspConf struct {
	// PropagationDelay is how long to wait for DNSKEY RRsets to propagate
	// through all caches before allowing state transitions.
	// Used for published→standby and retired→removed transitions.
	// Accepts Go duration strings: "1h", "3600s", "90m".
	// Default: "1h".
	PropagationDelay string `yaml:"propagation_delay" mapstructure:"propagation_delay"`

	// StandbyZskCount is the number of standby ZSKs to maintain per zone.
	// When the count drops below this, the KeyStateWorker generates new ZSKs.
	// Default: 1. A value of 0 (or omitted) means use the default.
	StandbyZskCount int `yaml:"standby_zsk_count" mapstructure:"standby_zsk_count"`

	// StandbyKskCount is the number of standby KSKs to maintain per zone.
	// When the count drops below this, the KeyStateWorker generates new KSKs.
	// Default: 0 (no standby KSKs). Set to 1+ to enable standby KSK maintenance.
	StandbyKskCount int `yaml:"standby_ksk_count" mapstructure:"standby_ksk_count"`

	// CheckInterval is how often the KeyStateWorker runs its checks.
	// Accepts Go duration strings: "1m", "60s", "5m".
	// Default: "1m".
	CheckInterval string `yaml:"check_interval" mapstructure:"check_interval"`
}

KaspConf holds Key and Signing Policy parameters for the signer. Controls the KeyStateWorker's automatic key state transitions and standby key maintenance. YAML key: "kasp:"

Example:

kasp:
    propagation_delay: 1h
    standby_zsk_count: 1
    standby_ksk_count: 0
    check_interval: 1m

type KeyBootstrapperRequest

type KeyBootstrapperRequest struct {
	Cmd          string
	KeyName      string
	ZoneName     string
	ZoneData     *ZoneData
	Key          string
	Verified     bool
	Keyid        uint16
	Algorithm    uint8
	Imr          *Imr
	ResponseChan chan *VerificationInfo
}

type KeyConf

type KeyConf struct {
	Tsig []TsigDetails
}

type KeyDB

type KeyDB struct {
	DB *sql.DB

	// Sig0Cache   map[string]*Sig0KeyCache
	KeystoreSig0Cache   map[string]*Sig0ActiveKeys
	TruststoreSig0Cache *Sig0StoreT            // was *Sig0StoreT
	KeystoreDnskeyCache map[string]*DnssecKeys // map[zonename]*DnssecActiveKeys
	Ctx                 string
	UpdateQ             chan UpdateRequest
	DeferredUpdateQ     chan DeferredUpdate
	KeyBootstrapperQ    chan KeyBootstrapperRequest
	Options             map[AuthOption]string
	// contains filtered or unexported fields
}

func NewKeyDB

func NewKeyDB(dbfile string, force bool, options map[AuthOption]string) (*KeyDB, error)

NewKeyDB creates and initializes a KeyDB backed by the sqlite3 file at dbfile. It validates that dbfile is provided, ensures the file is writable, opens the sqlite3 database, and sets up required tables. If force is true, existing default tables are dropped before setup. On success it returns a KeyDB with caches, an update channel, and Options set to the provided map; on failure it returns an error describing the problem.

func (*KeyDB) APIkeystore

func (kdb *KeyDB) APIkeystore(conf *Config) func(w http.ResponseWriter, r *http.Request)

func (*KeyDB) APItruststore

func (kdb *KeyDB) APItruststore() func(w http.ResponseWriter, r *http.Request)

func (*KeyDB) ApplyChildUpdateToDB

func (kdb *KeyDB) ApplyChildUpdateToDB(ur UpdateRequest) error

func (*KeyDB) ApplyZoneUpdateToDB

func (kdb *KeyDB) ApplyZoneUpdateToDB(ur UpdateRequest) error

func (*KeyDB) ApprovePendingEdit

func (kdb *KeyDB) ApprovePendingEdit(editID int) (*PendingEditRecord, error)

ApprovePendingEdit moves a pending edit to the approved table. Returns the original pending edit data for processing.

func (*KeyDB) Begin

func (db *KeyDB) Begin(context string) (*Tx, error)

func (*KeyDB) CleanupExpiredHsyncData

func (kdb *KeyDB) CleanupExpiredHsyncData() error

CleanupExpiredData removes expired data from HSYNC tables. Should be called periodically (e.g., daily).

func (*KeyDB) ClearApprovedEdits

func (kdb *KeyDB) ClearApprovedEdits(zone string) (int64, error)

ClearApprovedEdits deletes rows from CombinerApprovedEdits. If zone is empty, all rows are deleted; otherwise only rows for that zone.

func (*KeyDB) ClearContributions

func (kdb *KeyDB) ClearContributions(zone string) (int64, error)

ClearContributions deletes rows from CombinerContributions. If zone is empty, all rows are deleted; otherwise only rows for that zone.

func (*KeyDB) ClearPendingEdits

func (kdb *KeyDB) ClearPendingEdits(zone string) (int64, error)

ClearPendingEdits deletes rows from CombinerPendingEdits. If zone is empty, all rows are deleted; otherwise only rows for that zone.

func (*KeyDB) ClearRejectedEdits

func (kdb *KeyDB) ClearRejectedEdits(zone string) (int64, error)

ClearRejectedEdits deletes rows from CombinerRejectedEdits. If zone is empty, all rows are deleted; otherwise only rows for that zone.

func (*KeyDB) Close

func (db *KeyDB) Close() error

func (*KeyDB) CreateAutoZone

func (kdb *KeyDB) CreateAutoZone(zonename string, addrs []string, nsNames []string) (*ZoneData, error)

func (*KeyDB) DeferredUpdaterEngine

func (kdb *KeyDB) DeferredUpdaterEngine(ctx context.Context) error

func (*KeyDB) DelegationSyncher

func (kdb *KeyDB) DelegationSyncher(ctx context.Context, delsyncq chan DelegationSyncRequest, notifyq chan NotifyRequest, conf *Config) error

func (*KeyDB) DeleteContributions

func (kdb *KeyDB) DeleteContributions(zone, senderID string) error

DeleteContributions removes all rows for a specific agent/zone pair.

func (*KeyDB) DeletePublishInstruction

func (kdb *KeyDB) DeletePublishInstruction(zone, senderID string) error

DeletePublishInstruction removes the stored instruction for (zone, senderID).

func (*KeyDB) DnssecKeyMgmt

func (kdb *KeyDB) DnssecKeyMgmt(tx *Tx, kp KeystorePost) (*KeystoreResponse, error)

func (*KeyDB) Exec

func (db *KeyDB) Exec(query string, args ...interface{}) (sql.Result, error)

func (*KeyDB) GenerateAndStageKey

func (kdb *KeyDB) GenerateAndStageKey(zone, creator string, alg uint8, keytype string, isMultiProvider bool) (uint16, error)

GenerateAndStageKey generates a new DNSSEC key and stages it for the key pipeline. For multi-provider zones (isMultiProvider=true): created → mpdist (awaits remote confirmation) For normal zones: created → published (sets published_at, enters time-based pipeline)

func (*KeyDB) GenerateKeypair

func (kdb *KeyDB) GenerateKeypair(owner, creator, state string, rrtype uint16, alg uint8, keytype string, tx *Tx) (*PrivateKeyCache, string, error)

XXX: FIXME: This is not yet ready to generate DNSSEC keys, because in the DNSSEC case we also need the

flags field, which is not yet set here.

func (*KeyDB) GetAggregatedMetrics

func (kdb *KeyDB) GetAggregatedMetrics() (*HsyncMetricsInfo, error)

GetAggregatedMetrics retrieves aggregated operational metrics.

func (*KeyDB) GetDnssecKeyPropagation

func (kdb *KeyDB) GetDnssecKeyPropagation(zonename string, keyid uint16) (bool, time.Time, error)

GetDnssecKeyPropagation returns the propagation state for a specific key. Returns (confirmed bool, confirmedAt time.Time, err error).

func (*KeyDB) GetDnssecKeys

func (kdb *KeyDB) GetDnssecKeys(zonename, state string) (*DnssecKeys, error)

func (*KeyDB) GetDnssecKeysByState

func (kdb *KeyDB) GetDnssecKeysByState(zone string, state string) ([]DnssecKeyWithTimestamps, error)

GetDnssecKeysByState returns all DNSSEC keys in a given state, with lifecycle timestamps. If zone is empty, returns keys across all zones.

func (*KeyDB) GetKeyInventory

func (kdb *KeyDB) GetKeyInventory(zonename string) ([]KeyInventoryItem, error)

GetKeyInventory returns the complete DNSKEY inventory for a zone — all keys across all states. Used by the signer to respond to RFI KEYSTATE requests. Returns lightweight entries (keytag, algorithm, flags, state, keyrr) without private keys.

func (*KeyDB) GetKeyStatus

func (kdb *KeyDB) GetKeyStatus(zonename string, keyID uint16) (*edns0.KeyStateOption, error)

func (*KeyDB) GetPeer

func (kdb *KeyDB) GetPeer(peerID string) (*PeerRecord, error)

GetPeer retrieves a peer by ID.

func (*KeyDB) GetPendingEdit

func (kdb *KeyDB) GetPendingEdit(editID int) (*PendingEditRecord, error)

GetPendingEdit retrieves a single pending edit by edit_id.

func (*KeyDB) GetPublishInstruction

func (kdb *KeyDB) GetPublishInstruction(zone, senderID string) (*StoredPublishInstruction, error)

GetPublishInstruction returns the stored instruction for (zone, senderID), or nil.

func (*KeyDB) GetSig0KeyRaw

func (kdb *KeyDB) GetSig0KeyRaw(zonename, state string) (algorithm, privatekey, keyrr string, found bool, err error)

GetSig0KeyRaw returns the raw database column values for a SIG(0) key. Used for transferring key material between agents via RFI CONFIG.

func (*KeyDB) GetSig0Keys

func (kdb *KeyDB) GetSig0Keys(zonename, state string) (*Sig0ActiveKeys, error)

func (*KeyDB) GetSyncOperation

func (kdb *KeyDB) GetSyncOperation(distributionID string) (*SyncOperationRecord, error)

GetSyncOperation retrieves a sync operation by distribution ID.

func (*KeyDB) HandleKeyStateOption

func (kdb *KeyDB) HandleKeyStateOption(opt *dns.OPT, zonename string) (*edns0.KeyStateOption, error)

func (*KeyDB) IncrementPeerFailedContacts

func (kdb *KeyDB) IncrementPeerFailedContacts(peerID string) error

IncrementPeerFailedContacts increments the failed contacts counter.

func (*KeyDB) InitCombinerEditTables

func (kdb *KeyDB) InitCombinerEditTables() error

InitCombinerEditTables initializes only the combiner edit tables. Call this on combiner startup — avoids creating agent-only HSYNC tables.

func (*KeyDB) InitHsyncTables

func (kdb *KeyDB) InitHsyncTables() error

InitHsyncTables initializes the HSYNC tables in the KeyDB. Call this during application startup after KeyDB is created.

func (*KeyDB) KeyBootstrapper

func (kdb *KeyDB) KeyBootstrapper(ctx context.Context) error

func (*KeyDB) ListApprovedEdits

func (kdb *KeyDB) ListApprovedEdits(zone string) ([]*ApprovedEditRecord, error)

ListApprovedEdits returns all approved edits, optionally filtered by zone. If zone is empty, returns all approved edits across all zones.

func (*KeyDB) ListPeers

func (kdb *KeyDB) ListPeers(state string) ([]*PeerRecord, error)

ListPeers retrieves all peers, optionally filtered by state.

func (*KeyDB) ListPendingEdits

func (kdb *KeyDB) ListPendingEdits(zone string) ([]*PendingEditRecord, error)

ListPendingEdits returns all pending edits for a zone.

func (*KeyDB) ListRejectedEdits

func (kdb *KeyDB) ListRejectedEdits(zone string) ([]*RejectedEditRecord, error)

ListRejectedEdits returns all rejected edits for a zone.

func (*KeyDB) ListSyncConfirmations

func (kdb *KeyDB) ListSyncConfirmations(distributionID string, limit int) ([]*SyncConfirmationRecord, error)

ListSyncConfirmations retrieves confirmations, optionally filtered by distribution ID.

func (*KeyDB) ListSyncOperations

func (kdb *KeyDB) ListSyncOperations(zoneName string, limit int) ([]*SyncOperationRecord, error)

ListSyncOperations retrieves sync operations, optionally filtered by zone.

func (*KeyDB) ListTransportEvents

func (kdb *KeyDB) ListTransportEvents(peerID string, limit int) ([]*HsyncTransportEvent, error)

ListTransportEvents retrieves transport events, optionally filtered by peer.

func (*KeyDB) LoadAllContributions

func (kdb *KeyDB) LoadAllContributions() (map[string]map[string]map[string]map[uint16]core.RRset, error)

LoadAllContributions loads the entire CombinerContributions table and returns it structured as zone → senderID → owner → rrtype → RRset. Used at startup to hydrate AgentContributions for all combiner zones.

func (*KeyDB) LoadAllPublishInstructions

func (kdb *KeyDB) LoadAllPublishInstructions() (map[string]map[string]*StoredPublishInstruction, error)

LoadAllPublishInstructions loads all stored instructions, keyed by zone then senderID.

func (*KeyDB) LoadDnskeyTrustAnchors

func (kdb *KeyDB) LoadDnskeyTrustAnchors() error

XXX: This should die now that we have a real IMR

func (*KeyDB) LoadOutgoingSerial

func (kdb *KeyDB) LoadOutgoingSerial(zone string) (uint32, error)

func (*KeyDB) LoadSig0ChildKeys

func (kdb *KeyDB) LoadSig0ChildKeys() error

func (*KeyDB) Lock

func (kdb *KeyDB) Lock()

Lock and Unlock expose the mutex for code that moves to tdns-mp and can no longer access the unexported kdb.mu.

func (*KeyDB) LogTransportEvent

func (kdb *KeyDB) LogTransportEvent(peerID, zoneName, eventType, transportType, direction string, success bool, errorCode, errorMessage string, context map[string]interface{}) error

LogTransportEvent logs a transport event for debugging.

func (*KeyDB) MarkSyncOperationConfirmed

func (kdb *KeyDB) MarkSyncOperationConfirmed(distributionID string) error

MarkSyncOperationConfirmed marks a sync operation as confirmed.

func (*KeyDB) NextEditID

func (kdb *KeyDB) NextEditID() (int, error)

NextEditID returns the next available edit ID across all three tables.

func (*KeyDB) Prepare

func (db *KeyDB) Prepare(q string) (*sql.Stmt, error)

func (*KeyDB) ProcessKeyState

func (kdb *KeyDB) ProcessKeyState(ks *edns0.KeyStateOption, zonename string) (*edns0.KeyStateOption, error)

func (*KeyDB) PromoteDnssecKey

func (kdb *KeyDB) PromoteDnssecKey(zonename string, keyid uint16, oldstate, newstate string) error

func (*KeyDB) Query

func (db *KeyDB) Query(query string, args ...interface{}) (*sql.Rows, error)

func (*KeyDB) QueryRow

func (db *KeyDB) QueryRow(query string, args ...interface{}) *sql.Row

func (*KeyDB) RecordMetrics

func (kdb *KeyDB) RecordMetrics(peerID, zoneName string, metrics *HsyncMetricsInfo) error

RecordMetrics records operational metrics for a time period.

func (*KeyDB) RejectPendingEdit

func (kdb *KeyDB) RejectPendingEdit(editID int, reason string) (*PendingEditRecord, error)

RejectPendingEdit moves a pending edit to the rejected table with a reason. Returns the original pending edit data for sending rejection confirmation.

func (*KeyDB) ResolvePendingEdit

func (kdb *KeyDB) ResolvePendingEdit(editID int, approvedRecords, rejectedRecords map[string][]string, reason string) error

ResolvePendingEdit removes a pending edit and writes the approved and rejected portions to their respective tables. This correctly handles partial results where some records were applied and others were rejected by policy.

approvedRecords: owner→[]rrstring for records that were applied or removed rejectedRecords: owner→[]rrstring for records that were rejected reason: rejection reason (used for all rejected records)

func (*KeyDB) RolloverKey

func (kdb *KeyDB) RolloverKey(zonename string, keytype string, tx *Tx) (uint16, uint16, error)

RolloverKey performs a manual key rollover for the specified zone and key type. It swaps the oldest standby key to active and the current active key to retired. Returns the old active keyid and the new active keyid. If tx is non-nil, uses the existing transaction; otherwise begins its own.

func (*KeyDB) SaveContributions

func (kdb *KeyDB) SaveContributions(zone, senderID string, contributions map[string]map[uint16]core.RRset) error

SaveContributions replaces all rows for (zone, senderID) with the current contributions. Runs in a transaction: DELETE old rows, INSERT new rows.

func (*KeyDB) SaveOutgoingSerial

func (kdb *KeyDB) SaveOutgoingSerial(zone string, serial uint32) error

func (*KeyDB) SavePeer

func (kdb *KeyDB) SavePeer(peer *PeerRecord) error

SavePeer inserts or updates a peer in the database.

func (*KeyDB) SavePendingEdit

func (kdb *KeyDB) SavePendingEdit(rec *PendingEditRecord) error

SavePendingEdit inserts a new pending edit.

func (*KeyDB) SavePublishInstruction

func (kdb *KeyDB) SavePublishInstruction(zone, senderID string, instr *core.PublishInstruction, publishedNS []string) error

SavePublishInstruction upserts a publish instruction for (zone, senderID).

func (*KeyDB) SaveSyncConfirmation

func (kdb *KeyDB) SaveSyncConfirmation(conf *SyncConfirmationRecord) error

SaveSyncConfirmation inserts a confirmation record.

func (*KeyDB) SaveSyncOperation

func (kdb *KeyDB) SaveSyncOperation(op *SyncOperationRecord) error

SaveSyncOperation inserts a new sync operation.

func (*KeyDB) SendSig0KeyUpdate

func (kdb *KeyDB) SendSig0KeyUpdate(ctx context.Context, childpri, parpri string, gennewkey bool) error

XXX: FIXME: This is only used from the CLI. It should change into code used by TDNS-SERVER and

accessed via API. The code should store the newly generated key in the keystore.

func (*KeyDB) SetPropagationConfirmed

func (kdb *KeyDB) SetPropagationConfirmed(zonename string, keyid uint16) error

SetPropagationConfirmed marks a DNSKEY as propagation-confirmed in the keystore. Called when the agent sends KEYSTATE "propagated" to the signer.

func (*KeyDB) Sig0KeyMgmt

func (kdb *KeyDB) Sig0KeyMgmt(tx *Tx, kp KeystorePost) (*KeystoreResponse, error)

func (*KeyDB) Sig0TrustMgmt

func (kdb *KeyDB) Sig0TrustMgmt(tx *Tx, tp TruststorePost) (*TruststoreResponse, error)

func (*KeyDB) TransitionMpdistToPublished

func (kdb *KeyDB) TransitionMpdistToPublished(zonename string, keyid uint16) error

TransitionMpdistToPublished transitions a key from mpdist to published state. Called when the signer receives a "propagated" KEYSTATE signal from the agent, indicating all remote providers have confirmed the key. If the key is not in mpdist state, this is a no-op (returns nil).

func (*KeyDB) TransitionMpremoveToRemoved

func (kdb *KeyDB) TransitionMpremoveToRemoved(zonename string, keyid uint16) error

TransitionMpremoveToRemoved transitions a key from mpremove to removed state. Called when the signer receives a "propagated" KEYSTATE signal from the agent, indicating all remote providers have confirmed the key removal. If the key is not in mpremove state, this is a no-op (returns nil).

func (*KeyDB) TriggerChildKeyVerification

func (kdb *KeyDB) TriggerChildKeyVerification(childZone string, keyid uint16, keyRR string)

TriggerChildKeyVerification starts an async verification of a child KEY that was just stored in the TrustStore. It uses the KeyBootstrapper's retry pattern: verify via DNS lookup, retry with backoff, then trust.

func (*KeyDB) Unlock

func (kdb *KeyDB) Unlock()

func (*KeyDB) UpdateDnssecKeyState

func (kdb *KeyDB) UpdateDnssecKeyState(zonename string, keyid uint16, newstate string) error

UpdateDnssecKeyState transitions a DNSSEC key to a new state and sets the appropriate lifecycle timestamp. When transitioning to "published", sets published_at. When transitioning to "retired", sets retired_at. Invalidates the cache for both old and new states.

func (*KeyDB) UpdateKeyState

func (kdb *KeyDB) UpdateKeyState(ctx context.Context, keyName string, keyid uint16, imr *Imr, algorithm uint8) error

UpdateKeyState sends a KeyState inquiry to the DSYNC target for the given key and updates the local key store with the parent's response. If the parent reports the key as unknown, triggers a bootstrap.

func (*KeyDB) UpdatePeerContact

func (kdb *KeyDB) UpdatePeerContact(peerID string) error

UpdatePeerContact updates the last contact timestamp.

func (*KeyDB) UpdatePeerState

func (kdb *KeyDB) UpdatePeerState(peerID, state, reason string) error

UpdatePeerState updates just the state fields of a peer.

func (*KeyDB) UpdateSyncOperationStatus

func (kdb *KeyDB) UpdateSyncOperationStatus(distributionID, status, message string) error

UpdateSyncOperationStatus updates the status of a sync operation.

func (*KeyDB) ZoneUpdaterEngine

func (kdb *KeyDB) ZoneUpdaterEngine(ctx context.Context) error

type KeyInventoryItem

type KeyInventoryItem struct {
	KeyTag    uint16
	Algorithm uint8
	Flags     uint16
	State     string // "created","mpdist","published","standby","active","retired","removed","foreign"
	KeyRR     string // Full DNSKEY RR string (public key data, no private key)
}

KeyInventoryItem is a lightweight DNSKEY entry for inventory responses. Does not include private key material — only the metadata needed for key classification.

type KeyInventorySnapshot

type KeyInventorySnapshot struct {
	SenderID  string
	Zone      string
	Inventory []KeyInventoryItem
	Received  time.Time
}

KeyInventorySnapshot stores a complete key inventory received from the signer.

NOTE: MP type in tdns because it is a field of ZoneMPExtension.

type KeyLifetime

type KeyLifetime struct {
	Lifetime    uint32
	SigValidity uint32
}

func GenKeyLifetime

func GenKeyLifetime(lifetime, sigvalidity string) KeyLifetime

type KeystateInfo

type KeystateInfo struct {
	OK        bool   `json:"ok"`
	Error     string `json:"error,omitempty"`
	Timestamp string `json:"timestamp,omitempty"`
}

KeystateInfo reports the health of the KEYSTATE exchange with the signer for a zone.

type KeystateInventoryMsg

type KeystateInventoryMsg struct {
	SenderID  string
	Zone      string
	Inventory []KeyInventoryItem
}

KeystateInventoryMsg carries a complete KEYSTATE inventory from signer to agent. Delivered via MsgQs.KeystateInventory channel.

type KeystateSignalMsg

type KeystateSignalMsg struct {
	SenderID string
	Zone     string
	KeyTag   uint16
	Signal   string // "propagated", "rejected", "removed"
	Message  string
}

KeystateSignalMsg carries a per-key KEYSTATE signal from agent to signer. Delivered via MsgQs.KeystateSignal channel. Signals: "propagated" (all remote providers confirmed), "rejected" (some provider rejected).

type KeystorePost

type KeystorePost struct {
	Command         string // "sig0"
	SubCommand      string // "list" | "add" | "delete" | ...
	Zone            string
	Keyname         string
	Keyid           uint16
	Flags           uint16
	KeyType         string
	Algorithm       uint8 // RSASHA256 | ED25519 | etc.
	PrivateKey      string
	KeyRR           string
	DnskeyRR        string
	PrivateKeyCache *PrivateKeyCache
	State           string
	ParentState     uint8
	Creator         string
}

type KeystoreResponse

type KeystoreResponse struct {
	AppName  string
	Time     time.Time
	Status   string
	Zone     string
	Dnskeys  map[string]DnssecKey // TrustAnchor
	Sig0keys map[string]Sig0Key
	Msg      string
	Error    bool
	ErrorMsg string
}

type LeaderElection

type LeaderElection struct {
	Zone          ZoneName
	Leader        AgentId
	LeaderExpiry  time.Time
	Active        bool
	Term          uint64
	MyVote        uint32
	Votes         map[AgentId]uint32
	Confirms      map[AgentId]AgentId
	ExpectedPeers int
	VoteTimer     *time.Timer
	ConfirmTimer  *time.Timer
	ReelectTimer  *time.Timer
	// contains filtered or unexported fields
}

LeaderElection tracks the per-zone election state.

type LeaderElectionManager

type LeaderElectionManager struct {
	// contains filtered or unexported fields
}

LeaderElectionManager coordinates leader election across all zones. Phase 6: supports both per-zone elections (legacy) and per-group elections. When a ProviderGroupManager is set, elections are per-group and the leader covers all zones in the group. IsLeader checks group membership first.

func NewLeaderElectionManager

func NewLeaderElectionManager(localID AgentId, leaderTTL time.Duration, broadcastFunc func(ZoneName, string, map[string][]string) error) *LeaderElectionManager

func (*LeaderElectionManager) ApplyGossipElection

func (lem *LeaderElectionManager) ApplyGossipElection(groupHash string, state GroupElectionState)

ApplyGossipElection updates the LEM with election state received via gossip. Only accepts the update if the gossip term is higher than the local term.

func (*LeaderElectionManager) DeferElection

func (lem *LeaderElectionManager) DeferElection(zone ZoneName)

DeferElection records a zone as needing an election once peers become operational. Called from OnFirstLoad when peers aren't ready yet at zone load time.

func (*LeaderElectionManager) DeferGroupElection

func (lem *LeaderElectionManager) DeferGroupElection(groupHash string)

DeferGroupElection records a group as needing an election when OnGroupOperational fires.

func (*LeaderElectionManager) GetAllLeaders

func (lem *LeaderElectionManager) GetAllLeaders() []LeaderStatus

GetAllLeaders returns the current leader status for all zones with an active leader.

func (*LeaderElectionManager) GetGroupElectionState

func (lem *LeaderElectionManager) GetGroupElectionState(groupHash string) GroupElectionState

GetGroupElectionState returns the election state for a group (for gossip).

func (*LeaderElectionManager) GetGroupLeader

func (lem *LeaderElectionManager) GetGroupLeader(groupHash string) (AgentId, bool)

GetGroupLeader returns the current leader for a provider group.

func (*LeaderElectionManager) GetLeader

func (lem *LeaderElectionManager) GetLeader(zone ZoneName) (AgentId, bool)

GetLeader returns the current leader for a zone, if known and not expired.

func (*LeaderElectionManager) GetParentSyncStatus

func (lem *LeaderElectionManager) GetParentSyncStatus(zone ZoneName, zd *ZoneData, kdb *KeyDB, imr *Imr, ar *AgentRegistry) ParentSyncStatus

GetParentSyncStatus computes the current parent sync status for a zone on demand.

func (*LeaderElectionManager) GetPendingElections

func (lem *LeaderElectionManager) GetPendingElections() []ZoneName

GetPendingElections returns zones with deferred elections (waiting for peers).

func (*LeaderElectionManager) HandleGroupMessage

func (lem *LeaderElectionManager) HandleGroupMessage(groupHash string, senderID AgentId, rfiType string, records map[string][]string)

HandleGroupMessage dispatches an election message that carries a _group record.

func (*LeaderElectionManager) HandleMessage

func (lem *LeaderElectionManager) HandleMessage(zone ZoneName, senderID AgentId, rfiType string, records map[string][]string)

HandleMessage dispatches an incoming election message to the appropriate handler.

func (*LeaderElectionManager) InvalidateGroupLeader

func (lem *LeaderElectionManager) InvalidateGroupLeader(groupHash string)

InvalidateGroupLeader clears the leader for a group (e.g., when the group degrades). Keeps the term so "invalidated" status can be reported.

func (*LeaderElectionManager) IsLeader

func (lem *LeaderElectionManager) IsLeader(zone ZoneName) bool

IsLeader returns true if the local agent is the current leader for a zone. Checks group-based election first (if provider groups are configured), then falls back to per-zone election.

func (*LeaderElectionManager) NotifyPeerOperational

func (lem *LeaderElectionManager) NotifyPeerOperational(peerZones map[ZoneName]bool)

NotifyPeerOperational is called when a peer becomes operational. Checks if deferred elections or leaderless zones can now hold elections. Elections require ALL configured peers to be operational.

func (*LeaderElectionManager) SetConfiguredPeersFunc

func (lem *LeaderElectionManager) SetConfiguredPeersFunc(f func(zone ZoneName) int)

SetConfiguredPeersFunc sets the callback that returns the number of configured peers for a zone (from HSYNC3 records, minus self). Elections require ALL configured peers to participate — partial elections are aborted.

func (*LeaderElectionManager) SetOnLeaderElected

func (lem *LeaderElectionManager) SetOnLeaderElected(f func(zone ZoneName) error)

SetOnLeaderElected sets the callback invoked when the local agent wins an election. Used to trigger delegation sync setup (SIG(0) key generation + bootstrap with parent).

func (*LeaderElectionManager) SetOperationalPeersFunc

func (lem *LeaderElectionManager) SetOperationalPeersFunc(f func(zone ZoneName) int)

SetOperationalPeersFunc sets the callback used to count operational peers for re-election.

func (*LeaderElectionManager) SetProviderGroupManager

func (lem *LeaderElectionManager) SetProviderGroupManager(pgm *ProviderGroupManager)

SetProviderGroupManager sets the provider group manager for group-based elections.

func (*LeaderElectionManager) StartElection

func (lem *LeaderElectionManager) StartElection(zone ZoneName, expectedPeers int)

StartElection initiates a new election for a zone. If expectedPeers is 0, the local agent immediately becomes leader without sending any messages.

func (*LeaderElectionManager) StartGroupElection

func (lem *LeaderElectionManager) StartGroupElection(groupHash string, members []string, zones []ZoneName)

StartGroupElection initiates a leader election for a provider group. The election is broadcast using one of the group's zones as the RFI channel. The winner becomes leader for ALL zones in the group.

type LeaderStatus

type LeaderStatus struct {
	Zone   ZoneName
	Leader AgentId
	IsSelf bool
	Term   uint64
	Expiry time.Time
}

LeaderStatus describes the current leader for a single zone.

type LocalAgentApiConf

type LocalAgentApiConf struct {
	Addresses struct {
		Publish []string
		Listen  []string
	}
	BaseUrl  string
	Port     uint16
	CertFile string
	KeyFile  string
	CertData string
	KeyData  string
}

type LocalAgentDnsConf

type LocalAgentDnsConf struct {
	Addresses struct {
		Publish []string
		Listen  []string
	}
	BaseUrl     string
	Port        uint16
	ControlZone string `yaml:"control_zone" mapstructure:"control_zone"` // Zone used for NOTIFY(CHUNK) QNAMEs in DNS mode (default: agent identity)
	// Chunk config (same key names as combiner for consistency)
	ChunkMode          string `yaml:"chunk_mode" mapstructure:"chunk_mode"`                     // "edns0" | "query"; query = store payload, receiver fetches via CHUNK query (default: edns0)
	ChunkQueryEndpoint string `yaml:"chunk_query_endpoint" mapstructure:"chunk_query_endpoint"` // "include" | "none"; required when chunk_mode=query. include = signal in NOTIFY (EDNS0); none = receiver uses combiner.agents[].address
	// ChunkMaxSize: maximum size (bytes) of each data chunk when fragmenting payloads via PrepareDistributionChunks.
	// 0 = default (60000). Useful for testing fragmentation with small values (e.g. 500).
	ChunkMaxSize int `yaml:"chunk_max_size" mapstructure:"chunk_max_size"`
	// Message retention times for CHUNK distributions (in seconds)
	MessageRetention MessageRetentionConf `yaml:"message_retention" mapstructure:"message_retention"`
}

type LogConf

type LogConf struct {
	File       string            `yaml:"file" validate:"required"`
	Level      string            `yaml:"level"`      // "debug"|"info"|"warn"|"error"; default "info"
	Subsystems map[string]string `yaml:"subsystems"` // per-subsystem level overrides
}

type MPTransportBridge

type MPTransportBridge struct {
	*transport.TransportManager // generic (fields promoted via embedding)

	// SupportedMechanisms lists active transports ("api", "dns")
	SupportedMechanisms []string
	// contains filtered or unexported fields
}

MPTransportBridge manages multiple transports for agent communication. MPTransportBridge aggregates MP-specific transport state and methods. It holds a reference to the generic transport.TransportManager and adds multi-provider functionality (message routing, authorization, agent discovery, DNSKEY propagation, reliable delivery wrappers).

func NewMPTransportBridge

func NewMPTransportBridge(cfg *MPTransportBridgeConfig) *MPTransportBridge

NewTransportManager creates a new MPTransportBridge with both API and DNS transports.

func (*MPTransportBridge) DiscoverAndRegisterAgent

func (tm *MPTransportBridge) DiscoverAndRegisterAgent(ctx context.Context, identity string) error

DiscoverAndRegisterAgent performs discovery and registration in one step.

func (*MPTransportBridge) EnqueueForCombiner

func (tm *MPTransportBridge) EnqueueForCombiner(zone ZoneName, update *ZoneUpdate, distID string) (string, error)

EnqueueForCombiner enqueues a zone update for reliable delivery to the combiner. Called by SynchedDataEngine when a zone update needs to reach the combiner. If distID is non-empty, it is used as the distribution ID; otherwise a new one is generated. Returns the distributionID for tracking and any error.

func (*MPTransportBridge) EnqueueForSpecificAgent

func (tm *MPTransportBridge) EnqueueForSpecificAgent(zone ZoneName, agentID AgentId, update *ZoneUpdate, distID string) error

EnqueueForSpecificAgent enqueues a zone update for a single agent. Used by "resync-targeted" to respond only to the requesting agent.

func (*MPTransportBridge) EnqueueForZoneAgents

func (tm *MPTransportBridge) EnqueueForZoneAgents(zone ZoneName, update *ZoneUpdate, distID string) error

EnqueueForZoneAgents enqueues a zone update for reliable delivery to all remote agents involved with this zone (as determined by AgentRegistry). Called by SynchedDataEngine when a locally-originated update needs to reach all peer agents. Uses the same distID for all agents so the originating agent can correlate confirmations from combiner and agents.

func (*MPTransportBridge) GetDistributionRecipients

func (tm *MPTransportBridge) GetDistributionRecipients(zone ZoneName, skipCombiner bool) []string

GetDistributionRecipients returns the list of recipient identities that will receive an update for the given zone. This is used by the SynchedDataEngine to populate TrackedRR.ExpectedRecipients so that ProcessConfirmation knows who must confirm before transitioning Pending → Accepted. If skipCombiner is true, the combiner is excluded from the list.

func (*MPTransportBridge) GetPreferredTransportName

func (tm *MPTransportBridge) GetPreferredTransportName(agent *Agent) string

GetPreferredTransportName returns the preferred transport name for an agent.

func (*MPTransportBridge) GetQueuePendingMessages

func (tm *MPTransportBridge) GetQueuePendingMessages() []transport.PendingMessageInfo

GetQueuePendingMessages returns a snapshot of all pending messages in the queue.

func (*MPTransportBridge) GetQueueStats

func (tm *MPTransportBridge) GetQueueStats() transport.QueueStats

GetQueueStats returns statistics from the reliable message queue.

func (*MPTransportBridge) HasAPITransport

func (tm *MPTransportBridge) HasAPITransport(agent *Agent) bool

HasAPITransport returns true if API transport is available for an agent.

func (*MPTransportBridge) HasDNSTransport

func (tm *MPTransportBridge) HasDNSTransport(agent *Agent) bool

HasDNSTransport returns true if DNS transport is available for an agent.

func (*MPTransportBridge) IsPeerAuthorized

func (tm *MPTransportBridge) IsPeerAuthorized(senderID string, zone string) (bool, string)

IsPeerAuthorized checks if a peer is authorized to communicate with us. Authorization can come from three sources:

1. Configured peers: Provided via AuthorizedPeers callback (role-specific) 2. LEGACY state: Established relationship with zero shared zones 3. HSYNC3 membership: Both peers listed in HSYNC3 RRset for a shared zone

This function is role-agnostic. Each role injects its own AuthorizedPeers callback at TransportManager creation time.

Parameters:

  • senderID: Identity of the agent attempting to communicate
  • zone: Optional zone name for HSYNC3 membership check (empty string skips HSYNC3 check)

Returns:

  • authorized: true if agent is authorized
  • reason: human-readable explanation of authorization decision

func (*MPTransportBridge) MarkDeliveryConfirmed

func (tm *MPTransportBridge) MarkDeliveryConfirmed(distributionID string, senderID string) bool

MarkDeliveryConfirmed marks a queued message as confirmed by the recipient. senderID is the identity of the confirming party (= the original message recipient).

func (*MPTransportBridge) OnAgentDiscoveryComplete

func (tm *MPTransportBridge) OnAgentDiscoveryComplete(agent *Agent)

OnAgentDiscoveryComplete is called when agent discovery completes. It syncs the Agent to a transport.Peer and sets preferred transport.

func (*MPTransportBridge) ProcessDnskeyConfirmation

func (tm *MPTransportBridge) ProcessDnskeyConfirmation(distID string, source string, status string, rejectedItems []RejectedItemInfo) bool

ProcessDnskeyConfirmation checks if a confirmation is for a pending DNSKEY propagation. If so, marks the agent as confirmed. When all agents have confirmed, sends KEYSTATE "propagated" to the signer. If any agent rejects, sends KEYSTATE "rejected". Returns true if this confirmation was for a DNSKEY propagation (handled here).

func (*MPTransportBridge) RegisterChunkNotifyHandler

func (tm *MPTransportBridge) RegisterChunkNotifyHandler() error

RegisterChunkNotifyHandler registers the CHUNK NOTIFY handler with tdns. This should be called during agent initialization.

func (*MPTransportBridge) RegisterDiscoveredAgent

func (tm *MPTransportBridge) RegisterDiscoveredAgent(result *AgentDiscoveryResult) error

RegisterDiscoveredAgent adds a discovered agent to the PeerRegistry and optionally to AgentRegistry.

func (*MPTransportBridge) SelectTransport

func (tm *MPTransportBridge) SelectTransport(peer *transport.Peer) transport.Transport

SelectTransport selects the appropriate transport for communicating with a peer.

func (*MPTransportBridge) SendBeatWithFallback

func (tm *MPTransportBridge) SendBeatWithFallback(ctx context.Context, agent *Agent, sequence uint64) (*transport.BeatResponse, error)

SendBeatWithFallback sends a heartbeat to a peer with transport fallback. SendBeatWithFallback sends a Beat heartbeat to a peer (legacy name). UPDATED: Now sends Beat on ALL supported transports independently when both are configured. Returns success if ANY transport succeeds. Updates per-transport LastContactTime in Agent struct.

func (*MPTransportBridge) SendHelloWithFallback

func (tm *MPTransportBridge) SendHelloWithFallback(ctx context.Context, agent *Agent, sharedZones []string) (*transport.HelloResponse, error)

SendHelloWithFallback sends a Hello handshake to a peer with transport fallback (legacy name). UPDATED: Now sends Hello on ALL supported transports independently when both are configured. Returns success if ANY transport succeeds. Updates per-transport state in Agent struct.

func (*MPTransportBridge) SendPing

SendPing sends a ping to a peer, preferring API transport when available.

func (*MPTransportBridge) SendSyncWithFallback

func (tm *MPTransportBridge) SendSyncWithFallback(ctx context.Context, peer *transport.Peer, req *transport.SyncRequest) (*transport.SyncResponse, error)

SendWithFallback sends a message using the preferred transport, falling back if it fails.

func (*MPTransportBridge) StartIncomingMessageRouter

func (tm *MPTransportBridge) StartIncomingMessageRouter(ctx context.Context)

StartIncomingMessageRouter starts a goroutine that routes incoming DNS messages to the appropriate hsyncengine channels. ctx is intentionally unused: kept in the signature for API stability and future use.

func (*MPTransportBridge) StartReliableQueue

func (tm *MPTransportBridge) StartReliableQueue(ctx context.Context)

StartReliableQueue wires up the sendFunc and starts the queue's background worker. Must be called after MPTransportBridge is fully initialized (transports, combiner peer, etc.).

func (*MPTransportBridge) SyncPeerFromAgent

func (tm *MPTransportBridge) SyncPeerFromAgent(agent *Agent) *transport.Peer

SyncPeerFromAgent creates or updates a transport.Peer from an existing Agent.

func (*MPTransportBridge) TrackDnskeyPropagation

func (tm *MPTransportBridge) TrackDnskeyPropagation(zone ZoneName, distID string, keyTags []uint16, agents []AgentId)

TrackDnskeyPropagation registers a DNSKEY distribution for confirmation tracking. Called by SynchedDataEngine after enqueueing DNSKEY changes for remote agents.

type MPTransportBridgeConfig

type MPTransportBridgeConfig struct {
	LocalID       string
	ControlZone   string
	APITimeout    time.Duration
	DNSTimeout    time.Duration
	AgentRegistry *AgentRegistry
	MsgQs         *MsgQs
	// ChunkMode: "edns0" or "query"; when "query", agent stores payload and sends NOTIFY without EDNS0; receiver fetches via CHUNK query
	ChunkMode         string
	ChunkPayloadStore ChunkPayloadStore
	// ChunkQueryEndpoint: for query mode, address (host:port) where agent answers CHUNK queries
	ChunkQueryEndpoint string
	// ChunkQueryEndpointInNotify: when true, include endpoint in NOTIFY (EDNS0 option 65005); when false, receiver uses static config (e.g. combiner.agents[].address)
	ChunkQueryEndpointInNotify bool
	// ChunkMaxSize: maximum data chunk size in bytes for PrepareDistributionChunks.
	// 0 = default (60000). Set small (e.g. 500) for fragmentation testing.
	ChunkMaxSize int

	// PayloadCrypto enables JWS/JWE encryption for CHUNK payloads (optional)
	// If set and Enabled, all outgoing CHUNK payloads will be encrypted and signed
	PayloadCrypto *transport.PayloadCrypto

	// DistributionCache: when set, outgoing CHUNK operations (ping, hello, etc.) are registered for "agent distrib list"
	DistributionCache *DistributionCache

	// SupportedMechanisms lists active transports ("api", "dns"); default: both if configured
	SupportedMechanisms []string

	// CombinerID is the identity of the combiner for this agent (from config).
	// Used by EnqueueForCombiner to know which AgentRegistry entry is the combiner.
	CombinerID string

	// SignerID is the identity of the local signer for KEYSTATE signaling (Phase 6).
	SignerID string
	// SignerAddress is the DNS address (host:port) of the local signer.
	SignerAddress string

	// AuthorizedPeers returns the list of peer identities authorized via config.
	// Called at runtime during authorization. Each role provides its own implementation.
	// If nil, only HSYNC-based and LEGACY-based authorization is used.
	AuthorizedPeers func() []string

	// MessageRetention returns retention seconds for a given message type (operation).
	// Used by distribution cache for expiration. If nil, default retention is used.
	MessageRetention func(operation string) int

	// GetImrEngine returns the IMR resolver for DNS-based agent discovery (optional).
	// Uses a closure because ImrEngine starts asynchronously after TM creation.
	// Only the agent needs this; combiner/signer/external apps pass nil.
	GetImrEngine func() *Imr

	// ClientCertFile and ClientKeyFile are the TLS client certificate presented when
	// connecting to peers' sync API servers. Required when peers enforce mutual TLS
	// (e.g. combiner/signer sync routers verify client cert against agent's TLSA record).
	ClientCertFile string
	ClientKeyFile  string
}

MPTransportBridgeConfig holds configuration for creating a MPTransportBridge.

type MPZoneInfo

type MPZoneInfo struct {
	Providers  []string     `json:"providers"`
	Signers    []string     `json:"signers"`
	NSmgmt     string       `json:"nsmgmt"`
	ParentSync string       `json:"parentsync"`
	Suffix     string       `json:"suffix,omitempty"`
	Options    []ZoneOption `json:"options"`
}

MPZoneInfo carries multi-provider zone details for the mplist command.

type MPdata

type MPdata struct {
	WeAreProvider bool                // At least one of our agent identities matches an HSYNC3 Identity
	OurLabel      string              // Our provider label from the matching HSYNC3 record
	WeAreSigner   bool                // Our label appears in HSYNCPARAM signers (or zone is unsigned)
	OtherSigners  int                 // Count of other signers in HSYNCPARAM
	ZoneSigned    bool                // HSYNCPARAM signers= is non-empty (zone uses multi-signer)
	Options       map[ZoneOption]bool // MP-specific options (future: migrate from zd.Options)
}

MPdata caches multi-provider membership and signing state for a zone. nil means the zone is not confirmed as a multi-provider zone (either OptMultiProvider is not set, or the zone owner hasn't declared it via HSYNC3+HSYNCPARAM, or we are not a listed provider). Populated during zone refresh by populateMPdata().

NOTE: This is an MP type that lives in tdns (not tdns-mp) because it is a field of ZoneMPExtension, which is a field of ZoneData.

type MSCAPIConf

type MSCAPIConf struct {
	BaseURL    string `validate:"required,url"`
	ApiKey     string `validate:"required_if=AuthMethod apikey"`
	AuthMethod string `validate:"required,oneof=none apikey"`
	UseTLS     bool   `validate:"required"`
}

type MSCNotifyConf

type MSCNotifyConf struct {
	Addresses []string `validate:"required"` // XXX: must not be in addr:port format
	Port      string   `validate:"required"`
	Targets   []string
}

type MemChunkPayloadStore

type MemChunkPayloadStore struct {
	// contains filtered or unexported fields
}

MemChunkPayloadStore is an in-memory store with TTL.

func NewMemChunkPayloadStore

func NewMemChunkPayloadStore(ttl time.Duration) *MemChunkPayloadStore

NewMemChunkPayloadStore creates a store with the given TTL (e.g. 5*time.Minute).

func (*MemChunkPayloadStore) Get

func (s *MemChunkPayloadStore) Get(qname string) ([]byte, uint8, bool)

func (*MemChunkPayloadStore) GetChunk

func (s *MemChunkPayloadStore) GetChunk(qname string, sequence uint16) (*core.CHUNK, bool)

GetChunk returns a specific chunk by sequence number from a stored chunk array. Sequence 0 = manifest, 1..N = data chunks. The index into the array equals the sequence number.

func (*MemChunkPayloadStore) Set

func (s *MemChunkPayloadStore) Set(qname string, payload []byte, format uint8)

func (*MemChunkPayloadStore) SetChunks

func (s *MemChunkPayloadStore) SetChunks(qname string, chunks []*core.CHUNK)

SetChunks stores a chunk array (manifest + data chunks) under the given qname. The chunks are deep-copied to prevent mutation by the caller.

type MemberState

type MemberState struct {
	Identity   string            `json:"identity"`
	PeerStates map[string]string `json:"peer_states"` // key: peer identity, value: state string
	Zones      []string          `json:"zones"`       // zones this member serves in this group
	Timestamp  time.Time         `json:"timestamp"`   // set by the member itself
}

MemberState is one member's view of all other members in the group. Only the member itself updates its own MemberState (sets Timestamp). Other agents propagate it via gossip without modification.

type MemberZone

type MemberZone struct {
	ZoneName      string    `json:"zone_name"`
	Hash          string    `json:"hash"`
	ServiceGroups []string  `json:"service_groups"` // Groups associated with this zone (RFC 9432 terminology)
	SigningGroup  string    `json:"signing_group"`  // Signing group for this zone
	MetaGroup     string    `json:"meta_group"`     // Meta group for this zone
	DiscoveredAt  time.Time `json:"discovered_at"`
}

MemberZone represents a zone discovered in a catalog zone

type MessageRetentionConf

type MessageRetentionConf struct {
	Beat     int `yaml:"beat" mapstructure:"beat"`         // Beat message retention (default: 30s)
	Ping     int `yaml:"ping" mapstructure:"ping"`         // Ping message retention (default: 30s)
	Hello    int `yaml:"hello" mapstructure:"hello"`       // Hello message retention (default: 300s)
	Sync     int `yaml:"sync" mapstructure:"sync"`         // Sync message retention (default: 300s)
	Relocate int `yaml:"relocate" mapstructure:"relocate"` // Relocate message retention (default: 300s)
	Default  int `yaml:"default" mapstructure:"default"`   // Default retention for other types (default: 300s)
}

MessageRetentionConf defines retention times for different message types in CHUNK distributions. Times are in seconds. Beat and ping messages expire quickly to reduce clutter, while other message types are kept longer for debugging purposes.

func (*MessageRetentionConf) GetRetentionForMessageType

func (m *MessageRetentionConf) GetRetentionForMessageType(messageType string) int

GetRetentionForMessageType returns the retention time in seconds for a given message type. Returns the configured value if set, otherwise returns the appropriate default.

type MetaGroupConfig

type MetaGroupConfig = ConfigGroupConfig

MetaGroupConfig is deprecated, use ConfigGroupConfig instead

type MsgQs

type MsgQs struct {
	Hello chan *AgentMsgReport // incoming /hello from other agents
	Beat  chan *AgentMsgReport // incoming /beat from other agents
	Ping  chan *AgentMsgReport // incoming /ping from other agents
	// Msg               chan *AgentMsgReport    // incoming /msg from other agents
	Msg               chan *AgentMsgPostPlus     // incoming /msg from other agents
	Command           chan *AgentMgmtPostPlus    // local commands TO the agent, usually for passing on to other agents
	DebugCommand      chan *AgentMgmtPostPlus    // local commands TO the agent, usually for passing on to other agents
	SynchedDataUpdate chan *SynchedDataUpdate    // incoming combiner updates
	SynchedDataCmd    chan *SynchedDataCmd       // local commands TO the combiner
	Confirmation      chan *ConfirmationDetail   // combiner confirmation feedback
	KeystateInventory chan *KeystateInventoryMsg // incoming KEYSTATE inventory from signer
	KeystateSignal    chan *KeystateSignalMsg    // incoming KEYSTATE signals (propagated/rejected) from agent to signer
	EditsResponse     chan *EditsResponseMsg     // incoming EDITS response from combiner
	ConfigResponse    chan *ConfigResponseMsg    // incoming CONFIG response from peer agent
	AuditResponse     chan *AuditResponseMsg     // incoming AUDIT response from peer agent
	StatusUpdate      chan *StatusUpdateMsg      // incoming STATUS-UPDATE notifications

	// OnRemoteConfirmationReady is called when this agent (acting as a remote agent)
	// receives a combiner confirmation for a sync that originated from another agent.
	// The callback sends the final confirmation NOTIFY back to the originating agent.
	OnRemoteConfirmationReady func(detail *RemoteConfirmationDetail)
}

type MultiProviderConf

type MultiProviderConf struct {

	// Role: "agent", "combiner", or "signer". Determines which fields are active.
	Role string `yaml:"role"`
	// Active: master switch for multi-provider mode.
	// Must be true AND zone must have options: [multi-provider] for MP behavior.
	Active bool `yaml:"active"`
	// Identity: this node's identity (FQDN) for transport protocol.
	Identity string `yaml:"identity"`
	// LongTermJosePrivKey: path to JOSE private key for secure CHUNK.
	LongTermJosePrivKey string `yaml:"long_term_jose_priv_key"`
	// ChunkMode: "edns0" | "query" for outbound NOTIFY(CHUNK).
	ChunkMode string `yaml:"chunk_mode" mapstructure:"chunk_mode"`
	// ChunkMaxSize: maximum size (bytes) of each data chunk when fragmenting payloads.
	// 0 = default (60000). Useful for testing fragmentation with small values.
	ChunkMaxSize int `yaml:"chunk_max_size" mapstructure:"chunk_max_size"`
	// Agents: the agent peers (address, JOSE public key, optional API URL).
	// Used by signer and combiner roles.
	Agents []*PeerConf `yaml:"agents"`
	// SyncApi: sync API server config for inbound HELLO/BEAT/PING over HTTPS.
	// Used by signer and combiner roles.
	SyncApi struct {
		Addresses struct {
			Listen []string
		}
		CertFile string `yaml:"cert_file" mapstructure:"cert_file"`
		KeyFile  string `yaml:"key_file" mapstructure:"key_file"`
	} `yaml:"sync_api" mapstructure:"sync_api"`

	// CombinerOptions: list of combiner-specific option strings parsed at startup.
	// Known options: "add-signature".
	CombinerOptionsStrs []string                `yaml:"combiner-options" mapstructure:"combiner-options"`
	CombinerOptions     map[CombinerOption]bool `yaml:"-" mapstructure:"-"`

	// ChunkQueryEndpoint: "include" | "none"; required when chunk_mode=query (combiner role).
	ChunkQueryEndpoint string `yaml:"chunk_query_endpoint" mapstructure:"chunk_query_endpoint"`
	// Signature: template string for a TXT record injected into combined zones (demo feature).
	// Supports {identity} and {zone} placeholders.
	Signature    string `yaml:"signature"`
	AddSignature bool   `yaml:"add-signature" mapstructure:"add-signature"` // DEPRECATED: use combiner-options: [add-signature]
	// ProtectedNamespaces: list of domain suffixes that belong to this provider.
	// NS records from remote agents whose targets fall within any of these namespaces
	// are rejected (prevents namespace intrusion).
	ProtectedNamespaces []string `yaml:"protected-namespaces" mapstructure:"protected-namespaces"`
	// ProviderZones: zones owned by the provider where agents may make targeted edits
	// (e.g. _signal KEY records). Unlike MP zones, these use config-driven RRtype
	// restrictions and allow non-apex owners.
	ProviderZones []ProviderZoneConf `yaml:"provider-zones" mapstructure:"provider-zones"`

	// SignerOptions: list of signer-specific option strings parsed at startup.
	SignerOptionsStrs []string              `yaml:"signer-options" mapstructure:"signer-options"`
	SignerOptions     map[SignerOption]bool `yaml:"-" mapstructure:"-"`

	// AgentOptions: list of agent-specific option strings parsed at startup.
	AgentOptionsStrs []string             `yaml:"agent-options" mapstructure:"agent-options"`
	AgentOptions     map[AgentOption]bool `yaml:"-" mapstructure:"-"`
	// SupportedMechanisms: List of active transport mechanisms (default: ["api", "dns"] if both configured)
	SupportedMechanisms []string `yaml:"supported_mechanisms" mapstructure:"supported_mechanisms"`
	Local               struct {
		Notify      []string
		Nameservers []string `yaml:"nameservers,omitempty"`
	}
	Remote struct {
		LocateInterval int
		BeatInterval   uint32
	}
	Syncengine struct {
		Intervals struct {
			HelloRetry int
		}
	}
	Api LocalAgentApiConf
	Dns LocalAgentDnsConf
	// Combiner peer (agent only): address and combiner's JOSE public key path for secure CHUNK
	Combiner *PeerConf `yaml:"combiner"`
	// Signer peer (agent only): address and JOSE public key path for KEYSTATE signaling
	Signer *PeerConf `yaml:"signer"`
	// AuthorizedPeers: List of agent identities authorized to communicate
	AuthorizedPeers []string `yaml:"authorized_peers"`
	// Peers (DEPRECATED): Old format with embedded addresses/keys - use authorized_peers instead
	Peers map[string]*PeerConf `yaml:"peers"`
	Xfr   struct {
		Outgoing struct {
			Addresses []string `yaml:"addresses,omitempty"`
			Auth      []string `yaml:"auth,omitempty"`
		}
		Incoming struct {
			Addresses []string `yaml:"addresses,omitempty"`
			Auth      []string `yaml:"auth,omitempty"`
		}
	}
}

MultiProviderConf holds config for multi-provider DNSSEC (RFC 8901). Used by all three MP roles: agent, combiner, and signer. The Role field determines which role-specific fields are relevant. YAML key: "multi-provider:"

func (*MultiProviderConf) FindAgent

func (c *MultiProviderConf) FindAgent(identity string) *PeerConf

FindAgent returns the PeerConf for the agent with the given identity, or nil if not found.

type MultiSignerConf

type MultiSignerConf struct {
	Name       string
	Controller MultiSignerController
}

type MultiSignerController

type MultiSignerController struct {
	Name   string
	Notify MSCNotifyConf
	API    MSCAPIConf
}

type MultiSignerPost

type MultiSignerPost struct {
	Command string // "fetch-rrset" | "update" | "remove-rrset"
	Zone    string
	Name    string
	Type    uint16
}

type MultiSignerResponse

type MultiSignerResponse struct {
	AppName  string
	Time     time.Time
	RRset    core.RRset
	Msg      string
	Error    bool
	ErrorMsg string
}

type MusicSyncRequest

type MusicSyncRequest struct {
	Command         string
	ZoneName        string
	ZoneData        *ZoneData
	OldDnskeys      *core.RRset
	NewDnskeys      *core.RRset
	MusicSyncStatus *MusicSyncStatus
	Response        chan MusicSyncStatus // used for API-based requests
}

type MusicSyncStatus

type MusicSyncStatus struct {
	ZoneName       string
	MsignerAdds    []dns.RR
	MsignerRemoves []dns.RR
	DnskeyAdds     []dns.RR
	DnskeyRemoves  []dns.RR
	Msg            string
	Error          bool
	ErrorMsg       string
	Status         bool
}

type NotifyHandlerFunc

type NotifyHandlerFunc func(ctx context.Context, req *DnsNotifyRequest) error

NotifyHandlerFunc is the function signature for registered NOTIFY handlers. Returns ErrNotHandled if the handler doesn't handle this NOTIFY (allows fallthrough). Returns nil if the handler successfully handled the NOTIFY. Returns other error if handler attempted to handle but encountered an error.

type NotifyRequest

type NotifyRequest struct {
	ZoneName string
	ZoneData *ZoneData
	RRtype   uint16
	Targets  []string // []addr:port
	Urgent   bool
	Response chan NotifyResponse
}

type NotifyResponse

type NotifyResponse struct {
	Msg      string
	Rcode    int
	Error    bool
	ErrorMsg string
}

type NotifyStatus

type NotifyStatus struct {
	Zone          string // zone that the update applies to
	ChildZone     string // zone that the update applies to
	Type          uint16 // CDS | CSYNC | DNSKEY | DELEG
	ScanStatus    string // "ok" | "changed" | "failed"
	SafetyChecked bool   // true if the update has been safety checked
	PolicyChecked bool   // true if the update has been policy checked
	Approved      bool   // true if the update has been approved
	Msg           string
	Error         bool
	ErrorMsg      string
	Status        bool
}

type OwnerData

type OwnerData struct {
	Name    string
	RRtypes *RRTypeStore
}

func NewOwnerData

func NewOwnerData(name string) *OwnerData

type Owners

type Owners []OwnerData

func (Owners) Len

func (owners Owners) Len() int

func (Owners) Less

func (owners Owners) Less(i, j int) bool

func (Owners) Swap

func (owners Owners) Swap(i, j int)

type ParentSyncStatus

type ParentSyncStatus struct {
	Zone            ZoneName        `json:"zone"`
	Leader          AgentId         `json:"leader"`
	LeaderExpiry    time.Time       `json:"leader_expiry"`
	ElectionTerm    uint64          `json:"election_term"`
	IsLeader        bool            `json:"is_leader"`
	KeyAlgorithm    string          `json:"key_algorithm,omitempty"`
	KeyID           uint16          `json:"key_id,omitempty"`
	KeyRR           string          `json:"key_rr,omitempty"`
	ApexPublished   bool            `json:"apex_published"`
	ParentState     uint8           `json:"parent_state"`
	ParentStateName string          `json:"parent_state_name,omitempty"`
	ChildNS         []string        `json:"child_ns,omitempty"`
	KeyPublication  map[string]bool `json:"key_publication,omitempty"`
	LastChecked     time.Time       `json:"last_checked"`

	// Sync scheme info from parent DSYNC discovery
	ParentZone   string            `json:"parent_zone,omitempty"`
	SyncSchemes  []DsyncSchemeInfo `json:"sync_schemes,omitempty"`
	ActiveScheme string            `json:"active_scheme,omitempty"` // best scheme: "UPDATE", "NOTIFY", etc.

	// CDS/CSYNC publication status
	CdsPublished   bool `json:"cds_published"`
	CsyncPublished bool `json:"csync_published"`
	ZoneSigned     bool `json:"zone_signed"`

	// Peer agents for this zone
	Peers []PeerSyncInfo `json:"peers,omitempty"`
}

ParentSyncStatus holds on-demand status information about parent delegation sync for a zone.

type PeerConf

type PeerConf struct {
	Address            string `yaml:"address"`
	LongTermJosePubKey string `yaml:"long_term_jose_pub_key"`
	ApiBaseUrl         string `yaml:"api_base_url,omitempty"` // Optional: for API transport (e.g. https://combiner:8085/api/v1)
	Identity           string `yaml:"identity"`               // Peer identity (FQDN); required for combiner agents, optional for agent combiner
}

PeerConf holds address and public key path for the other party (agent or combiner).

type PeerInfo

type PeerInfo struct {
	PeerID       string
	PeerType     string // "combiner" or "agent"
	Transport    string // "API" or "DNS"
	Address      string
	CryptoType   string    // "JOSE" or "HPKE" (for DNS), "TLS" (for API), or "-"
	DistribSent  int       // Number of distributions sent to this peer (deprecated - use TotalReceived)
	LastUsed     time.Time // Last time this peer was used
	Addresses    []string  // IP addresses from discovery
	Port         uint16    // Port number
	JWKData      string    // JWK data if available
	KeyAlgorithm string    // Key algorithm (e.g., "ES256")
	HasJWK       bool      // Whether JWK is available
	HasKEY       bool      // Whether KEY record is available
	HasTLSA      bool      // Whether TLSA record is available
	APIUri       string    // Full API URI
	DNSUri       string    // Full DNS URI
	Partial      bool      // Whether discovery was partial
	State        string    // Agent state
	ContactInfo  string    // Contact info status

	// Per-message-type statistics
	HelloSent     uint64
	HelloReceived uint64
	BeatSent      uint64
	BeatReceived  uint64
	SyncSent      uint64
	SyncReceived  uint64
	PingSent      uint64
	PingReceived  uint64
	TotalSent     uint64
	TotalReceived uint64
}

PeerInfo holds information about a peer agent with established keys

type PeerRecord

type PeerRecord struct {
	ID                   int64
	PeerID               string
	DiscoveryTime        time.Time
	DiscoverySource      string
	APIEndpoint          string
	APIHost              string
	APIPort              int
	APITlsaRecord        string
	APIAvailable         bool
	DNSHost              string
	DNSPort              int
	DNSKeyRecord         string
	DNSAvailable         bool
	OperationalHost      string
	OperationalPort      int
	OperationalTransport string
	EncryptionPubkey     string
	VerificationPubkey   string
	State                string
	StateReason          string
	StateChangedAt       time.Time
	PreferredTransport   string
	LastContactAt        time.Time
	LastHelloAt          time.Time
	LastBeatAt           time.Time
	BeatInterval         int
	BeatsSent            int64
	BeatsReceived        int64
	FailedContacts       int
	CreatedAt            time.Time
	UpdatedAt            time.Time
}

PeerRecord represents a row in the PeerRegistry table.

func PeerRecordFromAgent

func PeerRecordFromAgent(agent *Agent) *PeerRecord

PeerRecordFromAgent creates a PeerRecord from an Agent.

func PeerRecordFromTransportPeer

func PeerRecordFromTransportPeer(peer *transport.Peer) *PeerRecord

PeerRecordFromTransportPeer creates a PeerRecord from a transport.Peer.

type PeerSyncInfo

type PeerSyncInfo struct {
	Identity    AgentId `json:"identity"`
	State       string  `json:"state"`
	Transport   string  `json:"transport"`
	Operational bool    `json:"operational"`
}

PeerSyncInfo describes a peer agent's status for a zone.

type PendingDnskeyPropagation

type PendingDnskeyPropagation struct {
	Zone           ZoneName
	DistributionID string
	KeyTags        []uint16         // DNSKEY key tags being propagated
	ExpectedAgents map[AgentId]bool // Agents we're waiting for (true = confirmed)
	Rejected       bool             // True if any agent rejected
	RejectionMsg   string           // First rejection reason
	CreatedAt      time.Time
}

PendingDnskeyPropagation tracks a DNSKEY distribution awaiting confirmation from all remote agents.

type PendingEditRecord

type PendingEditRecord struct {
	EditID         int                 `json:"edit_id"`
	Zone           string              `json:"zone"`
	SenderID       string              `json:"sender_id"`
	DeliveredBy    string              `json:"delivered_by"`
	DistributionID string              `json:"distribution_id"`
	Records        map[string][]string `json:"records"`
	ReceivedAt     time.Time           `json:"received_at"`
}

PendingEditRecord represents a row in the CombinerPendingEdits table.

type PendingRemoteConfirmation

type PendingRemoteConfirmation struct {
	OriginatingDistID string
	OriginatingSender string
	Zone              ZoneName
	CreatedAt         time.Time
}

PendingRemoteConfirmation tracks the relationship between a combiner distID (generated locally on the remote agent) and the originating agent's distID/identity.

type PingPost

type PingPost struct {
	Msg   string
	Pings int
}

type PingResponse

type PingResponse struct {
	Time       time.Time
	Client     string
	BootTime   time.Time
	Version    string
	ServerHost string // "master.dnslab"
	Daemon     string // "tdnsd"
	Msg        string
	Pings      int
	Pongs      int
}

type PrivateKeyCache

type PrivateKeyCache struct {
	K          crypto.PrivateKey
	PrivateKey string // This is only used when reading from file with ReadKeyNG()
	CS         crypto.Signer
	RR         dns.RR
	KeyType    uint16
	Algorithm  uint8
	KeyId      uint16
	KeyRR      dns.KEY
	DnskeyRR   dns.DNSKEY
}

Migrating all DB access to own interface to be able to have local receiver functions.

func LoadSig0SigningKey

func LoadSig0SigningKey(keyfile string) (*PrivateKeyCache, error)

func PrepareKeyCache

func PrepareKeyCache(privkey, pubkey string) (*PrivateKeyCache, error)

func ReadPrivateKey

func ReadPrivateKey(filename string) (*PrivateKeyCache, error)

type ProviderGroup

type ProviderGroup struct {
	GroupHash    string             // truncated SHA-256 of sorted member identities
	Name         string             // human-friendly name (resolved via naming protocol)
	Members      []string           // sorted provider identities (FQDNs)
	Zones        []ZoneName         // zones served by this exact set of providers
	NameProposal *GroupNameProposal // our proposal for this group's name
}

ProviderGroup represents a set of providers that together serve a group of zones. The group is identified by a hash of the sorted member identities.

func (*ProviderGroup) GroupSummary

func (pg *ProviderGroup) GroupSummary() string

GroupSummary returns a compact string representation for logging.

type ProviderGroupManager

type ProviderGroupManager struct {
	Groups  map[string]*ProviderGroup // key: group hash
	LocalID string                    // our own identity
	// contains filtered or unexported fields
}

ProviderGroupManager manages provider group computation and naming.

func NewProviderGroupManager

func NewProviderGroupManager(localIdentity string) *ProviderGroupManager

NewProviderGroupManager creates a new provider group manager.

func (*ProviderGroupManager) GetGroup

func (pgm *ProviderGroupManager) GetGroup(groupHash string) *ProviderGroup

GetGroup returns a specific provider group by hash.

func (*ProviderGroupManager) GetGroupByName

func (pgm *ProviderGroupManager) GetGroupByName(name string) *ProviderGroup

GetGroupByName returns a provider group by its human-friendly name.

func (*ProviderGroupManager) GetGroupForZone

func (pgm *ProviderGroupManager) GetGroupForZone(zone ZoneName) *ProviderGroup

GetGroupForZone returns the provider group that contains the given zone.

func (*ProviderGroupManager) GetGroups

func (pgm *ProviderGroupManager) GetGroups() []*ProviderGroup

GetGroups returns a snapshot of all current provider groups.

func (*ProviderGroupManager) GetGroupsForIdentity

func (pgm *ProviderGroupManager) GetGroupsForIdentity(identity string) []*ProviderGroup

GetGroupsForIdentity returns all groups that include the given identity.

func (*ProviderGroupManager) ProposeGroupName

func (pgm *ProviderGroupManager) ProposeGroupName(groupHash, name string)

ProposeGroupName sets our name proposal for a group.

func (*ProviderGroupManager) RecomputeGroups

func (pgm *ProviderGroupManager) RecomputeGroups()

RecomputeGroups scans all loaded zones, extracts HSYNC3 identity sets, and rebuilds the provider group map. This is a pure function of zone data.

type ProviderZoneConf

type ProviderZoneConf struct {
	Zone           string   `yaml:"zone"`
	AllowedRRtypes []string `yaml:"allowed-rrtypes" mapstructure:"allowed-rrtypes"`
}

ProviderZoneConf configures a provider-owned zone that the combiner manages. Unlike MP zones (hardcoded RRtype whitelist, apex-only), provider zones use config-driven RRtype restrictions and allow non-apex record owners.

type QueryHandlerFunc

type QueryHandlerFunc func(ctx context.Context, req *DnsQueryRequest) error

QueryHandlerFunc is the function signature for registered query handlers. Returns ErrNotHandled if the handler doesn't handle this query (allows fallthrough). Returns nil if the handler successfully handled the query. Returns other error if handler attempted to handle but encountered an error.

type RRConfirmation

type RRConfirmation struct {
	Status    string    `json:"status"`           // "accepted", "rejected", "removed", "pending"
	Reason    string    `json:"reason,omitempty"` // rejection reason
	Timestamp time.Time `json:"timestamp"`
}

RRConfirmation records a single recipient's confirmation status for a tracked RR.

type RRState

type RRState uint8

RRState represents the lifecycle state of a tracked RR.

const (
	RRStatePending        RRState = iota // Sent to combiner, awaiting confirmation
	RRStateAccepted                      // Combiner accepted
	RRStateRejected                      // Combiner rejected (see Reason)
	RRStatePendingRemoval                // Delete sent to combiner, awaiting confirmation
	RRStateRemoved                       // Combiner confirmed removal (audit trail)
)

func (RRState) String

func (s RRState) String() string

type RRTypeStore

type RRTypeStore struct {
	// contains filtered or unexported fields
}

func NewRRTypeStore

func NewRRTypeStore() *RRTypeStore

func (*RRTypeStore) Count

func (s *RRTypeStore) Count() int

func (*RRTypeStore) Delete

func (s *RRTypeStore) Delete(key uint16)

func (*RRTypeStore) Get

func (s *RRTypeStore) Get(key uint16) (core.RRset, bool)

func (*RRTypeStore) GetOnlyRRSet

func (s *RRTypeStore) GetOnlyRRSet(key uint16) core.RRset

GetOnlyRRSet returns the RRset for key, or zero-value RRset if not found.

func (*RRTypeStore) Keys

func (s *RRTypeStore) Keys() []uint16

func (*RRTypeStore) Set

func (s *RRTypeStore) Set(key uint16, value core.RRset)

type RRsetString

type RRsetString struct {
	Name   string   `json:"name"`
	RRtype uint16   `json:"rrtype"`
	RRs    []string `json:"rrs"`
	RRSIGs []string `json:"rrsigs,omitempty"`
}

String-based versions of RRset for JSON marshaling

type RefreshCounter

type RefreshCounter struct {
	Name           string
	SOARefresh     uint32
	CurRefresh     uint32
	IncomingSerial uint32
	Upstream       string
	Downstreams    []string
	Zonefile       string
}

type RefresherResponse

type RefresherResponse struct {
	Time     time.Time
	Zone     string
	Msg      string
	Error    bool
	ErrorMsg string
}

type RejectedEditRecord

type RejectedEditRecord struct {
	EditID         int                 `json:"edit_id"`
	Zone           string              `json:"zone"`
	SenderID       string              `json:"sender_id"`
	DistributionID string              `json:"distribution_id"`
	Records        map[string][]string `json:"records"`
	ReceivedAt     time.Time           `json:"received_at"`
	RejectedAt     time.Time           `json:"rejected_at"`
	Reason         string              `json:"reason"`
}

RejectedEditRecord represents a row in the CombinerRejectedEdits table.

type RejectedItem

type RejectedItem struct {
	Record string // The RR string
	Reason string // Why it was rejected
}

RejectedItem describes an RR that was rejected and why.

type RejectedItemInfo

type RejectedItemInfo struct {
	Record string
	Reason string
}

RejectedItemInfo describes an RR rejected by the combiner.

type RemoteConfirmationDetail

type RemoteConfirmationDetail struct {
	OriginatingDistID string
	OriginatingSender string
	Zone              ZoneName
	Status            string
	Message           string
	AppliedRecords    []string
	RemovedRecords    []string
	RejectedItems     []RejectedItemInfo
	Truncated         bool
}

RemoteConfirmationDetail carries per-RR confirmation detail to send back to the originating agent after the remote agent's combiner confirms.

type RfiData

type RfiData struct {
	Status      string // ok | error | ...
	Time        time.Time
	Msg         string
	Error       bool
	ErrorMsg    string
	ZoneXfrSrcs []string
	ZoneXfrAuth []string
	ZoneXfrDsts []string
	AuditData   map[ZoneName]map[AgentId]map[uint16][]TrackedRRInfo `json:"audit_data,omitempty"`
	ConfigData  map[string]string                                   `json:"config_data,omitempty"` // key-value config data for RFI CONFIG
}

RfiData is defined in core package to avoid circular dependencies.

type ScanJobStatus

type ScanJobStatus struct {
	JobID           string              `json:"job_id"`
	Status          string              `json:"status"` // "queued", "processing", "completed", "failed"
	CreatedAt       time.Time           `json:"created_at"`
	StartedAt       *time.Time          `json:"started_at,omitempty"`
	CompletedAt     *time.Time          `json:"completed_at,omitempty"`
	TotalTuples     int                 `json:"total_tuples"`
	IgnoredTuples   int                 `json:"ignored_tuples"`
	ErrorTuples     int                 `json:"error_tuples"`
	ProcessedTuples int                 `json:"processed_tuples"`
	Responses       []ScanTupleResponse `json:"responses,omitempty"`
	Error           bool                `json:"error,omitempty"`
	ErrorMsg        string              `json:"error_msg,omitempty"`
}

ScanJobStatus represents the status of a scan job

type ScanRequest

type ScanRequest struct {
	Cmd              string
	ParentZone       string
	ScanZones        []string
	ScanType         ScanType // "cds" | "csync" | "dnskey"
	ScanTuples       []ScanTuple
	ChildZone        string
	CurrentChildData ChildDelegationData // Current parent-side delegation data for child
	ZoneData         *ZoneData
	RRtype           uint16
	Edns0Options     *edns0.MsgOptions
	Response         chan ScanResponse
	JobID            string // Job ID for async processing
}

type ScanResponse

type ScanResponse struct {
	Time     time.Time
	Zone     string
	RRtype   uint16
	RRset    core.RRset
	Msg      string
	Error    bool
	ErrorMsg string
}

type ScanTuple

type ScanTuple struct {
	Zone string // zone to scan	//
	// Type ScanType	// type of test to perform	// ScanRRtype | ScanCSYNC | ScanDNSKEY
	CurrentData CurrentScanData // current data for the test
	Options     []string        // "all-ns", ...
}

type ScanTupleResponse

type ScanTupleResponse struct {
	Qname       string              // The qname that was queried
	ScanType    ScanType            // The type of scan performed
	Options     []string            // Options that were used (e.g., "all-ns")
	NewData     CurrentScanDataJSON // The new data retrieved from the scan (JSON-serializable)
	DataChanged bool                // Whether the new data differs from the old data (from ScanTuple.CurrentData)
	AllNSInSync bool                // If "all-ns" option was set, whether all NS were in sync (false if not applicable)
	DSAdds      []dns.RR            // DS records to add to parent (from CDS→DS conversion)
	DSRemoves   []dns.RR            // DS records to remove from parent
	NSAdds      []dns.RR            // NS records to add at child apex (from CSYNC)
	NSRemoves   []dns.RR            // NS records to remove from child apex (from CSYNC)
	GlueAdds    []dns.RR            // A/AAAA glue records to add (owner in RR header)
	GlueRemoves []dns.RR            // A/AAAA glue records to remove
	Error       bool                // Whether an error occurred
	ErrorMsg    string              // Error message if Error is true
}

ScanTupleResponse contains the result of scanning a single ScanTuple

type ScanType

type ScanType uint8

ScanType represents the type of test to perform during scanning

const (
	ScanRRtype ScanType = iota + 1
	ScanCDS
	ScanCSYNC
	ScanDNSKEY
)

type Scanner

type Scanner struct {
	AuthQueryQ         chan AuthQueryRequest
	ImrEngine          *Imr
	Options            []string
	AtApexChecks       int
	AtApexInterval     time.Duration
	OnDelegationChange func(parentZone string, zd *ZoneData, resp ScanTupleResponse)
	LogFile            string
	LogTemplate        string
	Log                map[string]*log.Logger
	Verbose            bool
	Debug              bool
	Jobs               map[string]*ScanJobStatus
	JobsMutex          sync.RWMutex
}

func NewScanner

func NewScanner(authqueryq chan AuthQueryRequest, verbose, debug bool) *Scanner

func (*Scanner) AddLogger

func (scanner *Scanner) AddLogger(rrtype string) error

func (*Scanner) AuthQueryNG

func (scanner *Scanner) AuthQueryNG(qname, ns string, rrtype uint16, transport string) (*core.RRset, error)

func (*Scanner) CheckCDS

func (scanner *Scanner) CheckCDS(ctx context.Context, tuple ScanTuple, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

func (*Scanner) CheckCSYNC

func (scanner *Scanner) CheckCSYNC(sr ScanRequest, cdd *ChildDelegationData) (*ChildDelegationData, error)

func (*Scanner) CheckDNSKEY

func (scanner *Scanner) CheckDNSKEY(ctx context.Context, tuple ScanTuple, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

func (*Scanner) CsyncAnalyzeA

func (scanner *Scanner) CsyncAnalyzeA(zone string, new_nsrrs []*dns.NS, cdd *ChildDelegationData) ([]dns.RR, bool, error)

func (*Scanner) CsyncAnalyzeAAAA

func (scanner *Scanner) CsyncAnalyzeAAAA(zone string, new_nsrrs []*dns.NS, cdd *ChildDelegationData) ([]dns.RR, bool, error)

func (*Scanner) CsyncAnalyzeNS

func (scanner *Scanner) CsyncAnalyzeNS(zone string, cdd *ChildDelegationData) ([]dns.RR, bool, error)

Returns: new_rrs, changed=true, error

func (*Scanner) HasOption

func (scanner *Scanner) HasOption(name string) bool

func (*Scanner) ProcessCDSNotify

func (scanner *Scanner) ProcessCDSNotify(ctx context.Context, tuple ScanTuple, parentZD *ZoneData, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

ProcessCDSNotify handles a CDS NOTIFY by querying CDS from child nameservers, converting CDS→DS, and diffing against current DS. The scanner is read-only: results (DSAdds/DSRemoves) are returned in the ScanTupleResponse for the caller to act on.

func (*Scanner) ProcessCSYNCNotify

func (scanner *Scanner) ProcessCSYNCNotify(ctx context.Context, tuple ScanTuple, parentZD *ZoneData, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

ProcessCSYNCNotify handles a CSYNC NOTIFY by querying CSYNC, NS, and glue from child nameservers, diffing against current delegation data, and reporting NS/glue adds/removes in the ScanTupleResponse. The scanner is read-only: results are returned for the caller to act on. Follows RFC 7477 processing algorithm.

func (*Scanner) UpdateCsyncStatus

func (scanner *Scanner) UpdateCsyncStatus(zone string, csyncrr *dns.CSYNC) error

func (*Scanner) ZoneCSYNCKnown

func (scanner *Scanner) ZoneCSYNCKnown(zone string, csyncrr *dns.CSYNC) bool

type ScannerPost

type ScannerPost struct {
	Command    string      // "scan" | "status"
	ParentZone string      // Legacy field
	ScanZones  []string    // Legacy field
	ScanType   ScanType    // Legacy field: "cds" | "csync" | "dnskey"
	ScanTuples []ScanTuple // New field: list of scan tuples for scan requests
}

type ScannerResponse

type ScannerResponse struct {
	AppName  string
	Time     time.Time
	Status   string
	Msg      string
	Error    bool
	ErrorMsg string
	JobID    string `json:"job_id,omitempty"` // Job ID for async scan requests
}

type SensitiveString

type SensitiveString string

SensitiveString wraps a string that should not appear in logs. Use .Value() to get the actual string; String() returns a redacted form.

func (SensitiveString) String

func (s SensitiveString) String() string

String returns a redacted representation for safe logging.

func (SensitiveString) Value

func (s SensitiveString) Value() string

Value returns the actual string value.

type ServerAddrTuple

type ServerAddrTuple struct {
	Server *cache.AuthServer
	Addr   string
	NSName string
}

ServerAddrTuple represents a (server, address) pair for prioritization

type ServiceConf

type ServiceConf struct {
	Name       string `validate:"required"`
	Debug      *bool
	Verbose    *bool
	Identities []string      // this is a strawman attempt at deciding on what name to publish the ALPN
	Transport  TransportConf `yaml:"transport"`
}

type ShowAPIresponse

type ShowAPIresponse struct {
	Status int
	Msg    string
	Data   []string
}

type Sig0ActiveKeys

type Sig0ActiveKeys struct {
	Keys []*PrivateKeyCache
}

type Sig0Key

type Sig0Key struct {
	Name            string
	State           string
	Keyid           uint16
	Algorithm       string
	Creator         string
	Validated       bool   // has this key been validated
	PublishedInDNS  bool   // is this key published in DNS (as a KEY RR)
	DnssecValidated bool   // has this key been DNSSEC validated
	Trusted         bool   // is this key trusted
	Source          string // "dns" | "file" | "keystore" | "child-update"
	PrivateKey      string //
	Key             dns.KEY
	Keystr          string
}

type Sig0StoreT

type Sig0StoreT struct {
	Map *core.ConcurrentMap[string, Sig0Key]
}

func NewSig0StoreT

func NewSig0StoreT() *Sig0StoreT

type Sig0UpdateSigner

type Sig0UpdateSigner struct {
	Name      string   // from the SIG
	KeyId     uint16   // from the SIG
	Sig0Key   *Sig0Key // a key that matches the signer name and keyid
	Validated bool     // true if this key validated the update
}

A Signer is a struct where we keep track of the signer name and keyid for a DNS UPDATE message.

type Sig0tmp

type Sig0tmp map[string]TmpSig0Key

type SignerOption

type SignerOption uint8

type SigningGroupInfo

type SigningGroupInfo struct {
	Description string `yaml:"description" mapstructure:"description"`
}

SigningGroupInfo provides documentation for signing groups (RFC 9432 terminology)

type StatusUpdateMsg

type StatusUpdateMsg struct {
	SenderID  string
	Zone      string
	SubType   string
	NSRecords []string
	DSRecords []string
	Result    string
	Msg       string
}

StatusUpdateMsg carries a status-update notification. Delivered via MsgQs.StatusUpdate channel. Subtypes: "ns-changed", "ksk-changed", "parentsync-done".

type StoredPublishInstruction

type StoredPublishInstruction struct {
	Zone        string
	SenderID    string
	KEYRRs      []string
	CDSRRs      []string
	Locations   []string
	PublishedNS []string // NS targets with currently active _signal KEYs
	UpdatedAt   time.Time
}

StoredPublishInstruction is the persisted form of a PublishInstruction, augmented with the set of NS targets that currently have active _signal KEYs.

func (*StoredPublishInstruction) ToPublishInstruction

func (s *StoredPublishInstruction) ToPublishInstruction() *core.PublishInstruction

ToPublishInstruction converts back to the wire-format struct.

type SyncConfirmationRecord

type SyncConfirmationRecord struct {
	ID                 int64
	DistributionID     string
	ConfirmerID        string
	Status             string
	Message            string
	ItemsProcessed     []ConfirmationItem
	SignedProof        string
	ConfirmerSignature string
	ConfirmedAt        time.Time
	ReceivedAt         time.Time
}

SyncConfirmationRecord represents a row in the SyncConfirmations table.

type SyncOperationRecord

type SyncOperationRecord struct {
	ID             int64
	DistributionID string
	ZoneName       string
	SyncType       string
	Direction      string
	SenderID       string
	ReceiverID     string
	Records        []string
	Serial         uint32
	Transport      string
	Encrypted      bool
	Status         string
	StatusMessage  string
	CreatedAt      time.Time
	SentAt         time.Time
	ReceivedAt     time.Time
	ConfirmedAt    time.Time
	ExpiresAt      time.Time
	RetryCount     int
	LastError      string
	LastErrorAt    time.Time
}

SyncOperationRecord represents a row in the SyncOperations table.

type SyncRequest

type SyncRequest struct {
	Command      string
	ZoneName     ZoneName
	ZoneData     *ZoneData
	SyncStatus   *HsyncStatus
	OldDnskeys   *core.RRset
	NewDnskeys   *core.RRset
	DnskeyStatus *DnskeyStatus // Local DNSKEY adds/removes (Phase 5)
	Response     chan SyncResponse
}

type SyncResponse

type SyncResponse struct {
	Status   bool
	Error    bool
	ErrorMsg string
	Msg      string
}

type SyncStatus

type SyncStatus struct {
	Identity AgentId
	Agents   map[AgentId]*Agent
	Error    bool
	Response chan SyncStatus
}

type SynchedDataCmd

type SynchedDataCmd struct {
	Cmd         string
	Zone        ZoneName
	TargetAgent AgentId // For "resync-targeted": send only to this agent
	Response    chan *SynchedDataCmdResponse
}

type SynchedDataCmdResponse

type SynchedDataCmdResponse struct {
	Cmd      string
	Msg      string
	Error    bool
	ErrorMsg string
	Zone     ZoneName
	ZDR      map[ZoneName]map[AgentId]map[uint16][]TrackedRRInfo
}

type SynchedDataResponse

type SynchedDataResponse struct {
	Zone    ZoneName
	AgentId AgentId
	Time    time.Time
	Msg     string
	// RfiType     string
	RfiResponse RfiData
	Error       bool
	ErrorMsg    string
}

type SynchedDataUpdate

type SynchedDataUpdate struct {
	Zone              ZoneName
	AgentId           AgentId
	UpdateType        string // "local" or "remote"
	Update            *ZoneUpdate
	OriginatingDistID string   // Distribution ID from the originating agent (for remote updates)
	Force             bool     // Bypass dedup check (always send even if RR already present)
	SkipCombiner      bool     // Don't send to combiner (e.g. local DNSKEY changes — signer adds its own)
	DnskeyKeyTags     []uint16 // Key tags for DNSKEY propagation tracking (mpdist flow)
	// Response chan *SynchedDataResponse
	Response chan *AgentMsgResponse
}

type TAtmp

type TAtmp map[string]TmpAnchor

XXX: These should die

type TargetUpdateStatus

type TargetUpdateStatus struct {
	Sender     string
	Rcode      int
	Error      bool
	ErrorMsg   string
	EDEFound   bool
	EDECode    uint16
	EDEMessage string
}

type TemplateConf

type TemplateConf struct {
	Name         string `validate:"required"`
	Zonefile     string
	Type         string
	Store        string
	Primary      string // upstream, for secondary zones
	Notify       []string
	OptionsStrs  []string `yaml:"options"`
	UpdatePolicy UpdatePolicyConf
	DnssecPolicy string
	MultiSigner  string
}

type TmpAnchor

type TmpAnchor struct {
	Name   string
	Dnskey string
}

type TmpSig0Key

type TmpSig0Key struct {
	Name string
	Key  string
}

type TrackedRR

type TrackedRR struct {
	RR                 dns.RR
	State              RRState
	Reason             string // Rejection reason (empty unless rejected)
	DistributionID     string // Last distribution this RR was part of
	UpdatedAt          time.Time
	Confirmations      map[string]RRConfirmation // recipientID → per-recipient status
	ExpectedRecipients []string                  // Who must confirm before state transitions to accepted
}

TrackedRR wraps a dns.RR with lifecycle state for combiner confirmation tracking.

type TrackedRRInfo

type TrackedRRInfo struct {
	RR             string                    `json:"rr"`
	State          string                    `json:"state"`
	KeyState       string                    `json:"key_state,omitempty"`
	Reason         string                    `json:"reason,omitempty"`
	DistributionID string                    `json:"distribution_id"`
	UpdatedAt      string                    `json:"updated_at"`
	Confirmations  map[string]RRConfirmation `json:"confirmations,omitempty"`
}

TrackedRRInfo is the JSON-serializable form for dump output.

type TrackedRRset

type TrackedRRset struct {
	Tracked []TrackedRR
}

TrackedRRset holds a set of tracked RRs for a single RRtype.

type TransactionErrorSummary

type TransactionErrorSummary struct {
	DistributionID string `json:"distribution_id"`
	Age            string `json:"age"`
	Sender         string `json:"sender"`
	MessageType    string `json:"message_type"`
	ErrorMsg       string `json:"error_msg"`
	QNAME          string `json:"qname"`
	Timestamp      string `json:"timestamp"`
}

TransactionErrorSummary contains summary information about a transaction error

type TransactionPost

type TransactionPost struct {
	Command string `json:"command"`           // "open-outgoing", "open-incoming", "errors", "error-details"
	Last    string `json:"last,omitempty"`    // Duration filter for errors (e.g. "30m", "2h")
	DistID  string `json:"dist_id,omitempty"` // For error-details: specific distribution ID
}

TransactionPost represents a request to the transaction API

type TransactionResponse

type TransactionResponse struct {
	Time         time.Time                  `json:"time"`
	Error        bool                       `json:"error,omitempty"`
	ErrorMsg     string                     `json:"error_msg,omitempty"`
	Msg          string                     `json:"msg,omitempty"`
	Transactions []*TransactionSummary      `json:"transactions,omitempty"`
	Errors       []*TransactionErrorSummary `json:"errors,omitempty"`
	ErrorDetail  *TransactionErrorSummary   `json:"error_detail,omitempty"`
}

TransactionResponse represents a response from the transaction API

type TransactionSummary

type TransactionSummary struct {
	DistributionID string `json:"distribution_id"`
	Peer           string `json:"peer"` // Receiver (outgoing) or Sender (incoming)
	Operation      string `json:"operation"`
	Zone           string `json:"zone,omitempty"`
	Age            string `json:"age"`
	CreatedAt      string `json:"created_at"`
	State          string `json:"state,omitempty"`
}

TransactionSummary contains summary information about an open transaction

type TransportConf

type TransportConf struct {
	Type   string `yaml:"type" validate:"omitempty,oneof=tsync svcb none"`
	Signal string `yaml:"signal"`
}

type TruststorePost

type TruststorePost struct {
	Command         string // "sig0"
	SubCommand      string // "list-child-keys" | "trust-child-key" | "untrust-child-key"
	Zone            string
	Keyname         string
	Keyid           int
	Validated       bool
	DnssecValidated bool
	Trusted         bool
	Src             string // "dns" | "file"
	KeyRR           string // RR string for key
}

type TruststoreResponse

type TruststoreResponse struct {
	AppName       string
	Time          time.Time
	Status        string
	Zone          string
	ChildDnskeys  map[string]cache.CachedDnskeyRRset
	ChildSig0keys map[string]Sig0Key
	Msg           string
	Error         bool
	ErrorMsg      string
}

type TsigDetails

type TsigDetails struct {
	Name      string `validate:"required" yaml:"name"`
	Algorithm string `validate:"required" yaml:"algorithm"`
	Secret    string `validate:"required" yaml:"secret"`
}

type Tx

type Tx struct {
	*sql.Tx
	KeyDB *KeyDB
	// contains filtered or unexported fields
}

func (*Tx) Commit

func (tx *Tx) Commit() error

func (*Tx) Exec

func (tx *Tx) Exec(query string, args ...interface{}) (sql.Result, error)

func (*Tx) Query

func (tx *Tx) Query(query string, args ...interface{}) (*sql.Rows, error)

func (*Tx) QueryRow

func (tx *Tx) QueryRow(query string, args ...interface{}) *sql.Row

func (*Tx) Rollback

func (tx *Tx) Rollback() error

type UpdateHandlerFunc

type UpdateHandlerFunc func(ctx context.Context, req *DnsUpdateRequest) error

UpdateHandlerFunc is the function signature for registered UPDATE handlers. Returns ErrNotHandled if the handler doesn't handle this UPDATE (allows fallthrough). Returns nil if the handler successfully handled the UPDATE. Returns other error if handler attempted to handle but encountered an error.

type UpdateHandlerRegistration

type UpdateHandlerRegistration struct {
	Matcher UpdateMatcherFunc
	Handler UpdateHandlerFunc
}

UpdateHandlerRegistration stores an UPDATE handler registration

type UpdateMatcherFunc

type UpdateMatcherFunc func(req *DnsUpdateRequest) bool

UpdateMatcherFunc is the function signature for matching UPDATE messages. Returns true if the UPDATE should be handled by the associated handler. The matcher receives the DnsUpdateRequest and can inspect: - req.Qname (zone name from question section) - req.Msg.Ns (update section RRs) - req.Msg.Extra (additional section, e.g., SIG(0)) - req.Options (EDNS0 options)

type UpdatePolicy

type UpdatePolicy struct {
	Child    UpdatePolicyDetail
	Zone     UpdatePolicyDetail
	Validate bool
}

type UpdatePolicyConf

type UpdatePolicyConf struct {
	Child struct {
		Type         string // selfsub | self | sub | none
		RRtypes      []string
		KeyBootstrap []string // manual | dnssec-validated | consistent-lookup
		KeyUpload    string
		TTL          uint32 `yaml:"ttl"`
	}
	Zone struct {
		Type    string // "selfsub" | "self" | "sub" | ...
		RRtypes []string
		TTL     uint32 `yaml:"ttl"`
	}
	Validate bool
}

type UpdatePolicyDetail

type UpdatePolicyDetail struct {
	Type         string // "selfsub" | "self"
	RRtypes      map[uint16]bool
	KeyBootstrap []string
	KeyUpload    string
	TTL          uint32
}

type UpdateRequest

type UpdateRequest struct {
	Cmd            string
	UpdateType     string // "DSYNC", "KEY", ...
	ZoneName       string
	Adds           []dns.RR
	Removes        []dns.RR
	Actions        []dns.RR // The Update section from the dns.Msg
	Validated      bool     // Signature over update msg is validated
	Trusted        bool     // Content of update is trusted (via validation or policy)
	InternalUpdate bool     // Internal update, not a DNS UPDATE from the outside
	Status         *UpdateStatus
	Description    string
	PreCondition   func() bool
	Action         func() error
}

type UpdateResult

type UpdateResult struct {
	EDEFound     bool
	EDECode      uint16
	EDEMessage   string
	EDESender    string
	Rcode        int
	TargetStatus map[string]TargetUpdateStatus
}

This is only called from the CLI command "tdns-cli ddns sync" and uses a SIG(0) key from the command line rather than the one in the keystore. Not to be used by TDNS-SERVER.

func SendUpdate

func SendUpdate(msg *dns.Msg, zonename string, addrs []string) (int, UpdateResult, error)

Note: the target.Addresses must already be in addr:port format. func SendUpdate(msg *dns.Msg, zonename string, target *DsyncTarget) (int, error) { func SendUpdate(msg *dns.Msg, zonename string, addrs []string) (int, error, UpdateResult) {

type UpdateStatus

type UpdateStatus struct {
	Zone                  string             // zone that the update applies to
	ChildZone             string             // zone that the update applies to
	Type                  string             // auth | child
	Data                  string             // auth | delegation | key
	ValidatorKey          *Sig0Key           // key that validated the update
	Signers               []Sig0UpdateSigner // possible validators
	SignerName            string             // name of the key that signed the update
	SignatureType         string             // by-trusted | by-known | self-signed
	ValidationRcode       uint8              // Rcode from the validation process
	Validated             bool               // true if the update has passed validation
	ValidatedByTrustedKey bool               // true if the update has passed validation by a trusted key
	SafetyChecked         bool               // true if the update has been safety checked
	PolicyChecked         bool               // true if the update has been policy checked
	Approved              bool               // true if the update has been approved
	Msg                   string
	Error                 bool
	ErrorMsg              string
	Status                bool
}

type ValidatorRequest

type ValidatorRequest struct {
	Qname    string
	RRset    *core.RRset
	Response chan ValidatorResponse
}

type ValidatorResponse

type ValidatorResponse struct {
	Validated bool
	Msg       string
}

type VerificationInfo

type VerificationInfo struct {
	KeyName        string
	Key            string
	ZoneName       string
	AttemptsLeft   int
	NextCheckTime  time.Time
	ZoneData       *ZoneData
	Keyid          uint16
	FailedAttempts int
}

type ZoneAgentData

type ZoneAgentData struct {
	ZoneName      ZoneName
	Agents        []*Agent
	MyUpstream    AgentId
	MyDownstreams []AgentId
}

type ZoneConf

type ZoneConf struct {
	Name              string `validate:"required"`
	Zonefile          string
	Type              string `validate:"required"`
	Store             string // xfr | map | slice | reg (defaults to "map" if not specified)
	Primary           string // upstream, for secondary zones
	Notify            []string
	Downstreams       []string
	OptionsStrs       []string     `yaml:"options" mapstructure:"options"`
	Options           []ZoneOption `yaml:"-" mapstructure:"-"` // Ignore during both yaml and mapstructure decoding
	Frozen            bool         // true if zone is frozen; not a config param
	Dirty             bool         // true if zone has been modified; not a config param
	UpdatePolicy      UpdatePolicyConf
	DelegationBackend string `yaml:"delegation-backend" mapstructure:"delegation-backend"` // named backend for child delegation data
	DnssecPolicy      string
	Template          string
	MultiSigner       string
	Error             bool      // zone is broken and cannot be used
	ErrorType         ErrorType // "config" | "refresh" | "agent" | "DNSSEC"
	ErrorMsg          string    // reason for the error (if known)
	RefreshCount      int       // number of times the zone has been sucessfully refreshed (used to determine if we have zonedata)
	SourceCatalog     string    // if auto-configured, which catalog zone created this zone
}

ZoneConf represents the external config for a zone; it contains no zone data

func ExpandTemplate

func ExpandTemplate(zconf ZoneConf, tmpl *ZoneConf, appMode AppType) (ZoneConf, error)

type ZoneData

type ZoneData struct {
	ZoneName   string
	ZoneStore  ZoneStore // 1 = "xfr", 2 = "map", 3 = "slice". An xfr zone only supports xfr related ops
	ZoneType   ZoneType
	Owners     Owners
	OwnerIndex *core.ConcurrentMap[string, int]
	ApexLen    int
	//	RRs            RRArray
	Data  *core.ConcurrentMap[string, OwnerData]
	MP    *ZoneMPExtension // Multi-provider state; nil for non-MP zones
	Ready bool             // true if zd.Data has been populated (from file or upstream)

	XfrType string // axfr | ixfr
	Logger  *log.Logger
	// ZoneFile           string // TODO: Remove this
	IncomingSerial     uint32 // SOA serial that we got from upstream
	CurrentSerial      uint32 // SOA serial after local bumping
	FirstZoneLoad      bool   // true until first zone data has been loaded
	Verbose            bool
	Debug              bool
	IxfrChain          []Ixfr
	Upstream           string   // primary from where zone is xfrred
	Downstreams        []string // secondaries that we notify
	Zonefile           string
	DelegationSyncQ    chan DelegationSyncRequest
	MusicSyncQ         chan MusicSyncRequest // Multi-signer (communication between music-sidecars)
	Parent             string                // name of parentzone (if filled in)
	ParentNS           []string              // names of parent nameservers
	ParentServers      []string              // addresses of parent nameservers
	Children           map[string]*ChildDelegationData
	DelegationBackend  DelegationBackend // parent-side: backend for storing child delegation data
	Options            map[ZoneOption]bool
	UpdatePolicy       UpdatePolicy
	DnssecPolicy       *DnssecPolicy
	MultiSigner        *MultiSignerConf
	KeyDB              *KeyDB
	AppType            AppType
	SyncQ              chan SyncRequest
	Error              bool        // zone is broken and cannot be used
	ErrorType          ErrorType   // "config" | "refresh" | "notify" | "update"
	ErrorMsg           string      // reason for the error (if known)
	LatestError        time.Time   // time of latest error
	RefreshCount       int         // number of times the zone has been sucessfully refreshed (used to determine if we have zonedata)
	LatestRefresh      time.Time   // time of latest successful refresh
	SourceCatalog      string      // if auto-configured, which catalog zone created this zone
	TransportSignal    *core.RRset // transport signal RRset (SVCB or TSYNC)
	AddTransportSignal bool        // whether to attach TransportSignal in responses
	// RemoteDNSKEYs holds DNSKEY RRs from other signers (multi-signer mode 4).
	// These are DNSKEYs found in the incoming zone that do not match keys in our
	// local keystore. They are preserved across resignings and merged into the
	// DNSKEY RRset during PublishDnskeyRRs().
	RemoteDNSKEYs []dns.RR

	// OnFirstLoad holds one-shot callbacks executed after the zone's first successful load.
	// Apps register these before RefreshEngine starts, and RefreshEngine clears the slice
	// after executing them. Protected by zd.mu.
	OnFirstLoad []func(*ZoneData)
	// contains filtered or unexported fields
}

func FindZone

func FindZone(qname string) (*ZoneData, bool)

Find the closest enclosing auth zone that has qname below it (qname is either auth data in the zone or located further down in a child zone that we are not auth for). Return zone, case fold used to match

func FindZoneNG

func FindZoneNG(qname string) *ZoneData

func (*ZoneData) AddCombinerData

func (zd *ZoneData) AddCombinerData(senderID string, data map[string][]core.RRset) (bool, error)

AddCombinerData adds or updates local RRsets for the zone from a specific agent. Contributions are stored per-agent so that updates from different agents are accumulated (not replaced). The merged result is then written to CombinerData. senderID identifies the contributing agent (use "local" for CLI-originated data).

TODO: Add local/remote isolation policy. The policy must work at the individual RR level (not owner+rrtype), because multiple agents legitimately contribute to the same owner+rrtype (e.g. NS records from different providers at the apex). A local change must not delete or modify an RR contributed by a remote agent, and vice versa. The per-agent storage in AgentContributions already provides structural isolation; policy enforcement needs to happen at the RR level.

func (*ZoneData) AddCombinerDataNG

func (zd *ZoneData) AddCombinerDataNG(senderID string, data map[string][]string) (bool, error)

AddCombinerDataNG adds or updates local RRsets for the zone from a specific agent. The input map keys are owner names and values are slices of RR strings. senderID identifies the contributing agent (use "" for CLI-originated data).

func (*ZoneData) AddOwner

func (zd *ZoneData) AddOwner(owner *OwnerData)

XXX: This MUST ONLY be called from the ZoneUpdater, due to locking issues

func (*ZoneData) AgentJWKKeyPrep

func (zd *ZoneData) AgentJWKKeyPrep(publishname string, kdb *KeyDB) error

AgentJWKKeyPrep publishes a JWK record for the agent's JOSE/HPKE long-term public keys. This provides RFC 7517 compliant public key discovery for payload encryption/signing.

NOTE: This is separate from SIG(0) keys (which use KEY records). JOSE/HPKE keys are used for CHUNK payload encryption/signing, not DNS UPDATE authentication.

Parameters:

  • publishname: The name where the JWK record will be published (typically "dns.<identity>")
  • kdb: The key database (unused, kept for interface compatibility)

func (*ZoneData) AgentSig0KeyPrep

func (zd *ZoneData) AgentSig0KeyPrep(name string, kdb *KeyDB) error

func (*ZoneData) AnalyseZoneDelegation

func (zd *ZoneData) AnalyseZoneDelegation(imr *Imr) (DelegationSyncStatus, error)

Return insync (bool), adds, removes ([]dns.RR) and error

func (*ZoneData) ApplyChildUpdateToZoneData

func (zd *ZoneData) ApplyChildUpdateToZoneData(ur UpdateRequest, kdb *KeyDB) (bool, error)

func (*ZoneData) ApplyZoneUpdateToZoneData

func (zd *ZoneData) ApplyZoneUpdateToZoneData(ur UpdateRequest, kdb *KeyDB) (bool, error)

func (*ZoneData) ApproveAuthUpdate

func (zd *ZoneData) ApproveAuthUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Updates to auth data must be validated.

func (*ZoneData) ApproveChildUpdate

func (zd *ZoneData) ApproveChildUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Child updates are either validated updates for child delegation data, or unvalidated key upload requests. Returns approved, updatezone, error

func (*ZoneData) ApproveTrustUpdate

func (zd *ZoneData) ApproveTrustUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Trust updates are either validated updates (signed by already trusted key) or unvalidated (selfsigned initial uploads of key). In both cases the update section must only contain a single KEY RR. Returns approved, updatezone, error

func (*ZoneData) ApproveUpdate

func (zd *ZoneData) ApproveUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Returns approved, updatezone, error

func (*ZoneData) BestSyncScheme

func (zd *ZoneData) BestSyncScheme(ctx context.Context, imr *Imr) (string, *DsyncTarget, error)

Find the best scheme (from the POV of the child) to sync the deletation with the parent

func (*ZoneData) BootstrapSig0KeyWithParent

func (zd *ZoneData) BootstrapSig0KeyWithParent(ctx context.Context, alg uint8) (string, UpdateResult, error)

func (*ZoneData) BumpSerial

func (zd *ZoneData) BumpSerial() (BumperResponse, error)

func (*ZoneData) BumpSerialOnly

func (zd *ZoneData) BumpSerialOnly() (BumperResponse, error)

BumpSerialOnly increments the SOA serial and updates the SOA RR, but does not notify downstreams. Use when the caller will handle notification separately or when notification is not appropriate (e.g. inside a NOTIFY handler where triggering downstream NOTIFYs could cause side effects).

func (*ZoneData) CollectDynamicRRs

func (zd *ZoneData) CollectDynamicRRs(conf *Config) []*core.RRset

CollectDynamicRRs collects all dynamically generated RRsets for a zone that need to be repopulated after refresh. These RRs are stored outside ZoneData (in database or generated from config) and will be lost when the zone is reloaded.

Returns a slice of RRsets that should be repopulated into the zone after refresh: - DNSKEY records (from DnssecKeyStore database, if online-signing enabled) - SIG(0) KEY records (from Sig0KeyStore database, if needed) - Transport signals (SVCB/TSYNC) - if Config provided and add-transport-signal enabled

func (*ZoneData) CombineWithLocalChanges

func (zd *ZoneData) CombineWithLocalChanges() (bool, error)

Returns true if the zone data was modified.

func (*ZoneData) ComputeIndices

func (zd *ZoneData) ComputeIndices()

func (*ZoneData) CreateTransportSignalRRs

func (zd *ZoneData) CreateTransportSignalRRs(conf *Config) error

CreateTransportSignalRRs orchestrates construction of a transport signal RRset for this zone. It delegates to the chosen mechanism (svcb|tsync) and assigns zd.TransportSignal and zd.AddTransportSignal when successful.

func (*ZoneData) DelegationData

func (zd *ZoneData) DelegationData() (*DelegationData, error)

func (*ZoneData) DelegationDataChangedNG

func (zd *ZoneData) DelegationDataChangedNG(newzd *ZoneData) (bool, DelegationSyncStatus, error)

func (*ZoneData) DelegationSyncSetup

func (zd *ZoneData) DelegationSyncSetup(ctx context.Context, kdb *KeyDB) error

func (*ZoneData) DnskeysChanged

func (zd *ZoneData) DnskeysChanged(newzd *ZoneData) (bool, DelegationSyncStatus, error)

func (*ZoneData) DnskeysChangedNG

func (zd *ZoneData) DnskeysChangedNG(newzd *ZoneData) (bool, error)

func (*ZoneData) DoTransfer

func (zd *ZoneData) DoTransfer() (bool, uint32, error)

Return shouldTransfer, new upstream serial, error

func (*ZoneData) EnsureMP

func (zd *ZoneData) EnsureMP()

EnsureMP initializes the MP extension if nil. Must be called with zd.mu held or before concurrent access begins.

func (*ZoneData) FetchChildDelegationData

func (zd *ZoneData) FetchChildDelegationData(childname string) (*ChildDelegationData, error)

func (*ZoneData) FetchFromFile

func (zd *ZoneData) FetchFromFile(verbose, debug, force bool, dynamicRRs []*core.RRset) (bool, error)

Return updated, error

func (*ZoneData) FetchFromUpstream

func (zd *ZoneData) FetchFromUpstream(verbose, debug bool, dynamicRRs []*core.RRset) (bool, error)

Return updated, err

func (*ZoneData) FetchParentData

func (zd *ZoneData) FetchParentData(imr *Imr) error

func (*ZoneData) FindDelegation

func (zd *ZoneData) FindDelegation(qname string, dnssec_ok bool) *ChildDelegationData

XXX: This should be merged with the FetchChildDelegationData() function Returns [] NS RRs + [] v4glue RRs + [] v6glue RRs

func (*ZoneData) FindDnskey

func (zd *ZoneData) FindDnskey(signer string, keyid uint16) (*cache.CachedDnskeyRRset, error)

If key not found *CachedDnskeyRRset is returned with nil value

func (*ZoneData) FindGlue

func (zd *ZoneData) FindGlue(nsrrs core.RRset, dnssec_ok bool) (*core.RRset, *core.RRset)

Returns two RRsets with A glue and AAAA glue. Each RRset may be nil. XXX: This is wrong. The v4 (and v6) glue is not an *RRset, but a []*RRset

func (*ZoneData) FindGlueSimple

func (zd *ZoneData) FindGlueSimple(nsrrs core.RRset, dnssec_ok bool) ([]dns.RR, []dns.RR, []dns.RR, []dns.RR)

func (*ZoneData) FindSig0KeyViaDNS

func (zd *ZoneData) FindSig0KeyViaDNS(signer string, keyid uint16) (*Sig0Key, error)

func (*ZoneData) FindSig0TrustedKey

func (zd *ZoneData) FindSig0TrustedKey(signer string, keyid uint16) (*Sig0Key, error)

This is about locating a SIG(0) key that is trusted, i.e. that is present in the TrustStore. It is not about looking in the Keystore, nor looking in the DNS. If key not found *TrustAnchor is nil

func (*ZoneData) GenerateNsecChain

func (zd *ZoneData) GenerateNsecChain(kdb *KeyDB) error

func (*ZoneData) GetCombinerData

func (zd *ZoneData) GetCombinerData() (map[string][]core.RRset, error)

GetCombinerData retrieves all local combiner data for the zone

func (*ZoneData) GetCombinerDataNG

func (zd *ZoneData) GetCombinerDataNG() map[string][]RRsetString

GetCombinerDataNG returns the combiner data in string format suitable for JSON marshaling

func (*ZoneData) GetKeystateError

func (zd *ZoneData) GetKeystateError() string

func (*ZoneData) GetKeystateOK

func (zd *ZoneData) GetKeystateOK() bool

func (*ZoneData) GetKeystateTime

func (zd *ZoneData) GetKeystateTime() time.Time

func (*ZoneData) GetLastKeyInventory

func (zd *ZoneData) GetLastKeyInventory() *KeyInventorySnapshot

func (*ZoneData) GetOwner

func (zd *ZoneData) GetOwner(qname string) (*OwnerData, error)

func (*ZoneData) GetOwnerNames

func (zd *ZoneData) GetOwnerNames() ([]string, error)

func (*ZoneData) GetRRset

func (zd *ZoneData) GetRRset(qname string, rrtype uint16) (*core.RRset, error)

func (*ZoneData) GetRemoteDNSKEYs

func (zd *ZoneData) GetRemoteDNSKEYs() []dns.RR

func (*ZoneData) GetSOA

func (zd *ZoneData) GetSOA() (*dns.SOA, error)

func (*ZoneData) HsyncChanged

func (zd *ZoneData) HsyncChanged(newzd *ZoneData) (bool, *HsyncStatus, error)

func (*ZoneData) InjectSignatureTXT

func (zd *ZoneData) InjectSignatureTXT(conf *MultiProviderConf) bool

InjectSignatureTXT adds a combiner signature TXT record to the zone data. The record is placed at "hsync-signature.{zone}" to avoid conflicts with apex TXT records. Returns true if the signature was injected.

func (*ZoneData) IsChildDelegation

func (zd *ZoneData) IsChildDelegation(qname string) bool

XXX: Is qname the name of a zone cut for a child zone?

func (*ZoneData) LoadDynamicZoneFile

func (zd *ZoneData) LoadDynamicZoneFile(zoneDirectory string) (bool, uint32, error)

LoadDynamicZoneFile loads a zone from a file in the dynamic zones directory Returns true if zone was updated, the serial number, and any error If the file is corrupted, creates the zone but sets an error state

func (*ZoneData) LocalDnskeysChanged

func (zd *ZoneData) LocalDnskeysChanged(newzd *ZoneData) (bool, *DnskeyStatus, error)

LocalDnskeysChanged compares old and new DNSKEY RRsets, filtering out known remote DNSKEYs, and returns whether local DNSKEYs changed. Modeled on HsyncChanged() but operates on dns.TypeDNSKEY.

"Remote" keys are those whose key tag matches zd.RemoteDNSKEYs. Everything else in the DNSKEY RRset is "local" (from our signer).

func (*ZoneData) LocalDnskeysFromKeystate

func (zd *ZoneData) LocalDnskeysFromKeystate() (bool, *DnskeyStatus, error)

LocalDnskeysFromKeystate derives local DNSKEY adds/removes from the KEYSTATE inventory rather than from the zone transfer's DNSKEY RRset. The KEYSTATE inventory (from the signer) is the authoritative source for which keys are local vs foreign. Each inventory entry's KeyRR field contains the full DNSKEY RR string, so we can build dns.RR objects directly.

Returns (changed, status, error). If KEYSTATE is unavailable (LastKeyInventory == nil), returns (false, nil, nil) — caller should suppress SYNC-DNSKEY-RRSET.

func (*ZoneData) Lock

func (zd *ZoneData) Lock()

Lock and Unlock expose the mutex for code that moves to tdns-mp and can no longer access the unexported zd.mu.

func (*ZoneData) LookupAndValidateRRset

func (zd *ZoneData) LookupAndValidateRRset(qname string, qtype uint16,
	verbose bool) (*core.RRset, bool, error)

func (*ZoneData) LookupChildRRset

func (zd *ZoneData) LookupChildRRset(qname string, qtype uint16,
	v4glue, v6glue *core.RRset, verbose bool) (*core.RRset, error)

XXX: This should die in favor of LookupChildRRsetNG

func (*ZoneData) LookupChildRRsetNG

func (zd *ZoneData) LookupChildRRsetNG(qname string, qtype uint16,
	addrs []string, verbose bool) (*core.RRset, error)

func (*ZoneData) LookupRRset

func (zd *ZoneData) LookupRRset(qname string, qtype uint16, verbose bool) (*core.RRset, error)

func (*ZoneData) MusicSig0KeyPrep

func (zd *ZoneData) MusicSig0KeyPrep(name string, kdb *KeyDB) error

MusicSig0KeyPrep and ParentSig0KeyPrep are identical except for the source of the keygen algorithm which is specified in the relevant section of the configuration file.

func (*ZoneData) NameExists

func (zd *ZoneData) NameExists(qname string) bool

func (*ZoneData) NotifyDownstreams

func (zd *ZoneData) NotifyDownstreams() error

func (*ZoneData) ParentSig0KeyPrep

func (zd *ZoneData) ParentSig0KeyPrep(name string, kdb *KeyDB) error

func (*ZoneData) ParseZoneFromReader

func (zd *ZoneData) ParseZoneFromReader(r io.Reader, force bool, filename string) (bool, uint32, error)

func (*ZoneData) PrintApexRRs

func (zd *ZoneData) PrintApexRRs() error

func (*ZoneData) PrintOwnerNames

func (zd *ZoneData) PrintOwnerNames() error

func (*ZoneData) PrintOwners

func (zd *ZoneData) PrintOwners()

func (*ZoneData) PublishAddrRR

func (zd *ZoneData) PublishAddrRR(name, addr string) error

func (*ZoneData) PublishCdsRRs

func (zd *ZoneData) PublishCdsRRs() error

func (*ZoneData) PublishCsyncRR

func (zd *ZoneData) PublishCsyncRR() error

func (*ZoneData) PublishDnskeyRRs

func (zd *ZoneData) PublishDnskeyRRs(dak *DnssecKeys) error

func (*ZoneData) PublishDsyncRRs

func (zd *ZoneData) PublishDsyncRRs() error

func (*ZoneData) PublishJWKFromKeyRR

func (zd *ZoneData) PublishJWKFromKeyRR(owner string, keyRR *dns.KEY) error

PublishJWKFromKeyRR publishes a JWK record by extracting the public key from a KEY RR. This is useful for migrating existing KEY records to JWK format.

Parameters:

  • owner: The DNS name for the JWK record
  • keyRR: The existing KEY record containing the public key

Returns error if key extraction or encoding fails.

func (*ZoneData) PublishJWKRR

func (zd *ZoneData) PublishJWKRR(owner string, publicKey crypto.PublicKey, use string) error

PublishJWKRR publishes a JWK record for the specified owner and public key. The public key is encoded to RFC 7517 JWK format and published as a JWK RR.

Parameters:

  • owner: The DNS name for the JWK record (typically the agent identity)
  • publicKey: The crypto.PublicKey to encode and publish
  • use: The intended use ("" to omit, "sig" for signing, "enc" for encryption)

Returns error if encoding fails or zone update fails.

func (*ZoneData) PublishKeyRRs

func (zd *ZoneData) PublishKeyRRs(sak *Sig0ActiveKeys) error

func (*ZoneData) PublishSvcbRR

func (zd *ZoneData) PublishSvcbRR(name string, port uint16, value []dns.SVCBKeyValue) error

func (*ZoneData) PublishTlsaRR

func (zd *ZoneData) PublishTlsaRR(name string, port uint16, certPEM string) error

func (*ZoneData) PublishUriRR

func (zd *ZoneData) PublishUriRR(owner, target, baseurl string, port uint16) error

Example: target: ms1.music.axfr.net baseurl: https://{TARGET}/api/v1 port: 443

func (*ZoneData) QueryResponder

func (zd *ZoneData) QueryResponder(ctx context.Context, w dns.ResponseWriter, r *dns.Msg,
	qname string, qtype uint16, msgoptions *edns0.MsgOptions, kdb *KeyDB, imr *Imr) error

func (*ZoneData) ReadZoneData

func (zd *ZoneData) ReadZoneData(zoneData string, force bool) (bool, uint32, error)

func (*ZoneData) ReadZoneFile

func (zd *ZoneData) ReadZoneFile(filename string, force bool) (bool, uint32, error)

func (*ZoneData) Refresh

func (zd *ZoneData) Refresh(verbose, debug, force bool, conf *Config) (bool, error)

func (*ZoneData) ReloadZone

func (zd *ZoneData) ReloadZone(refreshCh chan<- ZoneRefresher, force bool, wait bool, timeoutStr string) (string, error)

func (*ZoneData) RemoveCombinerDataByRRtype

func (zd *ZoneData) RemoveCombinerDataByRRtype(senderID string, owner string, rrtype uint16) ([]string, error)

RemoveCombinerDataByRRtype removes all RRs of a given type from an agent's contributions for a specific owner. Used for ClassANY delete semantics. Returns the list of RR strings that were removed.

func (*ZoneData) RemoveCombinerDataNG

func (zd *ZoneData) RemoveCombinerDataNG(senderID string, data map[string][]string) ([]string, error)

RemoveCombinerDataNG removes specific RRs from the agent's contributions. Input: senderID identifies the agent, data maps owner → RR strings (ClassINET format). Returns the list of RR strings that were actually removed. If an RR was already absent, it is not included in the returned list (true no-op detection).

func (*ZoneData) ReplaceCombinerDataByRRtype

func (zd *ZoneData) ReplaceCombinerDataByRRtype(senderID, owner string, rrtype uint16, newRRs []dns.RR) (applied []string, removed []string, changed bool, err error)

ReplaceCombinerDataByRRtype atomically replaces an agent's contributions for a specific owner+rrtype with a new set of RRs. Returns the lists of actually added and removed RR strings, plus whether any change occurred. Used for "replace" operation semantics at the combiner level.

func (*ZoneData) RepopulateDynamicRRs

func (zd *ZoneData) RepopulateDynamicRRs(dynamicRRs []*core.RRset)

RepopulateDynamicRRs repopulates dynamically generated RRsets into the zone data after refresh. The RRsets are passed in from RefreshEngine which collected them before the refresh.

func (*ZoneData) RequestAndWaitForEdits

func (zd *ZoneData) RequestAndWaitForEdits(ctx context.Context)

RequestAndWaitForEdits sends an RFI EDITS to the combiner and waits for the contributions response. Applies the received records to the SynchedDataEngine as confirmed data (the combiner already has them).

Modeled on RequestAndWaitForKeyInventory.

func (*ZoneData) RequestAndWaitForKeyInventory

func (zd *ZoneData) RequestAndWaitForKeyInventory(ctx context.Context)

RequestAndWaitForKeyInventory sends an RFI KEYSTATE to the signer and waits for the inventory response. Uses the inventory to populate zd.RemoteDNSKEYs by matching foreign key tags against the actual DNSKEY RRset in the zone.

Sets zd.KeystateOK/KeystateError/KeystateTime to reflect success or failure. KEYSTATE failure is an error condition — the agent depends on KEYSTATE for DNSKEY classification and must not guess when it's unavailable.

func (*ZoneData) RolloverSig0KeyWithParent

func (zd *ZoneData) RolloverSig0KeyWithParent(ctx context.Context, alg uint8, action string) (string, uint16, uint16, UpdateResult, error)

Returns msg, old keyid, new keyid, error, UpdateResult

func (*ZoneData) SendNotify

func (zd *ZoneData) SendNotify(ntype uint16, targets []string) (int, error)

func (*ZoneData) SetError

func (zd *ZoneData) SetError(errtype ErrorType, errmsg string, args ...interface{})

func (*ZoneData) SetKeystateError

func (zd *ZoneData) SetKeystateError(err string)

func (*ZoneData) SetKeystateOK

func (zd *ZoneData) SetKeystateOK(ok bool)

func (*ZoneData) SetKeystateTime

func (zd *ZoneData) SetKeystateTime(t time.Time)

func (*ZoneData) SetLastKeyInventory

func (zd *ZoneData) SetLastKeyInventory(inv *KeyInventorySnapshot)

func (*ZoneData) SetOption

func (zd *ZoneData) SetOption(option ZoneOption, value bool)

func (*ZoneData) SetRemoteDNSKEYs

func (zd *ZoneData) SetRemoteDNSKEYs(keys []dns.RR)

func (*ZoneData) SetupZoneSigning

func (zd *ZoneData) SetupZoneSigning(resignq chan<- *ZoneData) error

func (*ZoneData) SetupZoneSync

func (zd *ZoneData) SetupZoneSync(delsyncq chan<- DelegationSyncRequest) error

func (*ZoneData) ShowNsecChain

func (zd *ZoneData) ShowNsecChain() ([]string, error)

func (*ZoneData) Sig0KeyPreparation

func (zd *ZoneData) Sig0KeyPreparation(name string, alg uint8, kdb *KeyDB) error

func (*ZoneData) SignRRset

func (zd *ZoneData) SignRRset(rrset *core.RRset, name string, dak *DnssecKeys, force bool) (bool, error)

func (*ZoneData) SignZone

func (zd *ZoneData) SignZone(kdb *KeyDB, force bool) (int, error)

XXX: MaybesignRRset should report on whether it actually signed anything At the end, is anything hass been signed, then we must end by bumping the SOA Serial and resigning the SOA.

func (*ZoneData) SortFunc

func (zd *ZoneData) SortFunc(rr dns.RR, firstSoaSeen bool) bool

func (*ZoneData) SyncZoneDelegation

func (zd *ZoneData) SyncZoneDelegation(ctx context.Context, kdb *KeyDB, notifyq chan NotifyRequest, syncstate DelegationSyncStatus, imr *Imr) (string, uint8, UpdateResult, error)

SyncZoneDelegation() is used for delegation synchronization request via API.

func (*ZoneData) SyncZoneDelegationViaNotify

func (zd *ZoneData) SyncZoneDelegationViaNotify(kdb *KeyDB, notifyq chan NotifyRequest, syncstate DelegationSyncStatus,
	dsynctarget *DsyncTarget) (string, uint8, error)

func (*ZoneData) SyncZoneDelegationViaUpdate

func (zd *ZoneData) SyncZoneDelegationViaUpdate(kdb *KeyDB, syncstate DelegationSyncStatus,
	dsynctarget *DsyncTarget) (string, uint8, UpdateResult, error)

func (*ZoneData) TrustUpdate

func (zd *ZoneData) TrustUpdate(r *dns.Msg, us *UpdateStatus) error

BERRA TODO kolla om man kan förbättra detta, så man kan skicka en EDE Evaluate the keys that signed the update and determine the trust status of the update.

func (*ZoneData) Unlock

func (zd *ZoneData) Unlock()

func (*ZoneData) UnpublishAddrRR

func (zd *ZoneData) UnpublishAddrRR(name, addr string) error

func (*ZoneData) UnpublishCdsRRs

func (zd *ZoneData) UnpublishCdsRRs() error

UnpublishCdsRRs removes the CDS RRset from the zone apex.

func (*ZoneData) UnpublishCsyncRR

func (zd *ZoneData) UnpublishCsyncRR() error

func (*ZoneData) UnpublishDsyncRRs

func (zd *ZoneData) UnpublishDsyncRRs() error

func (*ZoneData) UnpublishKeyRRs

func (zd *ZoneData) UnpublishKeyRRs() error

func (*ZoneData) UnpublishSvcbRR

func (zd *ZoneData) UnpublishSvcbRR(name string) error

func (*ZoneData) UnpublishTlsaRR

func (zd *ZoneData) UnpublishTlsaRR(port uint16) error

func (*ZoneData) UnpublishUriRR

func (zd *ZoneData) UnpublishUriRR(owner, target string) error

func (*ZoneData) ValidateChildDnskeys

func (zd *ZoneData) ValidateChildDnskeys(cdd *ChildDelegationData, verbose bool) (bool, error)

ValidateChildDnskeys: we have the ChildDelegationData for the child zone, containing both the NS RRset and the DS RRset. 1. Fetch the child DNSKEY RRset from one of the child nameservers 2. Verify the child KSK against the DS that we have 3. Verify the child DNSKEY RRset against the verified KSK 4. Store the child DNSKEY RRset in the TrustAnchor store 5. Return true if the child DNSKEY RRset is validated

func (*ZoneData) ValidateHsyncRRset

func (zd *ZoneData) ValidateHsyncRRset() (bool, error)

ValidateHsyncRRset checks that HSYNC3 and HSYNCPARAM records exist at the zone apex and that the HSYNCPARAM has valid keys. With HSYNCPARAM, NSmgmt is in a single record so per-RR consistency checks are unnecessary. Returns true if both record types exist and are valid, false otherwise. error is non-nil for errors other than the records not existing.

func (*ZoneData) ValidateRRset

func (zd *ZoneData) ValidateRRset(rrset *core.RRset, verbose bool) (bool, error)

XXX: This should not be a method of ZoneData, but rather a function.

func (*ZoneData) ValidateUpdate

func (zd *ZoneData) ValidateUpdate(r *dns.Msg, us *UpdateStatus) error

XXX: This should perhaps not be a method of ZoneData, but rather of KeyDB.

func (*ZoneData) VerifyPublishedKeyRRs

func (zd *ZoneData) VerifyPublishedKeyRRs() error

func (*ZoneData) WriteDynamicZoneFile

func (zd *ZoneData) WriteDynamicZoneFile(zoneDirectory string) (string, error)

WriteDynamicZoneFile writes a zone file to the dynamic zones directory using atomic writes Returns the full path to the written file, or an error

func (*ZoneData) WriteFile

func (zd *ZoneData) WriteFile(filename string) (string, error)

func (*ZoneData) WriteTmpFile

func (zd *ZoneData) WriteTmpFile(lg *log.Logger) (string, error)

func (*ZoneData) WriteZone

func (zd *ZoneData) WriteZone(tosource bool, force bool) (string, error)

func (*ZoneData) WriteZoneToFile

func (zd *ZoneData) WriteZoneToFile(f *os.File) error

func (*ZoneData) XXfindServerTSYNCRRset

func (zd *ZoneData) XXfindServerTSYNCRRset() *core.RRset

findServerTSYNCRRset returns a TSYNC RRset from any owner under this zone that starts with _dns.

func (*ZoneData) ZoneFileName

func (zd *ZoneData) ZoneFileName() (string, error)

ZoneFileName returns the path to use for this zone's file. Only zones with zonefile: set (in config) are written to disk; autozones and secondaries without zonefile are not persisted.

func (*ZoneData) ZoneTransferIn

func (zd *ZoneData) ZoneTransferIn(upstream string, serial uint32, ttype string) (uint32, error)

func (*ZoneData) ZoneTransferOut

func (zd *ZoneData) ZoneTransferOut(w dns.ResponseWriter, r *dns.Msg) (int, error)

func (*ZoneData) ZoneUpdateChangesDelegationDataNG

func (zd *ZoneData) ZoneUpdateChangesDelegationDataNG(ur UpdateRequest) (DelegationSyncStatus, error)

type ZoneDataRepo

type ZoneDataRepo struct {
	// Repo map[ZoneName]ZoneRepo // map[zonename]ZoneRepo
	Repo core.ConcurrentMap[ZoneName, *AgentRepo] // map[zonename]ZoneRepo

	// Tracking stores per-RR lifecycle state parallel to Repo.
	// Accessed only from the SynchedDataEngine goroutine.
	// Structure: zone → agentId → rrtype → TrackedRRset
	Tracking map[ZoneName]map[AgentId]map[uint16]*TrackedRRset

	// PendingRemoteConfirms maps a combiner distID (generated by this remote agent)
	// to the originating agent's distID and sender identity. When the combiner confirms
	// the remote agent's enqueue, this mapping is used to send the final confirmation
	// back to the originating agent with the correct originating distID.
	PendingRemoteConfirms map[string]*PendingRemoteConfirmation
	// contains filtered or unexported fields
}

func NewZoneDataRepo

func NewZoneDataRepo() (*ZoneDataRepo, error)

func (*ZoneDataRepo) AddConfirmedRR

func (zdr *ZoneDataRepo) AddConfirmedRR(zone ZoneName, agentID AgentId, rr dns.RR)

AddConfirmedRR adds a single RR to the repo and tracking as RRStateAccepted. Used to hydrate the SDE with pre-confirmed data from the combiner (RFI EDITS).

func (*ZoneDataRepo) EvaluateUpdate

func (zdr *ZoneDataRepo) EvaluateUpdate(synchedDataUpdate *SynchedDataUpdate) (bool, string, error)

func (*ZoneDataRepo) Get

func (zdr *ZoneDataRepo) Get(zone ZoneName) (*AgentRepo, bool)

func (*ZoneDataRepo) MarkRRsPending

func (zdr *ZoneDataRepo) MarkRRsPending(zone ZoneName, agent AgentId, update *ZoneUpdate, distID string, recipients []string)

MarkRRsPending marks all RRs in a ZoneUpdate as pending with the given distribution ID. Called after successful enqueue for combiner delivery. recipients is the list of expected confirmation sources (combiner + agents) for this distID. The RR transitions to accepted only after ALL recipients have confirmed.

For ClassINET RRs: marks as RRStatePending (addition awaiting confirmation). For ClassNONE RRs: finds the matching existing tracked RR and marks it as RRStatePendingRemoval. For ClassANY RRs: marks all tracked RRs for the rrtype as RRStatePendingRemoval.

func (*ZoneDataRepo) ProcessConfirmation

func (zdr *ZoneDataRepo) ProcessConfirmation(detail *ConfirmationDetail, msgQs *MsgQs)

ProcessConfirmation updates tracked RR states based on combiner confirmation feedback. For additions: Pending → Accepted or Rejected. For deletions: PendingRemoval → Removed (and the RR is actually deleted from the ZoneDataRepo). If a PendingRemoval RR appears in the rejected list, it transitions back to Accepted.

func (*ZoneDataRepo) ProcessUpdate

func (zdr *ZoneDataRepo) ProcessUpdate(synchedDataUpdate *SynchedDataUpdate) (bool, string, error)

Returns change (true/false), msg (string), error (error)

func (*ZoneDataRepo) SendUpdate

func (zdr *ZoneDataRepo) SendUpdate(update *SynchedDataUpdate) error

func (*ZoneDataRepo) Set

func (zdr *ZoneDataRepo) Set(zone ZoneName, agentRepo *AgentRepo)

type ZoneDsyncPost

type ZoneDsyncPost struct {
	Command   string // status | bootstrap | ...
	Zone      string
	Algorithm uint8
	Action    string
	OldKeyID  uint16
	NewKeyID  uint16
}

type ZoneDsyncResponse

type ZoneDsyncResponse struct {
	AppName      string
	Time         time.Time
	Status       string
	Zone         string
	Functions    map[string]string
	Todo         []string
	Msg          string
	OldKeyID     uint16
	NewKeyID     uint16
	Error        bool
	ErrorMsg     string
	UpdateResult UpdateResult
}

type ZoneMPExtension

type ZoneMPExtension struct {
	CombinerData *core.ConcurrentMap[string, OwnerData]
	UpstreamData *core.ConcurrentMap[string, OwnerData] // Original upstream apex data (combiner NS fallback)
	MPdata       *MPdata                                // Multi-provider membership/signing state; nil = not MP
	// AgentContributions stores per-agent contributions for the combiner.
	// Key: agentID (e.g. "agent.alpha.dnslab."), Value: map[owner]map[rrtype]core.RRset
	// When merging, all agents' contributions for the same owner/rrtype are combined
	// into a single RRset in CombinerData.
	AgentContributions map[string]map[string]map[uint16]core.RRset
	// PersistContributions is set by the combiner at init time to persist an agent's
	// contributions to the snapshot table after every write. Non-combiner apps leave it nil.
	// Args: zone, senderID, agent's contributions (owner → rrtype → RRset).
	PersistContributions func(string, string, map[string]map[uint16]core.RRset) error

	// LastKeyInventory stores the most recent KEYSTATE inventory received from the signer.
	// Used for diagnostics (CLI show-key-inventory command).
	LastKeyInventory *KeyInventorySnapshot

	// LocalDNSKEYs holds DNSKEY RRs that the signer classifies as local (not foreign).
	// Derived from KEYSTATE inventory. Used to compute adds/removes on DNSKEY updates.
	LocalDNSKEYs []dns.RR

	// KEYSTATE health tracking — we depend on KEYSTATE for DNSKEY classification.
	// Failure is an error condition that must be visible to the operator.
	KeystateOK    bool      // true after successful KEYSTATE exchange
	KeystateError string    // error message from last failed attempt (empty on success)
	KeystateTime  time.Time // time of last KEYSTATE attempt
}

ZoneMPExtension holds multi-provider state for a zone. Access via zd.MP.

NOTE: This is an MP type that lives in tdns (not tdns-mp) because it is a field of ZoneData. tdns-mp code accesses these fields via zd.MP.

type ZoneName

type ZoneName string

func (ZoneName) String

func (name ZoneName) String() string

type ZoneOption

type ZoneOption uint8
const (
	OptDelSyncParent ZoneOption = iota + 1
	OptDelSyncChild
	OptAllowUpdates
	OptAllowChildUpdates
	OptAllowEdits // Dynamically et if app=combiner and zone contains a HSYNC RRset
	OptFoldCase
	OptBlackLies
	OptDontPublishKey
	OptDontPublishJWK
	OptOnlineSigning
	OptInlineSigning
	OptMultiProvider
	OptDirty
	OptFrozen
	OptAutomaticZone
	// OptServerSvcb
	OptAddTransportSignal
	OptCatalogZone
	OptCatalogMemberAutoCreate
	OptCatalogMemberAutoDelete
	OptMPManualApproval
	OptMultiSigner     // Dynamically set by signer when HSYNC shows multiple signers
	OptMPNotListedErr  // Warning: zone has HSYNC3 but we are not listed as a provider
	OptMPDisallowEdits // Zone is signed but we are not a signer: no edits allowed
)

type ZoneOptionHandler

type ZoneOptionHandler func(zname string, options map[ZoneOption]bool)

ZoneOptionHandler is a callback invoked during ParseZones when a zone has a specific option set. Handlers are registered before ParseZones runs and fire synchronously during parsing.

Parameters:

  • zname: the FQDN zone name
  • options: all parsed options for this zone (read-only)

type ZonePost

type ZonePost struct {
	Command    string
	SubCommand string
	Zone       string
	Force      bool
	Wait       bool
	Timeout    string
}

type ZoneRefresher

type ZoneRefresher struct {
	Name         string
	ZoneType     ZoneType // primary | secondary
	Primary      string
	Notify       []string
	ZoneStore    ZoneStore // 1=xfr, 2=map, 3=slice
	Zonefile     string
	Options      map[ZoneOption]bool
	Edns0Options *edns0.MsgOptions
	UpdatePolicy UpdatePolicy
	DnssecPolicy string
	MultiSigner  string
	Force        bool // force refresh, ignoring SOA serial
	Wait         bool // wait for refresh to complete before responding
	Response     chan RefresherResponse
}

type ZoneResponse

type ZoneResponse struct {
	AppName  string
	Time     time.Time
	Status   string
	Zone     string
	Names    []string
	Zones    map[string]ZoneConf
	MPZones  map[string]MPZoneInfo `json:"mp_zones,omitempty"`
	Msg      string
	Error    bool
	ErrorMsg string
}

type ZoneStore

type ZoneStore uint8
const (
	XfrZone ZoneStore = iota + 1
	MapZone
	SliceZone
)

type ZoneType

type ZoneType uint8
const (
	Primary ZoneType = iota + 1
	Secondary
)

type ZoneUpdate

type ZoneUpdate struct {
	Zone       ZoneName
	AgentId    AgentId
	ZoneClass  string                   // "mp" (default) or "provider"
	RRsets     map[uint16]core.RRset    // remote updates are only per RRset (i.e. full replace)
	RRs        []dns.RR                 // local updates can be per RR
	Operations []core.RROperation       // explicit operations (takes precedence over RRsets/RRs)
	Publish    *core.PublishInstruction // KEY/CDS publication instruction for combiner
}

type ZonefileDelegationBackend

type ZonefileDelegationBackend struct {
	// contains filtered or unexported fields
}

func (*ZonefileDelegationBackend) ApplyChildUpdate

func (b *ZonefileDelegationBackend) ApplyChildUpdate(parentZone string, ur UpdateRequest) error

ApplyChildUpdate persists the update to the DB (as source of truth) and then regenerates the child's zone file fragment from the DB state.

func (*ZonefileDelegationBackend) GetDelegationData

func (b *ZonefileDelegationBackend) GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

func (*ZonefileDelegationBackend) ListChildren

func (b *ZonefileDelegationBackend) ListChildren(parentZone string) ([]string, error)

func (*ZonefileDelegationBackend) Name

Source Files

Directories

Path Synopsis
cli module
crypto module
hpke module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL