limb is the host-side half of a limb device: a Rust library and one thin CLI
for discovering devices, moving raw pixels and PCM, managing device-carried
memory, and installing sandboxed WebAssembly reflexes.
Install the per-user I/O broker after building the CLI:
limb daemon install
limb daemon status
limb listlimbd owns the authenticated UDP gate-notification socket and arbitrates
local consumers. It keeps no idle connection to a device, runs no model, and
stores no conversation state. Pairing tokens are reloaded on every local
request, so pairing or revoking a device does not require a daemon restart.
limb listen is deliberately one transcription cycle, not an assistant. Run
it first, then use the device's physical gate gesture. The daemon accepts that
gate epoch, drains the device's bounded pre-roll buffer, and sends gated PCM to
the selected host-side STT provider. The command emits the first final
transcript, closes the gate early, and exits. limb listen --follow keeps the
same local subscription across multiple physical gate cycles.
To form a speech loop, choose any command that accepts one speech NDJSON line on stdin and writes its response text to stdout:
limb watch --on-speech 'your-agent --once'watch holds one --follow subscription for its complete lifetime, invokes
that command after each final transcript, and sends its stdout through
limb say. The subscription stays present while the command runs and while
playback completes, so there is no receive gap between turns. The command owns
the model, context, and conversation lifetime; limb remains only the gated
pipe.
Exactly one local speech consumer may subscribe to a device. A second
listen, watch, or channel account fails immediately with
LISTEN_ALREADY_RUNNING and identifies the current owner. An STT, stream, or
control failure closes only its gate epoch; the daemon and subscription remain
available for the next physical gesture. Use limb daemon clients to inspect
ownership and limb daemon logs for broker failures. --direct retains the
legacy device-polling path for old firmware and raw diagnostic capture.
Integrations may pass listen --consumer NAME; limb list and
limb daemon clients then report that stable owner instead of only a child
process id.
The device never connects to a speech cloud. Gated PCM travels only from the
device to a paired host; the host-side limb listen command chooses an STT
provider. Likewise, limb say chooses a host-side TTS provider and sends only
the resulting raw PCM to the device. ListenAI is the default provider, not a
binding. Edit providers.toml to replace it, or use whisper-local without
sending audio to a cloud.
For an incremental text producer, limb say --stream - keeps one provider
session and one device audio lease open. Each non-empty stdin line is sent as
the next text chunk while PCM is fetched and played concurrently; closing
stdin finishes synthesis.
Create a minimal behavior-neutral C project, edit its source and explicit manifest, then compile and audit it locally:
mkdir my-reflex && cd my-reflex
limb reflex init --name my-reflex
limb reflex build
limb reflex checkEvery project has the fixed layout reflex.c, manifest.json, and the
generated build/reflex.wasm. init creates a .gitignore containing
/build/, or preserves an existing file and adds that rule. From another
directory, pass only the project root: limb reflex build path/to/reflex.
build invokes a locally installed LLVM clang and wasm-ld; it never
downloads a compiler. Set LIMB_CLANG and LIMB_WASM_LD, or pass --clang
and --wasm-ld, when the tools are outside PATH. The command fixes the
wasm32 freestanding linker flags and writes the output only after checking the
ABI 1 import, lifecycle exports, memory bounds, module size, and manifest.
check performs the same audit without device discovery. limb push repeats
the audit before it opens a device connection.
The generated header exposes only the numeric limb.call syscall. It does not
add rendering primitives, fonts, codecs, networking, or a behavior template.
Build and inspect the complete command reference:
cargo build --release -p limb
target/release/limb --helpTagged CI runs also produce standalone archives for macOS arm64, macOS x86-64, and static Linux x86-64 (musl).
Store a ListenAI API key without putting it in shell history or a process list:
limb config set-credential listenaiThe command reads the secret from a terminal prompt and stores it in a
user-only credential file. LISTENAI_API_KEY overrides the stored value for
ephemeral use.
See providers.md for the registry format and provider exit paths. See FIRST-CONTACT.md for the clean-environment and physical-device acceptance record.
Applications can use liblimb directly without shelling out:
use liblimb::{ConfigStore, ControlClient, Discovery};
use std::time::Duration;
# async fn inspect() -> liblimb::Result<()> {
let config = ConfigStore::discover()?;
let device = Discovery::new(Duration::from_secs(2))
.resolve("limb-a1b2c3.local", &config)
.await?;
let token = config.token(&device.id)?;
let mut control = ControlClient::connect(
device,
token.as_ref().map(|record| record.token.as_str()),
)
.await?;
println!("{}", control.describe().await?);
# Ok(())
# }