Repository navigation
v0.51.0
filex v0.51.0
Self-hosted file manager - Go single binary + multi-framework frontend.
Download a binary below, or pull a Docker image:
docker pull ghcr.io/brf-tech/filex:slim-v0.51.0
docker pull ghcr.io/brf-tech/filex:full-v0.51.0What changed
Added
- Who may encrypt is an organisation's decision (E2E-ENCRYPTION.md → Who may encrypt).
Three layers must all say yes before anybody starts encrypting:- the platform operator's tenant ceiling (
e2e_allowed, Admin → Encryption → Tenants; multi-tenant installs only); - the tenant policy - Off (administrators included), Administrators only, Permitted (the default: today's behaviour), or Approval;
- the new permission
files.encrypt- per role, per person, per folder. The built-in User role and the Standard and Upload-only presets hold it (a drop-box account could encrypt before, and still can); a Viewer never does. - Under Approval, Request encryption… leaves a request with a reason; the tenant's administrators approve or reject it under Admin → Encryption. An approval is for that person, is used once, and lasts 7 days, and it opens only its own kind, in the folder it was asked for and never in one below: a folder encrypted where it is, one new encrypted folder directly inside a folder (never spent on a folder that already holds something), or one new encrypted file in a folder (a single file's approval is its folder's). The explorer asks for the kind it would use and is answered from the same approvals the create doors spend, so it offers what the server accepts. A request must name a folder or a file that is there (
404 path_missing), and one person has at most 20 waiting (429 too_many_pending). A request nobody answers lapses after 7 days too; an hourly sweep closes both, and answering one that lapsed, or one that was answered already, is409 not_pending. - "Encrypting" is creating a key file where there is none, or a new
.fxe- and landing on one of those names by a rename or a move (the explorer and the queue, the agent API, WebDAV, SFTP, FTPS and NFS ask it like a create at the destination), unless it only carries what is encrypted already: a folder of any name, a.fxethat stays a.fxe, or a key file that stays its own folder's. A.fxegiven the key file's name, or a key file moved into another folder, is asked. A copy of what is encrypted makes a second one and is asked like a create where it lands, whatever its name: a.fxeor a key file copied anywhere, and a folder that holds either anywhere below it (asked once, as a new encrypted folder) - the explorer's paste and Copy to, the queue, the agent API's and MCP'sfile_copy, and WebDAVCOPY. A move or a rename of the same item stays free. A queued rename, move or copy that was free because its item was a folder fails when it runs if a file has taken the folder's place. "Creates" means there is no file at the path: overwriting a key file or a.fxethat is there stays free - a password change cannot be told from new key material, so the policy cannot stop the re-keying of what is encrypted already (an honest limit, in the docs) - and a folder with the name does not count as the file. Opening, adding to, re-keying and decrypting what is already encrypted stay free, and so do restores from the trash or a version and the document server's save. - Asked on every door: the web and desktop apps, uploads (single and chunked), the text editor, New document and a draft's save, the agent API (upload, move, MCP
file_writeandfile_move, ShareX, upload tickets, and 0.50'sfile_copy,archive_create,archive_extractandapp_run), file requests, archives (a refused member is skipped), apps, WebDAV, SFTP, FTPS, NFS and S3 (over HTTP a refusal is403 e2e_not_allowedwith areason; an MCP tool answers an error result naming it; the protocols refuse in their own words). On the agent surface an encrypted folder's key file is refused before the rule,403 RESERVED_NAME- a folder's too: that surface never writes one. A file request is judged for the link's creator and never spends the creator's approval: under Approval its key file or.fxeis refused. When a store failure leaves the rule undecided - a lookup behind the rule, or the storage row, the app's account or the file request's creator a door looks up to ask it - the door fails the write as its own server failure, not as a refusal: the web app's routes and the agent REST API500, an MCP tool an error result, an upload ticket and a file request503 storage_unavailable, an archive skips the member, WebDAV500, S3InternalError, SFTPSSH_FX_FAILURE, FTPS550saying so, NFSACCESfor a create. The rule's own log line, one for each write it could not decide, names the storage, the person and the error, never the path or a member's name; an app job that fails this way is also reported by the queue, which names the output. - The new Admin → Encryption page holds the policy, the requests and - for the platform operator - every tenant's ceiling. On a multi-tenant install the platform operator sees every tenant's requests there and answers only the platform's own; another tenant's is that tenant's administrators' decision (its row says so, and the API answers
403 not_decidable). The explorer asks ahead (POST /api/files/e2e/allowed, up to 1000 paths, each with the kind it would use; the answer carries the refusing layer inreasons) and offers Create encrypted folder… and Encrypt with E2EE… - or, under Approval, Request encryption… - only where it would be allowed; capabilities carrye2e_policyfor signed-in callers.filex encryptasks the same question before it asks for a password, and says why not in words. - New notifications and audit rows.
e2e.request_created(to whoever decides it: a tenant's administrators, as one broadcast the platform operator's bell does not take; the platform's own request to the supertenant's administrators; webhooks get every tenant's, once each) ande2e.request_decided(the person who asked); their two switches in a person's notification settings are offered only where they can reach that person - under Approval with the tenant's ceiling on - and greyed elsewhere, also for an administrator whose own tenant is under another policy. Audit:e2e_request.create|approve|reject|expire|use- theuserow names the approval and, inencrypted, the folder that was actually encrypted -e2e_policy.updateande2e_tenant.update. - Changing who may encrypt is a signed-in administrator's: an API key gets
403 session_requiredon/api/admin/e2e, on a tenant's ceiling, on the decision of a request, and on thee2e.policysetting through/api/admin/settings,/api/ai/admin/settingsand the MCP settings tools (a session's write there is audited ase2e_policy.update, like the page's own). A tenant's policy and the operator's ceiling are written one column at a time, so neither save can undo the other; saving the value already stored writes nothing; the ceiling answers409 single_tenanton a single-tenant install. - Migration
00080addsproviders.e2e_allowed,providers.e2e_policyand thee2e_requeststable. A single-tenant install keeps its policy in thee2e.policysetting; when it turns multi-tenant mode on, the first start copies that setting to the platform's own tenant once (logged and audited), so an install that had switched encryption off does not find it on again. - Contributed by Berk Başarır (#83). The copy rule, the separate approval kinds, the operator's role, the request limits, the policy's carry into multi-tenant mode and the CLI's question were added on top of it before the release.
- the platform operator's tenant ceiling (
- A
.csvopens in ONLYOFFICE (ONLYOFFICE.md → CSV files, GitHub #81).
With ONLYOFFICE configured, a CSV opens in its spreadsheet editor and is edited there; without it, in filex's read-only table, as before. It is the product's default, not a setting each install makes, and only.csv.- A choice like any other. ONLYOFFICE is an open handler of
.csv(onlyoffice): Open with lists it beside filex's table, Choose an app… and Always use this app keep it on the account (PUT /api/me/open-with/csv), and Admin → Plugins → Default apps lists.csvwith ONLYOFFICE first while it is configured (PUT /api/admin/file-types/csv). Switched off, a.csvopens in the table without an error and a choice or rule that names ONLYOFFICE is kept for when it is back; an administrator sees Open with ONLYOFFICE greyed with where to set it up. - Opened without the "Choose CSV options" question. filex reads the file's first 64 KiB and passes its delimiter (comma, semicolon, tab or
|) and UTF-8 to the editor. A file that is not UTF-8 gets ONLYOFFICE's question, rather than a guess. - Saved as the same kind of CSV. ONLYOFFICE writes an edited CSV comma-separated with a UTF-8 byte order mark whatever it was (measured on Docs 9.4); filex puts the file's own delimiter, byte order mark (or none) and line ends back before it writes it. A document server set to
assemblyFormatAsOrigin: falsesaves it as XLSX: filex converts that back to CSV through the document server and never writes XLSX bytes under a.csvname. Anything else is not written: the file stays as it was, the log says why, the people who edited it get a bell notice in their language (file.upload_failed) and the audit log a row (file.office_save_refused). - What CSV cannot keep is said before it is lost: a line under the viewer's bar, in the person's language, while a CSV is open for editing (only the active sheet's values; not formatting, formulas or other sheets).
- A choice like any other. ONLYOFFICE is an open handler of
Changed
-
The admin panel's navigation is a mega menu (ADMIN-PANEL.md,
#82). The long sidebar is gone:
the top bar holds the Dashboard link and three panels - Files & storage,
People & security and System - each laid out in sections (Files, Apps,
Storage; People & access, Security; Plugins & integrations, Customization,
Maintenance & records), with a short line under every page. Every page is two
clicks away and keeps its address, so links and bookmarks still work, and who
is offered which page is unchanged. A panel opens on a click or a key, never on
hover; with the keyboard, ↓ enters a panel, ↑ / ↓ / Home / End move inside it,
← / → move along the bar and Esc goes back to the button; a screen reader hears
a named landmark, which panel each button opens and whether it is open, lists
labelled by their headings and the current page. Below 1024 pixels a Menu
button opens a drawer with the same pages as a headed list, every section open.
The menu is one component in the core package (MegaMenu); the grouping is one
list in the admin app (web/src/lib/adminNav.ts). -
The trail under the bar names the menu section between the dashboard and the
page (Dashboard › People & access › Users), as words rather than a link. -
Auth providers is called Identity providers in English, as the guides
already called it (the Turkish was already Kimlik sağlayıcılar), and the
menu's Search is Search index, the page's own title. -
An upgrade still changes nobody's access: on first start, every saved role
(the built-in User role, and each custom role and its folder parts) that holds
files.createalso getsfiles.encrypt, and so does every person whose own
exceptions allowfiles.create- creating a key file or a.fxeneeded only
files.createbefore. A rename, a move or a copy onto one of those names now
needsfiles.encryptbeside its own permission, so a role that may rename or
move but not create cannot give a file such a name any more. The catalogue a server has seen is kept in the
permissions.cataloguesetting, so a permission added later is merged once and
never again; the setting only ever grows, so a version started after a later
one does not make the later one's permissions new again. -
⚠ Rolling back to a version without
files.encrypt(0.50 or older): the
older Roles and People pages cannot save a custom role's list or a person's
exceptions the upgrade gavefiles.encryptto (400, an unknown permission)
until the server is upgraded again. Two saves go through without a word and
leavefiles.encryptout: the built-in User role (the older page does not
show the permission and writes the list back without it) and a custom role's
folder part when the role's own list does not holdfiles.encrypt; applying
a preset to a custom role on the older page does the same. Upgrading again
does not give it back to those, nor where an administrator took it away in
between: it was merged once, at the first upgrade. After upgrading again,
check that the User role and those folder parts still allow encrypting
(PERMISSIONS.md). The release after 0.51.0 points them
out on Admin → Roles, with one click to givefiles.encryptback or to
dismiss it as on purpose. -
A refused encryption in the explorer is said in words (the server's reason)
instead of "could not create the encrypted folder", and a folder made before
its key file was refused is listed at once. -
⚠ File request links whose creator is not on record - the creator was
deleted before 0.49.0, so the link names nobody - keep taking ordinary files
but no longer take an encrypted folder's key file or a.fxe: a file request
is judged for its creator, and there is nobody to judge. Make a new link if
such a drop is needed. -
⚠ MySQL: migration
00080makes two columns byte-exact. The new
e2e_requests.pathisutf8mb4_0900_bin(the collation00041chose for
names and paths), so an approval forMuhasebeis not one formuhasebe. And
settings.setting_key, which00001made case- and accent-insensitive, is
altered to the same collation, so that no spelling of a setting key reaches
another key's row: the settings API guardse2e.policy(a session, one of the
four values, an audit row) by comparing the key exactly, and on MySQL
E2E.POLICYore2é.policywould have passed that guard and still read and
written thee2e.policyrow. TheALTER TABLErebuildssettings, a small
table, and cannot fail on existing data (keys that were unique ignoring case
and accent are unique byte for byte). filex's own keys are lower-case and
are read in the spelling they were written in; only something that leaned on
MySQL matching a key by its case or accent (a hand-written row, a script)
stops matching. Rolling back restores the old collation and stops with
error 1062 if two keys that differ only by case or accent were written after
the upgrade; rename or remove one of each first. SQLite and PostgreSQL
already compared keys exactly. -
The repository no longer describes a maintainer's machine. The README
and the API reference show the first-run admin password as<printed once>
instead of an example that looked real, the end-to-end tests look for the
local language packs in a sibling checkout (../filex-lang-es) instead of
at a fixed drive path, comments no longer name the project's own machines,
and a unit test refuses a drive-letter path in any file the export publishes. -
The release's macOS jobs run on
macos-15. GitHub retires its macOS 14
images on 2026-11-02. The runner stays pinned, nevermacos-latest: its
architecture is the package's (arm64).
Fixed
-
A choice made just before the tab closes is no longer lost. Preferences are sent 400 ms after the last change, and anything still waiting when the page was hidden or closed never left the browser - most visibly an app interface's
state.set, which the next open read back as the older value. The waiting document is now sent at once onpagehideand when the page turns hidden, withkeepalive, from the same shared code on every surface. -
An office file in an older format no longer gets newer-format bytes under its old name (ONLYOFFICE.md → A save in another format).
ONLYOFFICE writes no Word 97, Excel 97 or PowerPoint 97 file: an edited.doccomes back as DOCX and a.xlsas XLSX (measured on Docs 9.4 with its defaults), and filex wrote those bytes over the old file. Now the old file is not touched: the edit is saved beside it under the format's extension (rapor.docstays,rapor.docxis the edit;rapor (2).docxwhen that name is taken), the people who edited it are told in their language, and the audit log writesfile.office_saved_beside. The new file is created only for an editor of that session who may create it in that folder (their account, their tenant,files.createon the new name); when nobody may, nothing is written and they are told to download the edit from ONLYOFFICE before closing. A save that is not one of the document formats, or not the package it says it is, is not written at all (file.office_save_refused). -
The admin panel's menu button on a phone was announced as "Dashboard"; it is
Menu now, and says whether the drawer is open. While closed, the drawer is
out of the tab order (its links could be reached with Tab off screen). -
The trail on a file's versions page read Dashboard › admin-files › Versions;
the middle step is File history, the menu's name for the page. -
The Audit page names
e2e.folder_cleanup,e2e.fxe_header_rewrittenand
e2e.key_file_rewritten- written since 0.48.0 - in every language; they
showed as their raw ids. -
An app's interface can save a new file.
file.saveAs(fx.saveAsin
the SDK) answeredunavailableeverywhere since app interfaces arrived in
0.48.1: no screen that draws an interface gave it the folder dialog the
documentation promised. Now filex asks in its own folder dialog - the one
Move to… uses, titled with the app's name and the file's, opened in the
file's folder - in the viewer and in an app's dialog, page and details
section alike, in the web app, the desktop app and an embed
(APP-PLUGINS-API.md). -
Every Go test runs from the backend module alone (#141). A thumbnail
test read its clip frome2e/fixtures, outside the module the release tool
copies, and the public export's Go gate failed on it with "no such file". The
test makes its clip with ffmpeg now, and a web test fails when a Go test
reaches for a file outsidebackend/. -
The release's export no longer stops on build output it does not
publish. Its check for a private name, a server address or a credential
read every file in the public checkout, ignored ones included, and at 0.50
stopped half-way on the previous release's UI left inbackend/embed/web.
It now reads only the files the export publishes, and empties the two embed
directories the release fills with its own UI. -
The release's browser suite installs the echo test app it just built. On
Windows thego: echo.wasm fixturegate built the module only in its WSL
mirror, so the checkout's copy, the one Playwright installs, could predate
the change a spec tested (the v0.50.0 release check lost an hour to it). The
gate now builds in the Go module and writes the result back into the
checkout, and the specs that install the app fail on a module older than its
sources, namingbash scripts/build-wasm-fixture.sh, instead of running it. -
The folder dialog's first breadcrumb shows its whole focus ring. The breadcrumb line scrolls sideways, and a scroller clips its children on both axes, so the browser's default outside ring lost its left, top and bottom edge when the first crumb was focused (all three engines). The ring is now drawn inside the crumb, like the list rows'.
This release has more to it than fits on one page. The rest of the
entry - and every earlier release - is in CHANGELOG.md.
Verify: sha256sum -c checksums.txt
- Documentation - https://docs.filex.sh
- Report a bug - https://github.com/BRF-Tech/filex/issues
- Full changelog - https://github.com/BRF-Tech/filex/blob/main/CHANGELOG.md
- Every release - https://github.com/BRF-Tech/filex/releases