`make rootfs` unsquashed rootfs.squashfs out of the stock package into
work/rootfs, and a thin container bind-mounted it with PROG_DUMP supplying
/usr/prog from the factory image. Every caller set PRINTER_IMAGE anyway --
both workflows wrote it into test.env BEFORE running `make rootfs`, so CI
unpacked a 93MB package, unsquashed a rootfs and downloaded a 182MB factory
image on every push and read none of it. build-printer-image.sh never used
work/rootfs either: Dockerfile.full does its own extraction from the same
public firmware and needs no stock package at all.
So the fallback went, and PROG_DUMP with it -- it was only ever read when the
replica was built locally, and both implementations already passed it empty
otherwise. Gone: the target, extract-rootfs.py, gates.extract_rootfs and its
two tar helpers, the thin printer/Dockerfile, the `prebuilt` flag threaded
through both Replica implementations, and Replica.start's want_output, whose
only message was that the local build was about to be slow.
PRINTER_IMAGE is pinned to 1.9.7-1.2.9-20260810 rather than :latest. It is
now the entire substrate the brick-safety gates run on and the only input
nothing checksums, so a floating tag would let it change under a green suite
-- the drift the sha256 pins in both workflows exist to prevent.
THREE THINGS THAT COULD NOT HAVE PASSED, found while doing it:
* Neither workflow built the feed. bin/patch.sh has refused to start
without one since a1b4ea6, 45 commits after ci.yml was last touched.
Both ran unpack/patch/pack straight into that guard.
* `make release` ran bin/build.sh on $(RUN), which has no docker socket,
while patch.sh assembles the payload inside the replica. `build` has
used RUNBUILD all along; `release` was left behind. It also built no
feed. It now builds one, once, outside the per-model loop -- nothing in
it is model-specific.
* ci.yml's shellcheck gate named pkgs/*/payload/init.d/S* and
pkgs/*/prog/*.sh, neither of which exists any more; shellcheck exits 2
on a path it cannot open. It is a `find` with an asserted count now,
because the opposite failure is worse: a glob that quietly matches
nothing checks nothing and still reports green.
Both workflows now go through `make` instead of hand-rolling `docker run`.
Hand-rolling meant CI exercised entry points no human uses, which is exactly
why the `release` runner bug could sit there unseen; it also ran as root and
left a work/ that the later `make release` could not write to.
ci.yml's test job builds the feed before the static lane. A good part of that
lane asks what is inside an .ipk and those questions do not fail without one
-- they return, and the lane reports a pass having never opened a package.
build-packages.sh needs no stock firmware, which is what makes that possible.
Docs, and the packages skill, which still documented pkg_buildopkg and
work/.opkg-host as how the payload is assembled -- the host opkg went in
dcd037c and the printer's own installs the feed now -- and still claimed
qa/static holds an ABI gate over every ELF in every package. It does not:
qa/replica/test_abi.py is the only one, and qa/static asserts a seventh does
not grow back.
Verified: qa/static is 122 passed / 110 errors here, identical to master --
every error is `vendor/opkg-utils is missing` in a checkout that has not run
`make vendor`. mkdocs build --strict passes. The new shellcheck gate was run
as written. The workflows themselves were not executed.
tools/replica/printer/*.sh live inside the published image, so those edits
take effect on the next `make printer-image`; behaviour is unchanged either
way, because nothing sets PROG_DUMP any more.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TrhjKabxDihvVjNQGGT4MK
Reforge
Custom firmware for the FlashForge Creator 5 and Creator 5 Pro.
It installs the way a normal FlashForge update does: copy one file to a USB stick, plug it in, power the printer on. No jailbreak, no soldering, no opening the case, no ssh needed to get started — the printer's own updater does the work.
Packages are published on the Releases page — two files per release, one per model, marked pre-release while the mod is young. Help and support: the Discord.
Unofficial and unaffiliated with FlashForge. It voids your warranty and you are responsible for your machine. See Status for what has actually been tried on hardware before you flash anything.
Status
It runs on a real printer — a Creator 5 Pro, flashed and printing:
A checked box happened on that machine; an empty one has not, yet.
For everyone:
- Install and migration from stock firmware — the printer's own updater does it; your unit's factory calibration imports itself on the first boot
- Printing — gcode straight from the slicer, nothing edited
- Toolchanges — a current Klipper with real toolchanger support, in place of the 0.12-era tree FlashForge ships
- Nozzle-offset calibration
- Mainsail at
http://<printer-ip>/, with a current Moonraker behind it — anything that speaks the Klipper API works - Camera — mjpg-streamer, already on the printer; the mod just starts it
- Wifi
- HelixScreen on the printer's own screen
- Chamber heater
- Creator 5 (non-Pro) — its package builds and passes the same replica test suite, but has not touched hardware
- Going back to stock — proven on the printer replica, not yet needed on the machine. The stock package restores everything it carries; Moonraker is not in it, so the mod's build stays and keeps working — see Support
For the tinkerer:
- ssh as root — a random password is chosen on the first install
and written to
anvil-password.txton your stick; updates keep it (make passwdbakes in your own)
What you give up
FlashForge's application is one binary, and replacing it takes everything
that lived inside it: the network API on :8898, and with it FlashPrint,
Orca's FlashForge profile and the mobile app. Moonraker's API takes over.
Flashing the stock package brings them back.
If your prints already come out the way you want and you like the printer's own screen, this mod is not for you. It trades FlashForge's polish for Klipper's reach, and it expects you to be comfortable with a Klipper config and an ssh session when something breaks — Is this for you is the honest version of that question.
Get it
Creator 5 and Creator 5 Pro are not interchangeable. The printer refuses a package built for the other model — the most common reason a flash "does nothing". Yours is in Settings → About.
| Your printer | The file on the Releases page |
|---|---|
| Creator 5 | Creator5-anvil-<date>.tgz |
| Creator 5 Pro | Creator5Pro-anvil-<date>.tgz |
Download the stock FlashForge package for your model before you begin. It is the undo button: flashing it back puts the printer as it was.
Start here
| Is this for you | Who should flash this, who should stay on stock, and what it asks of you |
| Installing | The flash, the first boot, your root password |
| Your first print | The go/no-go checks, calibration, and the two verification files |
| Support | The logs, and the way back to stock |
The full manual is at
reforge.readthedocs.io. If you want the
insides — one file, firmwareExe, with the stock boot scripts, updater and
recovery path left untouched — How it works is under
For contributors.
Credits
Built on the work of others: ghzserg/FF publishes the stock packages and factory image everything here starts from; Mainsail, Moonraker and HelixScreen are shipped as released; the toolchanger status API follows viesturz/klipper-toolchanger so tool-aware UIs work unchanged.

