Squirreleye LLC Squirreleye Service Notes Naked Labs Mirror · Revival Series Index Tools About
13
Section 13 · follows 12

The prompt that stopped me

Written
2026-05-16
Log
NakedMirrorDebug/logs/current.txt

v0.5.0’s diagnostic gate produced exactly the kind of byte I’d been hoping for. The pre-flight read of mirrorExternalState came back .error (0x9), the follow-on read of mirrorError came back .noInternet (0x3), and for the first time in the entire project’s history I had a specific, named, actionable failure cause. Then I spent the next two hours finding out the answer required a layer of access I didn’t have.

This post is about what I learned in those two hours, why opening the case is now the path, and what I’m picking up tomorrow.

§ 13.1

The verdict the DeviceState gate produced

The relevant lines from the log:

17:42:23.829 INFO HTTP 200 body={"status":"ok","version":"0.5.0"}17:42:23.878 INFO wifiConnectionStatus = .connected (0x1 = 1 dec)…18:30:25.065 INFO Pre-flight mirrorExternalState = .error (0x9 = 9 dec)18:30:25.127 ERR DeviceState = .error. mirrorError = .noInternet (0x3).

Two bytes, three sentences of meaning:

  1. WiFi association is fine. wifiConnectionStatus = .connected. The mirror’s radio is talking to the access point.
  2. The mirror itself thinks it has no internet. mirrorError = .noInternet. Whatever the firmware’s check is, it’s failing.
  3. The mirror refuses all scan actions until that flag clears. This is why every alignScale for the past two weeks has been rejected at the device-state layer with no accompanying scanError.

The fix is: make the mirror’s internet check pass. Which means: figure out what the check is.

§ 13.2

What the network actually shows

I pulled out Firewalla, a Firewalla Gold sits on my LAN, and looked at the mirror’s full outbound profile. With nothing else running on the device, just a fresh boot and an idle scan-init UI, the mirror reaches out to four services in the first sixty seconds:

12:45 PM tunnel.us.ngrok.com (4 separate flows in 60 seconds, retry pattern)12:45 PM dns.google.com (DoH, DNS over HTTPS)12:46 PM s3.amazonaws.com (single hit, small)12:46 PM api.logdna.com (telemetry)

What’s not in the list is the surprise: api.nakedfit.net. The domain I’d been planning the entire intercept strategy around, the one I’d written into Ref/SCAN_WORKFLOW.md as the upload-coordination endpoint, the mirror does not contact on its own. That domain is touched by the Android app during scan initiation (the token fetch step), not by the mirror’s firmware at idle.

The mirror’s internet check is something else, and the retry pattern on tunnel.us.ngrok.com is the clue. Four separate flows to the same host in sixty seconds is failure-and-retry behavior. A successful tunnel registration would establish one persistent connection that stays open. Four short-lived flows means: TLS handshake, application-layer rejection, reconnect.

Naked Labs almost certainly used ngrok to maintain inbound tunnels into the fleet for remote support, a common pattern in industrial IoT. The tunnel registration requires an auth token tied to a specific ngrok account. Their account is now expired or deauthorized. The mirror’s firmware keeps trying to register, the registration keeps failing, and the firmware concludes: no internet.

It’s the right verdict from the firmware’s perspective (“I can’t reach the place I’m supposed to reach”). It just happens to be wrong about what kind of internet failure is happening.

Fig. 13-1
sequenceDiagram
    participant Mirror
    participant Ngrok as tunnel.us.ngrok.com
    loop every ~15s, 4x in 60s
        Mirror->>Ngrok: TLS handshake + tunnel registration
        Ngrok-->>Mirror: rejected (account expired/deauthorized)
    end
    Note over Mirror: concludes "no internet"
    Note over Mirror: mirrorError = .noInternet, all scan actions blocked
§ 13.3

Why the obvious intercept doesn’t work

My plan from earlier that week was: override DNS for api.nakedfit.net at the LAN layer, run a local HTTPS server, capture and replay the request. Firewalla supports per-device DNS overrides. Simple.

Three problems with that plan, in increasing severity:

  1. api.nakedfit.net is the wrong target. The mirror doesn’t contact it. Overriding it does nothing.
  2. DNS overrides on the LAN don’t apply. The mirror uses DoH to dns.google.com, encrypted DNS queries over HTTPS that bypass the LAN resolver entirely. To force the mirror onto LAN DNS, I’d have to block dns.google.com first at the firewall.
  3. The real target is ngrok, and ngrok’s tunnel protocol is proprietary, binary, and certificate-pinned. I can’t just stand up a local server pretending to be tunnel.us.ngrok.com, the mirror’s ngrok client will reject my cert during the TLS handshake. Reproducing the protocol meaningfully is a 1-2 week project at minimum, and I’d still have to decide what shape of response satisfies the firmware’s “is the tunnel registered” check.

None of these are solvable with what I have on the outside of the mirror.

§ 13.4

What’s behind the SSH prompt

The mirror has SSH open on port 22. OpenSSH 7.6, both publickey and password accepted. I checked, methodically, in this order:

  • Existing SSH keys on my Mac (any prior trust). None matched.
  • Eight standard usernames (root, naked, admin, support, ubuntu, pi, nakedlabs, squirrel) via pubkey-only, all rejected.
  • Twenty common default logins (root with root, root with an empty password, naked with naked, support with support, pi with raspberry, and others in that spirit), all rejected.
  • Thirteen project-specific guesses (root with squirrelfit, root with nakedfit, root with the device’s own identifier, service with nakedfit, and similar), all rejected.

Thirty-three attempts. Nothing. Naked Labs did basic security hygiene on their fleet: no default logins, no obvious patterns, no shipped-in-APK service accounts (I grepped the decompile for SSH/RSA/key literals; there are none).

I also asked the same question of public sources. There’s a Reddit thread on r/3DScanning from 2 years ago where a user named aDutchofMuch posted that they had successfully logged in to the Poky Yocto OS on the Naked scanner and had been looking through run files. They didn’t say how. Bob Free, Naked Labs’ founding VP of Engineering, employee #1, who personally wrote the cloud/web/IoT/mobile code, replied saying he can’t help with reverse-engineering until the IP owner authorizes. The IP owner is Prism Labs (Naked rebranded, pivoted to iPhone-camera scanning, indifferent to the legacy hardware). The thread is dormant.

So I got the most informative possible result from the SSH attempts: the mirror’s root login is non-trivial, and the community has the answer but it’s locked behind people who can’t share it.

That ends the non-invasive path. I’m not finding the login by guessing; I’m not finding it on the internet; I’m not getting it from Bob Free.

§ 13.5

Two corrections to my model

Two beliefs I’d been operating on got revised today:

The mirror runs Poky Yocto Linux, not Android. aDutchofMuch’s comment said “the Poky Yocto OS.” The OpenSSH 7.6 + dnsmasq 2.78 fingerprint matches Yocto Pyro (April 2017) or Rocko (October 2017), both shipped exactly those versions. The mirror is a Linux box, not an Android one. That changes the toolbox once I’m in: standard systemd, standard ssh, standard /etc/hosts. No adb, no Dalvik VM, no Android-specific quirks.

The compute board is an H110-SF100. Visible in whoamarcos’s teardown photos as a labeled mini-ITX-class industrial board: Intel H110 chipset, LGA 1151 socket (so a 6th/7th gen Celeron CPU), DDR4 SO-DIMM RAM, M.2 NVMe/SATA SSD storage. Commodity x86 hardware. Boots with a standard UEFI/BIOS, almost certainly GRUB-based.

Both corrections matter for the path forward.

§ 13.6

And one architectural finding that does not fix itself

The DisplayPort cable visible in the teardown photos is not driving a consumer-accessible display. The mirror has no screen on its outer surface, just the LED ring. The DP cable is going to some internal service/dev port I haven’t characterized. Which means the boot-interrupt plus GRUB-edit path is blocked: I’d be typing kernel-cmdline edits without being able to see the console. Possible, maybe 20-30% reliable, but not the path of first resort.

§ 13.7

What’s left

The remaining viable path is also the most surgical: pull the M.2 SSD, mount it externally on my Mac (via a UTM Linux VM, since macOS can’t write ext4 natively), add my public key to /root/.ssh/authorized_keys, put it back, SSH in.

I’ve documented the full procedure in Ref/PHYSICAL_ACCESS.md: teardown steps from whoamarcos’s notes, what to look for inside, how to identify the SSD form factor, the M.2-to-USB adapter needed (combo NVMe+SATA, since I don’t know which interface this one uses), and the Mac-side mount-and-edit procedure. The procedure is reversible until the moment I write to the filesystem, and even then the SSD just goes back in and the device boots normally, no irreversible commit.

Once I have root, the project unblocks substantially. I can:

  • Find what code path sets MirrorError.NO_INTERNET (some systemd unit, probably tied to ngrok-client status), disable it, or make it always succeed.
  • Locate the actual scan-upload script, probably a Python or shell wrapper that PUTs to a hardcoded S3 bucket. Read it. Understand its request shape. Either rewrite its target locally or redirect S3 to my LAN coordinator with my own cert installed in the mirror’s trust store.
  • Replace the inferred-from-Android-client model in Ref/SCAN_WORKFLOW.md with observed reality.
§ 13.8

What I learned

A few takeaways for the series:

  • The right answer is sometimes also the most involved answer. A week of careful protocol audit, instrumentation, version stamping, and streaming pipelines all paid off, but the last 20% of the problem still required physical access. Refusing to open the device would have stranded the project at a wall built out of someone else’s encrypted business logic.
  • The byte-level signal that closes a hypothesis is worth weeks of speculation. mirrorError = .noInternet (0x3) is two bytes. Before v0.5.0 landed those bytes, I’d been building plan scenarios around the wrong endpoint. After they landed, the path forward shifted within an hour. The streaming-pipeline-plus-precondition-gate was specifically designed to make this kind of byte legible, and it earned its keep.
  • Public sources aren’t always alive. Sometimes the community has the answer, has posted it, and has moved on, and the trail goes cold not because the answer is wrong but because the people who knew it have left the room. Plan around dormant communities the way you plan around defunct vendors.
  • Naked Labs did basic security hygiene. No shipped-in logins, no default passwords. That’s inconvenient from where I’m sitting, but it speaks reasonably well of how the original engineering team built the fleet. The thing that broke this product was the cloud shutdown, not the device’s posture.
§ 13.9

What’s next

Tomorrow:

  1. Open the mirror per whoamarcos’s teardown notes.
  2. Pull the M.2 SSD.
  3. Add my public key, reassemble.
  4. Get a shell.
  5. Find the ngrok-check code and disable it.
  6. Watch DeviceState transition from .error to .idle for the first time in this project’s history.

If everything goes right, that unblocks alignScale. Then I find out whatever’s behind the next door.

§ 13.10
§ 13.11
Agent prompts Procedure 13-A as prompts for a coding agent · 4 prompts

Paste into Claude Code or a similar agent, one at a time, from the root of your iOS project. Each prompt maps to the step with the same number above.

1
Confirm the device-state error first
Before building any network-intercept theory, read mirrorExternalState (MirrorUUIDs.mirrorExternalState, 82B0745A) and, if it reports .error, mirrorError (25E29F6F) from NakedMirrorDebug's stepDryRunScan pre-flight. If mirrorError decodes to .noInternet, treat that as the firmware's own belief about its connectivity state — a different problem from a failing BLE write — and use it to scope the next investigation to the device's actual network dependencies rather than the BLE protocol.
2
Profile the mirror's outbound traffic
Capture the mirror's full outbound network profile for the first 60 seconds after boot using a device-aware firewall or a packet capture tool (e.g. tcpdump on the LAN segment the mirror is on). Enumerate every distinct host contacted — do not assume it is api.nakedfit.net just because that hostname is referenced in the BLE/APK audit notes (Ref/SCAN_WORKFLOW.md). Look specifically for a retry pattern (multiple short-lived flows to the same host within the capture window), which indicates a failing registration or handshake rather than a one-off call, and record the actual host list in Ref/SCAN_WORKFLOW.md.
3
Check for DNS-over-HTTPS
In the packet capture of the mirror's outbound traffic, check whether the device queries a known DoH endpoint (e.g. dns.google.com) over HTTPS instead of using the LAN's resolver. If it does, document in Ref/SCAN_WORKFLOW.md that a LAN DNS override will not redirect the mirror's traffic on its own, and that the DoH host itself would need to be blocked at the firewall before any LAN DNS override could take effect.
4
Test the suspected endpoint for cert pinning
For the suspected blocking endpoint found in the traffic capture, stand up a local server answering on the same hostname/port and attempt the TLS handshake the mirror would perform. If the handshake fails against your own certificate, document the service as certificate-pinned (not spoofable from the network) in Ref/SCAN_WORKFLOW.md, and recommend that further work target local/on-device access rather than continuing to impersonate the endpoint over the network.