flashgate
flashgate answers one specific question: the firmware you just built — does it actually run on the board?
It automates the whole chain: compile, flash over ST-Link, wait for the board to report its own identity, then drive the actual feature over the console and assert on hardware register readbacks. Any broken step ends with a non-zero exit code and a reason.
build → flash over ST-Link → board boots → evidence → probes → exit code
How the board testifies
Two evidence channels, picked per bench (evidence.mode: uart / swd / auto):
The firmware prints its identity over the console UART, and also publishes a 64-byte signature (magic + git sha + build time + CRC32) at a fixed RAM address that the host reads through the ST-Link alone — no serial cable needed for boot verification:
FLASHGATE-BOOT board=apollo-h743 git=2c58bd3 build=2026-08-28T07:26:35Z rtos=FreeRTOS
Both carry the git sha plus -dirty when the tree differs from HEAD, so a
passing verify proves the board is running exactly the code you're looking
at. Functional probes then send real commands and assert on the answers,
including register readbacks (TIM3 CCR), not firmware self-reports:
- send: "led0?"
expect: "OK led0 state={state} ccr={ccr:d}" # mirror of the firmware's printf
assert: "state == BREATH and ccr <= 1000"
Quick start
pip install -e . # Python 3.11+
pip install -e ".[mcp]" # optional MCP server
flashgate doctor # ST-Link / serial / toolchain sanity
flashgate verify --all-probes # build → flash → evidence → sha → probes
echo $? # 0 = the board confirms it works
Requires an ST-Link; a USB-TTL adapter on the console UART adds probes.
The repo ships a complete buildable example for the ALIENTEK Apollo
STM32H743 (examples/apollo-h743/) with the Stop hook pre-wired, so a
fresh clone verifies out of the box once wired up.
Exit codes
| code | meaning |
|---|---|
| 0 | verified |
| 1 | build failed |
| 2 | flash failed |
| 3 | board stayed silent (no banner / no signature within timeout) |
| 4 | error string seen on serial (HardFault, assertion) |
| 5 | on-board identity ≠ repo state (git sha or board name) |
| 6 | environment error (no ST-Link / serial / tools) — including probes explicitly required via --probe/--all-probes but the console UART is unavailable: a check that cannot run never counts as a pass |
| 7 | functional probe failed |
The Stop hook
hooks/flashgate_stop.py is a Claude Code Stop hook (any harness
implementing the same hook contract works). When the agent tries to finish after
touching watched firmware files, the hook fingerprints the tree and allows
instantly if that exact state already passed hardware verify (~0.7 s
cached). Otherwise it runs the full verify on the real board and blocks
the stop on failure, feeding the agent the board's testimony:
[flashgate] BLOCKED (attempt 1/2): firmware changes are not verified on hardware (verify rc=7).
[flashgate] last verify output:
step 1: led-demo> led0 breath
board: OK led0 state=OFF ← readback exposes the silent no-op
The same broken tree is blocked at most twice, then released with a loud warning — the session can never wedge, and a failure is never silently swallowed.
Install as a Claude Code plugin
The repo doubles as a Claude Code plugin: the Stop hook, an MCP server, a board-integration skill and /flashgate commands, all at once:
pip install git+https://github.com/Lion-1209/flashgate # CLI + hook runtime
pip install "flashgate[mcp] @ git+https://github.com/Lion-1209/flashgate" # + MCP
Then inside Claude Code:
/plugin marketplace add Lion-1209/flashgate
/plugin install flashgate@flashgate
The hook needs to know your board profile: set FLASHGATE_BOARD to your
board yaml (or a repo boards/ default is used when present). Without a
profile the gate stays idle and says so.
MCP server
{ "mcpServers": { "flashgate": {
"command": "flashgate-mcp",
"args": ["--board", "/path/to/boards/apollo-h743.yaml"] } } }
board_info, doctor, build, flash, verify, probe, console_send, console_read. Any MCP-capable agent can drive the board directly. mcp 1.x and 2.x supported.
Demos
Real-hardware recordings, indexed in demo/: doctor, green-path verify, boot-timeout catch, probe catching a silent no-op, Stop-hook escalation — plus the full session of a real Claude Code agent getting blocked, diagnosing the firmware↔profile contract, fixing both sides, and passing on hardware (24 MB GIF, release asset).
Board profiles
One yaml per board (boards/): build command, artifact, flash address,
serial adapter hints, banner template, probes, watch globs. The console-side
USB adapter is a property of your bench, not the board — port resolution
goes explicit serial.port / FLASHGATE_SERIAL_PORT, then VID/PID hint,
then the sole serial port, with the banner match as the final identity
proof. See the guide for the
firmware-side integration recipe (three levels, with code) and the
full profile field reference.
Status
Boot gate, probe gate, Stop hook, MCP server, SWD signature channel — all implemented and validated on real hardware. Windows-first; Linux/macOS untested.
License
MIT
中文说明
flashgate 回答一个很具体的问题:刚编译出来的固件,烧到板子上到底能 不能跑。它把整条链路自动化:编译、ST-Link 烧录、等板子报告身份、经 串口实际调用功能并断言,任何一步断了就以非零退出码结束。
完整的使用说明在 docs/GUIDE.md:接线、安装、第一次 验证、给自己的板子写档案、探针写法、Stop hook 和 MCP 的配置、排错。
要点:
- 板子有两种方式自证:串口 banner,或者在固定 RAM 地址发布 64 字节 签名(后者只靠 ST-Link 就能验证,不用串口线)
- 版本身份带
-dirty语义,验证通过意味着板上跑的就是当前工作区 - 探针下真命令、断言硬件寄存器读回值;响应报的是实际状态不是回声, 静默失效的设置第一步就会露馅
- Stop hook 挂进 Claude Code:agent 改了固件没过真机验证就说"完成", 会被拦下并收到板子的失败证词;同一棵坏树最多拦两次,之后放行但 打警告,会话不会被卡死
- 仓库带完整的示例固件(examples/apollo-h743),接好线 clone 下来 就能跑通第一次验证
pip install -e ".[mcp]"
flashgate doctor # 体检
flashgate verify --all-probes # 完整验证
echo $?
六个真机演示 GIF 在 demo/,包括一段完整的 Claude Code 会话: agent 改固件、被拦、自己定位到固件与板卡档案的契约不一致、两侧改齐、 真机通过后放行。
MIT License
Release files for flashgate 0.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| flashgate-0.4.2.tar.gz | 31.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flashgate-0.4.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.4 kB
Release files / flashgate-0.4.2.tar.gz
| Download URL | flashgate-0.4.2.tar.gz |
|---|---|
| Size | 31.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cb2db7634ca5bd775e2bbb94f7bb1143ab4914ea62e1634f6df00761f708bccf
|
|
BLAKE2b-256 checksum How to use checksums |
171ae43cf7fabdc48dc54c38b759884320cd6f18e2a299fc2eadfdde804bc2f3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / flashgate-0.4.2-py3-none-any.whl
| Download URL | flashgate-0.4.2-py3-none-any.whl |
|---|---|
| Size | 27.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a83310487abae91e49c268b2580084d0f999f337c9401371f9a506471b372cd5
|
|
BLAKE2b-256 checksum How to use checksums |
7ab6d8fd170f9cde17c5bb8ca801856cfd56f8c60c8887638e6599b4b97c4cf4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency log