Docs / Contact / protocols/nano-agent

Nano Agent

The Tendrl Nano Agent is a lightweight, language-agnostic messaging gateway that runs locally on your device or server. It exposes a Unix socket that any application can write JSON to, no SDK required.

Why use the Nano Agent?

AF_UNIX Socket Requirements

Windows

Unix/Linux/macOS

The tendrl Group and Socket Access

The agent runs on Windows, Linux, and macOS. Access to the socket is restricted by group membership on all platforms.

Unix/Linux (and macOS): The tendrl group is required for socket security. The agent runs as user/group tendrl, and the socket directory (/var/lib/tendrl) and socket file are owned by that group with restricted permissions (e.g. 770 for the directory, 660 for the socket). Only root or users in the tendrl group can connect. Add application users with: sudo usermod -aG tendrl your-app-user. Creating the group and setting these permissions during installation is mandatory for correct and secure operation.

Windows: The same idea is enforced using a local group named tendrl and a directory ACL. On first run, the agent creates the group (if missing) and sets the ACL on C:\ProgramData\tendrl so that only the tendrl group has access. Only members of that group (and administrators) can use the socket. Add application or service accounts to the group so they can connect: net localgroup tendrl YourUser /add (run in an elevated prompt). The service account running the agent should also be in tendrl so it can create and use the socket.

Installation

One command on any platform — the installer detects your OS and architecture, verifies checksums against the release manifest, and puts tendrl-agent on your PATH:

macOS / Linux

bash

curl -fsSL https://app.tendrl.com/api/public/tools/nano-agent/v1/latest/install.sh | sh

On Linux, also create the socket directory and access group once:

bash

sudo mkdir -p /var/lib/tendrl
sudo groupadd tendrl
sudo chown :tendrl /var/lib/tendrl
sudo chmod 770 /var/lib/tendrl

(On macOS, or to run without root, set TENDRL_SOCKET to a user-writable path instead.)

Windows (x86_64)

powershell

powershell -c "irm https://app.tendrl.com/api/public/tools/nano-agent/v1/latest/install.ps1 | iex"

# Create the socket directory (or let the agent create it on first run; it will also create the "tendrl" local group and set ACL so only that group can access the socket)
mkdir "C:\ProgramData\tendrl"

Starting the Agent

bash

# Using a flag
./tendrl-agent -apiKey=YOUR_API_KEY

# Using an environment variable
export TENDRL_KEY=YOUR_API_KEY
./tendrl-agent

The agent creates a Unix socket and begins listening for messages:

Platform Socket Path
Linux/macOS /var/lib/tendrl/tendrl_agent.sock
Windows C:\ProgramData\tendrl\tendrl_agent.sock

CLI Client

The tendrl binary is a command-line wrapper around the socket protocol. It is useful for debugging, install verification, and shell scripts, especially on Windows where nc -U may not be available.

Download tendrl-* from the same tool downloads listing alongside tendrl-agent. No API key is required on the client.

bash

tendrl ping
tendrl publish -data '{"temperature": 22.5}' -tags sensor
tendrl check -limit 5
tendrl state read

See CLI Client for the full command reference.

Configuration

Flag Env Default Description
-apiKey TENDRL_KEY Entity API key (required)
-appURL TENDRL_APP_URL https://app.tendrl.com/api API base URL
-minBatchSize 10 Minimum messages per batch
-maxBatchSize 200 Maximum messages per batch
-maxQueue 1000 Maximum queued messages before backpressure
-targetCPU 70.0 Target CPU % for dynamic batch sizing
-targetMem 80.0 Target memory % for dynamic batch sizing
-flushInterval 250ms Maximum time between flushes

Sending Messages

Connect to the socket and write JSON. Every message needs a msg_type. The data field accepts a JSON object or a string.

Publish a Message

json

{
  "msg_type": "publish",
  "data": {
    "temperature": 22.5,
    "unit": "celsius"
  },
  "context": {
    "tags": ["sensor", "building-a"]
  }
}

Messages are queued and batched automatically. To wait for the server response instead, set "wait": true in the context:

json

{
  "msg_type": "publish",
  "data": { "temperature": 22.5 },
  "context": { "wait": true }
}

Send to a Specific Entity

json

{
  "msg_type": "publish",
  "data": { "command": "reboot" },
  "dest": "control-panel-01"
}

Send a Heartbeat

Heartbeats are sent directly (not batched) and require system metrics:

json

{
  "msg_type": "heartbeat",
  "data": {
    "mem_free": 1024.0,
    "mem_total": 4096.0,
    "disk_free": 50000.0,
    "disk_size": 100000.0
  }
}

State Messages

Create or update your entity's state table:

json

{
  "msg_type": "state_new",
  "data": { "firmware": "1.2.0", "mode": "active" }
}
json

{
  "msg_type": "state_update",
  "data": { "mode": "standby" }
}

Read State Table

Retrieve your entity's current state table:

json

{
  "msg_type": "state_read"
}

Response:

json

{
  "statusTable": {
    "firmware": "1.2.0",
    "mode": "standby"
  }
}

Checking for Messages

Poll for messages sent to your entity:

json

{
  "msg_type": "msg_check",
  "context": { "limit": 5 }
}

Response is an array of messages, or 204 if none are pending:

json

[
  {
    "msg_type": "publish",
    "data": { "command": "reboot" },
    "tags": ["maintenance"],
    "source": "123456:us-1:entity:control-panel",
    "timestamp": "2024-01-15T10:30:45Z"
  }
]

Quick Start Examples

Python

python


sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("/var/lib/tendrl/tendrl_agent.sock")

message = {
    "msg_type": "publish",
    "data": {"temperature": 22.5, "humidity": 65},
    "context": {"tags": ["sensor"]}
}
sock.sendall(json.dumps(message).encode())
sock.close()

C

c

#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <unistd.h>

int main() {
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr = { .sun_family = AF_UNIX };
    strncpy(addr.sun_path, "/var/lib/tendrl/tendrl_agent.sock", sizeof(addr.sun_path) - 1);
    connect(fd, (struct sockaddr *)&addr, sizeof(addr));

    const char *msg = "{\"msg_type\":\"publish\",\"data\":{\"temp\":22.5}}";
    write(fd, msg, strlen(msg));
    close(fd);
    return 0;
}

Bash

bash

echo '{"msg_type":"publish","data":{"temperature":22.5}}' | \
  nc -U /var/lib/tendrl/tendrl_agent.sock

Running as a Service

systemd (Linux)

ini

[Unit]
Description=Tendrl Nano Agent
After=network.target

[Service]
Type=simple
Environment=TENDRL_KEY=your_api_key
ExecStart=/usr/local/bin/tendrl-agent
Restart=always
User=tendrl
Group=tendrl

[Install]
WantedBy=multi-user.target
bash

sudo cp tendrl-agent.service /etc/systemd/system/
sudo systemctl enable --now tendrl-agent

Windows

powershell

nssm install TendrlAgent "C:\Program Files\Tendrl\tendrl-agent.exe"
nssm set TendrlAgent Environment "TENDRL_KEY=your_api_key"
nssm start TendrlAgent

Context Fields

Field Type Description
tags string[] Tags for routing and fanout fan-out (max 10)
wait boolean Wait for server response instead of batching
limit integer Number of messages to retrieve (msg_check only)
entity string Entity identifier

Error Responses

json

{
  "status": "error",
  "message": "Queue full, try again later"
}
Error Cause
Queue full, try again later Message queue at capacity. Reduce send rate or increase -maxQueue.
Too many tags provided; maximum is 10 Context has more than 10 tags.
Unknown message type msg_type is not one of: publish, heartbeat, state_new, state_update, state_read, msg_check.

Troubleshooting

Windows AF_UNIX Issues

  1. AF_UNIX Not Supported
powershell

# Check if AF_UNIX driver is available
sc query afunix

# If not available, ensure Windows 10 1803+ or Windows Server 2019+
winver
  1. Socket Permission Denied

Only the tendrl local group (and administrators) have access to C:\ProgramData\tendrl. Add the user or service account that needs to connect:

powershell

# Run in an elevated (Administrator) prompt
net localgroup tendrl YourUser /add

Then check that the directory ACL includes the tendrl group:

powershell

icacls "C:\ProgramData\tendrl"

Unix/Linux Issues

  1. Permission Denied

The socket is only accessible to root and members of the tendrl group (see The tendrl group and socket access). Ensure the socket and directory are owned by tendrl and add your application user to the group if it needs to connect:

bash

# Fix socket ownership
sudo chown :tendrl /var/lib/tendrl/tendrl_agent.sock
sudo chmod 660 /var/lib/tendrl/tendrl_agent.sock

# Allow your app user to use the socket
sudo usermod -aG tendrl your-app-user
  1. Connection Refused
bash

# Check if agent is running
systemctl status tendrl-agent

# Check socket exists
ls -l /var/lib/tendrl/tendrl_agent.sock

Common Issues

  1. Message Queue Full
bash

# Check agent logs
# Unix: journalctl -u tendrl-agent -f
# Windows: Check Event Viewer or agent console

# Increase queue size
./tendrl-agent -maxQueue=20000

Logging

Unix/Linux: The agent logs to systemd journal by default:

bash

# View all logs
journalctl -u tendrl-agent

# Follow new logs
journalctl -u tendrl-agent -f

# View errors only
journalctl -u tendrl-agent -p err

Windows: Check Windows Event Viewer under Applications or run agent in console mode for direct output.

Security Considerations

Socket Security

API Key Protection

Network Security