{"id":20469257,"url":"https://github.com/terminalstudio/dartssh2","last_synced_at":"2025-04-08T12:09:25.228Z","repository":{"id":37478394,"uuid":"413380233","full_name":"TerminalStudio/dartssh2","owner":"TerminalStudio","description":"SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use.","archived":false,"fork":false,"pushed_at":"2025-02-24T08:58:40.000Z","size":463,"stargazers_count":223,"open_issues_count":66,"forks_count":65,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-01T11:06:24.188Z","etag":null,"topics":["dart","flutter","flutter-package","sftp","sftp-client","ssh","ssh-client"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/dartssh2","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TerminalStudio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2021-10-04T10:47:23.000Z","updated_at":"2025-03-27T13:32:02.000Z","dependencies_parsed_at":"2024-06-18T20:10:37.092Z","dependency_job_id":"f907c650-f383-43ae-8dad-b1d6f01424db","html_url":"https://github.com/TerminalStudio/dartssh2","commit_stats":{"total_commits":236,"total_committers":12,"mean_commits":"19.666666666666668","dds":0.4279661016949152,"last_synced_commit":"bb8fb2984445daa90d832d18f697c0e8424a90d2"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TerminalStudio%2Fdartssh2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TerminalStudio%2Fdartssh2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TerminalStudio%2Fdartssh2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TerminalStudio%2Fdartssh2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TerminalStudio","download_url":"https://codeload.github.com/TerminalStudio/dartssh2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247838444,"owners_count":21004580,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["dart","flutter","flutter-package","sftp","sftp-client","ssh","ssh-client"],"created_at":"2024-11-15T14:08:23.456Z","updated_at":"2025-04-08T12:09:25.199Z","avatar_url":"https://github.com/TerminalStudio.png","language":"Dart","readme":"\u003c!-- Title--\u003e\n\u003ch1 align=\"center\"\u003eDartSSH 2\u003c/h1\u003e\n\n\u003c!-- Badges--\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pub.dartlang.org/packages/dartssh2\"\u003e\n    \u003cimg src=\"https://img.shields.io/pub/v/dartssh2.svg\" alt=\"DartSSH2 package version on Pub\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.dartdocs.org/documentation/dartssh2/latest/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Docs-dartssh2-blue.svg\" alt=\"DartSSH2 documentation\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/TerminalStudio/dartssh2/actions/workflows/dart.yml\"\u003e\n    \u003cimg src=\"https://github.com/TerminalStudio/dartssh2/actions/workflows/dart.yml/badge.svg\" alt=\"DartSSH2 GitHub Actions workflow status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://ko-fi.com/F1F61K6BL\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Buy%20Me%20a%20Coffee-F16061?style=flat\u0026logo=buy-me-a-coffee\u0026logoColor=white\u0026labelColor=555555\" alt=\"Support me on Ko-fi\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp style=\"text-align: center;\"\u003e\nSSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use.\n\u003c/p\u003e\n\n\u003e **dartssh2** is now a complete rewrite of [dartssh].\n\n## ✨ Features\n\n-  **Pure Dart**: Working with both Dart VM and Flutter.\n-  **SSH Session**: Executing commands, spawning shells, setting environment variables, pseudo terminals, etc.\n-  **Authentication**: Supports password, private key and interactive authentication method.\n-  **Forwarding**: Supports local forwarding and remote forwarding.\n-  **SFTP**: Supports all operations defined in [SFTPv3 protocol](https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02) including upload, download, list, link, remove, rename, etc.\n\n## 🧬 Built with dartssh2\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003c!-- ServerBox --\u003e\n    \u003ctd style=\"text-align: center;\"\u003e\n      \u003cb\u003e\u003ca href=\"https://github.com/LollipopKit/flutter_server_box\"\u003eServerBox\u003c/a\u003e\u003c/b\u003e\n    \u003c/td\u003e\n    \u003c!-- NoPorts --\u003e\n    \u003ctd style=\"text-align: center;\"\u003e\n      \u003cb\u003e\u003ca href=\"https://github.com/atsign-foundation/noports\"\u003eNoPorts\u003c/a\u003e\u003c/b\u003e\n    \u003c/td\u003e\n    \u003c!-- dartShell --\u003e\n    \u003ctd style=\"text-align: center;\"\u003e\n      \u003cb\u003e\u003ca href=\"https://github.com/hsduren/dartshell\"\u003eDartShell\u003c/a\u003e\u003c/b\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e \n    \u003c!-- ServerBox --\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/TerminalStudio/dartssh2/master/media/showcase-1-serverbox.1.jpg\" width=\"150px\" alt=\"ServerBox interface displaying connection management options\"\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/TerminalStudio/dartssh2/master/media/showcase-1-serverbox.2.png\" width=\"150px\" alt=\"ServerBox user interface for server control and monitoring\"\u003e\n    \u003c/td\u003e\n    \u003c!-- NoPorts --\u003e\n    \u003ctd\u003e\n      \u003ca href=\"https://asciinema.org/a/496148\"\u003e\n        \u003cimg src=\"https://user-images.githubusercontent.com/6131216/185263634-07e8dba7-b5a8-44fc-ac44-8703e247143f.png\" width=\"300px\" alt=\"NoPorts demo showcasing SSH connectivity without open ports\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n    \u003c!-- dartShell --\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://github.com/hsduren/dartshell/blob/main/info1.png\" width=\"300px\" alt=\"dartShell displaying terminal and session information for SSH operations\"\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\n\u003e Feel free to add your own app here by opening a pull request.\n\n\n\n## 🧪 Try\n\n```sh\n# Install the `dartssh` command.\ndart pub global activate dartssh2_cli\n\n# Then use `dartssh` as regular `ssh` command.\ndartssh user@example.com\n\n# Example: execute a command on remote host.\ndartssh user@example.com ls -al\n\n# Example: connect to a non-standard port.\ndartssh user@example.com:\u003cport\u003e\n\n# Transfer files via SFTP.\ndartsftp user@example.com\n```\n\n\u003e If the `dartssh` command can't be found after installation, you might need to [set up your path](https://dart.dev/tools/pub/cmd/pub-global#running-a-script-from-your-path).\n\n## 🚀 Quick start \n\n### Connect to a remote host\n```dart\nvoid main() async {\n  final client = SSHClient(\n    await SSHSocket.connect('localhost', 22),\n    username: '\u003cusername\u003e',\n    onPasswordRequest: () =\u003e '\u003cpassword\u003e',\n  );\n}\n```\n\n\u003e `SSHSocket` is an interface and it's possible to implement your own `SSHSocket` if you want to use a different underlying transport rather than standard TCP socket. For example WebSocket or Unix domain socket.\n\n### Spawn a shell on remote host\n\n```dart\nvoid main() async {\n  final shell = await client.shell();\n  stdout.addStream(shell.stdout); // listening for stdout\n  stderr.addStream(shell.stderr); // listening for stderr\n  stdin.cast\u003cUint8List\u003e().listen(shell.write); // writing to stdin\n\n  await shell.done; // wait for shell to exit\n  client.close();\n}\n```\n\n### Execute a command on remote host\n\n\n```dart\nvoid main() async {\n  final uptime = await client.run('uptime');\n  print(utf8.decode(uptime));\n}\n```\n\nIgnoring stderr:\n```dart\nvoid main() async {\n  final uptime = await client.run('uptime', stderr: false);\n  print(utf8.decode(uptime));\n}\n```\n\n\u003e `client.run()` is a convenience method that wraps `client.execute()` for running non-interactive commands.\n\n### Start a process on remote host\n```dart\nvoid main() async {\n  final session = await client.execute('cat \u003e file.txt');\n  await session.stdin.addStream(File('local_file.txt').openRead().cast());\n  await session.stdin.close(); // Close the sink to send EOF to the remote process.\n\n  await session.done; // Wait for session to exit to ensure all data is flushed to the remote process.\n  print(session.exitCode); // You can get the exit code after the session is done\n}\n```\n\n\u003e `session.write()` is a shorthand for `session.stdin.add()`. It's recommended to use `session.stdin.addStream()` instead of `session.write()` when you want to stream large amount of data to the remote process.\n\n**Killing a remote process by sending signal**\n\n```dart\nvoid main() async {\n  session.kill(SSHSignal.KILL);\n  await session.done;\n  print('exitCode: ${session.exitCode}'); // -\u003e exitCode: null\n  print('signal: ${session.exitSignal?.signalName}'); // -\u003e signal: KILL\n}\n```\n\nProcesses killed by signals do not have an exit code, instead they have an exit signal property.\n\n### Forward connections on local port 8080 to the server\n\n```dart\nvoid main() async {\n  final serverSocket = await ServerSocket.bind('localhost', 8080);\n  await for (final socket in serverSocket) {\n    final forward = await client.forwardLocal('httpbin.org', 80);\n    forward.stream.cast\u003cList\u003cint\u003e\u003e().pipe(socket);\n    socket.pipe(forward.sink);\n  }\n}\n```\n\n### Forward connections to port 2222 on the server to local port 22\n\n```dart\nvoid main() async {\n  final forward = await client.forwardRemote(port: 2222);\n\n  if (forward == null) {\n    print('Failed to forward remote port');\n    return;\n  }\n\n  await for (final connection in forward.connections) {\n    final socket = await Socket.connect('localhost', 22);\n    connection.stream.cast\u003cList\u003cint\u003e\u003e().pipe(socket);\n    socket.pipe(connection.sink);\n  }\n}\n```\n\n### Authenticate with public keys\n\n```dart\nvoid main() async {\n  final client = SSHClient(\n    socket,\n    username: '\u003cusername\u003e',\n    identities: [\n      // A single private key file may contain multiple keys.\n      ...SSHKeyPair.fromPem(await File('path/to/id_rsa').readAsString())\n    ],\n  );\n}\n```\n\n### Use encrypted PEM files\n```dart\nvoid main() async {\n  // Test whether the private key is encrypted.\n  final encrypted = SSHKeyPair.isEncrypted(await File('path/to/id_rsa').readAsString());\n  print(encrypted);\n\n// If the private key is encrypted, you need to provide the passphrase.\n  final keys = SSHKeyPair.fromPem('\u003cpem text\u003e', '\u003cpassphrase\u003e');\n  print(keys);\n}\n```\n\nDecrypt PEM file with [`compute`](https://api.flutter.dev/flutter/foundation/compute-constant.html) in Flutter\n\n```dart\nvoid main() async {\n  List\u003cSSHKeyPair\u003e decryptKeyPairs(List\u003cString\u003e args) {\n    return SSHKeyPair.fromPem(args[0], args[1]);\n  }\n\n  final keypairs = await compute(decryptKeyPairs, ['\u003cpem text\u003e', '\u003cpassphrase\u003e']);\n}\n```\n\n### Get the version of SSH server\n\n```dart\nvoid main() async {\n  await client.authenticated;\n  print(client.remoteVersion); // SSH-2.0-OpenSSH_7.4p1\n}\n```\n\n### Connect through a jump server\n\n```dart\nvoid main() async {\n  final jumpServer = SSHClient(\n    await SSHSocket.connect('\u003cjump server\u003e', 22),\n    username: '...',\n    onPasswordRequest: () =\u003e '...',\n  );\n\n  final client = SSHClient(\n    await jumpServer.forwardLocal('\u003ctarget server\u003e', 22),\n    username: '...',\n    onPasswordRequest: () =\u003e '...',\n  );\n\n  print(utf8.decode(await client.run('hostname'))); // -\u003e hostname of  \u003ctarget server\u003e\n}\n```\n}\n\n\n## SFTP\n\n### List remote directory\n```dart\nvoid main() async {\n  final sftp = await client.sftp();\n  final items = await sftp.listdir('/');\n  for (final item in items) {\n    print(item.longname);\n  }\n}\n```\n\n### Read remote file\n```dart\nvoid main() async {\n  final sftp = await client.sftp();\n  final file = await sftp.open('/etc/passwd');\n  final content = await file.readBytes();\n  print(latin1.decode(content));\n}\n```\n\n### Write remote file\n```dart\nvoid main() async {\n  final sftp = await client.sftp();\n  final file = await sftp.open('file.txt', mode: SftpFileOpenMode.write);\n  await file.writeBytes(utf8.encode('hello there!') as Uint8List);\n}\n```\n\n**Write at specific offset**\n```dart\nvoid main() async {\n  final data = utf8.encode('world') as Uint8List;\n  await file.writeBytes(data, offset: 6);\n}\n```\n\n### File upload\n```dart\nvoid main() async {\n  final sftp = await client.sftp();\n  final file = await sftp.open('file.txt', mode: SftpFileOpenMode.create | SftpFileOpenMode.write);\n  await file.write(File('local_file.txt').openRead().cast());\n}\n```\n\n#### Pause and resume file upload\n```dart\nvoid main() async {\n  final uploader = await file.write(File('local_file.txt').openRead().cast());\n// ...\n  await uploader.pause();\n// ...\n  await uploader.resume();\n  await uploader.done;\n}\n```\n\n**Clear the remote file before opening it**\n\n```dart\nvoid main() async {\n  final file = await sftp.open('file.txt',\n      mode: SftpFileOpenMode.create | SftpFileOpenMode.truncate | SftpFileOpenMode.write\n  );\n}\n```\n\n### Directory operations\n```dart\nvoid main() async {\n  final sftp = await client.sftp();\n  await sftp.mkdir('/path/to/dir');\n  await sftp.rmdir('/path/to/dir');\n}\n```\n\n### Get/Set attributes from/to remote file/directory\n```dart\nvoid main() async {\n  await sftp.stat('/path/to/file');\n  await sftp.setStat(\n    '/path/to/file',\n    SftpFileAttrs(mode: SftpFileMode(userRead: true)),\n  );\n}\n```\n\n### Get the type of a remote file\n```dart\nvoid main() async {\n  final stat = await sftp.stat('/path/to/file');\n  print(stat.type);\n  // or\n  print(stat.isDirectory);\n  print(stat.isSocket);\n  print(stat.isSymbolicLink);\n  // ...\n}\n```\n\n### Create a link\n```dart\nvoid main() async {\n  final sftp = await client.sftp();\n  sftp.link('/from', '/to');\n}\n```\n\n### Get (estimated) total and free space on the remote filesystem\n```dart\nvoid main() async {\n  final sftp = await client.sftp();\n  final statvfs = await sftp.statvfs('/root');\n  print('total: ${statvfs.blockSize * statvfs.totalBlocks}');\n  print('free: ${statvfs.blockSize * statvfs.freeBlocks}');\n}\n```\n\n## 🪜 Example\n\n### SSH client:\n\n- [example/example.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/example.dart)\n- [example/execute.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/execute.dart)\n- [example/forward_local.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/forward_local.dart)\n- [example/forward_remote.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/forward_remote.dart)\n- [example/pubkey.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/pubkey.dart)\n- [example/shell.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/shell.dart)\n- [example/ssh_jump.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/ssh_jump.dart)\n\n### SFTP:\n- [example/sftp_read.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/sftp_read.dart)\n- [example/sftp_list.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/sftp_list.dart)\n- [example/sftp_stat.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/sftp_stat.dart)\n- [example/sftp_upload.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/sftp_upload.dart)\n- [example/sftp_filetype.dart](https://github.com/TerminalStudio/dartssh2/blob/master/example/sftp_filetype.dart)\n\n\n\n## 🔐 Supported algorithms\n\n**Host key**: \n- `ssh-rsa`\n- `rsa-sha2-[256|512]`\n- `ecdsa-sha2-nistp[256|384|521]`\n- `ssh-ed25519`\n\n**Key exchange**: \n- `curve25519-sha256`\n- `ecdh-sha2-nistp[256|384|521] `\n- `diffie-hellman-group-exchange-sha[1|256]`\n- `diffie-hellman-group14-sha[1|256]`\n- `diffie-hellman-group1-sha1 `\n  \n**Cipher**: \n- `aes[128|192|256]-ctr`\n- `aes[128|192|256]-cbc`\n\n**Integrity**: \n- `hmac-md5`\n- `hmac-sha1`\n- `hmac-sha2-[256|512]`\n\n**Private key**:\n\n| **Type**            | **Decode** | **Decrypt** | **Encode** | **Encrypt** |\n|---------------------|------------|-------------|------------|-------------|\n| **RSA**             | ✔️         | ✔️          | ✔️         | WIP         |\n| **OpenSSH RSA**     | ✔️         | ✔️          | ✔️         | WIP         |\n| **OpenSSH ECDSA**   | ✔️         | ✔️          | ✔️         | WIP         |\n| **OpenSSH Ed25519** | ✔️         | ✔️          | ✔️         | WIP         |\n\n\n  \n## ⏳ Roadmap\n\n- [x] Fix broken tests.\n- [x] Sound null safety.\n- [x] Redesign API to allow starting multiple sessions.\n- [x] Full SFTP.\n- [ ] Server.\n\n## References\n\n- [`RFC 4250`](https://datatracker.ietf.org/doc/html/rfc4250) The Secure Shell (SSH) Protocol Assigned Numbers.\n- [`RFC 4251`](https://datatracker.ietf.org/doc/html/rfc4251) The Secure Shell (SSH) Protocol Architecture.\n- [`RFC 4252`](https://datatracker.ietf.org/doc/html/rfc4252) The Secure Shell (SSH) Authentication Protocol.\n- [`RFC 4253`](https://datatracker.ietf.org/doc/html/rfc4253) The Secure Shell (SSH) Transport Layer Protocol.\n- [`RFC 4254`](https://datatracker.ietf.org/doc/html/rfc4254) The Secure Shell (SSH) Connection Protocol.\n- [`RFC 4255`](https://datatracker.ietf.org/doc/html/rfc4255) Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints.\n- [`RFC 4256`](https://datatracker.ietf.org/doc/html/rfc4256) Generic Message Exchange Authentication for the Secure Shell Protocol (SSH).\n- [`RFC 4419`](https://datatracker.ietf.org/doc/html/rfc4419) Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol.\n- [`RFC 4716`](https://datatracker.ietf.org/doc/html/rfc4716) The Secure Shell (SSH) Public Key File Format.\n- [`RFC 5656`](https://datatracker.ietf.org/doc/html/rfc5656) Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer.\n- [`RFC 8332`](https://datatracker.ietf.org/doc/html/rfc8332) Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol.\n- [`RFC 8731`](https://datatracker.ietf.org/doc/html/rfc8731) Secure Shell (SSH) Key Exchange Method Using Curve25519 and Curve448.\n- [`draft-miller-ssh-agent-03`](https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-03) SSH Agent Protocol.\n- [`draft-ietf-secsh-filexfer-02`](https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02) SSH File Transfer Protocol.\n- [`draft-dbider-sha2-mac-for-ssh-06`](https://datatracker.ietf.org/doc/html/draft-dbider-sha2-mac-for-ssh-06) SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol.\n\n## Credits\n\n- [https://github.com/GreenAppers/dartssh](https://github.com/GreenAppers/dartssh) by GreenAppers.\n\n## License\n\ndartssh is released under the terms of the MIT license. See [LICENSE](LICENSE).\n\n[dartssh]: https://github.com/GreenAppers/dartssh\n","funding_links":["https://ko-fi.com/F1F61K6BL"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fterminalstudio%2Fdartssh2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fterminalstudio%2Fdartssh2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fterminalstudio%2Fdartssh2/lists"}