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_toolkitalready generated — Permission Guard registers itself as a command inside it
Step 1: Generate Permission Guard
Permission Guard is a Pro tier brick.
archipelago generate permission_guardYou will be prompted for:
- appName —
MyApp(must match your monorepo app name) - isForMonorepo —
true
Or use a config file:
{
"appName": "MyApp",
"isForMonorepo": true
}archipelago generate permission_guard --config permission_guard_config.jsonStep 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 iOSThe 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:
# Android
flutter build apk --flavor production
# iOS (requires macOS + Xcode)
flutter build ipa --flavor productionStep 5: Initialize the Baseline
On first run, generate the baseline files that will be committed to the repo:
# 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 iosThis writes the current permission list into permission_baseline/android.txt and permission_baseline/ios.txt. Review the contents, then commit both files:
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:
dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
permission-guard android \
--apk build/app/outputs/flutter-apk/app-production-release.apkFor iOS:
dart run devtools/monorepo_toolkit/bin/monorepo_toolkit.dart \
permission-guard ios \
--ipa build/ios/ipa/MyApp.ipaIf 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:
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:
# .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.apkThe 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
| Goal | What to do |
|---|---|
| Check multiple flavors | Run the guard once per APK/IPA artifact |
| Suppress a known false positive | Update the baseline and document the reason in the commit message |
| Enforce iOS entitlements too | Run permission-guard ios in a separate macOS CI job |
| Gate on specific permissions | Extend permission_guard.dart to treat certain permissions as hard errors |
Next Steps
- Set up push notifications — adds notification permissions you will want to baseline
- Configure monitoring — infra SDKs may inject permissions worth tracking