Skip to content

Sharing & importing nodes

Distribution is ComfyUI’s model minus the registry: a node is a folder, publishing is git push, installing is a clone into ~/.config/threadle/nodes/.


There is no packaging step. Put node.json and your entry file at the root of a repository and push it.

Terminal window
cd ~/.config/threadle/nodes/head-tail
git init && git add . && git commit -m "head-tail node"
git remote add origin git@github.com:you/threadle-head-tail.git
git push -u origin main

Two things worth getting right before you push:

  • Set id. The descriptor’s id — not the folder or repo name — is what graphs reference. Pick it once and keep it.
  • .gitignore your node_modules. Consumers run npm install in the node folder themselves.

The repo name does not matter. Whatever it is called, threadle renames the installed directory to the id (see below).


Settings → threadle internals → custom nodes has three ways in.

SourceWhat you enterWhat happens
Githttps://… or git@…git clone --depth 1 into the nodes directory. Terminal prompts are disabled, so private repos need an agent/credential helper already set up. 60 s cap.
Local pathAn absolute path or ~/…The directory is copied in, .git excluded. Works for an already-cloned repo or any project folder.
⌸ Browse…A folder from the file pickerThe browser uploads the folder’s files (it never exposes real paths). .git and node_modules are skipped; max 200 files, 2 MB per file, 10 MB total.

You can equally skip the UI and git clone into ~/.config/threadle/nodes/ yourself — the scan picks up whatever is there.

Every import path ends the same way:

  1. A provisional directory name is derived from the source (last path segment, .git stripped, lowercased to a-z0-9-).
  2. If that name already exists, the import stops with "<name>" already exists — remove it first. Delete the old folder first; threadle never overwrites an installed node.
  3. After the files land, node.json is read and the directory is renamed to the descriptor’s id.

So graphs referencing head-tail keep working whether the repo was threadle-head-tail, my-nodes-fork, or a dated download folder. If the id is already taken by another node, the import keeps the provisional name and the Settings list flags the duplicate id instead of clobbering anything.

Bare class nodes without a node.json keep the derived directory name, which then serves as their id.


Cloning or copying a node runs no code from it:

  • Metadata comes from node.json, which is parsed as data.
  • For a bare class file with no descriptor, metadata is read by a sandboxed child process that instantiates the class and prints its fields — user code never runs inside the threadle server, and the result is cached by file mtime.
  • Your node’s run executes only when you wire it into a graph and press Run (or test the node).

A broken or hostile descriptor therefore shows up as a red row in Settings with the parse error, not as a compromised server.


Imported nodes are your local files running as you. threadle never downloads or auto-updates a node on its own; there is no registry to be typosquatted and no post-install hook. Read the code before you install it — the same bar you apply to curl | sh.

The runtime guardrails apply to imported and hand-written nodes alike:

GuardrailDetail
EnvironmentAn allowlist — PATH, HOME, LANG, LC_ALL, TMPDIR, TERM, SHELL, plus THREADLE_NODE and any params. The server’s own environment, including API keys, is not passed through.
"env": "inherit"A descriptor may opt into the full parent environment. It is a visible line in node.json — a trust decision you make, not a default.
No shellcommand is an argv array; there is no shell, no interpolation, no globbing.
Working directoryPinned to the node’s own folder.
BoundstimeoutMs (default 60 s, max 300 s), 4 MB output cap, at most 8 concurrent custom-node processes.
VisibilityEvery installed node is listed under Settings → threadle internals → custom nodes with its flavor and argv, and a button that opens the folder in your editor.

Nothing is hidden: the nodes directory is a plain folder. Inspect, diff, or rm -rf it like any other. threadle deliberately does not bulk-clear it from the internals panel — your nodes are not cache.