MCPInstall

gooserelayvpn-android-client

Android VPN client that tunnels SOCKS5 traffic through Google Apps Script to a VPS exit server with AES-256-GCM encryption and domain fronting

Installs
213
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/reason-machines/devtools-skills --skill gooserelayvpn-android-client
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads gooserelayvpn-android-client automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

GooseRelayVPN Android Client Skill

Skill by ara.so — Devtools Skills collection.

Overview

GooseRelayVPN Android Client is an Android application that provides a VPN service tunneling TCP traffic through Google Apps Script to a VPS exit server. It uses:

  • Local SOCKS5 proxy for app/browser traffic
  • AES-256-GCM encryption for tunnel security
  • Domain fronting via Google infrastructure (Apps Script)
  • Android VpnService integration with tun2socks
  • Profile-based configuration with JSON import/export

Architecture flow:

  1. Android app traffic → SOCKS5 (127.0.0.1:1080)
  2. GooseRelay core encrypts with AES key
  3. HTTPS transport through Google Apps Script deployment
  4. VPS exit server decrypts and forwards to target

Prerequisites

Before using the Android client, you must set up upstream infrastructure:

  1. VPS server running goose-server (from main GooseRelayVPN project)
  2. Google Apps Script deployment with apps_script/Code.gs
  3. Tunnel encryption key generated via scripts/gen-key.sh
  4. Deployment ID(s) from Apps Script

Upstream project: https://github.com/kianmhz/GooseRelayVPN

Building the Android Client

Requirements

  • Android Studio
  • JDK 17
  • Go 1.22+
  • Android SDK with NDK
  • gomobile tool

Build Go Mobile AAR

The core GooseRelay logic is compiled to an Android AAR library:

# From project root
bash android/build_go_mobile.sh

This script:

  • Installs gomobile if needed
  • Compiles Go code to AAR for arm64-v8a, armeabi-v7a, x86, x86_64
  • Outputs to android/app/libs/gooserelay.aar

Manual AAR build:

go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init

gomobile bind \
  -target=android \
  -androidapi=21 \
  -o android/app/libs/gooserelay.aar \
  -v \
  ./mobile

Build Debug APK

cd android
./gradlew :app:assembleDebug
# Output: android/app/build/outputs/apk/debug/app-debug.apk

Build Release APK

cd android
./gradlew :app:assembleRelease
# Requires signing configuration in android/app/build.gradle

Profile Configuration

Profile JSON Structure

{
  "debug_timing": false,
  "socks_host": "127.0.0.1",
  "socks_port": 1080,
  "google_host": "216.239.38.120",
  "sni": [
    "www.google.com",
    "mail.google.com",
    "accounts.google.com"
  ],
  "script_keys": [
    "DEPLOYMENT_ID_FROM_APPS_SCRIPT",
    "OPTIONAL_SECOND_DEPLOYMENT_ID"
  ],
  "tunnel_key": "BASE64_ENCODED_AES_KEY_FROM_GEN_KEY_SCRIPT"
}

Field Descriptions

FieldTypeDescription
debug_timingboolEnable timing debug logs
socks_hoststringLocal SOCKS5 bind address (usually 127.0.0.1)
socks_portintLocal SOCKS5 port (default 1080)
google_hoststringGoogle IP for domain fronting (216.239.38.120 is common)
sni[]stringSNI hostnames for TLS handshake rotation
script_keys[]stringApps Script deployment IDs (one or more for redundancy)
tunnel_keystringBase64 AES-256 key (must match server-side key)

Generating Tunnel Key

Use the upstream script to generate a secure key:

# From GooseRelayVPN main repo
bash scripts/gen-key.sh
# Outputs base64-encoded key
# Example: a3d7f9e2b1c4...

Important: The same tunnel_key must be used on both the Android client and the VPS goose-server.

In-App Profile Management

Create new profile:

  1. Open app → Profiles tab
  2. Tap "+" button
  3. Enter profile name and configuration
  4. Save

Import profile from JSON:

  1. Profiles tab → menu → Import
  2. Select JSON file from storage
  3. Profile is added to list

Export profile to JSON:

  1. Profiles tab → long-press profile
  2. Select Export
  3. JSON saved to Downloads

Android VPN Service Integration

VpnService Implementation

The app uses Android's VpnService API to capture device traffic:

// Simplified example from Android codebase
class GooseVpnService : VpnService() {
    private val socksHost = "127.0.0.1"
    private val socksPort = 1080
    
    fun startVpn(profile: Profile) {
        // 1. Start GooseRelay core (via JNI to Go AAR)
        GooseRelay.start(profile.toJson())
        
        // 2. Configure VPN interface
        val builder = Builder()
            .setSession("GooseRelayVPN")
            .addAddress("10.0.0.2", 24)
            .addRoute("0.0.0.0", 0)
            .addDnsServer("8.8.8.8")
        
        // 3. Exclude apps if split tunneling enabled
        if (profile.splitTunnel) {
            profile.allowedApps.forEach { pkg ->
                builder.addAllowedApplication(pkg)
            }
        }
        
        val vpnInterface = builder.establish()
        
        // 4. Start tun2socks to forward traffic to SOCKS5
        Tun2Socks.start(
            vpnInterface.fd,
            socksHost,
            socksPort
        )
    }
}

Permissions Required

AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    
    <application>
        <service
            android:name=".service.GooseVpnService"
            android:permission="android.permission.BIND_VPN_SERVICE">
            <intent-filter>
                <action android:name="android.net.VpnService" />
            </intent-filter>
        </service>
    </application>
</manifest>

Go Mobile Bridge

The Go core is exposed to Android via gomobile:

Mobile Package Interface

mobile/mobile.go:

package mobile

import (
    "encoding/json"
    "github.com/Hidden-Node/GooseRelayVPN/client"
)

// Config matches Android profile JSON structure
type Config struct {
    DebugTiming bool     `json:"debug_timing"`
    SocksHost   string   `json:"socks_host"`
    SocksPort   int      `json:"socks_port"`
    GoogleHost  string   `json:"google_host"`
    SNI         []string `json:"sni"`
    ScriptKeys  []string `json:"script_keys"`
    TunnelKey   string   `json:"tunnel_key"`
}

var relayClient *client.Client

// Start initializes and starts the GooseRelay client
func Start(configJSON string) error {
    var cfg Config
    if err := json.Unmarshal([]byte(configJSON), &cfg); err != nil {
        return err
    }
    
    relayClient = client.NewClient(client.Config{
        DebugTiming: cfg.DebugTiming,
        SocksAddr:   fmt.Sprintf("%s:%d", cfg.SocksHost, cfg.SocksPort),
        GoogleHost:  cfg.GoogleHost,
        SNI:         cfg.SNI,
        ScriptKeys:  cfg.ScriptKeys,
        TunnelKey:   cfg.TunnelKey,
    })
    
    return relayClient.Start()
}

// Stop gracefully stops the relay client
func Stop() error {
    if relayClient != nil {
        return relayClient.Stop()
    }
    return nil
}

// GetStats returns JSON statistics
func GetStats() string {
    if relayClient == nil {
        return "{}"
    }
    stats := relayClient.GetStats()
    data, _ := json.Marshal(stats)
    return string(data)
}

Calling from Android (Kotlin)

import gooserelay.Gooserelay // Generated from AAR

class RelayManager {
    fun startRelay(profile: Profile) {
        val configJson = profile.toJson()
        try {
            Gooserelay.start(configJson)
            Log.i("GooseRelay", "Started successfully")
        } catch (e: Exception) {
            Log.e("GooseRelay", "Start failed: ${e.message}")
        }
    }
    
    fun stopRelay() {
        try {
            Gooserelay.stop()
        } catch (e: Exception) {
            Log.e("GooseRelay", "Stop failed: ${e.message}")
        }
    }
    
    fun getStats(): Stats? {
        return try {
            val json = Gooserelay.getStats()
            Json.decodeFromString<Stats>(json)
        } catch (e: Exception) {
            null
        }
    }
}

Common Usage Patterns

Full Device VPN

Route all device traffic through the tunnel:

// In profile configuration
val profile = Profile(
    name = "Full VPN",
    socksPort = 1080,
    scriptKeys = listOf(System.getenv("APPS_SCRIPT_DEPLOYMENT_ID")),
    tunnelKey = System.getenv("GOOSE_TUNNEL_KEY"),
    splitTunnel = false, // All apps
    excludeLocalNetwork = true
)

vpnService.startVpn(profile)

Split Tunneling

Route only specific apps through the tunnel:

val profile = Profile(
    name = "Split Tunnel",
    socksPort = 1080,
    scriptKeys = listOf(System.getenv("APPS_SCRIPT_DEPLOYMENT_ID")),
    tunnelKey = System.getenv("GOOSE_TUNNEL_KEY"),
    splitTunnel = true,
    allowedApps = listOf(
        "com.android.chrome",
        "org.telegram.messenger"
    )
)

vpnService.startVpn(profile)

Manual SOCKS5 Proxy

Use without VpnService (manual app configuration):

// Start only the SOCKS5 server, no VPN
fun startSocksOnly(profile: Profile) {
    GooseRelay.start(profile.toJson())
    // Apps must be configured to use 127.0.0.1:1080 as SOCKS5 proxy
}

Logging and Telemetry

Real-time Logs

The app provides a Logs tab showing Android and Go core logs:

// Android side logger forwarding to UI
object LogCollector {
    private val logs = mutableListOf<LogEntry>()
    
    fun addLog(level: String, tag: String, message: String) {
        logs.add(LogEntry(
            timestamp = System.currentTimeMillis(),
            level = level,
            tag = tag,
            message = message
        ))
        // Notify UI observers
        notifyLogListeners()
    }
}

// Go side: logs are captured via custom writer

Telemetry Stats

Real-time connection statistics:

data class TelemetryStats(
    val bytesUploaded: Long,
    val bytesDownloaded: Long,
    val activeConnections: Int,
    val successRate: Float,
    val avgLatency: Long
)

fun updateTelemetry() {
    val statsJson = Gooserelay.getStats()
    val stats = Json.decodeFromString<TelemetryStats>(statsJson)
    
    // Update UI cards
    uploadCard.text = formatBytes(stats.bytesUploaded)
    downloadCard.text = formatBytes(stats.bytesDownloaded)
    latencyCard.text = "${stats.avgLatency}ms"
}

CI/CD and Release

GitHub Actions Workflows

Debug CI (.github/workflows/android-ci.yml):

name: Android CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.22'
      
      - name: Build Go Mobile AAR
        run: bash android/build_go_mobile.sh
      
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      
      - name: Build Debug APK
        run: |
          cd android
          ./gradlew assembleDebug
      
      - name: Upload APK
        uses: actions/upload-artifact@v3
        with:
          name: app-debug
          path: android/app/build/outputs/apk/debug/app-debug.apk

Release Workflow (.github/workflows/release.yml):

name: Release Build

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build AAR
        run: bash android/build_go_mobile.sh
      
      - name: Decode Keystore
        run: |
          echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > android/keystore.jks
      
      - name: Build Release APK
        env:
          KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
          KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
          KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
        run: |
          cd android
          ./gradlew assembleRelease
      
      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          files: android/app/build/outputs/apk/release/app-release.apk

Required secrets:

  • ANDROID_KEYSTORE_BASE64: Base64-encoded keystore file
  • ANDROID_KEYSTORE_PASSWORD: Keystore password
  • ANDROID_KEY_ALIAS: Key alias in keystore
  • ANDROID_KEY_PASSWORD: Key password

Troubleshooting

Connection Stuck in "Preparing" State

Symptoms: VPN shows "Preparing" but never connects.

Causes:

  1. Invalid script_keys (deployment IDs)
  2. Mismatched tunnel_key between client and server
  3. Apps Script deployment not accessible

Solution:

# Check logs tab in app for specific errors
# Common log patterns:
# "Failed to connect to script" → Check deployment ID
# "Decryption failed" → Check tunnel_key matches server
# "Connection timeout" → Verify VPS server is running

# Verify server-side key matches:
# On VPS: cat /etc/goose-server/config.json | jq .tunnel_key
# In Android profile: check tunnel_key field

SOCKS Port Busy Error

Symptoms: "bind: address already in use" in logs.

Solution:

// Ensure clean disconnect before reconnect
fun reconnect() {
    // Stop VPN service
    vpnService.stop()
    
    // Wait for port release
    Thread.sleep(2000)
    
    // Check no other app uses port 1080
    // Change profile socks_port if needed
    profile.socksPort = 1081
    
    vpnService.start()
}

No Traffic Flowing Through VPN

Symptoms: VPN connected but no internet access.

Checklist:

  1. VPN permission granted
  2. Split tunnel app selection correct
  3. DNS servers configured
  4. Local network excluded if needed

Solution:

// Verify VPN builder configuration
val builder = Builder()
    .addAddress("10.0.0.2", 24)
    .addRoute("0.0.0.0", 0)
    .addDnsServer("8.8.8.8")
    .addDnsServer("1.1.1.1")

// Exclude local network
if (profile.excludeLocalNetwork) {
    builder.addRoute("0.0.0.0", 5)
    builder.addRoute("8.0.0.0", 7)
    builder.addRoute("11.0.0.0", 8)
    // ... exclude 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
}

val vpnInterface = builder.establish()

Apps Script Deployment Issues

Symptoms: "Script execution failed" in logs.

Solution:

# Verify deployment ID format (should be long alphanumeric string)
# Example: AKfycbzXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Test deployment manually:
curl -L "https://script.google.com/macros/s/DEPLOYMENT_ID/exec?action=ping"

# Ensure Apps Script has correct VPS endpoint in Code.gs:
# const VPS_ENDPOINT = "https://your-vps-ip:8443";

High Latency or Slow Speeds

Symptoms: Connected but slow performance.

Optimization:

{
  "debug_timing": false,
  "sni": [
    "www.google.com"
  ],
  "script_keys": [
    "PRIMARY_DEPLOYMENT_ID",
    "BACKUP_DEPLOYMENT_ID"
  ]
}

Tips:

  • Use multiple script_keys for load balancing
  • Choose closer Google IP in google_host
  • Optimize VPS server configuration
  • Enable debug_timing temporarily to identify bottlenecks

AAR Build Failures

Symptoms: build_go_mobile.sh fails.

Solution:

# Ensure gomobile is installed
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init

# Check Android NDK is installed
# In Android Studio: Tools → SDK Manager → SDK Tools → NDK

# Set environment variables
export ANDROID_HOME=$HOME/Android/Sdk
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/25.1.8937393

# Retry build
bash android/build_go_mobile.sh

Certificate/TLS Errors

Symptoms: "x509: certificate signed by unknown authority"

Solution:

// If using self-signed cert on VPS, Apps Script must trust it
// Better: Use Let's Encrypt on VPS

// In Code.gs, configure:
const ALLOW_SELF_SIGNED = false; // Set true only for testing

Best Practices

  1. Key Management: Store tunnel_key in Android Keystore, not plaintext
  2. Multiple Deployments: Use 2-3 script_keys for redundancy
  3. Battery Optimization: Exclude VPN service from battery optimization
  4. Testing: Test with debug_timing: true during setup
  5. Backup Profiles: Export profiles before app updates
  6. Server Monitoring: Monitor VPS goose-server logs alongside Android logs

Additional Resources

FAQ

What does the gooserelayvpn-android-client skill do?

Android VPN client that tunnels SOCKS5 traffic through Google Apps Script to a VPS exit server with AES-256-GCM encryption and domain fronting

How do I install gooserelayvpn-android-client?

Run npx skills add https://github.com/reason-machines/devtools-skills --skill gooserelayvpn-android-client in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/gooserelayvpn-android-client/ (personal) or .claude/skills/gooserelayvpn-android-client/ (project).

Which AI agents can use gooserelayvpn-android-client?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is gooserelayvpn-android-client safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains gooserelayvpn-android-client?

It is published by reason-machines in reason-machines/devtools-skills. It has 213 recorded installs.

More from reason-machines/devtools-skills

View all →

cc-switch-cli

reason-machines/devtools-skills

Cross-platform CLI tool for managing Claude Code, Codex, Gemini, OpenCode & OpenClaw providers, MCP servers, prompts, skills, and proxies.

AI & Agents316 installs

wx-cli-wechat-local-data

reason-machines/devtools-skills

Open the skill to read its SKILL.md instructions.

284 installs

chrome-devtools-mcp-automation

reason-machines/devtools-skills

Use Chrome DevTools MCP to control Chrome, debug web apps, analyze performance, and automate browser tasks via MCP tools

AI & Agents265 installs

officecli-office-automation

reason-machines/devtools-skills

Use OfficeCLI to create, read, and edit Word, Excel, and PowerPoint files from the command line with AI-friendly commands.

cli-anything-agent-native-software

reason-machines/devtools-skills

Generate and use command-line interfaces that make any software controllable by AI agents with structured JSON output

AI & Agents258 installs

autocli-web-scraping

reason-machines/devtools-skills

Open the skill to read its SKILL.md instructions.

Popular mobile skills

View category →

design-mobile-apps

designed-by-ai/skills

Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.

Mobile1.1M installs

ios-design

uizze.sh

Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.

Mobile416.5K installs

vercel-react-native-skills

vercel-labs/agent-skills

React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.

OfficialMobile256.7K installs

animate-expo

emilkowalski/skills

Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.

Mobile140.1K installs

xcode-project-setup

firebase/agent-skills

Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).

OfficialMobile131.2K installs

firebase-remote-config-basics

firebase/agent-skills

Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.

OfficialMobile121.7K installs