A WebAssembly port of libssh2 that enables SSH client functionality in JavaScript environments, including browsers and Node.js.
libssh2.js provides a complete SSH client implementation compiled to WebAssembly using Emscripten. It supports:
- SSH Connections: Establish SSH connections to remote servers
- Authentication: Password and public key authentication
- Terminal Sessions: Interactive shell sessions with PTY support
- Custom Transport: WebSocket bridge support for browser environments
- Cross-Platform: Works in browsers, Node.js, and other JavaScript runtimes
- 🔐 SSH Protocol Support: Full SSH2 protocol implementation
- 🌐 WebAssembly: Native performance in JavaScript environments
- 🔌 Custom Transport: WebSocket bridge for browser compatibility
- 🖥️ Terminal Support: Interactive shell sessions with PTY
- 🔑 Multiple Auth Methods: Password and public key authentication
- 📱 Browser Compatible: Works in modern browsers via WebSocket
- 🚀 Node.js Ready: Native performance in Node.js environments
- 📝 TypeScript Support: Full type definitions included
- Docker: For building the WebAssembly module
- Node.js: Version 22.14.0 or later
- Modern Browser: For browser-based usage (Chrome, Firefox, Safari, Edge)
The project includes a Docker-based build system that compiles libssh2 and OpenSSL to WebAssembly.
# Build the WebAssembly module
docker build -t libssh2-wasm .
# Extract the built files
docker create --name temp-container libssh2-wasm
docker cp temp-container:/workspace/libssh2.js ./
docker cp temp-container:/workspace/libssh2.wasm ./
docker rm temp-container
The build process:
- Sets up Ubuntu 22.04 with Node.js 22.14.0
- Installs Emscripten SDK
- Compiles OpenSSL 1.1.1w for WebAssembly
- Compiles libssh2 with OpenSSL backend
- Compiles the custom bindings to WebAssembly
This package includes full TypeScript support with comprehensive type definitions. The types are automatically included when you install the package and will be resolved by TypeScript automatically.
The package includes a types
field in package.json
that points to index.d.ts
, ensuring TypeScript automatically finds the type definitions:
{
"types": "index.d.ts"
}
import SSH2Module from "@verdigris/libssh2.js";
async function sshExample() {
// Initialize the module
const SSH2 = await SSH2Module();
// Initialize libssh2
SSH2.ccall("ssh2_init", "number", [], []);
// Create session
const session = SSH2.ccall("ssh2_session_init_custom", "number", [], []);
// Set custom transport callbacks
SSH2.customSend = (socket, buffer, length) => {
// Implement your transport logic here
// For WebSocket: send data via WebSocket
return length;
};
SSH2.customRecv = (socket, buffer, length) => {
// Implement your transport logic here
// For WebSocket: receive data from WebSocket
return receivedLength;
};
// Perform handshake
const handshakeResult = SSH2.ccall(
"ssh2_session_handshake_custom",
"number",
["number"],
[session]
);
// Authenticate
const authResult = SSH2.ccall(
"ssh2_userauth_password_custom",
"number",
["number", "string", "string"],
[session, "username", "password"]
);
}
import SSH2Module from "./libssh2.js";
// Initialize the module
const SSH2 = await SSH2Module();
// Initialize libssh2
SSH2.ccall("ssh2_init", "number", [], []);
// Create session
const session = SSH2.ccall("ssh2_session_init_custom", "number", [], []);
// Set custom transport callbacks
SSH2.customSend = (socket, buffer, length) => {
// Implement your transport logic here
// For WebSocket: send data via WebSocket
return length;
};
SSH2.customRecv = (socket, buffer, length) => {
// Implement your transport logic here
// For WebSocket: receive data from WebSocket
return receivedLength;
};
// Perform handshake
const handshakeResult = SSH2.ccall(
"ssh2_session_handshake_custom",
"number",
["number"],
[session]
);
// Authenticate
const authResult = SSH2.ccall(
"ssh2_userauth_password_custom",
"number",
["number", "string", "string"],
[session, "username", "password"]
);
// Open channel
const channel = SSH2.ccall(
"ssh2_channel_open_session_custom",
"number",
["number"],
[session]
);
// Request PTY
SSH2.ccall(
"ssh2_channel_request_pty_custom",
"number",
["number", "string"],
[channel, "xterm"]
);
// Set PTY size
SSH2.ccall(
"ssh2_channel_request_pty_size",
"number",
["number", "number", "number"],
[channel, 80, 24]
);
// Start shell
SSH2.ccall("ssh2_channel_shell_custom", "number", ["number"], [channel]);
// Read from channel
const buffer = SSH2._malloc(1024);
const bytesRead = SSH2.ccall(
"ssh2_channel_read_custom",
"number",
["number", "number", "number"],
[channel, buffer, 1024]
);
// Write to channel
SSH2.ccall(
"ssh2_channel_write_custom",
"number",
["number", "string", "number"],
[channel, "ls -la\n", 8]
);
// Set up WebSocket connection
const ws = new WebSocket("ws://your-ssh-proxy-server");
// Custom transport implementation
SSH2.customSend = (socket, buffer, length) => {
const data = new Uint8Array(SSH2.HEAPU8.buffer, buffer, length);
ws.send(data);
return length;
};
SSH2.customRecv = (socket, buffer, length) => {
// Handle incoming WebSocket data
// Copy to the provided buffer
return receivedLength;
};
Function | Description |
---|---|
ssh2_init() |
Initialize libssh2 library |
ssh2_exit() |
Cleanup libssh2 library |
ssh2_version() |
Get libssh2 version string |
Function | Description |
---|---|
ssh2_session_init_custom() |
Create new SSH session with custom transport |
ssh2_session_handshake_custom() |
Perform SSH handshake |
ssh2_session_set_blocking() |
Set session blocking mode |
ssh2_session_free() |
Free session resources |
Function | Description |
---|---|
ssh2_userauth_password_custom() |
Authenticate with username/password |
ssh2_userauth_publickey_fromfile() |
Authenticate with public key |
Function | Description |
---|---|
ssh2_channel_open_session_custom() |
Open new session channel |
ssh2_channel_request_pty_custom() |
Request PTY allocation |
ssh2_channel_shell_custom() |
Start interactive shell |
ssh2_channel_read_custom() |
Read data from channel |
ssh2_channel_write_custom() |
Write data to channel |
ssh2_channel_close() |
Close channel |
ssh2_channel_free() |
Free channel resources |
If you encounter errors like:
There are types at '.../index.d.ts', but this result could not be resolved when respecting package.json "exports"
Solution: Make sure you're using a recent version of TypeScript (4.7+) and that your tsconfig.json
has modern module resolution:
{
"compilerOptions": {
"moduleResolution": "node",
"module": "ESNext",
"target": "ES2020"
}
}
The package uses ES modules. If you're using CommonJS, you may need to use dynamic imports:
// ES Module import (recommended)
import SSH2Module from "@verdigris/libssh2.js";
// CommonJS dynamic import
const SSH2Module = await import("@verdigris/libssh2.js");
The project consists of three main components:
- ssh2_bindings.c: C bindings that expose libssh2 functionality to JavaScript
- Dockerfile: Build environment for compiling to WebAssembly
- Generated Files:
libssh2.js
andlibssh2.wasm
(built output)
The bindings implement a custom transport layer that allows:
- WebSocket bridging for browser environments
- Custom I/O handling for different network protocols
- Integration with existing JavaScript networking code
- Chrome: 67+ (WebAssembly support)
- Firefox: 52+ (WebAssembly support)
- Safari: 11+ (WebAssembly support)
- Edge: 79+ (WebAssembly support)
Note: The package is specifically built for browser environments with -s ENVIRONMENT=web
to ensure compatibility with bundlers like Webpack, Vite, and Next.js.
- Node.js: 22.14.0+ (for building)
- Runtime: Any Node.js version with WebAssembly support
- Transport Security: Ensure your WebSocket or transport layer is secure (WSS, etc.)
- Key Management: Store private keys securely
- Authentication: Use strong passwords and key-based authentication when possible
- Network Security: SSH connections should be over secure networks
libssh2.js/
├── ssh2_bindings.c # C bindings for libssh2
├── Dockerfile # Build environment
├── README.md # This file
└── .circleci/ # CI configuration
- Clone the repository
- Ensure Docker is running
- Run the build command
- Extract the generated files
The ssh2_bindings.c
file can be modified to:
- Add new SSH functionality
- Customize transport behavior
- Expose additional libssh2 features
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
This project is based on libssh2, which is licensed under the BSD 3-Clause License.
- libssh2 - The original C library
- Emscripten - WebAssembly compilation toolchain
- OpenSSL - Cryptographic library
For issues and questions:
- Check the libssh2 documentation
- Review the Emscripten WebAssembly guide
- Open an issue in this repository
Note: This is a WebAssembly port of libssh2. For production use, ensure your transport layer (WebSocket, etc.) provides appropriate security measures.