Drop host-filesystem nodes from the production node surface - #4743
Merged
Conversation
The cloud profile already curates production down to the creative node set, but it whole-lists `nodetool.input`, `nodetool.image`, and their siblings — so the nodes that read and write paths on the server's own disk survived. `nodetool.input.DocumentFileInput` is the one users hit first: a file picker in a browser tab that can only point at the container's filesystem, not theirs. The matching HTTP surfaces are already refused in production (`/api/files/local`, the workspace routes, tRPC `files.list`), so these nodes offered a picker that cannot pick and a run that cannot resolve. Collect them in CLOUD_HOST_FILE_NODES and embed that in the existing CLOUD_NODE_DENYLIST, keeping the "no host filesystem in the cloud" rule as one reviewable list rather than entries scattered by namespace. The asset-store counterparts — the `*Assets` loaders, AssetFolderInput, and the plain SaveImage/SaveAudio/SaveVideo savers — are untouched, since assets are how the cloud moves files. Two node shapes needed naming: those declaring a `file_path`/`folder_path picker property, and those taking a plain string path and calling `fs` in `process()`. Only the first is visible in metadata, so a registry-level test asserts no picker survives the policy — a new one lands in CI rather than in production. Nodes using scratch files under `os.tmpdir()` (ffmpeg work in the video and timeline nodes) are not host file access and stay. Self-hosted deployments setting NODETOOL_NODE_PROFILE=full are unaffected, as with the rest of the cloud curation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TfkxHL5wPA3ZPKAvie8rbh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In production we don't want file-access nodes like the document file input.
The cloud profile (
NODETOOL_ENV=production→ cloud, unlessNODETOOL_NODE_PROFILE=full) already curates the catalog down to the creative node set, but it whole-listsnodetool.input,nodetool.image,nodetool.audio,nodetool.video, andnodetool.model3d. The nodes inside those namespaces that read and write paths on the server's own disk therefore survived.nodetool.input.DocumentFileInputis the one users hit first: a native file picker rendered in a browser tab that can only ever point at the container's filesystem, not the user's machine.The matching HTTP surfaces are already refused in production —
/api/files/local(file-api.ts:163), the workspace routes (workspace-api.ts:76), tRPCfiles.list(trpc/routers/files.ts:46). So these nodes were offering a picker that cannot pick and a run that cannot resolve.Change
Collect the offenders in a new
CLOUD_HOST_FILE_NODESand embed it in the existingCLOUD_NODE_DENYLIST(packages/protocol/src/cloud-profile.ts).applyCloudNodePolicyalready unregisters denied types at bootstrap, so the palette,/api/nodes/metadata, and the runner all agree with no new mechanism.Thirteen node types, all verified to exist in the registry (a typo here would be a silent no-op):
nodetool.input.DocumentFileInput,FilePathInput,FolderPathInputLoadAudioFile,LoadAudioFolder,SaveAudioFileLoadImageFile,LoadImageFolder,SaveImageFileLoadVideoFile,SaveVideoFileLoadModel3DFile,SaveModel3DFileAssets stay. They're how the cloud moves files: the
*Assetsloaders,AssetFolderInput,DocumentInput, and the plainSaveImage/SaveAudio/SaveVideo/SaveModel3Dsavers are untouched, and a test pins that.Why the list is written out rather than derived
Two shapes needed naming, and only one is visible in metadata:
json_schema_extra: { type: "file_path" }) — detectable.LoadAudioFileand friends) take a plainstrprop and callfsinprocess(). Nothing in their metadata distinguishes them from any other string node.So the list is explicit, and a registry-level drift guard covers the detectable half: a test asserts no node with a file/folder picker property survives the policy, so one added later inside an allowed namespace fails CI instead of shipping. I verified the guard actually fails by temporarily removing
DocumentFileInputfrom the list — it caught it — then restored.Nodes that use scratch files under
os.tmpdir()(ffmpeg work innodetool.video.*andnodetool.timeline.*) are not host file access and correctly stay; I checked each against its source rather than trusting anfsgrep.Scope
Self-hosted deployments running
NODETOOL_NODE_PROFILE=fullare unaffected, consistent with the rest of the cloud curation. I deliberately did not gate on bareNODETOOL_ENV=production, which would override that documented opt-out — say the word if you'd rather these nodes be gone on every production server regardless of profile.No shipped example workflow references any of the removed nodes (checked). Existing user workflows in the cloud that contain one will now fail validation up front rather than at the node — those runs could not have resolved a host path anyway.
Testing
packages/protocol: 794 tests pass; added coverage that the host-file nodes are denied and their asset counterparts are kept.packages/websocketnode-registry-setup: 19 pass, including the new drift guard.npm run lintpasses; rootnpm run typecheckpasses.Two pre-existing environment failures, confirmed identical on a clean tree via
git stash: electron's typecheck and 26 websocket test files fail to import becausepackages/websocket/distand@nodetool-ai/dslaren't built in this sandbox. All 1918 collected websocket tests pass.Generated by Claude Code