Compare commits

...

12 Commits

Author SHA1 Message Date
Ari b761f70042 refactor: extract helper functions into dedicated helpers.js module 2026-04-08 15:33:06 -04:00
Ari 5afb9f48f5 feat(openalias): add BTC, ETH, and LTC OpenAlias records
Extends OpenAlias configuration with Bitcoin, Ethereum, and Litecoin donation addresses alongside the existing Monero entry.

Also tightens the openalias() helper to use strict type checks.
2026-03-22 17:41:49 -04:00
Ari 11792cba26 style: add section dividers to helper function groups 2026-03-20 20:21:48 -04:00
Ari 901ea165e5 fix(openalias): output CRC-32 checksum as uppercase hex string and handle UTF-8 correctly
UTF-8 characters (e.g. '€') will now be handled correctly instead of strictly ASCII.
2026-03-20 20:03:53 -04:00
Ari 1621d82c56 feat(openalias): add openalias helper with crc32 checksum support
Replaces the hand-crafted OpenAlias TXT record for arirex.me with a reusable openalias() function that builds spec-compliant oa1 records. Includes an optional CRC-32 checksum appended as the last field.
2026-03-20 19:54:21 -04:00
Ari a4e058fe53 refactor(minecraft): extract minecraft server records into reusable helper function 2026-03-20 14:30:30 -04:00
Ari aadfc83e9d feat(minecraft): add Frantic and The Furry Cult server DNS entries
Replace single Minecraft CNAME with two named subdomains and add
corresponding SRV records for both servers under arirex.me.
2026-03-19 14:24:16 -04:00
Ari 142b3f014b chore: update flake and dns config 2026-03-12 14:29:14 -04:00
Ari aa6431e6e7 Use builders where possible. Minor changes and fixes. 2026-03-08 14:17:07 -04:00
Ari 77293ba260 Add README and make some sanity changes. 2026-03-08 13:36:30 -04:00
Ari 57930abc00 Add opencode.jsonc configuration for DNSControl documentation. 2026-03-08 11:43:12 -04:00
Ari 738d1d9753 Refactor CAA records to use CAA_BUILDER and update type definitions to version 4.35.0. 2026-03-08 11:42:01 -04:00
6 changed files with 397 additions and 86 deletions
+10
View File
@@ -0,0 +1,10 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"dnscontrol-docs": {
"enabled": true,
"type": "remote",
"url": "https://docs.dnscontrol.org/~gitbook/mcp"
}
}
}
+30
View File
@@ -0,0 +1,30 @@
# DNSControl Configuration
## Overview
| Domain | Purpose |
|----------------|--------------------------------|
| `arirex.me` | Primary domain |
| `achl.fr` | Legacy domain |
| `arirex.email` | Email proxy for primary domain |
| `achlfr.email` | Email proxy for legacy domain |
## Usage
```bash
# Unlock credentials
git-crypt unlock
# Check planned changes
dnscontrol preview
# Apply DNS changes
dnscontrol push
```
## Update
```bash
nix flake update
dnscontrol write-types
```
+52 -76
View File
@@ -1,6 +1,8 @@
// @ts-check
/// <reference path="types-dnscontrol.d.ts" />
require("./helpers.js");
var REG_101DOMAIN = NewRegistrar("none");
var DNS_CLOUDFLARE = NewDnsProvider("cloudflare");
@@ -9,29 +11,57 @@ var DNS_CLOUDFLARE = NewDnsProvider("cloudflare");
\* ****************************************************************************************************************** */
var rexbox = "rexbox.prm.achl.fr.";
var rexcloud = "rexcloud.cld.achl.fr.";
var reportEmail = "0acbbb8a-1558-419a-ab2d-3f2773a1247d@arirex.email";
var dmarcPolicy = "v=DMARC1; p=quarantine; adkim=s; aspf=s; rua=mailto:" + reportEmail + "; ruf=mailto:" + reportEmail + "; pct=100; fo=1";
var reportEmail = "mailto:0acbbb8a-1558-419a-ab2d-3f2773a1247d@arirex.email";
var dmarcRecord = DMARC_BUILDER({
policy: "reject",
subdomainPolicy: "reject",
alignmentDKIM: "strict",
alignmentSPF: "strict",
rua: [reportEmail],
ruf: [reportEmail],
percent: 100,
failureOptions: "1",
});
DEFAULTS(
DnsProvider(DNS_CLOUDFLARE),
DefaultTTL(1),
CF_MANAGE_COMMENTS, // opt into comments syncing
CAA("@", "iodef", "mailto:" + reportEmail),
CAA("@", "issue", "letsencrypt.org"),
CAA_BUILDER({
iodef: reportEmail,
iodef_critical: true,
issue: ["letsencrypt.org"],
issue_critical: true,
issuewild: ["letsencrypt.org"],
issuewild_critical: true,
issuevmc: "none",
issuevmc_critical: true,
issuemail: "none",
issuemail_critical: true,
}),
);
/* ****************************************************************************************************************** *\
Primary Domains
Managed Domains
\* ****************************************************************************************************************** */
/* -------------------------------------------------------------------------- *\
Primary Domains
\* -------------------------------------------------------------------------- */
D("arirex.me", REG_101DOMAIN,
ALIAS("@", rexbox),
protonmail("6fd60590dc31588ca5a85c7e311649ff5f93cab2", "dodai2qaszneyk5jeyfloq24ttjcqfer2gdopw3nfmxn3bugtw2hq"),
// Verifications
TXT("@", "oa1:xmr recipient_address=89dQNyY3E9gJGYrEeRw4EFAdezWQg7BBbHJdBpLRwrjH52ngNfAYRcEhAHQotCswGxTeSoFi5nQ7Gf86kySmXzuQE9CXjUH; recipient_name=AriRexouium;", CF_COMMENT("OpenAlias > XMR > Kraken")),
TXT("_discord", "dh=1c93b7effbe0bf428cb55d33175c2721ef715bb6", CF_COMMENT("Discord Verify")),
TXT("_atproto", "did=did:plc:53kf45pcsqgayjmoau42lhsk", CF_COMMENT("BlueSky Verify")),
TXT("_github-pages-challenge-arirexouium", "0b62c2fb7a8422145d5b5e6637257d", CF_COMMENT("GitHub Pages Verify")),
// OpenAlias
openalias("xmr", "89dQNyY3E9gJGYrEeRw4EFAdezWQg7BBbHJdBpLRwrjH52ngNfAYRcEhAHQotCswGxTeSoFi5nQ7Gf86kySmXzuQE9CXjUH", { name: "AriRexouium", checksum: true, }),
openalias("btc", "3DQEYUDquSvh6DX54o2Gw1TXT9ZGWWoRTh", { name: "AriRexouium", checksum: true, }),
openalias("eth", "0x3489c73A2F3DE6236624d43a3767f4dB8181d0c4", { name: "AriRexouium", checksum: true, }),
openalias("ltc", "Lg6sV5PfRrBvpQoAzsyCcWMs26aq5VBssS", { name: "AriRexouium", checksum: true, }),
);
D("achl.fr", REG_101DOMAIN,
@@ -46,12 +76,11 @@ D("achl.fr", REG_101DOMAIN,
// Verifications
TXT("_discord", "dh=d041188169640d1f23c6b379d97935981a7a07da", CF_COMMENT("Discord Verify")),
TXT("_github-pages-challenge-arirexouium", "134234f292827135d74e0637efc575", CF_COMMENT("GitHub Pages Verify")),
);
/* ****************************************************************************************************************** *\
/* -------------------------------------------------------------------------- *\
Email Proxy Domains
\* ****************************************************************************************************************** */
\* -------------------------------------------------------------------------- */
D("arirex.email", REG_101DOMAIN,
simplelogin("ngmfowygibangqmiobjznfmjhxniyi"),
);
@@ -68,36 +97,37 @@ D("achlfr.email", REG_101DOMAIN,
RexBox Services
\* -------------------------------------------------------------------------- */
cnames("arirex.me", rexbox, [
"OpenWebUI@ai",
"Traefik Forward Auth@auth",
"Chhoto URL@l",
"Enclosed@bin",
"Matrix / Client@chat",
"Pocket ID@id",
"IT Tools@it",
"Karakeep@karakeep",
"Chhoto URL@l",
"Matrix / Server@matrix",
"Minecraft@mc",
"Matrix > Client@chat",
"Matrix > Server@matrix",
"Ntfy@ntfy",
"OpenWebUI@ai",
"Pocket ID@id",
"Traefik@traefik",
"Traefik Forward Auth@auth",
]);
cnames("achl.fr", rexbox, [
"Matrix / Client@chat",
"Matrix / Server@matrix",
"Matrix > Client@chat",
"Matrix > Server@matrix",
]);
minecraft("Frantic", "frantic.mc", "arirex.me", 63548);
minecraft("The Furry Cult", "thefurrycult.mc", "arirex.me", 54924);
/* -------------------------------------------------------------------------- *\
RexCloud Services
\* -------------------------------------------------------------------------- */
cnames("arirex.me", rexcloud, [
"Beszel@beszel",
"Gitea@git",
"IPFS@gw",
"IPFS@*.ipfs.gw",
"IPFS@*.ipns.gw",
// "IPFS Subdomain Gateway@*.ipfs.gw",
// "IPFS Subdomain Gateway@*.ipns.gw",
// "IPFS Path Gateway@gw",
"SearXNG@search",
// "LibreSpeed@speedtest",
]);
/* -------------------------------------------------------------------------- *\
@@ -113,57 +143,3 @@ cnames("arirex.me", rexcloud, [
CNAME(i.toLowerCase() + ".servarr", rexbox, CF_COMMENT(i))
);
});
/* ****************************************************************************************************************** *\
Helper Functions
\* ****************************************************************************************************************** */
/**
* Create CNAME records from "comment@subdomain" strings
* @param {string} domain - Domain to extend
* @param {string} target - Server target
* @param {string[]} records - Array of "comment@subdomain" strings
*/
function cnames(domain, target, records) {
records.forEach(function(r) {
var parts = r.split("@");
D_EXTEND(domain, CNAME(parts[1], target, CF_COMMENT(parts[0])));
});
}
/**
* Generate ProtonMail DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - ProtonMail verification token
* @param {string} dkimKey - ProtonMail DKIM domain key
* @returns {DomainModifier[]} Array of DNS records
*/
function protonmail(verification, dkimKey) {
return [
MX("@", 10, "mail.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
MX("@", 20, "mailsec.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
TXT("@", "v=spf1 include:_spf.protonmail.ch mx ~all", CF_COMMENT("ProtonMail SPF")),
TXT("@", "protonmail-verification=" + verification, CF_COMMENT("ProtonMail Verify")),
TXT("_dmarc", dmarcPolicy, CF_COMMENT("ProtonMail DMARC")),
CNAME("protonmail._domainkey", "protonmail.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail2._domainkey", "protonmail2.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail3._domainkey", "protonmail3.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
];
}
/**
* Generate SimpleLogin DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - SimpleLogin verification token
* @returns {DomainModifier[]} Array of DNS records
*/
function simplelogin(verification) {
return [
MX("@", 10, "mx1.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
MX("@", 20, "mx2.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
TXT("@", "v=spf1 include:simplelogin.co ~all", CF_COMMENT("SimpleLogin SPF")),
TXT("@", "sl-verification=" + verification, CF_COMMENT("SimpleLogin Verify")),
TXT("_dmarc", dmarcPolicy, CF_COMMENT("SimpleLogin DMARC")),
CNAME("dkim._domainkey", "dkim._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim02._domainkey", "dkim02._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim03._domainkey", "dkim03._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
];
}
Generated
+3 -3
View File
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1772773019,
"narHash": "sha256-E1bxHxNKfDoQUuvriG71+f+s/NT0qWkImXsYZNFFfCs=",
"lastModified": 1773821835,
"narHash": "sha256-TJ3lSQtW0E2JrznGVm8hOQGVpXjJyXY2guAxku2O9A4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "aca4d95fce4914b3892661bcb80b8087293536c6",
"rev": "b40629efe5d6ec48dd1efba650c797ddbd39ace0",
"type": "github"
},
"original": {
+152
View File
@@ -0,0 +1,152 @@
/* ****************************************************************************************************************** *\
Helper Functions
\* ****************************************************************************************************************** */
/* -------------------------------------------------------------------------- *\
Basic Builders
\* -------------------------------------------------------------------------- */
/**
* Create CNAME records from "comment@subdomain" strings
* @param {string} domain - Domain to extend
* @param {string} target - Server target
* @param {string[]} records - Array of "comment@subdomain" strings
*/
function cnames(domain, target, records) {
records.forEach(function(rec) {
var part = rec.split("@");
D_EXTEND(domain, CNAME(part[1], target, CF_COMMENT(part[0])));
});
}
/**
* Create a Minecraft server subdomain with CNAME and SRV records
* @param {string} comment - Human-readable server name
* @param {string} subdomain - Subdomain for the server
* @param {string} domain - Domain to extend
* @param {number} port - Port the server listens on
*/
function minecraft(comment, subdomain, domain, port) {
var fqdn = subdomain + "." + domain + ".";
D_EXTEND(domain,
CNAME(subdomain, rexbox, CF_COMMENT("Minecraft > " + comment)),
SRV("_minecraft._tcp." + subdomain, 0, 0, port, fqdn, CF_COMMENT("Minecraft > " + comment)),
);
}
/* -------------------------------------------------------------------------- *\
Email Builders
\* -------------------------------------------------------------------------- */
/**
* Generate ProtonMail DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - ProtonMail verification token
* @param {string} dkimKey - ProtonMail DKIM domain key
* @returns {DomainModifier[]} Array of DNS records
*/
function protonmail(verification, dkimKey) {
return [
// Stage 1: Verify
TXT("@", "protonmail-verification=" + verification, CF_COMMENT("ProtonMail Verify")),
// Stage 2: MX
MX("@", 10, "mail.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
MX("@", 20, "mailsec.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
// Stage 3: SPF
TXT("@", "v=spf1 include:_spf.protonmail.ch mx ~all", CF_COMMENT("ProtonMail SPF")),
// Stage 3: DKIM
CNAME("protonmail._domainkey", "protonmail.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail2._domainkey", "protonmail2.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail3._domainkey", "protonmail3.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
// Stage 4: DMARC
dmarcRecord,
];
}
/**
* Generate SimpleLogin DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - SimpleLogin verification token
* @returns {DomainModifier[]} Array of DNS records
*/
function simplelogin(verification) {
return [
// Stage 1: Verify
TXT("@", "sl-verification=" + verification, CF_COMMENT("SimpleLogin Verify")),
// Stage 2: MX
MX("@", 10, "mx1.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
MX("@", 20, "mx2.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
// Stage 3: SPF
TXT("@", "v=spf1 include:simplelogin.co ~all", CF_COMMENT("SimpleLogin SPF")),
// Stage 4: DKIM
CNAME("dkim._domainkey", "dkim._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim02._domainkey", "dkim02._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim03._domainkey", "dkim03._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
// Stage 5: DMARC
dmarcRecord,
];
}
/* -------------------------------------------------------------------------- *\
OpenAlias Builder
\* -------------------------------------------------------------------------- */
/**
* Generate OpenAlias TXT record
* @param {string} prefix - Application prefix (e.g., "xmr", "btc")
* @param {string} address - Recipient address
* @param {object} [opts] - Optional key-value pairs
* @param {string} [opts.name] - Recipient name
* @param {string} [opts.description] - Transaction description
* @param {string} [opts.amount] - Transaction amount
* @param {string} [opts.paymentId] - Payment ID (e.g., for Monero)
* @param {string} [opts.signature] - Address signature
* @param {boolean} [opts.checksum] - Optional CRC-32 checksum
* @returns {DomainModifier} TXT record
*/
function openalias(prefix, address, opts) {
// Prefix and address are minimum requirement.
// Everything else is optional.
opts = opts || {};
var record = "oa1:" + prefix + " recipient_address=" + address + ";";
if (typeof opts.name === "string") record += " recipient_name=" + opts.name + ";";
if (typeof opts.description === "string") record += " tx_description=" + opts.description + ";";
if (typeof opts.amount === "string") record += " tx_amount=" + opts.amount + ";";
if (typeof opts.paymentId === "string") record += " tx_payment_id=" + opts.paymentId + ";";
if (typeof opts.signature === "string") record += " address_signature=" + opts.signature + ";";
if (opts.checksum === true) record += " checksum=" + crc32(record.trim()).toString(16).toUpperCase() + ";";
// Checksum must be last: CRC-32 of the record trimmed of surrounding spaces.
return TXT("@", record, CF_COMMENT("OpenAlias > " + prefix.toUpperCase() + (opts.name ? " > " + opts.name : "")));
}
/**
* Calculate CRC-32 checksum of a string
* Handles UTF-8 strings correctly for use with DNSControl.
* @param {string} str - Input string
* @returns {number} CRC-32 value
* @see https://github.com/nabijaczleweli/openalias.rs/blob/master/src/crypto_addr.rs
*/
function crc32(str) {
// 1. Generate the CRC Table
var table = [];
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
}
table[i] = c;
}
// 2. Convert string to UTF-8 "binary" string
// This ensures characters like '€' are treated as 3 bytes, not 1.
var utf8Str = unescape(encodeURIComponent(str));
// 3. Calculate CRC
var crc = 0xFFFFFFFF;
for (var k = 0; k < utf8Str.length; k++) {
crc = table[(crc ^ utf8Str.charCodeAt(k)) & 0xFF] ^ (crc >>> 8);
}
return (crc ^ 0xFFFFFFFF) >>> 0;
}
+150 -7
View File
@@ -1,7 +1,7 @@
// This file was automatically generated by DNSControl. Do not edit it directly.
// To update it, run `dnscontrol write-types`.
// 4.34.0
// 4.36.1
// WARNING: These type definitions are experimental and subject to change in future releases.
interface Domain {
@@ -784,13 +784,13 @@ declare function CNAME(name: string, target: string, ...modifiers: RecordModifie
* In this situation, you will see an error message such as:
*
* ```
* Skipping registrar REGISTRAR: No nameservers declared for domain "example.com". Add {no_ns:'true'} to force
* Skipping registrar REGISTRAR: No nameservers declared for domain "example.com". Add {no_ns: "true"} to force
* ```
*
* To add this, add the meta data to the zone immediately following the registrar.
*
* ```javascript
* D("example.com", REG_MY_PROVIDER, {no_ns:'true'},
* D("example.com", REG_MY_PROVIDER, {no_ns: "true"},
* ...
* ...
* ...
@@ -959,11 +959,11 @@ declare const DISABLE_IGNORE_SAFETY_CHECK: DomainModifier;
* pubkey: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC5/z4L",
* label: "subdomain",
* version: "DKIM1",
* hashtypes: ['sha1', 'sha256'],
* hashtypes: ["sha1", "sha256"],
* keytype: "rsa",
* note: "some human-readable notes",
* servicetypes: ['email'],
* flags: ['y', 's'],
* servicetypes: ["email"],
* flags: ["y", "s"],
* ttl: 150
* }),
* );
@@ -1400,6 +1400,57 @@ declare function FRAME(name: string, target: string, ...modifiers: RecordModifie
*/
declare function HASH(algorithm: "SHA1" | "SHA256" | "SHA512", value: string): string;
/**
* `HEDNS_DDNS_KEY` enables Dynamic DNS on a record managed by the Hurricane Electric DNS provider and sets a specific DDNS key (token). This implies [`HEDNS_DYNAMIC_ON`](HEDNS_DYNAMIC_ON.md).
*
* The DDNS key can then be used with the HE DDNS update API (`https://dyn.dns.he.net/nic/update`) to update the record's value.
*
* **Note:** DDNS keys are **write-only**. dnscontrol sets the key on the provider but cannot read back the current key. This means a key-only change (same record data, new key) will not be detected as a difference. To force an update, also change another field such as the TTL.
*
* ```javascript
* D("example.com", REG_NONE, DnsProvider(DSP_HEDNS),
* A("dyn", "0.0.0.0", HEDNS_DDNS_KEY("my-secret-token")),
* AAAA("dyn6", "::1", HEDNS_DDNS_KEY("another-token")),
* );
* ```
*
* @see https://docs.dnscontrol.org/language-reference/record-modifiers/service-provider-specific//hedns_ddns_key
*/
declare function HEDNS_DDNS_KEY(key: string): RecordModifier;
/**
* `HEDNS_DYNAMIC_OFF` explicitly disables Dynamic DNS on a record managed by the Hurricane Electric DNS provider. This will clear any DDNS key previously associated with the record.
*
* Use this modifier when you want to ensure a record that was previously dynamic is returned to a static state.
*
* ```javascript
* D("example.com", REG_NONE, DnsProvider(DSP_HEDNS),
* A("static", "5.6.7.8", HEDNS_DYNAMIC_OFF),
* );
* ```
*
* @see https://docs.dnscontrol.org/language-reference/record-modifiers/service-provider-specific//hedns_dynamic_off
*/
declare const HEDNS_DYNAMIC_OFF: RecordModifier;
/**
* `HEDNS_DYNAMIC_ON` enables [Dynamic DNS](https://dns.he.net/) on a record managed by the Hurricane Electric DNS provider. When enabled, HE DNS assigns a DDNS key to the record that can be used with the HE DDNS update API (`https://dyn.dns.he.net/nic/update`).
*
* If a record is already dynamic, its dynamic state is preserved across modifications even without explicitly specifying this modifier.
*
* To set a specific DDNS key, use [`HEDNS_DDNS_KEY()`](HEDNS_DDNS_KEY.md) instead.
*
* ```javascript
* D("example.com", REG_NONE, DnsProvider(DSP_HEDNS),
* A("dyn", "0.0.0.0", HEDNS_DYNAMIC_ON),
* AAAA("dyn6", "::1", HEDNS_DYNAMIC_ON),
* );
* ```
*
* @see https://docs.dnscontrol.org/language-reference/record-modifiers/service-provider-specific//hedns_dynamic_on
*/
declare const HEDNS_DYNAMIC_ON: RecordModifier;
/**
* HTTPS adds an HTTPS record to a domain. The name should be the relative label for the record. Use `@` for the domain apex. The HTTPS record is a special form of the SVCB resource record.
*
@@ -2425,6 +2476,98 @@ declare function LUA(name: string, rtype: string, contents: string | string[], .
*/
declare function M365_BUILDER(opts: { label?: string; mx?: boolean; autodiscover?: boolean; dkim?: boolean; skypeForBusiness?: boolean; mdm?: boolean; domainGUID?: string; initialDomain?: string }): DomainModifier;
/**
* `MIKROTIK_FORWARDER` manages a RouterOS DNS forwarder entry (`/ip/dns/forwarders`). The `name` parameter can be a domain name (e.g. `corp.example.com`) or an arbitrary alias (e.g. `my-upstream`). These named entries can then be referenced as the target of [`MIKROTIK_FWD`](MIKROTIK_FWD.md) records.
*
* Forwarder records must be placed in the synthetic zone `_forwarders.mikrotik`. This zone should appear **before** any zones that reference its entries by name in `dnsconfig.js` to ensure proper creation order.
*
* See the [MikroTik RouterOS provider page](../../provider/mikrotik.md) for full configuration details.
*
* Metadata keys supported:
*
* | Key | Description |
* |--------------------|----------------------------------------------------|
* | `doh_servers` | DoH server URLs for this forwarder. |
* | `verify_doh_cert` | Set to `"true"` to verify the DoH certificate. |
* | `comment` | Comment stored on the RouterOS forwarder entry. |
*
* ```javascript
* D("_forwarders.mikrotik", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
* // Domain-based forwarder: forward corp.example.com to internal DNS servers.
* MIKROTIK_FORWARDER("corp.example.com", "10.0.0.53,10.0.0.54"),
*
* // Alias-based forwarder with DoH.
* MIKROTIK_FORWARDER("doh-upstream", "1.1.1.1", {doh_servers: "https://cloudflare-dns.com/dns-query", verify_doh_cert: "true"}),
* );
*
* // Then reference the alias in a FWD record:
* D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
* MIKROTIK_FWD("@", "doh-upstream", {match_subdomain: "true"}),
* );
* ```
*
* @see https://docs.dnscontrol.org/language-reference/domain-modifiers/service-provider-specific//mikrotik_forwarder
*/
declare function MIKROTIK_FORWARDER(name: string, dns_servers: string, ...modifiers: RecordModifier[]): DomainModifier;
/**
* `MIKROTIK_FWD` creates a RouterOS FWD (conditional DNS forwarding) static entry. These records instruct the MikroTik router to forward DNS queries matching the name to a specified upstream server, optionally populating a RouterOS address list with resolved addresses.
*
* The `target` can be an IP address (e.g. `8.8.8.8`) or the name of a [`MIKROTIK_FORWARDER`](MIKROTIK_FORWARDER.md) entry (e.g. `my-upstream`).
*
* See the [MikroTik RouterOS provider page](../../provider/mikrotik.md) for full configuration details.
*
* Metadata keys supported:
*
* | Key | Description |
* |-------------------|--------------------------------------------------------------------|
* | `match_subdomain` | Set to `"true"` to also match subdomains of the name. |
* | `regexp` | RouterOS regexp for query matching. |
* | `address_list` | RouterOS address list to populate with resolved addresses. |
* | `comment` | Comment stored on the RouterOS record. |
*
* ```javascript
* D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
* // Forward all queries for example.com and subdomains to 8.8.8.8,
* // add resolved addresses to the "vpn-list" address list.
* MIKROTIK_FWD("@", "8.8.8.8", {match_subdomain: "true", address_list: "vpn-list"}),
*
* // Forward internal.example.com to a named forwarder entry.
* MIKROTIK_FWD("internal", "corp-dns", {match_subdomain: "true"}),
* );
* ```
*
* @see https://docs.dnscontrol.org/language-reference/domain-modifiers/service-provider-specific//mikrotik_fwd
*/
declare function MIKROTIK_FWD(name: string, target: string, ...modifiers: RecordModifier[]): DomainModifier;
/**
* `MIKROTIK_NXDOMAIN` creates a RouterOS NXDOMAIN static entry. The router will respond with NXDOMAIN for any DNS queries matching the specified name. This is commonly used for DNS-based ad blocking or blackholing.
*
* See the [MikroTik RouterOS provider page](../../provider/mikrotik.md) for full configuration details.
*
* Metadata keys supported:
*
* | Key | Description |
* |-------------------|--------------------------------------------------------------------|
* | `match_subdomain` | Set to `"true"` to also match subdomains of the name. |
* | `regexp` | RouterOS regexp for query matching. |
* | `comment` | Comment stored on the RouterOS record. |
*
* ```javascript
* D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
* // Block ads.example.com with NXDOMAIN.
* MIKROTIK_NXDOMAIN("ads"),
*
* // Block tracking.example.com and all its subdomains.
* MIKROTIK_NXDOMAIN("tracking", {match_subdomain: "true"}),
* );
* ```
*
* @see https://docs.dnscontrol.org/language-reference/domain-modifiers/service-provider-specific//mikrotik_nxdomain
*/
declare function MIKROTIK_NXDOMAIN(name: string, ...modifiers: RecordModifier[]): DomainModifier;
/**
* `MX` adds a [Mail exchange record](https://www.rfc-editor.org/rfc/rfc1035) to the domain.
*
@@ -3126,7 +3269,7 @@ declare const PURGE: DomainModifier;
* * _S3 bucket_ (configured as website): specify the hosted zone ID for the region that you created the bucket in. You can find it in [the List of regions and hosted Zone IDs](https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region)
* * _Another Route 53 record_: you can either specify the correct zone id or do not specify anything and DNSControl will figure out the right zone id. (Note: Route53 alias can't reference a record in a different zone).
*
* Target health evaluation can be enabled with the [`R53_EVALUATE_TARGET_HEALTH`](../record-modifiers/R53\_EVALUATE\_TARGET\_HEALTH.md) record modifier.
* Target health evaluation can be enabled with the [`R53_EVALUATE_TARGET_HEALTH`](../record-modifiers/R53_EVALUATE_TARGET_HEALTH.md) record modifier.
*
* ```javascript
* D("example.com", REG_MY_PROVIDER, DnsProvider("ROUTE53"),