{"ok": true, "manifest_version": "1", "protocol": "NamID", "network": "testnet11", "address_prefix": "txch", "base_url": "https://namid.net", "summary": "NamID names are singletons on the Chia blockchain. A root (river) is won at auction or held by Web3UTXO; a name (alice.river) is registered under a root. There are no accounts and no sessions: a write is authorised by a payment the wallet makes, a signature by the key that owns the thing, or both.", "conventions": {"names": "lowercase a-z and 0-9, 1-63 characters per label, no hyphens. 'river' is a root, 'alice.river' a name under it.", "amounts": "whole mojos (1 XCH = 10^12 mojos), as integers", "keys": "BLS12-381 G1 public keys, 96 hex characters, no 0x. The key a name belongs to should be one of the wallet's ADDRESS (synthetic) keys, so the wallet can later spend the name's coin.", "errors": "every refusal is JSON {ok:false, code, error}. `code` is stable; `error` is a sentence for a person. `retry:true` means the same request may succeed later; `queued:true` means the registry kept the signed request and is retrying it itself.", "idempotency": "registering or issuing the same name to the same key twice answers {ok:true, already_issued:true}; one name has one coin."}, "signing": {"challenge": {"method": "POST", "path": "/api/vault/challenge", "body": {"tld": "<asset: a root, or a full name>", "action": "<see each flow>", "payload": "<the exact payload object of the write, when the flow has one>"}, "returns": {"nonce": "single use", "message": "the text to sign"}}, "signature": "BLS AugSchemeMPL over sha256tree((\"Chia Signed Message\" . message)) \u2014 CHIP-0002 message signing, which is what a Chia wallet's 'sign message' produces.", "send_with_the_write": {"nonce": "from the challenge", "signature": "hex", "owner_pk": "the public key that signed", "index": 0}, "notes": ["A challenge is bound to its asset, action and payload: a signature for one cannot be used for another.", "A nonce is spent by the first verification, valid or not. Ask for a new challenge for every attempt."]}, "wallets": {"sage_app_bridge": {"what": "a Sage App inside Sage (the NamID app is one)", "pay": "wallet.sendXch({address, amount, fee, memos}) -> summary.inputs[].outputs[]", "sign": "wallet.signMessage({message, publicKey})", "keys": "wallet.getDerivations({hardened:false, offset, limit})", "human": "each call opens an approval screen; it shows only for the app in front and expires after 3 minutes"}, "sage_rpc": {"what": "the wallet's local RPC, for an agent on the same machine", "url": "https://127.0.0.1:9258 (mutual TLS: <data dir>/ssl/wallet.crt + wallet.key)", "pay": "POST /send_xch {address, amount, fee, memos:[], auto_submit:true}", "sign": "POST /sign_message_with_public_key {message, publicKey}", "keys": "POST /get_derivations {hardened:false, offset, limit}", "human": "no approval screen: the caller already holds the wallet's certificate"}, "chiax_walletconnect": {"what": "the Chia reference wallet, from an app inside it", "pay": "chia_sendTransaction {walletId:1, address, amount, fee} \u2014 the paying coin is the parent_coin_info of the launcher addition in the answer", "sign": "chia_signMessageByAddress {address, message}", "keys": "chia_getWalletAddresses"}}, "flows": {"request": {"what": "ask for a name (or a card on an existing name) for a person to approve \u2014 the agent spends and signs nothing", "steps": [{"method": "POST", "path": "/api/agent/requests", "body": {"name": "mybot.x", "card": "an agent card (see flow agent_card), optional", "note": "\u2264280 characters for the person approving", "from": "who is asking, \u226480"}, "returns": "id, approve_url, the price and the TLD owner's rules"}, {"what": "give approve_url to the person who will own the name; they approve in Sage"}, {"method": "GET", "path": "/api/agent/requests?id=<id>", "returns": "state: waiting | registered | done | expired"}]}, "agent_card": {"what": "publish or read an agent card: what the agent behind a name does, where to reach it and how to pay it (A2A-compatible; checkable against the name's records on chain)", "steps": [{"method": "GET", "path": "/api/names/agent?name=<name>", "returns": "card, card_hash, the A2A view, and how to verify the card"}, {"method": "GET", "path": "/api/agents?q=<words>", "returns": "every name with a card"}, {"method": "POST", "path": "/api/names/records", "body": {"name": "<name>", "patch": {"agent": "<card>"}, "nonce": "\u2026", "signature": "\u2026", "owner_pk": "\u2026"}, "signs": "the 'records' challenge from /api/vault/challenge, by the key that owns the name", "fields": {"name": "required, \u226464", "description": "\u2264500", "role": "agent | service | assistant", "url": "https endpoint of the agent", "version": "\u226432", "skills": "\u226416 of {id, name, description, tags}", "protocols": "from a2a, mcp, http, openapi, namid-pay", "mcp": "\u22648 of {name, endpoint https/wss}", "openapi": "https", "skill_md": "https", "pay_to": "\"name\" (the name's own address record) or a Chia address", "accepts": "XCH, TXCH, a ticker or CAT:<asset id>", "spending": "owner-approves | within-limit | none", "operator": "the NamID name of the person or company running the agent"}}]}, "rules": {"what": "the TLD owner's rules, which come before any registrar's", "steps": [{"method": "GET", "path": "/api/namespace/rules?root=<tld>", "returns": "the rule book, its hash, who decides (the owner, or a registrar it names)"}, {"method": "GET", "path": "/api/namespace/terms?root=<tld>", "returns": "whether names expire and can change hands, and whether the on-chain registrar carries those rules"}]}, "resolve": {"what": "look a name up, with a proof", "steps": [{"method": "GET", "path": "/api/resolve?name=<name>", "returns": "found, did, owner_pk, root (the commitment) and a membership or non-membership proof"}, {"method": "GET", "path": "/api/names/resolve?name=<name>", "returns": "records (address, profile, socials, dns), terms, and chain {launcher_id, coin_id, puzzle, owner_pk}; 404 = not registered. `authority` says whether the chain's registrar blesses those records; when `authority.serve` is false, `records` is BLANK and `records_withheld` is true"}, {"method": "GET", "path": "/api/names/authority?name=<name>", "returns": "{ok, state, why} \u2014 the handover gate on its own, without records"}], "rule": {"what": "a name's records are served only while the chain's registrar holds the label for this record's holder and its term is running", "serve": ["held", "permanent", "off-registrar", "unregistered"], "standard": ["held", "permanent"], "withhold": ["lapsed", "handed-over", "store-differs", "unchecked"], "unregistered": "served but NOT NX-standard: the root has a registrar and this label has no node in it. A node is never removed (the puzzle has no removal spend), so this can never be a handover \u2014 it is a name that was never written to the chain. Ask /api/names/standard if you require one.", "fail_closed": "a verdict that is not one of `serve` means the name resolves to NOTHING \u2014 never to the previous holder's data. `unchecked` (the registrar or the chain peak could not be read) is a refusal, not a pass.", "recompute_it_yourself": "do not trust this field: `authority.node` carries the node's OWNER, STORE and EXPIRY and `authority.peak` the height, which is everything needed to reach the same verdict from the chain (namid-protocol.md \u00a711.1)", "ttl": "cap every cached row at the name's remaining term: a TTL that outlives the label is how an expired-domain hijack keeps working", "spec": "standards/namid-protocol.md \u00a78.6, \u00a711.1, \u00a717; standards/NMPD.md \u00a710"}}, "list": {"what": "what a key holds", "steps": [{"method": "GET", "path": "/api/names/mine?owner_pk=<key>", "returns": "names under roots"}, {"method": "GET", "path": "/api/tlds/mine?owner_pk=<key>&fingerprint=<fp>", "returns": "roots"}, {"method": "GET", "path": "/api/namidbase/my-bids?bidder=<fingerprint>", "returns": "auction bids; next=='won' is a root this wallet won"}]}, "register": {"what": "register <label>.<root> to the wallet's own key", "where": "GET /api/agent/roots lists the roots that take this and their terms", "action": "register", "cost_mojos": 100000001, "steps": [{"n": 1, "method": "GET", "path": "/api/names/wallet-register/plan?root=<root>&label=<label>", "returns": "launcher_address, amount_mojos, lock_mode, renewal, valid, available \u2014 or a refusal code"}, {"n": 2, "do": "pay amount_mojos to launcher_address in ONE payment and note the id of the coin that paid it (launcher_parent_id)"}, {"n": 3, "method": "GET", "path": "/api/roots/issue/funding?launcher_parent_id=<coin id>", "until": "funded == true (the payment is confirmed)"}, {"n": 4, "do": "challenge with tld=<root>, action='register', payload = {label, holder_pk, launcher_parent_id[, recovery_keys][, address]}; sign with holder_pk's key"}, {"n": 5, "method": "POST", "path": "/api/names/wallet-register", "body": "{root, payload, nonce, signature, owner_pk, index}", "returns": "{ok, name, launcher_id, did_namid, lock_mode}"}, {"n": 6, "method": "GET", "path": "/api/roots/issue/status?root=<root>&label=<label>", "when": "the write answered queued:true", "until": "state == 'issued' (or 'failed' / 'expired')"}], "notes": ["holder_pk must be a key the signer controls: the name goes to the key that signed.", "recovery_keys is either absent or exactly two keys (authority, then watcher).", "address is optional and is written as the name's address record when the root's service has one."]}, "issue": {"what": "the owner of a root won at auction issues <label>.<root> to any key", "action": "issue", "cost_mojos": 100000001, "steps": "as register, with /api/roots/issue/plan and POST /api/roots/issue; sign with the key the root belongs to (plan.root_owner_pk); holder_pk may be anyone's key"}, "renew": {"what": "extend a root's lease", "steps": [{"n": 1, "method": "GET", "path": "/api/namidbase/renew-status?tld=<root>", "returns": "renewal_address, fee_mojos (per term), term_days, lease_expiry"}, {"n": 2, "do": "pay fee_mojos x terms to renewal_address"}, {"n": 3, "method": "GET", "path": "/api/namidbase/renew-status?tld=<root>", "until": "lease_expiry has increased"}], "notes": ["no signature: the payment is the authorisation", "names under a root are not leased here"]}, "records": {"what": "set what a name publishes (address, profile, socials, dns)", "action": "records", "steps": [{"n": 1, "do": "challenge with tld=<full name>, action='records', payload=<patch>; sign with the key the name belongs to"}, {"n": 2, "method": "POST", "path": "/api/names/records", "body": "{name, patch, nonce, signature, owner_pk, index}"}], "notes": ["which record kinds a name carries depends on its root's service"]}, "prove_records": {"what": "a name's records as a Chia DataLayer store: root, per-key proofs, and publishing the store (the owner's act)", "action": "datalayer", "read": ["/api/names/datalayer?name=<full name>", "/api/names/datalayer/proof?name=<full name>&key=<key, e.g. dns/A/0>"], "steps": [{"n": 1, "do": "GET /api/names/datalayer: the root the current records hash to, the keys, the batch_update changelist"}, {"n": 2, "do": "challenge with tld=<full name>, action='datalayer', payload={name, op:'publish'}; sign with the key the name belongs to"}, {"n": 3, "method": "POST", "path": "/api/names/datalayer/publish", "body": "{name, nonce, signature, owner_pk, index}", "answers": "ok with store_id, or code 'no-datalayer-service' WITH the changelist and the CLI to publish from your own node"}, {"n": 4, "do": "if you published yourself: challenge with payload {name, op:'bind', store_id}; POST /api/names/datalayer/bind {name, store_id, root?, nonce, signature, owner_pk, index}"}], "verify": {"method": "POST", "path": "/api/names/datalayer/verify", "body": "{root, key, value, node_hash, layers}", "pure": true}, "notes": ["hashing is Chia DataLayer's (leaf = tree hash of (key . value)); a proof verifies with sha256 alone", "the name's coin does not carry the root yet; the registry keeps the name -> store binding until the on-chain registry lands"]}, "open_a_root": {"what": "the owner of an auction root lets any wallet register under it", "action": "namespace-policy", "steps": [{"n": 1, "do": "challenge with tld=<root>, action='namespace-policy' (no payload); sign with the key the root belongs to"}, {"n": 2, "method": "POST", "path": "/api/namespace/policy", "body": "{root, registration:'open'|'owner', nonce, signature, master_pk}"}]}, "auctions": {"what": "roots are allocated by sealed-bid second-price auction", "read": ["/api/namidbase/auctions", "/api/namidbase/status?tld=<root>", "/api/namidbase/root-status?root=<root>", "/api/namidbase/reserved", "/api/namidbase/bids?tld=<root>"], "notes": ["bidding is bid-draft -> pay the escrow -> reveal -> register; the NamID wallet app is the reference client for it", "special-use labels (onion, local, test ...) are blocked for everyone"]}}, "codes": {"needs-signature": "no valid signature for this exact asset, action and payload", "no-datalayer-service": "this registry publishes no DataLayer stores; the answer carries the changelist and CLI for your own node", "no-such-key": "the name has no such record key", "root-mismatch": "the store you bound does not hold the name's current records", "not-your-key": "the name must go to a key the signing wallet controls", "not-the-owner": "the signer does not control the key the root belongs to", "owner-only": "the root was won at auction and its owner has not opened it", "reserved": "the root's owner is holding this label back", "not-open": "this registry issues no names under that root", "not-an-auction-root": "issue is for a root won at auction; use register for the others", "issuer-only": "issue does not apply to a venue root; use register", "no-owner-key": "the root's mint record carries no owner key", "owner-not-current": "a fresh read of the root singleton's current tip does not confirm its recorded owner key (rotated, recovered, or the chain could not be read) \u2014 retry later, or sign with the current owner's key", "refused": "the mint backend refused the request as written", "not-minted": "the root is not on chain yet", "taken": "the name belongs to another key", "takeover": "the name's term has run out: it is open to anyone, but it is taken over in place (POST /api/names/takeover/build, then /submit) \u2014 a registration cannot reach it, because its node is still in the root's on-chain registrar. A takeover RETIRES the lapsed owner's deed: the node's records pointer is cleared in the same spend and the name starts with no records at all", "not-handed-over": "the deed-retirement repair route was asked about a name the chain does not read as handed over: only a name whose registrar node holds the label for a different holder than its record has a deed to retire", "same-owner": "a takeover to the key that already holds the node is a renewal, not a takeover: it would buy the same term for the same price", "grammar": "the label is not a valid NamID label", "bad-request": "a field is missing or malformed", "bad_recovery_key": "recovery_keys must be absent or exactly two valid keys", "bad-address": "the address is not a Chia address this registry can store: wrong shape, a bech32m checksum that does not match, or the other network's prefix", "mint-pending": "the payment or the mint is not confirmed yet (retry:true)", "mint-failed": "the mint was refused for a reason that will not change", "ambiguous-funding": "that coin funded more than one launcher", "launcher-spent": "that payment has already been used to mint a name", "bad-funding": "the launcher holds an amount a mint does not accept; pay exactly the plan's amount", "unavailable": "the registry could not answer just now", "insufficient": "the wallet's spendable XCH is less than the price, the name's coin and the fee", "coins": "the wallet's coins could not be read from its addresses", "no_derivations": "the wallet gave no addresses with keys to look up", "build": "the one-bundle transaction could not be prepared", "push": "the network refused the bundle \u2014 nothing was spent", "expired": "the prepared bundle is older than 15 minutes; build it again", "payment": "the TLD owner's price was not paid with this name's memo", "dao-root": "the root belongs to a DAO and issues through it", "brand-root": "a brand-protected root issues no names", "icann-locked": "an ICANN string (root zone or applied for in the 2026 round) takes no new names"}, "limits": {"challenges": "about 9 challenge requests in a burst from one address, then 429", "approvals": "a wallet approval screen expires after 3 minutes", "retry_queue": "a refused mint is retried every 45 s for 2 hours"}, "safety": ["This registry is on testnet11. Check the wallet's network before paying.", "Never send a private key, seed or mnemonic to any route here: none asks for one.", "A payment to a launcher address is final; pay only the amount the plan states."]}