A multi-framework AI agent SDK for the Zynd AI Network. Supports LangChain, LangGraph, CrewAI, PydanticAI, and custom agents with a unified invoke() interface. Provides Ed25519 Identity, Agent Discovery via agent-dns, HTTP Webhook Communication, Agent Cards, and x402 Micropayments.
Project description
ZyndAI Agent SDK
A Python SDK for building AI agents on the ZyndAI Network. Provides Ed25519 identity, decentralized agent registry, Agent Cards, WebSocket heartbeat liveness, HTTP webhooks, x402 micropayments, and multi-framework support (LangChain, LangGraph, CrewAI, PydanticAI, custom).
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ ZyndAIAgent │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ Ed25519 │ │ Agent Card │ │ WebSocket Heartbeat │ │
│ │ Identity │ │ (.well-known│ │ (30s signed pings) │ │
│ │ │ │ /agent.json)│ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └───────────┬────────────┘ │
│ │ │ │ │
│ ┌──────┴───────┐ ┌──────┴───────┐ ┌──────────┴────────────┐ │
│ │ DNS Registry │ │ x402 │ │ Webhook Server │ │
│ │ Client │ │ Payments │ │ (Flask + ngrok) │ │
│ └──────────────┘ └──────────────┘ └───────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Framework Adapters │ │
│ │ LangChain │ LangGraph │ CrewAI │ PydanticAI │ Custom │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
agent-dns Other Agents End Users
Registry (via webhooks) (via x402)
Key flows:
- Init —
zynd agent initscaffolds a project, derives an Ed25519 keypair from your developer key - Register —
zynd agent registerregisters the agent on the network with developer proof and ZNS name binding - Run —
zynd agent runstarts the agent, writes Agent Card, begins WebSocket heartbeat - Liveness — Background thread opens WebSocket to registry, sends signed heartbeat every 30s. Server marks agent active only after first valid signature
- Discovery — Other agents find this agent via
POST /v1/searchor FQAN resolution (GET /v1/resolve/{developer}/{agent}) - Communication — Incoming requests hit Flask webhook server; outgoing requests use x402 payment middleware
Installation
pip install zyndai-agent
With optional extras:
pip install zyndai-agent[ngrok] # Ngrok tunnel support
pip install zyndai-agent[heartbeat] # WebSocket heartbeat (websockets>=14.0)
pip install zyndai-agent[mqtt] # Legacy MQTT communication
Or install from source:
git clone https://github.com/zyndai/zyndai-agent.git
cd zyndai-agent
pip install -e ".[heartbeat]"
Quick Start
1. Authenticate with a Registry
pip install zyndai-agent[heartbeat]
# Login via browser-based onboarding (creates ~/.zynd/developer.json)
zynd auth login --registry https://dns01.zynd.ai
2. Create an Agent Project
# Interactive wizard — scaffolds agent.py, agent.config.json, .env, keypair
zynd agent init
This derives an Ed25519 keypair from your developer key, creates the project files, and writes ZYND_AGENT_KEYPAIR_PATH to .env.
3. Register on the Network
# Registers agent with developer proof and ZNS name binding
zynd agent register
4. Run Your Agent
# Starts the agent, serves Agent Card, begins heartbeat
zynd agent run
On startup the SDK will:
- Load the Ed25519 keypair from
ZYND_AGENT_KEYPAIR_PATH - Write a signed
.well-known/agent.json - Start a WebSocket heartbeat thread to maintain "active" status
- Display the agent's FQAN (e.g.,
dns01.zynd.ai/your-handle/your-agent) if registered
Custom Agent Code
The agent.py generated by zynd agent init reads settings from agent.config.json:
from zyndai_agent.agent import AgentConfig, ZyndAIAgent
from zyndai_agent.message import AgentMessage
import json, os
from dotenv import load_dotenv
load_dotenv()
_config = {}
if os.path.exists("agent.config.json"):
with open("agent.config.json") as f:
_config = json.load(f)
agent_config = AgentConfig(
name=_config.get("name", "My Agent"),
description=_config.get("description", "A helpful assistant"),
category=_config.get("category", "general"),
tags=_config.get("tags", ["assistant"]),
summary=_config.get("summary", ""),
webhook_port=_config.get("webhook_port", 5001),
registry_url=os.environ.get("ZYND_REGISTRY_URL", _config.get("registry_url", "https://dns01.zynd.ai")),
keypair_path=os.environ.get("ZYND_AGENT_KEYPAIR_PATH", _config.get("keypair_path")),
)
agent = ZyndAIAgent(agent_config=agent_config)
Ed25519 Identity
Every agent has an Ed25519 keypair. The agent ID is derived from the public key:
agent_id = "agdns:" + sha256(public_key_bytes).hex()[:32]
Keypair Resolution (priority order)
ZYND_AGENT_KEYPAIR_PATHenv var — path to keypair JSONZYND_AGENT_PRIVATE_KEYenv var — base64-encoded private key seedagent_config.keypair_path— explicit path in config.agent/config.json— legacy auto-provisioned config
HD Key Derivation
Derive multiple agent keys from a single developer identity:
zynd keys derive --index 0 # First agent
zynd keys derive --index 1 # Second agent
Derivation uses SHA-512(developer_seed || "agdns:agent:" || index_bytes)[:32], producing a deterministic agent seed. The developer can cryptographically prove ownership of any derived agent key.
from zyndai_agent.ed25519_identity import derive_agent_keypair, create_derivation_proof
agent_kp = derive_agent_keypair(developer_private_key, index=0)
proof = create_derivation_proof(developer_kp, agent_kp.public_key, index=0)
# proof contains: developer_public_key (ed25519:...), agent_index, developer_signature
# The signature is over (agent_public_key_bytes || uint32_be(index)), matching the Go registry
Fully Qualified Agent Names (FQANs)
Agents with a developer handle and registered name get a human-readable FQAN:
{registry-host}/{developer-handle}/{agent-name}
For example: dns01.zynd.ai/acme-corp/doc-translator
FQANs are created automatically during zynd agent register when the developer has a claimed handle. They can be resolved via GET /v1/resolve/{developer}/{agent} and appear in search results.
Agent Cards
Agent Cards are self-describing JSON documents served at /.well-known/agent.json. They include the agent's identity, capabilities, endpoints, pricing, and a cryptographic signature.
{
"agent_id": "agdns:8e92a6ed48e821f4...",
"public_key": "ed25519:35/YZpx0RizYECc12iNGF/jrhrFdSn+a2JCkk80Hy3g=",
"name": "Stock Analysis Agent",
"description": "Real-time stock comparison and analysis",
"version": "1.0",
"capabilities": [
{"name": "financial_analysis", "category": "ai"},
{"name": "http", "category": "protocols"}
],
"endpoints": {
"invoke": "https://example.com/webhook/sync",
"invoke_async": "https://example.com/webhook",
"health": "https://example.com/health",
"agent_card": "https://example.com/.well-known/agent.json"
},
"pricing": {
"model": "per-request",
"currency": "USDC",
"rates": {"default": 0.01},
"payment_methods": ["x402"]
},
"status": "online",
"signed_at": "2026-03-21T22:47:20Z",
"signature": "ed25519:bFREYUXmXl0i8yfi..."
}
The card is regenerated and re-signed on every startup. If the card content changes (name, description, capabilities, etc.), the registry is automatically updated.
Viewing Agent Cards
# From registry
zynd card show agdns:8e92a6ed48e821f4...
# From local file
zynd card show --file .well-known/agent.json
# As raw JSON
zynd card show agdns:8e92a6ed48e821f4... --json
Heartbeat & Liveness
Agents maintain an "active" status on the registry via WebSocket heartbeat:
Agent Registry
|--- WS UPGRADE --------------->| GET /v1/agents/{agentID}/ws
|<-- 101 Switching Protocols ----|
| |
|--- signed heartbeat ---------->| First valid msg → "active" + gossip broadcast
|--- signed heartbeat ---------->| Subsequent msgs → last_heartbeat updated
| ... |
|--- (silence > 5min) --------->| Server marks agent "inactive"
Each heartbeat message contains a UTC timestamp and its Ed25519 signature. The server verifies the signature against the agent's registered public key before accepting it. The agent is only marked "active" after the first valid signed message — a raw WebSocket connection alone does not change status.
The SDK starts the heartbeat thread automatically on zynd agent run. It sends a signed message every 30 seconds and reconnects on failure. The agent must be registered via zynd agent register first.
To install heartbeat support: pip install zyndai-agent[heartbeat]
Agent Discovery
Search from Code
# Semantic keyword search
results = agent.search_agents(keyword="stock analysis", limit=5)
# Filter by category and tags
results = agent.search_agents(
keyword="data",
category="finance",
tags=["stocks", "crypto"],
federated=True, # Search across registry mesh
enrich=True, # Include full Agent Card in results
)
for r in results:
print(f"{r['name']} [{r['status']}] — {r['agent_url']}")
# Legacy convenience methods
results = agent.search_agents_by_keyword("stock comparison")
results = agent.search_agents_by_capabilities(["financial_analysis"], top_k=5)
Search from CLI
# Keyword search
zynd search "stock analysis"
# Filter by category and tags
zynd search --category finance --tags stocks crypto
# Federated search (across registry mesh)
zynd search "data pipeline" --federated
# Resolve a specific agent
zynd resolve agdns:8e92a6ed48e821f4...
Agent-to-Agent Communication
Webhook Endpoints
When your agent starts, these HTTP endpoints are available:
| Endpoint | Method | Description |
|---|---|---|
/webhook |
POST | Async message handler (fire-and-forget) |
/webhook/sync |
POST | Sync request/response (30s timeout) |
/health |
GET | Health check |
/.well-known/agent.json |
GET | Signed Agent Card |
Sending Messages
from zyndai_agent.message import AgentMessage
# Find an agent
agents = agent.search_agents_by_keyword("stock comparison")
target = agents[0]
# Send sync request (with automatic x402 payment if required)
msg = AgentMessage(
content="Compare AAPL and GOOGL",
sender_id=agent.agent_id,
message_type="query",
)
sync_url = target['agent_url'] + "/webhook/sync"
response = agent.x402_processor.post(sync_url, json=msg.to_dict(), timeout=60)
print(response.json()["response"])
x402 Micropayments
Enable Payments on Your Agent
agent_config = AgentConfig(
name="Premium Agent",
webhook_port=5001,
price="$0.01", # Charge $0.01 per request
registry_url="https://dns01.zynd.ai",
)
agent = ZyndAIAgent(agent_config=agent_config)
# x402 payment middleware is automatically enabled on /webhook/sync
Pay for Other Agent Services
# The x402 processor handles payment negotiation automatically
response = agent.x402_processor.post(
"https://paid-agent.example.com/webhook/sync",
json=msg.to_dict()
)
# Or access any x402-protected API
response = agent.x402_processor.get(
"https://api.premium-data.com/stock",
params={"symbol": "AAPL"}
)
Multi-Framework Support
The SDK wraps any AI framework behind a unified invoke() method:
# LangChain
from langchain_classic.agents import AgentExecutor
agent.set_langchain_agent(executor)
# LangGraph
agent.set_langgraph_agent(compiled_graph)
# CrewAI
agent.set_crewai_agent(crew)
# PydanticAI
agent.set_pydantic_ai_agent(pydantic_agent)
# Custom function
agent.set_custom_agent(lambda input_text: f"Response: {input_text}")
# All use the same interface
response = agent.invoke("What is the price of AAPL?")
See examples/http/ for complete working examples of each framework.
Ngrok Tunnel Support
Expose local agents to the internet:
agent_config = AgentConfig(
name="My Public Agent",
webhook_port=5003,
use_ngrok=True,
ngrok_auth_token="your-ngrok-auth-token", # Or set NGROK_AUTH_TOKEN env var
registry_url="https://dns01.zynd.ai",
)
The public ngrok URL is automatically registered with the registry. Other agents can discover and reach your agent from anywhere.
CLI Reference
The zynd CLI manages agent lifecycle, keypairs, registration, and discovery.
Agent Workflow (primary commands)
zynd auth login --registry URL Authenticate with a registry (browser-based)
zynd agent init Interactive wizard — scaffolds project + keypair
zynd agent register Register agent on the network (or update if exists)
zynd agent update Push config changes to registry
zynd agent run Run the agent from current directory
Identity & Keys
zynd init Create developer keypair (~/.zynd/developer.json)
zynd info Show developer and agent identity details
zynd keys list List all keypairs
zynd keys create [--name NAME] Create standalone agent keypair
zynd keys derive --index N HD-derive agent key from developer key
zynd keys show NAME Show keypair details
Registration & Discovery
zynd register [--name N] [--index N] Register agent on registry (legacy)
zynd deregister AGENT_ID Remove agent from registry
zynd search [QUERY] [--category C] Search agents
[--tags T1 T2] [--federated]
zynd resolve AGENT_ID [--json] Look up agent by ID
zynd card init [--index N] Set up keypair + .env for a project
zynd card show [AGENT_ID|--file PATH] Display Agent Card
Configuration Reference
AgentConfig Fields
| Field | Type | Default | Description |
|---|---|---|---|
name |
str |
"" |
Agent display name |
description |
str |
"" |
Agent description |
category |
str |
"general" |
Registry category |
tags |
list[str] |
None |
Searchable tags |
summary |
str |
None |
Short description (max 200 chars) |
capabilities |
dict |
None |
Structured capabilities |
webhook_host |
str |
"0.0.0.0" |
Bind address |
webhook_port |
int |
5000 |
Webhook server port |
webhook_url |
str |
None |
Public URL (if behind NAT) |
registry_url |
str |
"http://localhost:8080" |
Registry endpoint |
auto_reconnect |
bool |
True |
Reconnect on disconnect |
keypair_path |
str |
None |
Path to Ed25519 keypair JSON |
config_dir |
str |
None |
Config directory (default: .agent) |
price |
str |
None |
x402 price per request (e.g. "$0.01") |
use_ngrok |
bool |
False |
Enable ngrok tunnel |
ngrok_auth_token |
str |
None |
Ngrok auth token |
developer_keypair_path |
str |
None |
Developer key for HD derivation |
agent_index |
int |
None |
HD derivation index |
card_output |
str |
None |
Output path for Agent Card |
Environment Variables
| Variable | Description |
|---|---|
ZYND_AGENT_KEYPAIR_PATH |
Path to agent keypair JSON |
ZYND_AGENT_PRIVATE_KEY |
Base64-encoded Ed25519 private key |
ZYND_AGENT_PUBLIC_KEY |
Base64-encoded Ed25519 public key |
ZYND_REGISTRY_URL |
Default registry endpoint |
ZYND_HOME |
Override ~/.zynd/ directory |
NGROK_AUTH_TOKEN |
Ngrok authentication token |
Running Multiple Agents
Use different config_dir values and ports:
agent1 = ZyndAIAgent(AgentConfig(
name="Agent 1", webhook_port=5001, config_dir=".agent-1", ...
))
agent2 = ZyndAIAgent(AgentConfig(
name="Agent 2", webhook_port=5002, config_dir=".agent-2", ...
))
With HD derivation, derive separate keypairs for each:
zynd keys derive --index 0 # For agent 1
zynd keys derive --index 1 # For agent 2
Examples
See examples/http/ for complete working agents:
stock_langchain.py— LangChain agent with search toolsstock_langgraph.py— LangGraph compiled graph agentstock_crewai.py— CrewAI multi-agent crewstock_pydantic_ai.py— PydanticAI typed agentuser_agent.py— Orchestrator that discovers and delegates to specialist agents
Support
- GitHub Issues: Report bugs
- Documentation: docs.zynd.ai
- Email: zyndainetwork@gmail.com
- Twitter: @ZyndAI
License
MIT License - see LICENSE for details.
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 zyndai_agent-0.3.2.tar.gz.
File metadata
- Download URL: zyndai_agent-0.3.2.tar.gz
- Upload date:
- Size: 226.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff3b98a87ed189573d1d5187dd2f6c63a4220aa239b7e8009b9645081dc9ade8
|
|
| MD5 |
bbdfa66b6987597da44908c4e5aeb902
|
|
| BLAKE2b-256 |
7598a17ecd6119dd963597291b9672e3d19b570f566169e3029f0b85fa65174e
|
File details
Details for the file zyndai_agent-0.3.2-py3-none-any.whl.
File metadata
- Download URL: zyndai_agent-0.3.2-py3-none-any.whl
- Upload date:
- Size: 79.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cff6dd8b638ff7bd841ff53208f99f5a725f5c50c105c270f49d4e8966708f71
|
|
| MD5 |
474425cbaff1da366aae424afe3f3979
|
|
| BLAKE2b-256 |
f2fc92ab7204614639086967694bcdcb681cca4a21a378ae01b915711c294630
|