Repository navigation
v0.2.0 — research workbench
On-device Flutter app research workbench. Offline, no root, no INTERNET permission.
What changed in 0.2.0
Scanning that survives real use
- One foreground scan owner shared by the UI and the headless
am start -e scanentry, so a scan
keeps running when you leave the screen. Terminal notification when it finishes or fails. - Per-job identity: a timed-out or cancelled scan can never write into a newer report.
- Cooperative cancellation at every pipeline checkpoint, staged progress, 20-minute per-app bound.
- The installed-app inventory is cached in-process — going back from a report no longer reloads
the whole device list. - Atomic report and history writes (temp file, fsync, rename) with inflight markers, so a kill
leaves either the old report or the new one, never half of each.
A real report UI
- Navigation is now real routes: Home → Details → Section, with one Back handler, so Back from a
See-all list returns to the report, and Back from the report returns to the loaded inventory. - Three panes: Overview (grade with a stated reason, severity summary including zero rows,
artifact, extraction/Flutter-check metadata, signing, change card), Findings (expandable
rows, masked evidence, per-row reveal), Explore (endpoints, candidate route tree,
dependencies with pub.dev links, recovered paths, channels, APK artifact browser). - Long sections show a bounded window plus See all, which opens a searchable full-screen list
instead of an endless inline scroll. - Scan all Flutter apps queues every detected app behind a cost dialog, with a dedicated queue
screen: live per-app stage, determinate bar, cancel with confirmation, honest done/cancelled
counts. - Every scan is appended to a per-app, versioned, bounded scan log; change cards are computed from
it.
Evidence you can trust
- Sensitive values are masked by construction; full values live only in memory, are never
serialized, never logged, and reset when you switch apps. A report loaded from disk says
"rescan to reveal" rather than showing a dead control. - Revealing a value sets
FLAG_SECUREon that screen and marks clipboard copies sensitive. - Endpoints open exactly the URL shown, or are labelled not openable. Recovered strings that look
like concatenations are demoted, not presented as hosts. Candidate routes are grouped by prefix
and never drawn as a navigation graph. - The grade legend is derived from the actual counts: an F earned on weighted severity no longer
claims critical findings.
Exports
- Section-selected JSON or Markdown, default-redacted, with an explicit per-export opt-in for
sensitive values. Unique file names, and an unknown format is an error instead of a silent
Markdown fallback.
Design
- Semantic Material 3 tokens: one radius family, scheme-derived severity colours, one cyan accent,
a 4/8/12/16/24/32 spacing scale, 48 dp targets, light and dark both verified on device.
Requirements
Android 8.0+ (API 26). Sideload the signed APK; no Play Services needed.
Verification
217 unit tests green. Verified on a physical Redmi Note 9 Pro (LineageOS, API 35): inventory
caching, scan continuity across navigation, queue start/cancel, stored-report loading, endpoint
honesty, masked evidence, exports, light and dark rendering.
Known limits
Candidate routes are recovered strings, not a navigation path. libapp.so is AOT machine code:
you get strings, symbols and routes, not decompiled Dart. AABs are not supported. Signature
verification is structural, not a full apksigner check. Dependencies and APK artifacts still
filter inline in Explore rather than routing to their own full-screen search.