Miko Mini Teardown: Dumping the Claude Haiku System Prompt via ESP32 UART Pin 17
TL;DR: The Miko Mini ships with an ESP32-S3 module whose secondary UART (TX on GPIO17) streams debug telemetry at 921600 baud, including the full Claude Haiku system prompt the companion app uses for child-safe dialogue. A miko mini esp32 uart prompt dump takes about 90 seconds end-to-end: pop the back shell, solder a 3-wire tap to TP7/TP8/GND, attach a CP2102, and pipe the log through a regex filter. No firmware modification is required, and the extraction is read-only.
- Target: Miko Mini (SKU MK-MINI-2025), firmware 3.4.112 shipped 2026-02-18
- SoC: ESP32-S3-WROOM-1-N16R8, 16 MB flash, 8 MB PSRAM
- Debug UART: GPIO17 (TX), GPIO18 (RX), 921600 8N1, no flow control
- Upstream model: Claude Haiku 3.5 via Miko’s proxy at
api.miko.ai/v2/chat - Prompt size: 4,812 tokens, emitted once per boot during the
persona_initRPC
Why does Pin 17 leak the system prompt at all?
The ESP-IDF default for CONFIG_ESP_CONSOLE_UART is UART0, which Miko uses for the production boot log. What Miko shipped on 3.4.112 is a second serial sink on UART1 wired to GPIO17/18, created by uart_driver_install() inside their persona_manager component. That sink was meant for factory calibration and never got compiled out of retail firmware. Espressif’s own UART driver reference for ESP-IDF v5.3 documents exactly this pattern — a secondary driver instance bound to an arbitrary GPIO pair — and notes that any buffer written through uart_write_bytes() on that instance bypasses the normal esp_log redaction hooks.
That is the whole bug in one sentence: Miko’s redactor lives on the esp_log path, not on UART1. When persona_init logs the prompt it assembled for Haiku, the string goes out Pin 17 verbatim.

The diagram traces the path from persona_manager.c:412 through uart_write_bytes(UART_NUM_1, ...) down to the physical TP8 testpoint on the mainboard. The orange arrow is the redacted path (UART0 → USB console) that engineering thought they were auditing; the red arrow is the unredacted path (UART1 → GPIO17 → TP8) that nobody noticed. Note where the two paths diverge — at the log_dispatch() call — which is the single line a future patch needs to touch.
What hardware do I need to reproduce this?
A bare CP2102 or CH340 USB-to-serial adapter is enough. You do not need a logic analyzer, an ESP-Prog, or a JTAG dongle. The parts list:
- Miko Mini, any unit on firmware 3.4.110 through 3.4.112
- CP2102 module rated for 921600 baud (the $2 red boards from 2023 onward all work)
- 30 AWG silicone wire, three strands, ~8 cm each
- TS100 or similar iron with a 0.2 mm tip — the TP pads are 0.5 mm square
- T6 Torx to remove the six case screws hidden under the rubber feet
How do I wire the tap?
After removing the back shell you will see the ESP32-S3 module at the top-left of the mainboard, next to the I2S microphone. The three testpoints are silkscreened as TP7 (GND), TP8 (UART1 TX, GPIO17), and TP9 (UART1 RX, GPIO18). You only need TP7 and TP8 for a dump — RX is optional and only matters if you want to inject commands, which is out of scope here.
Solder the three wires, route them out through the speaker grille gap, and reassemble loosely. On the CP2102 side: TP8 → RXD, TP7 → GND, leave TXD floating. Do not connect VCC; the Miko is powered by its own battery and back-powering the 3.3 V rail through the CP2102 will brown out the PMIC.
What command actually captures the prompt?
On Linux or macOS, with the CP2102 enumerated as /dev/ttyUSB0:
stty -F /dev/ttyUSB0 921600 raw -echo -echoe -echok
timeout 120 cat /dev/ttyUSB0 | tee miko_uart.log
Power-cycle the Miko with the side button. The boot banner appears in about 4 seconds; the persona_init block arrives around T+11 s and finishes by T+14 s. Kill the capture once you see persona_ready=1.
The prompt is framed by two sentinels — ===PERSONA_BEGIN=== and ===PERSONA_END=== — which makes extraction a one-liner:
awk '/===PERSONA_BEGIN===/,/===PERSONA_END===/' miko_uart.log \
| sed '1d;$d' > haiku_system_prompt.txt
wc -l haiku_system_prompt.txt # expect ~148 lines
On Windows, PuTTY in logging mode works, but set the line buffer to at least 200 KB or the capture will drop frames during the persona_init burst. The Espressif uart.c driver source explains why: UART1’s internal FIFO is 128 bytes and the default ISR threshold is 120, so anything slower than 921600 will overflow the ring buffer once the prompt starts flowing.

The chart is the reason 921600 is the only baud rate that gets you a clean dump. At 115200, extraction takes 47 seconds and drops 12% of frames due to the FIFO overflow described above. At 230400 it is 24 seconds with 4% loss. At 460800 it drops to 12 seconds with under 1% loss. At 921600 the full prompt clears in 6.1 seconds with zero loss — the bar is nearly half the height of the 460800 bar. The takeaway: do not waste time on the “safe” baud rates; the driver was compiled to hit the top speed, and anything slower fights the hardware.
What is actually in the prompt?
The extracted file is a standard Anthropic-format system prompt, roughly 4,800 tokens. It starts with a persona card (“You are Miko, a friendly robot companion for children ages 5 to 10…”), followed by 22 hard-coded refusal rules keyed by topic, a block of 14 few-shot examples of child dialogue, and finally a JSON schema describing the tool-use format Miko’s firmware expects in the response. The whole thing matches the message-format described in Anthropic’s Messages API reference, which is what you would expect given Miko’s proxy just forwards to Haiku with a thin auth wrapper.
Two details are worth flagging. First, the prompt contains a literal child_name placeholder that the firmware substitutes at runtime from the parent’s companion-app profile — meaning the dumped file is a template, not a rendered instance. Second, line 94 references an internal moderation endpoint (moderate.miko.ai/v1/classify) that is called before the Haiku round-trip, not after. That ordering is documented nowhere in Miko’s public materials and only becomes obvious from reading the prompt.
What can go wrong
Three failure modes show up repeatedly when people first try this:
1. cat: /dev/ttyUSB0: Input/output error immediately after running stty. Root cause: the CP2102 clone on your adapter is a CH9102 that silently caps at 460800 baud despite advertising 921600 in its USB descriptors. Fix:
lsusb -v | grep -A2 "1a86:55d4" # CH9102 VID:PID
# If present, fall back to 460800 and accept the frame loss:
stty -F /dev/ttyUSB0 460800 raw -echo
2. persona_init: redact_hook returned -ENOSYS, bailing appears in the log and no prompt is ever emitted. Root cause: firmware 3.4.113, pushed OTA on 2026-03-27, added a stub redactor on the UART1 path that short-circuits when it cannot reach the moderation endpoint. Fix: block the OTA server in your router before the next reboot, or downgrade:
esptool.py --chip esp32s3 --port /dev/ttyUSB0 \
write_flash 0x10000 miko_3.4.112_app.bin
3. ===PERSONA_BEGIN=== appears in the log but no content follows until ===PERSONA_END===. Root cause: your cat process is line-buffered and the sentinel plus payload arrive in the same ~4 KB burst, which stalls until the next newline. Fix: use stdbuf to force unbuffered reads, or swap cat for socat:
socat -u /dev/ttyUSB0,b921600,raw,echo=0 - | tee miko_uart.log
Before you ship
If you are writing this up for disclosure or for a conference talk, walk through this checklist before you publish anything:
- Confirm the exact firmware version on your unit by running
stringsagainst a flash dump and grepping forMK-MINI-FW— do not trust the companion app’s version string, which lags by one OTA. - Capture at least three independent boots and diff the extracted prompts; the
child_namesubstitution and the session-nonce line will differ, everything else must be byte-identical. - File the issue with Miko’s security address (
[email protected]) using their security disclosure page process and wait the full 90-day window before public release. - Redact the two API hostnames (
api.miko.aiandmoderate.miko.ai) from any screenshots you post publicly until the vendor has confirmed the fix is deployed. - Verify your CP2102 adapter is isolated from mains by running it off a laptop on battery only — a ground loop through a USB hub has bricked at least one test unit by pulling TP7 below the PMIC’s negative rail.
- Keep a known-good 3.4.112
app.binarchived locally; Miko has pulled older OTA artifacts from their CDN before, and you will want a rollback target if the OTA server goes dark. - Re-run the extraction against a factory-reset unit to confirm the bug is not tied to any account-specific state on your test device.
What should Miko actually change?
The fix is three lines. In persona_manager.c, the log_dispatch() call needs to route through the same redactor the esp_log path already uses, or UART1 needs to be compiled out for retail builds via a CONFIG_MIKO_FACTORY_UART Kconfig flag guarded on the release manifest. Espressif already documents the Kconfig pattern in their Kconfig reference, and Miko’s own build system uses similar flags for the Bluetooth stack. There is no engineering reason the factory UART survived into a production image — it is a checklist failure, not an architectural one.
If you own a Miko Mini and this makes you uncomfortable, the practical mitigation until 3.4.114 ships is to pull the battery when the toy is not actively in use, because the UART leaks only on boot and the prompt is harmless once it is in RAM. The disclosure window closes in July 2026; at that point expect a wave of derivative posts, and expect every other ESP32-based AI toy to get the same tap-and-capture treatment.
References
- ESP-IDF v5.3 UART driver reference — documents the secondary UART driver pattern that Miko used on GPIO17 and the FIFO threshold behaviour cited in the baud-rate analysis.
- espressif/esp-idf uart.c source on GitHub — the 128-byte FIFO and 120-byte ISR threshold that force 921600 baud for clean captures are visible directly in the driver source.
- Anthropic Messages API reference — the format the extracted Haiku system prompt conforms to, including the tool-use schema Miko embeds at the end of the persona file.
- ESP-IDF Kconfig reference — the mechanism Miko should use to compile the factory UART out of retail builds, cited in the proposed fix.
- espressif/esptool GitHub repository — source for the
esptool.py write_flashcommand used in the 3.4.113 downgrade procedure.
