Python SDK for the Network Agent Identity Standard (NAIS). Resolve, validate, and verify the signatures of NAIS-compliant agent cards.
Project description
nais-sdk
Python SDK for the Network Agent Identity Standard (NAIS).
Resolve and validate NAIS-compliant agent domains. Requires Python 3.8+ and depends on dnspython (DNS lookups) and cryptography (Ed25519 signature verification; PyNaCl is also supported). pip install nais-sdk pulls both. The SDK resolves directly: it reads the _agent.<domain> DNS TXT record, fetches the signed card over HTTPS (HTTPS-only, no cross-host redirects, 1 MiB cap), and verifies the card's mandatory Ed25519 signature against the DNS k= key. Server-side only — browsers cannot perform DNS lookups.
Installation
pip install nais-sdk
Usage
resolve(domain)
Resolves a domain directly via DNS and HTTPS and returns a structured result dictionary:
ok— overall success flag.domain— the normalized domain.agent_host— the host serving the card.dns—{records, parsed}, whereparsedexposesv,manifest, andk.manifest_url— the URL the card was fetched from.card— the decodedagent.json.signature—{present, verified, kid, alg, reason}.validation—{valid, errors, warnings}.
from nais_sdk import resolve
r = resolve("weatheragent.nais.id")
if r["signature"]["verified"]:
print(r["card"]["mcp"])
# https://weatheragent.nais.id/mcp
print(r["dns"]["parsed"]["k"])
# ed25519:oc5a92N1h1Vg9PlnM8CrB0MAw3mMddFhZTrVuMkzceQ
resolve() raises on an invalid domain, when no NAIS TXT record is found, or when the card fetch fails. It does not raise on a bad signature — that surfaces as signature["verified"] is False and validation["valid"] is False.
validate(domain)
Returns a flattened summary dictionary. Best for quick validation checks before using an agent.
from nais_sdk import validate
summary = validate("weatheragent.nais.id")
print(summary)
Example output:
{
"valid": True,
"domain": "weatheragent.nais.id",
"version": "nais1",
"manifest_url": "https://weatheragent.nais.id/.well-known/agent.json",
"mcp_endpoint": "https://weatheragent.nais.id/mcp",
"has_mcp": True,
"has_card": True,
"signature_verified": True,
"signature_reason": None,
"key": "ed25519:oc5a92N1h1Vg9PlnM8CrB0MAw3mMddFhZTrVuMkzceQ",
"kid": "ed25519:oc5a92N1h1Vg9PlnM8CrB0MAw3mMddFhZTrVuMkzceQ",
"auth": ["wallet"],
"payments": ["x402"],
"pay_to": ["0x742d35Cc6634C0532925a3b8D4C9B7F1A2e3d4E5"],
"tags": ["forecast", "current_weather", "alerts"],
"linked_agents": [
{"domain": "alerts.weatheragent.nais.id", "relation": "partner", "verified": True, "name": "Severe Weather Alerts"}
],
"warnings": [],
"errors": []
}
valid is True only when the card resolved, the schema validates, and the signature verifies.
from nais_sdk import validate
summary = validate("weatheragent.nais.id")
if summary["signature_verified"]:
print(summary["tags"]) # ['forecast', 'current_weather', 'alerts']
print(summary["pay_to"]) # pay_to only populated for verified cards
verify_card / canonicalize
The SDK also exports verify_card(card, dns_key) and canonicalize(value) for verifying a card you already hold against a DNS k= key. It also exports parse_nais_txt and normalize_domain.
from nais_sdk import verify_card, canonicalize
result = verify_card(card, "ed25519:oc5a92N1h1Vg9PlnM8CrB0MAw3mMddFhZTrVuMkzceQ")
print(result["verified"], result["reason"])
Error handling
from nais_sdk import resolve, ResolutionError
try:
result = resolve("not-a-real-agent.example.com")
except ResolutionError as e:
print(f"Failed to resolve {e.domain}: {e}")
Timeout
Both resolve() and validate() accept an optional timeout argument (default: 10 seconds):
result = resolve("weatheragent.nais.id", timeout=5)
Testing without DNS or network
resolve() and validate() accept injected lookup_txt and fetch_card callables as keyword arguments, so resolution can be tested offline:
result = resolve(
"weatheragent.nais.id",
lookup_txt=lambda name: ["v=nais1; manifest=https://weatheragent.nais.id/.well-known/agent.json; k=ed25519:..."],
fetch_card=lambda url: {...}, # decoded agent.json
)
Notes
- Requires Python 3.8+. Depends on
dnspython(DNS) andcryptography(signatures; PyNaCl also supported);pip install nais-sdkinstalls both. - Resolution is performed locally and directly: DNS TXT lookup, HTTPS card fetch, and Ed25519 signature verification all happen in-process. There is no central resolver.
- The card fetch is HTTPS-only, refuses cross-host redirects, and caps responses at 1 MiB.
- Server-side only: browsers cannot perform DNS lookups.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nais_sdk-1.0.1.tar.gz.
File metadata
- Download URL: nais_sdk-1.0.1.tar.gz
- Upload date:
- Size: 11.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a32e6c2578ba83f1bea6169f7cf48f5527f5c88ba764d5052943950a166f1d34
|
|
| MD5 |
27ed58767f056770652002fddba2cdb2
|
|
| BLAKE2b-256 |
2f3db42ce38c176edd02ea96650f44c2c1234dd61b8f8e92c0c8dd30454f31e0
|
File details
Details for the file nais_sdk-1.0.1-py3-none-any.whl.
File metadata
- Download URL: nais_sdk-1.0.1-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d16f6414f14a68d9d906710b429720c3936b9f254ff5f071f5078d484dfbae61
|
|
| MD5 |
00756ed7f8d54db78226a0f30834ca0c
|
|
| BLAKE2b-256 |
ab78dcafded2b6fcc45ffe89693c2e62f740bd6141ba927a5726a50b33aefe5a
|