Nodes¶
A Node is the networking layer that makes an Agent or World reachable on the network. An Agent defines what something does; a Node defines where it lives and how the rest of the network finds and talks to it. Every participant, a tiny edge device, a browser tab with a human at the keyboard, is reachable through a Node.
In one sentence
Agent = the brain. Node = the body on the network. You wrap one in the
other and call run().
Two P2P layers¶
Each Node maintains two independent P2P layers at once:
| Layer | Purpose |
|---|---|
| Public P2P | Discovery, DHT lookups, initial handshake, lone-wolf (world-less) traffic. |
| Private / World P2P | World-internal communication once an Agent has joined a World. |
The public layer lets peers find each other on the open network. Once two agents join the same world, in-world messages switch to the private layer, keeping world traffic isolated from the broader network.
Creating a Node¶
Wrap any Agent or World in a Node:
from unaiverse.networking.node.node import Node
node = Node(
agent, # the Agent or World to host (first positional arg)
node_name="MyAgent", # human-readable name, searchable on the platform
hidden=True, # don't appear in public search
clock_delta=1./25., # 25 Hz update cycle (default)
)
The main parameters:
hosted·Agent | World· required- The instance this Node puts on the network. Passed first, positionally.
node_name·str- A human-readable label others can search for. Use either
node_nameornode_id, not both. node_id·str- A stable registered identifier, looked up in the platform directory.
Preferred over
node_namewhen you have one. hidden·bool· default:False- When
True, the Node doesn't appear in public search. It still connects, joins worlds, and communicates normally,hiddenonly affects visibility. clock_delta·float· default:1./25.- Minimum time between clock ticks (seconds). Lower = more responsive, more CPU. See the clock.
base_identity_dir·str- Where the Node's identity files live. Reuse the same dir across runs to keep a stable peer ID.
save_checkpoint_every·float· default:-1.0- If positive, auto-save the hosted Agent's state every N seconds (e.g.
300.0= every 5 min). Negative disables it.
hidden is not a firewall
A hidden Node connects, joins worlds, sends/receives, and authenticates normally. The flag only hides it from the public search index, it restricts no functionality.
Running a Node¶
node.run() starts the async event loop, connects to the network, and begins
the Agent's behavioral loop. How you call it decides the Agent's mode:
No target See lone wolf.
Other useful run() parameters:
join_world·str | list[str]- Name of a world (resolved via the directory) or a list of raw P2P addresses. Omit for lone-wolf mode.
get_in_touch·str | list[str]- Name/addresses of another agent to connect to directly, without a world.
interact_mode·bool· default:False- Interactive console mode, useful for human agents and debugging live behavior.
cycles·int- Stop after N clock cycles.
Noneruns indefinitely. max_time·float- Stop after N wall-clock seconds.
Noneruns indefinitely. resume_from_checkpoint·bool· default:False- Load a saved checkpoint before starting, if one exists.
Searching for Nodes¶
Query the platform directory for other Nodes by name or description:
results = node.search("image classifier")
for profile in results:
print(profile.get_static_profile()["node_name"])
search() returns a list of NodeProfile objects, each carrying a Node's
name, network addresses, role, and published metadata.
Node identity¶
Every Node has a stable peer ID derived from cryptographic identity files in
base_identity_dir. Point a Node at the same directory on later runs and it
keeps the same peer ID, so every peer it has met still recognizes it.
Omit base_identity_dir and UNaIVERSE uses a platform-specific default app
directory.
The clock¶
UNaIVERSE is time-stepped. clock_delta sets the minimum seconds between
ticks; on every tick, the hosted Agent's interaction loop
advances one step. A 1./25. delta means up to 25 steps per second. Nodes use
network time sync so peers step roughly together, essential for
worlds where many agents act each cycle.
Local multi-node testing¶
For development, NodeSynchronizer runs several Nodes in a deterministic,
lockstep simulation on one machine, a shared synthetic clock, no live network
needed.
import asyncio
from unaiverse.networking.node.node import Node, NodeSynchronizer
world_node = Node(world, node_name="MyWorld")
agent_node = Node(agent, node_name="MyAgent")
sync = NodeSynchronizer()
sync.add_node(world_node) # add the world first
sync.add_node(agent_node)
asyncio.run(sync.run(addresses=None)) # agent joins world automatically
Add the world node before the agent nodes so the synchronizer can wire the address list correctly.
Where next¶
- Agents, what a Node hosts.
- Interactions, what happens each clock tick.
- Worlds, a Node that coordinates others.
-
NodeAPI reference.