Files and SSH

File manager API, SSH/SFTP access with key management, and sub-user folder scoping.

In preview files version 1.0.0 Part of Files, terminal and editor

Actions

40 actions, callable from the panel, the command palette and the API as POST /api/v1/a/<id>. Internal actions used between modules are not listed.

ActionWhat it doesRiskPreview
files.list List a folder (read) low No dry run
files.stat Stat a file or folder (read) low No dry run
files.read Read part of a file (read) low No dry run
files.write Save a file low No dry run
files.mkdir Create a folder low No dry run
files.move Move or rename low No dry run
files.copy Copy a file or folder low No dry run
files.delete Delete to trash medium No dry run
files.trash.list List the trash (read) low No dry run
files.trash.restore Restore from trash medium No dry run
files.trash.empty Empty the trash medium No dry run
files.chmod Change permissions medium No dry run
files.archive.create Create an archive low No dry run
files.archive.extract Extract an archive medium No dry run
files.search Search by name or content (read) low No dry run
files.usage.scan Disk usage of a folder (read) low No dry run
files.upload.chunk Upload a chunk low No dry run
files.upload.finish Finish an upload low No dry run
files.upload.abort Cancel an upload low No dry run
files.ssh.get SSH access of an account (read) low No dry run
files.ssh.set Set SSH mode high
files.ssh.status Effective sshd settings for an account (read) low No dry run
files.sshkey.add Add an SSH public key medium No dry run
files.sshkey.generate Generate an ed25519 key pair medium No dry run
files.sshkey.remove Remove an SSH key medium No dry run
files.ssh.reconcile Re-apply SSH access and drop expired keys medium No dry run
files.sshd.apply Apply the sshd hardening profile critical
files.share.create Create a share link medium
files.share.list List share links (read) low No dry run
files.share.revoke Stop sharing low
files.access.list List FTP and WebDAV users (read) low No dry run
files.access.create Create an FTP or WebDAV user medium
files.access.update Change an FTP or WebDAV user medium
files.access.delete Delete an FTP or WebDAV user medium
files.server.get File transfer settings of a server (read) low No dry run
files.server.set Change file transfer settings high
files.firewall.requirements Ports the file transfer services need (read) low No dry run
files.version.list List versions of a file (read) low No dry run
files.version.restore Restore a version of a file medium
files.sshd.get SSH server settings last applied (read) low No dry run

Permissions and limits

Permissions

  • files.view Browse and read files
  • files.write Create and change files
  • files.delete Delete files and manage the trash
  • files.permissions Change file permissions
  • files.ssh.manage Manage SSH access and keys
  • files.server.manage Manage sshd on servers
  • files.share Share files and folders with a link
  • files.access.manage Manage FTP and WebDAV users

Plan limits

  • files.ssh_keys SSH keys

Engineering notes

Generated from modules/files/docs.md at build f3a3c074. These are the notes the engineers keep next to the code: precise, technical, and honest about what is not done yet.

Security model (read this first)

  • Every file op runs in a helper process that is the account's Unix user (internal/agent/files_helper.go: the agent re-executes itself with SysProcAttr.Credential{uid,gid,groups}; the helper refuses to run as root and sets no_new_privs). The kernel enforces ordinary permissions; files are created account:account.
  • Paths are virtual (/ = home) and resolved with openat2(RESOLVE_BENEATH|NO_MAGICLINKS) against a dirfd of the home (files_root.go). Absolute symlinks, .. escapes and /proc links are refused by the kernel, race-free. Without openat2 (old kernel/seccomp) a component-wise O_NOFOLLOW walk is used, which refuses every symlink. Mutations use parent dirfd + validated leaf with *at() calls; writes are temp + rename and replace (never follow) a symlink at the target.
  • Sub-users (actor.Kind == "subuser") are confined: only scoped folder grants count (an unscoped role permission is ignored) and the helper refuses every symlink, so a link inside a granted folder cannot reach the rest of the home. Trash and usage ops need a grant on /.
  • Archives: names validated (no .., absolute, backslash, NUL), links/devices never extracted, entries created with NOFOLLOW walks, limits (4 GiB, 200k files, ratio 200) enforced on bytes actually written, rollback on failure.
  • .ssh, .rc-trash, .rc-uploads are panel-managed: not writable through the API; internals are not listed.
  • SSH keys are not read from ~/.ssh (user-writable). sshd is pointed at root-owned /etc/ssh/rc-keys/%u, rendered from typed key objects (restrictions: from CIDRs, git/sftp forced command, expiry; restrict always on, so no port forwarding). Modes via groups: rc-ssh-off (AuthorizedKeysFile none), rc-sftp (chroot /srv/rc-chroot/<u> bind-mounting the home via rc-chroot@<u>.service, internal-sftp, no TTY), rc-shell. Snippet /etc/ssh/sshd_config.d/50-respirecloud.conf, hardening 10-respirecloud-hardening.conf; every change is sshd -t validated and reverted on failure.

Actions

files.list/stat/read/write/mkdir/move/copy/delete/trash.*/chmod/archive.create/archive.extract/search/usage.scan, files.upload.chunk/finish/abort (resumable, 256 KiB chunks), files.ssh.get/set/status, files.sshkey.add/generate/remove, files.ssh.reconcile (drops expired keys), files.sshd.apply (admin, dry-run diff). Admins/resellers pass account_id; owners and sub-users act on their own account.

Verified (lab w2-05, Ubuntu 24.04 container, 2026-10-09)

See docs/tasks/w2/w2-05-files-terminal.handover.md.

Sharing, versions, FTP(S) and WebDAV (w11-22)

  • Share links (files.share.create/list/revoke, public page /s/<token>): 256-bit token shown once (only its SHA-256 is stored), optional expiry, bcrypt password (5 wrong tries per link and address in 15 min, then 429), download limit and counter, revocation that also stops a running download (the link is re-checked at every 256 KiB chunk). Files are served as application/octet-stream + Content-Disposition: attachment + nosniff + sandboxing CSP from the panel origin, never from a site; every read is openat2 RESOLVE_BENEATH|NO_SYMLINKS as the account user, so a shared file that becomes a symlink fails (410 page). Folder shares list the folder and stream a zip (symlinks and special files skipped, 20,000 files at most). The list shows who created each link. Not built: drop-box (upload) mode.
  • Versions (files.version.list/restore): a save through files.write or an overwriting upload keeps the replaced content in <home>/.rc-versions/ (counts against the quota; hidden from the file manager; symlinks never followed). Retention: count (default 10), age (30 days), size cap (25 MB; a bigger file is saved without a version and the response carries version_note). Restore keeps the current content as a new version first. Settings: files.server.set.
  • FTP(S) (files.server.set ftp_*, files.access.*): ProFTPD (Debian proftpd-core + proftpd-mod-crypto; RHEL EPEL proftpd) as its own unit rc-ftpd.service with a panel-owned config; off until an administrator enables it. Explicit TLS is required (TLSRequired on) unless staff allow plain FTP (a warning is shown). Virtual users in /etc/proftpd/rc-ftp.passwd (SHA-512 crypt made by the agent, root-only; never the account's password) with the account's uid/gid and one folder as the chroot (DefaultRoot ~), optional read-only (<Limit WRITE>), allowed addresses, expiry. Passive ports: a range of at most 100 (default 40000-40049) published through files.firewall.requirements and opened by security.firewall.files. AllowOverwrite on (a re-upload replaces the file; the replaced content is NOT versioned over FTP: ProFTPD runs hooks inside the user's chroot where no panel binary exists, so versions come from the file manager and WebDAV only). Brute force: mod_ban MaxLoginAttempts only (4 connections that each use their 4 password tries within 10 min = the ADDRESS is banned for 1 h) and ClientConnectRate (60 connections in a minute = the address is banned 10 min; it stops one-try-per-connection scripts). No LoginRate rule: a ban on successful logins punishes normal clients, and a ban keyed on a user name lets anyone lock that user out (w11-41). Without a certificate path a self-signed certificate is created (fingerprint shown).
  • WebDAV at /dav/ on the panel's own TLS, served by the panel with golang.org/x/net/webdav over files.dav.op: chosen over nginx (its dav module has no PROPFIND and dav-ext is not packaged on both families) and Apache (a second web server). Basic auth with the access user's own password (bcrypt), 8 failures in 15 min per name and address lock it, confined to the user's folder with symlinks refused, read-only enforced server side.
  • SSH server screen (admin, File transfer > SSH server): review (dry-run diff) then apply files.sshd.apply; the last applied values are remembered (files.sshd.get). files.sshkey.add/files.ssh.set answer "SSH is not set up on this server yet" when openssh-server is missing.
  • Previews: images (as an inert <img>), text and code (first 64 KB as plain text; HTML and SVG are shown as source).
  • Upkeep: files.maintenance runs hourly (expired SSH keys, finished shares removed after 30 days, old versions pruned).

Not done / next

PDF preview in the panel (its CSP has no frame/object source); share drop-box mode; FTP certificate from the ssl module; a per-user activity/bytes log and "kill session" for FTP; remote storage; bubblewrap cage (exec path is cageArgv in pty.go); per-account SSH on/off gated by package; password SSH + TOTP; sshd port change and the second-connection login test; session list/kill for SSH.