Appearance
Files (Whole-Filesystem Explorer)
The Files view is a top-level file manager for browsing and managing the entire filesystem of any cluster node by absolute path. Pick a node, then navigate it like a desktop file manager: breadcrumbs, an editable path bar, folder navigation, text/media preview, and full file management.
This is distinct from the right-sidebar File Explorer, which is project-scoped — contained to a project's
working_dirand path-traversal-guarded. The Files view has no project sandbox; see the security note below.
Capabilities
- Node selection — a reusable node picker lists the connected node plus its peers. Switching nodes re-roots the explorer on that node's filesystem.
- Browse — lists one directory level at a time, folders first. Starts at the node's default browse directory (the run-as user's home). Breadcrumbs, an Up/Home button, and an editable absolute-path input drive navigation.
- Preview — text files open in an inline editor; images, audio, video, and PDFs render in a media viewer (built from a
data:URL). - Edit — text files save with SHA-256 optimistic-concurrency: if the file changed on disk since it was opened, the save is rejected as a conflict.
- Manage — create file, create folder, rename, copy / cut + paste, delete (behind a confirm dialog), upload (file picker), and download. Download streams the file to disk chunk by chunk with no size cap (native Save-As on desktop; the Downloads directory on mobile), showing a progress toast.
- Cross-node — every request carries an optional
node_id; when it targets a peer, the daemon forwards the request to that node and relays the reply (the same forwarding pattern used by the remote log fetch).
Limits
- Listings are capped at 5000 entries; very large directories show a "truncated" notice rather than streaming an unbounded frame.
- Text reads/writes are capped at 1 MiB; binary reads (in-app preview of images/audio/video/PDFs) and uploads at 25 MiB (bytes travel base64-encoded in a single WebSocket frame).
- Download has no size cap — it streams in 8 MiB windows over
FsReadChunkrather than fetching the whole file in one frame, so files of any size download. Unlimited streaming download applies to the native desktop and mobile apps; the web (PWA) client's download path does not stream yet. - Symlinks are reported distinctly and are not followed recursively; a delete removes the link itself, never its target.
Security
The Files view performs filesystem operations with the daemon's own privileges. Because the packaged system service runs hived as root, the Files view grants root-level read / write / delete of any path on the selected node, with no project containment and no run-as-user restriction.
Mitigations built in:
- Defaults to the run-as user's home directory; never auto-lists
/. - Refuses to delete or move a filesystem root.
- Deletes require an explicit confirm dialog.
- The read/upload caps above bound any single preview, edit, or upload transfer. Download is exempt (it streams in bounded windows), so a large download moves a lot of bytes off the host - operators should keep that in mind.
Operators who do not want this exposure should be aware that any user who can reach the daemon with a valid token can browse and modify the whole host. A future [files] allow_root_browse config gate may be added to restrict this.
Wire protocol
The view is backed by an Fs* message family that mirrors the project-file family but is keyed by (node_id, absolute_path) instead of (project_id, relative_path):
| Client message | Reply | Purpose |
|---|---|---|
FsList { path?, node_id? } | FsListResult / FsError | List a directory (empty path = home) |
FsReadText { path, node_id? } | FsTextContent / FsError | Read a UTF-8 text file |
FsReadBinary { path, node_id? } | FsBinaryContent / FsError | Read a binary file for in-app preview (base64 + MIME, 25 MiB cap) |
FsReadChunk { path, offset, len, request_id, node_id? } | FsChunk / FsChunkError | Read one window of a file for streaming download (stateless ranged read, no total-size cap) |
FsWriteText { path, content, base_sha256?, node_id? } | FsSaved / FsWriteConflict / FsError | Write text with conflict detection |
FsCreateFile / FsCreateDir | FsActionResult / FsActionError | Create a file / directory |
FsMove / FsCopy / FsDelete | FsActionResult / FsActionError | Move/rename, copy, delete |
FsUpload { path, data, request_id, node_id? } | FsActionResult / FsActionError | Upload bytes to an absolute path |
FsListResult carries the resolved absolute path, its parent (null at a root), a directory-first entries list, and a truncated flag. Every response echoes the serving node_id. See Protocol for the full message definitions.