Skip to content

Repo map

What every repo in the stack does, where it lives, and whether you need the upstream or a fork.

Bespoke (written from scratch)

Repo Language Purpose
goeland86/snapmaker_moonraker Go Moonraker-compatible bridge that turns a Snapmaker J1S into a Klipper-style printer from Mainsail's perspective. Also ships a self-contained Pi 3 SD image build.
goeland86/klipper-nfc-daemon Python Polls an NFC reader, looks tags up in Spoolman, assigns the spool to the right tool via Moonraker JSON-RPC and Klipper macros. Same daemon works against real Klipper or the bridge.

Forks (patched, divergence noted)

Repo Upstream Branch Why the fork
goeland86/Spoolman Donkie/Spoolman pr/nfc-support Adds /api/v1/nfc/lookup endpoint, SPOOLMAN_TIGERTAG_ENABLED and SPOOLMAN_NFC_ENABLED env vars. Required by klipper-nfc-daemon. Upstream PR pending.
goeland86/mainsail mainsail-crew/mainsail — Personal-branch tweaks; not required by anything else in this stack. The J1S Pi image ships the latest upstream Mainsail release, not this fork.
goeland86/moonraker Arksine/moonraker — Personal-branch tweaks for non-J1S printers in the fleet. The bridge is a from-scratch Moonraker reimplementation, not a fork of this.

Upstream (used as-is)

Repo Used by What we use it for
Klipper3d/klipper Every Klipper printer in the fleet Motion control firmware on MCU + host-side macros.
Arksine/moonraker Every Klipper printer JSON-RPC + websocket API in front of Klipper.
viesturz/klipper-toolchanger Voron 2.4 (StealthChanger) only The toolchanger plugin that makes StealthChanger possible. Vanilla — we haven't patched it.
mainsail-crew/mainsail Every printer Web UI. Latest release on the J1S Pi image and on every Klipper host.
fluidd-core/fluidd Optional Alternative web UI; works against any host in the fleet.
mainsail-crew/crowsnest J1S Pi image Webcam streaming.
KlipperScreen/KlipperScreen Voron hosts (optional) Touchscreen UI; the NFC daemon's prompt dialogs render in KlipperScreen too.

Dependency graph

flowchart TD
    NFCDaemon[klipper-nfc-daemon] -->|"requires NFC<br/>endpoints"| SpoolmanFork[goeland86/Spoolman<br/>pr/nfc-support]
    NFCDaemon -->|"speaks<br/>Moonraker JSON-RPC"| Host{{Moonraker host}}

    Host -.->|"option A"| RealMoonraker[Arksine/moonraker<br/>+ Klipper3d/klipper]
    Host -.->|"option B"| Bridge[goeland86/snapmaker_moonraker]

    Voron[Voron 2.4] --> RealMoonraker
    RealMoonraker -->|"loads<br/>module"| Toolchanger[viesturz/klipper-toolchanger]

    J1S[Snapmaker J1S] --> Bridge

    UI[Mainsail / Fluidd] -->|"JSON-RPC"| Host

    classDef bespoke fill:#a29bfe,stroke:#6c5ce7,color:#fff
    classDef fork fill:#ffeaa7,stroke:#fdcb6e,color:#000
    classDef vanilla fill:#dfe6e9,stroke:#b2bec3,color:#000

    class NFCDaemon,Bridge bespoke
    class SpoolmanFork fork
    class RealMoonraker,Toolchanger,UI vanilla

Why we don't fork more

Each fork is a tax — every upstream release needs a re-rebase, every bug report risks "have you tried vanilla?". The rule of thumb:

Fork only when the patch can't reasonably live outside the project.

That's why:

  • The bridge intercepts Klipper macros instead of patching Klipper.
  • The NFC daemon calls Moonraker over JSON-RPC instead of being a Moonraker plugin.
  • Spoolman is the only mandatory fork in the stack, because the tag lookup endpoint is a server-side change that has to live in the Spoolman codebase.

When the Spoolman PR merges upstream, that fork goes away too and the stack becomes "two custom projects + entirely upstream everything else".