3mfctl makes a Bambu Studio project 3MF reproducible from Git-managed sources.
A .3mf file is convenient for Bambu Studio, but it is a poor source of truth for a software-style workflow: model geometry, print settings, object placement, filament assignments, and other metadata are bundled into one archive that is difficult to review in Git.
3mfctl splits that project into editable inputs and can build the 3MF again later:
existing project.3mf
|
| 3mfctl probe
v
project.3mf.yaml + assets/*.stl <-- commit these to Git
|
| edit settings / update STL
| 3mfctl build
v
project.3mf <-- generated artifact
The intended workflow is therefore:
- Create the initial print setup in Bambu Studio.
- Run
3mfctl probeonce to import the existing 3MF. - Treat the generated manifest and referenced STL files as the source of truth.
- Review changes to print settings as YAML diffs in Git.
- Regenerate the
.3mfwith3mfctl buildwhenever it is needed for slicing or printing.
The generated 3MF no longer needs to be the authoritative file in the repository. It can be treated like any other build artifact.
Install 3mfctl:
go install github.com/tingtt/3mfctl@latestSuppose original.3mf is an existing Bambu Studio project whose print settings you want to preserve.
Import it into a manifest and external STL assets:
mkdir -p print
3mfctl probe \
-o print/model.3mf.yaml \
--assets-dir assets \
original.3mfBecause --assets-dir is relative to the manifest, the result looks roughly like this:
print/
├── model.3mf.yaml
└── assets/
├── ... .stl
└── ... .stl
model.3mf.yaml contains the project topology, object placement, filament assignments, Bambu Studio metadata, and typed project/preset configuration. The STL files contain the mesh geometry referenced by the manifest.
Commit those files:
git add print/model.3mf.yaml print/assets
git commit -m 'Add printable project sources'At this point, the Git-managed manifest and STL files are the source of truth.
Build a Bambu Studio project from them whenever needed:
mkdir -p dist
3mfctl build -o dist/model.3mf print/model.3mf.yamldist/model.3mf is generated from the manifest and STL assets. With the Go toolchain pinned by go.mod, identical manifest and STL bytes produce a byte-identical 3MF. Different Go versions are not covered by the ZIP compression byte guarantee.
Update or regenerate the STL file referenced by the manifest, then build again:
3mfctl build -o dist/model.3mf print/model.3mf.yamlBy default, build recalculates STL hashes and atomically refreshes stale hash values in the manifest. This makes an intentional STL update part of the normal Git diff together with any print-setting changes.
If you want CI or another verification step to reject unexpected geometry changes instead, lock the hashes:
3mfctl build \
-lock-stl-hash \
-o dist/model.3mf \
print/model.3mf.yamlThe YAML manifest is the canonical editable representation used by build, so print configuration can be changed and reviewed as text instead of replacing an opaque .3mf file.
For settings that are easier to adjust in Bambu Studio, you can also edit a generated 3MF there and run probe again, then review the resulting manifest diff before committing it. When probe selects an STL filename that already exists, it verifies the geometry and reuses the file; a geometry mismatch is an error and never silently overwrites the existing asset.
A typical project can separate printable sources from build output:
project/
├── model/ # CAD / OpenSCAD / other model sources
├── print/
│ ├── model.3mf.yaml # canonical print project
│ └── assets/ # STL inputs consumed by 3mfctl
└── dist/
└── model.3mf # generated for Bambu Studio / printing
If STL files are generated reproducibly by the project, they can be produced by the same build workflow before 3mfctl build; the important part is that the manifest references explicit external STL paths rather than hiding geometry inside the authoritative 3MF.
For a project that wants to pin 3mfctl in go.mod instead of relying on a globally installed binary:
go get -tool github.com/tingtt/3mfctl@latestThen run it through the Go toolchain:
go tool 3mfctl probe -o print/model.3mf.yaml --assets-dir assets original.3mf
go tool 3mfctl build -o dist/model.3mf print/model.3mf.yamlThis works well from go generate, Taskfile, Make, or CI workflows that generate STL files and then assemble the printable 3MF.
3mfctl probe -o MANIFEST --assets-dir DIR INPUT.3mfprobe handles exactly one input archive. Asset paths in the generated manifest are relative to the manifest. A relative --assets-dir is also interpreted relative to the manifest.
If a selected STL filename already exists, probe verifies its geometry and reuses it. If it does not exist, probe extracts a deterministic binary STL. A geometry mismatch is an error and never overwrites the existing asset.
3mfctl build -o OUTPUT.3mf [-lock-stl-hash] MANIFESTbuild handles exactly one manifest and requires -o. It strictly decodes and validates the complete manifest and every STL asset before creating the archive.
By default, stale STL hashes are updated in the manifest. -lock-stl-hash instead makes a geometry or triangle-order hash mismatch fail without changing the manifest or output. The output and any updated manifest are written through temporary files.
See docs/manifest.md for the manifest v1 contract, supported archive entries, normalization rules, and geometry matching behavior.
- ❌ Manual support setting (face-level support painting) is not supported. Ordinary support configuration is preserved, but fine-grained face selection in
paint_supportsis not. - ❌ Painted fuzzy-skin regions and generic per-face properties are not supported (
paint_fuzzy_skinandface_property). - ❌ 3MF material/property assignments and required extensions other than the Production extension (
p) are not supported. - ❌ 3MF topologies outside Bambu Studio's component-based project layout are not supported, including inline root meshes and nested components.
- ❌ Preview images and thumbnails are not round-tripped;
probeintentionally omits them.
Unsupported required data causes probe or build to fail instead of silently discarding it.
go test ./...
go vet ./...
go run . probe --help
go run . build --help