This is a React Native library that provides SSH and SFTP client functionality for iOS and Android. The library wraps native SSH implementations:
- iOS: NMSSH library (jim-lake's fork)
- Android: JSch library (Matthias Wiedemann fork)
react-native-ssh-sftp/
├── src/ # TypeScript source code
│ └── sshclient.ts # Main API implementation
├── ios/ # iOS native implementation
│ ├── RNSSHClient.m # React Native bridge
│ ├── RNSSHClient.h # Header file
│ ├── SSHClient.m # Native SSH client wrapper
│ └── SSHClient.h # Header file
├── android/ # Android native implementation
├── example/ # Example React Native app
│ ├── App.tsx # Main example app
│ ├── tests/ # Test infrastructure
│ │ ├── e2e/ # End-to-end tests using Detox
│ │ ├── ssh-keys/ # Test SSH keys
│ │ ├── test-data/ # Test files for SFTP
│ │ ├── docker-compose.yml # SSH test server
│ │ └── start-test-server.sh # Test server setup
│ └── package.json # Example app dependencies
├── dist/ # Compiled TypeScript output
└── package.json # Main library package
- Node.js >= 20
- React Native development environment
- For iOS: Xcode, iOS Simulator,
applesimutils - For Android: Android Studio, Android SDK, AVD named
Pixel_3a_API_36_ARM
# Install dependencies
npm install
# Compile TypeScript
npm run compile
# Lint code
npm run lintcd example
# Install dependencies
npm install
# Run on iOS
npm run ios
# Run on Android
npm run android
# Start Metro bundler
npm startThe project uses a Docker-based SSH server for testing:
cd example/tests
# Start SSH test server
./start-test-server.sh
# Stop SSH test server
./stop-test-server.shTest Server Details:
- Host:
127.0.0.1:2222 - Username:
user - Password:
password - SSH keys: Located in
tests/ssh-keys/ - Test data: Located in
tests/test-data/
Location: example/android/app/src/androidTest/java/com/sshsftpexample/
Test Files:
SSHClientDirectTest.java- Direct JSch library unit tests (5 tests)DetoxTest.java- UI instrumentation test (1 test)
Unit Test Coverage (SSHClientDirectTest.java):
testAuthenticateWithPasswordSuccess()- Tests successful password authenticationtestAuthenticateWithRSAKeySuccess()- Tests successful RSA key authenticationtestAuthenticateWithBadPassword()- Tests authentication failure with wrong passwordtestAuthenticateWithUnknownRSAKey()- Tests authentication failure with unauthorized key
Test Framework:
- JUnit 4: Test runner and assertions
- AndroidX Test: Android testing framework
- JSch: Direct SSH library testing (bypasses React Native bridge)
- UiAutomator: UI automation for instrumentation tests
How Unit Tests Work:
- Tests connect directly to Docker SSH server using JSch library
- Validates both successful and failed authentication scenarios
- Uses embedded RSA private keys and test credentials
- Provides detailed console output for debugging
- Tests run in Android instrumentation environment
Running Android Tests:
cd example
# Run Android tests with real-time logs
./run-android-tests-with-logs.shAndroid Test Dependencies (build.gradle):
androidTestImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'Test Output Location:
- Test reports:
android/app/build/reports/androidTests/connected/debug/index.html - Console logs: Real-time via
run-android-tests-with-logs.sh
Prerequisites for E2E Tests:
- SSH test server must be running (
./start-test-server.sh) - iOS Simulator or Android Emulator must be available
- For iOS: Detox must be properly configured
- For Android: Android instrumentation testing framework
Running E2E Tests:
cd example
# iOS E2E Tests (Detox)
npm run e2e:build:ios # Build app for testing
npm run e2e:test:ios # Run tests
# Android E2E Tests (Android Instrumentation)
./run-android-tests-with-logs.shTest Results (Current Status):
- iOS E2E Tests: ✅ ALL 18 TESTS PASSING (158s total runtime)
auth-methods.test.js: ✅ 12 tests passing (98s)simple-launch.test.js: ✅ 1 test passing (9s)negative-auth.test.js: ✅ 2 tests passing (22s)app.test.js: ✅ 1 test passing (19s)basic-app.test.js: ✅ 2 tests passing (10s)
- Android Tests: ✅ ALL TESTS PASSING
- Unit Tests:
SSHClientDirectTest.java- Direct JSch library testing (5 tests)- Password authentication success ✅
- RSA key authentication success ✅
- Bad password authentication failure ✅
- Unknown RSA key authentication failure ✅
- UI Test:
DetoxTest.java- Tests app launch and RSA key authentication via UI ✅ - Test Framework: Android instrumentation testing with UiAutomator and JUnit
- Test Coverage: Full authentication workflow testing plus UI interaction
- Unit Tests:
The library provides a modern Promise-based API with separate connection and authentication:
// 1. Connect to host
const client = await SSHClient.connect("host", 22, "username");
// 2. Authenticate (multiple methods available)
await client.authenticateWithPassword("password");
await client.authenticateWithKey(privateKey, passphrase?);
await client.authenticateWithSignCallback(publicKey, signCallback);
// 3. Check authentication
if (client.isAuthenticated()) {
// Perform operations
}// Combined connection + authentication (deprecated)
const client = await SSHClient.connectWithPassword(host, port, username, password);
const client = await SSHClient.connectWithKey(host, port, username, privateKey, passphrase?);-
Password Authentication (
Dbutton)- Connects to Docker SSH server with username/password
- Test Status: ✅ PASSING (7.3s)
-
RSA Key Authentication (
Rbutton)- Uses embedded RSA private key
- Test Status: ✅ PASSING (4.8s)
-
OpenSSH Key Authentication (
Obutton)- Uses OpenSSH format private key
- Test Status: ✅ PASSING (4.8s)
-
Encrypted RSA Key Authentication (
Ebutton)- Uses password-protected RSA key with passphrase
- Test Status: ✅ PASSING (3.6s)
-
SFTP Operations (
Fbutton)- Connects via password, then establishes SFTP
- Lists directory contents
- Test Status: ✅ PASSING (5.8s)
-
Sign Callback Authentication (
Cbutton)- Uses public key with callback-based signing
- Mock implementation for testing
- Test Status: ✅ PASSING (6.8s)
-
Basic SSH Test (
Sbutton)- Tests connection to non-existent local SSH server
- Expected result: "Native call successful (connection failed as expected)"
- Test Status: ✅ PASSING (3.6s)
- Location:
src/sshclient.ts - Purpose: Main API interface, handles Promise wrapping of native calls
- Key Methods:
connect()- Establish connectionauthenticateWithPassword()- Password authauthenticateWithKey()- Key-based authauthenticateWithSignCallback()- Callback-based signingexecute()- Run SSH commandsconnectSFTP()- Establish SFTP sessionsftpLs(),sftpUpload(),sftpDownload()- SFTP operations
- Location:
ios/RNSSHClient.m - Purpose: React Native bridge to native NMSSH library
- Key Features:
- Connection pooling with unique keys
- Event emission for shell, progress, sign callbacks
- Async operation handling with dispatch queues
- Sign callback implementation with semaphores
- Location:
example/App.tsx - Purpose: Demonstrates all authentication methods
- UI: Compact interface with single-letter buttons (S,D,R,O,E,F,C)
- Status Display: Shows current operation status
- SSH Test Server Running:
./start-test-server.shmust be executed - Network Connectivity: Local Docker container must be accessible on port 2222
- SSH Keys Present: All test keys must exist in
tests/ssh-keys/ - Test Data Available: SFTP test files must exist in
tests/test-data/ - Device/Simulator Ready: iOS Simulator or Android Emulator running
- App Built:
npm run e2e:build:iosornpm run e2e:build:androidcompleted
- ScrollView Rendering Problem: RESOLVED - Root cause was compiled JavaScript files (App.js, App.js.map, App.d.ts) overriding TypeScript source code
- Button Accessibility: RESOLVED - Implemented proper ScrollView layout with 15px padding and 16px font size for better test accessibility
- Comprehensive Testing: All 7 authentication methods now pass E2E tests through actual button interactions on iOS
- Android Testing Strategy: RESOLVED - Implemented Android instrumentation testing with UiAutomator and direct JSch unit tests instead of Detox
- Android Test Execution: Use
./run-android-tests-with-logs.shfor real-time test output and automatic SSH server management - Critical Fix: Always remove compiled JS files when making TypeScript changes to prevent source override issues
- Test Strategy: Functionality testing with proper alert handling and afterEach cleanup ensures robust sequential test execution
- TypeScript Interface: Add method to
SSHClientclass insrc/sshclient.ts - Native Implementation: Implement in
ios/RNSSHClient.mand Android equivalent - Example Usage: Add button and handler in
example/App.tsx - Tests: Add test case in
example/tests/e2e/auth-methods.test.js
- API Changes: Update TypeScript definitions and implementation
- Native Changes: Modify bridge methods in native code
- Example Updates: Update example app to demonstrate changes
- Test Updates: Modify or add tests to cover new functionality
- TypeScript: Add method to SSHClient class
- Native Bridge: Implement RCT_EXPORT_METHOD in native code
- Testing: Add test cases for new operations
npm run compile # TypeScript compilation to dist/# iOS
cd example
npm run bundle # Create React Native bundle
npm run e2e:build:ios # Build iOS app for testing
# Android
# Use ./run-android-tests-with-logs.sh which handles building automatically- TypeScript: For type-safe development
- React Native: Peer dependency
- ESLint: Code linting
- cross-env: Cross-platform environment variables
- React Native 0.81.1: Framework version
- Detox: E2E testing framework
- Jest: Test runner
- Docker: For SSH test server
- iOS: NMSSH (jim-lake's fork) via CocoaPods
- Android: JSch library
- Always run tests: Ensure E2E tests pass after modifications
- Update documentation: Keep README.md and this file current
- Test all auth methods: Verify all 7 authentication methods work
- Check both platforms: Test on both iOS and Android
- Maintain backward compatibility: Don't break existing API usage
- Remove compiled files: CRITICAL - Delete App.js, App.js.map, App.d.ts when modifying TypeScript to prevent override issues
- Compiled File Override: CRITICAL - Remove compiled JavaScript files (App.js, App.js.map, App.d.ts) when making TypeScript changes to prevent source override
- UI Rendering Issues: Be aware of React Native rendering problems with complex layouts
- Async Timing: SSH operations are async, ensure proper Promise handling
- Native Bridge: Changes to native code require rebuilding the app
- Test Server: E2E tests require the Docker SSH server to be running
- Key Management: SSH keys must be properly formatted and accessible
This documentation provides the foundation for understanding and modifying the React Native SSH SFTP library effectively.