High-performance FFI bindings between Bebop binary serialization and the V programming language for Industrial IoT edge computing
bebop-v-ffi provides a small, explicit FFI boundary that exposes the
Bebop binary serialization format to the V
programming language.
The project defines a stable C ABI (the "FFI contract") and allows multiple implementations behind it (initially Zig; Rust welcome), so that V applications can decode and encode Bebop messages without reimplementing the wire format.
This library is a core component of the Kaldor IIoT ecosystem, enabling efficient message passing between resource-constrained edge devices (ESP32-C6, RISC-V microcontrollers) and backend services.
This project is deliberately named bebop-v-ffi because the hard part here is not
"connecting two projects" — it’s defining and maintaining a correct Foreign Function
Interface.
An FFI boundary is where you must be explicit about things that higher-level code usually hides:
-
ABI stability - calling conventions, alignment/padding
-
Ownership and lifetimes - who frees what, when
-
Error propagation - how failures cross the boundary
-
Message framing - for networked data
Getting any of those wrong can produce silent corruption rather than a clean crash.
So this repo treats the C ABI as the core deliverable: a small, boring, stable contract that V can rely on, while different implementations can evolve underneath without changing user code.
| Component | Status | Notes |
|---|---|---|
ABI header |
drafted |
|
V bindings |
drafted |
|
Zig implementation |
placeholder skeleton |
|
Rust implementation |
help wanted |
|
Example framing |
included |
|
-
Generate C bindings from schema with
bebopc --lang c(seescripts/gen_c_from_schema.sh) -
Build an implementation (Zig today)
-
Use the
bebop_bridge.vmodule inv/directory from your V app
Bebop is a schema-based binary serialization format optimized for real-time applications:
-
10-100x faster than Protocol Buffers, MessagePack, and JSON
-
Zero-copy deserialization - reads directly from wire format
-
Tiny code generation - ~50KB for full runtime (ideal for microcontrollers)
-
Schema evolution - backwards-compatible message updates
-
No reflection - compile-time code generation, no runtime overhead
Benchmark: 1M messages (100-byte payload)
┌────────────────┬───────────┬─────────────┐
│ Format │ Serialize │ Deserialize │
├────────────────┼───────────┼─────────────┤
│ Bebop │ 12ms │ 8ms │
│ FlatBuffers │ 45ms │ 15ms │
│ Protocol Bufs │ 180ms │ 220ms │
│ MessagePack │ 350ms │ 410ms │
│ JSON │ 890ms │ 1,200ms │
└────────────────┴───────────┴─────────────┘V is a systems programming language designed for reliability and performance:
-
Compiles to native code - C backend, ~1MB binaries
-
No hidden allocations - predictable memory usage
-
No garbage collector pauses - manual memory management option
-
C interop - seamless FFI with existing C libraries
-
Fast compilation - ~1 second for most projects
-
Ideal for embedded - targets ESP32, ARM, RISC-V
V fills the gap between Rust’s complexity and C’s lack of safety, making it perfect for IIoT firmware development.
Kaldor IIoT needs to move telemetry data (temperature, humidity, loom status, spinning metrics) between:
-
Edge devices (ESP32-C6 running V firmware)
-
Gateway nodes (Raspberry Pi, RISC-V SBCs)
-
Backend services (Deno/TypeScript, Rust WASM)
Bebop-V-FFI provides the glue layer, enabling:
┌─────────────────┐ Bebop Binary ┌─────────────────┐
│ V Firmware │◄────────────────────►│ Deno Backend │
│ (ESP32-C6) │ ~8 bytes/msg │ (TypeScript) │
└────────┬────────┘ └────────┬────────┘
│ │
│ Matter Protocol (Thread mesh) │ WASM Compute
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Gateway Node │ │ TimescaleDB │
│ (RISC-V SBC) │ │ (Time-series) │
└─────────────────┘ └─────────────────┘-
Zero-Copy Parsing - Reads Bebop messages without intermediate allocations
-
Code Generation - V source generated from
.bopschema files -
Memory Safe - Bounds checking, no buffer overflows
-
Tiny Footprint - <20KB compiled FFI layer
-
Cross-Platform - Linux, macOS, Windows, embedded targets
-
Thread Safe - No global state, safe for concurrent use
-
Offline-First - Works completely air-gapped
-
RSR Compliant - Rhodium Standard Repository Bronze tier
# Clone repository
git clone https://github.com/hyperpolymath/bebop-v-ffi.git
cd bebop-v-ffi
# Build the FFI library
v build -prod src/
# Run tests
v test tests/
# Install Bebop compiler (for schema compilation)
# See: https://bebop.sh/guide/installation/Create a .bop schema file:
// schemas/telemetry.bop
struct SensorReading {
uint32 device_id;
uint64 timestamp_ms;
float32 temperature_c;
float32 humidity_pct;
uint8 status_flags;
}
message LoomStatus {
1 -> uint32 loom_id;
2 -> string pattern_name;
3 -> uint32 picks_completed;
4 -> uint32 picks_total;
5 -> float32 efficiency_pct;
}# Generate V bindings from Bebop schema
bebop --generator v --input schemas/telemetry.bop --output src/telemetry.vimport bebop_ffi
import telemetry
fn main() {
// Create a sensor reading
reading := telemetry.SensorReading{
device_id: 0x1001
timestamp_ms: u64(time.now().unix_milli())
temperature_c: 23.5
humidity_pct: 65.2
status_flags: 0x01
}
// Serialize to Bebop binary (zero-copy)
buffer := bebop_ffi.encode(reading)
println('Serialized: ${buffer.len} bytes')
// Send over network/Matter protocol...
// Deserialize (zero-copy)
decoded := bebop_ffi.decode[telemetry.SensorReading](buffer)
println('Temperature: ${decoded.temperature_c}°C')
}┌─────────────────────────────────────────────────────────┐
│ User Application │
│ (V source code) │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Generated V Bindings │
│ (from .bop schemas via bebop compiler) │
├─────────────────────────────────────────────────────────┤
│ struct SensorReading { ... } │
│ fn encode(msg) -> []u8 │
│ fn decode[T](buf) -> T │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Bebop-V-FFI Core │
│ (this library - V + C) │
├─────────────────────────────────────────────────────────┤
│ - Buffer management (arena allocator) │
│ - Endianness handling │
│ - Bounds checking │
│ - Zero-copy view creation │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Bebop Runtime │
│ (C library, ~50KB compiled) │
└─────────────────────────────────────────────────────────┘Why V instead of Rust for firmware?
While Rust is superior for complex systems, V offers:
-
Faster iteration - 1s compile times vs 30s+ for Rust
-
Simpler mental model - No borrow checker complexity on microcontrollers
-
Smaller binaries - Critical for ESP32’s 4MB flash
-
C-like FFI - Direct integration with ESP-IDF SDK
Why FFI instead of pure V?
Bebop’s core runtime is C-based, battle-tested, and optimized. Wrapping it via FFI gives us:
-
Proven serialization - No bugs in the hot path
-
Future-proofing - Bebop updates flow through automatically
-
Multi-language parity - Same wire format across V, Rust, TypeScript
Bebop-V-FFI is designed to work seamlessly with the Kaldor IIoT platform:
// firmware/main.v - ESP32-C6 Matter device
import matter
import bebop_ffi
import kaldor.telemetry
fn on_sensor_tick() {
reading := collect_sensor_data()
// Serialize with Bebop
payload := bebop_ffi.encode(reading)
// Send via Matter protocol (Thread mesh)
matter.publish("kaldor/telemetry", payload)
}
fn collect_sensor_data() telemetry.SensorReading {
return telemetry.SensorReading{
device_id: matter.get_device_id()
timestamp_ms: u64(time.now().unix_milli())
temperature_c: read_temperature_sensor()
humidity_pct: read_humidity_sensor()
status_flags: get_device_status()
}
}| Message Type | Size | Use Case |
|--------------|------|----------|
| SensorReading | 17 bytes | Environmental telemetry |
| LoomStatus | ~40 bytes | Weaving progress tracking |
| SpinnerMetrics | 24 bytes | Spinning node performance |
| AlertEvent | ~60 bytes | Fault notifications |
| GossipHeartbeat | 12 bytes | Mesh network health |
-
ROADMAP.adoc - Development roadmap and milestones
-
CLAUDE.md - AI assistant and developer guide
-
SECURITY.md - Security policy and vulnerability reporting
-
CONTRIBUTING.md - Contribution guidelines
-
API Reference - Full API documentation
Bebop-V-FFI meets Rhodium Standard Repository (RSR) Bronze tier requirements:
-
Type safety (V compile-time guarantees)
-
Memory safety (bounds checking, no raw pointers in public API)
-
Offline-first (no network dependencies)
-
Complete documentation
-
.well-known/directory -
Build system (justfile)
-
CI/CD pipeline
This project uses the Tri-Perimeter Contribution Framework (TPCF):
-
Perimeter 1: Core maintainers only
-
Perimeter 2: Trusted contributors
-
Perimeter 3: Community Sandbox - Open to all
All contributions are welcome! See CONTRIBUTING.md.
-
V compiler 0.4.4+ (
v version) -
C compiler (GCC 11+ or Clang 14+)
-
Bebop compiler (
bebopc --version) -
justcommand runner
# Run all tests
v test tests/
# Run with verbose output
v test tests/ -stats
# Run specific test
v test tests/test_encode.v
# Benchmark serialization
v run benchmarks/bench_encode.v| Operation | Time (1M msgs) | Memory | |-----------|----------------|--------| | Encode SensorReading | 8ms | 0 allocs | | Decode SensorReading | 5ms | 0 allocs | | Encode LoomStatus | 12ms | 1 alloc (string) | | Decode LoomStatus | 9ms | 1 alloc (string) |
Binary sizes:
-
FFI library: ~18KB (stripped)
-
Full firmware (with Matter): ~280KB
-
Bebop runtime: ~50KB
See SECURITY.md for:
-
Supported versions
-
Vulnerability reporting process
-
Security best practices
SPDX-License-Identifier: PMPL-1.0 OR LicenseRef-Palimpsest-0.8`
Dual licensed under your choice of:
-
PMPL-1.0-or-later - GNU Affero General Public License
-
Palimpsest License v0.8 - Community ownership with reversibility
-
kaldor-iiot - Parent IIoT platform
-
bunsenite - Nickel config parser with similar FFI architecture
-
Bebop - Binary serialization format
-
V Language - Systems programming language
See ROADMAP.adoc for the full development roadmap including:
-
Phase 1: Core FFI implementation and schema codegen
-
Phase 2: Release and packaging (crates.io, vpkg)
-
Phase 3: Next-gen Rust-V-Bebop hybrid implementation
I’d love a contributor to implement the same ABI in Rust (staticlib/cdylib), so V
users can choose Zig or Rust underneath.
What you’d do:
-
Implement the functions in
include/bebop_v_ffi.h -
Use
VBytes(ptr+len) for all strings/bytes (do not assume NUL-terminated data) -
Match the framing + golden test vectors under
test-vectors/
Good first PR:
-
bebop_decode_sensor_reading()forSensorReading -
bebop_ctx_new()/bebop_ctx_free()andbebop_free_reading()
See docs/60-contributing-implementations.adoc for notes and pitfalls.
-
Discussions: https://github.com/hyperpolymath/bebop-v-ffi/discussions
-
Kaldor Community: https://github.com/hyperpolymath/kaldor-iiot/discussions
-
Bebop Team - For the excellent serialization format
-
V Language Community - For the pragmatic systems language
-
Kaldor Contributors - For the IIoT vision
-
RSR Framework contributors
-
TPCF community
Version: 0.1.0
Status: Active Development
Last Updated: 2025-12-24
"Microseconds matter when you’re weaving the future."