Skip to content

merklebot/robot-agent

Repository files navigation

Merklebot's Robot-Agent

Roadmap

  • auto restart and system service launch
  • config on the platform
  • tui for agent state monitoring
  • key based auth
  • autonomous config update

Installation and run

You can run agent directly from terminal

  1. Manually download latest release of agent for your system
  2. Make agent executable
chmod +x robot-agent-...
  1. Run agent with api-key (run only with -k parameter if do not need platform)
./robot-agent-... -a <API_KEY> -r http://robots.merklebot.com:8888 -k <BASE_64_OF_ED25519>

Run agent as systemd service

Use easy to run script:

curl -s https://app.merklebot.com/install.sh | bash -s <API_KEY>

The copy of script could be found at this repository

To stop agent

systemctl disable merklebot 

Modules

The agent consists of 4 main modules:

  • Platform module
  • Docker module
  • Libp2p interaction module
  • Unix Socket Interface

Agent Scheme

Main (Platform)

First of all, robot-agent can be connected to merklebot's platform (app.merklebot.com) to get jobs from the cloud. For authentication, agent needs api-key which is available on a platform after your robot instance is created.

Note In next version of agent api-key will be changed for key (check libp2p section)

Docker

All the jobs sent from platform are splitted to 2 categories:

  1. Execution of container wrapper code
  2. Direct access to the terminal (of containerized environment)

Via port forwarding and docker volumes system it could be extended to manipulation of the whole host system.

It is also possible to store some data after job execution in bucket on merklebot's platform.

Libp2p

Libp2p protocol is used for advanced discovery of other agents (via mDNS) and messaging between agents.

For usage of this module, you'll need a key - base64 encoded key.

Unix Socket Interface

Interface for applications and containers on devices. It creates a merklebot.socket file which provides JSON API for interactions with an agent.

Feautures

Docker Job running via Platform

With platform or via API some code wrapped in docker container could be executed as job.

Docker Job Screen

  1. Docker Image

Insert link for a container wrapped code. It should be stored in some publicly accessible container register (in other words docker pull LINK should work)

  1. Docker Container Name

Name container to your liking, but with standard docker naming limitations

  1. Environmental variables

Add all the variables you think you'll need (like API keys, super secrets and other cool things)

  1. Network mode: Switch between closed default network or allow host one

  2. Privileged mode

Run container with admin privileges. It could be used, if devices or specific host system volumes are needed in use.

  1. Port binding

Share ports between container and host system

  1. Volumes

Share folders or devices between container and host system

Store data on the platform

To store data after job is executed:

  1. Create bucket at app.merklebot.com
  2. Assign the bucket to the robot
  3. Turn on Store data in job configuration
  4. Place required files in /merklebot/job_data/ folder

After the code execution, files would be loaded to your bucket and accessible in job result screen

Access container's terminal

To access terminal of container via web-interface, add sh to your custom tty filed of job config.

The terminal could be reached in job status screen (OPEN TERMINAL).

Note To end job, don't forget to send exit.

Docker Job running via CLI

You can use CLI tool to interact with your agents. Currently, it is in development. Two functions are supported:

  1. Get robot states
  2. Send docker jobs

Detailed information could be obtained here.

Devices discovery

To connect different to devices (with different agents on them), you should create a device group config and spread it around devices. With the usage of this config, agent can match PeerId's and return IP addresses of devices in local network.

It could be used to avoid static IP for your group of devices.

To get devices, send JSON {"action": "/local_robots"} to merklebot.socket

Example of config.json:

{
  "robots": [
    {
      "robot_id": "device-0",
      "robot_peer_id": "12D3KooWKnY2J5CFny3Ef8abndmg9U4gAkndUDPM4oMP7yVbftBK",
      "name": "spot",
      "tags": [],
      "interfaces": []
    },
    {
      "robot_id": "device-1",
      "robot_peer_id": "12D3KooWK8ogtRq7DXD21ji9nwkTgCfuz6AYU9dys8GrH7weC2AC",
      "name": "jetson",
      "tags": [],
      "interfaces": []
    }
  ]
}

Example of python code:

import socket
import os
import json
import pprint

socket_path = "FOLDER_WITH_AGENT/merklebot.socket"

def local_robots():
    client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    client.connect(os.path.realpath(socket_path))
    message = {"action": "/local_robots"}
    client.sendall(json.dumps(message).encode())
    response = client.recv(1024).decode()
    client.close()
    return json.loads(response)

pprint.pprint(local_robots()) # prints json with found devices

Libp2p messaging

Agents can exchange data using libp2p messaging protocol. Therefore, you won't need to worry about addressing between your robots. To use it, you'll need a robot group config like in previous section.

To send message, send JSON {"action": "/send_message", "content": "TEXT_OF_MESSAGE"} to merklebot.socket

To receive one, subscribe with JSON {"action": "/subscribe_messages"}

Python code example:

import socket
import os
import json
import pprint
import time

socket_path = "FOLDER_WITH_AGENT/merklebot.socket"

def send_message(message_content):
    client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    client.connect(os.path.realpath(socket_path))
    message = {"action": "/send_message"}
    message["content"] = message_content
    client.sendall(json.dumps(message).encode())
    response = client.recv(1024).decode()
    client.close()
    return json.loads(response)

def subscribe_messages():
    with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
        client.connect(os.path.realpath(socket_path))
        client.sendall(json.dumps({"action": "/subscribe_messages"}).encode())
        while True:
            time.sleep(0.1)
            response = client.recv(1024).decode()
            if response:
                print(response)