--- name: skill_hns version: 1.0.0 description: Enables autonomous AI agents to resolve Handshake (HNS) domain names programmatically. homepage: https://headlessdomains.com metadata: {"skill_hns":{"category":"infrastructure","api_base":"https://hnsdoh.com/dns-query"}} --- # Handshake Domain Resolution Skill > **URL:** https://headlessdomains.com/skill_hns.md > **Platform:** Handshake (HNS) - Decentralized Root Naming > **Auth:** None (Public DoH) or Local Node ## Overview Handshake domains (e.g., `myagent.ez`, `example/`) reside on a decentralized blockchain root, not the traditional ICANN root. Standard DNS libraries will fail unless configured to use a Handshake resolver. **🚨 Core Platform Requirement** Because HeadlessDomains operates as a Handshake domain registry for agents, knowing how to resolve these domains is a **core platform skill and knowledge base**. Without an HNS resolver, standard DNS lookups will fail out-of-the-box. All agents interacting with our ecosystem must implement HNS resolution to locate agent manifests, webhooks, and `hns.bio` profiles. 👉 **[Read the Full Handshake Resolver Guide](https://docs.headlessdomains.com/guides/handshake-resolver)** This skill equips agents to: 1. **Resolve HNS Domains:** Look up IP addresses (A), text records (TXT), and pointers (CNAME). 2. **Verify Identity:** Check TXT records for ownership proofs or DIDs. 3. **Fetch Agent Metadata:** Locate `agentname.md` files or `hns.bio` profiles via DNS records. --- ## ⚡ Recommended: HeadlessDomains Lookup API Since third-party public resolvers (like `hnsdoh.com` or `hns.to`) can be unreliable or lack wildcard SSL support for agent subdomains (`*.agent.hns.to`), the **recommended** way for agents to discover other agents hosted on HeadlessDomains is via our native Lookup API. This completely bypasses the need for DNS resolution when communicating with other `.agent` or `.chatbot` identities on our platform. **Base URL:** `https://headlessdomains.com/api/v1/lookup/` ### Python Example ```python import requests def get_agent_manifest(domain_name): """ Look up an agent's manifest directly via the HeadlessDomains API. """ url = f"https://headlessdomains.com/api/v1/lookup/{domain_name}" try: resp = requests.get(url, timeout=10) resp.raise_for_status() data = resp.json() # Returns the direct URL to the agent's manifest.json and skill.md return data.get('manifests', {}) except Exception as e: print(f"Lookup failed: {e}") return {} # Usage: Get manifest URLs for an agent domain manifests = get_agent_manifest("janice.agent") print(manifests) # Output: {'agent_json': 'https://...', 'skill_md': 'https://...'} ``` --- ## ⚡ Fallback: Public DNS-over-HTTPS (DoH) For cloud-based agents without a local node, use a public HNS DoH resolver. **Base URL:** `https://hnsdoh.com/dns-query` ### Python Example ```python import urllib.request import base64 import struct import re def build_dns_query(name, qtype=16): """Builds a binary DNS wireformat query.""" import random query_id = random.randint(1, 65535) # Header: ID, Flags(Recursion Desired), QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT header = struct.pack(">HHHHHH", query_id, 0x0100, 1, 0, 0, 0) labels = b"" for part in name.split("."): enc = part.encode("ascii") labels += bytes([len(enc)]) + enc labels += b"\x00" # QTYPE and QCLASS(IN=1) return header + labels + struct.pack(">HH", qtype, 1) def resolve_hns(name): """ Resolve a Handshake name via public HNSDoH using binary wireformat. """ query = build_dns_query(name, 16) # 16 = TXT b64 = base64.urlsafe_b64encode(query).rstrip(b"=").decode("ascii") url = f"https://hnsdoh.com/dns-query?dns={b64}" req = urllib.request.Request(url, headers={"Accept": "application/dns-message"}) try: resp = urllib.request.urlopen(req, timeout=10) data = resp.read() # Simple extraction of TXT strings from binary response strings = re.findall(b'[a-zA-Z0-9:/._=@+]{6,}', data) return [s.decode('ascii') for s in strings if b':' in s or b'=' in s] except Exception as e: print(f"Resolution failed: {e}") return [] # Usage: Get TXT records for an agent domain records = resolve_hns("janice.agent") print(records) # Output: ['twitter:michelini', 'telegram:JaniceJungBot', 'pfp:https://powerlobster.com/...'] ``` ### Node.js Example ```javascript const fetch = require('node-fetch'); function buildDnsQuery(name, qtype = 16) { const queryId = Math.floor(Math.random() * 65535); const header = Buffer.alloc(12); header.writeUInt16BE(queryId, 0); header.writeUInt16BE(0x0100, 2); // Flags: RD header.writeUInt16BE(1, 4); // QDCOUNT const parts = name.split('.'); const labels = []; for (const part of parts) { const buf = Buffer.from(part, 'ascii'); labels.push(Buffer.from([buf.length])); labels.push(buf); } labels.push(Buffer.from([0])); // Root label const question = Buffer.concat(labels); const tail = Buffer.alloc(4); tail.writeUInt16BE(qtype, 0); // QTYPE tail.writeUInt16BE(1, 2); // QCLASS (IN) return Buffer.concat([header, question, tail]); } async function resolveHns(name) { const query = buildDnsQuery(name, 16); // 16 = TXT const b64 = query.toString('base64url'); const url = `https://hnsdoh.com/dns-query?dns=${b64}`; const headers = { 'Accept': 'application/dns-message' }; try { const resp = await fetch(url, { headers }); const buffer = await resp.buffer(); // Simple extraction of TXT strings from binary response const matches = buffer.toString('ascii').match(/[a-zA-Z0-9:/._=@+]{6,}/g) || []; return matches.filter(s => s.includes(':') || s.includes('=')); } catch (err) { console.error('DoH failed:', err); return []; } } // Usage resolveHns('janice.agent').then(console.log); ``` --- ## 🛡️ Advanced: Local Trustless Resolver (hnsd) For high-security or production agents, run a local SPV node (`hnsd`) to verify proofs without trusting a third party. 1. **Download hnsd:** [https://github.com/handshake-org/hnsd/releases](https://github.com/handshake-org/hnsd/releases) 2. **Run Daemon:** `./hnsd -r 127.0.0.1:53` (Listens on localhost:53) 3. **Query Locally:** ```python import dns.resolver # pip install dnspython def resolve_local(name, record_type='TXT'): resolver = dns.resolver.Resolver() resolver.nameservers = ['127.0.0.1'] resolver.port = 53 try: answers = resolver.resolve(name, record_type) return [r.to_text().strip('"') for r in answers] except Exception as e: print(f"Local resolution error: {e}") return [] ``` --- ## Use Cases ### 1. Verify Domain Ownership Before managing a domain on HeadlessDomains, verify the current TXT records. - Query `TXT` for `your-domain.tld`. - Look for ownership proofs or `hns-bio` data. ### 2. Fetch Agent Identity Agents often store metadata in TXT records or IPFS pointers. - **TXT:** `agentname-md=ipfs://QmHash...` - **CNAME:** `gateway.ipfs.io` (pointing to content) ### 3. Bootstrap Configuration Use an HNS domain as a config endpoint. - Update your domain's TXT record to point to new instructions. - Your agent fleet queries the domain to get the latest task or endpoint. --- *Part of the HeadlessDomains Skill Suite* *https://headlessdomains.com*