Inspiration

LibChecker makes Android packages easier to understand, but package inspection should not require installing another Android app or uploading a private APK to an analysis service. This project began as a Telegram bot that could inspect APK download links. That flow worked well for quick previews, but local files needed a safer and more capable home.

LibChecker WebUI brings the same kind of package insight to any modern browser. A developer can open the page, drop in an APK, and inspect it without sending the original file to a server. The browser also makes the tool useful on desktop systems, iOS, and other devices where the Android app is unavailable.

What it does

LibChecker WebUI analyzes APK, APKS, APKM, XAPK, and LCAPPS files. Local package analysis runs inside a Web Worker, so the original file stays in the browser. For direct download links, the app can use HTTP Range requests to read the metadata it needs without downloading the full APK when the host supports ranges.

Each report includes app and package information, SDK levels, build features, permissions, components, native libraries, application metadata, and signing certificates. It also matches native libraries and Android components against generated LibChecker rules, then shows the detected SDKs with their names, icons, developers, contributors, and source links. Native library details include architecture and 16 KB page alignment information.

Reports are saved in local browser history and can be reopened in list or grid view. Compare mode accepts two uploads or history entries and highlights added, removed, moved, and changed items. Users can export report JSON or create a public report link. Sharing publishes the structured report only, not the APK file.

How we built it

The repository is an npm workspace with three parts: a Cloudflare Worker for the Telegram bot and report storage, a Cloudflare Pages app for the WebUI, and a shared analyzer used by both runtimes.

The frontend uses vanilla JavaScript modules, Vite, HTML, and CSS. File parsing runs in a dedicated Web Worker to keep large packages from freezing the interface. The shared parser reads ZIP structures, binary Android manifests, resource tables, DEX markers, ELF headers, APK signing blocks, and X.509 certificates using browser-compatible APIs. It can select the main APK from split-package containers while collecting useful data from the other splits.

LibChecker rules and SDK icons are generated into local bundles. The analyzer loads those larger catalogs only when a report needs them, which keeps the first screen small. Report rendering and compare mode are lazy-loaded for the same reason.

Cloudflare Pages Functions handle remote URL reports and stream analysis progress back to the page. The Telegram Worker uses Hono, stores opt-in shared reports in R2 behind opaque references, and serves the same report model back to the WebUI. Shared contracts and locale catalogs keep the browser, Worker, bot, and report links consistent.

We test pure parsing code with Node's test runner, Cloudflare-specific behavior with the Workers Vitest pool, and browser interactions with Playwright in Chromium and WebKit. Automated axe checks cover the first screen, completed reports, and populated compare views. Build scripts also enforce JavaScript, worker, request-count, and gzip-size budgets.

Challenges we ran into

Android packages are designed for Android tooling, not for a browser. We had to parse binary XML, resource references, adaptive and vector icons, signing schemes, and native ELF metadata without relying on Node.js or Android framework APIs. The same shared code also had to run inside Cloudflare Workers.

Package containers added another layer. APKS, APKM, and XAPK files may contain many split APKs, and the most obvious file is not always the correct base package. Some entries are stored while others are deflated, and remote link analysis cannot always inspect a compressed inner APK without fetching the whole container. We added explicit selection rules and limited diagnostics so the UI does not present an incomplete remote preview as a full analysis.

Performance was another constraint. The LibChecker rule and icon catalogs are valuable, but loading all of them on the first screen would make the app feel heavy. Splitting core and detail data, moving analysis off the main thread, compacting browser history, and enforcing build budgets kept the interface responsive.

The privacy boundary also needed care. Local analysis had to remain fully local, URL analysis needed strict validation and request limits, and report sharing had to be an explicit action with a validated payload.

Accomplishments that we're proud of

We built a useful Android package analyzer that runs from a static browser page and produces much more than a manifest dump. It reconstructs app icons, reads modern APK signatures, identifies SDK markers, understands common split-package containers, and presents the results in a report that works on both desktop and mobile browsers.

The same analyzer and report contracts now power local WebUI analysis, remote URL reports, Telegram previews, history, comparisons, and public report links. That shared foundation prevents each surface from developing a slightly different interpretation of the same APK.

We are also proud of the less visible work: runtime-specific tests, cross-browser smoke coverage, automated accessibility scans, localization support, and strict performance budgets. Those checks let us keep adding analysis depth without quietly making the first screen slower or breaking another runtime.

What we learned

Modern browser APIs are capable enough for serious binary analysis, but memory use and main-thread work have to be designed from the start. Web Workers, bounded reads, streaming progress, and lazy-loaded data made a larger difference than visual optimization alone.

We also learned that privacy works best as an architectural boundary. Local files never need to cross the network for analysis. Remote URLs belong at a server boundary where requests can be validated, while public sharing should publish only the report a user chose to share.

Finally, sharing one parser is not enough. Stable contracts, generated catalogs, runtime tests, and a common localization source are what let browser and edge runtimes evolve together.

What's next for LibChecker WebUI

Next, we want to close more of the gap between browser analysis and the Android app. That includes deeper handling of split manifests and resources, more container and ZIP64 edge cases, and richer DEX and native-library inspection.

Compare mode can also grow beyond raw added and removed items. We plan to add clearer release-oriented summaries, filters for security-relevant changes, and better export options for automated review workflows.

The WebUI already exposes a public URL-report API and machine-readable discovery documents. We want to make those integrations easier to use from developer tools and agents while preserving the current privacy model. We will also continue expanding localization, accessibility coverage, and browser compatibility.

Built With

Share this project:

Updates