{"id":38815016,"url":"https://github.com/zhongwm/cable","last_synced_at":"2026-01-17T12:59:22.140Z","repository":{"id":57735468,"uuid":"300812554","full_name":"zhongwm/cable","owner":"zhongwm","description":"Functional, Practical, a scala ssh library","archived":false,"fork":false,"pushed_at":"2021-04-17T03:12:28.000Z","size":421,"stargazers_count":35,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2023-07-04T12:41:50.326Z","etag":null,"topics":["scala","scala-ssh","scala-ssh-library","ssh-config-scala","ssh-proxying","zio","zio-ssh"],"latest_commit_sha":null,"homepage":"https://github.com/zhongwm/cable","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zhongwm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-03T06:28:12.000Z","updated_at":"2022-02-12T08:26:11.000Z","dependencies_parsed_at":"2022-08-23T20:30:32.212Z","dependency_job_id":null,"html_url":"https://github.com/zhongwm/cable","commit_stats":null,"previous_names":[],"tags_count":6,"template":null,"template_full_name":null,"purl":"pkg:github/zhongwm/cable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongwm%2Fcable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongwm%2Fcable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongwm%2Fcable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongwm%2Fcable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zhongwm","download_url":"https://codeload.github.com/zhongwm/cable/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongwm%2Fcable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28508932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T11:50:55.898Z","status":"ssl_error","status_checked_at":"2026-01-17T11:50:55.569Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":["scala","scala-ssh","scala-ssh-library","ssh-config-scala","ssh-proxying","zio","zio-ssh"],"created_at":"2026-01-17T12:59:21.621Z","updated_at":"2026-01-17T12:59:22.133Z","avatar_url":"https://github.com/zhongwm.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[comment]: \u003c\u003e ([![Release Artifacts][Badge-MavenCentralReleases]][Link-MavenCentralReleases])\n\n[comment]: \u003c\u003e ([![Badge-Scaladoc]][Link-Scaladoc])\n\n# Cable - scala ssh library\n\n## A SSH client lib that is Functional, Monadic\n\nIt's scala, It's purely functional, monadic.\n\n## Practical, Functionality rich\n\nCable is functionality-rich, task-centric, succinct, handy.\nCable Supports ssh proxying, jumping over networks, tasks chaining.\n\nWe support $HOME/.ssh config as well as the global ssh client config.\n\n#### Installation\n\n```scala\nlibraryDependencies += \"io.github.zhongwm\" %% \"cable\" % \"0.4.1\"\n```\n\n### Supports ssh proxying, in a monadic way!\n\nYour host behind a bastion machine? You have a series of remote tasks to deal with? no problem.\nAnd connections are reused by multiple tasks for the same machine.\n\nA DSL to represent composite ssh tasks.\n\n#### Simple ssh task\n\n```scala\nimport cable.zssh.TypeDef._\nimport HostConnS._\nimport cable.zssh.Zssh._\n\nval simpleTask =\n  Action(\"192.168.99.100\", password = Some(\"password\"), action = scriptIO(\"sleep 5; ls /\"))\n```\n\nMost of the parameters can be omitted only the hostname or address (the first parameter) is\nrequired, We can use a private key instead of password, it can be your default .ssh rsa ssh key. Cable\nwill read your ssh key from that file and use that key for authentication.\n      \n#### Simple ssh task with multiple tasks on a same host or connection.\n```scala\n  val simpleData =\n    Action(\n      \"192.168.99.100\"\n      action = scriptIO(\"hostname\") \u003c\u0026\u003e\n        scpUploadIO(\"build.sbt\") \u003c\u0026\n        scpDownloadIO(\"/etc/issue\")\n    )\n\n```\n\n#### Multiple ssh tasks example\n\nTo get multiple tasks on different hosts executed one after another, chain them up using `+:`.\n\n```scala\n  val simpleListTasks =\n    Action(\"192.168.99.100\", Some(privateKey), action = scriptIO(\"cat /etc/issue\")) +:\n    Action(\"192.168.99.100\", port = Some(2023), username=Some(\"user\"), password=(\"password\"), scpDownloadIO(\"/etc/issue\"))\n```\n\n#### Nested ssh tasks example\n\nWith nested ssh tasks composing, parent level acts as the jumper host for the child tasks, also\nparent level tasks get gets executed before the latter.\n\n```scala\n  val simpleNestedTasks = Parental(\n    JustConnect(\"192.168.99.100\", username=Some(\"user\"), password=Some(\"password\")),\n      Action(\"192.168.99.100\" password = Some(\"password\"), action = scpUploadIO(\"build.sbt\"))\n  )\n```\n\n#### Compound example\n\n```scala\n  val compoundTasks =\n    JustConnect(\"192.168.99.100\", 2022, \"user\", \"password\") +:\n    Parental(\n      JustConnect(\"192.168.99.100\", 2022, \"user\", \"password\" ),\n        Action(\"192.168.99.100\", 2022, \"user\", \"password\", scriptIO(\"hostname\")) +:\n        Action(\"192.168.99.100\", 2022, \"user\", \"password\", scpUploadIO(\"build.sbt\"))\n    ) +:\n    Action(\"192.168.99.100\", 2023, \"user\", \"password\", scpDownloadIO(\"/etc/issue\"))\n```\n\n### Running\n\nTap on `run` to fire task execution. Result types are inferred and reflecting the task composition\nstructure.\n\n```scala\nval nestedResult = simpleNestedTasks.run() // Inferred type: NestedC[Unit, (Int, (Chunk[String], Chunk[String]))]\nval listResult = simpleListTasks.run()     // Inferred type: (Int, (Chunk[String], Chunk[String])) +|: (Int, (Chunk[String], Chunk[String]))\n```\n\n### Resource Safe\n\nAs we can see in the previous sample code, we don't need to concern about connections' management, yet it's safely managed. \n\nConnections are guaranteed to be released correctly\n\n#### Docs\n\n[Documentation](wiki/Contents.md)\n\n[To get started](src/test/scala/cable/zssh/ExecSpec.scala)\n\n## Full Support for ZIO programming\n\nFull support for ZIO composition, ready to be embedded into ZIO project, \ncompatible with ZIO ecosystem.\n\n```scala\n  val action = {\n    scriptIO(\"hostname\") \u003c\u0026\u003e\n      scpUploadIO(\"build.sbt\") \u003c\u0026\n      scpDownloadIO(\"/etc/issue\")\n  }\nval jumperLayer = Zssh.sessionL(\"192.168.99.100\", 2022, username = Some(\"test\"), password = Some(\"test\"))\n\n  val jumpedLayer =\n    Zssh.jumpSessionL(jumperLayer, \"192.168.99.100\", 2023, Some(\"test\"), Some(\"test\"))\n\n  val layer2 =\n    ((jumperLayer ++ Blocking.live) \u003e\u003e\u003e Zssh.jumpAddressLayer(\"192.168.99.100\", 2023)) ++ Blocking.live\n\n  val layer3 = layer2 \u003e\u003e\u003e Zssh.jumpSshConnL(Some(\"test\"), Some(\"test\"))\n\n  val layer4 = (Zssh.clientLayer ++ layer3 ++ Blocking.live) \u003e\u003e\u003e Zssh.sessionL\n\n```\n\n```scala\nprivate val process = for {\n    connJump \u003c- Zssh.make(\n                  Left(\"192.168.99.100\", 2022),\n                  username = Some(\"test\"),\n                  password = Some(\"test\")\n                )\n    rst      \u003c- connJump.sessionM { outerSession =\u003e\n                  Zssh.jumpTo(\"192.168.99.100\", 2023)(outerSession) \u003e\u003e= { fwd =\u003e\n                    val conn = Zssh(Right(fwd.getBoundAddress), Some(\"test\"), password = Some(\"test\"))\n                    conn.sessionM { innerSession =\u003e\n                      Zssh.script(\"hostname\")(innerSession) \u003c\u0026\u003e\n                      Zssh.scpUpload(\"build.sbt\")(innerSession) \u003c\u0026\n                      Zssh.scpDownload(\"/etc/issue\")(innerSession)\n                    }\n                  }\n                }\n    _        \u003c- putStrLn(rst._1._2._1.mkString)\n    _        \u003c- putStrLn(rst._1._2._2.mkString)\n    xc       \u003c- ZIO.succeed {\n                  zio.ExitCode(rst._1._1)\n                }\n  } yield xc\n```\n\n## Efficient, fast\n\nBased on mina-sshd-netty\n\n## P.S.\n\nThis project is greatly inspired by a famous python project [ansible](https://ansible.com), which is\na very famous project in devops. This project strives to join the functional world and the devops\nworld in the field of remote host related tasks. Not all of them, not all of ansible, but in some way.\n\n\n[comment]: \u003c\u003e ([Link-MavenCentralReleases]: https://repo1.maven.org/maven2/io/github/zhongwm/cable_2.13/ \"Maven Central Releases\")\n\n[comment]: \u003c\u003e ([Badge-MavenCentralReleases]: https://maven-badges.herokuapp.com/maven-central/io.github.zhongwm/cable_2.13/badge.svg \"Maven Central Release\")\n\n[comment]: \u003c\u003e ([Badge-Scaladoc]: https://javadoc-badge.appspot.com/io.github.zhongwm/cable_2.13.svg?label=scaladoc \"Scaladoc\")\n\n[comment]: \u003c\u003e ([Link-Scaladoc]: https://javadoc.io/doc/io.github.zhongwm/cable_2.13/latest/cable/index.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhongwm%2Fcable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzhongwm%2Fcable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhongwm%2Fcable/lists"}