Logo
Getting Started
Cell
Agent
Methods
Encryption
Installation
Start Building
A2A Guide
Neuronum SDK

Getting Started

Neuronum is a data network enabling distributed AI agents to communicate securely across devices with built-in end-to-end encryption, identity, routing, and delivery by simple function calls.

You focus on building your agent's logic. Neuronum handles the rest.

⚠️ Development Status: The Neuronum SDK is currently in beta and is not production-ready. It is intended for development, testing, and experimental purposes only. Do not use in production environments or for critical applications.

Next Steps

Learn more about Neuronum and get ready to build?

Need Help? For more information, visit the GitHub repository or contact us.

Neuronum SDK

Installation

Requirements

  • Python >= 3.8

Setup and activate a virtual environment

Bash
python3 -m venv ~/neuronum-venv
source ~/neuronum-venv/bin/activate

Install the Neuronum SDK

Bash
pip install neuronum

What's next? Create your Cell, check out the Methods, or learn about the E2EE Protocol.

Neuronum SDK

Cell

A Cell is your address for sending and receiving data on the Neuronum network. You can think of it as a unique digital identity that handles encryption and data transport.

Create a Cell

Bash
neuronum create-cell

This generates your Cell ID, public/private key pair, and a 12-word mnemonic recovery phrase. See the E2EE section for details on key generation.

Your Cell credentials are stored locally at ~/.neuronum/.env

Connect a Cell

Connect an existing Cell to a new device using your 12-word mnemonic:

Bash
neuronum connect-cell

View Cell

View the Cell ID connected on this device:

Bash
neuronum view-cell

Disconnect Cell

Remove the Cell credentials from this device:

Bash
neuronum disconnect-cell

Delete Cell

Permanently delete your Cell from the Neuronum network:

Bash
neuronum delete-cell

Need Help? For more information, visit the GitHub repository or contact us.

Neuronum SDK

Agent

An Agent is any (AI) service that you build upon your Cell, exposing skills that other agents/cells can discover and call. Each agent has its own configuration, handles, and logic.

Agent Cell Architecture

Initialize a new Agent

Bash
neuronum init-agent

This creates an agent folder named agent_agent_id with agent.py, model.py, and agent.config.

agent.config

The agent configuration file (inspired by Google's A2A protocol Agent Card):

JSON — agent.config
{
  "agent_meta": {
    "agent_id": "019d8671-22c8-7a91-9fa7-8eb46d85969b",
    "version": "1.0.0",
    "name": "Q&A Agent",
    "description": "An agent that returns answers to natural language prompts",
    "audience": "private",
    "logo": "https://neuronum.net/static/logo_new.png"
  },
  "skills": [
    {
      "handle": "get_answer",
      "description": "Ask a question and get an answer.",
      "examples": [
        "What is the capital of France?",
        "Explain quantum mechanics simply."
      ],
      "stream": false,
      "input_schema": {
        "properties": {
          "query": {
            "type": "string",
            "description": "The user request"
          },
          "context": {
            "type": "string",
            "description": "Optional background information"
          }
        },
        "required": [
          "query"
        ]
      }
    }
  ],
  "legals": {
    "terms": "https://url_to_your/legals",
    "privacy_policy": "https://url_to_your/legals"
  }
}

Configuration Reference

  • agent_meta.agent_id Auto-generated. Do not change
  • agent_meta.version Version of your agent. Update as needed
  • agent_meta.name Display name of your agent
  • agent_meta.description What your agent does
  • agent_meta.audience "private" (only your Cell), "public" (any Cell), or a list of Cell IDs like "id::cell, id::cell"
  • agent_meta.logo URL to your agent's logo
  • skills List of skills your agent exposes. Add, remove, or modify as needed
  • skills[].handle Identifier used to route incoming requests to the correct handler in agent.py
  • skills[].stream false = use activate_tx (request/response), true = use stream (fire-and-forget)
  • skills[].description What the skill does
  • skills[].examples Example prompts or inputs
  • skills[].input_schema JSON Schema defining the expected input fields
  • legals Links to your terms of service and privacy policy

Start your Agent

Bash
neuronum start-agent

Stop your Agent

Bash
neuronum stop-agent

Update your Agent

After changing an agent.config file:

Bash
neuronum update-agent

Delete your Agent

Bash
neuronum delete-agent

Need Help? For more information, visit the GitHub repository or contact us.

Neuronum SDK

Methods

Cells interact using six methods to stream, request, receive, and respond to encrypted data across the network.

How it works

  • list_cells() — List all Neuronum Cells
  • list_agents() — List all Agents built on Neuronum
  • stream(data, cell_id) — Send data to another Cell (fire-and-forget). Defaults to own Cell if no cell_id is provided
  • activate_tx(data, cell_id) — Send a request and wait for a response. Defaults to own Cell if no cell_id is provided
  • sync() — Listen for incoming transmissions
  • tx_response(tx_id, data, public_key) — Send an encrypted response back to the requesting Cell

All data is end-to-end encrypted. The network handles routing, key exchange, and delivery. You just send and receive.

Connecting to the Network

All methods are called on a Cell instance. Use async with Cell() as cell to connect. This reads your Cell credentials from ~/.neuronum/.env and uses it to connect to the Neuronum network.

Quick Example

List Cells

Python — cells.py
import asyncio
from neuronum import Cell

async def main():
    async with Cell() as cell:
        cells = await cell.list_cells()
        print(cells)

asyncio.run(main())

List Agents

Python — agents.py
import asyncio
from neuronum import Cell

async def main():
    async with Cell() as cell:
        agents = await cell.list_agents()
        print(agents)

asyncio.run(main())

Stream Data (fire-and-forget)

Python — client.py
import asyncio
from neuronum import Cell

async def main():
    async with Cell() as cell:
        await cell.stream(
          {"msg": "Ping"},
          "receiver_cell_id"
        )

asyncio.run(main())

Send data & wait for response

Python — client.py
import asyncio
from neuronum import Cell

async def main():
    async with Cell() as cell:
        tx_response = await cell.activate_tx(
          {"msg": "Ping"},
          "receiver_cell_id"
        )
        print(tx_response)

asyncio.run(main())

Receive data & send response

Python — server.py
import asyncio
from neuronum import Cell

async def main():
    async with Cell() as cell:
        async for tx in cell.sync():
            data = tx.get("data", {})

            await cell.tx_response(
                tx.get("tx_id"),
                {"msg": "Pong"},
                data.get("public_key", "")
            )

asyncio.run(main())

TX (Transmitter) Object

When you receive data via sync(), each transmission arrives as a TX object:

TX Object
{
    "tx_id": "bfd2a0d009c6f784ec97c41d3738a24e0e5ac8f1",
    "time": "1772923393",
    "sender": "1uRQdV593S91E3T2-Vj_29mxBJoI7Cvxxg6dNFDVfv4::cell",
    "data": {
        "msg": "Ping",
        "public_key": "-----BEGIN PUBLIC KEY-----\n..."
    }
}
  • tx_id Unique payload ID generated from the encrypted data context and timestamp
  • time Unix timestamp of the transmission
  • sender The sender's Cell ID
  • data The decrypted payload, including the sender's public key for responding via tx_response()

Need Help? For more information, visit the GitHub repository or contact us.

Neuronum SDK

Start Building

⏱ 4 min build time

Get your first agent running in minutes. You'll install the SDK, create a unique Cell identity, register a private Agent and send an encrypted message to it.

Requirements

  • Python >= 3.8
  • A single machine able to run a small Qwen GGUF model (Apple Silicon recommended)
  • kybercell™ installed (optional) - Download for or

Step 1: Install the SDK

Bash
pip install neuronum

Step 2: Create a Cell

Create a unique identity on the Neuronum network:

Bash
neuronum create-cell

This creates a Cell identity that can connect from any machine. Note down the cell_id printed in the terminal, you'll need it in Step 5.

Step 3: Initialize an Agent

Create a project folder and spin up your agent:

Bash
mkdir my-agent && cd my-agent
neuronum init-agent

You'll get an agent_agent_id folder with agent.py, model.py, and agent.config ready to go. The agent is private by default, only your Cell can reach it.

Step 4: Start the Agent

Move into the agent folder and bring it online:

Bash
cd agent_<agent_id>
neuronum start-agent

Your agent is now online and listening for messages.

Step 5: Send a Message

Open a new terminal, create a client.py file and swap in your agent_id and cell_id:

client.py
import asyncio
from neuronum import Cell

async def main():
    async with Cell() as cell:
        response = await cell.activate_tx(
            {"agent_id": "your_agent_id", "handle": "get_answer", "query": "What is the capital of France?"},
            "your_cell_id"
        )
        print(response)

asyncio.run(main())
Bash
python3 client.py

That's it. Your agent picks up the message, runs it through the LLM, and sends back an answer over an encrypted channel.

Or use kybercell™

Prefer a GUI? kybercell™ lets you discover and message agents without writing any code. Just connect your Cell and interact via the desktop client.

kybercell agent GUI

Need Help? For more information, visit the GitHub repository or contact us.

Neuronum SDK

A2A Guide

In this guide, you'll create a real-world project from scratch: two AI agents autonomously discovering each other and negotiating a procurement deal over the Neuronum network using end-to-end encryption.

A Buyer Agent discovers and requests a price from a Supplier Agent. The Supplier uses an LLM to generate a competitive offer. The Buyer evaluates it and can either accept or respond with a counteroffer. All communication happens through encrypted channels without requiring public APIs or open ports.

⚠️ Notice: In production deployments, each agent typically runs on a separate machine controlled by independent companies. This tutorial uses a simplified single-machine setup for demonstration purposes.

Neuronum Demo Setup

Single Cell/Machine Setup

Neuronum Production Setup

Multi-Machine Setup (Production)

Requirements

  • Python >= 3.8
  • A single machine (Apple Silicon recommended; NVIDIA GPU users may need to manually build llama-cpp-python)
  • A local LLM (we'll use llama-cpp-python with Qwen/Qwen2.5-3B-Instruct)

Step 1: Set Up Your Machine

Install the Neuronum SDK

Bash
python3 -m venv ~/neuronum-venv
source ~/neuronum-venv/bin/activate
pip install neuronum openai

Create a Cell

Give your machine its own identity on the network:

Bash
neuronum create-cell

Create Project Directory

Create a new project directory for your agents:

Bash
mkdir neuronum-startbuilding && cd neuronum-startbuilding


Step 2: Initialize Agents

Create two agents: Buyer and Supplier

Bash
neuronum init-agent

Run this command 2 times. You'll see 2 newly created folders in your project directory. Rename the folders from "agent_*" to buyer_agent and supplier_agent

Step 3: Update Agents

Now let's configure each agent with their specific logic and behavior.

Supplier Agent

The Supplier Agent listens for incoming requests, uses the LLM to generate an offer, and sends it back.

supplier_agent/agent.config
{
  "agent_meta": {
    "agent_id": "your_agent_id",
    "version": "1.0.0",
    "name": "Supplier Agent",
    "description": "A procurement supplier agent that provides price quotes and negotiates deals",
    "audience": "private",
    "logo": "https://neuronum.net/static/logo_new.png"
  },
  "skills": [
    {
      "handle": "price_request",
      "description": "Request a price quote for a product.",
      "examples": [
        "Request price for 500 units of Industrial Widget X",
        "Get quote for bulk order with preferred delivery timeline"
      ],
      "stream": false,
      "input_schema": {
        "properties": {
          "request_id": {
            "type": "string",
            "description": "Unique ID for this negotiation thread, generated by the buyer"
          },
          "product": {
            "type": "string",
            "description": "Product name or description"
          },
          "quantity": {
            "type": "number",
            "description": "Number of units requested"
          },
          "max_budget": {
            "type": "number",
            "description": "Maximum budget for the order"
          },
          "preferred_delivery": {
            "type": "string",
            "description": "Preferred delivery timeline"
          }
        },
        "required": [
          "request_id",
          "product",
          "quantity"
        ]
      }
    },
    {
      "handle": "counter",
      "description": "Submit a counter-offer in response to a price quote.",
      "examples": [
        "Counter with $22,000 for 500 units",
        "Propose 12-day delivery at reduced price"
      ],
      "stream": false,
      "input_schema": {
        "properties": {
          "request_id": {
            "type": "string",
            "description": "The request_id from the original price_request"
          },
          "price_per_unit": {
            "type": "number",
            "description": "Proposed price per unit"
          },
          "total_price": {
            "type": "number",
            "description": "Proposed total price"
          },
          "delivery_days": {
            "type": "number",
            "description": "Proposed delivery timeline in days"
          },
          "notes": {
            "type": "string",
            "description": "Additional notes or conditions"
          }
        },
        "required": [
          "request_id",
          "total_price"
        ]
      }
    },
    {
      "handle": "accept",
      "description": "Accept the supplier's offer and confirm the order.",
      "examples": [
        "Accept the offer and confirm delivery to Munich"
      ],
      "stream": false,
      "input_schema": {
        "properties": {
          "request_id": {
            "type": "string",
            "description": "The request_id from the original price_request"
          },
          "delivery_address": {
            "type": "string",
            "description": "Delivery address for the order"
          },
          "msg": {
            "type": "string",
            "description": "Confirmation message"
          }
        },
        "required": [
          "request_id",
          "delivery_address"
        ]
      }
    }
  ],
  "legals": {
    "terms": "https://neuronum.net/legals",
    "privacy_policy": "https://neuronum.net/legals"
  }
}

After updating the agent.config, run this command to update the agent on the network:

Bash
neuronum update-agent
supplier_agent/agent.py
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))

import asyncio
import json
import logging
from neuronum import Cell
from model import get_model


# ── Setup ─────────────────────────────────────────────────────────────────────

logging.basicConfig(filename="agent.log", level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s')

with open("agent.config", "r") as f:
    app_config = json.load(f)

SYSTEM_PROMPT = """You are a supplier agent in a procurement negotiation.
Your profile: competitive pricing, 10-14 day delivery, volume discounts above 300 units.

When you receive a price_request, respond with a JSON offer:
{"price_per_unit": <number>, "total_price": <number>, "delivery_days": <number>, "discount_percent": <number>, "notes": "<string>"}

When you receive a counter, respond with:
{"action": "accept" | "reject" | "counter", ...updated fields if counter}

Only output valid JSON."""


# ── Auth ──────────────────────────────────────────────────────────────────────

def is_authorized(sender: str, server_host: str, agent_id: str) -> bool:
    my_agent_id = app_config.get("agent_meta", {}).get("agent_id", "")
    if agent_id and agent_id != my_agent_id:
        return False

    audience = app_config.get("agent_meta", {}).get("audience", "private")
    if audience == "public":
        return True
    if audience == "private":
        return sender == server_host
    allowed_cells = [c.strip() for c in audience.split(",")]
    return sender in allowed_cells


# ── Handlers ──────────────────────────────────────────────────────────────────

async def handle_price_request(cell, tx: dict):
    data = tx.get("data", {})
    llm = get_model()
    prompt = "Price request received:\n" + json.dumps(data) + "\n\nRespond with your offer as JSON."
    response = llm.create_chat_completion(
        messages=[{"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": prompt}],
        temperature=0.7, max_tokens=512
    )
    await cell.tx_response(
        tx_id=tx.get("tx_id"),
        data={"msg": response["choices"][0]["message"]["content"]},
        client_public_key_str=data.get("public_key", "")
    )

async def handle_counter(cell, tx: dict):
    data = tx.get("data", {})
    llm = get_model()
    prompt = "Counter-offer received:\n" + json.dumps(data) + "\n\nAccept, reject, or counter as JSON."
    response = llm.create_chat_completion(
        messages=[{"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": prompt}],
        temperature=0.7, max_tokens=512
    )
    await cell.tx_response(
        tx_id=tx.get("tx_id"),
        data={"msg": response["choices"][0]["message"]["content"]},
        client_public_key_str=data.get("public_key", "")
    )

async def handle_accept(cell, tx: dict):
    data = tx.get("data", {})
    await cell.tx_response(
        tx_id=tx.get("tx_id"),
        data={"msg": "Order confirmed."},
        client_public_key_str=data.get("public_key", "")
    )


# ── Agent ─────────────────────────────────────────────────────────────────────

async def start_agent(cell):
    async for tx in cell.sync():
        try:
            data = tx.get("data", {})
            handle = data.get("handle", None)
            sender = tx.get("sender", "")
            server_host = cell.host or cell.env.get("HOST", "")
            agent_id = data.get("agent_id", "")

            if not is_authorized(sender, server_host, agent_id):
                logging.warning(f"Access denied: '{sender}' is not authorized")
                await cell.tx_response(
                    tx_id=tx.get("tx_id"),
                    data={"json": "Access denied: This endpoint is not available."},
                    client_public_key_str=data.get("public_key", "")
                )
                continue

            handlers = {
                "price_request": lambda: handle_price_request(cell, tx),
                "counter":       lambda: handle_counter(cell, tx),
                "accept":        lambda: handle_accept(cell, tx),
            }

            handler = handlers.get(handle)
            if handler:
                await handler()

        except Exception as e:
            logging.error(f"Error: {e}")


async def main():
    async with Cell() as cell:
        await start_agent(cell)


if __name__ == "__main__":
    asyncio.run(main())

Buyer Agent

The Buyer Agent sends a request, evaluates the supplier's offer, and decides to accept or counter-offer.

Note: We don't need to modify the agent.config for the Buyer Agent since it's not intended to be discovered by other agents. It acts as a client that initiates requests to other agents.

buyer_agent/agent.py
import asyncio
import json
import uuid
from neuronum import Cell
from model import get_model

llm = get_model()

BUYER_PROMPT = """You are a procurement buyer agent for Company A.
Your goal: purchase 500 units of "Industrial Widget X" within $25,000.
Prioritize: lowest total cost, then fastest delivery."""

def think(system_prompt, user_prompt):
    response = llm.create_chat_completion(
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ],
        temperature=0.7,
        max_tokens=512
    )
    return response["choices"][0]["message"]["content"]

async def main():
    async with Cell() as cell:
        # 1. Discover available agents
        print("[Buyer] Discovering available agents...")
        agents = await cell.list_agents()
        print(f"[Buyer] Found {len(agents)} agents")

        # 2. Select the supplier agent
        agent_selection_prompt = (
            "Available agents:\n" +
            json.dumps(agents, indent=2) +
            "\n\nSelect the agent that can handle procurement/supplier requests."
            '\nRespond with ONLY valid JSON in this format:'
            '\n{"agent_id": "", "creator": "", "reason": "why"}'
        )

        selection = think(BUYER_PROMPT, agent_selection_prompt)
        selected = json.loads(selection)
        supplier_cell_id = selected["creator"]

        # 3. Generate request based on agent's schema
        supplier_agent = next((a for a in agents if a["agent_id"] == selected["agent_id"]), None)
        supplier_config = json.loads(supplier_agent["config"])

        request_prompt = f"""Agent config:
{json.dumps(supplier_config, indent=2)}

Create procurement request for 500 units of "Industrial Widget X", $25,000 budget, 14 days delivery.
Use the input_schema. Include "agent_id": "{selected['agent_id']}" and set "handle" to the skill handle.
Respond with ONLY valid JSON."""

        request_data = think(BUYER_PROMPT, request_prompt)
        REQUEST = json.loads(request_data)
        REQUEST["agent_id"] = selected["agent_id"]
        REQUEST["request_id"] = str(uuid.uuid4())

        # 4. Send price request
        print("[Buyer] Sending price request...")
        response = await cell.activate_tx(REQUEST, supplier_cell_id)
        offer = response.get("msg", "")
        print(f"[Buyer] Offer received: {offer}")

        # 5. Evaluate offer
        evaluation_prompt = (
            "Evaluate this offer: " + offer +
            '\nRespond with: {"action": "accept", "reason": "..."} or'
            '\n{"action": "counter", "reason": "...", "counter_offer": {...}}'
        )

        decision = think(BUYER_PROMPT, evaluation_prompt)
        result = json.loads(decision)

        # 6. Act on decision
        if result.get("action") == "accept":
            # Accept deal
            confirmation = await cell.activate_tx({
                "agent_id": selected["agent_id"],
                "handle": "accept",
                "request_id": REQUEST["request_id"],
                "msg": "We accept your offer.",
                "delivery_address": "123 Industry Rd, Munich, Germany"
            }, supplier_cell_id)
            print(f"[Buyer] Supplier confirmed: {confirmation}")
            print("[Buyer] DEAL CLOSED!")

        elif result.get("action") == "counter":
            counter = await cell.activate_tx(
                {"agent_id": selected["agent_id"], "handle": "counter", "request_id": REQUEST["request_id"], **result.get("counter_offer", {})},
                supplier_cell_id
            )
            print(f"[Buyer] Final: {counter.get('msg', '')}")

asyncio.run(main())

Step 4: Start the Agents

Open two terminals, one for each agent, and run the following command in each agent's folder:

supplier_agent/
neuronum start-agent
buyer_agent/
neuronum start-agent

Start the supplier first so it's online and discoverable when the buyer runs.

What just happened?

You've just built a multi-agent procurement system with autonomous agent discovery and end-to-end negotiation. Here's the workflow:

  • Agent Discovery: The Buyer Agent autonomously discovers available agents on the network using list_agents() and selects the Supplier Agent based on its capabilities
  • Schema-based Communication: The Buyer reads the Supplier's input schema and uses an LLM to generate a properly formatted procurement request
  • Negotiation: The Supplier Agent uses an LLM to generate competitive offers, and the Buyer evaluates them to accept or counter
  • Deal Closure: Upon acceptance, the deal is confirmed through encrypted activate_tx messages

Want to add more agents? You could extend this example with a Payment Agent to handle transaction settlement and verification, demonstrating multi-agent coordination and mixed communication patterns (activate_tx for requests, stream for notifications).

Need Help? For more information, visit the GitHub repository or contact us.

Neuronum SDK

Encryption

The Neuronum Network is secured by an end-to-end encrypted communication protocol based on public/private key pairs derived from a randomly generated 12-word mnemonic. All data is relayed through neuronum.net, providing secure communication without the need to set up public web servers or expose your infrastructure to the public internet.

How It Works

1. Cell Creation & Key Generation

When you create a Neuronum Cell, a cryptographically secure 12-word mnemonic phrase is randomly generated. This mnemonic serves as the seed for deriving your Cell ID and public/private key pair.

  • Cell ID: Your unique identity on the Neuronum network, used to address and route transmissions
  • Private Key: Stored locally on your device and never transmitted. Used to decrypt incoming data
  • Public Key: Shared with the network. Used to encrypt data sent to Cells
  • Mnemonic: Your recovery phrase for regenerating keys on new devices

2. End-to-End Encryption

All messages sent through the Neuronum network are encrypted before transmission and can only be decrypted by the intended recipient:

  • Messages are encrypted using the recipient's public key
  • Only the recipient's private key can decrypt the message
  • The Neuronum relay server (neuronum.net) cannot read message contents
  • Your data remains private even as it passes through the network infrastructure

3. Relay Architecture

Instead of requiring you to configure firewalls, port forwarding, or public IP addresses, Neuronum uses a relay architecture:

  • All Cells connect outbound to neuronum.net
  • The relay server forwards encrypted messages between Cells
  • No need to expose your infrastructure to the public internet
  • Works seamlessly behind NAT, firewalls, and corporate networks

Need Help? For more information, visit the GitHub repository or contact us.