Skip to content

[stealth 1/4] build profile + flag plumbing - #8859

Merged
reflog merged 3 commits into
mainfrom
stealth/pr1-foundation
Jun 24, 2026
Merged

reflog merged 3 commits into
mainfrom
stealth/pr1-foundation

Conversation

@reflog

@reflog reflog commented Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

Part of epic getlantern/engineering#3569 — stacked PR 1/4, targets main.

Build-profile generation + flag plumbing: a per-build stealth profile (mode, package/app/session name, obfuscation seed, denylist version) fed into Android Gradle, Flutter dart-defines, Go build tags, and CI metadata, plus the central AppBuildInfo flag surface. All stealth behavior is build-time gated — normal builds are unchanged.

Implements getlantern/engineering#3623.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added stealth build support with identity obfuscation, custom package names, and privacy-focused configurations.
    • Introduced build profile system for generating stealth app variants with customizable parameters.
    • Added code obfuscation capability for stealth builds.
    • Added stealth leakage detection and reporting in build workflows.
  • Documentation

    • Added stealth build profile generation and configuration guide.
    • Added stealth feature gates documentation.

@reflog reflog changed the title stealth(foundation): build profile + flag plumbing [stealth 1/6] build profile + flag plumbing Jun 15, 2026
@reflog
reflog marked this pull request as ready for review June 15, 2026 15:28
Copilot AI review requested due to automatic review settings June 15, 2026 15:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces the first slice of “Stealth Lantern” build-time gating by adding a build-profile generator and plumbing profile-derived flags through Make/Flutter/Android Gradle plus CI workflow inputs, with accompanying documentation.

Changes:

  • Adds scripts/stealth/generate_profile.py (+ unit tests) to generate/normalize stealth profile JSON and emit dart-defines, artifact metadata, and Go tag suffixes.
  • Extends the Makefile and Android Gradle build to consume stealth profiles, generate auxiliary build outputs (identity profile, icons, manifests), and optionally build Go code via garble.
  • Adds documentation and CI workflow inputs to support stealth leakage scanning and new build options.

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
scripts/stealth/generate_profile.py New profile generator/validator producing normalized profile + derived outputs.
scripts/stealth/generate_profile_test.py Unit tests covering profile validation and output generation.
Makefile Adds stealth profile generation, Go tag plumbing, optional obfuscation, and Android build wiring.
android/app/build.gradle Loads stealth/identity profiles, switches source sets/manifest/icons for stealth builds, and exports BuildConfig fields/placeholders.
lib/core/common/app_build_info.dart Adds Dart environment flag accessors and feature gates for stealth builds.
docs/stealth-build-profile.md Documents stealth profile generation and consumption via Make/Gradle.
docs/stealth-feature-gates.md Documents Dart define-based feature gates for stealth artifacts.
.github/workflows/build-android.yml Adds inputs/steps for leakage scanning and optional Go obfuscation build path.
.github/workflows/release.yml Propagates optional stealth leakage scan mode from manual release dispatch to Android build workflow.
pubspec.yaml Adds a new Flutter asset directory entry for stealth assets.
pubspec.lock Dependency lockfile updates.
.gitignore Ignores built APKs and .so artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pubspec.yaml Outdated
Comment thread Makefile
Comment thread Makefile
Comment thread Makefile Outdated
Comment thread android/app/build.gradle
Comment thread .github/workflows/build-android.yml
Comment thread lib/core/common/app_build_info.dart
Comment thread docs/stealth-build-profile.md Outdated
Comment thread docs/stealth-build-profile.md Outdated
@reflog
reflog force-pushed the stealth/pr1-foundation branch from def4ef0 to 82e0867 Compare June 15, 2026 15:45
@reflog
reflog requested a review from jigar-f June 15, 2026 15:51
@reflog
reflog force-pushed the stealth/pr1-foundation branch 2 times, most recently from bab8b58 to fa33939 Compare June 15, 2026 16:27
@reflog reflog changed the title [stealth 1/6] build profile + flag plumbing [stealth 1/4] build profile + flag plumbing Jun 15, 2026
@reflog
reflog force-pushed the stealth/pr1-foundation branch 2 times, most recently from fb322ce to ec5db08 Compare June 15, 2026 16:51
Comment thread android/app/build.gradle

@jigar-f jigar-f left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few comments.

Comment thread android/app/build.gradle Outdated
Comment thread android/app/build.gradle Outdated
reflog and others added 2 commits June 23, 2026 14:20
- Always include core-ktx / lifecycle-livedata-ktx / work-runtime-ktx:
  these are used by the real app code (VPN status, logs, background work)
  in stealth builds too, not just regular builds.
- Drop the dangling proguard-stealth-novpn.pro reference; the existing
  proguard-rules.pro applies to all release variants (incl. stealth-novpn).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@reflog
reflog force-pushed the stealth/pr1-foundation branch from ec5db08 to 03e6ede Compare June 23, 2026 12:22
@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

📝 Walkthrough

Walkthrough

This PR introduces a complete "stealth build" pipeline: a Python script generates and validates stealth build profiles (with Android identity, obfuscation seeds, and dart defines), new Makefile variables and targets propagate stealth configuration across all platforms (Android, iOS, macOS, Linux, Windows) using optional garble obfuscation, android/app/build.gradle loads stealth profiles to switch namespace/manifest/BuildConfig, AppBuildInfo exposes compile-time stealth feature gates, and CI workflows add inputs for obfuscation and leakage scanning.

Changes

Stealth Build Pipeline

Layer / File(s) Summary
Stealth profile generation script and tests
scripts/stealth/generate_profile.py, scripts/stealth/generate_profile_test.py
Adds a CLI Python tool that validates, normalizes, and generates stealth build profile JSON, plus Dart defines, artifact metadata, and Go tag suffix outputs. Tests cover end-to-end CLI output, normalization, alias resolution, validation failures, manifest placeholder sanitization, and default_package_name() token-exclusion.
Dart stealth feature gates and documentation
lib/core/common/app_build_info.dart, docs/stealth-feature-gates.md, docs/stealth-build-profile.md
Extends AppBuildInfo with stealth build-time constants, isStealthBuild, suppressIdentifyingFeatures, and derived feature toggles (enableOAuth, enableAppLinks, enableSocialLinks, enableAutoUpdate). Adds documentation for compile-time feature gates and the stealth build profile workflow.
Makefile stealth variables, profile pipeline, and garble config
Makefile (lines 76–422)
Introduces stealth linker flags, log-level derivation, Android identity profile variables, garble toolchain configuration, STEALTH_ENABLED logic, dart-define injection, go-tag-suffix handling, FLUTTER_DART_DEFINES composition, and new targets: stealth-profile, stealth-android-sources, garble validation targets, stealth-android-icons, and install-garble.
Makefile platform build target updates
Makefile (lines 442–1025)
Threads MAYBE_STEALTH_PROFILE, STEALTH_GO_TAGS, GO_EXTRA_LDFLAGS, and STEALTH_DART_DEFINES into all platform build targets: desktop, macOS, Linux (including obfuscated lanternd and lanternd-log-level bundle file), Windows, Android (android-identity-profile, verify-stealth-jni, obfuscated builds, stealth release targets, stealth-leakage-check), and iOS.
Android Gradle stealth profile integration
android/app/build.gradle
Loads stealth profile JSON in Groovy, derives androidIdentity, conditionally switches namespace, sourceSets.main (manifest, Java/Kotlin/res), defaultConfig.applicationId, manifestPlaceholders, and many buildConfigField entries; registers generateStealthManifest and generateStealthAndroidIcons Exec tasks wired into preBuild.
CI workflow stealth wiring and .gitignore
.github/workflows/build-android.yml, .github/workflows/release.yml, .gitignore
Extends build-android.yml with stealth inputs (obfuscate_go, stealth_leakage_mode, STEALTH_GARBLE_SEED) and conditional garble/obfuscated build and leakage check steps; extends release.yml with stealth_leakage_mode dispatch input flowing through set-metadata to build-android; adds *.apk and *.so to .gitignore.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(100, 100, 200, 0.5)
    note over ReleaseCIWorkflow,BuildAndroidWorkflow: CI dispatch with stealth_leakage_mode
  end
  participant ReleaseCIWorkflow as release.yml
  participant SetMetadata as set-metadata job
  participant BuildAndroidWorkflow as build-android.yml

  ReleaseCIWorkflow->>SetMetadata: workflow_dispatch(stealth_leakage_mode)
  SetMetadata->>SetMetadata: derive STEALTH_LEAKAGE_MODE (skip if "none")
  SetMetadata-->>BuildAndroidWorkflow: with.stealth_leakage_mode, obfuscate_go, android_identity_seed

  alt obfuscate_go == true
    BuildAndroidWorkflow->>BuildAndroidWorkflow: install garble
    BuildAndroidWorkflow->>BuildAndroidWorkflow: run android-release-ci-obfuscated (ANDROID_IDENTITY_SEED, STEALTH_GARBLE_SEED)
  else
    BuildAndroidWorkflow->>BuildAndroidWorkflow: run android-release-ci (ANDROID_IDENTITY_SEED)
  end

  opt stealth_leakage_mode != ""
    BuildAndroidWorkflow->>BuildAndroidWorkflow: stealth-leakage-check → stealth-leakage-report.txt
    BuildAndroidWorkflow->>BuildAndroidWorkflow: upload artifact stealth-leakage-report.txt
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐇 Hop hop, the rabbit wears a new disguise,
No lantern in the package name — surprise!
garble scrambles symbols, seeds are hashed with care,
Stealth profiles whisper secrets through the air.
The leakage checker sniffs each final APK,
And only safe, unbranded bytes may stay! 🕵️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[stealth 1/4] build profile + flag plumbing' accurately describes the main change: introducing stealth build profile generation and flag infrastructure as the first part of a multi-part series.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stealth/pr1-foundation

Comment @coderabbitai help to get the list of available commands.

@reflog
reflog requested a review from jigar-f June 23, 2026 12:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-android.yml:
- Around line 196-199: Replace all floating GitHub Actions version references
with pinned commit SHAs to eliminate supply-chain security risks. For each
instance of actions/upload-artifact@v4 (and any other floating action references
in the workflow), replace the floating tag with the full 40-character commit SHA
followed by the version tag as an inline comment (e.g., uses:
actions/upload-artifact@<full-sha> # v4). Apply this pattern consistently across
all action uses declarations in the file, including the multiple occurrences of
upload-artifact references.

In @.github/workflows/release.yml:
- Around line 270-276: The workflow directly injects the GitHub Actions template
expansion of stealth_leakage_mode into the shell script on line 272, which
creates a template injection risk. Move the template expansion outside of the
shell code by adding REQUESTED_STEALTH_LEAKAGE_MODE environment variable in the
step's env section with the value of github.event.inputs.stealth_leakage_mode,
then reference this environment variable within the shell conditional instead of
directly using the template syntax. This separates template processing from
shell interpretation and improves security.

In `@android/app/build.gradle`:
- Around line 195-203: The code resolves the stealth mode in lines 195-203 by
processing the stealthProfile.mode and falling back to the STEALTH_MODE project
property, resulting in the processed stealthMode variable. However, at line 408
where the stealth mode is emitted, the original stealthProfile.mode is still
being used instead of the resolved stealthMode variable. Replace the usage of
stealthProfile.mode at line 408 (and line 409 if applicable) with the resolved
stealthMode variable to ensure the emitted value matches the actual resolved
stealth mode.

In `@docs/stealth-build-profile.md`:
- Around line 15-16: The quick-start command examples in the
stealth-build-profile.md documentation are incomplete and will fail during
execution because they lack required stealth identity name parameters. Update
the example commands (around line 15-16 with the make stealth-profile
STEALTH_MODE=stealth-vpn command, and also at lines 55-57) to include the
required app/session name parameters that are now mandatory for stealth mode
profile generation. Ensure each command example shows the complete set of
parameters needed for successful execution.

In `@Makefile`:
- Around line 336-340: Replace all hardcoded python3 invocations with the
$(PYTHON) variable in the stealth-related Makefile recipes to use the configured
interpreter consistently. This includes the python3 calls in the
STEALTH_PROFILE_SCRIPT section around lines 336-340 and all other stealth recipe
sections at lines 350, 420, and 787-794. Update each occurrence where python3
appears in these stealth recipes to use $(PYTHON) instead, ensuring the Makefile
respects the configured Python interpreter across all environments.
- Around line 76-79: The STEALTH_GO_LDFLAGS variable currently only applies
stealth build flags when BUILD_TYPE matches "stealth stealth-%", but stealth can
be activated through STEALTH_MODE or STEALTH_PROFILE without changing
BUILD_TYPE. Update the conditional check in the STEALTH_GO_LDFLAGS assignment to
gate the ldflags on stealth activation by also checking if STEALTH_MODE or
STEALTH_PROFILE are defined, in addition to the existing BUILD_TYPE filter. This
ensures the StealthBuild and StealthLogLevel flags are applied regardless of
which method activates stealth mode.

In `@scripts/stealth/generate_profile_test.py`:
- Around line 221-254: Add two new test methods following the pattern of
test_stealth_build_requires_app_name and
test_stealth_build_requires_session_name to explicitly verify that branded
stealth names are rejected. Create a test that passes appName="Lantern" with
stealth-vpn mode and asserts the exit code is non-zero, and another test that
passes sessionName="LanternVpn" with stealth-vpn mode and asserts the exit code
is non-zero. These tests will lock in the de-branding privacy rule through
regression testing.

In `@scripts/stealth/generate_profile.py`:
- Around line 206-214: The validation block in the stealth mode section only
verifies that app_name and session_name are not empty, but the error messages
and the comment indicate these values must also not be Lantern-branded. Add
additional checks after the non-empty validation to detect and reject
Lantern-branded values (such as "Lantern", "LanternVpn", or other branded
keywords) in both app_name and session_name parameters. Raise ProfileError with
an appropriate message if a branded value is detected, ensuring stealth
artifacts do not leak branding information.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d305df14-ff96-47c8-8ee4-5885601ba616

📥 Commits

Reviewing files that changed from the base of the PR and between 91ed2dd and 03e6ede.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .github/workflows/build-android.yml
  • .github/workflows/release.yml
  • .gitignore
  • Makefile
  • android/app/build.gradle
  • docs/stealth-build-profile.md
  • docs/stealth-feature-gates.md
  • lib/core/common/app_build_info.dart
  • scripts/stealth/generate_profile.py
  • scripts/stealth/generate_profile_test.py

Comment thread .github/workflows/build-android.yml
Comment thread .github/workflows/release.yml
Comment thread android/app/build.gradle
Comment thread docs/stealth-build-profile.md Outdated
Comment thread Makefile
Comment thread Makefile Outdated
Comment thread scripts/stealth/generate_profile_test.py
Comment thread scripts/stealth/generate_profile.py
- generate_profile.py: enforce non-branded (no "Lantern") app/session names
  for stealth modes, matching the de-branding contract the error messages
  already promised; add regression tests for branded names.
- build.gradle: emit the resolved stealth mode in the STEALTH_MODE
  BuildConfig field so it can't read "normal" while STEALTH_ENABLED=true
  (e.g. when stealth is enabled via -PSTEALTH_MODE without a profile).
- Makefile: gate STEALTH_GO_LDFLAGS and lanternd log level on stealth
  activation (BUILD_TYPE | STEALTH_MODE | STEALTH_PROFILE), not BUILD_TYPE
  alone; use $(PYTHON) instead of hardcoded python3 in stealth recipes.
- release.yml: read stealth_leakage_mode via env indirection with an
  explicit allowlist instead of inlining the template expansion in shell.
- docs: make quick-start commands runnable (include required app/session
  names).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@jigar-f jigar-f left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one comment

Comment thread android/app/build.gradle

defaultConfig {
applicationId = "org.getlantern.lantern"
applicationId = androidIdentity.applicationId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be like this

applicationId = stealthMode ? androidIdentity.applicationId : "org.getlantern.lantern"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

androidIdentity.applicationId already defaults to org.getlantern.lantern for normal builds (defaultStealthProfile.packageName → androidIdentityDefaults.applicationId), and line 285 validates it. It is only overridden when an identity profile is explicitly passed — which is intentional: identity randomization is a separate axis from stealth mode (e.g. the ./gradlew assembleRelease -PandroidIdentityProfile=… side-by-side install path). Gating on stealthMode here would ignore a provided profile's applicationId whenever full stealth is not enabled, so I'd keep it as-is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh, I see.

Comment thread android/app/build.gradle
// BOTH stealth and regular builds (stealth now compiles the real handlers,
// not the legacy stub). Only STEALTH_ENABLED differs by build.
buildConfigField "boolean", "STEALTH_ENABLED", stealthMode ? "true" : "false"
buildConfigField "String", "STEALTH_MODE", buildConfigString(resolvedStealthMode)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick question: why do we need the STEALTH_MODE? As a separate key, shouldn't we consider if this stealth enable is true? It is by default in stealth mode, and if not, then normal. Am I missing something here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like I got my answers. I think we have two modes: VPN on and no VPN, correct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jigar-f yeah, regular Stealth is just 'white label lantern', the NOVPN stealth removes tunnels, etc, and just leaves a SOCKs proxy mode

@reflog
reflog merged commit 560357e into main Jun 24, 2026
9 checks passed
@reflog
reflog deleted the stealth/pr1-foundation branch June 24, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants