Technical Documentation: OutlineFoundation/outline-apps
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`README.md`)
Outline Apps
Outline makes it easy for anyone to create a VPN server, allowing you to share access to the free and open internet with those in need. We have two core applications:
Outline Manager (/server_manager): A graphical user interface for managing Outline servers. It is available for Windows, macOS, and Linux. You can install the manager here. See server_manager/README.md for more information.
Outline Client (/client): A cross-platform proxy client for Windows, macOS, iOS, Android, and Linux. The Outline Client is designed for use with the server deployed with the Outline Manager, but it is also fully compatible with any Shadowsocks server. You can install the client here. See client/README.md for more information.
Community and Support
Interested in contributing to Outline? See our Contributing Guide for more information.
See AGENTS.md for AI agent and developer guidance.
You can also join the Outline Community by signing up for the IFF Mattermost!
For customer support and to contact us directly, go to https://support.getoutline.org.
Overview (`client/README.md`)
Outline Client
The Outline Client is a cross-platform VPN or proxy client for Windows, macOS, iOS, Android, and Debian-based Linux. The Outline Client is designed for use with the Outline Server software, but it is fully compatible with any Shadowsocks server.
The client's user interface is implemented in Polymer 2.0. Platform support is provided by Cordova and Electron, with additional native components in this repository.
Requirements for all builds
All builds require Node 22 (lts/hydrogen), and Go 1.22 installed in addition to other per-platform requirements.
๐ก NOTE: if you have
nvminstalled, runnvm useto switch to the correct node version!
After cloning this repo, install all node dependencies:
npm installBuilding the shared web app
Outline clients share the same web app across all platforms. This code is located in the web directory. If you are making changes to the shared web app and do not need to test platform-specific functionality, you can test in a desktop browser by running:
npm run action client/web/startThe latter command will open a browser instance running the app. Browser platform development will use fake servers to test successful and unsuccessful connections.
The app logic is located in web/app. UI components are located in web/ui_components. If you want to work specifically on an individual UI element, try the storybook!:
npm run action storybook[!NOTE]
Every script in this repository can be run withnpm run action-
for a CLI-like experience, add something likeshalias outline="npm run action"(you can call it whatever you like)
to your shell, then try
outline www/start!
Passing configuration flags to actions
Certain actions take configuration flags - but since we're running them through npm, you'll have to use the -- seperator to funnel them through to the underlying process. For example, to set up a MacOS project in release mode, you'd run:
SENTRY_DSN=<your sentry dsn> npm run action client/src/cordova/setup macos -- --buildMode=release --versionName=<your version name>Life of a Packet
How does the Outline Client work?
Accepting a server invite
Looking for instructions on how to accept a server invite?
Platform-specific development
Each platform is handled differently:
- Developing for Apple (MacOS and iOS)
- Developing for Android
- Developing for Electron (Windows and Linux)
Error reporting
To enable error reporting through Sentry for local builds, run:
export SENTRY_DSN=[Sentry development API key]
[platform-specific build command]Release builds on CI are configured with a production Sentry API key.
Overview (`server_manager/README.md`)
Outline Manager
Running
To run the Outline Manager Electron app:
npm run action server_manager/electron/start ${PLATFORM}To run the Outline Manager Electron app with a development build (code not minified):
BUILD_ENV=development npm run action server_manager/electron/start ${PLATFORM}Where ${PLATFORM} is one of linux, macos, windows.
Development Server
To run the Outline Manager as a web app on the browser and listen for changes:
npm run action server_manager/www/startUpdating Cloud Locations
When new cloud regions/zones appear (especially for GCP), update all of the
following so the location picker shows proper city and country data.
- Add geolocation constants in
server_manager/model/location.ts. - Map cloud region IDs to
GeoLocationinserver_manager/model/gcp.ts
(Zone.LOCATION_MAP). - Add localized city message keys:
- Source messages:
server_manager/messages/master_messages.jsonusinggeo_*keys. - Runtime English messages:
server_manager/messages/en.jsonandserver_manager/messages/en-GB.jsonusinggeo-*keys.
- Source messages:
- Keep all
geo_*/geo-*message keys alphabetically sorted. - Update tests:
server_manager/model/gcp.spec.tsregion coverage list and assertions.server_manager/www/location_formatting.spec.tsif formatting/sorting
behavior changes.
- Verify:
npx tsc -p server_manager --outDir output/build/js/server_manager --module commonjs
npx jasmine output/build/js/server_manager/model/gcp.spec.js output/build/js/server_manager/www/location_formatting.spec.js
npm run action server_manager/www/testNotes:
- Missing entries in
Zone.LOCATION_MAPcause unknown location cards (?and
raw zone IDs) in the picker. - Region discovery can be checked programmatically with:
gcloud compute regions list --format="value(name)"
gcloud compute zones list --format="value(name)"Debug an existing binary
You can run an existing binary in debug mode by setting OUTLINE_DEBUG=true.
This will enable the Developer menu on the application window.
Packaging
To build the app binary:
npm run action server_manager/electron/package ${PLATFORM} -- --buildMode=[debug,release]Where ${PLATFORM} is one of linux, macos, windows.
The per-platform standalone apps will be at output/build/server_manager/electron/static/dist.
- Windows: An
.exefile. Only generated if you have wine installed. - Linux: An
.AppImagefile. - macOS: A
.dmgand a.zipfile as required by auto update.
NOTE: If you are building for macOS, you may need to run
security unlock-keychain login.keychainso electron-builder has access to your certificates.
Testing
npm run action server_manager/testError reporting
To enable error reporting through Sentry for local builds, run:
export SENTRY_DSN=[Sentry DSN URL]
npm run action server_manager/electron/start ${PLATFORM}Overview (`client/capacitor/README.md`)
Capacitor client
This directory is the Capacitor-based client shell. These instructions cover the browser dev workflow (webpack bundle under client/capacitor/www/) and the native iOS and Android builds.
Run all npm run action commands from the repository root.
Requirements (browser)
Node.js 22 (see root
package.jsonengines)Install dependencies after clone:
shnpm ciIf you use
nvm, runnvm usein the repo root so the Node version matches.
Start (development server)
Runs a browser build once, then starts the webpack dev server (HMR):
npm run action client/capacitor/start- URL: http://localhost:8080 (server binds to
0.0.0.0:8080). - Live reload: edit sources under
client/web/and other bundled paths; the UI updates as you save.
Note: In pure browser mode, Capacitor native plugins are not available. The app uses a browser MethodChannel shim for a subset of behavior (for example parsing common static access keys). Use this for UI and web-layer development.
Build (browser)
The web build action accepts browser (default), ios, or android (see web_build.action.mjs); the native platforms use it as the first step of their full build.
Debug (default):
npm run action client/capacitor/web_buildRelease: pass --buildMode=release and a version, with SENTRY_DSN set in the environment. This builds the bundle with webpack in production mode:
SENTRY_DSN=<dsn> npm run action client/capacitor/web_build android -- --buildMode=release --versionName=<version>Output
Artifacts land in client/capacitor/www/, including for example:
index.html,bundle.jsenvironment.json(version and build numbers, and the Sentry DSN if set)- Copied assets:
messages/,assets/, etc. (seewebpack.config.js)
App icons and splash screens
client/capacitor/assets/ is the single source of truth for launcher icons and splash screens on Android and iOS. Everything under android/app/src/main/res/mipmap-*, the drawable-* splash variants, and the iOS asset catalog (ios/App/App/Assets.xcassets) is generated from it โ do not edit those files by hand.
The source files follow the @capacitor/assets input convention:
| File | Purpose |
|---|---|
assets/icon.png |
Base app icon (1024ร1024) |
assets/icon-only.png |
Icon without background (used where the platform composes its own shape) |
assets/icon-foreground.png |
Android adaptive icon foreground layer |
assets/icon-background.png |
Android adaptive icon background layer |
assets/splash.png |
Splash screen (light mode, 2732ร2732) |
assets/splash-dark.png |
Splash screen (dark mode) |
To regenerate the platform resources after changing any source image:
cd client/capacitor
npm run assets:generateThe script runs capacitor-assets generate once per platform rather than a single --android --ios invocation: combining the flags changes the generated Android PNGs (the platform runs are not isolated inside the tool), which would break reproducibility against the committed res/ files.
then re-add license headers to the regenerated adaptive-icon XMLs (from the repo root):
go tool task addlicenseThe web/browser build does not use this directory โ its favicon and images come from client/web/ (see webpack.config.js). If a PWA target is ever added, @capacitor/assets can generate its icons too (--pwa).
iOS (device or simulator)
Use this flow when you need the real Outline iOS plugin (VPN, native method channel, and so on). All npx cap commands below are run from client/capacitor (the directory that contains capacitor.config.json and ios/).
Requirements (iOS)
- A Mac with Xcode installed, including the Xcode command-line tools, and a recent iOS SDK.
- Native iOS dependencies are managed with Swift Package Manager (see
ios/App/CapApp-SPM/Package.swift, generated bycap sync); CocoaPods is not used. - A physical device with a valid provisioning profile/signing setup, or an iOS Simulator runtime installed via Xcode.
- Go on your
PATH(npm run capacitor:sync:beforerunsgo tool taskto build the tun2socks xcframework; seepackage.jsonin this directory).
Build the debug app (one command)
From the repository root:
npm run action client/capacitor/build iosThis runs the full build: the web bundle, cap sync ios (tun2socks + native sync), and an unsigned xcodebuild of ios/App/App.xcodeproj for a generic iOS device. It is what CI runs. Because it disables code signing, the resulting app cannot be installed on a device; to run the app, use the steps below instead.
Build the release
Releases are built and published by the scripts in outline-release, which supply the Sentry DSN and the development team and call this action:
npm run action client/capacitor/build ios -- --buildMode=release --versionName=<version>It reads SENTRY_DSN and DEVELOPMENT_TEAM from the environment, and needs that team's signing identity set up in Xcode. It runs xcodebuild archive with the given version and the build number (hours since the Unix epoch), and leaves the signed archive at output/client/ios/Outline.xcarchive.
Steps to build and start the app
Build the web bundle (
www/), from the repository root:shnpm run action client/capacitor/web_build iosGo to the Capacitor project directory:
shcd client/capacitorSync the iOS project (runs
capacitor:sync:before: Go tun2socks; then copies the web assets built in step 1 into the native app and refreshes the Swift package manifest for the plugins):shnpx cap sync iosInstall and launch on the default device or running simulator:
shnpx cap run ios
Optional Capacitor CLI commands (iOS)
Open in Xcode (inspect build settings, configure signing, run/debug from the IDE):
shcd client/capacitor npx cap open iosWeb-only iteration (faster when you did not change native code or
Info.plist): copy assets without refreshing native dependencies. Buildwww/first, then copy:shnpm run action client/capacitor/web_build ios cd client/capacitor npx cap copy iosUse
npx cap sync iosagain whenever you change native plugins, Swift package dependencies, orInfo.plistentries.Sanity check the iOS toolchain from Capacitor's point of view:
shcd client/capacitor npx cap doctor ios
Android (device or emulator)
Use this flow when you need the real Outline Android plugin (VPN, native method channel, and so on). All npx cap commands below are run from client/capacitor (the directory that contains capacitor.config.json and android/).
Requirements (Android)
- Android Studio with a recent Android SDK (match the versions in
client/capacitor/android/variables.gradleand root Gradle files). - JDK 21 โ the Android Gradle build compiles against Java 21 (see
client/capacitor/android/app/build.gradle). This differs from the Cordova Android client, which builds with JDK 17. - A physical device with USB debugging or an AVD emulator.
- Go on your
PATH(npm run capacitor:sync:beforerunsgo tool taskfor tun2socks and Android configure; seepackage.jsonin this directory).
Build the debug APK (one command)
From the repository root:
npm run action client/capacitor/build androidThis runs the full build: the web bundle, cap sync android (tun2socks + native sync), and gradlew assembleDebug. It is what CI runs. To also install and launch the app on a device, use the steps below instead.
Build the release
Releases are built and published by the scripts in outline-release, which supply the signing keystore and the Sentry DSN and call this action:
npm run action client/capacitor/build android -- --buildMode=release --versionName=<version>It reads SENTRY_DSN, ANDROID_KEY_STORE_CONTENTS (a base64-encoded PKCS#12 keystore whose key alias is privatekey), ANDROID_KEY_STORE_PASSWORD and JAVA_HOME (JDK 21) from the environment. It leaves the signed app-release.aab and universal.apk (built from the AAB with bundletool, and checked for 16 KB alignment) in client/capacitor/android/app/build/outputs/bundle/release/.
Steps to build and start the app
Build the web bundle (
www/), from the repository root:shnpm run action client/capacitor/web_build androidGo to the Capacitor project directory:
shcd client/capacitorSync the Android project (runs
capacitor:sync:before: Go tun2socks, Android configure; then copies the web assets built in step 1 into the native app and refreshes plugins):shnpx cap sync androidInstall and launch on the default device or running emulator:
shnpx cap run android
Optional Capacitor CLI commands (Android)
Open in Android Studio (inspect Gradle, run/debug from the IDE):
shcd client/capacitor npx cap open androidWeb-only iteration (faster when you did not change native code or
AndroidManifest.xml): copy assets without refreshing native Gradle dependencies. Buildwww/first, then copy :shcd client/capacitor npm run build:web npx cap copy androidUse
npx cap sync androidagain whenever you change native plugins, Gradle, or manifest entries.Sanity check the Android toolchain from Capacitorโs point of view:
shcd client/capacitor npx cap doctor android
Overview (`client/electron/README.md`)
Electron Development Instructions
Unlike the Android and Apple clients, the Windows and Linux clients use the Electron framework, rather than Cordova.
To build the Electron clients, run (it will also package an installer executable into output/client/electron/build):
npm run action client/electron/build [windows|linux] -- --arch=[x64|ia32|arm64]To run the Electron clients, run:
npm run action client/electron/start [windows|linux] -- --arch=[x64|ia32|arm64]Cross-Compiling
To build the app for a platform target on a different host target, you will need a cross-compiler. We use zig to cross-compile with cgo.
Install zig and make sure it's in the PATH.
You can download the binary tarball, or use a package manager, like Homebrew:
brew install zigOn an M-series ARM Macs it is necessary to install Rosetta to allow the chip to run x86_64 commands. (A code-signing step in our windows build uses an intel version of wine.)
softwareupdate --install-rosetta --agree-to-licenseRequirements
Windows
- Visual Studio 2022 or later
If you see an error about node-gyp not finding Visual Studio, try one of the following:
- (Recommended) Install the VSSetup PowerShell package using
Install-Module VSSetup -Scope CurrentUser - Change PowerShell LanguageMode using
$ExecutionContext.SessionState.LanguageMode="FullLanguage"
Linux
patchelf0.14+ is required for building the Debian package:- Debian/Ubuntu:
sudo apt install patchelf - macOS:
brew install patchelf - Windows: https://github.com/NixOS/patchelf/releases
- Debian/Ubuntu:
Release
To build the release version of Windows installer, you'll also need:
- Java 8+ Runtime. This is required for the cross-platform Windows executable signing tool Jsign. If you don't need to sign the executables, feel free to skip this.
Electron Builder
The Electron Builder config uses an asarUnpack entry. This entry
lists files for resources that must be extracted from the ASAR archive. This is necessary for files that the operating system needs to access directly, such as executable binaries that your application runs or other native modules.
To find where unpacked files are used, look for calls to unpackedAppPath() (example).
It's helpful to list the ASAR contents to understand what's in there and what patterns to use.
For the Linux client:
npx asar list 'output/client/electron/build/linux-unpacked/resources/app.asar'For the Windows client:
npx asar list 'output/client/electron/build/win-ia32-unpacked/resources/app.asar'Overview (`client/electron/windows/smartdnsblock/README.md`)
`smartdnsblock`
smartdnsblock.exe is a Windows only utility application.
How to Build
We recommend to use Visual Studio 2019+ for building. But if it is not applicable to do so (for example, when in a CI server), feel free to download the standalone build tools without Visual Studio.
The simplest way to build this application is by double clicking tools/smartdnsblock/smartdnsblock.sln which will open the Visual Studio GUI, and then:
- Note that there are two dropdown buttons in the toolbar area:
- For "Solution Configurations" dropdown, choose "Release"
- For "Solution Platforms" dropdown , choose "x86"
- Use menu item "Build โ Build Solution" to build the project
smartdnsblock.exewill be generated intools/smartdnsblock/binfolder
You can also build it through command line tools without launching Visual Studio:
- Launch "Developer PowerShell for VS 2019" from Start Menu
cd <working-dir>/tools/smartdnsblockmsbuild smartdnsblock.sln /p:Configuration=Release /p:Platform=x86smartdnsblock.exewill be generated intools/smartdnsblock/binfolder
Overview (`client/go/outline/configregistry/README.md`)
Outline Config Registry
Overview
The Outline Config framework is a composable and extensible system to specify network strategies:
- Composable because it lets strategies to be combined to build more advanced strategies.
For example, Shadowsocks-over-Websocket, or multi-hop. - Extensible because it's possible to register new strategies without having to change the
framework.
This framework underpins how the Outline client understands and establishes connections through various proxy protocols and combinations.
Configuration Format: YAML
The config framework is based on YAML. JSON is a strict subset of YAML, so JSON notation can be used
as well. YAML was chosen for several reasons that enhance readability and maintainability:
- Removes the need for quotes in field names and many string values.
- Allows the use of comments.
- Has anchors, which can reduce duplication.
We use the github.com/goccy/go-yaml library for parsing. This library was chosen after encountering issues with bugs in corner cases with the gopkg.in/yaml.v3 library.
Core concepts
A ParseFunc[T any] is a generic function that defines how to transform a configuration (typically a YAML config pre-parsed as a map, list or primitive type) into a specific Go type T.
type ParseFunc[T any] func(ctx context.Context, input ConfigNode) (OutputType, error)The Context was added to enable parse-time context and sharing between parsers. This is useful for
implementing features like named objects in the config that can be referred to by other parts of the configuration.
A TypeParser[T any] is a utility that manages the parsing of a specific type T which can have multiple implementations or "subtypes".
It allows for the registration of different subparsers (ParseFuncs), each associated with a unique subtype name.
When a TypeParser encounters a YAML map with a $type attribute, it uses the value of $type to look up the corresponding registered subparser and delegates the parsing of that map to it.
Example of $type usage in YAML:
endpoint:
$type: shadowsocks
address: "127.0.0.1:8080"
cipher: "chacha20-ietf-poly1305"
password: "your_password"Here, a TypeParser for an "endpoint" would see $type: shadowsocks and delegate to the "shadowsocks" subparser.
When you create a TypeParser, a fallback handler must be specified. If the input config is not
a map, or is a map without a $type attribute, the fallback handler is called. This is helpful
for handling cases like parsing strings and the empty value. For example, a stream endpoint
can be specified simply as a string address (e.g., "proxy.example.com:443"). This string would be parsed by the stream dialer TypeParser's fallback handler.
Application Concepts
The Outline config defines a few concepts that are closely tied to the client application.
In types.go, we define a few higher-level types for the Outline config.
Some of these are wrappers around concepts from the Outline SDK, augmented with additional behavior or metadata needed by the application. For instance, we might add information about the first hop of a strategy, indicating whether it's a direct connection or tunneled, and if tunneled, the tunnel address. The use of generics in these types helps minimize code duplication.
One of the central pieces is the TransportPair, an object used by the VPN code to create TCP and UDP tunnels. The NewDefaultTransportProvider function is responsible for creating the TypeParser for TransportPair. This is where different network strategies (like Shadowsocks, HTTP proxies, Websockets, etc.) are registered.
Example of registering subparsers in NewDefaultTransportProvider:
// Websocket support.
streamEndpoints.RegisterSubParser("websocket", NewWebsocketStreamEndpointSubParser(streamEndpoints.Parse))
packetEndpoints.RegisterSubParser("websocket", NewWebsocketPacketEndpointSubParser(streamEndpoints.Parse))Notice how the dependencies between the subparsers and the parsers they depend on are explicit
and will cause compile-time errors if not provided.
Adding a New Strategy or Dialer
To introduce a new network capability (e.g., custom proxy protocol, dialing strategy), follow these steps:
Create the Parser Function:
Write a parser package or function (SubParser) that transforms the YAML block (map[string]any) into a native connection interface, such as*Dialer[transport.StreamConn]. If your dialer delegates downward to a nested endpoint, require that parser as an argument.gofunc NewMyStrategyDialerSubParser(parseEndpoint configyaml.ParseFunc[*Endpoint[transport.StreamConn]]) func(ctx context.Context, input map[string]any) (*Dialer[transport.StreamConn], error) { return func(ctx context.Context, input map[string]any) (*Dialer[transport.StreamConn], error) { // 1. Unmarshal `input` logic into your custom struct // 2. Instantiate your streaming strategy // 3. Return a `Dialer` conforming to the interface requirements } }Register the Strategy:
Locate theNewDefaultTransportProviderfunction inregistry.go.
Register your parsing function into the relevant parser category (streamDialersorpacketDialers). The string key you provide will act as the native identifier for the matching$typefield inside Outline configuration files.gostreamDialers.RegisterSubParser("my_strategy", NewMyStrategyDialerSubParser(streamEndpoints.Parse))
Overview (`client/src/cordova/android/README.md`)
Android Development Instructions
This document describes how to develop and debug for Android.
The main entrypoint to Android's Java code is client/src/cordova/plugin/android/java/org/outline/OutlinePlugin.java
Set up your environment
Install these pre-requisites:
Java Development Kit (JDK) 17+. On macOS:
shellbrew install openjdk@17 # Make it visible to `java_home` sudo ln -sfn "$(realpath "$(brew --prefix)")/opt/openjdk@17/libexec/openjdk.jdk" /Library/Java/JavaVirtualMachines/openjdk-17.jdk export JAVA_HOME=$(/usr/libexec/java_home -v 17.0)Gradle 8.14.2+. On macOS:
brew install gradle.
Then we need to install and configure the Android components. You can follow the Cordova Android Platform Guide, which uses Android Studio to configure the environment. You need an Android Studio compatible with the Android Gradle Plugin 8.10.1 (see build.gradle) we use (compatibility table).
Alternatively, you can do it on the command-line:
Set environmental variables:
shellexport ANDROID_HOME=$HOME/Library/Android/sdk export ANDROID_NDK=$ANDROID_HOME/ndkInstall the Android command-line tools and make them available on the command line:
shellcurl -o /tmp/commandlinetools.zip https://dl.google.com/android/repository/commandlinetools-mac-13114758_latest.zip unzip /tmp/commandlinetools.zip -d /tmp mkdir -p "$ANDROID_HOME/cmdline-tools/latest" mv /tmp/cmdline-tools/* "$ANDROID_HOME/cmdline-tools/latest/" export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin/"Install platform and build tools:
shellsdkmanager "platforms;android-36" "build-tools;36.0.0" "ndk;28.2.13676358"The NDK version must match the
ndkVersionpin in build-extras.gradle, whose comment explains why we pin it (AGP uses it to strip the packaged native libraries).Install optional components that help development (source code, emulator and image):
shellsdkmanager "sources;android-36" "system-images;android-36;default;arm64-v8a"
Note: you will need the system-images;android-36;default;x86_64 image on an Intel computer.
For development of the OutlineAndroidLib, we recommend installing Android Studio. That also make it easier to create virtual devices and run the emulator.
You can check your environment with:
npm run action client/src/cordova/setup android
cd client
npx cordova requirements androidImportant Versions
| Component | version | constrained by | set in |
|---|---|---|---|
| Android API Level | 36+ | Play Store | config.xml, build.gradle |
| cordova-android | 15 | Android API Level | package.json |
| JDK | 17 | cordova-android | install instruction |
| Gradle | 8.14.2+ | cordova-android | gradle-wrapper.properties |
| Android Gradle Plugin (AGP) | 8.10.1 | cordova-android | build.gradle |
| Android Build Tools | 36.0.0+ | cordova-android | install instructions |
| Android NDK | 28.2.13676358 | native-lib stripping | build-extras.gradle, install instructions |
| Android Studio | 2025.1.1 (Narwhal) | AGP |
Build the app
To build the app for Android, run:
npm run action client/src/cordova/build androidThe built apk will be at: client/platforms/android/app/build/outputs/apk/debug/app-debug.apk.
We also support passing a --verbose option on cordova android:
npm run action client/src/cordova/build android -- --verboseMake sure to rebuild after modifying platform dependent files!
๐ก NOTE: If this command ever gives you unexpected Cordova errors, try runnning
npm run resetfirst.
Run the app
Start the simulator or connect an Android device and enable USB debugging.
From the project root, run:
shelladb install -r -d client/platforms/android/app/build/outputs/apk/debug/app-debug.apk
Alternatively, run with:
cd client
npx cordova run android -- --gradleArg=--include-build=../../src/cordova/android/OutlineAndroidLibDevelop code
Open src/cordova/android/OutlineAndroidLib on Android Studio. That will allow you to leverage the IDE features (auto-complete, warnings, etc) and run unit tests easily.
This project represents a library that implements most of the Outline native functionality and does not depend on Cordova. That library is imported by the "app" module that is generated by Cordova. The only native Android code outside of this library is OutlinePlugin.java, which integrates with CordovaLib.
To make the entire application load properly on Android Studio, you need to edit the generated /client/platforms/android/settings.gradle and add:
includeBuild "../../src/cordova/android/OutlineAndroidLib"To debug the web app
Run Outline on your phone with USB debugging enabled, then on Chrome:
- Go to chrome://inspect
- Find Outline
- Click inspect
- Open the Console
- Note that all TypeScript code is browserified in a single main.cordova.js
To debug Java code
You can debug the Java code using logcat on Android Studio:
- Start the app
- Select the "Logcat" tab at the bottom
- Select
org.outline.android.clientin the middle menu (it should not say "no debuggable processes")
It's also possible to use the logcat command.
Overview (`client/src/cordova/apple/README.md`)
Outline Client Apple Development Instructions
This document describes how to develop and debug the Outline Client for iOS and MacOS.
Getting Started
You will need:
- Node (
lts/hydrogen- download), - An Apple Device and Developer Account.
- XCode 15.2 (download)
- XCode command line tools:
xcode-select --install
Initalizing the XCode Project
The XCode project is assembled by Cordova. To initialize and open the iOS project, run the following commands:
npm run action client/src/cordova/setup ios
open ./client/src/cordova/apple/client.xcworkspaceFor macOS:
npm run action client/src/cordova/setup macos
open ./client/src/cordova/apple/client.xcworkspace[!NOTE]
Should you encounter issues with your build, there may be apple-specific dependencies that are out of date.
Runnpm run resetand try again!
Configure code signing
Apple is quite particular when it comes to making sure your app is properly signed, even in development.
- In XCode, make sure you are logged into your Apple Developer account and that your personal devices are registered to it. Go to Preferences > Accounts and make sure your account is set.
- Select the "Outline" target in the left navigation bar and go to the "Signing & Capabilities" tab.
Internal Contributors
- Ensure that the "Jigsaw Operations LLC" option is selected for "Team".
- Do the same for the "VPN Extension" target.
External Contributors
For release builds via the command line, set the DEVELOPMENT_TEAM environment variable to override the default team ID without editing any project files:
DEVELOPMENT_TEAM=<your_team_id> npm run action client/src/cordova/build ios -- --buildMode=release
DEVELOPMENT_TEAM=<your_team_id> npm run action client/src/cordova/build macos -- --buildMode=releaseFor development in Xcode:
- Select the "Team" for your own account.
- Change the bundle identifier (e.g.
org.outline.ios.client) to something unique. - Remove the app group
group.org.getoutline.client. - Do the same for the "VPN Extension" target.
Running the App
Specify the Run Destination
For the iOS client, you have a few options:
- Run on your macOS computer: Product >> Destination >> My Mac (Mac Catalyst)
- Run on a simulator
- This is helpful to test the UI, but the VPN doesn't work on simulators, so this option is not recommended.
- (Best) Run on a physical iOS device
- This is the best option to evaluate how the app performs on a real device. Remember, you will need to enable development mode on the device and register it in your developer account.
See Devices and Simulator for details on running on a physical iOS device or the simulator.
You may find that your iOS version is too modern for XCode. You'll need to do the following:
- Download a folder corresponding to your iOS version from this community-managed repository.
- Unzip the file and add it to XCode:
textApplications >> Xcode >> Right Click >> Show Package Contents >> Contents >> Developer >> Platforms >> iPhoneOS.platform >> DeviceSupport
- Restart XCode.
For the macOS client, you can simply select your macOS computer: Product > Destination > My Mac.
Build and Start the App
- First clean the build (Product >> Clean Build Folderโฆ or โ โง K)
[!WARNING]
If you don't clean the build first, it will fail withCommand CodeSign failed with a nonzero exit code.
- Run (Product >> Run or โ R), via the menu or the play button:
Development
Most of the Apple-specific development can happen directly on XCode. However, if you edit files in the generated platforms/ios, you will need to copy your changes to the appropriate version-controlled location at src/cordova/apple/xcode or src/cordova/plugin/apple.
Changes to the OutlineAppleLib package don't need to be copied, since the package is linked by the XCode workspace and the changes happen in the original location.
Inspect Logs
The easiest way to inspect logs is to use the log command. To see the client app logs in real time, you can use log stream with a --predicate flag to select the messages sent by the Outline code:
log stream --info --predicate 'senderImagePath contains "Outline.app"'In the Console app, select the Action > Include Info Messages manu, and set the filter to "Library Path" "contains" "Outline.app":
๐ก Tip: You can save searches in the MacOS Console app.
For further debugging, you can include relevant messages from the Network Extension subsystem:
log stream --info --predicate 'senderImagePath contains "Outline.app" or (processImagePath contains "Outline.app" and subsystem contains "com.apple.networkextension")'To see past logs use log show and the --last flag.
Private values are redacted from Apple system logs by default. To include private debug values while developing, run:
sudo log config --mode "private_data:on"Restore the default redaction behavior when you are done:
sudo log config --mode "private_data:off"For details on Apple logging, see Your Friend the System Log and Mac Logging and the log Command: A Guide for Apple Admins.
Debug the Vpn Extension
The VpnExtension runs in a separate process and its output is not logged to the Xcode console. To view its log statements see the "Inspect Logs" section.
XCode doesn't automatically attach to the VpnExtension because it's started on demand by the system.
- If the Vpn Extension is running:
- In XCode, select Debug > Attach to Process > VpnExtension
- If the VpnExtension is not running:
- In Xcode, select Debug > Attach to Process by PID or Nameโฆ
- Fill PID or Process Name with
VpnExtensionand press Attach
You won't see the log messages in the Xcode console. To see the messagges, refer to the "Inspect Logs" instructions.
For more info, see Debug, Profile, and Test Your App Extension.
Debug `VpnStartFailure`
Sometimes the app will refuse to connect, with a VpnStartFailure error:
If that happens, it may be the case that the app is trying to use the wrong version of the plugin.
To fix, run:
pkill -9 VpnExtension
pluginkit -Amv | cut -f 4 | grep Outline | while IFS= read -r p; do pluginkit -r "$p"; done;
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -gcThis will kill any running VPN extension, unregister all versions of the Outline VPN plugin and garbage collect the Launch Services database.
Delete the existing VPN configuration
You may want to also delete the VPN configurations:
- Open the System Settings
- Go to VPN
- Press the Info button for the Outline service
- Press Remove Configurationโฆ and confirm
Restart
If all fails, restart your device. That usually takes care of the issue.
Debug the web UI
To debug the webview:
- You may need to enable the Develop menu first, by selecting Settings > Advanced > Show Develop menu in menu bar
- In your terminal, run
defaults write org.outline.ios.client WebKitDeveloperExtras -bool true. This is only needed once, to make the Outline webview debuggable. You may need to re-run the whole Outline app (use Cmd+R). - In the Outline Client app, right click โ Inspect Context. This will open the Safari debugger
To reload the UI without re-running the application, right-click โ Reload.
Overview (`client/src/cordova/plugin/README.md`)
cordova-plugin-outline
Cordova plugin that implements a system-wide VPN to tunnel device traffic over Shadowsocks.
Supported Platforms
This plugin supports Android, iOS, and macOS.
Android
This plugin targets Android devices running Lollipop (API 22), or higher.
Apple
This plugin targets Apple devices running iOS 11.0+ and macOS/OS X 10.11+.
tun2socks
Native platforms consume outline-go-tun2socks as a library to assemble IP traffic and implement the Shadowsocks protocol: it receives all of the deviceโs traffic through the VPN network interface (TUN) and forwards it to a Shadowsocks proxy server.
Additional Apple Dependencies
We use Swift Packages for third party dependencies: sentry-cocoa and CocoaLumberjack.
To upgrade the Swift Package dependencies, update the version field for the corresponding package under the XCRemoteSwiftPackageReference section in the Outline.pbxproj file.
Alternatively, open the xcworkspace file for the curresponding OS, and update the packages via the XCode UI.
Overview (`server_manager/www/ui_components/licenses/README.md`)
HOWTO re-generate `license.txt`
Steps
cdto the root of your clone of this repo- Ensure
node_modulesis up to date and only include dependencies of the Electron app by runningnpm ci && npm run action server_manager/www/build - Copy
LICENSEand the entirenode_modulesfolder to./server_manager/LICENSEand./server_manager/node_modules cd server_managernpx generate-license-file --input package.json --output www/ui_components/licenses/licenses.txtcd www/ui_components/licensescat db-ip_license.txt >> licenses.txt
Done! (remember to delete the ./server_manager/node_modules folder)
Note that the third step of copying
LICENSEandnode_modulesis required because:
generate-license-fileiterates all dependencies undernode_modulesfolder in the current directorygenerate-license-filewill try to find aLICENSEfile in the current directory, if not found, it will useREADME.mdinstead which is not what we want
Check
To quickly look for non-compliant licenses:
yarn licenses list --prod|grep -Ev \(@\|VendorUrl:\|VendorName:\|URL:\)Overview (`client/src/cordova/apple/OutlineAppleLib/README.md`)
OutlineAppleLib
This package provides all Outline Apple logic, including the Outline Tun2socks
framework, as a Swift Package.
Overview (`client/web/views/licenses_view/licenses/README.md`)
HOWTO re-generate `licenses.txt`
Steps
cdto the root of your clone of this repo.- Ensure
node_modulesis up to date by runningnpm ci. npx generate-license-file --input package.json --output client/web/views/licenses_view/licenses/licenses.txt./client/web/views/licenses_view/licenses/third_party.sh >> ./client/web/views/licenses_view/licenses/licenses.txt
Done!
Invitation Instructions (`docs/invitation-instructions.md`)
Moved to invitation_instructions.md.
Invitation Instructions (`docs/invitation_instructions.md`)
Invitation instructions
Have an invitation to connect to an Outline server? Follow these instructions to access to the open internet from your device. No personal information will be collected.
1. Copy your access key
ss://xxx located at the bottom of your invitation email or message. The Outline app will instantly add this server from your clipboard.
2. Install Outline
| Download | |
|---|---|
| Android โบ | If Google Play is not accessible, get it here. |
| iOS โบ | Get Outline on the App Store |
| Windows โบ | Download Outline.exe and double click to launch. |
| macOS โบ | Get Outline on the Mac App Store |
| Linux โบ | Follow the instructions to install the Outline client on Linux. |
3. Add server and connect
Open the app and confirm your new server. Tap or click to connect.
Having trouble? Try copying your access key again to add your server.
Life Of A Packet (`docs/life_of_a_packet.md`)
Life of a Packet
flowchart LR
A(Apps) <--> S(Socket)
subgraph Operating System
S <--> R(Routing Table)
end
R <-- Outline OFF --> IF(Network Interface)
IF <--> INET(((Internet)))
R <-- Outline ON --> TUN(Tun Device)
TUN <--> T2S(Tun2sockets)
subgraph Outline Client
T2S <--> SC(Shadowsocks Client)
end
SC <--> IF
click TUN "https://en.wikipedia.org/wiki/TUN/TAP" _blank--- METRICS ---
- Files Extracted: 16
- Estimated Token Budget: ~12436 tokens
- Canonical Reference: https://codewiki.google/github.com/OutlineFoundation/outline-apps