# Office editing filex edits Word, Excel and PowerPoint documents (and their OpenDocument kin) in the browser, alone or with others, and saves the edits straight back into the storage the file came from. There are two ways to do it: | | The Office editor app | An ONLYOFFICE Document Server | |---|---|---| | What you run | nothing: an app you install in filex | a server of your own, next to filex | | What it opens | the standard ONLYOFFICE set, the older `doc`, `xls`, `ppt`, `rtf` and `.csv` included (0.4.0; before it `docx`, `xlsx`, `pptx`, `odt`, `ods`, `odp`) | the standard ONLYOFFICE set ([Supported file types](#supported-file-types)), the older `doc`, `xls`, `ppt` and `.csv` included | | Editing together | yes, through filex's own relay | yes, through the Document Server | | Print, *Download as*, PDF | yes, in the browser | yes | | **+ New** | Word, Excel, PowerPoint and OpenDocument (0.4.0; before it Word, Excel, PowerPoint) | Word, Excel, PowerPoint and OpenDocument | | A file of an encrypted folder | yes, decrypted in your browser | no | | Office thumbnails, the apps' office conversions | yes, on the server, through the app's [office engine](#the-office-engine-without-a-document-server) (0.4.0, filex 0.57) | yes | **Start with the app** ([The Office editor app](#the-office-editor-app), and the whole of what it does on its own page, [OFFICE-EDITOR.md](OFFICE-EDITOR.md)): it needs no server, no address and no secret, and since its 0.4.0 it also brings the [office engine](#the-office-engine-without-a-document-server) that filex runs on the server for the [thumbnails](#thumbnails) of office documents and the [office conversions apps ask for](#office-conversions-for-apps-the-office-engine) (the Convert app's office targets, PDF from an office file) - the work filex used to ask a Document Server for. A Document Server stays supported and optional ([The ONLYOFFICE Document Server](#the-onlyoffice-document-server-optional)): keep or connect one for the fonts added to it ([Where it differs from a Document Server](#where-it-differs-from-a-document-server)), or to edit in it. The two can be installed side by side; [Default apps](APP-PLUGINS.md#default-apps-which-app-opens-a-file-and-which-draws-its-thumbnail) decides which one opens a file, and its **Office engine** section which one converts and draws ([The engine and a Document Server](#the-engine-and-a-document-server)). Without either, office files open in the built-in read-only preview ([What happens if it's not configured](#what-happens-if-its-not-configured)). Both work on every surface that embeds the explorer - the web app, the [desktop app](DESKTOP.md) and any host page using ``. The desktop app also opens Office files from your own disk in the editor and writes them back to that path ([Opening documents from your computer](DESKTOP.md#opening-documents-from-your-computer)), so a machine with no Office installed gets an editor for the documents already on it. --- ## The Office editor app The Office editor app, [`filex-office-editor`](https://github.com/BRF-Tech/filex-office-editor), runs ONLYOFFICE's editor inside filex, in the browser: the editor's files come from the app's own package and the document is converted in the browser, so nothing runs on a server of yours. It is an [app](APP-PLUGINS.md) like any other, AGPL-3.0, based on ONLYOFFICE Docs by Ascensio System SIA. **Install it** - an administrator, once; filex never installs it by itself: - **From a store.** On [filex Apps](https://apps.filex.sh), press **Install** on *Office editor* and give your filex's address: it opens the install review on your filex ([Installing from a store](APP-PLUGINS.md#installing-from-a-store)). People who are not administrators can ask for it on the **Apps → App store** page, when it is shown to them ([The store screen](APP-PLUGINS.md#the-store-screen)). - **From GitHub.** *Admin → Plugins → Apps → Install an app → GitHub repository*, `BRF-Tech/filex-office-editor` and the tag of its latest release ([From GitHub, step by step](APP-PLUGINS.md#from-github-step-by-step)). - **From its files**, on a server that cannot reach GitHub. Download `filex-app.json`, `ui.zip` and `engine.zip` from the release, then *Admin → Plugins → Apps → Install an app → Upload files*: the manifest, **Interface** `ui.zip`, **Office engine** `engine.zip`, and no module. The wizard says which file is missing before it uploads anything. Read the permission review and press **Install**. From then on: - **Opening.** A `.docx`, `.xlsx`, `.pptx`, `.odt`, `.ods` or `.odp` - and, from 0.4.0, every kind of [Supported file types](#supported-file-types), 45 in all - opens in the editor in place of the preview; a person who may only read it cannot save it. A password-protected document opens after the editor asks for its password, and stays protected. The editor's frame loads pictures, styles and fonts only from the app's own package, `data:` and `blob:` (the app asks for no address of its own), so a document's linked picture from another server is not fetched. On a phone a document opens to read, with **Edit** ([On a phone](E2E-OFFICE.md#on-a-phone)). - **Saving.** **Save** writes a new version of the file, like any save in filex: the old one stays in its [history](TRASH-VERSIONING.md). A file that changed since the editor opened it is not saved over: the save becomes a conflict copy beside it, as a Document Server's does ([When the document changes while it is open](#when-the-document-changes-while-it-is-open)). A format the editor cannot write (`.doc`, `.xls`, `.ppt`...) is saved beside the file as DOCX, XLSX or PPTX ([A save in another format](#a-save-in-another-format)). - **Who and which language.** Comments and tracked changes carry the person's name, and the editor opens in the person's language or in the one fixed for everybody ([The editor's language](#the-editors-language)). - **Spell checking and hyphenation** work in filex's interface languages out of the box; an administrator adds any other language on the app's page ([Dictionaries for the office editor](APP-PLUGINS.md#dictionaries-for-the-office-editor)). - **Editing together.** Several people edit one document at once through filex's own relay, with no Document Server ([Editing together](APP-PLUGINS-API.md#editing-together-coedit-056)). - **Print, *Download as*, PDF.** Through filex, from the editor's menus; a PDF is made in the browser from the editor's own page layout. - **+ New.** *New document* gets Word, Excel and PowerPoint rows, and from 0.4.0 OpenDocument rows too ([Creating new documents](#creating-new-documents)). - **Macros** run after the editor asks whether they should (0.4.0). - **Encrypted folders.** A file of an encrypted folder you have unlocked opens in the app, decrypted in your browser and encrypted again before a save leaves it ([E2E-OFFICE.md](E2E-OFFICE.md)). On the server, with no Document Server (0.4.0 and filex 0.57): - **Office thumbnails.** The app's office engine draws the picture of an office document's first page, the same one a Document Server draws ([Thumbnails](#thumbnails)). - **Office conversions for apps.** The Convert app's office targets, PDF from an office file and MCP's `file_convert` run on the app's engine ([The office engine without a Document Server](#the-office-engine-without-a-document-server)). - **The older and rarer formats** - `.doc`, `.xls`, `.ppt`, `.rtf` and the rest of [Supported file types](#supported-file-types) - and **`.csv`**, which the app opens in its own delimiter and encoding and saves back the same way ([CSV files](#csv-files)). Before 0.4.0 the app did none of these: office documents showed their type icon, office conversions needed a Document Server, and those formats opened in the preview, a CSV in filex's own table. --- ## The ONLYOFFICE Document Server (optional) A self-hosted [ONLYOFFICE Document Server](https://www.onlyoffice.com/) (Community Edition is fine) opens office documents for in-browser editing and co-authoring, draws office [thumbnails](#thumbnails), converts office documents for apps, opens a **`.csv`** in its spreadsheet editor ([CSV files](#csv-files)), and opens in each person's own filex language or in one the administrator chooses for everybody ([The editor's language](#the-editors-language)). filex's default install runs none; the shipped compose files keep one behind the `onlyoffice` profile, pinned to the version filex is tested against ([1. Run the Document Server](#1-run-the-document-server-with-a-jwt-secret)). **Set it up:** [Prerequisites](#prerequisites), the three steps of [Setup](#setup), and - before you fill in any address - [Three machines, three addresses](#three-machines-three-addresses). Everything after that is reference. ## Prerequisites - A reachable **OnlyOffice Document Server** (Community Edition is fine). - Three addresses that each work from the machine that needs them - read [Three machines, three addresses](#three-machines-three-addresses) first. It is the single most common cause of "it tested fine and then did not work". ## Setup ### 1. Run the Document Server with a JWT secret The compose files filex ships have it as an optional service, off unless you ask for it: - **`docker-compose.yml`** (the quick start): uncomment the ONLYOFFICE lines of `.env` - `ONLYOFFICE_JWT_SECRET` at least, and filex's own two (or do [step 2](#2-point-filex-at-it) in the admin UI) - then `docker compose --profile onlyoffice up -d`. - **`deploy/compose/docker-compose.full.yml`** (the full stack, behind Caddy): add `onlyoffice` to `COMPOSE_PROFILES` in its `.env`, and fill in `ONLYOFFICE_DOMAIN`, `ONLYOFFICE_JWT_SECRET` and `FILEX_ONLYOFFICE_URL` there (the lines are commented out). Its Caddyfile already has the Document Server's host. Anywhere else, the service is: ```yaml # docker-compose.yml (excerpt) services: onlyoffice: image: onlyoffice/documentserver:9.4.0.1 environment: JWT_ENABLED: "true" JWT_SECRET: "a-long-random-shared-secret" # keep this JWT_HEADER: "Authorization" ports: - "8080:80" ``` Pick a long random `JWT_SECRET` and keep it - filex needs the **same** value. ⚠ **Pin the version.** `9.4.0.1` is the one filex is tested against; the shipped compose files use it and take another from `ONLYOFFICE_IMAGE` in `.env`. Never `latest`: it moves under you, and a new Document Server can change what a save looks like (Docs 10.0 writes a CSV with the file's own delimiter, where 9.4 wrote commas; filex reads both - [CSV files](#csv-files)). Move to another version when you choose to, and test a save first. ⚠⚠ **Always set one.** The save callback route is public, and filex tells a genuine save from a forged one only by its signature: with a secret, an unsigned callback is refused. **Without** one there is nothing to check a callback against, so filex does not run ONLYOFFICE at all: a URL with no secret leaves opening and saving off, and the admin panel and *External services* show a red warning that cannot be dismissed until a secret is set. ### 2. Point filex at it Two ways, and either is complete on its own. **In the admin UI** - *Settings → External services*: fill in the URL and the JWT secret, press **Test now**, save. **Test now** tests the values in the form as they are, before you save them, and saves nothing (the card says so), so a wrong address never reaches the editors that are already open. **Saving applies immediately; filex does not need a restart.** This is the right route when the Document Server lives in a separate compose file or you would rather not hand filex a secret through the environment. **In the environment** (or the equivalent `external_services.onlyoffice` block in `config.yaml`): ```bash FILEX_ONLYOFFICE_URL=https://office.example.com # Document Server base URL FILEX_ONLYOFFICE_JWT=a-long-random-shared-secret # MUST match JWT_SECRET above ``` Both are required: filex treats OnlyOffice as **enabled only when both are set**. ⚠ **The environment gives the first value; the admin page keeps it.** A service configured through env or `config.yaml` is written onto its stored row on a start where nothing is configured in it yet, and from then on the row is the admin page's: an edit there - switching OnlyOffice off included, which turns the Document Server off for the editor, the office thumbnails and the apps' office conversions alike (the Office editor app's engine, where there is one, goes on) - stands across restarts, and the card is labelled **"First set by the environment"**. After changing `FILEX_ONLYOFFICE_URL` or `FILEX_ONLYOFFICE_JWT` on an install that already has the service, run [`filex settings reseed --external`](CONFIGURATION.md#writing-the-environment-again-filex-settings-reseed) and restart filex (or make the same change on the card). ### 3. Make sure all three addresses work The full picture is [Three machines, three addresses](#three-machines-three-addresses); the short version: - Serve both filex and the Document Server over **HTTPS** in production. Browsers block an HTTPS page from loading an HTTP iframe (mixed content), so an HTTP Document Server behind an HTTPS filex silently fails to load. The admin page names this case rather than reporting it as "unreachable". - The Document Server URL must be one **a browser** can open - not only one filex can reach from inside the container network. - `FILEX_PUBLIC_URL` must be resolvable **from the Document Server container/host** (it fetches source + posts callbacks there). In Docker, that usually means a real hostname or the compose service name - never `http://localhost`. Then press **Test** on *Settings → External services* and read **both** lines it prints. *From the filex server: reachable* and *From this browser: not reachable* together mean the address is container-internal: the editor loads in the browser, so it will fail there. That's it - reopen an Office file in filex and it should launch the editor. --- ## Three machines, three addresses ⚠ **Read this before you fill in the Document Server URL.** Almost every "it tests fine and then does not work" report is this and only this: | Address | Who has to reach it | How it is checked | |---|---|---| | the **Document Server URL** | your **browser** - it loads the editor's JavaScript straight from there | the admin page probes it **from your browser** | | the same **Document Server URL** | the **filex process** - it polls `/healthcheck` | the **Test** button | | the **callback URL** (or `FILEX_PUBLIC_URL` when it is empty) | the **Document Server** - it fetches the document and POSTs the save back | the **Test** button asks the Document Server to download a one-shot URL through filex's signed document fetch endpoint and reports whether it arrived and was served | | the Document Server's **converted copy** (`/cache/files/.../Editor.bin`) | your **browser** again - after the Document Server has fetched and converted the document, the editor loads the converted copy from this address | not by the Test. The Document Server builds this address from the `Host`, `X-Forwarded-Host` and `X-Forwarded-Proto` headers it receives, so the reverse proxy in front of it has to pass them (see [The browser leg after the download](#the-browser-leg-after-the-download)). When the editor says "Download failed", filex says whether it got this far | These are three different machines, and an address that works for one can be useless to another. The classic case: on Docker or podman you type the container name, `http://onlyoffice`. filex reaches it, **Test goes green**, and your browser cannot resolve that name at all - so the editor fails with the same message as a missing configuration. **A green server line means "the filex process reached that URL". Nothing more.** That is why the Test reports each leg on its own: - **The browser leg.** The admin page probes the Document Server URL from your browser and prints *From the filex server: …* and *From this browser: …* as two separate lines; next to the field it warns about an address a browser cannot load (a bare container name, or `localhost` on an install published elsewhere). - **The callback leg.** filex hands the Document Server's conversion endpoint a one-shot URL on the **document fetch endpoint itself** (`/api/files/onlyoffice/fetch`), signed the way a document's URL is and checked by the same signature check, with filex's configuration in force, and watches for it to arrive: the page answers *the document server reached filex* or *it did not*, with the exact URL it was given. A reverse proxy that forwards some other path but not this one, or a fetch filex refuses, fails the Test as it fails every document; the line then says *filex refused the request* and why. Only the storage read is left out, because there is no document behind a probe (the editor's own diagnosis covers that: [Failure: editor shows "Download failed"](#failure-editor-shows-download-failed)). A Document Server that refuses the request's signature is reported as **unmeasured**, not as a broken route: that is a JWT problem. - **JWT.** The Test asks the Document Server a **second time, without a token**. One that enforces JWT refuses it (error -8); one that takes it has JWT off, and the card shows a warning: with JWT off the Document Server still checks the token in filex's editor configuration, against its own secret, and refuses it, so no document opens ("The document security token is not correctly formed"); it refuses to download from private addresses, and posts its save callbacks unsigned, which filex refuses. Its log then says `checkJwt error ... invalid signature` for the editor and `... is not allowed. Because, It is private IP address` for the download. filex also warns about the shapes that certainly cannot work before you press anything: a callback address of `localhost`, `127.0.0.1` or `0.0.0.0`, or a hostname filex itself cannot resolve. ### Two commands that say which half is wrong ```bash # the browser leg - run this from a workstation, not from the server curl -I /web-apps/apps/api/documents/api.js # the callback leg - run this from INSIDE the document server container podman exec -it onlyoffice curl -I "$FILEX_PUBLIC_URL/healthz" ``` Both must answer `200`. The first failing while filex's own Test passes is exactly the container-name case above. The second failing means saves will be lost even though the editor opens. ### When the Document Server needs a different address from your users `FILEX_PUBLIC_URL` builds two different things: every share link a person clicks, and the document URL the Document Server fetches. Those usually want the same address, and sometimes they cannot be the same - a Document Server on a container network may only reach filex as `http://filex:5212`, while your users need `https://files.example.com`. Set the **callback URL** for that. It is the address the Document Server uses, and nothing else reads it: ```bash FILEX_PUBLIC_URL=https://files.example.com # people, share links, emails FILEX_ONLYOFFICE_CALLBACK_URL=http://filex:5212 # the Document Server alone ``` The same field is in the admin page under the Document Server URL, and applies live. Leave it empty - the default, and what every single-address install wants - and the public URL is used. ## Configuration reference | Env var | `config.yaml` | Required | Description | |---|---|---|---| | `FILEX_ONLYOFFICE_URL` | `external_services.onlyoffice.url` | yes | Document Server base URL (e.g. `https://office.example.com`) | | `FILEX_ONLYOFFICE_JWT` | `external_services.onlyoffice.jwt_secret` | yes | Shared HS256 secret - identical to the Document Server's `JWT_SECRET` | | `FILEX_ONLYOFFICE_CALLBACK_URL` | `external_services.onlyoffice.callback_url` | no | The address the **Document Server** uses to reach filex. Empty (the default) means `FILEX_PUBLIC_URL`. Set it only when those two must differ - see [When the Document Server needs a different address](#when-the-document-server-needs-a-different-address-from-your-users) | | `FILEX_ONLYOFFICE_FRAME_ORIGIN` | `external_services.onlyoffice.frame_origin` | no (recommended) | The Document Server's own origin (`https://docs.example.com`), whose proxy sends `/filex-frame/*` to filex: the editor's `api.js` runs in a frame there, not in filex's page. Read at start - see [The editor in a frame of its own](#the-editor-in-a-frame-of-its-own) | | `FILEX_APP_UI_ORIGIN` | `app_ui_origin` | no | The origin app interfaces are served from; when `FILEX_ONLYOFFICE_FRAME_ORIGIN` is empty the editor's frame is served there | | `FILEX_ONLYOFFICE_LANG` | `external_services.onlyoffice.editor_lang` | no | The editor's language: `auto` (the default - each person's own filex language) or a language the editor offers (`de`, `fr`, `tr`, `pt-PT`, `zh-TW`, ...) for everybody. Seeds the row's language when it has none, like the URL; see [The editor's language](#the-editors-language) | Both are optional in the sense that the **admin UI** can supply them instead - whichever way they arrive, the value the running process uses is the one in the `external_services` table, read on every request. `GET /api/admin/external` returns `env_managed: true` for a service the environment pins. The signed fetch URL is valid for **1 hour** by default. --- ## How it works Three pieces cooperate, all signed with one shared secret (HS256 / HMAC-SHA256): ``` Browser filex OnlyOffice Document Server │ open doc ───────────►│ │ │ ◄── JWT-signed editor config (documentServerUrl + doc url) │ │ load iframe ──────────────────────────────────────────────────►│ │ │◄── GET signed fetch URL (source bytes) │ (1) fetch │ │ /api/files/onlyoffice/fetch │ │ …user edits… │ │ │ │◄── POST callback (JWT) on save ────────│ (2) save │ │ /api/files/onlyoffice/callback │ │ │──► write revision back to storage │ ``` 1. **Config** - filex builds a JSON editor descriptor, signs it with the shared secret, and hands it to the embedded iframe. It contains a **signed, short-lived fetch URL** the Document Server uses to pull the current bytes. 2. **Fetch** - `GET /api/files/onlyoffice/fetch?...&sig=...` streams the source to the Document Server. Public but unguessable (HMAC over node id + expiry) and time-limited - no filex session needed, because the Document Server is a server, not the user's browser. 3. **Callback** - on save the Document Server POSTs to `POST /api/files/onlyoffice/callback?node=` with a JWT; filex verifies the JWT, downloads the saved revision **from the Document Server's own address**, and writes it back through the storage driver. It acts on the signed payload only, and only on a document key it made for that document - see [What a save callback is trusted for](#what-a-save-callback-is-trusted-for). The shared secret configured in filex - on *Settings → External services*, or seeded from `FILEX_ONLYOFFICE_JWT` - **must equal** the Document Server's JWT secret. That is the entire trust relationship. ### Thumbnails The same Document Server draws the thumbnail of an office document - a picture of its first page - through its **conversion service** (`ConvertService.ashx`) rather than the editor. What is sent, the limits, the retries and what each answer means are in [thumbnails.md → Office through OnlyOffice](thumbnails.md#office-through-onlyoffice). What it means for the Document Server: - **Its own fetch address.** It downloads the document from the same fetch endpoint the editor uses, with an address signed **for that purpose** (`p=thumb`, part of the HMAC, 10 minutes; an app's office conversion uses `p=convert`), so a thumbnail's address is never an editor's, nor the other way round. filex downloads the picture **only from the Document Server's own address**; an answer naming any other address is refused and that address is never fetched. - **Never an encrypted file.** The thumbnail pipeline does not ask for an end-to-end encrypted file, and the fetch endpoint refuses ciphertext for a conversion (415). - **The editors go first.** A Community Edition Document Server runs one converter, shared with the editors, and an editor opening a document has the higher priority. filex therefore sends it one document at a time per process (`thumbs.office_slots`, 1 to 4), at most `thumbs.office_max_mb` (25 MB), 60 seconds each, and asks again after a back-off (2, 8, 32 minutes, about 2 and about 8 hours) when it did not answer: an install with many office documents gets them drawn gradually, as they are listed, and an editor is never queued behind a folder of thumbnails for long. - **The editor's diagnosis is not touched.** What the fetch endpoint answers a thumbnail's download is kept apart from the record behind the editor's "Download failed" explanation (`GET /api/files/onlyoffice/diagnose`). Without a Document Server, the office engine the Office editor app brings (0.4.0) draws the same pictures on the server ([The office engine without a Document Server](#the-office-engine-without-a-document-server)); with both, **Default apps → Office engine** says which one draws. With neither, office documents show their type icon, and *Admin → Tools → Thumbnail repair* lists them as "No office engine runs". Install the app or connect a Document Server (no restart) and they are drawn as they are listed. ### What a save does A save-back is a write like any other write in filex, and goes through the same shared post-write gate every other surface does. First the callback checks that the file is still the version the editing session opened; a file that changed since is not written over, the save goes beside it ([When the document changes while it is open](#when-the-document-changes-while-it-is-open)). Before a byte is fetched it asks the question every write door asks: not a name filex keeps for itself (the desktop's working copy and its owner's draft excepted), not a document an app has frozen, and **nothing inside a vault** (encryption level 3, [E2E-VAULT-FORMAT.md](E2E-VAULT-FORMAT.md#writes-from-anywhere-else)), where only the vault API writes - the save is answered `{"error": 1}` and nothing is written, over the document or beside it. Then, in order, it: 1. takes a **version snapshot**, so the revision it is about to replace stays recoverable from the file's history - and **refuses the save** if that snapshot cannot be taken, because losing history is not a reason to also lose the file; 2. writes the revision through the storage driver; 3. refreshes the node row's size, mime, etag and mtime from the driver; 4. runs the post-write gate: **re-index** (so content search returns the new text, not the old), **thumbnail**, a **realtime change frame** so a browser with the folder open updates instead of finding out on its next navigation, the canonical **`file.updated`** webhook event stamped `meta.origin: "onlyoffice"`, and an **antivirus scan** ([PROTECTION.md](PROTECTION.md#antivirus-clamav)). ⚠ The event is `file.updated` and never `file.uploaded`: a save-back replaces a document that was already there. A subscriber that watched `file.uploaded` for edits needs to subscribe to `file.updated` as well. ⚠ The event carries **no actor**, so the in-app notification is admin-visible rather than scoped to one person's bell: the callback is a public route the Document Server posts, not a signed-in browser, so there is no request user to attribute it to - the same treatment every other actorless write gets (the sync walk, the async ops worker). The **webhook** delivery is unaffected. #### When the scan runs, and why the status code decides it The Document Server tells filex which kind of save this is, and the two kinds get different answers: | Callback status | What it means | Scan | |---|---|---| | **2** - ready for saving | Every editor **closed** the document and the server assembled the final revision. It arrives once per editing session, roughly **10 s after the last editor disconnects**. | **Immediately**, like an upload. The bytes are final and nobody is still typing. | | **6** - force save | An **interim** save with the document still open. filex never asks for one and the document server does not send them by default (`autoAssembly` is off); an operator can switch them on, and then they repeat for as long as somebody keeps the document open. | **Debounced** - one scan per file per save window, the same treatment a Ctrl+S burst gets in the built-in text editor. | Statuses 1, 3, 4 and 7 write nothing, so they announce nothing. ⚠ On a default install this means exactly **one scan per editing session**, run at once, not deferred. With force-save switched on, a long session costs one scan per window while it is open **plus** the immediate scan of the final revision when it closes - because a document server that dies mid-session never sends status 2 at all, and then the debounced scan of the interim bytes is the only one there will ever be. The window is the setting on **Admin → Protection** (`antivirus.save_scan_window_minutes`, default 30 min), one window shared with the text editor, not a second knob. --- ## The editor in a frame of its own The Document Server's editor API is a script, `web-apps/apps/api/documents/api.js`, that an integrator loads into its own page - and a script in filex's page can do whatever that page can: read the key the web client keeps for the session (`sessionStorage`), call filex's API as the signed-in person, read the page. That is why the [security notes](#security-notes) say to trust the Document Server as much as filex itself. **Set `FILEX_ONLYOFFICE_FRAME_ORIGIN` to the Document Server's own origin and that script runs there instead.** filex then serves a small page, `/filex-frame/editor`, through the Document Server's reverse proxy, and the viewer frames it instead of loading `api.js` into its own page. The script runs on the origin it came from: no origin gets anything it did not have. ``` filex's page (files.example.com) the frame (docs.example.com) POST /api/files/onlyoffice/config ──► filex ◄── { documentServerUrl, config (signed), frame }