Skip to content

Add an optional local import server for receiving model files from browser-based CAD Packages#15451

Open
JFKingsley wants to merge 6 commits into
prusa3d:masterfrom
JFKingsley:feature/local-import-server
Open

Add an optional local import server for receiving model files from browser-based CAD Packages#15451
JFKingsley wants to merge 6 commits into
prusa3d:masterfrom
JFKingsley:feature/local-import-server

Conversation

@JFKingsley

Copy link
Copy Markdown

What this does

Adds an optional, off-by-default HTTP endpoint that listens on localhost so an
external tool can POST a model file (STEP/STL/3MF/OBJ) and have it open in the
already-running PrusaSlicer window.

The motivation is a "Send to PrusaSlicer" button for browser-based CAD tools
(Onshape, etc.), similar to what Fusion 360 offers. As a former long-suffering Fusion 360 user currently migrating much of my work to Onshape, this is one of my biggest current frustrations, hence the PR!

I was recently having to debug some issues with the 3DConnexion server interface that proxies SpaceMouse data into Onshape using a similar local REST server interface, so this is where much of the stylistic inspiration comes from in terms of implementation detail.

How it works

The endpoint reuses the existing Plater::load_files() path; the same one already
used by the drag-and-drop interface. The server runs on its own thread. When a file arrives it's written to a temp file and the path is handed to a callback that marshals onto the GUI thread via CallAfter() before calling load_files(). We make no changes to the import/loading logic itself.

Request flow:

POST http://127.0.0.1:8126/open (model bytes, optional X-Filename / X-Prusa-Token)
-> LocalImportServer (worker thread): validate origin/token/size, write temp file
-> CallAfter -> Plater::load_files() (GUI thread) -> opens in the running window

Settings

Two checkboxes under Preferences -> Other, both default OFF:

  • Enable local import server - the on/off toggle.
  • Require an access key - when on, every request must present a matching
    X-Prusa-Token header. A random key is generated into local_import_token on first
    enable, so it's secure without manual setup. You can regenerate and copy the key from within the settings UI.

Advanced options live in PrusaSlicer.ini:

Key Default Meaning
enable_local_import_server 0 Master toggle
local_import_require_key 0 Require the X-Prusa-Token header
local_import_token (empty) The access key (auto-generated when required)
local_import_bind_address 127.0.0.1 Loopback bind address
local_import_server_port 8126 Port
local_import_allowed_origin (empty) The only browser origin allowed (CORS); empty = none

Security

  • Off by default; binds loopback only (never 0.0.0.0).
  • CORS is reflected only for the exact configured origin - never *.
  • Optional access key (X-Prusa-Token) means you have the ability to add an additional security checkpoint to further restrict any errant browser JS from writing local files without your say-so.
  • Request body size cap, for obvious reasons!
  • Server-side sanitized temp filenames - the caller supplies only a leaf name, which
    is stripped of path components and unsafe characters; it cannot influence where the
    file is written. The same name reuses the same temp file so re-sends replace cleanly rather than creating new objects.
  • No process execution - bytes are only written to a temp file and handed to the existing
    importer logic.

Why plain HTTP (no TLS/OpenSSL)

The endpoint is plain HTTP on loopback only. It does not pull in OpenSSL or any TLS
stack (not least as we don't do that at present afaik): the browser pages that call it are themselves served over HTTPS, and browsers already permit an HTTPS page to call http://127.0.0.1 because loopback is a "potentially trustworthy" origin under W3C Secure Contexts. This keeps the change small and avoids touching the dependency graph.

Build / opt-out

Gated behind a new CMake option SLIC3R_LOCAL_IMPORT_SERVER (default ON).

Testing

  • SLIC3R_LOCAL_IMPORT_SERVER=ON, enable the toggle in Preferences, then:
    curl http://127.0.0.1:8126/ping
    curl -X POST --data-binary @part.step -H 'X-Filename: part.step'
    http://127.0.0.1:8126/open
    -> the part opens in the running window.
  • With Require an access key on, the same POST without a matching X-Prusa-Token
    is rejected (401); with the key it succeeds.
  • With the feature disabled (default), nothing listens.

Notes

  • The endpoint is intentionally generic - no Onshape/vendor-specific logic lives in
    PrusaSlicer; that stays in the external sender.
  • Happy to adjust defaults (port, whether the feature flag defaults on/off, exposing the
    port/origin in the Preferences UI) to match the preferences - I couldn't seem to find much if any docs on requested styles/security preferences here, so entirely up for feedback!

Thanks for all for good work y'all do on PrusaSlicer :)
Jon

JFKingsley and others added 6 commits June 9, 2026 17:21
This endpoint exists so any external tool can POST a model file (STEP/STL/3MF/OBJ) and have it open in the
running PrusaSlicer window - e.g. a browser CAD app's "Send to
PrusaSlicer" button, similar to Fusion 360.

It reuses the existing Plater::load_files() path (the same one used by
single-instance IPC and drag-and-drop): the server thread writes the
received bytes to a temp file and hands the path to a callback that
marshals onto the GUI thread via CallAfter().

Notes:
- Plain HTTP on loopback only. No TLS needed (didn't want to start linking OpenSSL!): callers are served over
  HTTPS, and browsers permit an HTTPS page to call http://127.0.0.1
  (loopback is a "potentially trustworthy" origin).

Settings (Preferences > General, both default OFF):
- "Enable local import server" - the on/off toggle.
- "Require an access key" - when on, requests must present a matching
  X-Prusa-Token header; a random key is generated into local_import_token
  on first enable.

Configurable via PrusaSlicer.ini: local_import_bind_address (default
127.0.0.1), local_import_server_port, local_import_allowed_origin.
@JFKingsley

Copy link
Copy Markdown
Author

Only other note: I've confirmed this builds/runs correctly on macOS Tahoe 26.5, but have not been able to test it on other platforms as of yet - wasn't sure if there was a requirement there!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant