Documentation/omnitron/File workspace
In this section

File workspace

Open Files in Console. Panels A and B both open on the local daemon. Each panel can hold tabs for local and remote locations. Opening Files does not contact a remote filesystem. A node must run a compatible Omnitron daemon. After a node upgrade or policy change, use the panel’s Refresh button to reload its available roots and directory listing. Connection and permission failures remain visible until a successful retry; an unavailable node is not treated as an empty filesystem. File operations use the authenticated Netron mesh; no SFTP server or browser-held SSH credential is required.

Local means the daemon host. Upload and Download refer to the computer running your browser. Selecting a project does not restrict filesystem authority: this is an administrator workspace.

Start with a location

  1. To work locally, navigate immediately in either panel. To open another location, press + in that panel and choose a node. To compare two directories on the same node, choose that node again. Escape cancels without opening a tab or reading the remote filesystem.

  2. A node that exports one file area opens it directly. The default Filesystem is / on the daemon host, within its operating-system permissions. If a node exports several areas, choose one; read-only areas are marked. The tab keeps its node, area and directory history. To change node or area, open another tab. If access to an area is revoked, the tab reports that and does not switch to a different area.

  3. Open a directory with its name, Enter or Right. Each non-root listing includes ..; Enter on it or Left returns to the parent. The parent row remains available in empty/filtered directories and is never a selection or mutation target. Use path breadcrumbs, Back/Forward, Parent, or the path editor for navigation. Each panel supports eight independently navigable tabs.

  4. Select entries with checkboxes or Space; Shift+click selects a range. The compact navigation toolbar keeps the copy direction visible as A → B or B → A, with named icon actions for New folder, Upload and panel focus. Copy reviews the exact source and destination before starting a daemon job.

  5. Open Transfers for phase, file/byte progress, the current path, failures and Pause/Resume/Cancel. For a selected directory, the copy dialog also offers Archive and Pack & restore. The queue distinguishes source snapshot, encoding, encoded-byte transfer, restoration and publication; transferred bytes alone do not mean the destination is ready.

On narrow windows the A/B switch shows one panel at a time without discarding the other panel. On wider windows drag the separator, or focus it and press Left/Right, to adjust widths. Lists scroll inside the panel. Directory pages contain up to 200 entries; Load next 200 entries extends the current list. Filtering and sorting apply to loaded entries; directories sort first. Hidden entries are available from the panel menu.

Shortcut, while focus is inside a panelAction
Tab / Shift+TabSwitch file panels, after entering from either keyboard or pointer
EscapeSwitch between list and panel tools; Tab in tools follows normal controls
F6 / Shift+F6Leave the panel for the next/previous Console region
Enter / SpaceOpen / select focused entry
Left / RightParent directory / open the focused directory
Up / DownMove row focus
Alt+Left / Alt+Right / Alt+UpBack / forward / parent
Ctrl/Command+LEdit the root-relative path
F2 / F4 / F5Rename / edit / review copy to other panel
DeleteReview moving selected entries to trash

Keyboard entry from the sidebar lands at the same list focus target as pointer entry. Clicking a field or selector keeps native input behavior; dialogs and the editor own their keys. The active cursor is shown only in its owning panel, independently of selected files. Holding navigation keys repeats row movement.

Panel menus also provide New file, New folder, Upload, Download and Restore from trash. Symlinks are displayed but not followed; navigate to the real target path. Special files, sockets and devices are not opened as regular content.

Edit without losing another writer's change

The editor loads on demand and supports UTF-8 files up to 1 MiB, eight buffers, search/replace, undo, line numbers and JSON/JavaScript/TypeScript highlighting. Ctrl/Command+S saves a changed writable buffer. Theme, wrapping, language-label and read-only changes preserve the CodeMirror instance, cursor and undo history. The editor focuses the active document, exposes line/column and selection length, and offers a Wrap control for long lines. F6 cycles tabs, tools and document inside the dialog; Escape can minimize the editor without discarding buffers. Buffers stay in memory when the editor is minimized or you navigate to another Console section; a page unload warns about unsaved buffers. They are cleared on sign-out and are not stored in browser storage.

Saving requires the version returned by the read. A stale version is rejected, leaving the buffer intact. Save copy creates a different filename without overwriting. Reload requires confirmation before replacing the current buffer. Closing a dirty buffer requires confirmation. Renaming a file or its parent within the workspace updates open buffer locations without losing unsaved text; an already-stale version remains stale. UTF-8 BOM and uniform LF/CRLF/CR line endings are preserved. Mixed line endings are previewed read only. Binary data and larger files use transfers/download instead.

A staged regular file is synchronized before atomic publication. Existing text files retain owner/group and basic permission bits. ACLs, extended attributes, sparse layout and hard-link relationships are not preserved by text replacement or copy. Version checks serialize Omnitron writes; they are not an operating-system compare-and-swap against unrelated processes. Avoid editing a file concurrently through another privileged program.

Configure access per node

The default / export does not elevate the daemon to root. Unreadable directories and files return permission errors. Configure each node independently in ~/.omnitron/config.json, restart it through your normal controlled rollout, and inspect Files → Node → Root. Merge the files field into the existing JSON object; preserve its role, authentication and transport settings. This is daemon configuration, not a daemon block in a project’s omnitron.config.ts.

{
  "files": {
    "enabled": true,
    "roots": [
      { "id": "projects", "label": "Projects", "path": "/srv/projects", "protectedPaths": ["production/shared-data"] },
      { "id": "logs", "label": "Logs", "path": "/var/log", "readOnly": true }
    ]
  }
}
ConfigurationEffect
files omitted, or files: { enabled: true }Administrator access to the / filesystem root
files.roots: [...]Replace the default with explicit exports, up to 32 roots
readOnly: true on a rootDeny editing, uploads, copy destination, rename, trash and restore
files.roots: [] or files.enabled: falseExport no directories

Root IDs are stable API identifiers; labels are presentation. Paths must be absolute. Root-relative request paths reject traversal, reserved staging names, control characters and symlink components. Roots are pinned to their canonical path and filesystem identity for the daemon lifetime. Changing a pinned directory requires configuration review and a restart. These checks constrain API clients; they do not form an OS sandbox against a privileged local process racing directory replacement. Use OS identities/mount isolation for hostile local workloads.

Structural safety is enforced on each node, independently of Console. The exported root and fixed critical host directories (including /etc, /usr, /var, macOS /System and /Users, and their structural aliases/subdirectories) cannot be renamed or moved to trash. Add protectedPaths to a root for application-specific entries, using root-relative paths. A protected entry and its ancestors cannot be relocated; ordinary children remain browsable, editable and renameable according to the existing root permissions. This is structural protection, not a blanket filesystem write lock or an OS sandbox. Custom paths add to the fixed safeguards.

Rename changes one name within the same parent directory. Both UI and node reject empty/reserved names, slashes, control characters and names exceeding 255 UTF-8 bytes. The form validates before submission; direct API/CLI requests receive the same node checks. protectedPaths returned with roots is an advisory UI projection, not authorization. Older nodes need a backend update to enforce the new policy.

Temporary writes and recoverable trash live in a private .omnitron-files directory in the item's parent directory, on the destination filesystem. The directory must belong to the daemon user and have mode 0700. It is hidden from listings and cannot be addressed through the API. This permits a / export without requiring write permission at /, and avoids cross-filesystem atomic-renaming problems.

Transfer protocol and recovery

OmnitronFiles is the human-facing service shared by Console and CLI. Every public method requires admin; sensitive calls revalidate the current human session independently of cached transport authorization. Content reads and write intents are audited. Required intent persistence fails closed. File contents and chunk payloads are not written to audit details.

OmnitronFileNode is the authenticated mesh endpoint. It requires a signed service_role identity with a mesh: subject and accepts the typed v1 file request union, never arbitrary RPC names or shell commands. The control-plane peer namespaces the actor identity; upload receipts and trash are owned by that actor. The master resolves registered node IDs through the existing SlaveConnector. Human browser credentials cannot call this internal endpoint. The fleet TCP transport carries the verified peer identity into the same invocation context used by service guards and audit; missing identity fails closed.

Upload admission has a caller-generated idempotency key, persisted before the remote begin request so a lost response does not allocate another session. A copy first creates a bounded manifest, then transfers one verified block at a time. The receiving node synchronizes bytes before advancing its durable acknowledgement. Duplicate frames are accepted only when their already-acknowledged contents match. SHA-256 protects each block; commit records the complete destination hash. Regular-file publication uses a no-overwrite link from private staging, so partial files are not visible under their final name. A lost commit response is reconciled against the recorded destination inode and receipt, not blindly repeated as a new copy.

The current transport uses 256 KiB base64 frames over existing Netron RPC. It provides bounded memory and explicit backpressure with roughly one-third encoding overhead. Cross-node bytes currently pass through the coordinating daemon, not the browser. Direct peer binary streams and parallel adaptive transfer lanes are future protocol optimizations, not present guarantees.

The coordinator persists manifests and progress in the existing daemon state store. Restarted jobs become interrupted and require explicit Resume. New jobs persist their original request, so explicit Resume can retry interrupted planning. Legacy jobs without that request require a new copy. An archive plan already saved always reuses its admitted immutable snapshot, rather than silently recapturing changed source files. Pause takes effect at the next checkpoint; the in-flight block can finish. Current authority is rechecked during planning and between transfer blocks. Cancel removes unfinished staging when its outcome is known; it never rolls back already-published files. An ambiguous commit must be reconciled before cancellation.

In ordinary Files mode, existing destination files are never overwritten and existing directories can receive additional copied entries. Copy is a per-file operation, not a transactional tree snapshot: source changes fail their file's version check, and earlier completed files remain. A source directory changing after manifest creation does not turn the manifest into a live mirror.

BoundCurrent value
Text editor1 MiB UTF-8, 8 browser buffers
Directory10,000 entries; 200 per page
Copy manifest200 selected sources, 10,000 entries, 32 path components
File / copy payload100 GiB
Concurrent coordinator jobs4
Per-node archive preparation / restoration2 / 2
Concurrent node requests / unfinished writes32 / 32
Retained jobs100; completed/cancelled jobs with confirmed cleanup make room
Node upload/trash receipts1,000 each; unfinished writes and unrestored trash never expire automatically
Portable browser download32 MiB; use CLI for larger files

The coordinator acknowledges final receipts only after persisting its next cursor; this frees receipt capacity without deleting published files. Browser and CLI uploads acknowledge after receiving a successful commit. Unacknowledged completed/aborted receipts can be reclaimed after 30 days when the journal is full. Restored trash receipts can be reclaimed when full. Reaching the limit without reclaimable records fails explicitly; files are not silently purged. Restore reconciles a move that succeeded before its journal acknowledgement failed, using the original filesystem identity; an unrelated destination remains a conflict. Trash consumes disk until restored or handled by an administrator outside the workspace. Permanent purge and configurable disk quotas are not implemented yet. Do not use the workspace as a backup system.

Browser upload/download progress belongs to that page; leaving or closing it can interrupt these operations. Node-to-node transfers are daemon-owned. After a browser upload loses its publication response, inspect its displayed receipt before retrying. Do not assume a network error means no file was published.

Copy a directory through an archive

Select exactly one directory, choose the other panel's destination, and open Copy. The output mode is explicit:

ModeResult
FilesOrdinary recursive copy; each file is published separately
ArchiveA new archive file at the destination, without extraction
Pack & restoreA source archive transferred and verified, then restored into a new destination folder

The format selector appears after a capability probe on the chosen nodes. Supported adapters cover TAR, gzip/bzip2/xz/Zstandard TAR, ZIP and 7-Zip when the required codecs pass their probe. Archive availability is checked on the source; restoration requires the destination decoder too. The current destination probe requires a working local encoder/decoder pair; decoder-only installations are not advertised. 7-Zip currently produces archive files only. Formats are never inferred from file extensions or displayed as available merely because an executable exists. The chosen archive filename must end in the format's extension; a restored folder uses a plain validated name. Capabilities show their observation time and can be retried after an unavailable node; ordinary copy remains available.

The source creates a private snapshot with a bounded manifest, verifies each file and rechecks the tree before encoding. Only regular files and directories are accepted: no symlinks, devices, exported roots or protected structural trees. Limits are 10,000 entries, 32 path components and 100 GiB each for source and encoded bytes. Source snapshot storage and encoded data consume disk until completion or cancellation. Pause stops coordinator work at checkpoints; already admitted snapshot/restore work on a node can finish. Active native archive tasks also block backend handoff; shutdown cancels and drains them before releasing their journal. Cancel requests cleanup and preserves an unresolved publication for explicit recovery.

Archive files use the regular atomic file-publication receipt. Restored directories use an exclusive destination reservation and recorded filesystem identities: existing entries are never replaced, and the folder can become visible while its verified contents are published. This is not an atomic tree rename. The queue marks incomplete publication; Resume reconciles the same receipt rather than starting another copy. Consumers must wait for completed before using that folder. Archive utilities emit bytes only; they do not choose filesystem paths or extract archive entries into public directories. The admitted manifest determines paths, lengths and hashes. Restored files retain the manifest's basic permission bits. Portable archive encoding preserves executable/group/world bits while ensuring owner-read for files and owner-read/write/search for directories; ownership, ACLs and extended attributes are not transported.

If copying or cancellation has completed but source snapshot removal or destination receipt cleanup cannot be confirmed, the result remains completed/cancelled with cleanup pending. Retry cleanup (CLI files resume) retries only that cleanup. It does not repeat the transfer or overwrite the destination. Such jobs are retained until cleanup is confirmed.

CLI

omnitron files uses the same service. All commands support the global --json mode. The local CLI has the trusted local socket identity; it does not inherit the browser account, so its transfer/trash ownership is separate.

omnitron --json files roots --node local
omnitron --json files list srv --node local --root system
omnitron --json files stat srv/project/config.json --root system
omnitron --json files read srv/project/config.json --root system
omnitron --json files copy srv/project --root system \
  --to-node <registered-node-id> --to-root system --to-path srv/staging
omnitron --json files archive-formats --node <source-node> --to-node <destination-node>
omnitron --json files copy srv/project --node <source-node> --root system \
  --to-node <destination-node> --to-root system --to-path srv/staging \
  --archive-format tar.gz --archive-output archive --archive-name project.tar.gz
omnitron --json files copy srv/project --node <source-node> --root system \
  --to-node <destination-node> --to-root system --to-path srv/staging \
  --archive-format tar.gz --archive-output directory --archive-name restored-project
omnitron --json files transfers
omnitron --json files pause <transfer-id>
omnitron --json files resume <transfer-id>
omnitron --json files cancel <transfer-id>

files list --after <name> --revision <version> requests the next directory page. files save <path> --from <local-file> --version <version> performs a checked save; --version new creates without replacement. A successful omnitron --json files save returns the file metadata with its new revision. Use that revision for the next checked edit. files rename <path> --to <root-relative-path> --version <version> and files trash <path> --version <version> require an explicit observed version; the rename destination must retain the same parent directory. Use files trash-list --root <id> and files restore <trash-id> --node <id> for recovery. files mkdir <path> --root <id> creates a directory.

files upload <path> --from <local-file> --root <id> streams verified chunks. files download <path> --to <new-local-file> --root <id> stages locally and refuses overwriting. After an ambiguous upload, files upload-status <id>, files upload-finish <id> and files upload-abort <id> inspect/reconcile its receipt on the selected --node. Finish cannot publish incomplete data; abort cannot discard an unresolved committing receipt.

Typed API clients

The same authenticated service is available through IOmnitronFilesService from @omnitron-dev/omnitron/dto/services. Obtain its OmnitronFiles proxy through your existing authenticated Netron connection; human calls require the same live admin session as Console. Do not call the internal mesh endpoint directly.

import type {
  IOmnitronFilesService,
  FileLocation,
  FileArchiveFormat,
  FileArchiveOptions,
  FileTransfer,
} from '@omnitron-dev/omnitron/dto/services';

async function copyFolder(
  files: IOmnitronFilesService,
  source: FileLocation,
  destination: FileLocation,
  format: FileArchiveFormat,
  name: string,
  output: FileArchiveOptions['output'],
): Promise<FileTransfer> {
  const entry = await files.stat(source);
  if (entry.kind !== 'directory') throw new Error('Select one directory');
  const capabilities = await files.archiveFormats({
    nodeId: source.nodeId, destinationNodeId: destination.nodeId,
  });
  if (!capabilities.formats.some(item => item.format === format && item.outputs.includes(output))) {
    throw new Error('The selected node pair does not support this format and output');
  }
  return files.copy({
    sources: [{ ...source, version: entry.version }],
    destination,
    archive: { format, name, output },
  });
}

The returned job is an admission result, not a completion receipt. Read transfers and use controlTransfer to pause, resume or cancel. Wait for completed and inspect cleanupPending separately; bytes === totalBytes does not confirm restoration or publication. Capabilities can change after observation, so admission and execution validate the selected format again.

ContractMeaning
FileLocationRegistered node, exported root and root-relative path
FileRoot, FileEntryRoot policy and observed metadata/version; metadata does not grant authority
FileDocumentBounded UTF-8 content, BOM and version used for checked saves
FileArchiveCapabilitiesObserved node formats, supported output modes and limitations
FileTransferDurable phase, progress, publication evidence, errors and cleanup state

FILE_ARCHIVE_FORMATS lists protocol formats; it does not claim they are installed on a node. fileNameError and fileStructureError support advisory UI validation; the node independently enforces name, structural and authorization checks.

Design references and extension points

The interaction model compares representative managers across desktop, terminal and browser environments; it is not a claim to inventory every file manager.

ReferenceApplied principle
WinSCP Commander and transfer queueIndependent panes, explicit source/destination, background progress
Total CommanderTwo-panel operations and server-to-server work
Midnight CommanderProductive keyboard navigation and integrated editing
YaziTabs, fast navigation and progressive detail
Directory OpusSource/destination clarity and independent views
File Browser and FilestashA browser workspace combining navigation, transfer and editing
VS Code Remote SSHVisible remote identity while editing

Prism supplies tabs with individual close controls, table viewports, confirmation dialogs, theme and application layout. MUI supplies tables, menus, breadcrumbs, responsive dialogs and accessible controls. CodeMirror supplies the lazily loaded code-editing engine rather than a second rich-text editor. No shared Titan/Prism package is rebuilt to add the workspace.

Future work is deliberately separated from the v1 contract: direct authenticated binary transfer, persistent browser-upload reconciliation, directory comparison/synchronization, recursive indexed search, configurable trash retention/quotas, richer preview codecs and optional per-root delegated agent grants. Agent access must be an explicit attenuation of human authority, not an admin token handed to a provider SDK. See agent architecture and access.

Active archive snapshots and restoration tasks block backend handoff until they settle. Daemon shutdown cancels and drains their subprocesses before closing the private staging store.