{"id":36602635,"url":"https://github.com/smqd/smqd-core","last_synced_at":"2026-01-12T08:40:13.230Z","repository":{"id":37336563,"uuid":"138820748","full_name":"smqd/smqd-core","owner":"smqd","description":"Framework for building a scalable, resilient, concurrent messaging server written in Scala","archived":false,"fork":false,"pushed_at":"2022-06-22T11:51:55.000Z","size":1549,"stargazers_count":4,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2023-07-02T22:06:11.142Z","etag":null,"topics":["iot","mqtt","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","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/smqd.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}},"created_at":"2018-06-27T02:43:10.000Z","updated_at":"2022-06-21T11:14:57.000Z","dependencies_parsed_at":"2022-09-09T09:31:14.072Z","dependency_job_id":null,"html_url":"https://github.com/smqd/smqd-core","commit_stats":null,"previous_names":[],"tags_count":15,"template":null,"template_full_name":null,"purl":"pkg:github/smqd/smqd-core","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smqd%2Fsmqd-core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smqd%2Fsmqd-core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smqd%2Fsmqd-core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smqd%2Fsmqd-core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smqd","download_url":"https://codeload.github.com/smqd/smqd-core/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smqd%2Fsmqd-core/sbom","scorecard":{"id":833872,"data":{"date":"2025-08-11","repo":{"name":"github.com/smqd/smqd-core","commit":"ad0b6f4d1159a040ecfa4cf2f10a538e83420d8c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/smqd/smqd-core/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/smqd/smqd-core/build.yml/main?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-23T18:26:59.870Z","repository_id":37336563,"created_at":"2025-08-23T18:26:59.870Z","updated_at":"2025-08-23T18:26:59.870Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28337599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["iot","mqtt","scala"],"created_at":"2026-01-12T08:40:13.160Z","updated_at":"2026-01-12T08:40:13.219Z","avatar_url":"https://github.com/smqd.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SMQD core\n\n[![Sonatype Nexus (Releases)](https://img.shields.io/nexus/r/https/oss.sonatype.org/com.thing2x/smqd-core_2.12.svg)](https://oss.sonatype.org/content/groups/public/com/thing2x/smqd-core_2.12/)\n[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/https/oss.sonatype.org/com.thing2x/smqd-core_2.12.svg)](https://oss.sonatype.org/content/groups/public/com/thing2x/smqd-core_2.12/)\n[![Actions Status](https://github.com/smqd/smqd-core/workflows/CI%20build/badge.svg)](https://github.com/smqd/smqd-core/actions)\n[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)\n\nSMQD :: Scala MQtt Daemon\n\n## Usage\n\n```scala\n    libraryDependencies += \"com.thing2x\" %% \"smqd-core\" % \"x.y.z\"\n```\n\nIf you want to try snapshot version, add a resolver for soatype repository.\nSince snapshot version can be updated in any time,\nit would be better to mark `changing()` on the dependency\nso that sbt will check if there is updated snapshot version in the repository\n\n```scala\n    resolvers += Resolver.sonatypeRepo(\"public\")\n\n    val smqdVersion = \"x.y.z-SNAPSHOT\"\n\n    if (smqdVersion.endsWith(\"-SNAPSHOT\"))\n        libraryDependencies += \"com.thing2x\" %% \"smqd-core\" % smqdVersion changing()\n    else\n        libraryDependencies += \"com.thing2x\" %% \"smqd-core\" % smqdVersion\n```\n\n## Features\n\n- [x] Mqtt 3.1.1 (protocol level 0x04)\n- [x] Mqtt over TLS (mqtt, mqtts)\n- [x] Mqtt over Websockets (ws, wss)\n- [x] Clustering (inter-nodes message routing)\n- [x] Local Topic\n- [x] Queued Topic\n- [x] Shared Topic\n- [x] System Topic ($SYS)\n- [x] Request \u0026 Response pattern in embed mode\n- [x] Http RESTful API (http, https)\n- [x] Bridges (through external plugins)\n- [x] Third-party plugins\n\n#### Local Subscription: \n\nSMQD will not make cluster ranged routes for local subscription, and only deliver the messages on the node\n\n```\n    sub '$local/topic'\n    pub 'topic'\n```\n\n![sub_local_1](docs/img/sub_local_1.png)\n\nOther normal subscribers are not affected by local subscription.\n\n![sub_local_2](docs/img/sub_local_2.png)\n\n#### Queue Subscription (Load balancing)\n\nThe messages are distributed among *queue* subscribers.\n\n\n```\n    sub '$queue/topic'\n    pub 'topic'\n```\n\n![sub_queue](docs/img/sub_queue.png)\n\n#### Shared Subscription (Load balancing among the same group subscribers)\n\nThe messages are distributed among same *group* subscribers.\n\n```\n    sub '$share/\u003cgroup\u003e/topic'\n    pub 'topic'\n```\n\n![sub_share](docs/img/sub_share.png)\n\n#### System topics\n\n- [x] $SYS/faults : system fault messages\n- [x] $SYS/protocols : MQTT network control packet tracking\n- [x] $SYS/metric/data : system metric collections\n- [x] $SYS/plugins/events : plugin events of changing status\n\n\n* how to subscribe to smqd via mqtt\n\n```\nmosquitto_sub -t sensor/1/# -h 127.0.0.1 -p 1883 -i client_sub -u user -P user -d -V mqttv311\n```\n\n* how to publish to smqd via mqtt\n\n```\nmosquitto_pub -t sensor/1/temp -h 127.0.0.1 -p 1883 -i client_pub -m \"test message\" -u user -P user -d -V mqttv311 -q 2\n```\n\n#### Mqtt over WebSocket\n\n* how to subscribe to smqd via ws\n\n```\n$ npm install mqtt --save\n```\n\n```javascript\nvar mqtt = require('mqtt')\nvar client  = mqtt.connect('ws://127.0.0.1:8086')\n\nclient.on('connect', function () {\n  client.subscribe('sensor/+/temperature')\n})\n\nclient.on('message', function (topic, message) {\n  // message is Buffer\n  console.log(message.toString())\n})\n```\n\n* how to publish to smqd via ws\n\n```javascript\nvar mqtt = require('mqtt')\nvar client  = mqtt.connect('ws://127.0.0.1:8086')\n\nclient.on('connect', function () {\n  client.publish('sensor/1/temperature', '10')\n  client.publish('sensor/2/temperature', '20')\n  client.publish('sensor/3/temperature', '30')\n})\n\n```\n\n## Embeded Mode\n\n\u003e SMQD is work-in-progress and may break backward compatibility.\n\n\n### Initialize\n\n#### Simplest way\n\n```scala\nval config = ConfigFactory.load(\"application.conf\")\nval smqd = SmqdBuilder(config).build()\n\nsmqd.start()\n\nscala.sys.addShutdownHook {\n    smqd.stop()\n}\n```\n\n#### Customized way\n\n```scala\n  val config = ConfigFactory.load(\"application.conf\")\n  val system = ActorSystem.create(\"smqd\", config)\n\n  val services: Map[String, Config] = ...\n\n  val smqd = SmqdBuilder(config)\n    .setActorSystem(system)\n    .setUserDelegate(new CustomUserDelegate())\n    .setClientDelegate(new CustomClientDelegate())\n    .setRegistryDelegate(new CustomRegistryDelegate())\n    .setSessionStoreDelegate(new CustomSessionStore())\n    .setServices(services)\n    .build()\n\n  smqd.start()\n\n  scala.sys.addShutdownHook {\n    smqd.stop()\n    system.terminate()\n  }\n```\n\n#### Subscribe API\n\n* actor subscription\n\n```scala\nclass SubsriberActor extends Actor {\n  override def receive: Receive = {\n    case (topic: TopicPath, msg: Any) =\u003e\n      printlns\"====\u003e ${topic} ${msg}\")\n  }\n}\n\nval myActor = system.actorOf(Props(classOf[SubsriberActor]), \"myActor\")\n\nsmqd.subscribe(\"registry/test/#\", myActor)\n\n1 to 100 foreach { i =\u003e\n    smqd.publish(s\"registry/test/$i/temp\", s\"Hello\")\n}\n\nsmqd.unsubscribe(myActor)\n```\n\n* callback subscription\n\n```scala\nval subr = smqd.subscribe(\"registry/test/+/temp\"){\n    case (topic, msg) =\u003e\n        logger.info(s\"====\u003e ${topic} ${msg}\")\n}\n\n1 to 100 foreach { i =\u003e\n    smqd.publish(s\"registry/test/$i/temp\", s\"Hello World - $i\")\n}\n\nsmqd.unsubscribe(subr)\n```\n\n#### Publish API\n\n```scala\nsmqd.publish(\"my/topic\", \"Hello Message\")\n```\n\n#### Request-response API\n\n```scala\nimplicit val timeout: Timeout = 1 second\nimplicit val ec: ExecutionContext = system.dispatcher\n\nval f: Future[String] = smqd.request[String](\"request/func\", classOf[String], \"Hello\")\n\nf.onComplete {\n    case Success(str) =\u003e\n        println(\"Ok Responsed: {}\", str)\n    case Failure(ex) =\u003e\n        println(\"exception\", ex)\n}\n```\n\n## Cluster\n\n### non-cluster mode\n\nsmqd runs non-cluster mode by default. It is configured by akka's provider.\nIf the provider is set as 'local', other settings of cluster (`smqd.cluster.*`) are ignored\n\n```\nakka.actor.provider = local\n```\n\n### cluster mode\n\nTo start smqd in cluster mode, akka provider should be `cluster`\n\n```\nakka.actor.provider = cluster\n```\n\nAnd akka cluster provider requires seed-nodes information for bootstrapping.\nsmqd supports multiple ways discovering seed-nodes addresses to akka.\n\n#### static\n\nThe static discovery method is the default of akka cluster. Please refer the seed-nodes fundamental from\n[akka documents](https://doc.akka.io/docs/akka/current/cluster-usage.html#joining-to-seed-nodes).\nThe only differencie from original akka cluster is using `smqd.static.seeds` instead of `akka.cluster.seed-nodes`.\nThe other considerations should be same as the akka document says\n\n\u003e Since smqd manages the cluster configuration and bootstrapping procedure by itself,\nDo not use `akka.cluster.seed-nodes` in configuration.\n\n```\nsmqd {\n  cluster {\n      discovery = static\n      static {\n        seeds = [\"192.168.1.101:2551\", \"192.168.1.102:2551\", \"192.168.1.103:2551\"]\n      }\n  }\n}\n```\n\n#### etcd\n\nYou can use etcd as a storage of dynamic seed-node information. In this discovery mode,\nyou don't need to manage the addresses of nodes in the cluster\n\n```\nsmqd {\n  cluster {\n      discovery = etcd\n      etcd {\n        server = \"http://192.168.1.105:2379\"\n        prefix = /smqd/cluster/seeds\n        node_ttl = 1m\n      }\n  }\n}\n```\n\n## Customize behaviors\n\n### Delegates\n\n#### Client Authentication\n\nEvery application has its own policy to authenticate client's connections. SMQD put this role under `ClientDelegate`.\nApplication that needs to customize the authentication policy shlould implement `Clientelegate`.\nThe following code is SMQD's default ClientDelegate implimentation.\n\nThere are three parameters for the method `clientLogin()`.\n`clientId` represents client-id that is defined in MQTT specification.\nAnd `userName` and `password` are `Option` as MQTT protocol.\nIf your application doesn't want to allow zero-length clientId or empty `username`,\njust return `BaseNameOrpassword` instead of `SmqSuccess`\n\n\u003e The ClientDelegate is called only when a client is connecting to the SMQD via network as a mqtt client.\n\u003e Internal publishing/subscription through api is not a subject of the authentication\n\n```scala\nclass MyAuthDelegate extends com.thing2x.smqd.ClientDelegate {\n  override def clientLogin(clientId: String, username: Option[String], password: Option[Array[Byte]]): Future[SmqResult] = {\n    Future {\n      println(s\"[$clientId] username: $username password: $password\")\n      if (username.isDefined \u0026\u0026 password.isDefined) {\n        if (username.get == new String(password.get, \"utf-8\"))\n          SmqSuccess\n        else\n          BadUserNameOrPassword(clientId, \"Bad user name or password \")\n      }\n      else {\n        SmqSuccess\n      }\n    }\n  }\n}\n```\n\nThere are three ways to register your `ClientDelegate`.\n\n1) Change configuration to replace `ClientDelegate`.\n\n\u003e In this case your customized AuthDelegate class shouldn't have any parameter to instantiate.\n\n```\nsmqd {\n  delegates {\n    client = com.sample.MyClientDelegate\n  }\n}\n```\n\n2) `SmqdBuilder` has `setClientDelegate()` that takes an instance of `ClientDelegate`.\n\n\u003e If your `ClientDelegate` needs parameters to instantiate, this is the way to do.\n\n```scala\nval smqd = SmqdBuilder(config)\n    .setClientDelegate(new MyClientDelegate(...params...))\n    .build()\n```\n\n3) If you want to get full control of customization delegates, define `FacilityFactory` class and register the factory class in `smqd.conf`.\nThe default factory is defined as below.\n\n```\nsmqd.facility_factory = com.thing2x.smqd.impl.DefaultFacilityFactory\n```\n\nThe `FacilityFactory` is a factory class to produce all kind of delegates implementation.\nYou can replace it your own factory implementation by extending `DefaultFacilityFactory`.\n\n```scala\nclass DefaultFacilityFactory(config: Config) extends FacilityFactory {\n\n  override def userDelegate: UserDelegate = {\n    new DefaultUserDelegate()\n  }\n\n  override def clientDelegate: ClientDelegate = {\n    new DefaultClientDelegate()\n  }\n\n  override def registryDelegate: RegistryDelegate = {\n    new DefaultRegistryDelegate()\n  }\n\n  override def sessionStoreDelegate: SessionStoreDelegate = {\n    new DefaultSessionStoreDelegate()\n  }\n}\n```\n\n### Plugins\n\n[smqd](https://github.com/smqd/smqd) mqtt broker is also built on smqd-core, it consists of several plugins that provides basic features. It is possible to extend its features by installing additional plugins. Not only the smqd itself but also your application that uses smqd-core as a framework can use the plugin facility, which means that you can build an application logic or business login as a plugin and then install/reinstall/start/stop it by smqd-core REST API or its web UI while the application is running.\n\n![img](docs/img/plugins.jpg)\n\nWhen smqd-core is instantiated, it refer to `smqd.plugin.manifest` configuration to find and load the plugin manifest that lists available plugins for the instance. The manifest file can located in local disk of the same machine or remote server. smqd-core supports `http` for remotely located manifest file.\n\nA plugin manifest file contains multiple desciptions of plugin packages. A package can be a plain jar file in a local directory specified by `smqd.plugin.dir` or can be downloaded from remote web server. We recommand to use `maven` repository as a plugin distribution method, since smqd-core supports remote maven repositories to search and download plugins. Using maven repository for plugins has many benefits like downloading dependencies together, easy to integrate with CI tools, guarantee the version of plugin, security and so on.\n\n\n#### Gateway plugins\n\n- smqd-plugin-coap: CoAP gateway, receiving CoAP messages and translate them to mqtt messages and vice versa.\n\n#### Bridge plugins\n\n- [smqd-bridge-mqtt](http://github.com/smqd/smqd-bridge-mqtt/): bridge between smqd and external mqtt broker\n- [smqd-bridge-http](http://github.com/smqd/smqd-bridge-http/): bridge between smqd and external http server\n\n### Configuration\n\n- [Reference config](src/main/resources/smqd-ref.conf)\n\n## For Developers\n\n- Core API\n\n  Postman json file is available [here](src/test/conf/SMQD.postman_collection.json)\n\n  Run smqd-core process with following sbt command to test rest api\n\n```bash\nsbt '; set javaOptions ++= Seq(\"-Dconfig.file=./src/test/conf/smqd-1.conf\", \"-Dlogback.configurationFile=./src/test/conf/logback.xml\", \"-Djava.net.preferIPv4Stack=true\"); runMain com.thing2x.smqd.Main'\n```\n\n- Multi Jvm Test\n\nRun all multi-jvm test cases\n\n```bash\nsbt multi-jvm:test\n```\n\nRun a specific multi-jvm test case\n\n```bash\nsbt multi-jvm:testOnly \u003ctest class\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmqd%2Fsmqd-core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmqd%2Fsmqd-core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmqd%2Fsmqd-core/lists"}