Skip to content

Setting Up Permission Guard

What you'll learn

  • Generating the Permission Guard devtool
  • Understanding why APK/IPA binary scanning catches what source diffing misses
  • Committing your permission baseline and running guard checks locally
  • Adding the permission check to your CI pipeline to block unexpected permissions in PRs

Prerequisites

  • An existing Archipelago monorepo (see Monorepo Scaffolding)
  • monorepo_toolkit already generated — Permission Guard registers itself as a command inside it

Step 1: Generate Permission Guard

Permission Guard is a Pro tier brick.

bash
archipelago generate permission_guard

You will be prompted for:

  • appNameMyApp (must match your monorepo app name)
  • isForMonorepotrue

Or use a config file:

json
{
  "appName": "MyApp",
  "isForMonorepo": true
}
bash
archipelago generate permission_guard --config permission_guard_config.json

Step 2: Understand the Generated Structure

The generator adds a Dart script and a committed baseline directory:

devtools/
└── scripts/
    └── permission_guard/
        ├── pubspec.yaml
        └── lib/
            └── permission_guard.dart

permission_baseline/
├── android.txt    # Committed — the approved permission list for Android
└── ios.txt        # Committed — the approved permission list for iOS

The post_gen hook also registers the permission-guard command in your monorepo_toolkit so you can invoke it through the unified devtools entry point.

Step 3: Why Binary Scanning Instead of Source Diffing

Flutter dependencies can inject Android permissions and iOS entitlements into your compiled binary without modifying your AndroidManifest.xml or Info.plist directly. For example:

  • A pub package might include a compiled AAR with its own AndroidManifest.xml
  • An iOS framework might declare entitlements in its module map

Source-level diffing misses these entirely. Permission Guard decompiles the built APK or inspects the IPA archive to read the final merged permission list — the same list the app store and device enforcement actually see.

Step 4: Build Your App First

Permission Guard operates on a built artifact, not source. Build once before running a check:

bash
# Android
flutter build apk --flavor production

# iOS (requires macOS + Xcode)
flutter build ipa --flavor production

Step 5: Initialize the Baseline

On first run, generate the baseline files that will be committed to the repo:

bash
# Android baseline
dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
  permission-guard update-baseline android

# iOS baseline
dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
  permission-guard update-baseline ios

This writes the current permission list into permission_baseline/android.txt and permission_baseline/ios.txt. Review the contents, then commit both files:

bash
git add permission_baseline/
git commit -m "chore: add initial permission baseline"

Step 6: Run a Permission Check Locally

After building, run the guard against your APK:

bash
dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
  permission-guard android \
  --apk build/app/outputs/flutter-apk/app-production-release.apk

For iOS:

bash
dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
  permission-guard ios \
  --ipa build/ios/ipa/MyApp.ipa

If the permission list matches the baseline, the command exits 0. If new permissions are detected, it prints a diff and exits non-zero:

ERROR: Unexpected permissions detected in android build.

  + android.permission.RECORD_AUDIO   (added by: package:speech_to_text)

Run `permission-guard update-baseline android` to approve these changes.

Step 7: Approve New Permissions

When you intentionally add a dependency that requires new permissions, update the baseline and commit it alongside the dependency change:

bash
dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
  permission-guard update-baseline android

git add permission_baseline/android.txt
git commit -m "chore: approve android.permission.RECORD_AUDIO for speech_to_text"

This creates a reviewable audit trail — every permission change is an explicit commit, not a side effect of upgrading a package.

Step 8: Add to CI

Add a permission guard step to your PR workflow. The job must build the app before running the check:

yaml
# .github/workflows/permission_guard.yml
- name: Build APK
  run: flutter build apk --flavor production

- name: Check Android permissions
  run: |
    dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
      permission-guard android \
      --apk build/app/outputs/flutter-apk/app-production-release.apk

The job fails automatically if any permission not in the committed baseline is detected, blocking the PR until the baseline is explicitly updated and reviewed.

Common Customizations

GoalWhat to do
Check multiple flavorsRun the guard once per APK/IPA artifact
Suppress a known false positiveUpdate the baseline and document the reason in the commit message
Enforce iOS entitlements tooRun permission-guard ios in a separate macOS CI job
Gate on specific permissionsExtend permission_guard.dart to treat certain permissions as hard errors

Next Steps

Built by Banua Coder