Status: experimental (0.0.x). Works offline (L1): inbox directory, decisions, config files, install hook, report, with the decider and executor as separate, sandboxed processes. Document sync tested in a deterministic simulation. No network transports yet. Like all of weft, it is built heavily with AI; see How weft is made.
The agent that runs on Linux devices and gateways around weft-verifier: it receives signed documents over any path, lets the verifier decide, saves the decision state and executes the result. Design: weft-design docs/20-agent.md.
network / USB ──► [ transport ] unprivileged, sandboxed: Zenoh/MQTT/USB/registry I/O, document sync
│ raw bytes (documents, artifacts by digest)
▼
[ decider ] no network, no privileges: weft-verifier + persisted state
│ typed instructions: install(hash) · apply_config(hash) · start(app)
▼
[ executor ] privileged, tiny: re-checks hashes, activation gates,
calls SWUpdate / Podman / systemd; nothing else
weft-agent --tenant acme --anchor root.pub --label site=s1 --label line=l1 \
--state-dir /var/lib/weft --inbox /media/usb/weft --config-dir /etc/weft/config \
--on-config 'systemctl reload "$WEFT_COMPONENT"' \
--on-install 'my-ota-tool install --sha256 "$WEFT_ARTIFACT"'Each round (every --interval seconds, or once with --once):
- reads signed documents from the inbox (never modifies it) and stores new ones in
<state-dir>/documents/, before deciding; - lets weft-verifier decide; the verifier state is saved durably before anything runs;
- writes the effective configuration to
<config-dir>/<component>.jsonand runs--on-config; runs--on-installwithWEFT_COMPONENTandWEFT_ARTIFACT(SHA-256) for installs; - writes
<state-dir>/outbox/report.json: installed, applied, executed, failed, conflicts, withheld, rejected, pending, quarantined.
Documents are made with weft-cli (weft config, weft deploy, ...). Hooks are run with values only in environment variables and at least once, so
they must be idempotent, and exit successfully only once their effect is durable (weft records success
durably and does not repeat it). Hooks run with /bin/sh -c. The system clock is not trusted unless --trust-system-clock is given.
weft-agent executor --socket /run/weft/executor.sock # root; hooks from WEFT_ON_CONFIG / WEFT_ON_INSTALL
weft-agent decider --executor-socket /run/weft/executor.sock --tenant acme --anchor root.pub ...The decider parses all untrusted input (inbox files, documents) and runs as user weft without
privileges, without network and on a read-only system
(dist/systemd/weft-decider.service). It sends the executor
only typed instructions over a Unix socket (root:weft, 0660): apply this configuration or
install this artifact. The executor re-checks the configuration hash and the component path,
refuses everything else, and runs the hooks it was configured with; the decider cannot name a
command or a directory. weft-agent run keeps both in one process.
tests/vm/l1.nix boots NixOS with the shipped units from
dist/systemd/ and two read-only USB sticks of signed documents
(tests/vm/fixtures/, regenerated and checked by cargo test). It checks:
- stick 1: configuration applied and the artifact installed exactly once;
- the running decider is sandboxed: user
weft, no capabilities,NoNewPrivs, its own network namespace, a read-only system, and asystemd-analyze securityexposure of at most 2.5; - stick 2, then a power cut at a random moment of the round: after the reboot the configuration is one of the two versions, whole; then it converges to the new one, the forged document is rejected and not stored, and nothing is installed twice.
Run it with just vm (Linux with KVM); CI runs it as a separate job.
| Crate | Contents |
|---|---|
weft-transport |
Transport contract (best-effort publish/subscribe, portable topic keys) and a turmoil adapter for simulations |
weft-sync |
Document sync without I/O: content-addressed store, push plus anti-entropy pull, pending vs. shared, quarantine, limits |
weft-decider |
Runs weft_verifier::step, saves the state before acting (durable, atomic FileStore, power-loss tested), drives the executor, classifies documents for sync |
weft-agent |
The weft-agent binary (L1: inbox, document store, config and install hook executor, report) and the networked node runtime; the simulation and the requirements gate live in its tests |
crates/weft-agent/tests/simulation.rs runs a small
fleet in turmoil, deterministic per seed:
cloud ── gateway ──┬── dev-a (site=s1, line=l1)
├── dev-c (site=s1, line=l2; technician USB stick)
└── relay ── dev-b (site=s1, line=l1; behind a MALICIOUS relay)
Random latency, message loss, link failures, a partition and a restart of dev-a; documents enter at random times from the cloud and a USB stick. The relay drops, tampers with and replays documents. After the network heals:
- Convergence: all honest nodes hold exactly the issued documents, and nothing forged.
- Agreement: dev-a and dev-b (same labels, different paths) decide the same.
- Oracle: each device's final state equals a fresh evaluation of the same documents.
- Safety: tampered documents are never accepted; time floor and version marks never go back.
- Outcome: the tie between two site keys is reported, the revoked technician's change is gone.
The simulation found two design flaws on its first runs, both fixed:
- Documents signed by unknown keys were stored and spread by every node, so anyone could flood the fleet. Now only verified documents travel (AGT-003).
- weft-verifier reported "revoked" for documents that merely claimed a revoked key. Now "revoked" requires a valid signature by that key.
nix develop # or: rustup reads rust-toolchain.toml; needs ../weft-verifier next to this repo
just test # all tests, 16 simulation seeds
just sim 500 # more seeds
WEFT_SIM_SEED=13 just sim # replay one seed
just ci # everything CI checksrequirements/agent.sdoc (StrictDoc): AGT-001 to AGT-016, each
satisfying system requirements in
weft-design and traced
to functions and tests with @relation markers. cargo test fails when an active requirement has
no implementation or no test; just requirements renders HTML and ReqIF.
MIT, see LICENSE. Security reports: SECURITY.md.