MCPcopy Index your code
hub / github.com/serverless-dns/serverless-dns

github.com/serverless-dns/serverless-dns @v1.4.9 sqlite

repository ↗ · DeepWiki ↗ · release v1.4.9 ↗
485 symbols 1,016 edges 46 files 34 documented · 7%
README

It's a bird, it's a plane, it's... a self-hosted, pi-hole esque, DNS resolver

serverless-dns is a Pi-Hole esque content-blocking, serverless, stub DNS-over-HTTPS (DoH) and DNS-over-TLS (DoT) resolver. Runs out-of-the-box on Cloudflare Workers, Deno Deploy, and Fly.io. Free tiers of all these services should be enough to cover 10 to 20 devices worth of DNS traffic per month.

The RethinkDNS resolver

RethinkDNS runs serverless-dns in production at these endpoints:

Cloud platform Server locations Protocol Domain Usage
⛅ Cloudflare Workers 200+ (ping) DoH basic.rethinkdns.com configure
🦕 Deno Deploy 30+ (ping) DoH private beta
🪂 Fly.io 30+ (ping) DoH and DoT max.rethinkdns.com configure

Server-side processing takes from 0 milliseconds (ms) to 2ms (median), and end-to-end latency (varies across regions and networks) is between 10ms to 30ms (median).

Self-host

Cloudflare Workers is the easiest platform to setup serverless-dns:

Deploy to Cloudflare Workers

For step-by-step instructions, refer:

Platform Difficulty Runtime Doc
⛅ Cloudflare Easy v8 Isolates Hosting on Cloudflare Workers
🦕 Deno.com Moderate Deno Isolates Hosting on Deno.com
🪂 Fly.io Hard Node MicroVM Hosting on Fly.io

To setup blocklists, visit https://<my-domain>.tld/configure from your browser (it should load something similar to RethinkDNS' configure page).

For help or assistance, feel free to open an issue or submit a patch.


Development

Setup

Code:

# navigate to work dir
cd /my/work/dir

# clone this repository
git clone https://github.com/serverless-dns/serverless-dns.git

# navigate to serverless-dns
cd ./serverless-dns

Node:

# install node v16+ via nvm, if required
# https://github.com/nvm-sh/nvm#installing-and-updating
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
nvm install --lts

# get js dependencies
npm i

# (optional) update dependencies
npm update

# run serverless-dns on node
./run n

# run a clinicjs.org profiler
./run n [cpu|fn|mem]

Deno:

# install deno.land v1.18+
# https://github.com/denoland/deno/#install
curl -fsSL https://deno.land/install.sh | sh

# run serverless-dns on deno
./run d

Wrangler:

# install Cloudflare Workers (cli) aka Wrangler
# https://developers.cloudflare.com/workers/cli-wrangler/install-update
npm i @cloudflare/wrangler -g

# run serverless-dns on Cloudflare Workers (cli)
# Make sure to setup Wrangler first:
# https://developers.cloudflare.com/workers/cli-wrangler/authentication
./run w

# profile wrangler with Chrome DevTools
# blog.cloudflare.com/profiling-your-workers-with-wrangler

Code style

Commits on this repository enforces the Google JavaScript style guide (ref: .eslintrc.cjs). A git pre-commit hook that runs linter (eslint) and formatter (prettier) on .js files. Use git commit --no-verify to bypass this hook.

Pull requests are also checked for code style violations and fixed automatically where possible.

Env vars

Configure env.js if you need to tweak the defaults. For Cloudflare Workers, setup env vars in wrangler.toml, instead.

Request flow

  1. The request/response flow: client <-> src/server-[node|workers|deno] <-> doh.js <-> plugin.js
  2. The plugin.js flow: userOperation.js -> cacheResponse.js -> cc.js -> dnsResolver.js

A note about runtimes

Deno Deploy (cloud) and Deno (the runtime) do not expose the same API surface (for example, Deno Deploy only supports HTTP/S server-listeners; whereas, Deno suports raw TCP/UDP/TLS in addition to plain HTTP and HTTP/S).

Except on Node, serverless-dns uses DoH upstreams defined by env vars, CF_DNS_RESOLVER_URL / CF_DNS_RESOLVER_URL_2. On Node, the default DNS upstream is 1.1.1.2 (ref).

The entrypoint for Node and Deno are src/server-node.js, src/server-deno.ts respectively, and both listen for TCP-over-TLS, HTTP/S connections; whereas, the entrypoint for Cloudflare Workers, which only listens over HTTP (cli) or over HTTP/S (prod), is src/server-workers.js.

For prod setups on Deno and local (non-prod) setups on Node, the key (private) and cert (public chain) files, by default, are read from paths defined in env vars, TLS_KEY_PATH and TLS_CRT_PATH.

Whilst for prod setup on Node (on Fly.io), either TLS_OFFLOAD must be set to true or key and cert must be base64 encoded in env var TLS_CERTKEY (ref), like so:

# EITHER: offload tls to fly.io and set tls_offload to true
TLS_OFFLOAD="true"
# OR: base64 representation of both key (private) and cert (public chain)
TLS_CERTKEY="KEY=b64_key_content\nCRT=b64_cert_content"

Process bringup is different for each of these runtimes: For Node, src/core/node/config.js governs the bringup; while for Deno, it is src/core/deno/config.ts and for Workers it is src/core/workers/config.js. src/system.js pub-sub co-ordinates the bringup phase among various modules.

On Node and Deno, in-process DNS caching, backed by @serverless-dns/lfu-cache is used; on Cloudflare Workers, both, Cache Web API and in-process caches are used. To disable caching altogether on all three platfroms, set env var, PROFILE_DNS_RESOLVES=true.

Cloud

Cloudflare Workers and Deno Deploy are ephemeral, as in, the process that serves client request is not long-lived, and in fact, two back-to-back requests may be served by two different isolates (processes). Resolver on Fly.io, running Node, is backed by persistent VMs and is hence longer-lived, like traditional "serverfull" environments.

Cloudflare Workers build-time and runtime configurations are defined in wrangler.toml. Webpack5 bundles the files in an ESM module which is then uploaded to Cloudflare by Wrangler.

For Deno Deploy, the code-base is bundled up in a single javascript file with deno bundle and then handed off to Deno.com.

For Fly.io, which runs Node, the runtime directives are defined in fly.toml (used by dev and live deployment-types), while deploy directives are in node.Dockerfile. flyctl accordingly sets up serverless-dns on Fly.io's infrastructure.

For deploys offloading TLS termination to Fly.io (B1 deployment-type), the runtime directives are instead defined in fly.tls.toml, which sets up HTTP2 Cleartext and HTTP/1.1 on port 443, and DNS over TCP on port 853.

Ref: github/workflows.

Blocklists

190+ blocklists are compressed in a Succinct Radix Trie (based on Steve Hanov's impl) with modifications to speed up string search (lookup) at the expense of "succintness". The blocklists are versioned with unix timestamp (env var: CF_LATEST_BLOCKLIST_TIMESTAMP), and generated once every week, but we'd like to generate 'em daily / hourly, if possible see), and hosted on Lightsail Object Store (env var: CF_BLOCKLIST_URL). serverless-dns downloads 3 blocklist files required to setup the radix trie during runtime bringup or, lazily, when serving a DNS request.

serverless-dns compiles around ~5M entries (as of Feb 2022) in to a succinct radix trie, from around 190+ blocklists. These are defined in serverless-dns/blocklists repository.

Extension points exported contracts — how you extend this code

Window (Interface)
(no doc)
src/core/deno/config.ts

Core symbols most depended-on inside this repo

get
called by 59
src/core/env.js
now
called by 25
src/core/log.js
set
called by 20
src/core/env.js
stub
called by 20
src/commons/util.js
close
called by 16
src/server-node.js
withTags
called by 15
src/core/log.js
registerParameter
called by 14
src/core/plugin.js
get
called by 14
src/plugins/rethinkdns/trie-cache.js

Shape

Function 286
Method 154
Class 44
Interface 1

Languages

TypeScript100%

Modules by API surface

src/commons/util.js50 symbols
src/commons/envutil.js39 symbols
src/core/dns/transact.js25 symbols
src/core/dns/conns.js25 symbols
src/commons/dnsutil.js25 symbols
src/plugins/rdns-util.js23 symbols
src/server-node.js19 symbols
src/plugins/cache-util.js19 symbols
src/core/io-state.js19 symbols
src/commons/bufutil.js17 symbols
src/plugins/command-control/cc.js16 symbols
src/plugins/rethinkdns/trie.js15 symbols

Dependencies from manifests, versioned

@serverless-dns/dns-parsergithub:serverless-dn · 1×
@serverless-dns/lfu-cachegithub:serverless-dn · 1×
@types/node16.11.7 · 1×
buffer6.0.3 · 1×
clinic11.1.0 · 1×
dotenv10.0.0 · 1×
eslint8.5.0 · 1×
eslint-config-google0.14.0 · 1×
httpx-server1.4.3 · 1×
husky7.0.4 · 1×
lint-staged12.1.4 · 1×

For agents

$ claude mcp add serverless-dns \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact