Thank you for your interest in contributing to SSH Tunnel Proxy! This document provides guidelines and setup instructions for contributors.
- Development Environment Setup
- Project Structure
- Building the Project
- Running Tests
- Code Style
- Submitting Changes
- Reporting Issues
Before you begin, ensure you have the following installed:
-
Java Development Kit (JDK) 17 or higher
-
Android Studio (Latest stable version recommended)
- Download from developer.android.com
- Includes Android SDK and required build tools
- Alternative: IntelliJ IDEA Ultimate with Android plugin
-
Android SDK
- Minimum API Level: 26 (Android 8.0)
- Target API Level: 34 (Android 14)
- Install via Android Studio SDK Manager:
- Android SDK Platform 34
- Android SDK Build-Tools 34.0.0
- Android SDK Platform-Tools
- Android Emulator (for testing)
-
Git
- Download from git-scm.com
- Verify installation:
git --version
-
Android Device or Emulator
- Physical device with USB debugging enabled (preferred for VPN testing)
- Or Android Emulator (API 26+) via Android Studio
-
SSH Server for Testing
- Local SSH server or cloud instance
- Must support SOCKS5 dynamic port forwarding
- OpenSSH recommended
git clone https://github.com/badscrew/selfproxy.git
cd selfproxyCreate or edit local.properties in the project root:
sdk.dir=/path/to/your/Android/SdkWindows example:
sdk.dir=C\:\\Users\\YourUsername\\AppData\\Local\\Android\\SdkmacOS/Linux example:
sdk.dir=/Users/YourUsername/Library/Android/sdkThe project uses Gradle wrapper, so no separate Gradle installation is needed.
Windows:
.\gradlew.bat --versionmacOS/Linux:
./gradlew --versionOpen the project in Android Studio:
- File → Open → Select the project directory
- Wait for Gradle sync to complete
- Resolve any SDK or dependency issues
-
Kotlin Plugin
- Should be installed by default
- Verify: Settings → Plugins → Kotlin
-
Code Style
- Settings → Editor → Code Style → Kotlin
- Set from: Kotlin style guide
- Or import
.editorconfigif provided
-
Enable Auto-Import
- Settings → Editor → General → Auto Import
- Check "Add unambiguous imports on the fly"
- Check "Optimize imports on the fly"
-
Increase Memory (Optional)
- Help → Edit Custom VM Options
- Add or modify:
-Xmx4096m -XX:MaxMetaspaceSize=512m
No special environment variables are required. The project uses standard Android/Kotlin tooling.
ssh-tunnel-proxy/
├── shared/ # Kotlin Multiplatform shared code
│ ├── src/
│ │ ├── commonMain/ # Platform-agnostic business logic
│ │ ├── commonTest/ # Shared unit tests
│ │ ├── androidMain/ # Android-specific implementations
│ │ └── androidUnitTest/ # Android-specific tests
│ └── build.gradle.kts
├── androidApp/ # Android application
│ ├── src/main/
│ │ ├── kotlin/ # Android UI (Jetpack Compose)
│ │ └── res/ # Android resources
│ └── build.gradle.kts
├── docs/ # Documentation
├── .kiro/ # Kiro IDE configuration
│ ├── specs/ # Feature specifications
│ └── steering/ # Development guidelines
├── build.gradle.kts # Root build configuration
├── settings.gradle.kts # Gradle settings
└── gradle.properties # Gradle properties
Build everything:
./gradlew buildBuild Android APK (Debug):
./gradlew androidApp:assembleDebugBuild Android APK (Release):
./gradlew androidApp:assembleReleaseInstall on connected device:
./gradlew androidApp:installDebugOr manually install:
adb install -r androidApp/build/outputs/apk/debug/androidApp-debug.apk- Debug APK:
androidApp/build/outputs/apk/debug/androidApp-debug.apk - Release APK:
androidApp/build/outputs/apk/release/androidApp-release.apk
File lock errors (Windows):
.\gradlew.bat --stop
taskkill /F /IM java.exe
.\gradlew.bat androidApp:assembleDebugClean build:
./gradlew clean buildRefresh dependencies:
./gradlew --refresh-dependenciesSee gradle-build-troubleshooting.md for more details.
Run all tests:
./gradlew testRun shared module tests:
./gradlew shared:testDebugUnitTestRun specific test class:
./gradlew shared:testDebugUnitTest --tests "com.sshtunnel.data.ServerProfilePropertiesTest"After running tests, view HTML reports:
- Shared module:
shared/build/reports/tests/testDebugUnitTest/index.html
This project uses Kotest for property-based testing. Tests use @Test annotations with checkAll:
@Test
fun `profile serialization round-trip should preserve data`() = runTest {
checkAll(100, Arb.serverProfile()) { profile ->
val json = Json.encodeToString(profile)
val deserialized = Json.decodeFromString<ServerProfile>(json)
deserialized shouldBe profile
}
}See testing-strategy.md for more details.
This project follows the official Kotlin coding conventions.
Key points:
- Use 4 spaces for indentation
- Use camelCase for functions and variables
- Use PascalCase for classes
- Maximum line length: 120 characters
- Use meaningful names
Check code style:
./gradlew ktlintCheckAuto-format code:
./gradlew ktlintFormatFollow Conventional Commits:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changestest: Adding or updating testsrefactor: Code refactoringchore: Build/tooling changes
Example:
feat(vpn): add UDP ASSOCIATE support for video calling
Implemented SOCKS5 UDP ASSOCIATE to enable video calling apps
like WhatsApp and Zoom to work through the tunnel.
Closes #42
See git-practices.md for more details.
-
Fork the repository
- Click "Fork" on GitHub
-
Create a feature branch
git checkout -b feature/your-feature-name
-
Make your changes
- Write code
- Add tests
- Update documentation
-
Test your changes
./gradlew test ./gradlew androidApp:assembleDebug -
Commit your changes
git add . git commit -m "feat: add amazing feature"
-
Push to your fork
git push origin feature/your-feature-name
-
Create a Pull Request
- Go to the original repository on GitHub
- Click "New Pull Request"
- Select your fork and branch
- Fill out the PR template
Before submitting:
- Code follows project style guidelines
- All tests pass locally
- New tests added for new features
- Documentation updated if needed
- Commit messages follow conventional format
- No merge conflicts with main branch
PR Description should include:
- Clear description of changes
- Related issue numbers (if applicable)
- Screenshots for UI changes
- Testing steps performed
- Maintainers will review your PR
- Address any feedback or requested changes
- Once approved, your PR will be merged
- Your contribution will be credited in release notes
When reporting bugs, please include:
-
Environment:
- Android version
- Device model
- App version
-
Steps to reproduce:
- Detailed steps to trigger the bug
- Expected behavior
- Actual behavior
-
Logs:
- Relevant logcat output
- Error messages
- Screenshots if applicable
-
Additional context:
- SSH server type (OpenSSH, etc.)
- Network conditions
- Any workarounds found
When requesting features:
- Use case: Describe the problem you're trying to solve
- Proposed solution: How you envision the feature working
- Alternatives: Other solutions you've considered
- Additional context: Why this would be valuable
- Shared module (commonMain): Platform-agnostic business logic
- Platform modules (androidMain): Android-specific implementations
- UI layer (androidApp): Jetpack Compose UI
See kotlin-multiplatform-architecture.md for details.
- Never commit credentials or API keys
- Use Android Keystore for sensitive data
- Follow security best practices in ssh-tunnel-security.md
- Write unit tests for business logic
- Use property-based tests for universal properties
- Test Android-specific code with instrumented tests
- See testing-strategy.md
- Documentation: Check the docs/ directory
- Issues: Search existing issues on GitHub
- Discussions: Start a discussion on GitHub
- Code: Review existing code for examples
By contributing to this project, you agree that your contributions will be licensed under the Apache License 2.0.
Parts of this project were developed using agentic coding with AI assistance. We welcome contributions from both human developers and AI-assisted development workflows.
Thank you for contributing to SSH Tunnel Proxy! 🚀