atlibs-marque
An atlibs wasm module (knotless://johnandersen777.bsky.social/atlibs) and a page that edit the DNS records of the domains an account registered through Marque, the AT Protocol registrar.
How Marque keeps DNS
The lexicons are published by the at.marque authority (_lexicon.marque.at TXT ->
did:plc:nckosudltxrtrjkt4zz4jy5y, com.atproto.lexicon.schema records on its PDS):
at.marque.domain: one record per domain in the owner's repo, keyed by the FQDN (domain,status,registeredAt,expiresAt,nameServers,dnssec,autoRenew, ...). Marque's partner flow writes it into the repo of the DID that paid for the domain; that record is what this module takes as "the account owns this domain".at.marque.dns: the domain's whole zone in ONE record with the same key:domain,subject(a strongRef to theat.marque.domainrecord),records(at.marque.dns#entry:namerelative to the apex or@,recordTypeA/AAAA/CNAME/MX/TXT/SRV/NS/CAA,value,ttl,priorityfor MX and SRV) andcreatedAt.
Marque's docs say its nameserver consumes the public
at.marque.dns record, so an edit is only a repo write: no XRPC call to Marque. (at.marque.dns.getRecords,
a query on did:web:marque.at#marque_registrar for the records it actually serves, exists but no Marque
permission set grants it; this module does not call it.)
The module
modules/marque, session records/marque.json, scope
atproto repo:at.marque.dns?action=create&action=update: it can write DNS zones and nothing else (not
at.marque.domain, and it never deletes a zone).
| entry | input | does |
|---|---|---|
list_domains | the account's at.marque.domain records (paged) | |
list_records | domain | the domain's zone: records, uri, cid, the domain's status |
create_record | domain name type value [ttl] [priority] | adds a record (TTL defaults to 3600) |
update_record | domain name type value [newName] [newValue] [ttl] [priority] | changes the record named by name, type and value; blank keeps its own |
delete_record | domain name type value | removes that record |
Reads are public reads of the account's own PDS, so listing works signed out for any DID. Writes read the
domain and zone records, refuse a domain with no at.marque.domain record in the repo, and put the zone
back with swapRecord (a concurrent edit fails with "try again" instead of being lost), keeping fields
Marque added (such as baseRevision) and pointing subject at the domain record as it is now. A domain
with no zone yet gets one with createRecord. Calls: listRecords, getRecord, createRecord,
putRecord; anyRecords: ["at.marque.dns"], as Marque writes the zone first.
Checked in the module before any call: the name (@, or relative labels of a-z0-9-_, a leading *
label, no trailing dot; SRV names _service._proto; no NS at @, those are the domain's nameservers),
the value per type (IPv4, IPv6, hostnames, SRV weight port target, CAA flags tag value, TXT 1 to 4096
bytes without control characters), TTL 1 to 2^31-1, priority 0 to 65535 on MX and SRV only; and against
the zone: no exact duplicates, a CNAME alone at its name. Values are stored as Marque stores them:
hostnames lowercase without the trailing dot, IPv6 compressed.
Layout
modules/marque: the module (Rust, against atlibs'abiSDK), itsmodule.jsonandtest.mjsweb/marque: a page: sign in, your domains, a domain's records with add, edit and removerecords/marque.json: the session record
Set up
atlibs is a sibling checkout: Cargo fetches the abi crate from git, and the tests and the page import
its JavaScript from ../atlibs.
git clone https://knotless.dev/johnandersen777.bsky.social/atlibs ../atlibs && git -C ../atlibs checkout -q 88b2ba8
Build and test
cargo wasm
node --test modules/*/test.mjs web/*/*.test.mjs
(cd ../atlibs && cargo host) # the atlibs CLI, for the Rust host
node --import ../atlibs/rust/host/swap.mjs --test modules/*/test.mjs # the same, on the Rust host
web/marque/marque.test.mjs boots the page in headless Chrome (skipped without Chrome).
Run
python3 -m http.server 8000 --bind 127.0.0.1 --directory web/marque
Open http://127.0.0.1:8000/ (?module=bobvmbuilder.bsky.social/marque runs the published module record instead
of your build).
From a shell or a server, the same entries (reads need no sign-in):
atlibs run modules/marque list_domains '{"did":"did:plc:..."}'
atlibs run modules/marque list_records '{"did":"did:plc:...","domain":"example.com"}'
atlibs login you.example
atlibs run modules/marque create_record '{"domain":"example.com","name":"_atlibs-test","type":"TXT","value":"hello"}' --as you.example
atlibs stage --target spa --origin https://you.example/app/ --out dist modules/marque stages the module
and its OAuth client for a page.
