Skip to content

v0.53.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 15:05
· 9 commits to main since this release
v0.53.0

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

What changed

Added

  • The App store inside filex (#162). The navigation panel's Apps → App
    store
    opens filex's own page over a trusted store's catalog - read and
    verified on the server from the store's signed index, its icons through
    filex, no frame of the store and no change to the Content-Security-Policy;
    a catalog is kept ten minutes and served marked stale while the store is
    down; the desktop app shows the same screen under the same rule, in a
    window of its own. A person asks for an app with a reason and follows
    My requests;
    the request lands on Install requests beside an API key's. Approving it
    asks the store for a fresh install link and opens the store review -
    the same SHA-256, permission and license steps as any install link - and
    that install closes the request. Admin → Plugins → Apps → Store screen
    turns it on, picks the stores and who sees it (everyone, built-in roles or
    groups), per tenant; Trusted stores → Connect binds this filex to a
    store with the one-time code its "My instances" page makes (an ed25519 key
    sealed with FILEX_SECRET_KEY, every request signed with a timestamp and a
    single-use nonce). Reading a link, trusting, connecting and installing stay
    the platform operator's, signed in to the panel
    (APP-PLUGINS.md → The store screen).
  • The whole test chain runs on GitHub, split into parts that run side by
    side.
    ci.yml is a matrix on every push of main: the Go suite in the
    shards of scripts/test-shards.json beside PostgreSQL, MySQL, Redis and
    Samba and again under -race, the migrations on three engines and through
    the CLI (up, three steps down, up), the unit suites in UTC and on
    Istanbul's clock, the typechecks and the docs gates, Cypress, Playwright on
    Chromium, Firefox and WebKit in parts with the Document Server specs again
    with one, the store and S3 lines, both images, and one All tests (full)
    job that is green only when every part was. Every part is named by
    scripts/ci-parts.mjs, and the release gate reads the run part by part: a
    red part is named at once, and a run without a part is no full matrix.
    A pull request runs the same less -race, Firefox, WebKit and the Document
    Server, and a push is never cancelled by the next one
    (CONTRIBUTING.md → Release process).
  • A tag run promotes what the dry run of its commit built. The dry run of
    an untagged version is the release candidate: it pushes both images by
    digest with no tag and keeps every desktop row's files with their sums, and
    the tag run checks each image on its own architecture (built from the
    tag's commit, filex --version naming it) and each file byte for byte
    before it tags or publishes anything - the images in minutes instead of a
    second build. The release workflow no longer runs the test suite itself:
    the push run of the commit is the test, verify asks that it was the full
    matrix, and the dry run packages at once. -f macos=false starts a dry run
    without the macOS row while GitHub has no macOS runner.
  • The release can be packaged off GitHub.
    node scripts/release/package-local.mjs X.Y.Z [--run [--publish]] runs the
    release workflow's packaging on the tag's tree from a maintainer's machine -
    goreleaser and the GitHub Release, both images for amd64 and arm64, the
    Windows and Linux desktop packages - and says what it leaves to GitHub
    (macOS, the arm64 snap) and to a person (npm, the stores)
    (CONTRIBUTING.md → When GitHub Actions is
    down
    ).
  • The Playwright and Go suites run in parts, side by side.
    node e2e/run.mjs local --shard i/N runs part i of N with Playwright's own
    split - taken after --grep and the engines, so the parts add up to the
    whole run - each part on a server, port, output directory and server log of
    its own. The Go tests have one shard list, scripts/test-shards.json, for
    every runner: internal/api/handlers and internal/wasmplugin are cut by
    test file into exact -run groups, the other packages run as package lists,
    and a test file or package the list does not name runs in a rest group.
    node scripts/test-shards.mjs check holds the list to go list ./... and
    go test -list, e2e-check holds the parts to the whole run, and
    rebalance re-cuts a package from the times a run measured
    (CONTRIBUTING.md → Shards).
  • Comments are an API key's own permission - comments, at Read
    (read) or Read and write (comments:rw), the first permission a key
    holds at a level. Adding and deleting a comment ask comments:rw on every
    door - /api/files/comments, /api/ai/comments and the MCP
    file_comment_add / file_comment_delete tools - through one check in the
    handler they all run; reading them asks read. A key is minted with it on
    both API keys screens (Comments: Read / Read and write, buttons, not a
    list), raised or lowered later with Edit on Admin → API / MCP, the
    row's Comments: allow writing on the API keys panel, or
    PATCH /api/tokens/{id} / PATCH /api/admin/ai-tokens/{id}
    {"permissions": {"comments": "rw"}} - the verbs never change - and every
    key list answers each key's levels in permissions. A key never gives a key
    a level above its own (403 token_ceiling). The rule for every such
    permission added from now on is in the code and the docs: each declares its
    default, read - existing keys get it with no migration - or none for a
    super-administrator kind, and tokenperm_test.go is red for one that does
    not (RBAC.md → Permissions with a
    level
    ,
    CONTRIBUTING.md → Adding a permission).
  • Multi-tenant mode is a switch: Admin → Multi-tenant mode (System →
    Customization), the platform operator's alone - a tenant's administrator is
    refused the page and its API (403 supertenant_only), an API key the change
    (403 session_required). FILEX_MULTI_TENANT or the config file's
    multi_tenant, when set, pin the mode and the switch shows it locked with
    the variable to change instead; unset, the switch decides, off until somebody
    turns it on. A change takes effect when filex is restarted and the page says
    so until then (the mode is handed to the route groups, the sign-in providers
    and the SFTP, FTPS, NFS, WebDAV and S3 servers once, at start). Turning the
    mode off while the install has tenants asks for their number first: they go
    into maintenance mode, nothing is deleted, and turning it back on brings
    every tenant back as it was. Both directions are in the audit log
    (tenancy.enable, tenancy.disable). /api/capabilities says
    multi_tenant (the mode in force) on every install, and every screen follows
    it. GET/PUT /api/admin/tenancy. (MULTI-TENANCY.md → Mode
    gating
    , ADMIN-PANEL.md)
  • The notification digest - one notification instead of a flood, if you
    want it.
    Optional and off out of the box: every kind of notification is
    still told at once, so an upgrade changes nobody's notifications. A kind
    turned off - by an administrator for everybody in their tenant (Admin →
    Notifications
    , which also sets the window, 1-15 minutes, default 1), or by
    a person for themselves (user settings → Notifications, an Urgent switch
    beside every kind and one for all the administrator alerts) - is held for
    the window and told in ONE notification that says, folder by folder, what
    changed - "Rapor: 30 files added; Fotoğraflar: 3 files moved to the trash, 1
    comment" - so a folder that receives 30 files in a minute is one badge step,
    one browser pop-up and one desktop toast, not 30. Every event still keeps
    its own row the moment it happens (the history, the admin list, the audit
    log and every webhook are unchanged); a held row is in the person's list,
    read, until the digest carries it. A digest names and counts only what the
    person's own bell shows (tenant and grants), survives a restart (the window
    is in the database) and is never written twice. A file-request owner who
    holds those notices gets one email per window too. Webhooks can subscribe to the new
    notification.digest event; only a target that ticks it receives it. API:
    urgent_overrides and digest on /api/notifications/settings,
    GET/PATCH /api/admin/notifications/digest. Migration 00087
    (NOTIFICATIONS.md → The digest).
  • A search for the whole admin panel (task #168). The top bar's search
    button opened the file search page, and a phone had no search at all. Now one
    search, beside the menu - Ctrl+K / ⌘K (the explorer's palette key, as
    you have it bound), and a button with a layer over the window on a phone -
    finds a page and its tabs, a single setting (Trash retention, Require
    two-factor authentication
    ), an installed app and what it does, a person, a
    group, an API key (by its name, never its value), a storage or a share, by
    its name in the interface's language or in English and by its synonyms
    (LDAP finds Identity providers), with Turkish letters and capitals
    folded. Files come on demand: the first three and a Search files row, or
    file: for files alone; user:, app:, key:, group:, storage: and
    setting: keep one kind. You find only what you may open: the pages are the
    menu's own, and every record comes from the list its page reads, through the
    same permission - a delegated administrator holding admin.users finds
    people and groups, a tenant's administrator their own tenant's. Your recent
    searches are kept on the server (the newest 20, migration 00090), so they
    follow you to another browser; remove one or all of them, and they are never
    in the audit log. New: GET /api/admin/panel-search, GET · POST ·
    DELETE /api/admin/panel-search/recent, DELETE …/recent/{id}, and
    ?stats=none on GET /api/admin/storages; core exports PanelSearch, its
    rules and foldText (ADMIN-PANEL.md → Search).
  • CircleCI stands in for GitHub Actions at the release gate.
    .circleci/config.yml runs, on every push of main, the Go suite in the
    shards of scripts/test-shards.json beside PostgreSQL and MySQL (and red
    unless both engines really ran), the web and package unit suites in UTC,
    the desktop unit tests and the Playwright suite in Chromium, in parts. When
    Actions is down, pnpm release X.Y.Z --resume --gate circleci reads that
    workflow on the export commit instead of ci.yml and the release dry run,
    runs here the heavy suites CircleCI does not, and records which CI passed
    the commit. Once Actions is back, the tag run publishes that commit: its
    verify takes the green CircleCI workflow when GitHub lacks its full
    matrix and dry run, and with no dry run to promote the tag run builds the
    images and every desktop package itself (the public repository needs a
    CIRCLECI_TOKEN secret for it).
    node scripts/test-shards.mjs go --node i/N gives copy i of a CI job its
    shard, and refuses a copy count that is not the list's
    (CONTRIBUTING.md → When GitHub Actions is
    down
    ).
  • The language packs keep up with main between releases.
    node scripts/langpacks.mjs status names, for every language pack checkout,
    the strings it does not translate yet, the ones whose English changed since
    the pack was translated (which pack.mjs sync used to keep without a word)
    and the ones filex dropped; todo writes the translator's worklists, and
    apply checks every answer against the language pack validator and the
    fixed names (filex, ONLYOFFICE) before it writes anything, then runs the
    pack's own sync, build and validators and commits locally. release X.Y.Z
    is the whole release-day step of a pack: the release's catalogue, one patch
    version up, the README's status block, the validators, a commit, and the
    signed tag and push commands printed, not run
    (CONTRIBUTING.md → Translations and language packs).
  • The language packs are translated every night, by an agent, on the
    build host.
    A timer of its own (scripts/chain/install-langpacks.sh
    installs it, the driver's copy and the pack checkouts) starts
    scripts/langpacks-nightly.mjs run, which waits for the nightly test run
    to end, fetches origin/main into a worktree of its own and, when a pack
    lacks something, gives each pack's worklist to a Claude Code session that
    can only read and edit its own directory of copies (no command, web or MCP
    tool, none of the host user's settings, a HOME of its own) with the
    project's Claude account asked from the work server at every run; only the
    answers are taken from it, refused answers or a red validator go back to it
    once, apply --commit commits each pack on the build host, and one
    notification says per language what was translated and what is left.
    Nothing is pushed or tagged there: on release day node scripts/langpacks.mjs pull fast-forwards a maintainer's packs to their nightly remote,
    release refuses a pack not pulled yet and prints the push of the release
    commit back. Worklist items now carry the answer right after the key, and
    every worklist's rules ask for the language's own letters and one term per
    concept
    (CONTRIBUTING.md → Translations and language packs).
  • The release train's tools (scripts/train/), for the maintainers:
    pnpm train X.Y.Z writes the day's release note (the train rule, read from
    CONTRIBUTING; the 10:00 cut; main since the last tag up to the cut; what
    came after it; the merge queue; the closing "every task to Done");
    pnpm merge-queue --queue <note> merges a train's branches one after another
    with --no-ff and their own messages, merges CHANGELOG.md conflicts by
    Keep a Changelog section, leaves [Unreleased] with each heading once, stops
    for a person on any other conflict and builds and vets the Go module after
    every merge; bash scripts/train/filex-ship.sh X.Y.Z takes a green tag run
    to everything read back in one command - backup, the trusted-proxy check,
    the deploy instance by instance with an automatic rollback, both update
    feeds and the CDN purge, the Releases page, docs.filex.sh, npm and the
    embeds, then pnpm release X.Y.Z --resume --only deploy - with a log per
    step; and when-done.mjs runs a long command and wakes whoever waits for it
    when it ends. Hosts and keys are settings (scripts/train/train.env.example),
    never in the repository. (CONTRIBUTING.md → The release train's
    tools
    )
  • A .csv opens with filex on the desktop too (#151). Open with filex
    handles eleven types now: the ten office ones and .csv, registered the
    same way on every system (Windows' "Open with" list, the Microsoft Store
    package, text/csv in the Linux desktop entry and xdg-mime, macOS with
    rank Alternate), never taking the type over. With ONLYOFFICE on the server
    it opens in the spreadsheet editor, and a save as CSV - which the server
    writes back in the file's own dialect, the text of every cell nobody changed
    kept - goes over the .csv as any save goes over its document; a save that
    comes back as a spreadsheet goes beside it as <name>.xlsx
    (DESKTOP.md → Opening documents from your computer).

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