Skip to content

frol/near-dns

Repository files navigation

NEAR DNS

A decentralized DNS system that resolves blockchain-based domain names by querying smart contracts on NEAR Protocol.

Overview

NEAR DNS enables domain name resolution for NEAR ecosystem TLDs (.near, .testnet, etc.) by storing DNS records in smart contracts. Each NEAR account can deploy a DNS contract as a subaccount (dns.<account>.<tld>) to manage their domain's DNS records.

Key Design Principle: NEAR DNS is not intended to be a centralized DNS provider. Since all DNS records are stored on the NEAR blockchain, the DNS server itself is essentially a stateless gateway that translates DNS queries into blockchain lookups. Self-hosting is encouraged — you can run your own instance and get the exact same results as any other instance, because the source of truth is always the blockchain.

How It Works

  1. DNS Query: Client queries example.near A record
  2. TLD Detection: Server identifies .near as a NEAR TLD
  3. Contract Lookup: Server queries dns.example.near contract for records
  4. Response: DNS records are returned from the blockchain

For traditional domains (.com, .org, etc.), queries are forwarded to upstream DNS servers (Google/Cloudflare).

Architecture

┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  DNS Client │────▶│  NEAR DNS Server │────▶│  NEAR Blockchain│
│   (dig)     │◀────│   (Rust/Hickory) │◀────│  (DNS Contract) │
└─────────────┘     └──────────────────┘     └─────────────────┘
                            │
                            ▼
                    ┌───────────────┐
                    │ Upstream DNS  │
                    │ (Google/CF)   │
                    └───────────────┘

Learn More

Public DNS Server

There is currently one publicly available NEAR DNS server connected to mainnet:

DNS Server: 185.149.40.161 (port 53)

Try it out:

# Query a mainnet domain
dig @185.149.40.161 neardns.near A

# Expected response:
# neardns.near.    1    IN    A    185.149.40.161

Note: This public server is provided for convenience, but self-hosting is encouraged. Since NEAR DNS is stateless (all data comes from the blockchain), running your own instance gives you the same results with better privacy and no single point of failure.

Components

DNS Server (dns-server/)

A Rust DNS server built with Hickory DNS that:

  • Resolves NEAR domains by querying smart contracts via RPC
  • Forwards non-NEAR domains to upstream DNS servers
  • Supports hierarchical subdomain resolution
  • Implements wildcard record matching
  • Caches responses for performance

DNS Contract (dns-contract/)

A NEAR smart contract that stores DNS records with:

  • Support for common record types (A, AAAA, CNAME, MX, TXT, etc.)
  • Owner-only record management (parent account controls records)
  • Wildcard record support (* entries)
  • Iterable storage for listing all records

Quick Start

Prerequisites

  • Rust 1.86
  • NEAR CLI
  • A NEAR account (testnet or mainnet)

Running the DNS Server

For Mainnet

# Clone and build
git clone https://github.com/frol/near-dns
cd near-dns
cargo build --release --package near-dns-server

# Run the server (mainnet)
RUST_LOG=info ./target/release/near-dns-server \
  --bind 127.0.0.1:5355 \
  --rpc-url https://rpc.mainnet.near.org

# Test with dig
dig @127.0.0.1 -p 5355 neardns.near A

For Testnet

# Run the server (testnet)
RUST_LOG=info ./target/release/near-dns-server \
  --bind 127.0.0.1:5355 \
  --rpc-url https://rpc.testnet.near.org

# Test with dig
dig @127.0.0.1 -p 5355 near-dns.testnet A
dig @127.0.0.1 -p 5355 near-dns.testnet TXT

Non-NEAR domains are forwarded to upstream DNS servers:

dig @127.0.0.1 -p 5355 google.com A  # Forwarded upstream

Running with Docker

Mainnet (Default)

# Run with mainnet RPC (default)
docker run -d --name near-dns \
  -p 53:53/udp \
  -p 53:53/tcp \
  frolvlad/near-dns

# Test it
dig @localhost neardns.near A

Testnet

# Run with testnet RPC
docker run -d --name near-dns-testnet \
  -p 5355:53/udp \
  -p 5355:53/tcp \
  frolvlad/near-dns \
  --bind 0.0.0.0:53 \
  --rpc-url https://rpc.testnet.near.org

# Test it
dig @localhost -p 5355 near-dns.testnet A

Additional Options

# Run with custom log level
docker run -d --name near-dns \
  -e RUST_LOG=debug \
  -p 53:53/udp \
  -p 53:53/tcp \
  frolvlad/near-dns

# View logs
docker logs -f near-dns

Building from Source

docker build -t near-dns .
docker run -d -p 5355:53/udp -p 5355:53/tcp near-dns

Register Your Own Domain (aka Deploying Your Own DNS Contract)

Build the contract first using cargo near:

cd dns-contract
cargo near build

Mainnet

# Create a subaccount for DNS
near account create-account fund-myself dns.youraccount.near '2.1 NEAR' \
  autogenerate-new-keypair save-to-keychain \
  sign-as youraccount.near network-config mainnet sign-with-keychain send

# Build the contract
cd dns-contract
cargo near build non-reproducible-wasm

# Deploy with initialization
near contract deploy dns.youraccount.near \
  use-file target/near/dns_contract.wasm \
  with-init-call new json-args '{}' \
  prepaid-gas '30 Tgas' attached-deposit '0 NEAR' \
  network-config mainnet sign-with-keychain send

Testnet

Unlike mainnet, you can create a new account and get some free NEAR tokens on testnet using this simple command:

near account create-account sponsor-by-faucet-service
# Create a subaccount for DNS
near account create-account fund-myself dns.youraccount.testnet '2.1 NEAR' \
  autogenerate-new-keypair save-to-keychain \
  sign-as youraccount.testnet network-config testnet sign-with-keychain send

# Deploy with initialization
near contract deploy dns.youraccount.testnet \
  use-file target/near/dns_contract.wasm \
  with-init-call new json-args '{}' \
  prepaid-gas '30 Tgas' attached-deposit '0 NEAR' \
  network-config testnet sign-with-keychain send

Managing DNS Records

The examples below use testnet. For mainnet, replace .testnet with .near and network-config testnet with network-config mainnet.

# Add an A record
near contract call-function as-transaction dns.youraccount.testnet dns_add \
  json-args '{"name": "@", "record": {"record_type": "A", "value": "1.2.3.4", "ttl": 300, "priority": null}}' \
  prepaid-gas '30 Tgas' attached-deposit '0 NEAR' \
  sign-as youraccount.testnet network-config testnet sign-with-keychain send

# Add a subdomain
near contract call-function as-transaction dns.youraccount.testnet dns_add \
  json-args '{"name": "www", "record": {"record_type": "A", "value": "1.2.3.5", "ttl": 300, "priority": null}}' \
  prepaid-gas '30 Tgas' attached-deposit '0 NEAR' \
  sign-as youraccount.testnet network-config testnet sign-with-keychain send

# Add a wildcard record (matches any subdomain)
near contract call-function as-transaction dns.youraccount.testnet dns_add \
  json-args '{"name": "*", "record": {"record_type": "A", "value": "1.2.3.100", "ttl": 300, "priority": null}}' \
  prepaid-gas '30 Tgas' attached-deposit '0 NEAR' \
  sign-as youraccount.testnet network-config testnet sign-with-keychain send

# Query records
near contract call-function as-read-only dns.youraccount.testnet dns_query \
  json-args '{"name": "@", "record_type": "A"}' \
  network-config testnet now

# List all records
near contract call-function as-read-only dns.youraccount.testnet dns_list_all \
  json-args '{}' network-config testnet now

Contract API

View Methods

Method Arguments Description
dns_query name: String, record_type: String Query specific record type for a name
dns_query_all name: String Get all record types for a name
dns_list_names - List all DNS names with records
dns_list_all - List all records in the contract
get_owner - Get the contract owner

Change Methods

Method Arguments Description
dns_add name: String, record: DnsRecord Add a DNS record
dns_update name: String, records: Vec<DnsRecord> Replace all records of a type
dns_delete name: String, record_type: Option<String> Delete records
transfer_ownership new_owner: AccountId Transfer contract ownership

DnsRecord Structure

{
  "record_type": "A",
  "value": "192.168.1.1",
  "ttl": 300,
  "priority": null
}

Supported record types: A, AAAA, CNAME, MX, TXT, NS, SRV, SOA, PTR, CAA

Resolution Logic

For a query like sub.example.near:

  1. Check if near is a known NEAR TLD
  2. Try dns.sub.example.near with name @
  3. Try dns.example.near with name sub
  4. Try dns.example.near with name * (wildcard)
  5. Return NXDOMAIN if no records found

Supported TLDs

The DNS server recognizes these NEAR TLDs:

  • near (mainnet)
  • testnet
  • aurora
  • tg
  • sweat
  • kaiching
  • sharddog

All other TLDs are forwarded to upstream DNS servers.

Deployed Contracts

Mainnet

  • Contract: dns.neardns.near
  • Owner: neardns.near

Testnet

  • Contract: dns.near-dns.testnet
  • Owner: near-dns.testnet

Development

# Run DNS server tests
cd dns-server
cargo test

# Run contract tests
cd dns-contract
cargo test

# Build contract WASM
cd dns-contract
cargo near build non-reproducible-wasm

License

Licensed under either of:

at your option.

Contributing

See CONTRIBUTING.md for guidelines.

About

Demo of how NEAR can be used as a storage layer for decentralized DNS records

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors