Obscura VPN library, CLI client, and App
No support is provided for this code directly. However, if you are experiencing issues with your Obscura VPN service please contact support@obscura.net.
At this time we are unable to accept external contributions. This is something that we plan to resolve soon. However until we finish the paperwork we are unable to look at any patches and will close all PRs without looking at them.
On macOS the app installs and manages a network extension (system extension). The network extension manages the virtual device and maintains the tunnel using the Rust code as library.
nix-env -iA nixpkgs.{cmake,rustup}bash
nix develop --print-build-logs --command just xcode-openProd Client build schemebash
nix develop --print-build-logs --command just xcode-openPick a build scheme using Xcode's GUI, one of:
ℹ️ INFO: Xcode differentiates between "build schemes" and "build configurations", see Apple's docs on this for more details.
Dev Client: Development Client
General purpose for development. Uses the main UI with additional developer and pre-release features exposed.
Uses the Debug* build configurations. Codesigned with the Apple Development xcode-managed identity.
⚠️ WARNING: When using this build scheme, make sure you are quitting the app via the top-right status menu bar and NOT using Xcode's "Stop" as doing so does not actually stop the dev server. This is because stopping via Xcode doesn't run the build scheme's "Run → Post-actions"
Prod Client: The App with a static web bundle
Useful for reproducing what the final shippable app will look like and be built as.
Uses the Release* build configurations. Codesigned with the Developer ID Application: Sovereign Engineering Inc. (5G943LR562) manually-managed identity.
The static web bundle built with the build scheme's "Build → Pre-actions".
If you encounter trouble with this build scheme, especially with codesigning or provisioning profiles:
Bare Client: The App with a minimal HTML UI
Useful for fine-grain control and debugging.
Uses the Debug* build configurations. Codesigned with the Apple Development xcode-managed identity.
Build or Run the App
⌘ + B (Build), or⌘ + R (Run)💡 TIP: It may initially seem like Xcode is doing nothing when you run or build, but it may just be running the build scheme's "Pre-actions", see the "Report navigator" in Xcode's top-left app menu: "View → Navigators → Reports" to track the actual status.
💡 TIP: If a build fails with could not find included file 'buildversion.xcconfig' in search paths, see the relevant troubleshooting entry.
Xcode places built products in a deeply nested directory structure that it controls, with seperate folders for each build configuration. The easiest way to locate where the app is:
⌘-Click the app icon to reveal it in the finder.💡 TIP: It is highly recommended to read through various sections in Development Tips to better understand the various ways we've configured the Xcode build system to work with our development process.
Nix builds provide an easy way to get a fully built APK. They are hermetic and reliable. However, they provide only coarse grained caching so if you are iterating during development you may prefer to use Incremental Builds.
nix build '.#apks-foss'
apksigner sign --ks your-keystore.jks --ks-pass pass:hunter2 --out=obscura-signed.apk result/app-foss-release-unsigned.apk # Sign.
adb install obscura-signed.apk # Push to your device.
Instead of app-foss-release-unsigned you can also use app-foss-debug for the debug build. Note that just the Android portion is a debug build, the Rust core and UI are still release builds.
The Android app requires a special build of the Rust library and Obscura UI. These are built using Nix, while the Android app itself can be built using Android Studio for local development, or the Gradle build system to create an official build.
bash
OBS_WEB_PLATFORM="android" nix develop '.#web' --print-build-logs -c just web-bundle-buildbash
nix develop '.#android' --command bash -c 'cd rustlib && cargo ndk -t arm64-v8a build --release'android directory, orbash
nix develop '.#android' --command bash -c 'cd android && gradle --no-daemon $GRADLE_OPTS build'In order to iterate you can just repeat the steps. 1 and 2 are only required if you changed the UI or Rust core respectively but the final APK build must always be re-run.
To ensure hermetic builds we pin our Gradle dependencies. If you change the dependencies you will need to regenerate the pin file.
bin/gradle-deps-update.sh
Install Visual Studio with the following Workloads:
Install Rust.
You may also need to install Windows App SDK manually to get the client app running.
Install Powershell 7
On Windows, definitely ARM64 machines, you need to add C:\Program Files\Microsoft Visual Studio\18\Community\VC\Tools\Llvm\ARM64\bin to path.
Download the signed wintun 0.14.1 DLLs.
You can use Get-FileHash -Path .\wintun-0.14.1.zip -Algorithm SHA256 to verify the hash against SHA2-256: 07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51.
Extract to windows/wintun-0.14.1 such that windows/wintun-0.14.1/bin/arm64/wintun.dll is a file.
To test the service, run sudo cargo run service. You need to enable sudo under System > Advanced settings. Alternatively, you can run cargo run service in an administrative terminal.
The default config directory is %APPDATA%\Obscura. When testing the service, you may find it beneficial to manually add in an account number.
A helpful command to clean DNS query manually is Remove-DnsClientNrptRule -Name "{fb157da8-6578-4f53-81ea-0a9168e96c1f}".
To run the desktop app, you need to install nvm-windows (winget install nvm-windows) and then run nvm install lts && nvm use lts && corepack enable.
The WinUI 3 Gallery app is very useful at showcasing features currently available with code snippets.
If for some reason you don't want to modify tag.json to set the version of the app, you can also add the following to manually create the environment variable (assuming non-CLI build).
<OBSCURA_VERSION Condition="'$(OBSCURA_VERSION)' == ''">1.156</OBSCURA_VERSION>
"Swift Testing" tests are placed in *Test.swift files, which need to be a member of the Tests target. Testing (not running) with the Tests scheme builds and executes all tests.
Both app and network extension logs are available via Apple's unified logging system.
There are tools for analyzing logs available as bin/log-*. They accept log files in JSON lines format. This can be found in the app's Debug Bundle or from the Apple log command by specifying --style=ndjson.
The main tool is bin/log-text.py which just turns the logs into a readable text format as well as applying some basic filtering with a few CLI options to apply more filters. Other tools are available, run with --help to get information about what they do.
For more in-depth analysis you are likely best using the tools as a starting point and modifying them as needed or using other tools like jq, sqlite or duckdb. If your analysis is generally useful consider committing it.
This will output logs starting at the point in time when you run this command:
log stream --info --debug --predicate 'process CONTAINS[c] "obscura" || subsystem CONTAINS[c] "obscura"'
[!WARNING] Since Apple may or may not persist logs at the
INFOorDEBUGlevel, logs at these level might be lost. See Apple's developer docs on this for more information.You may be able to set a log configuration to ensure that these logs are persisted, though this has not been tested, please update this
READMEwith instructions if you successfully test this. See Apple's docs on "Customizing Logging Behavior While Debugging" for more information.
log show --last 200 --info --debug --color always --predicate 'process CONTAINS[c] "obscura" || subsystem CONTAINS[c] "obscura"' | less +G -R
defaults read "net.obscura.vpn-client-app"
# delete all defaults including Sparkle related keys (SU*)
defaults delete-all "net.obscura.vpn-client-app"
# delete keys individually
defaults delete "net.obscura.vpn-client-app" <key>
nix develop --print-build-logs --command just lint
nix flake check
nix develop --print-build-logs --command just format-fix
Save authentication credentials for the Apple notary service (only need to do once)
bash
xcrun notarytool store-credentials "notarytool-password" --team-id 5G943LR562
Use appleid.apple.com --> App-Specific Passwords
(OPTIONAL) If we're doing a release, tag the version git tag -s v/1.23 -m v/1.23 && git push --tags.
security unlock-keychainBuild the signed and notarized disk image: just build-dmg
💡 TIP: This command uses AppleScript automation of Finder to change the background of Disk Images, so Finder windows may open.
The built disk image will appear in the current working directory as "Obscura VPN.dmg"
cargo not rebuilding when it shouldA lot of Xcode-set properties don't properly trigger a rebuild from cargo even
though they're supposed to. The most prominent of which is MACOSX_DEPLOYMENT_TARGET.
This is easily worked-around by "Product → Clean Build Folder..." in Xcode then rerunning the build.
Upstream status on this: - https://github.com/rust-lang/cc-rs/issues/906 - https://github.com/rust-lang/rust/issues/118204
This is necessary for:
- The systemextensionsctl commands to work, and
- To allow installing and running system extensions from places other than /Applications
According to Apple's docs for system extensions, as of 2024-07-04:
You must place all system extensions in the
Contents/Library/SystemExtensionsdirectory of your app bundle, and the app itself must be installed in one of the system’sApplicationsdirectories. To allow development of your app outside of these directories, use thesystemextensionsctlcommand-line tool to enable developer mode. When in developer mode, the system doesn't check the location of your system extension prior to loading it, so you can load it from anywhere in the file system.
To accomplish this:
1. Disable system integrity protection
1. Then, run
bash
systemextensionsctl developer on
$ claude mcp add obscuravpn-client \
-- python -m otcore.mcp_server <graph>