The 30 MB open-source AI agent runtime for edge devices.
Build an edge agent visually, deploy it to a Raspberry Pi, and let it talk to GPIO, MQTT and local SLMs — no cloud required.
Offline by default. GPIO, UART, MQTT as first-class nodes. Local SLMs alongside cloud LLMs in the same workflow. Industrial protocols (OPC-UA, Modbus) are on the roadmap.
Runs on Raspberry Pi 5 · Jetson Orin Nano · STM32MP25 · Bosch Rexroth ctrlX CORE.
⭐ Star the repo if you think AI agents belong beyond the cloud.
Today's AI agents live in datacenters. The interesting workloads — sensors, machines, vehicles, gateways — live everywhere else. edge-agents brings the agent paradigm to the devices that interact with the real world: small enough to run on a Pi 5, capable enough to drive an industrial controller, with hardware I/O as native primitives instead of REST shims.
- Voice assistant on a Pi with a local SLM — wake-word → STT → agent → TTS, no internet required
- Predictive maintenance on industrial gear — live vibration stream over MQTT → LLM decides → MQTT alert
- Local RAG on a Jetson (on the roadmap) — answers grounded in live sensor and machine state instead of the public web (today the retriever runs against an external backend; a fully on-device RAG path is in progress)
| edge-agents | n8n | LangGraph | Dify | OpenClaw | |
|---|---|---|---|---|---|
| Runtime size | ~30 MB container | ~500 MB Docker | Python library | ~500 MB Docker | ~1 GB Docker |
| Offline by default | ✅ | ❌ | depends on host | ❌ | ❌ datacenter-only |
| Hardware I/O (GPIO, UART, ADC) as nodes | ✅ first-class | ❌ | ❌ | ❌ | ❌ |
| On-device SLM provider | ✅ typed multi-endpoint | ❌ | partial via libs | ❌ | ❌ |
| MQTT as workflow transport | ✅ first-class | community node | ❌ | ❌ | ❌ |
| Visual builder | ✅ | ✅ | ❌ code-only | ✅ | ❌ |
| Industrial protocols (OPC-UA, Modbus) | on roadmap | community nodes | ❌ | ❌ | ❌ |
Two pieces: the engine (a small container that runs your workflows) and the
fh-workflow CLI (authors, validates, and visually edits workflow files). You can
run the engine without ever cloning this repo, and author workflows with a single
npm i -g @foresthubai/workflow-cli.
The lightest path needs no clone and no Docker — just the CLI and the visual builder:
npm i -g @foresthubai/workflow-cli
fh-workflow open my.workflow.json # opens the visual builder; Save writes back to the fileDon't have a workflow yet? Let Claude Code write one from a single sentence — see Generate workflows with Claude Code. Ready to run it on real hardware? Run the engine on the device.
The engine ships as a small container you build from go/Dockerfile
(multi-arch, distroless, nonroot). Most edge targets are arm64 (Pi, Jetson, STM32MP2,
ctrlX), so the common flow is to cross-build on an amd64 workstation and ship the
result to the device:
cd go
# Cross-build for an arm64 edge device (use --platform linux/amd64 for x86 targets)
docker buildx build --platform linux/arm64 -t edge-agents/engine:arm64 --load .
# Ship to an offline device: save to a tar, copy it across, load it there
docker save edge-agents/engine:arm64 -o edge-agents-engine-arm64.tar
# scp edge-agents-engine-arm64.tar device:/tmp/ ← then, on the device:
docker load -i edge-agents-engine-arm64.tar
docker run --rm -p 8081:8081 edge-agents/engine:arm64Building for the same architecture you're already on? A plain
docker build -t edge-agents/engine:dev . works too — the Dockerfile cross-compiles via
TARGETARCH, so QEMU only emulates the trivial copy into the final layer.
The engine HTTP API listens on :8081 on all interfaces. It runs standalone by
default — no control plane, no account, no outbound calls beyond LLM provider APIs.
The deploy API is gated by a bearer token: set ENGINE_SECRET to enable /deploy and
/stop (without it those endpoints are closed and you load a workflow via
ENGINE_CONFIG_FILE instead). :8081 is meant to sit behind your own network controls,
not face the public internet. Configure via ENGINE_* env vars; see
go/cmd/engine/config.go.
Hardware access: the image runs as a nonroot distroless user, so reaching real GPIO,
serial or analog devices needs them passed into the container with the right group — e.g.
--device /dev/gpiochip0 --group-add "$(stat -c '%g' /dev/gpiochip0)" (or run with
--privileged on a throwaway dev box). Pure-software workflows need none of this.
A workflow is a *.workflow.json file you author, validate, and open in the visual
builder. Install the fh-workflow CLI from npm — no clone required:
npm i -g @foresthubai/workflow-cli
# or run it without installing:
npx @foresthubai/workflow-cli <command>fh-workflow open my.workflow.json # open the visual builder; Save writes back to the file
fh-workflow validate my.workflow.json # semantic: wiring, references, types
fh-workflow check-schema my.workflow.json # structural: types, required fields, enums
fh-workflow update my.workflow.json # migrate a workflow to the current schema version
fh-workflow help # list all commandsfh-workflow open is the visual builder — the same React Flow canvas, served locally;
hit Save and it writes straight back to your file. See ts/workflow-cli
for the full command reference and the --static / --dev open modes.
Describe a workflow in plain language and the workflow-generate skill writes the
*.workflow.json and runs the validators for you. Install it into any project with the
skills CLI — no clone required:
npx skills add ForestHubAI/edge-agents --skill workflow-generateThe skill validates by shelling out to the fh-workflow CLI, so install that too
(npm i -g @foresthubai/workflow-cli). Then just describe a workflow — e.g.
"read a sensor every 10s and toggle a relay" — and the skill generates and validates
the file for you.
A workflow is binding-free: it declares what it needs — channels (GPIO, MQTT, …)
and custom models — but not where those live on a given device. You supply the where
through a few small config files mounted into the engine container alongside the
workflow. See go/docs/deployment-layers.md for the file
schemas and deploy-time validation rules.
What the engine reads, and when each file is needed:
| File | Engine env var | When you need it |
|---|---|---|
| workflow JSON | ENGINE_CONFIG_FILE |
always — the graph itself |
| device manifest | ENGINE_DEVICE_MANIFEST_FILE |
only with hardware channels (GPIO / ADC / DAC / PWM / UART) — maps a logical id to a physical /dev/… |
| external resources | ENGINE_EXTERNAL_RESOURCES_FILE |
only with MQTT channels or custom/self-hosted models — broker connections and LLM endpoints |
| deployment mapping | ENGINE_DEPLOYMENT_MAPPING_FILE |
as soon as any channel or custom model exists — binds each logical id to a resource (+ index for hardware) |
Rule of thumb: a workflow with no channels and only built-in catalog models (e.g.
claude-haiku-4-5) needs none of the extra files — just the workflow JSON and the
provider's API key. Add the mapping the moment a channel or a custom model appears; add
the device manifest for hardware, external resources for MQTT and self-hosted models.
Ship the image with the docker save / docker load flow from
Run the engine and start it with docker run, mounting the files
above with -v and pointing the ENGINE_* env vars at them. The repo ships no
compose.yaml — write your own if you prefer docker compose.
- Workflow engine — typed graph runtime; nodes for LLM calls, hardware I/O, MQTT, web search, memory, control flow.
- Multi-provider LLMs — Anthropic, OpenAI, Google Gemini, Mistral, plus a local SLM provider for
llama.cpp/vLLM/Ollama/ any OpenAI-compatible endpoint. - Visual React Flow builder — embeddable component or runnable as bundled SPA, with typed parameters and live validation.
- Contract-typed wire format — every API generated from
contract/*.yamlfor both Go and TypeScript; CI fails on schema drift.
To run a model on the device, use llama.cpp: pull its server image and run it as its
own container serving a .gguf model file (e.g. a quantized Gemma):
docker pull ghcr.io/ggml-org/llama.cpp:server-b8589
docker run --rm --network host -v "$PWD/models:/models:ro" \
ghcr.io/ggml-org/llama.cpp:server-b8589 \
--model /models/gemma-3-270m-it-Q4_0.gguf --host 0.0.0.0 --port 8090The model runs in its own container, separate from the engine — start it before the
engine (or write a compose.yaml to bring both up together). In the workflow you reference the
model as a custom LLMModel and point it at this endpoint through the deploy files
(see Deploy a workflow to a device).
- GPIO via
go-gpiocdev(digital in/out, edge triggers) - ADC / DAC / PWM via Linux character-device interfaces
- UART / serial via
go.bug.st/serial - MQTT via Eclipse Paho — topic-scoped channels for device-to-device messaging
- Web search as a pluggable node
Digital and analog signal types are first-class in the workflow contract.
✅ = brought up and exercised on our own bench. We don't yet publish a per-device CI matrix, so treat these as known-good targets rather than a continuously tested guarantee.
| Target | Status |
|---|---|
| Raspberry Pi 5 (8 GB) | ✅ |
| NVIDIA Jetson Orin Nano (8 GB) | ✅ |
| x86 NUC (16 GB) | ✅ |
| STM32MP25 (1 GB, Linux MCU) | ✅ |
| Bosch Rexroth ctrlX CORE | ✅ |
Other Linux amd64 / arm64 |
Works, untested |
macOS arm64 / amd64 |
Supported (development) |
| Bare-metal MCU (Cortex-M) | Not supported by the Go engine. Contract is portable; dedicated MCU runtime is on the roadmap. |
Want to hack on the engine, the builder, or the contract? Clone the repo — go/ and
ts/ are independently buildable and releasable; only contract/ edits touch both.
git clone https://github.com/ForestHubAI/edge-agents
cd edge-agentsGo engine (requires the Go version pinned in go/go.mod):
cd go
go build ./cmd/engine
./engine # runs standalone by default
go test ./... # testify-based testsTypeScript packages (Node ≥ 20):
cd ts
npm ci
npm run dev # Vite dev server with the visual builder → http://localhost:5173
npm run build # build all three packages
npm run typecheck && npm run lint && npm testThe CLI from your working tree — the root package.json delegates to
ts/workflow-cli, so a single root npm install (its postinstall builds the ts/
toolchain) runs the validators against your local changes instead of the published
package:
npm install
npm run check-schema -- my.workflow.json # the -- passes the path to the CLI, not to npm
npm run validate -- my.workflow.json
npm run open -- my.workflow.jsonAfter a git pull that changed dependencies, just run npm install again. If a stale
node_modules bites you after switching branches, do a clean reinstall:
rm -rf node_modules ts/node_modules && npm installEvery API type is generated from contract/*.yaml for both Go and
TypeScript — CI fails on drift. Never hand-edit generated bindings; edit the contract,
then regenerate both sides:
cd go && go generate ./... # → go/api/*/types.gen.go, server.gen.go
cd ts && npm run generate # → ts/workflow-core/src/api/workflow.tsSee go/CLAUDE.md and ts/CLAUDE.md for the full
contributor guide, conventions, and the domain-layer reconciliation each side needs.
A workflow is a directed graph of typed nodes — LLM call, hardware I/O, MQTT, memory, control flow, expressions — connected by edges with one of five types: control, tool, agentTask, agentChoice, agentDelegate. The engine interprets the graph as a state machine: wait for event → execute node → transition. The contract (contract/*.yaml) is the single source of truth — Go and TypeScript both regenerate from it, CI fails on drift.
See go/CLAUDE.md and ts/CLAUDE.md for deeper architecture notes.
| Path | What it contains |
|---|---|
contract/ |
OpenAPI 3.0.3 schemas — single source of truth for Go and TS. |
go/ |
Engine binary, LLM proxy, hardware drivers, MQTT transport. Module github.com/ForestHubAI/edge-agents/go. |
ts/workflow-core |
@foresthubai/workflow-core — headless workflow model, validation, (de)serialization. No React. |
ts/workflow-builder |
@foresthubai/workflow-builder — React canvas component. |
ts/workflow-cli |
@foresthubai/workflow-cli — the fh-workflow CLI + the reference SPA it serves. |
- Go runtime — tagged
go/vX.Y.Z; consume withgo get github.com/ForestHubAI/edge-agents/go@vX.Y.Z. - TypeScript packages —
@foresthubai/workflow-core,@foresthubai/workflow-builder, and@foresthubai/workflow-cliship in lockstep at the same version, published to public npm. - Container image — built from
go/Dockerfile: multi-arch (linux/amd64,linux/arm64), distroless, nonroot. Build it yourself (see Run the engine).
See RELEASING.md.
See CONTRIBUTING and the Code of Conduct. Open an issue before any non-trivial change. Every contribution is accepted under a Contributor License Agreement that preserves the dual-licensing model.
Do not open public issues for security vulnerabilities. Use GitHub private vulnerability reporting or email root@foresthub.ai. See SECURITY.md for scope and process.
edge-agents uses a two-tier license model designed to make the wire format and the headless workflow model maximally reusable while keeping the engine and the visual builder protected under copyleft.
| Component | License | Why |
|---|---|---|
contract/ (OpenAPI schemas) |
Apache-2.0 | Wire format. Third-party Python, Rust, or Java clients should be free to implement against it. |
ts/workflow-core (headless model) |
Apache-2.0 | Workflow model and validation. Same reasoning — should be embeddable into any TypeScript/JavaScript project without copyleft friction. |
go/ (engine, LLM proxy, drivers) |
AGPL-3.0-only or commercial | Keeps hosted "edge-agents as a service" offerings honest. For commercial use cases incompatible with AGPL, contact root@foresthub.ai. |
ts/workflow-builder (React canvas) |
AGPL-3.0-only or commercial | Same dual-license terms as the engine. |
ts/workflow-cli (@foresthubai/workflow-cli + reference SPA) |
AGPL-3.0-only or commercial | Bundles the AGPL builder; same dual-license terms. |
For the AGPL components, the AGPL network clause applies — providing a modified version over a network requires making the corresponding source available to users of that service.
Third-party components retain their own licenses; see THIRD_PARTY_NOTICES and NOTICE.
Built by ForestHub — the platform for embedded and edge AI agents.
