Blog ยท 11 June 2026
Teaching a virtual user to find fake keys
blep guides you to a lost Bluetooth device by signal strength โ warmer, colder, turn left, walk. The awkward part: how do you test that? You can't unit-test a hallway. Here's how blep regression-tests the whole experience on a plain JVM, without a single real Bluetooth packet.
You can't unit-test a hallway #
RSSI โ the signal-strength number every Bluetooth scan gives you โ is noisy, multipath-riddled, and shaped by whatever's between you and the device, including your own body. blep's guidance is a feedback loop on top of that mess: filter the signal, detect the trend, decide a phase (calibrate โ sweep โ walk โ pinpoint), say something useful on screen.
Tune one threshold and you've changed the behaviour of the whole loop. Testing that by pacing an actual corridor with actual keys takes twenty minutes per attempt, isn't reproducible, and definitely doesn't run in CI. For a long time the choice seemed to be: ship tuning changes on vibes, or don't ship them.
But blep's core is deliberately pure Kotlin with zero platform
dependencies โ the same engine compiles for Android, iOS, Wear OS and
watchOS. Nothing in the tracking loop knows what a Bluetooth stack is; it just
consumes (rssi, heading, steps) samples. And anything that consumes
plain numbers can be fed simulated numbers.
A pocket universe in ~80 lines #
The simulator's World is a tiny physics model that produces one
thing: the integer dBm a phone would read at your current position and heading.
It stacks the effects that matter in real life:
- โขLog-distance path loss โ the textbook
txAt1m โ 10ยทnยทlogโโ(d)falloff, with a tunable environment exponent. - โขBody shielding โ a cosine lobe that attenuates up to ~10 dB when the target is behind you. This is the effect blep's whole technique rides on, so the simulation must model it.
- โขWalls โ line segments that knock ~16 dB off any reading whose line-of-sight crosses them.
- โขForest canopy โ position-dependent fading (interfering sine fields), the "why is it cold here but warm two steps away" effect.
- โขSlopes and floors โ the world is 3-D, so "one floor up" is a real, structurally unsolvable scenario (you can't walk through a ceiling).
- โขA moving target โ the device itself can drift, like a tag on a wandering dog.
- โขDeterministic noise โ interfering sines instead of a random generator, so every run of a scenario is exactly reproducible. A flaky physics test helps nobody.
The user is a regex #
Here's the part I like most. The virtual user doesn't peek at the engine's internal state โ that would test what the engine thinks, not what it says. Instead it reads the actual on-screen guidance strings, the same ones a human sees, and obeys them literally: "turn 30ยฐ left" โ turn 30ยฐ left. "keep going" โ walk a step. "hold at your chest" โ stand still.
That closes the loop end-to-end: world โ signal model โ RSSI filter โ phase machine โ spatial tracker โ copywriting โ simulated legs โ new position โ world. If a phase transition fires too early, if the turn hint says left when it means right, if the pinpoint copy never triggers โ the virtual user gets lost, and the test sees it. The guidance is only correct if a literal-minded reader can follow it to the target.
Thirteen ways to lose your keys #
make sim runs the suite โ thirteen scenarios from trivial to
hostile โ and prints a table. This is real output from the current build:
โโ tracking simulation (timeout 180s) โโโโโโโโโโโโโโโโโโโโโโโโโโ
scenario straight closest found walked path-eff vol result
ahead 5 m 5.0 m 0.2 m 12s 8.8 m 0.6ร 2.2 โ
clean
behind 8 m 8.0 m 0.2 m 14s 39.3 m 0.8ร 2.1 โ
clean
to the side 6 m 6.0 m 0.0 m 14s 6.0 m 0.8ร 1.3 โ
clean
diagonal 14 m 14.1 m 0.3 m 38s 47.4 m 2.3ร 1.7 โ
clean
far 20 m 20.0 m 0.2 m 22s 42.7 m 0.9ร 2.0 โ
clean
extra-far 35 m 35.0 m 0.1 m 32s 48.2 m 0.9ร 2.1 โ
clean
through a wall 9.0 m 0.0 m 15s 28.9 m 0.8ร 2.2 โ
clean
up a slope 12.1 m 0.0 m 17s 17.3 m 0.8ร 1.9 โ
clean
forest 12 m 12.0 m 0.0 m 18s 56.0 m 0.9ร 2.6 โ
clean
noisy room 8.0 m 0.2 m 14s 17.5 m 0.8ร 3.6 โ
clean
randomized 13.0 m 0.2 m 26s 34.5 m 1.5ร 2.0 โ
clean
one floor up 5.0 m 3.0 m timeout 64.4 m 12.9ร 2.6 โ fail
moving device 8.0 m 0.5 m 27s 21.0 m 2.4ร 1.4 โ
clean
โโโโโโโโโโโโโโ clean 12/13 ยท reached 12/13 ยท avg path-eff 1.1ร โโ
path-eff is metres walked relative to a sensible search path โ it measures wandering, not just success. "one floor up" fails by design: the target is 3 m above your head and there are no stairs in the world; what's being checked is that blep gets you to the right spot under it (3.0 m closest approach โ directly below) instead of pacing in circles.
The suite is a CI gate, not a demo: the build fails if the clean count drops below 12/13, or if the trivial straight-ahead case is ever less than clean. A tuning tweak that helps the forest but breaks the hallway gets caught before it ships.
Chaos, robustness, and A/B physics #
Once the loop is closed and fast (~seconds for the whole suite), it becomes a tool, not just a test:
- โข
make chaosrandom-searches every tuning knob โ filter constants, phase thresholds, particle-filter parameters โ scoring each candidate against the suite. The current defaults came out of these runs, not out of guesses. - โข
make robustnessguards against the opposite failure: overfitting the thirteen named scenarios. It generates held-out random worlds and checks the tuning generalises to layouts it never saw. - โข
make sim-gpsis A/B physics: the same drift-prone outdoor walks with GPS fusion off vs. on, reporting mean closest-approach. It's how "does folding GPS into the local frame actually help?" became a number instead of a debate. - โข
make sim-safetypoints the same machinery at the anti-tracking side: simulated commutes where one device genuinely follows you and dozens just share your routine, checking the detector flags the right one.
The whole trick is purity #
None of this needed a test framework, an emulator, or a single permission
dialog. It needed one architectural decision, made early: the tracking engine
is a pure Kotlin Multiplatform module that has never heard of Android or
CoreBluetooth. Sensors and radios live behind expect/actual
boundaries; the logic consumes numbers.
The payoff compounds. The same purity that makes the simulator possible is why one engine ships on four platforms, why the anti-tracking detector has scenario tables instead of anecdotes, and why a contributor can verify a tuning change from a laptop with no hardware at all:
git clone https://github.com/fentas/blep.fyi
cd blep.fyi
make sim
The keys are fake. The walls are fake. The user is a regex. The confidence is real.