{"id":48921135,"url":"https://github.com/meshtastic/mqttastic-client-kmp","last_synced_at":"2026-04-28T17:01:11.037Z","repository":{"id":351856552,"uuid":"1212600523","full_name":"meshtastic/MQTTastic-Client-KMP","owner":"meshtastic","description":"Kotlin MultiPlatform MQTT 5.0 Client Library","archived":false,"fork":false,"pushed_at":"2026-04-17T03:25:51.000Z","size":736,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-17T05:37:31.381Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/meshtastic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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},"funding":{"open_collective":"meshtastic"}},"created_at":"2026-04-16T14:39:47.000Z","updated_at":"2026-04-17T03:24:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/meshtastic/MQTTastic-Client-KMP","commit_stats":null,"previous_names":["meshtastic/mqttastic-client-kmp"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/meshtastic/MQTTastic-Client-KMP","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meshtastic%2FMQTTastic-Client-KMP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meshtastic%2FMQTTastic-Client-KMP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meshtastic%2FMQTTastic-Client-KMP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meshtastic%2FMQTTastic-Client-KMP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/meshtastic","download_url":"https://codeload.github.com/meshtastic/MQTTastic-Client-KMP/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meshtastic%2FMQTTastic-Client-KMP/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32390067,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T14:34:11.604Z","status":"ssl_error","status_checked_at":"2026-04-28T14:32:37.009Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2026-04-17T05:04:04.118Z","updated_at":"2026-04-28T17:01:11.028Z","avatar_url":"https://github.com/meshtastic.png","language":"Kotlin","funding_links":["https://opencollective.com/meshtastic"],"categories":[],"sub_categories":[],"readme":"# MQTTastic Client KMP\n\n\u003cimg src=\"docs/images/app-icon.png\" alt=\"MQTTastic app icon\" align=\"right\" width=\"128\" height=\"128\" /\u003e\n\n[![Maven Central](https://img.shields.io/maven-central/v/org.meshtastic/mqtt-client?label=Maven%20Central)](https://central.sonatype.com/artifact/org.meshtastic/mqtt-client)\n[![Kotlin](https://img.shields.io/badge/Kotlin-2.3.20-7f52ff.svg?logo=kotlin)](https://kotlinlang.org)\n[![License](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](LICENSE)\n[![CI](https://github.com/meshtastic/MQTTastic-Client-KMP/actions/workflows/ci.yml/badge.svg)](https://github.com/meshtastic/MQTTastic-Client-KMP/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/meshtastic/MQTTastic-Client-KMP/graph/badge.svg)](https://codecov.io/gh/meshtastic/MQTTastic-Client-KMP)\n[![API Docs](https://img.shields.io/badge/API%20Docs-latest-blue.svg)](https://meshtastic.github.io/MQTTastic-Client-KMP)\n\nA fully-featured **MQTT 5.0 and 3.1.1** client library for **Kotlin Multiplatform** — connecting JVM, Android, iOS, macOS, Linux, Windows, and browsers through a single, idiomatic Kotlin API.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/images/sample-app.png\" alt=\"MQTTastic sample app running on Android, connected to mqtt.meshtastic.org and streaming live mesh traffic\" width=\"820\" /\u003e\n  \u003cbr /\u003e\n  \u003cem\u003eThe bundled Compose Multiplatform sample app, live on \u003ccode\u003etls://mqtt.meshtastic.org:8883\u003c/code\u003e.\u003c/em\u003e\n\u003c/p\u003e\n\n## Features\n\n- 📦 **Full MQTT 5.0 + 3.1.1** — all 15 packet types, properties, reason codes, and enhanced auth (5.0); backward-compatible 3.1.1 support\n- 🔄 **All QoS levels** — QoS 0, 1, and 2 with complete state machine handling\n- 🌍 **True multiplatform** — one codebase, 9 targets (see [Platform Support](#platform-support))\n- 🔒 **TLS/SSL** — secure connections on all native/JVM targets\n- 🌐 **WebSocket** — binary WebSocket transport on all platforms (behind LBs, CDNs, firewalls)\n- ⚡ **Coroutines-first** — `suspend` functions and `Flow`-based message delivery\n- 🪶 **Minimal dependencies** — only Ktor (transport) + kotlinx-coroutines + kotlinx-io\n- 🛡️ **Immutable by design** — `ByteString` payloads, validated inputs, data class models\n- 📝 **Configurable logging** — zero-overhead `MqttLogger` interface with level filtering\n- ✅ **Spec-validated** — topic filter wildcards, reserved bits, and packet structure per MQTT 5.0\n\n## Why MQTTastic?\n\n| | MQTTastic | Typical alternatives |\n|---|---|---|\n| **Pure KMP** | Single codebase, single API across all platforms | Wrappers around platform SDKs (Paho, Mosquitto) |\n| **MQTT 5.0 first** | Built from the ground up for 5.0 — not retrofitted from 3.1.1 | Bolt-on 5.0 support with incomplete property coverage |\n| **Coroutines-native** | `suspend` functions and `Flow` everywhere — no callbacks, no blocking | Callback-heavy APIs requiring manual coroutine bridging |\n| **Zero platform deps** | Only Ktor + kotlinx-coroutines + kotlinx-io | Bundles native C libraries or platform-specific SDKs |\n| **Immutable \u0026 validated** | `ByteString` payloads, validated topic filters, range-checked properties | Mutable byte arrays, silent truncation, unchecked inputs |\n\n## Platform Support\n\n| Platform | Target | Transport | Status |\n|----------|--------|-----------|--------|\n| JVM | `jvm` | TCP/TLS, WebSocket | ✅ |\n| Android | `android` | TCP/TLS, WebSocket | ✅ |\n| iOS | `iosArm64`, `iosSimulatorArm64` | TCP/TLS, WebSocket | ✅ |\n| macOS | `macosArm64` | TCP/TLS, WebSocket | ✅ |\n| Linux | `linuxX64`, `linuxArm64` | TCP/TLS, WebSocket | ✅ |\n| Windows | `mingwX64` | TCP/TLS, WebSocket | ✅ |\n| Browser | `wasmJs` | WebSocket | ✅ |\n\n## Architecture\n\nAll protocol logic — packet encoding/decoding, the client state machine, QoS flows, and property handling — lives in **`commonMain`** as pure Kotlin. Platform source sets contain _only_ transport implementations: TCP/TLS sockets for native/JVM targets and WebSocket frames for the browser. This means every bug fix, feature, and optimization applies to all 9 targets simultaneously.\n\n```\n┌─────────────────────────────────────────────┐\n│  MqttClient              (commonMain)       │  ← public API: suspend + Flow\n│  MqttConnection / QoS state machines        │  ← protocol logic, keepalive\n│  MqttPacket / Encoder / Decoder             │  ← MQTT 5.0 wire format\n├──────────────────────┬──────────────────────┤\n│  TcpTransport        │  WebSocketTransport  │\n│  (nonWebMain)        │  (nonWebMain +       │\n│  ktor-network + TLS  │   wasmJsMain)        │\n│                      │  ktor-client-ws      │\n└──────────────────────┴──────────────────────┘\n```\n\nThe `MqttTransport` interface is the sole platform abstraction boundary — it is `internal`, not part of the public API. Coroutines drive everything: `suspend` functions for operations, `SharedFlow\u003cMqttMessage\u003e` for incoming messages, and `StateFlow\u003cConnectionState\u003e` for lifecycle observation.\n\n## Installation\n\nAdd the dependency to your `build.gradle.kts`:\n\n```kotlin\n// settings.gradle.kts\nrepositories {\n    mavenCentral()\n}\n\n// build.gradle.kts\nkotlin {\n    sourceSets {\n        commonMain.dependencies {\n            implementation(\"org.meshtastic:mqtt-client:0.3.0\")\n        }\n    }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eGroovy DSL\u003c/summary\u003e\n\n```groovy\n// settings.gradle\nrepositories {\n    mavenCentral()\n}\n\n// build.gradle\nkotlin {\n    sourceSets {\n        commonMain {\n            dependencies {\n                implementation 'org.meshtastic:mqtt-client:0.3.0'\n            }\n        }\n    }\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSingle-platform (JVM / Android only)\u003c/summary\u003e\n\n```kotlin\ndependencies {\n    implementation(\"org.meshtastic:mqtt-client:0.3.0\")\n}\n```\n\u003c/details\u003e\n\n## Quick Start\n\n```kotlin\nimport org.meshtastic.mqtt.*\n\n// Create a client with the factory DSL\nval client = MqttClient(\"my-client\") {\n    keepAliveSeconds = 30\n    autoReconnect = true\n    defaultQos = QoS.AT_LEAST_ONCE  // all publishes default to QoS 1\n}\n\n// Connect, work, and auto-close\nclient.use(MqttEndpoint.parse(\"tcp://broker.example.com:1883\")) { c -\u003e\n    // Subscribe\n    c.subscribe(\"sensors/temperature\")\n\n    // Publish (uses defaultQos from config)\n    c.publish(\"sensors/temperature\", \"22.5\")\n\n    // Collect messages\n    c.messagesForTopic(\"sensors/temperature\").collect { msg -\u003e\n        println(\"Received: ${msg.payloadAsString()}\")\n    }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eVerbose equivalent (without convenience APIs)\u003c/summary\u003e\n\n```kotlin\nval config = MqttConfig(clientId = \"my-client\", keepAliveSeconds = 30, autoReconnect = true)\nval client = MqttClient(config)\n\nclient.connect(MqttEndpoint.Tcp(host = \"broker.example.com\", port = 1883))\nclient.subscribe(\"sensors/temperature\", QoS.AT_LEAST_ONCE)\nclient.publish(\n    MqttMessage(\n        topic = \"sensors/temperature\",\n        payload = ByteString(\"22.5\".encodeToByteArray()),\n        qos = QoS.AT_LEAST_ONCE,\n    ),\n)\nclient.messages.collect { msg -\u003e\n    if (msg.topic == \"sensors/temperature\") {\n        println(\"Received: ${msg.payload.toByteArray().decodeToString()}\")\n    }\n}\nclient.close()\n```\n\u003c/details\u003e\n\n### MQTT 3.1.1 Support\n\nBy default, the client automatically negotiates the protocol version. It connects with MQTT 5.0 first and, if the broker rejects it with `UNSUPPORTED_PROTOCOL_VERSION`, seamlessly retries with MQTT 3.1.1 on a fresh connection — no configuration needed:\n\n```kotlin\n// Auto-negotiation is on by default — works with both 5.0 and 3.1.1 brokers\nval client = MqttClient(\"my-client\") {\n    keepAliveSeconds = 30\n}\n\nclient.use(MqttEndpoint.parse(\"tcp://any-broker:1883\")) { c -\u003e\n    // After connect, check which version was negotiated:\n    println(\"Connected with ${c.negotiatedProtocolVersion}\")\n    c.subscribe(\"sensors/#\")\n    c.messagesForTopic(\"sensors/#\").collect { msg -\u003e\n        println(\"Received: ${msg.payloadAsString()}\")\n    }\n}\n```\n\nTo force a specific version or disable negotiation:\n\n```kotlin\n// Force MQTT 3.1.1 (no negotiation)\nval v311Client = MqttClient(\"my-client\") {\n    protocolVersion = MqttProtocolVersion.V3_1_1\n}\n\n// Force MQTT 5.0 only (disable fallback)\nval v5OnlyClient = MqttClient(\"my-client\") {\n    negotiateVersion = false\n}\n```\n\nMQTT 3.1.1 mode automatically:\n- Omits properties sections from all packets\n- Uses 3.1.1 CONNACK return codes (mapped to `ReasonCode`)\n- Encodes subscribe options as QoS-only (no `noLocal`, `retainAsPublished`, `retainHandling`)\n- Sends a body-less DISCONNECT on close\n- Skips topic aliases and flow control (Receive Maximum)\n\n5.0-only config options (`sessionExpiryInterval`, `authenticationMethod`) are rejected at config-build time when `V3_1_1` is explicitly selected. When using auto-negotiation, fallback is skipped if the config uses 5.0-only features — the original rejection is re-thrown so you know the broker doesn't support your configuration.\n\n### Convenience APIs\n\nThe library ships several ergonomic extensions to reduce boilerplate:\n\n| API | What it replaces |\n|-----|-----------------|\n| `MqttClient(\"id\") { ... }` | `MqttClient(MqttConfig(clientId = \"id\", ...))` |\n| `MqttEndpoint.parse(\"tcp://host:1883\")` | `MqttEndpoint.Tcp(host, port, tls)` |\n| `client.use(endpoint) { ... }` | Manual `connect` + `try/finally { close() }` |\n| `msg.payloadAsString()` | `msg.payload.toByteArray().decodeToString()` |\n| `client.messagesForTopic(\"x\")` | `client.messages.filter { it.topic == \"x\" }` |\n| `client.messagesMatching(\"x/+/y\")` | Manual wildcard matching on `messages` flow |\n| `client.publish(topic, payload)` | Constructing `MqttMessage` manually |\n| `defaultQos` / `defaultRetain` | Repeating `qos = QoS.AT_LEAST_ONCE` on every publish |\n| `will { topic = ...; payload(\"...\") }` | `will = WillConfig(topic = ..., payload = ByteString(...))` |\n\n#### Endpoint Parsing\n\nParse broker URIs instead of constructing endpoints manually:\n\n```kotlin\nMqttEndpoint.parse(\"tcp://broker:1883\")       // Plain TCP\nMqttEndpoint.parse(\"ssl://broker:8883\")       // TCP + TLS\nMqttEndpoint.parse(\"mqtts://broker\")          // TLS, default port 8883\nMqttEndpoint.parse(\"wss://broker/mqtt\")       // Secure WebSocket\n```\n\n#### Topic-Filtered Message Flows\n\n```kotlin\n// Exact topic match\nclient.messagesForTopic(\"sensors/temperature\").collect { ... }\n\n// Wildcard filter (supports + and #)\nclient.messagesMatching(\"sensors/+/temperature\").collect { ... }\n```\n\n### Builder DSL\n\nUse the builder DSL for complex configurations (annotated with `@MqttDsl` for scope safety, like Ktor's `@KtorDsl`):\n\n```kotlin\nval config = MqttConfig.build {\n    clientId = \"sensor-hub-01\"\n    keepAliveSeconds = 30\n    cleanStart = false\n    autoReconnect = true\n    defaultQos = QoS.AT_LEAST_ONCE\n    logger = MqttLogger.println()\n    logLevel = MqttLogLevel.DEBUG\n    will {\n        topic = \"sensors/status\"\n        payload(\"offline\")\n        qos = QoS.AT_LEAST_ONCE\n        retain = true\n    }\n}\n```\n\n### Logging\n\nThe library provides a zero-overhead logging interface. When no logger is configured (the default), message lambdas are never evaluated:\n\n```kotlin\n// Built-in println logger for quick debugging\nval config = MqttConfig(\n    clientId = \"debug-client\",\n    logger = MqttLogger.println(),\n    logLevel = MqttLogLevel.DEBUG,\n)\n\n// Custom logger (e.g., forwarding to your app's logging framework)\nval config = MqttConfig(\n    clientId = \"production-client\",\n    logger = object : MqttLogger {\n        override fun log(level: MqttLogLevel, tag: String, message: String, throwable: Throwable?) {\n            myAppLogger.log(level.name, \"[$tag] $message\", throwable)\n        }\n    },\n    logLevel = MqttLogLevel.INFO,\n)\n```\n\nLog levels from most to least verbose: `TRACE` → `DEBUG` → `INFO` → `WARN` → `ERROR` → `NONE`.\n\n## Android / KMP Integration\n\nThe library is designed as a drop-in MQTT client for KMP projects. Consumer ProGuard/R8 rules are bundled automatically.\n\n### ViewModel-scoped client\n\n```kotlin\nclass MqttViewModel : ViewModel() {\n    private val client = MqttClient(\"my-device\") {\n        autoReconnect = true\n        keepAliveSeconds = 30\n    }\n\n    val connectionState = client.connectionState\n\n    fun connect(broker: String) {\n        viewModelScope.launch {\n            client.connect(MqttEndpoint.parse(broker))\n            client.subscribe(\"msh/2/e/#\", QoS.AT_LEAST_ONCE)\n        }\n    }\n\n    fun observeMessages() = client.messagesMatching(\"msh/2/e/+/!/#\")\n\n    override fun onCleared() {\n        viewModelScope.launch { client.close() }\n    }\n}\n```\n\n### Collecting in Compose\n\n```kotlin\n@Composable\nfun MqttScreen(viewModel: MqttViewModel) {\n    val state by viewModel.connectionState.collectAsStateWithLifecycle()\n\n    LaunchedEffect(Unit) {\n        viewModel.observeMessages().collect { msg -\u003e\n            // Process message\n        }\n    }\n}\n```\n\n### Version alignment\n\nThe library uses **Ktor 3.4.2** and **kotlinx-coroutines 1.10.2**. If your project uses the same versions, no conflicts will arise. Pin versions in your `libs.versions.toml` to avoid Gradle resolution surprises.\n\n## MQTT 5.0 Coverage\n\n### Protocol\n\n| Feature | Status | Spec Section |\n|---------|--------|--------------|\n| All 15 packet types | ✅ | §2.1 |\n| Variable Byte Integer encoding | ✅ | §1.5.5 |\n| UTF-8 string pairs | ✅ | §1.5.7 |\n\n### Quality of Service\n\n| Feature | Status | Spec Section |\n|---------|--------|--------------|\n| QoS 0 (at most once) | ✅ | §4.3.1 |\n| QoS 1 (at least once) | ✅ | §4.3.2 |\n| QoS 2 (exactly once) | ✅ | §4.3.3 |\n| Duplicate detection (DUP flag) | ✅ | §3.3.1.1 |\n\n### Session \u0026 Connection\n\n| Feature | Status | Spec Section |\n|---------|--------|--------------|\n| Session management (cleanStart) | ✅ | §3.1.2.4 |\n| Will messages \u0026 Will Delay | ✅ | §3.1.3.2 |\n| Keep-alive \u0026 PINGREQ/PINGRESP | ✅ | §3.1.2.10 |\n| Automatic reconnection | ✅ | — |\n| Server redirect | ✅ | §4.13 |\n\n### Advanced Features\n\n| Feature | Status | Spec Section |\n|---------|--------|--------------|\n| Topic aliases | ✅ | §3.3.2.3.4 |\n| Enhanced authentication (AUTH) | ✅ | §4.12 |\n| Flow control (Receive Maximum) | ✅ | §3.3.4 |\n| Request/Response pattern | ✅ | §4.10 |\n| Shared subscriptions | ✅ | §4.8.2 |\n| Subscription identifiers | ✅ | §3.8.3.1 |\n| Topic filter validation | ✅ | §4.7 |\n\n### Observability\n\n| Feature | Status | Spec Section |\n|---------|--------|--------------|\n| Configurable logging (6 levels) | ✅ | — |\n| Connection state observation | ✅ | — |\n\n### Known Limitations\n\n| Limitation | Detail |\n|------------|--------|\n| Enhanced auth during CONNECT | Auth challenges are delivered only after the connection is established. SASL-style challenge/response during the CONNECT handshake (§4.12.1) is not yet supported. |\n| Client-side session persistence | When `cleanStart=false`, the broker resumes session state, but the client does not persist in-flight QoS 1/2 messages across reconnects. Unacknowledged messages may be lost. |\n\n## Building\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for build setup, development workflow, and the full command reference.\n\n## Documentation\n\n| Resource | Link |\n|----------|------|\n| API Reference | [meshtastic.github.io/MQTTastic-Client-KMP](https://meshtastic.github.io/MQTTastic-Client-KMP) |\n| Configuration Guide | [docs/configuration.md](docs/configuration.md) |\n| Topics \u0026 QoS Guide | [docs/topics-and-qos.md](docs/topics-and-qos.md) |\n| MQTT 5.0 Specification | [OASIS MQTT v5.0](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html) |\n| Changelog | [CHANGELOG.md](CHANGELOG.md) |\n\n## Contributing\n\nContributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on:\n- Setting up your development environment\n- Code style and conventions\n- Submitting pull requests\n\nFor vulnerability reports, see the [Security Policy](SECURITY.md).\nAll participants are expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md).\n\n## License\n\nThis project is licensed under the [GNU General Public License v3.0](LICENSE),\nconsistent with all repositories in the [Meshtastic](https://github.com/meshtastic) organization.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeshtastic%2Fmqttastic-client-kmp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmeshtastic%2Fmqttastic-client-kmp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeshtastic%2Fmqttastic-client-kmp/lists"}