{"id":50544780,"url":"https://github.com/tralamazza/mzmq_rs","last_synced_at":"2026-06-03T23:01:06.800Z","repository":{"id":353862744,"uuid":"1218068154","full_name":"tralamazza/mzmq_rs","owner":"tralamazza","description":"Minimal no_std ZMQ (ZMTP 3.1) PUB/RADIO library for embedded Rust.","archived":false,"fork":false,"pushed_at":"2026-04-25T23:50:20.000Z","size":237,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T00:34:19.583Z","etag":null,"topics":["embedded","rust","sans-io","zeromq","zmtp"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tralamazza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-22T13:56:03.000Z","updated_at":"2026-04-25T23:50:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tralamazza/mzmq_rs","commit_stats":null,"previous_names":["tralamazza/mzmq_rs"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/tralamazza/mzmq_rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tralamazza%2Fmzmq_rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tralamazza%2Fmzmq_rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tralamazza%2Fmzmq_rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tralamazza%2Fmzmq_rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tralamazza","download_url":"https://codeload.github.com/tralamazza/mzmq_rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tralamazza%2Fmzmq_rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33883102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["embedded","rust","sans-io","zeromq","zmtp"],"created_at":"2026-06-03T23:01:05.969Z","updated_at":"2026-06-03T23:01:06.790Z","avatar_url":"https://github.com/tralamazza.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mzmq\n\n[![Rust CI](https://github.com/tralamazza/mzmq_rs/actions/workflows/ci.yml/badge.svg)](https://github.com/tralamazza/mzmq_rs/actions/workflows/ci.yml)\n[![MSRV: 1.88](https://img.shields.io/badge/MSRV-1.88-blue)](https://github.com/rust-lang/rust/releases/tag/1.88.0)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA `no_std`, `no_alloc` Rust library that speaks [ZMTP 3.1](https://rfc.zeromq.org/spec/37/) as a **PUB** or **RADIO** endpoint. Built for Cortex-M-class targets that need to publish telemetry to ZMQ-based tooling without linking libzmq or pulling in `tokio`.\n\n## When to use this\n\n- You have an embedded device (or any `no_std` target) that needs to push data to a ZMQ subscriber\n- You want to use the standard PUB-SUB or RADIO-DISH wire protocol with zero heap allocation\n- You do **not** need to receive messages or act as a broker\n\n## Getting started\n\nAdd to `Cargo.toml`:\n\n```toml\n[dependencies]\nmzmq = \"0.1\"\n```\n\n### PUB-SUB\n\n```rust,no_run\nuse embedded_io_adapters::std::FromStd;\nuse mzmq::io::sync::Driver;\nuse std::net::TcpStream;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let stream = TcpStream::connect(\"127.0.0.1:5556\")?;\n    stream.set_nonblocking(true)?;\n\n    // Driver::\u003cSUB_CAP, PREFIX_CAP, FRAME_CAP, Transport\u003e\n    //   SUB_CAP    — max simultaneous subscriptions\n    //   PREFIX_CAP — max bytes per subscription prefix\n    //   FRAME_CAP  — internal frame buffer size\n    let mut driver = Driver::\u003c8, 32, 1024, _\u003e::new(FromStd::new(stream))?;\n\n    while !driver.poll()? {}                      // drive the ZMTP handshake\n\n    driver.publish(b\"hello\", b\"world\")?;          // returns 0 if no subscriber matches\n    Ok(())\n}\n```\n\n### RADIO-DISH (RFC 48)\n\nGroups are matched by **exact byte equality**, unlike the prefix matching of PUB-SUB.\n\n```rust,no_run\nuse embedded_io_adapters::std::FromStd;\nuse mzmq::io::sync::RadioDriver;\nuse std::net::TcpStream;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let stream = TcpStream::connect(\"127.0.0.1:5556\")?;\n    stream.set_nonblocking(true)?;\n\n    // RadioDriver::\u003cGROUP_CAP, GROUP_LEN_CAP, FRAME_CAP, Transport\u003e\n    let mut driver = RadioDriver::\u003c8, 32, 1024, _\u003e::new(FromStd::new(stream))?;\n\n    while !driver.poll()? {}\n\n    driver.publish(b\"alerts\", b\"temperature critical\")?;\n    Ok(())\n}\n```\n\nSee [`examples/pub_hello.rs`](examples/pub_hello.rs) for a runnable version with timeouts and error handling.\n\n### PLAIN security (optional)\n\nEnable the `plain` feature and use `Driver::new_plain(transport, authenticator)` instead of\n`Driver::new(transport)` to authenticate peers with a username/password pair. The authenticator\nmust implement `mzmq::plain::Authenticator`. PLAIN transmits credentials in clear text — only\nuse over trusted or encrypted transports.\n\n```rust,no_run\nuse mzmq::io::sync::Driver;\nuse mzmq::plain::Authenticator;\n\nstruct MyAuth { user: \u0026'static [u8], pass: \u0026'static [u8] }\nimpl Authenticator for MyAuth {\n    fn authenticate(\u0026self, username: \u0026[u8], password: \u0026[u8]) -\u003e bool {\n        username == self.user \u0026\u0026 password == self.pass\n    }\n}\n\nlet auth = MyAuth { user: b\"admin\", pass: b\"secret\" };\nlet mut driver = Driver::\u003c8, 32, 1024, _, _\u003e::new_plain(transport, auth)?;\n```\n\n## Features\n\n| Feature | Default | Description |\n|---------|:-------:|-------------|\n| `sync` | yes | Blocking driver over `embedded-io` |\n| `async` | no | Async driver over `embedded-io-async` |\n| `smoltcp` | no | Adapter for `smoltcp::socket::tcp::Socket` |\n| `std` | no | Opt out of `no_std`; required on hosted targets |\n| `plain` | no | ZMTP PLAIN security mechanism (RFC 27) — server role |\n| `python-tests` | no | Integration tests against a real `pyzmq` process |\n\n## smoltcp integration\n\nEnable the `smoltcp` feature to get a `TcpAdapter` that wraps a `smoltcp::socket::tcp::Socket` into an `embedded_io::Read + Write` transport:\n\n```toml\n[dependencies]\nmzmq = { version = \"0.1\", features = [\"smoltcp\"] }\n```\n\nBecause smoltcp sockets are managed through a `SocketSet`, you must borrow the socket\ntemporarily on each iteration and release it before calling `Interface::poll()`.\nUse the sans-IO `Connection` (or `RadioConnection`) directly:\n\n```rust,no_run\nuse mzmq::io::smoltcp::TcpAdapter;\nuse mzmq::connection::{Connection, State};\n\nlet mut conn = Connection::\u003c8, 32, 1024\u003e::new();\n\nloop {\n    // Drive the TCP stack first\n    iface.poll(Instant::now(), \u0026mut device, \u0026mut sockets);\n\n    // Borrow socket for one round of ZMTP I/O\n    {\n        let socket = sockets.get_mut::\u003cSocket\u003e(handle);\n        let mut transport = TcpAdapter(socket);\n\n        // Read → feed → write ready/pong/publish\n        let mut buf = [0u8; 512];\n        if let Ok(n) = transport.read(\u0026mut buf) {\n            conn.feed(\u0026buf[..n]);\n        }\n        if let State::Ready = *conn.state() {\n            let mut ready = [0u8; 32];\n            if let Ok(n) = conn.write_ready(\u0026mut ready) {\n                let _ = transport.write_all(\u0026ready[..n]);\n            }\n        }\n    }\n    // Socket borrow released — safe to call iface.poll() again\n}\n```\n\nSee [`examples/smoltcp_pub.rs`](examples/smoltcp_pub.rs) for a full runnable example.\n\n## `no_std` / embedded targets\n\nThe sans-IO core compiles with no default features:\n\n```bash\ncargo build --no-default-features --target thumbv7em-none-eabihf\n```\n\nAll capacity bounds (`SUB_CAP`, `PREFIX_CAP`, `FRAME_CAP`, …) are const generics resolved at compile time. The library uses [`heapless`](https://docs.rs/heapless) internally — no allocator required.\n\n## Protocol scope\n\n- **Transport**: ZMTP 3.1 (RFC 37), NULL and PLAIN (RFC 27, optional feature) security mechanisms\n- **Roles**: PUB (to SUB/XSUB peers) and RADIO (to DISH peers)\n- **Framing**: short frames (≤ 255 bytes) and long frames (\u003e 255 bytes)\n- **Subscriptions**: SUBSCRIBE/CANCEL (3.1) and legacy 0x01/0x00 prefix (3.0)\n- **Groups**: JOIN/LEAVE (RFC 48) with exact matching\n\n## Interop tests\n\nRun the integration tests against a live `pyzmq \u003e= 26` process:\n\n```bash\nuv sync\ncargo test --features python-tests -- --test-threads=1\n```\n\n## Releasing\n\nReleases are automated via [`cargo-release`](https://github.com/crate-ci/cargo-release).\n\n```bash\ncargo install cargo-release\ncargo release patch --execute   # or: minor / major / \u003cx.y.z\u003e\n```\n\nThis bumps the version in `Cargo.toml`, commits, tags `vX.Y.Z`, and pushes. The\n`Release` workflow then publishes to crates.io and creates a GitHub Release.\n\nRequired secret on the repo: `CARGO_REGISTRY_TOKEN` (crates.io API token with\npublish scope).\n\n## Development\n\nInstall git hooks:\n\n```bash\ngit config core.hooksPath .githooks\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftralamazza%2Fmzmq_rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftralamazza%2Fmzmq_rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftralamazza%2Fmzmq_rs/lists"}