Skip to main content

device-connect-server

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

Contents

Where This Fits

  device-connect-edge          device-connect-server           device-connect-agent-tools
  (edge runtime)          (server runtime — this)   (agent SDK)
        │                         │                         │
        └──────────────── Mesh ─────────────────────────────┘
  • device-connect-edge — 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

python3 -m venv .venv
source .venv/bin/activate
pip install device-connect-server

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-edge 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-edge  # 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)
# MESSAGING_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)
# MESSAGING_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.

pip install "device-connect-agent-tools[strands]"
from device_connect_agent_tools.adapters.strands_agent import StrandsDeviceConnectAgent

agent = StrandsDeviceConnectAgent(
    goal="Monitor devices and react to events",
    model_id="claude-sonnet-4-20250514",
)

async with agent:
    await agent.run()  # subscribes to events, batches, and prompts the LLM
# Insecure — Zenoh (no auth)
ZENOH_CONNECT=tcp/localhost:7447 \
  DEVICE_CONNECT_ALLOW_INSECURE=true \
  ANTHROPIC_API_KEY="sk-ant-..." python my_agent.py

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

# Insecure — NATS (no auth)
# DEVICE_CONNECT_ALLOW_INSECURE=true \
#   ANTHROPIC_API_KEY="sk-ant-..." python my_agent.py

# Authenticated — NATS (JWT auth)
# ANTHROPIC_API_KEY="sk-ant-..." \
#   MESSAGING_CREDENTIALS_FILE=~/.device-connect/credentials/my-agent.creds.json \
#   NATS_URL=nats://localhost:4222 python my_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-edge — Credentials for the credentials file format.

Multi-Tenant Deployment

For deployments where multiple groups share the same infrastructure (workshops, labs, multi-team environments), you can enforce per-tenant isolation at the NATS broker level using JWT credentials. Each tenant's devices get JWT tokens restricted to their own namespace, making cross-tenant access cryptographically impossible.

cd security_infra

# 1. Bootstrap (once)
./setup_deployment.sh --nats-host dc.example.com

# 2. Create tenants with device tokens
./manage_tenants.sh create-batch alpha,beta,gamma --devices 5 --nats-host dc.example.com

# 3. Start infrastructure
DC_TENANTS=alpha,beta,gamma docker compose -f ../infra/docker-compose-multitenant-nats.yml up -d

# 4. Verify isolation
./verify_tenants.sh --nats-host dc.example.com

Each tenant gets a distributable credential bundle (zip) in security_infra/tenant-bundles/. See the full guide in security_infra/README.md.

CLI Tools

# Device control
devctl list                                # list registered devices
devctl list --compact                      # compact output
devctl register --id myDevice --keepalive  # register a test device
devctl mdns-scan --timeout 5              # find uncommissioned devices (mDNS)
devctl scan --timeout 5                   # alias for mdns-scan
devctl discover "device(category:camera)" # discover registered devices by selector
devctl discover-labels                    # list selector label keys and values
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

CLI migration note

This release renames the local-network mDNS scan from devctl discover --timeout ... to devctl mdns-scan --timeout ...; devctl scan --timeout ... is the short alias. The discover verb now belongs to selector discovery over the registered fleet, for example devctl discover "device(category:camera)".

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. By default all users can communicate over device-connect.* subjects. Use --tenant to restrict a user's JWT to a specific tenant namespace:

Operator: device-connect-operator   (trust root)
  └─ Account: DEVICE_CONNECT        (shared namespace)
       ├─ User: registry             (privileged — device-connect.>)
       ├─ User: devctl               (privileged — device-connect.>)
       ├─ User: orchestrator         (privileged — device-connect.>)
       ├─ User: camera-001           (--user, default tenant)
       ├─ User: alpha-device-001     (--tenant alpha — device-connect.alpha.>)
       └─ User: beta-device-001      (--tenant beta — device-connect.beta.>)

For multi-tenant deployments (workshops, labs), see Multi-Tenant Deployment and the full guide in security_infra/README.md.

See device-connect-edge — 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.

Metadata

Release files for device-connect-server 0.2.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for device-connect-server 0.2.5
File Size Uploaded
device_connect_server-0.2.5.tar.gz 173.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for device-connect-server 0.2.5
File Interpreter ABI Platform
device_connect_server-0.2.5-py3-none-any.whl Python 3 none any Details

Total release size: 391.0 kB

Release files / device_connect_server-0.2.5.tar.gz

Download URL device_connect_server-0.2.5.tar.gz
Size 173.1 kB
Tags Source
SHA-256 checksum
How to use checksums
595f0c5421995edd186cf5bb18206e3cd1eb301d07fc3a57c655bdfb04969121
BLAKE2b-256 checksum
How to use checksums
cee286a75c8ae46f7466e54780aa70d755bcfb5dbc11a0d7dbb5f0064896cc8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 13, 2026.

Transparency log

Release files / device_connect_server-0.2.5-py3-none-any.whl

Download URL device_connect_server-0.2.5-py3-none-any.whl
Size 217.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
61c5296d610a1e1b678a461722d576d29e47f4b36d0d388d150aae27282fc3af
BLAKE2b-256 checksum
How to use checksums
bd8b2d0c4d268b10689fab3b519cd566da654473ec9c47facd05080b9966cfe6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.5 This release

2 release files

0.2.4

2 release files

0.2.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page