Skip to Content
Naylence Docs are in active development. Share feedback in Discord.
ConceptsClients

Clients

In Naylence, clients are not second-class citizens.

A client is simply code running on a node that initiates interactions with other agents. That same client node can also:

  • listen for incoming envelopes,
  • handle streaming (sending/receiving incremental responses),
  • and host local agents or handlers when that makes sense.

This is why a browser client still “counts” as part of the fabric: it runs on a node, participates in routing, and can both send and receive messages.

The difference between “client” and “agent” is usually role, not capability:

  • client role: start a request, drive a workflow, present results.
  • agent role: provide an addressable capability (tool/service/worker) that others call.

In many systems the same process can do both.

Ways to Connect

There are two ways to interact with agents in the Naylence fabric:

ApproachBest For
SDK ClientsNative apps, microservices, browser SPAs — full fabric features
HTTP GatewayExternal integrations, scripts, webhooks — simple HTTP calls

SDK Clients

SDK clients use the Naylence SDK (@naylence/agent-sdk or naylence-agent-sdk) to connect to the fabric. They run on a node and participate fully in the fabric protocol.

How SDK Clients Work

  1. Create a client node with CLIENT_CONFIG or custom configuration
  2. Connect to a sentinel (or run standalone)
  3. Use proxy objects to invoke remote agents
  4. Receive responses, including streaming data
import { Client, Agent, CLIENT_CONFIG } from '@naylence/agent-sdk'; // Define the remote agent interface class GreeterAgent extends Agent { async greet(name: string): Promise<string> { return ''; // Implemented remotely } } // Create and start a client const client = new Client(CLIENT_CONFIG); await client.start(); // Get a proxy to the remote agent const greeter = Agent.remoteByAddress(GreeterAgent, 'greeter@fame.fabric', client); // Invoke the remote method const result = await greeter.greet('World'); console.log(result); // "Hello, World!" await client.stop();
from naylence.agent import Client, Agent, CLIENT_CONFIG # Define the remote agent interface class GreeterAgent(Agent): async def greet(self, name: str) -> str: return "" # Implemented remotely # Create and start a client client = Client(CLIENT_CONFIG) await client.start() # Get a proxy to the remote agent greeter = Agent.remote_by_address(GreeterAgent, "greeter@fame.fabric", client) # Invoke the remote method result = await greeter.greet("World") print(result) # "Hello, World!" await client.stop()

SDK Client Capabilities

  • RPC (request/response) — Call agent methods and get results
  • Streaming — Receive incremental responses as they’re generated
  • Bidirectional messaging — Send and receive messages asynchronously
  • Routing participation — Your client can be addressed by other nodes
  • Event subscriptions — React to fabric events
  • Local agents — Host your own agents alongside client code

HTTP Gateway Clients

The Agent HTTP Gateway provides a simple REST API for invoking agents. It requires no SDK—any HTTP client works (curl, fetch, requests, etc.).

The HTTP Gateway is provided by the AgentHttpGatewayListener. See Listeners for configuration details.

Enabling the Gateway

The gateway is disabled by default. Enable it via environment variable:

export FAME_LISTENER_AGENT_HTTP_GATEWAY_ENABLED=true

Gateway Endpoints

EndpointMethodPurpose
/fame/v1/gateway/rpcPOSTSynchronous RPC invocations
/fame/v1/gateway/messagesPOSTFire-and-forget message delivery
/fame/v1/gateway/healthGETHealth check

RPC Requests

Send a POST request with JSON body:

curl -X POST http://localhost:8000/fame/v1/gateway/rpc \ -H "Content-Type: application/json" \ -d '{ "targetAddr": "greeter@fame.fabric", "method": "greet", "params": {"name": "World"} }'

Response:

{"ok": true, "result": "Hello, World!"}

RPC Request Format

FieldTypeRequiredDescription
methodstringYesThe agent method to invoke
targetAddrstringOne of theseDirect address of the agent (e.g., greeter@fame.fabric)
capabilitiesstring[]One of theseDiscover agent by capabilities
paramsobjectNoMethod parameters as key-value pairs
timeoutMsnumberNoRequest timeout in milliseconds (default: 30000, max: 120000)

By address:

{ "targetAddr": "math@fame.fabric", "method": "add", "params": {"x": 5, "y": 3} }

By capabilities:

{ "capabilities": ["math", "calculator"], "method": "add", "params": {"x": 5, "y": 3} }

Message Requests

Send fire-and-forget messages:

curl -X POST http://localhost:8000/fame/v1/gateway/messages \ -H "Content-Type: application/json" \ -d '{ "targetAddr": "logger@fame.fabric", "type": "log.info", "payload": {"message": "User logged in", "userId": "123"} }'

Response:

{"status": "message_accepted"}

Message Request Format

FieldTypeRequiredDescription
targetAddrstringOne of theseDirect address of the agent
capabilitiesstring[]One of theseDiscover agent by capabilities
typestringNoMessage type identifier
payloadanyNoMessage payload (any JSON value)

Authentication

If the sentinel uses security profiles, include the Authorization header:

curl -X POST http://localhost:8000/fame/v1/gateway/rpc \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <your-token>" \ -d '{"targetAddr": "agent@fame.fabric", "method": "doSomething"}'

Error Responses

The gateway returns structured error responses:

{"ok": false, "error": "No route to agent@fame.fabric", "code": "not_found"}
HTTP StatusCodeMeaning
400invalid_requestMalformed request body
401unauthorizedAuthentication failed
403forbiddenNot authorized for this operation
404not_foundNo route to target agent
429queue_fullAgent is overloaded (back pressure)
504timeoutRequest timed out
503—Node not initialized

JavaScript/TypeScript Example

async function invokeAgent(method: string, params: Record<string, any>) { const response = await fetch('http://localhost:8000/fame/v1/gateway/rpc', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ targetAddr: 'math@fame.fabric', method, params, }), }); const data = await response.json(); if (!data.ok) { throw new Error(data.error); } return data.result; } // Usage const sum = await invokeAgent('add', { x: 5, y: 3 }); console.log(sum); // 8

Python Example

import requests def invoke_agent(method: str, params: dict): response = requests.post( "http://localhost:8000/fame/v1/gateway/rpc", json={ "targetAddr": "math@fame.fabric", "method": method, "params": params, }, ) data = response.json() if not data.get("ok"): raise Exception(data.get("error")) return data["result"] # Usage result = invoke_agent("add", {"x": 5, "y": 3}) print(result) # 8

Feature Comparison

FeatureSDK ClientHTTP Gateway
RPC (request/response)âś…âś…
Streaming responses✅❌
Bidirectional messaging✅❌
Fire-and-forget messagesâś…âś…
Routing participation✅❌
Event subscriptions✅❌
Host local agents✅❌
Connection persistence✅❌
No SDK required❌✅
Works with any HTTP client❌✅

Choosing the Right Approach

Use SDK Clients when:

  • You need streaming responses (e.g., LLM token-by-token output)
  • You need bidirectional real-time communication
  • Your app will host agents alongside client code
  • You want full routing capabilities (your client can be addressed)
  • You’re building a native application or microservice
  • You’re using React or Vue with the Naylence integrations

Use HTTP Gateway when:

  • Integrating from systems that only support HTTP (webhooks, CI/CD pipelines, cron jobs)
  • Writing quick scripts or debugging with curl
  • Building serverless functions that invoke agents
  • You don’t need streaming or real-time features
  • You want the simplest possible integration
  • The calling system is outside your control (third-party integrations)

The HTTP Gateway is stateless—each request is independent. For long-running conversations or stateful interactions, consider using the SDK client or managing state within your agents.

See Also

Last updated on