FOR DEVELOPERS / DNS DATASETS
Distribute small datasets through DNS.
Publish example data files as DNS records, list datasets in a pinned catalogue, and read them through direct DNS or DNS-over-HTTPS. This is a separate data transport; it does not install a browser app or turn DNS into unlimited storage.
Quickstart: compile, publish, and check one TXT dataset
Use compiler version 0.27.3 or newer. The example domain, resolver, and zone ID below are placeholders: replace them with a domain you control, a reachable resolver, and that domain's Cloudflare zone ID. Create sample-a.json with small JSON data:
{"name":"sample","tiles":[[0,1],[1,0]]}- Compile. Export deterministic TXT records and note the SHA-256 printed by the compiler. The zone file is for manual import; this command does not upload anything.
amw-dns export sample-a.json sample-a.data.example.test sample-a.zone txt - Publish. Import
sample-a.zoneon a compatible DNS server, or use Cloudflare's separatecfCLI. Sign in withcf auth loginor set a zone-scoped DNS Edit token inCLOUDFLARE_API_TOKEN.amw-dnsis included with the Node compiler package. It generates the same TXT records from the input file, previews create/edit/skip counts, and publishes data records before the root.amw-dns publish-cloudflare sample-a.json sample-a.data.example.test 0123456789abcdef0123456789abcdef --dry-run amw-dns publish-cloudflare sample-a.json sample-a.data.example.test 0123456789abcdef0123456789abcdef - Read back. After DNS propagation, supply the digest printed in step 1 from trusted configuration. The Reader creates a new output file only after the fetched bytes match it.
amw-dns fetch sample-a.data.example.test YOUR_TRUSTED_SHA256 recovered.json https://dns.example.net/dns-query txt
For multiple datasets, build a pinned catalogue and publish it after the dataset records verify. For provider-neutral or Route 53 publication, use the manual zone and API-plan exports. Raw TYPE65280 RDATA requires a server that supports private-use record types; common hosted providers use TXT. DNS availability, propagation, and provider quotas still apply.
1. Choose names and trusted pins
Give each dataset its own DNS zone name, such as sample-a.data.example.test. TXT records carry base64url text. Private-use TYPE65280 records carry raw bytes when the DNS server and provider support that type. The root record declares size, chunk count, and SHA-256; each chunk has its own numbered owner name. A Reader checks the supplied SHA-256 before returning file bytes. Keep the pin in an independently trusted configuration, signed message, or app release. A pin discovered only from the same untrusted DNS answer provides no authentication.
Use bounded dataset files; the current file ceiling is 4 MiB, and a catalogue is at most 64 KiB with 256 datasets and four resolver locations per dataset. TXT is the portable provider option. DNS providers impose their own record, query, zone, and billing limits.
2. Compile dataset records
amw-dns export sample-a.json sample-a.data.example.test sample-a.zone txt
amw-dns export sample-b.json sample-b.other.example.test sample-b.zone rdataThe command writes master-file records and prints the file digest. Store each digest with your trusted release metadata. The raw RDATA option is for DNS servers that accept private-use record types, such as an appropriately configured CoreDNS or BIND server. Test the exact server configuration before publishing.
3. Build a cross-zone catalogue
Create a JSON configuration listing a revision and datasets. Each dataset names its file, zone, mode, and resolver locations. This example uses a public DoH endpoint plus a direct DNS server for one dataset, and a separate server for another:
{
"revision": 1,
"datasets": [
{ "id": "sample.a", "path": "sample-a.json",
"zone": "sample-a.data.example.test", "mode": "txt",
"resolvers": [
{ "kind": "doh", "url": "https://dns.example.net/dns-query" },
{ "kind": "dns", "server": "192.0.2.53", "port": 53 }
] },
{ "id": "sample.b", "path": "sample-b.json",
"zone": "sample-b.other.example.test", "mode": "rdata",
"resolvers": [{ "kind": "dns", "server": "198.51.100.53", "port": 53 }] }
]
}amw-dns build-catalogue datasets.json catalogue.json
amw-dns export catalogue.json catalogue.data.example.test catalogue.zone txtDistribute the printed catalogue SHA-256 separately from DNS. The catalogue itself is canonical JSON; it binds dataset IDs to zones, modes, byte lengths, digests, and resolver locations. Publish all dataset records and verify them before making the catalogue root visible. When updating data, advance the catalogue revision and distribute its new pin through the trusted channel. A pin alone does not enforce freshness against an older, previously trusted release.
4. Publish through Cloudflare or prepare manual plans
For a Cloudflare zone, install a Node.js version supported by the current cf package and the separate Cloudflare cf CLI. Sign in with cf auth login or set CLOUDFLARE_API_TOKEN to a zone-scoped DNS Edit token. This command is separate from Wrangler:
npm install --global cf
amw-dns publish-cloudflare sample-a.json sample-a.data.example.test 0123456789abcdef0123456789abcdef --dry-run
amw-dns publish-cloudflare sample-a.json sample-a.data.example.test 0123456789abcdef0123456789abcdefIf cf is missing, the tool offers to install it in an interactive terminal; scripts and CI receive installation guidance and fail. Dry run reads existing TXT records and reports create, edit, and skip counts without changing DNS. Publication reconciles records, checks each write, and writes the root last. It does not remove stale records or wait for public DNS caches. Use new versioned dataset names when possible, verify them through the intended resolver, then publish the new catalogue and trusted pin. Editing chunks under a live root can temporarily interrupt readers.
Manual exports remain available:
amw-dns plan-provider sample-a.json sample-a.data.example.test cloudflare-plan.json cloudflare 0123456789abcdef0123456789abcdef
amw-dns plan-provider sample-a.json sample-a.data.example.test route53-plan.json route53 ZEXAMPLE123These commands create credential-free JSON plans for Cloudflare DNS API and AWS Route 53 and send no requests. The provider paths use TXT only; use your own server configuration for raw RDATA. A controlled resolver may require direct IP access or a private DoH service; a browser may be unable to reach it.
5. Read pinned data
The Node.js and .NET DNS Reader APIs below require their respective 0.27.3 or newer packages. Check the registry version before installing; the native SDK has its own preview version.
Node.js:
import { directDnsResolver } from 'amoosedweb-reader';
import { readDnsCatalogue, readDnsDataset } from 'amoosedweb-reader';
const catalogue = await readDnsCatalogue('catalogue.data.example.test',
TRUSTED_CATALOGUE_SHA256, directDnsResolver('192.0.2.53'));
const dataset = await readDnsDataset(catalogue, 'sample.a');.NET 10:
using System.Net;
using AmoosedWeb.Reader;
var server = new IPEndPoint(IPAddress.Parse("192.0.2.53"), 53);
var catalogue = await AmoosedDnsReader.ReadCatalogueAsync(
"catalogue.data.example.test", trustedCatalogueSha256, server);
byte[] dataset = await AmoosedDnsReader.ReadDatasetAsync(catalogue, "sample.a");
// Single file: ReadBytesAsync(zone, trustedFileSha256, server) or a DoH Uri.Native C++ (pass the catalogue SHA-256 from trusted configuration):
#include "AmoosedWeb/DnsCatalogue.hpp"
#ifdef _WIN32
#include "AmoosedWeb/WindowsCrypto.hpp"
#else
#include "AmoosedWeb/OpenSslCrypto.hpp"
#endif
#include <iostream>
#include <vector>
int main(int argc, char** argv) {
if (argc != 2) return 2; // argv[1]: independently trusted 64-hex digest
#ifdef _WIN32
amoosed::WindowsCryptoHost crypto;
#else
amoosed::OpenSslCryptoHost crypto;
#endif
amoosed::DirectDnsResolver resolver(crypto, "192.0.2.53");
amoosed::DnsCatalogue catalogue;
std::string error;
if (!amoosed::read_dns_catalogue(resolver, crypto,
"catalogue.data.example.test", argv[1], catalogue, error)) {
std::cerr << error << '\n'; return 1;
}
std::vector<std::uint8_t> dataset;
if (!amoosed::read_dns_dataset(catalogue, "sample.a", crypto, dataset, error)) {
std::cerr << error << '\n'; return 1;
}
std::cout << "Verified " << dataset.size() << " bytes\n";
}The catalogue pins each dataset's hash and its direct DNS or DoH resolver. read_dns_dataset returns bytes only after that hash and length verify. The example addresses and digest are placeholders.
C preview ABI revision 20:
amw_preview_dns_blob* blob = NULL;
char error[256] = {0};
int32_t status = amw_preview_dns_open_catalogue_dataset(
catalogue_zone, catalogue_zone_length, trusted_pin, 64,
AMW_PREVIEW_DNS_DIRECT, server_ip, server_ip_length, 53,
dataset_id, dataset_id_length, &blob, error, sizeof error);
if (status != AMW_PREVIEW_OK) { /* inspect status */ exit(1); }
uint64_t required = 0;
status = amw_preview_dns_blob_copy(blob, NULL, 0, &required);
/* Allocate at most 4 MiB, copy verified bytes, then release blob. */
amw_preview_dns_blob_release(blob);Use the C ABI reference and its exact header for signatures, ownership, and status codes. The C API is synchronous; run network reads away from a UI thread. The .NET direct reader uses UDP with TCP fallback; DoH uses RFC 8484 binary messages. All returned bytes must pass the independent pin. The Readers do not treat a catalogue URL or a public key supplied by a dataset as trusted merely because it came from DNS.
6. Verify your deployment
Query through the resolvers users will actually reach. Check every dataset with its trusted pin, change one answer to confirm rejection, and test missing records and provider response limits. The repository's CoreDNS and BIND container gate covers a TXT sample, a raw RDATA sample, a cross-zone catalogue, direct DNS, DoH, and incorrect pins. DNS caches can delay updates; keep old and new records available during propagation and switch the trusted catalogue pin only after the new dataset resolves consistently.
For exact wire fields and the cryptographic boundary, see the format specification and Reader API reference.