BD Provenance
The run receipt your PNG was never carrying
- anchor
- anchor
- provenance
A ComfyUI PNG already tells a story: the whole graph is in its text chunks, so you can drag the file back onto the canvas and rebuild the pipeline. What it doesn't tell you is which seed actually ran, which checkpoint that loader resolved to on this machine, or which node packs were involved at which commits. Nor who made it. BD Provenance collects both halves of that and writes it into the workflow itself.
Half is knowledge only the run has. Half is knowledge only you have. This node is where they meet.
How it works
It reads the API prompt for the run - the flattened execution format, not your widget values - so a seed the frontend randomised before submitting is the one recorded. It walks every node's inputs for seed, noise_seed, rand_seed and random_seed, filing each with the node that used it.
Models are found the same lazy way: any string input ending in .safetensors, .ckpt, .pt, .gguf or one of a few other weight extensions counts as a model file, which is why this keeps working for loaders from packs the node has never heard of. Each is resolved through ComfyUI's own folder_paths to the real file on disk, with its size. Then every class_type is mapped back to its custom_nodes directory, whose git remote and short HEAD commit get read.
Everything lands in workflow.extra.bd_provenance, and that location is the clever part. extra is a real LiteGraph field, so it round-trips through a load, an edit and a save in the UI. The record isn't a parallel JSON file that drifts - it is the workflow, and any save node that writes a sidecar or a PNG text chunk carries it without knowing it exists.
Execution order is the one thing that can go wrong
This node and the save node read the same extra_pnginfo dict for the run, which this one mutates in place. So it has to run first. Wire anchor - a wildcard pass-through - into the chain ahead of the save and the dependency guarantees it. Leave it unwired and the node still runs and still shows you the record, but ComfyUI is free to schedule it after the save. The provenance output says so, rather than letting a file that looks fine quietly lie to you.
The inputs worth setting
- anchor - the pass-through above. Do this.
- title / creator / studio / website / reference / license - your half of the record.
referenceis where a likeness or source came from: a URL, a character, a shoot. - notes - multiline, freeform.
- hash_models - off by default. Exact, slow the first time, cached per path/size/mtime, so a moved file re-hashes.
Two outputs: anchor to keep the chain alive, and provenance - a readable summary of everything captured, the same text printed to the console.
One thing that isn't obvious: model links. A filename can't be turned into a URL by guessing, so url is only filled for files listed in config/model_sources.json - as shipped, one commented-out example. Expect paths, sizes and hashes, no links, until you add entries.
Install
ComfyUI Manager → search "BrainDead" → install, or:
cd ComfyUI/custom_nodes
git clone https://github.com/BizaNator/ComfyUI-BrainDead
cd ComfyUI-BrainDead
pip install -r requirements.txt
Restart afterwards. Nothing in requirements.txt is needed here - the git-pinned moge/utils3d/pipeline and natten are for the Pixal3D nodes and are imported defensively, so the pack loads without them. Manager installs them anyway. This is a V3-API pack, so use a current ComfyUI. The node lives under 🧠BrainDead/Cache - newer than the README's node tables, so searching the README won't help.
Where people get burned
The files don't carry the record. Almost always anchor left unwired, so it ran after the save.
Nothing is stamped at all. No UI graph in extra_pnginfo means there's nothing to stamp, and ComfyUI only fills that field for frontend submissions - a headless caller has to send the graph in extra_data. Same trap as API-generated PNGs that carry a prompt chunk and no workflow chunk.
It caches nothing. The name says cache; it just records.
What this is, and what it isn't
A paper trail, not proof. These are ancillary PNG chunks and sidecars, and any re-encode deletes them with no way back - post the file somewhere that recompresses and the record dies with the workflow. Even a perfect hash proves the identity of a model file, not that you made the image.
So treat it as internal bookkeeping: the archive copy that can still answer "how did we build this, out of which exact files, on which box" in eighteen months, when the paths point at models you've long since reorganised. The EU AI Act's Article 50 transparency obligations started applying on 2 August 2026, and "who made this, under what licence" is the sort of field people want recorded - but a strippable text chunk is not a compliance story. One node for a real record is a good trade. Just don't call it proof.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| anchoropt | * | Pass anything through on its way to the save node. This is what guarantees this node runs BEFORE the save, so the files carry the record. Unwired, ordering is not guaranteed. | |
| titleopt | STRING | What this run produced. | |
| creatoropt | STRING | — | |
| studioopt | STRING | — | |
| websiteopt | STRING | — | |
| referenceopt | STRING | Where the source or the likeness came from — a URL, a character name, a shoot, a ticket. | |
| licenseopt | STRING | — | |
| notesopt | STRING | — | |
| include_modelsopt | BOOLEAN | true | — |
| include_addonsopt | BOOLEAN | true | — |
| hash_modelsopt | BOOLEAN | false | sha256 every resolved model file. Exact, and slow the first time — results are cached per (path, size, mtime). |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| anchor | * | — |
| provenance | STRING | — |