Compare commits

..

10 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
6 changed files with 223 additions and 76 deletions
+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
```
+37 -72
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,15 +11,24 @@ 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_BUILDER({
iodef: "mailto:" + reportEmail,
iodef: reportEmail,
iodef_critical: true,
issue: ["letsencrypt.org"],
issue_critical: true,
@@ -31,17 +42,26 @@ DEFAULTS(
);
/* ****************************************************************************************************************** *\
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,
@@ -56,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"),
);
@@ -82,32 +101,32 @@ cnames("arirex.me", rexbox, [
"Enclosed@bin",
"IT Tools@it",
"Karakeep@karakeep",
"Matrix / Client@chat",
"Matrix / Server@matrix",
"Minecraft@mc",
"Matrix > Client@chat",
"Matrix > Server@matrix",
"Ntfy@ntfy",
"OpenWebUI@ai",
"Pocket ID@id",
// "Prompts@prompts",
"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 Subdomain Gateway@*.ipfs.gw",
"IPFS Subdomain Gateway@*.ipns.gw",
"IPFS Path Gateway@gw",
// "LibreSpeed@speedtest",
// "IPFS Subdomain Gateway@*.ipfs.gw",
// "IPFS Subdomain Gateway@*.ipns.gw",
// "IPFS Path Gateway@gw",
"SearXNG@search",
]);
@@ -124,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;
}
+1 -1
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.35.0
// 4.36.1
// WARNING: These type definitions are experimental and subject to change in future releases.
interface Domain {