Resolver SDK

@agtnames/resolver is a standalone TypeScript library that resolves a .agt name against the AGT Registry and verifies its manifest. It reads the registry over raw JSON-RPC — no wallet library, no API key — and ships the agt-resolve CLI. Read-only by design.

Install

npmsh
npm install @agtnames/resolver

Node 20+, Bun and Deno, and browsers (one caveat under Runtimes). Only dependencies: @noble/curves and @noble/hashes.

Quick start

the full picturets
import { AgtResolver } from "@agtnames/resolver";

const agt = new AgtResolver({ chain: "polygon" });

// Everything: chain state + the manifest, fetched and verified three ways.
const r = await agt.resolveAgent("launchpad.agt");

if (r.verified) {
  r.manifest.endpoints          // owner's claims, safe to act on as such
  r.manifest.capabilities
} else {
  r.reasons                     // e.g. ["no manifest set"] — say so; do not present manifest content as fact
}

Chains

A named chain fills in the RPC endpoint and the deployed contract addresses. localhost has no defaults — pass rpcUrl and registry.

chainidRegistryResolverMigration
polygon1370x5B9386C47395B0551c814cC03b69cbD20eb0C87A0x66Ae037d2A6a770B4772b889b6cA1704504399f20x4276d03AcbcA433D257FBd90c53F090F4B16d38E
amoy800020xd08E0d9BCB26572Eaa22fe27Df53a5D2721D3BCD0xE02f88b9BC0394742bBBe5c590E043B647B834190xC79A3fb86BcC3637BB58cDcd1f6E12Cf3fFDCBc8

Default RPCs: https://polygon-bor-rpc.publicnode.com and https://polygon-amoy-bor-rpc.publicnode.com. The table is exported as CHAINS; contracts are source-verified on Polygonscan.

API

new AgtResolver(options)

all options (each optional except what the chain lacks)ts
new AgtResolver({
  chain: "polygon",                          // polygon | amoy | localhost
  rpcUrl: "https://polygon-bor-rpc.publicnode.com",   // override the public default
  registry: "0x5B9386C47395B0551c814cC03b69cbD20eb0C87A", // required only for localhost / custom
  ipfsGateway: "https://dweb.link/ipfs/",    // used for ipfs:// manifests
  timeoutMs: 15_000,                         // per RPC request (manifest fetch uses 10 s)
  maxManifestBytes: 256 * 1024,
  legacyDns: false,                          // opt-in DNS-over-HTTPS fallback for pre-registry names
});

The constructor throws immediately when it cannot determine an RPC URL or a registry address — configuration errors surface before any call.

Methods

MethodReturns
resolve(name)NameRecord — chain state and records, no manifest fetch.
resolveAgent(name)AgentResolutionNameRecord plus the fetched manifest, CID check and verification.
text(name, key)One text record (e.g. description, url, avatar).
available(name)boolean — can the name be registered right now.
fnsOwner(name)Owner on the legacy registry, or null when no legacy address is configured.

One-shot helpers: resolveAgent(name, options) and isAgent(name, options).

NameRecord

FieldMeaning
name, label"launchpad.agt", "launchpad".
tokenId, nodeDecimal token ID and its hex namehash — the same number.
registered, ownerWhether anyone holds the name, and who.
active, perpetual, expiryStatus flags; expiry is unix seconds as a string, "perpetual", or null.
resolverThe resolver contract holding this name's records.
recordsaddr, manifestUri, endpoints (protocol → URL for mcp, a2a, http, ws), wallet, texts.
source"registry-v2", or a legacy fallback when enabled.

AgentResolution

Everything in NameRecord, plus:

FieldMeaning
manifestThe parsed manifest, or null.
manifestSource"onchain" normally; "dns" / "dns-inline-v1" only with the legacy fallback.
cid"match", "mismatch", "unsupported" (not a raw sha2-256 CIDv1) or "not-ipfs".
verified, reasons, signerOutcome of the three-way check, the reasons when it fails, and the recovered signer.

Helpers

names, manifests, chainsts
import {
  namehash, tokenIdOf, normalizeName, labelOf,          // names
  canonicalize, canonicalUnsigned, signManifest,        // manifests
  verifyManifest, fetchManifest, verifyCid, CHAINS,     // verification, chains
} from "@agtnames/resolver";

normalizeName("LaunchPad")            // "launchpad.agt"
namehash("launchpad.agt")             // "0xa41373928a1fe80b04d647faf417c8851dc61f4ab52f2347cf3715195ab01d20"
tokenIdOf("launchpad.agt").toString() // "74213674913414793103689332001647165694612910724881941644579943946836492950816"
CHAINS.polygon.registry               // "0x5B9386C47395B0551c814cC03b69cbD20eb0C87A"

// Verify a manifest you already hold against the current owner
const { verified, signer, reasons } = verifyManifest(manifest, r.owner);

signManifest(unsigned, privateKeyHex) signs with a raw key (servers, CI). In a browser, sign the canonical string with the wallet's personal_sign instead — that is what /manifest does. Canonical form and verification rules: Manifest Spec.

Errors

what throws and what does notts
import { AgtResolver } from "@agtnames/resolver";

try {
  const agt = new AgtResolver({ chain: "localhost" });   // throws: registry is required
} catch (e) { /* configuration problem — fix before making calls */ }

const agt = new AgtResolver({ chain: "polygon", timeoutMs: 5_000 });
try {
  const r = await agt.resolveAgent("launchpad.agt");
  // Manifest problems never throw: they land in r.reasons with verified === false.
} catch (e) {
  // Network problems do throw: RPC unreachable, non-JSON body, AbortError on timeout.
}

CLI

agt-resolve installs with the package. Output is pretty JSON on stdout; errors go to stderr. Exit codes: 0 ok, 1 the lookup failed, 2 usage error.

terminalsh
npx agt-resolve resolve  launchpad.agt        # full AgentResolution (manifest + verification)
npx agt-resolve record   launchpad.agt        # chain state only (NameRecord)
npx agt-resolve available someone-new.agt     # { "name": "someone-new.agt", "available": true }
npx agt-resolve text     launchpad.agt description   # one text record
npx agt-resolve namehash launchpad.agt        # node + tokenId — offline
npx agt-resolve fns      launchpad.agt        # legacy registry owner (needs --legacy / a configured FNS)

# flags (or env): --chain AGT_CHAIN · --rpc AGT_RPC_URL · --registry AGT_REGISTRY
#                 --gateway AGT_IPFS_GATEWAY · --doh AGT_DOH_URL · --legacy
npx agt-resolve resolve you.agt --chain amoy | jq .verified

How resolution works

  1. Derive the token ID from the name (namehash) and read ownership, status, the resolver address and the records from the registry over JSON-RPC.
  2. Fetch the manifest from its URI (ipfs:// through the gateway, https://, or inline data:), bounded by size and time.
  3. Verify: the bytes hash to the CID when it is one; the signature recovers to manifest.owner; that address is the current on-chain owner; agt is a 3.x version. Any failure becomes a reasons entry.

Runtimes

Resolution is fetch plus pure-JS cryptography, so it runs in Node 20+, Bun, Deno and browsers. One caveat in 1.0.x: manifests published as inline base64 data: URIs are decoded with Node's Buffer, which browsers and Deno lack; ipfs:// and https:// manifests are fully portable. The CLI is Node-only.

MCP server

The same resolution is available as tools for any MCP-compatible client through @agtnames/mcp — see Use with Claude Code.

Source & license

packages/resolver on GitHub. MIT licensed.