Skip to main content

Device Connect — edge device runtime with Zenoh/NATS messaging, D2D communication, and IoT orchestration

Project description

Device Connect

device-connect-server

Server-side runtime for the Device Connect framework. Extends device-connect-sdk with device registry, security (commissioning, ACLs), distributed state, audit logging, and CLI tools.

Contents

Where This Fits

  device-connect-sdk          device-connect-server           device-connect-agent-tools
  (Device Connect SDK)    (server runtime — this)   (agent SDK)
        │                         │                         │
        └──────────────── Mesh ─────────────────────────────┘
  • device-connect-sdk — runs on physical devices (Raspberry Pi, robots, cameras, sensors)
  • device-connect-server — runs on servers. Adds registry, security, state, and CLIs
  • device-connect-agent-tools — connects AI agents (Strands, LangChain, MCP) to the device mesh

Install

Not yet on PyPI. Install from Git:

python3 -m venv .venv
source .venv/bin/activate
pip install -e "../device-connect-sdk"
pip install -e ".[all]"

This pulls in device-connect-sdk automatically. Optional extras:

Extra Adds
[security] bcrypt, aiohttp, zeroconf, qrcode (commissioning + ACLs)
[telemetry] OpenTelemetry SDK + OTLP exporters
[state] etcd3gw (distributed state store)
[logging] pymongo (audit logging to MongoDB)
[mqtt] aiomqtt (MQTT messaging backend)
[all] All of the above + dev tools

Note: Zenoh is now a core dependency of device-connect-sdk and is included automatically.

Quick Start

Prerequisites: Complete the Install steps first (venv active, packages installed). You also need Docker. For JWT auth you additionally need nsc (brew install nsc).

1. Start infrastructure

Choose a deployment mode:

Zenoh (dev mode, no auth)
docker compose -f infra/docker-compose-dev.yml up -d
Secure — Zenoh (TLS)
# 1. Generate CA + Zenoh router + registry client certificates
./security_infra/generate_tls_certs.sh zenoh

# 2. Generate a client certificate for each device
./security_infra/generate_tls_certs.sh --client rng-001

# 3. Start infrastructure with TLS
docker compose -f infra/docker-compose.yml up -d
NATS (dev mode, no auth)
docker compose -f infra/docker-compose-nats-dev.yml up -d
Authenticated — NATS (JWT auth)
# 1. Generate NATS JWT config
./security_infra/setup_jwt_auth.sh dev

# 2. Generate credentials for built-in roles (registry, devctl)
./security_infra/gen_creds.sh --all --force

# 3. Generate credentials for each device and agent
./security_infra/gen_creds.sh --user rng-001
./security_infra/gen_creds.sh --user my-agent

# 4. Start infrastructure with JWT auth
docker compose -f infra/docker-compose-nats.yml up -d
Service Port Purpose
zenoh 7447 Zenoh messaging
nats / nats-jwt 4222 NATS messaging
etcd 2379 Distributed state store
device-registry-service 8080 Device registration and discovery

2. Connect a simulated device

The number generator simulator connects to the messaging backend, registers itself with the device registry, and emits number_generated events every 5 seconds.

cd ../device-connect-sdk  # sibling package in the monorepo

# Zenoh (dev mode, no auth)
DEVICE_CONNECT_ALLOW_INSECURE=true ZENOH_CONNECT=tcp/localhost:7447 \
  python examples/number_generator/device_simulator.py

# Secure — Zenoh (TLS)
# ZENOH_CONNECT=tls/localhost:7447 \
#   MESSAGING_TLS_CA_FILE=../device-connect-server/security_infra/ca.pem \
#   MESSAGING_TLS_CERT_FILE=../device-connect-server/security_infra/rng-001-cert.pem \
#   MESSAGING_TLS_KEY_FILE=../device-connect-server/security_infra/rng-001-key.pem \
#   python examples/number_generator/device_simulator.py

# Insecure — NATS (no auth)
# DEVICE_CONNECT_ALLOW_INSECURE=true python examples/number_generator/device_simulator.py

# Authenticated — NATS (JWT auth)
# NATS_CREDENTIALS_FILE=~/.device-connect/credentials/rng-001.creds.json \
#   NATS_URL=nats://localhost:4222 python examples/number_generator/device_simulator.py

3. Verify the device registered

devctl list queries the registry service and returns all connected devices with their capabilities, identity, and status. statectl reads the same data directly from etcd.

# Zenoh (dev mode, no auth)
ZENOH_CONNECT=tcp/localhost:7447 devctl list
statectl --raw list /device-connect/default/devices/

# Secure — Zenoh (TLS)
# ZENOH_CONNECT=tls/localhost:7447 \
#   MESSAGING_TLS_CA_FILE=security_infra/ca.pem \
#   MESSAGING_TLS_CERT_FILE=security_infra/rng-001-cert.pem \
#   MESSAGING_TLS_KEY_FILE=security_infra/rng-001-key.pem \
#   devctl list

# Insecure — NATS (no auth)
# devctl list

# Authenticated — NATS (JWT auth)
# NATS_CREDENTIALS_FILE=~/.device-connect/credentials/devctl.creds.json \
#   NATS_URL=nats://localhost:4222 devctl list

4. Connect an AI agent

The Strands Agent subscribes to all device events on the mesh (device-connect.default.*.event.>), batches them over a time window, and sends them to Claude for analysis. Claude can call back into devices using invoke_device() and get_device_status() tools.

git clone https://github.com/arm/strands-device-connect-example.git
cd strands-device-connect-example
pip install -r requirements.txt

# Insecure — Zenoh (no auth)
ZENOH_CONNECT=tcp/localhost:7447 \
  ANTHROPIC_API_KEY="sk-ant-..." python strands_agent.py

# Secure — Zenoh (TLS)
# ZENOH_CONNECT=tls/localhost:7447 \
#   MESSAGING_TLS_CA_FILE=../device-connect-server/security_infra/ca.pem \
#   MESSAGING_TLS_CERT_FILE=../device-connect-server/security_infra/my-agent-cert.pem \
#   MESSAGING_TLS_KEY_FILE=../device-connect-server/security_infra/my-agent-key.pem \
#   ANTHROPIC_API_KEY="sk-ant-..." python strands_agent.py

# Insecure — NATS (no auth)
# ANTHROPIC_API_KEY="sk-ant-..." python strands_agent.py

# Authenticated — NATS (JWT auth)
# ANTHROPIC_API_KEY="sk-ant-..." \
#   NATS_CREDENTIALS_FILE=~/.device-connect/credentials/my-agent.creds.json \
#   NATS_URL=nats://localhost:4222 python strands_agent.py

Get an API key at console.anthropic.com.

5. Tear down

# Insecure — Zenoh (no auth)
docker compose -f infra/docker-compose-dev.yml down

# Secure — Zenoh (TLS)
# docker compose -f infra/docker-compose.yml down

# Insecure — NATS (no auth)
# docker compose -f infra/docker-compose-nats-dev.yml down

# Authenticated — NATS (JWT auth)
# docker compose -f infra/docker-compose-nats.yml down

To clean up everything:

rm -rf .venv
rm -rf ~/.device-connect/credentials
rm -rf security_infra/.nsc security_infra/nats-jwt-generated.conf
rm -f security_infra/*.pem security_infra/*.srl

See device-connect-sdk — Credentials for the credentials file format.

CLI Tools

# Device control
devctl list                                # list registered devices
devctl list --compact                      # compact output
devctl register --id myDevice --keepalive  # register a test device
devctl discover --timeout 5               # find uncommissioned devices (mDNS)
devctl commission cam-001 --pin 1234-5678  # commission a device
devctl interactive                         # REPL for device operations

# State management
statectl get experiments/EXP-001           # read a key
statectl list experiments/                 # list keys under prefix
statectl set experiments/EXP-001 '{"status": "done"}' --ttl 3600
statectl delete experiments/EXP-001        # delete a key
statectl watch experiments/ --prefix       # watch for changes
statectl locks                             # list held locks
statectl stats                             # key counts by namespace

Device Commissioning Flow

New devices must be provisioned and commissioned before joining the mesh. The mechanism depends on your messaging backend:

  • Zenoh — uses mTLS (mutual TLS). Each device gets a client certificate signed by the shared CA. Generate one with ./security_infra/generate_tls_certs.sh --client <device-id>.
  • NATS — uses JWT credentials. Each device gets a JWT + NKey pair. Generate one with ./security_infra/gen_creds.sh --user <device-id>.

NATS JWT commissioning (detailed)

Using camera-001 as an example:

  1. Provision (factory): generate an identity with NKey keypair + factory PIN
  2. Generate credentials (admin): ./security_infra/gen_creds.sh --user camera-001
  3. Commission (admin): devctl commission camera-001 --pin 1234-5678
    • Delivers credentials to the device's commissioning HTTP server
    • Device saves credentials and connects to NATS
  4. Operate (every boot): device loads credentials from disk, connects, registers, starts heartbeats

Each device becomes a user under the shared DEVICE-CONNECT account — all users in the same account can communicate over device-connect.* subjects:

Operator: dc-operator              (trust root)
  └─ Account: DEVICE-CONNECT       (shared namespace)
       ├─ User: registry            (registry service)
       ├─ User: devctl              (CLI tools)
       ├─ User: orchestrator        (server coordination)
       ├─ User: camera-001          (per-device, via --user)
       └─ User: my-agent            (AI agent, via --user)

See device-connect-sdk — Credentials for how devices consume credentials at runtime.

AI agents connecting via device-connect-agent-tools also need their own credentials: ./security_infra/gen_creds.sh --user my-agent (NATS) or ./security_infra/generate_tls_certs.sh --client my-agent (Zenoh).

Testing

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[all]"
pytest tests/ -v --timeout=30

Unit tests run without external services. Integration tests are in tests/.

Contributing

We welcome contributions! Please open an issue to report bugs or suggest features, or submit a pull request directly.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

device_connect_server-0.1.1.tar.gz (51.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

device_connect_server-0.1.1-py3-none-any.whl (62.0 kB view details)

Uploaded Python 3

File details

Details for the file device_connect_server-0.1.1.tar.gz.

File metadata

  • Download URL: device_connect_server-0.1.1.tar.gz
  • Upload date:
  • Size: 51.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for device_connect_server-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f8257319e38763b3897dcb4b7d92340d26c7985810ff7f3b0310190a6f8f883d
MD5 d81dfa1da43083a68aa363e724666638
BLAKE2b-256 e893032aa69387721f69a0dc891ac256cd5fe12ab6043394cd93ad7f3ea89f95

See more details on using hashes here.

File details

Details for the file device_connect_server-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for device_connect_server-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 beb815c35b1c1b3c3f4d9912e5448a286c84b34279980d7c045ea7dd9515afb9
MD5 648798b47f9f6143ae655356cf5fdadd
BLAKE2b-256 988d759984d7632a75b8da71b84d0b8791fd4973986b37dcd29d3cde35795907

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page