Skip to content

v0.54.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 16:16
· 9 commits to main since this release
v0.54.0

filex v0.54.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.54.0
docker pull ghcr.io/brf-tech/filex:full-v0.54.0

What changed

⚠ Upgrading to 0.54:

  • Embedders: update the server with the packages. @brftech/filex,
    @brftech/filex-core and @brftech/filex-react 0.54 need a filex 0.54
    server: which files open for editing, the input limits and the version
    line come from its capabilities, with no list to fall back on
    (API.md).
  • API clients: the refusals that put an English sentence in error put
    a code there now, and every refusal carries the server's sentence in
    message - show that (API-ERRORS.md). The presigned
    S3 multipart upload (POST /api/files/upload/init, /finalize, /abort)
    is gone; the staged upload works on every driver
    (UPLOADS.md). The notification lists no longer read
    lang=: a row comes in the account's language.
  • Migrations 00096, 00097, 00098 and 00105 run at the first start (the
    vault's lock, Web Push devices, a symlink's reason, a webhook's language);
    nothing to do by hand.
  • The desktop app has no language of its own: an install that had
    pinned one hands it to its account once, if the account had none.

Added

  • Push notifications while filex is closed (#191). Push notifications
    on this device
    in user settings → Notifications (a browser tab, the
    installed app, and the app on an iPhone's or iPad's Home Screen, iOS 16.4
    or later) sends what the person's bell tells them to that phone or browser
    with filex closed: the same kinds, the same mutes and the same digest - an
    urgent kind at once, a held kind as its digest - in their language, a tap
    opening what the bell would. Web Push (RFC 8030, 8291, 8292) with a VAPID
    key made at the first start and stored sealed with FILEX_SECRET_KEY (no
    key, no push); only the browsers' push services are accepted as endpoints
    (FILEX_PUSH_HOSTS adds one), each row is pushed to a device once however
    many servers run, devices are listed and removable, a test push is one
    click, signing out forgets the browser, and Admin → Notifications → Push
    notifications
    rotates the key
    (NOTIFICATIONS.md → Web Push).
  • The ONLYOFFICE editor speaks the person's language (#214,
    GitHub Discussion #93).
    It opened in English for everybody; the server now chooses its language and
    regional setting (editorConfig.lang, editorConfig.region) for every
    surface that opens it: the administrator's fixed language, else the one the
    request names, the language on the person's screen (which the viewer sends),
    their account's, FILEX_DEFAULT_LOCALE, English - a language pack's
    language too, whenever ONLYOFFICE offers it, and the nearest one it offers
    for a regional tag (de-AT → de, zh-HK → zh-TW). External services
    → ONLYOFFICE → Editor language
    sets Automatic (the default) or one of
    the editor's 46 languages for everybody, and FILEX_ONLYOFFICE_LANG pins
    it like FILEX_ONLYOFFICE_URL
    (ONLYOFFICE.md → The editor's language).
  • The vault, encryption level 3, in the explorer (#94). Behind the server's
    FILEX_E2E_VAULT switch (off by default, capabilities.e2e_vault), the
    encrypted-folder dialog offers level 3 for a new folder, with its cost and
    the pack size (4 MiB or 16 MiB). An unlocked vault lists from its encrypted
    index in this tab - the server never hears a path below the vault folder -
    opens and downloads by byte ranges of its packs, and takes the write lock
    at the first change (upload, new folder, rename, move or copy inside the
    vault, delete), committing each change as one generation. A strip says who
    writes and counts down both clocks: the person's idle time (1 to 10
    minutes, a new row in Settings → Preferences), after which the writer goes
    back to read-only, and the 15 minutes after which an unused vault drops
    its keys and asks for the password again. The same code runs in the web
    app, the desktop app and the embeds (packages/core, lib/e2evault/*,
    useE2eVault), held byte for byte to the format's test vectors. An upload
    whose name is taken asks first, in the explorer's "already there" dialog,
    whether it goes up under the free name (a vault keeps no earlier version,
    so nothing is replaced). The folder chooser (Move to, Copy to, an app's
    chooser) says which folder is a vault, offers only that vault for what is
    inside it and no vault for anything else, and nobody sees the vault's
    layout on the storage (v/) as folders - the server's listing now marks a
    vault folder (e2e_vault) and a listing inside one (e2e_vault_root).
    What the Go writer writes the browser reads, and the other way round:
    testdata/vault-go and testdata/vault-web, frozen fixtures each side opens.
    The vault's engine is loaded with the first vault opened or made, not
    with the explorer, so the main chunk stays within workbox's 2 MiB
    precache limit (lib/e2evault, e2eVaultEngine; a test walks the static
    import graph).
  • Narrow a search on the server (#207). type (file, dir or a kind:
    image, spreadsheet...), mime, modified_after / modified_before,
    min_size / max_size, under / not_under, owner and hidden are
    parameters of /api/files/search, the explorer's name search, /api/ai/search,
    the MCP file_search tool and filex client search (one flag each), applied
    to every candidate before the limit counts it; a value the server cannot read
    is a 400 bad_filter. Search answers carry total, and every hit its
    score and kind (SEARCH.md → Narrowing a search).
  • A new link's answer carries its download command (#210). POST /api/files/share, POST /api/ai/share and the MCP file_share tool return
    download_command: the curl and PowerShell lines that fetch the link,
    written by the server (-L for an S3 redirect, ?zip=wait for a folder,
    the PIN as ?pin= on the creator's own answer). The share dialog shows both
    lines and no longer builds a command itself; an agent passes them on
    (SHARING.md → Command line).
  • The vault's server half, behind a switch (#94). With
    FILEX_E2E_VAULT=1 (off by default, and capabilities.e2e_vault says
    which) the server serves /api/files/e2e/vault/*: a new empty vault made in
    one request (folder, key file, generation 1's index, undone if a step
    fails), its state and a paged listing of packs and index files with the
    server's clock, the write lock (one session at a time, a 60-second lease,
    each person's idle time of 1 to 10 minutes, a break by the folder's owner or
    an administrator), packs stored whole and once, index files committed only
    as the next generation through a temporary file renamed into place and
    abandoned after 60 seconds, and the lock holder's garbage collection that
    never deletes the three newest generations. The lock lives in the database
    (vault_locks, migration 00096) with compare-and-set updates, so several
    filex processes on one database agree; its token is kept only as its
    SHA-256. Inside a vault folder only that API writes: the explorer, the
    queue, the agent API and MCP, archives, apps, the document server's save,
    WebDAV, S3, SFTP, FTPS and NFS are refused there (403 VAULT_PATH), and
    the key file keeps its vault block and its place (409 VAULT_KEYFILE). vault.create, vault.lock,
    vault.unlock and vault.lock_break are audited, vault.generation and
    vault.lock are realtime frames, and the filexvlt magic joins the
    content sniff. BACKEND.md,
    CONFIGURATION.md.
  • Vaults from the command line (#94): filex decrypt reads a vault (level
    3) from a copy or straight from the server (filex decrypt docs://Kasa, no
    lock, --generation N for an older state still kept), filex vault mount
    serves one from a WebDAV server on 127.0.0.1 that the system mounts (net
    use, mount_webdav, gio or davfs2; no FUSE) - the write lock at the first
    change, a commit within 5 seconds of the last write, out after 15 idle
    minutes - and filex vault prune collects and repacks. They work against
    a server with the vault API on (FILEX_E2E_VAULT).
    CLI.md.
  • The vault level's format, written down (#94). Level 3 of end-to-end
    encryption - built in this release, behind FILEX_E2E_VAULT (above) - has
    a normative format and protocol,
    E2E-VAULT-FORMAT.md: equal packs (4 MiB, or
    16 MiB chosen at creation) filled with random bytes, an encrypted index of
    the whole tree padded with Padmé, keys derived from the folder key with
    HKDF so that every key encrypts exactly one plaintext, one writer at a time
    under a lock the server keeps (idle after 3 minutes by default, at most 10),
    garbage collection by the lock holder, and test vectors from an independent
    reference implementation (backend/internal/e2edecrypt/testdata/gen_vault_vectors.mjs,
    node:crypto) that the browser, the server and the command line are held
    to. The roadmap records the
    decisions that replaced its open questions. What the first runs of all
    three together settled is in it too: the listing's e2e_vault /
    e2e_vault_root, an upload whose name is taken asked about first (a vault
    replaces nothing), release with locked_idle only from a session that
    still holds the write lock, and a repack branch of the test vectors
    (generations 4 to 6) that holds both writers to one repack layout.
  • access.changed on the live socket (#196). When a grant, a role, a
    group, a permission rule, the tenant's encryption policy or an encryption
    approval changes, the server tells the people it can concern -
    {"type":"access.changed"} to a grant's or a request's person, a group's
    members, "scope":"all" to every socket of the tenant the change was made
    in (never another tenant's), or to every open socket for a change at the
    platform level - and their explorers ask the answers their menus depend on
    again. The frame names no path, no person and no reason; a burst is one frame
    (REALTIME.md → When access changes).
  • Every listed link says where it stands (#210). GET /api/shares and
    GET /api/admin/shares give each link state: active, expired,
    exhausted (its download, visit or upload cap is used up) or revoked.
  • Recent, Starred, Shared with me and a tag page and sort on the server
    (#207): offset, sort, total and truncated on each, opened_at /
    starred_at on the rows; the explorer says when a view holds more than it
    loaded and loads the rest on request (BACKEND.md).
  • The server checks an e-mail address and a username while they are typed
    (#209). POST /api/auth/account/check answers with the save's own rules and
    words, in the reader's language; the profile and the "Add user" form ask it
    instead of a copy of the rules in the browser, which had already drifted (it
    let a,b@x and ada.@x through). Whether an address is taken is told only
    where the save would tell it (BACKEND.md).
  • GET /api/public/strings (#210): the public pages' sentences
    (server.public.*) in one language, for the JavaScript share and
    file-request pages (BACKEND.md).
  • share_link_max_days in GET /api/capabilities (#210): the longest
    life a new link made by THIS person may get - the install's ceiling or
    their permission rules' Maximum share-link lifetime, whichever is
    shorter.
  • Editing encrypted office documents: the design and a protocol
    prototype
    (#189). Nothing offers it yet. The ONLYOFFICE editor would run
    in the browser from its own unchanged files, with only its socket.io client
    replaced by a bridge that answers it the way a Document Server does; what
    the other editors need goes sealed (a session key under the folder key,
    AES-256-GCM, each entry bound to its place in the log and chained to the
    one before) through a filex relay that orders it without reading it, gives
    the right to write changes only to an editor that has every change before
    them, and records who joined, who left and what a save holds. Every bridge
    runs the Document Server's lock rules on the same sequence, so the first
    request for a paragraph or a range wins everywhere. Who saves is the same
    answer in every browser: every 10 minutes while changes are unsaved, on
    Save, and by the last writer to leave; unsaved work waits 30 days; a vault
    edits alone. filex's half of the prototype is the relay
    (backend/internal/e2eoffice, in memory, no route) and the keys and the
    log reader (packages/core/src/lib/e2eoffice.ts, e2eofficeSave.ts). The
    editor's half - the bridge, the socket.io stand-in and the x2t driver - is
    AGPL and is not part of filex: it is the start of an app of its own,
    filex-office-editor
    (AGPL-3.0-or-later, its own repository and versions), that needs no
    Document Server; it has no release yet (E2E-OFFICE.md).

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

Security advisories

  • GHSA-5896-rqg6-cqjr - a forged ONLYOFFICE save callback signed with an editor configuration's token was accepted (Critical, 9.6)
  • GHSA-25hj-4g7f-8c2c - a non-owner who could write an encrypted folder could delete its key file history (High, 8.1)
  • GHSA-w8mc-47jv-jp74 - the share e-mail sent a caller-supplied link under the instance's mail identity (Medium, 4.1)

All three are fixed in 0.54.0; upgrading is recommended.