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/.
Publishing
Section titled “Publishing”There is no packaging step. Put node.json and your entry file at the root of a repository and push it.
cd ~/.config/threadle/nodes/head-tailgit init && git add . && git commit -m "head-tail node"git remote add origin git@github.com:you/threadle-head-tail.gitgit push -u origin mainTwo things worth getting right before you push:
- Set
id. The descriptor’sid— not the folder or repo name — is what graphs reference. Pick it once and keep it. .gitignoreyournode_modules. Consumers runnpm installin the node folder themselves.
The repo name does not matter. Whatever it is called, threadle renames the installed directory to the id (see below).
Installing
Section titled “Installing”Settings → threadle internals → custom nodes has three ways in.
| Source | What you enter | What happens |
|---|---|---|
| Git | https://… 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 path | An 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 picker | The 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.
The id rename
Section titled “The id rename”Every import path ends the same way:
- A provisional directory name is derived from the source (last path segment,
.gitstripped, lowercased toa-z0-9-). - 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. - After the files land,
node.jsonis read and the directory is renamed to the descriptor’sid.
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.
Importing never executes anything
Section titled “Importing never executes anything”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
runexecutes 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.
Trust model for imported nodes
Section titled “Trust model for imported nodes”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:
| Guardrail | Detail |
|---|---|
| Environment | An 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 shell | command is an argv array; there is no shell, no interpolation, no globbing. |
| Working directory | Pinned to the node’s own folder. |
| Bounds | timeoutMs (default 60 s, max 300 s), 4 MB output cap, at most 8 concurrent custom-node processes. |
| Visibility | Every 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.
Related
Section titled “Related”- Custom nodes — writing one
- Params & named ports — the advanced descriptor fields
- Trust model — the whole-app security posture