import * as plugins from '../../plugins.js'; import type { OpsServer } from '../classes.opsserver.js'; import * as interfaces from '../../../ts_interfaces/index.js'; import { requireOpsAuth } from '../helpers/auth.js'; /** * DNS authority handlers. * * Adding a zone here is an authority claim — after it succeeds dcrouter answers * for that zone with the `aa` flag on a public port and will request * certificates for it. So it carries the same posture as the other DNS write * surfaces (admin identity or a `:write`-scoped token) *and* the claim itself is * gated on a delegation proof the caller cannot forge. Authorization decides who * may ask; the probe decides whether the answer is yes. */ export class DnsAuthorityHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } private async requireAuth( request: { identity?: interfaces.data.IIdentity; apiToken?: string }, requiredScope?: interfaces.data.TApiTokenScope, ): Promise { const auth = await requireOpsAuth(this.opsServerRef, request, { scope: requiredScope, requireAdminIdentity: requiredScope?.endsWith(':write'), }); return auth.userId; } private registerHandlers(): void { // Read the authority set: delegation-verified zones, and nothing else. this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getDnsAuthority', async (dataArg) => { await this.requireAuth(dataArg, 'dns-authority:read'); const manager = this.opsServerRef.dcRouterRef.dnsAuthorityManager; if (!manager) { // No manager means the set was never loaded. Reported as // 'unavailable' rather than as an empty set, so a reader cannot // mistake "we could not look" for "there is nothing". return { settings: { zones: [], state: 'unavailable' as const, expectedNameservers: [], updatedAt: 0, updatedBy: '', }, }; } return { settings: manager.getSettings() }; }, ), ); // Probe a zone's delegation without changing anything this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'probeDnsAuthorityZone', async (dataArg) => { await this.requireAuth(dataArg, 'dns-authority:read'); const manager = this.opsServerRef.dcRouterRef.dnsAuthorityManager; if (!manager) { return { probe: { zone: dataArg.zone, verdict: 'undeterminable' as const, observedNameservers: [], expectedNameservers: [], detail: 'DnsAuthorityManager is not initialized', }, }; } return { probe: await manager.probeDelegation(dataArg.zone) }; }, ), ); // Claim authority over a zone, against a delegation proof this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'verifyDnsAuthorityZone', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'dns-authority:write'); const manager = this.opsServerRef.dcRouterRef.dnsAuthorityManager; if (!manager) { return { success: false, message: 'DnsAuthorityManager is not initialized' }; } const result = await manager.verifyZone(dataArg.zone, userId); if (result.success) { this.opsServerRef.invalidateRealtime?.(['dns', 'routes'], { reason: 'dns-authority-verified', }); } return result; }, ), ); // Drop a verified zone. Every zone is revocable — there is no bootstrap floor. this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'revokeDnsAuthorityZone', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'dns-authority:write'); const manager = this.opsServerRef.dcRouterRef.dnsAuthorityManager; if (!manager) { return { success: false, message: 'DnsAuthorityManager is not initialized' }; } const result = await manager.revokeZone(dataArg.zone, userId); if (result.success) { this.opsServerRef.invalidateRealtime?.(['dns', 'routes'], { reason: 'dns-authority-revoked', }); } return result; }, ), ); // Compare declared authority against observed delegation, both directions this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getDnsAuthorityDrift', async (dataArg) => { await this.requireAuth(dataArg, 'dns-authority:read'); const manager = this.opsServerRef.dcRouterRef.dnsAuthorityManager; if (!manager) { return { drift: [] }; } return { drift: await manager.auditDelegationDrift() }; }, ), ); } }