Skip to the content.

Evidence capture — Appmiral app cohort: APK harvest + integration & live-config audit (2026-06-28)

Forensic snapshot of every Appmiral / Appstrakt app installed on the investigator’s phone, pulled byte-for-byte from the device as APKs, then statically scanned for the integrations Appmiral advertises on https://appmiral.com/features/integrations, and — for every app carrying the Crowd Connected location SDK — read for its live, server-pushed config: the active geofence km range and whether the remote kill-switch is ON or OFF.

This extends and supersedes the earlier static 114-app inventory and the hand-picked “12-app CrowdConnected club”: the cohort is re-enumerated from the live device (140 apps now installed), complete split-APK sets are pulled, and the tracking analysis is driven by the actual integration scan rather than a preset list.

The APK bytes (375 files, ~7.08 GB) are not committed to this public repo — they live in the companion private repo GraafG/tt-festival-app-evidence under apks_2026-06-28_154246/. Their byte-for-byte integrity is attested here by apk-manifest.sha256, which is identical to the private folder’s SHA256SUMS.txt. This mirrors the repo’s existing two-repo model (see ../CONTRIBUTING.md).

Snapshot summary

Field Value
Captured (local) 2026-06-28 15:42–15:59 CEST (UTC+02:00)
Capture method adb pull of exact on-device install bytes (no root needed for APKs)
Device Pixel 10a, Android 16, Magisk root, build …/stallion:16/CP1A.260505.005/15081906
ADB serial 5B161JEA308016
Apps harvested 140 (appmiral.*, be.appstrakt.*, pl.mysticfestival, com.triple.verstappen)
APK files 375 split-APK files (base + split_config.*)
Total APK bytes ~7.08 GB (private repo)
Crowd Connected DBs read (root) 6 apps with a live, populated config

Tooling, exact versions, and the full reproduction walk-through: ../../docs/appmiral-comparison/10-apk-harvest-reproduction.md.

What’s in this folder (public)

appmiral-apks_2026-06-28_154246/
├── README.md                 ← this capture log
├── SHA256SUMS.txt            ← SHA-256 of every file in THIS (public) folder
├── apk-manifest.sha256       ← SHA-256 of every harvested APK (== private SHA256SUMS.txt)
├── .capture-timestamp
├── data/                     ← machine-readable derived tables (frozen copies)
│   ├── installed_apps_2026-06-28.csv          ← 140 installed vs the old 114 inventory (+26 new)
│   ├── apk_files_manifest_2026-06-28.csv      ← 375 rows: pkg, versionName/Code, file, size, sha256, on-device path
│   ├── integration_matrix_2026-06-28.csv      ← app × 51 integration/SDK presence matrix
│   ├── integration_matrix_2026-06-28.identify.csv  ← per-app id: label, versions, minSdk/targetSdk, ABIs, location perms
│   ├── integration_matrix_2026-06-28.json     ← matrix + the exact matched byte-patterns (audit trail)
│   ├── cc_live_config_2026-06-28.csv          ← 6 apps: kill-switch, km radius, centre, cadences (identifiers redacted)
│   └── cc_live_config_2026-06-28.json
└── logs/
    ├── tool-versions.txt                       ← OS, adb, aapt2, Python, androguard, sqlite (captured programmatically)
    ├── evidence_header_2026-06-28.txt          ← device/build/wall-clock header
    └── appmiral_packages_2026-06-28.txt        ← the raw 140-package list as enumerated on device

The APK bytes (apks_2026-06-28_154246/<pkg>/*.apk) are in the private repo. The Crowd Connected Room databases (which contain device/account identifiers) are also private (cc_dbs_2026-06-28_154246/); only the redacted decoded config is published here in data/cc_live_config_*.

Headline findings

1. Advertised vs. actually-embedded integrations

Static scan of all 140 apps against Appmiral’s published integration catalogue (byte-scan of DEX + resources + native libs + manifest; presence-only, with the matched pattern recorded for every hit in the .json). Counts out of 140:

Integration Apps /140 Note
Instagram, Firebase (Analytics/Messaging/Crashlytics), Google Fused Location 140 platform baseline — in every app
YouTube, AWS Cognito, CM.com, Beatswitch, AWS Kinesis Firehose, Google Maps SDK 139 near-universal
Ticketmaster 137  
Spotify 128  
Get Moments 114  
Facebook SDK 52  
OneTrust (consent) 40  
Permutive (CDP) 36 variable adoption
Mapbox 16 variable adoption
Billetten 10  
Crowd Connected (literal pkg/host) 9 obfuscated away in most — see below
Tealium 2  
TikTok, Azure, Sports Alliance, ShowPass, Eventim, Ticketpay, Tickoweb, Bleep, SeeTickets, WeezEvents, GET, Mobile Locker, Insider, Fanalist, PwC, Halo by CM, Inventis, Jebbit, Komo 0 advertised but not present in any installed app

Two structural facts this exposes:

Location permissions: all 140 request fine + coarse location; 30 also request background location.

2. Live config — km range + kill-switch (the 6 apps with a populated CC DB)

Read with root from each app’s CrowdConnected-RoomDatabase (new SDK) or shared_prefs/CrowdConnected.xml (legacy heartbeat). Identifiers redacted.

App Kill-switch Active km range Geofence centre Schema
com.appmiral.ttfestival 🔴 ON 60 km 52.99, 6.56 (Assen) new Settings.active=1, Mode=BACKGROUND, SDK 2.1.0
com.appmiral.mdlbeast OFF 60 km (armed) 21.49, 39.19 (Jeddah) new Settings.active=0
com.appmiral.rockforpeople OFF 40 km (armed) 50.25, 15.84 (CZ) new Settings.active=0
com.appmiral.teamnl OFF 50 km (armed) 45.45, 9.16 (Milan) new Settings.active=0
com.appmiral.museumnachtamsterdam OFF 80 km (armed) 52.36, 4.89 (Amsterdam) legacy activeAPIResponse=120,false,…
com.appmiral.dutchgp OFF — (no zone cached this run) — legacy activeAPIResponse=120,false,…

Only TT Festival was live (active=1) at capture time, with a 60 km collection ring centred on Assen — far wider than the festival grounds — and the SDK in background mode. The kill-switch is server-pushed: every other app carried an armed geofence (40–80 km) but was toggled OFF by Crowd Connected’s backend, demonstrating the on/off is a remote decision, not a local one. Config is fetched by the SDK from its backend during normal polling; we only read the cached result locally and never contacted Crowd Connected.

Determinism caveat: server-pushed config drifts over time, and only apps that have been launched have a populated DB — so a replicator re-running this later may see different switch states / different apps with cached config. That is expected and itself documents that the operator controls the switch remotely.

Full analysis: 08-integration-matrix and 09-active-config-km-killswitch.

Verify integrity

This (public) folder:

sha256sum -c SHA256SUMS.txt          # Linux/macOS, run inside this folder
Get-Content SHA256SUMS.txt | ForEach-Object { $h,$p = $_ -split '  ',2
  if ((Get-FileHash -Algorithm SHA256 -LiteralPath $p).Hash.ToLower() -ne $h) { "MISMATCH: $p" } }

The APK bytes in the private repo, against the public manifest:

# inside the private repo's apks_2026-06-28_154246/ folder
sha256sum -c /path/to/this/folder/apk-manifest.sha256

apk-manifest.sha256 here is byte-for-byte identical to apks_2026-06-28_154246/SHA256SUMS.txt in the private repo, so this public record independently attests exactly which APK bytes were captured.

Reproduce

Everything was produced by the documented, parameterized scripts in ../../tools/apk-harvest/ (no hard-coded serial; idempotent). Follow the step-by-step guide 10-apk-harvest-reproduction.md — prerequisites, exact commands, expected outputs, checksum verification, and the determinism caveats above.