No description
  • Rust 65.7%
  • Nix 34.3%
Find a file
2026-07-27 19:59:21 +02:00
bin fix: adapt to rcgen 0.14 — key_pair → signing_key 2026-07-27 19:21:28 +02:00
crates fix: adapt to tokio-tungstenite 0.30 — tungstenite body is now Bytes, no .to_vec() 2026-07-27 19:30:46 +02:00
docs stream: let ecd fill in ?src= server-side 2026-07-11 04:30:16 +02:00
examples stream: let ecd fill in ?src= server-side 2026-07-11 04:30:16 +02:00
nix stream: let ecd fill in ?src= server-side 2026-07-11 04:30:16 +02:00
.gitignore speaker: implement audio-playback via an mpv idle-helper service 2026-07-06 01:43:36 +02:00
Cargo.lock Merge remote-tracking branch 'origin/renovate/toml-1.x' 2026-07-27 19:59:21 +02:00
Cargo.toml Merge remote-tracking branch 'origin/renovate/toml-1.x' 2026-07-27 19:59:21 +02:00
flake.lock initial commit 2026-06-19 23:52:00 +02:00
flake.nix tests: full QEMU VM test for the speaker + auto-discover test files 2026-07-06 02:51:05 +02:00
README.md speaker: implement audio-playback via an mpv idle-helper service 2026-07-06 01:43:36 +02:00
renovate.json Add renovate.json 2026-07-25 02:00:33 +00:00
rust-toolchain.toml initial commit 2026-06-19 23:52:00 +02:00

💠 edgecore

A lean, declarative agent that turns a physical device into a discoverable,
composable capability surface — speaking W3C Web of Things.

edgecore is a framework for special devices and a control daemon, ecd,
that exposes a device's capabilities and values. You describe a device once, in a
single declarative TOML manifest; ecd then:

  • generates a conformant WoT Thing Description (TD 1.1),
  • serves a WoT HTTP API plus a small built-in web GUI,
  • routes each interaction to its backing (a command, file, HTTP endpoint, …),
  • announces the device on the LAN over DNS-SD/mDNS (_wot._tcp).

Any WoT consumer can drive the device with zero edgecore-specific knowledge.

edgecore is a subproject of git.hydrar.de/jmarya/myverse.

Quick start

With Nix (flakes enabled):

nix develop          # dev shell with the pinned Rust toolchain
nix build .#ecd      # build the daemon
nix flake check      # build + clippy + rustfmt + tests

Run the daemon against a manifest:

ecd path/to/device.toml      # serve the device
ecd --check device.toml      # validate the manifest and exit
ecd --td    device.toml      # print the generated Thing Description

One process can host several Things (a WoT Servient) — pass multiple manifests
or a directory of them. They share one port; each keeps its own base path, and
GET /things lists every hosted Thing Description:

ecd things.d/                        # every *.toml in the directory
ecd lobby-01.toml door-02.toml       # or an explicit list

Then visit http://<host>:<port><base> for the GUI, or fetch the TD at
<base>/.well-known/wot.

A minimal manifest

[device]
id    = "urn:edgecore:kiosk:lobby-01"
title = "Lobby Kiosk"
class = "kiosk"

[property.brightness]
type    = "integer"
unit    = "percent"
minimum = 0
maximum = 100
source  = { exec = "backlight-get" }
sink    = { exec = "backlight-set {value}" }

[action.reload]
run = { exec = "kiosk-reload {hard}" }
  [action.reload.input.properties.hard]
  type = "boolean"

[expose]
bind = ["http"]
gui  = true
  [expose.http]
  port = 9688
  base = "/things/lobby-01"
  [expose.discovery]
  mdns = true

See the examples/ directory for full manifests:
lobby-01.toml (kiosk),
camera-01.toml (camera),
env-sensor-01.toml (sensor),
light-01.toml (smart light),
speaker-01.toml (speaker).
See docs/manifest.md for the complete schema.

HTTP surface

Route WoT operation
GET /.well-known/wot fetch the Thing Description
GET /properties/{name} read a property (SSE observe with Accept: text/event-stream)
PUT /properties/{name} write a property
POST /actions/{name} invoke an action (JSON body → backing)
GET /events/{name} subscribe to an event (SSE)
ANY /stream/{*rest} authenticated reverse proxy to a local media server (when [stream] is set)
GET / the web GUI (when expose.gui = true)

All routes are mounted under the device's expose.http.base, and are served over
HTTPS when [expose.http.tls] is set.

Documentation

Repository layout

crates/manifest    manifest types · parse · validate
crates/td          Thing Description model · generation
crates/backing     the Backing trait + exec/file/http/const/journal
crates/server      axum WoT HTTP surface + web GUI + auth
crates/discovery   mDNS / DNS-SD advertise + browse
bin/ecd            the daemon
bin/edgectl        fleet CLI/TUI: discover and drive Things
nix/modules        NixOS modules: ecd, kiosk, camera
examples           example manifests

edgectl — the fleet client

edgectl discovers Things on the LAN over mDNS and drives them by a short
handle — multi-Thing from the start, no per-command endpoint juggling.

edgectl discover                     # browse _wot._tcp → list the whole fleet
edgectl get webcam resolution        # read a property (Thing addressed by handle)
edgectl set lobby brightness 70      # write a property
edgectl invoke webcam snapshot '{"output_dir":"/tmp"}'
edgectl watch door state             # live SSE stream
edgectl tui                          # interactive fleet browser

Add --url <servient> to include hosts not on mDNS, --token/$ECD_TOKEN for
auth (applied per each Thing's advertised scheme), and -k to accept the
self-signed certs edge devices serve.

Status

v0 speaks HTTP(S) (optional TLS with an operator or persisted self-signed
cert — no ACME), uses SSE for property observation and events, and a single
device-wide security scheme that also gates an optional authenticated media
reverse proxy ([stream]). Implemented backings: exec, file, http,
const, journal. See docs/architecture.md for the
current state of each crate.