Stargazer

The telescope cost $400 on Kickstarter. Mirrosky raised three-quarters of a million dollars, shipped the hardware, and then — as Kickstarter stories go — never quite finished the software. The official app connects sometimes. The calibration flow hangs. The “signal weak” warning appears when the sensors aren’t ready and there’s no override because the firmware itself decides when to accept commands. Becky wanted to look at Saturn. The app wanted to crash.

So we decompiled it.

What’s inside

The telescope is a 127mm Maksutov-Cassegrain on an alt-azimuth GoTo mount — a serious optic wrapped in consumer electronics. Built-in 960x720 camera, GPS, IMU, compass, WiFi. It boots into AP mode, broadcasts its own network, and waits for a WebSocket connection on port 9090. The camera streams MJPEG on port 8888. The hardware is made by Hiuni and sold under at least three brand names. The optics don’t care about the branding.

How we learned the protocol

jadx cracked the APK’s Java layer — the native DeviceStatus class, the Indiclient calls into libnetmanager.so, the command gating logic. Then 627,000 lines of decompiled Hermes bytecode from the React Native side gave us the rest: command flow, state management, UI routing, the two independent gates on every command (app-side and firmware-side), the calibration sequence, the cloud API at api.mirrosky.com.

The protocol is JSON over WebSocket. Connect, authenticate, and then the telescope starts sending get_status heartbeats. Miss a keep_alive response within eight seconds and it drops you. Motor commands, GoTo coordinates, tracking modes, camera controls, focus, exposure — all documented now. The firmware has a typo in one of its command names (motor_raito instead of motor_ratio) and we faithfully preserved it in the docs because that’s how reverse engineering works. You document what is, not what should be.

What we built

A FastAPI app that proxies everything. WebSocket commands go through the backend to the telescope. The MJPEG camera stream gets relayed so Becky’s phone doesn’t need to be on the telescope’s isolated WiFi network — the app handles the VLAN boundary. Dark mobile-first UI designed for use outside at night. Live camera view, directional nudge controls, stop button. No framework on the frontend — just HTML, CSS, and enough JavaScript to pipe a video stream and wire up a d-pad.

The telescope joins the home network now instead of forcing you onto its own AP. Traefik routes the app through the standard reverse proxy. Docker stack, Woodpecker CI, same deploy pattern as everything else in the homelab.

The command locking problem

The firmware has opinions about when you’re allowed to talk to it. Three states: normal (all commands), meridian flip (only power off and live view), and home parking (only power off). The app enforces these too, independently — two gates, both of which have to agree before a command reaches the motors. The “signal weak” warning that blocks everything? Entirely firmware-side. The React Native code has no handler for it. The Java native layer has no override. The BNO055 IMU needs its figure-eight calibration dance and the GPS needs a fix, and until both are happy, the telescope sits there. Patiently. While you stand in the yard holding it like an idiot.

We documented all of this because the next person who buys one of these on eBay deserves to know.

Where it’s going

Phase 2 is cracking libnetmanager.so — the native binary that handles INDI protocol communication. If the telescope speaks INDI natively, KStars and Stellarium can drive it directly, which opens up the entire Linux astronomy ecosystem. Phase 3 is an MCP server wrapping the whole protocol. Point the telescope at M31 and take a photo — as a natural language command, through the same AI infrastructure that manages the rest of the homelab.

But the MVP is simpler than any of that. Becky opens her phone, taps a bookmark, sees the sky through the camera, nudges the telescope until Jupiter’s in frame. That’s the whole thing. A $400 Kickstarter telescope with broken software, brought back to life because someone wanted to look at the stars and the app kept crashing.