Skip to content

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 13:48
· 100 commits to main since this release
65e2fdc

Upgrading from 1.2.1

No format or env var was removed, and no database migration runs; /redoc, which rendered empty, is switched off. Every deployment should update: this release carries the fixes for critical advisories in the Python packages Pillow (GHSA-8g5g-8vmr-9782), certifi (GHSA-6q48-6c7w-9gw2) and brotli (CVE-2026-111497), and for two medium ones in the libheif that pillow-heif bundles (GHSA-9c75-9g8r-4728, GHSA-vv35-6hxg-95x8); see the Security entries in the changelog. Still accepted until 2026-11-02, because Swagger UI does not use the options they need: five DOMPurify advisories in the Swagger UI bundle on /docs, one rated critical and one high upstream (listed in docs/security-overview.md). Check before pulling:

  • Pinned to 1.2 or 1.2.1 (or their -office variants): there is no 1.2.2, so these image tags stay on 1.2.1 without the fixes. Move to 1.3 (or 1.3.0); for a backport onto 1.2, write to security@filemorph.io.
  • New setting: LOCAL_TOOLS_ENABLED (default false) prepares processing in the browser. Left off, nothing changes. FastAPI's automatic OpenTelemetry exporter setup now needs FASTAPI_OTEL_AUTO_CONFIGURE=true; this concerns only an install that added an OpenTelemetry SDK itself, as the image ships none.
  • New refusals: the table conversions (XLSX → CSV, CSV → XLSX, CSV → JSON, JSON → CSV) and PDF compression now run in a child process, as PDF → TXT already did, and stop after 90 seconds; on Linux the child is also capped at 1 GiB of address space (PDF compression: 3 GiB) and 2 GiB per file written (PDF compression and PDF → TXT: 1 GiB). A file past a limit gets 400 (invalid_input; PDF compression: pdf_too_complex) with the next step in the message. With pypdf 6.21 a few rare PDFs that 1.2.1 read now get the "could not read" message.
  • Target sizes: a result is now at most the target, no longer up to 3 % (PDF: 5 %) above it. Where even the lowest quality is too big, images, and a PDF's embedded images, are scaled down; such results say so in X-FileMorph-Warnings, the new X-FileMorph-Batch-Notes header and manifest.json, which a batch ZIP now also carries when no file failed. On /pdf/compress, X-FileMorph-Converged now means "at most the target".
  • Messages and codes: the limit messages no longer say "upgrade your plan"; they name a free account where accounts exist, or self-hosting. New error codes: pdf_too_complex and batch_too_large, and for the new video → GIF conversion video_too_long and video_too_complex. A file part with no file in it, which a plain browser form sends for an empty file input, now counts as missing: the upload routes answer the generic 422, and a batch that also carries real files leaves it out. A client that matches message texts needs updating.
  • Instances without accounts (no DATABASE_URL): no Sign In link, and the account pages answer 404.
  • APP_BASE_URL: the home page's curl example now targets it on every instance instead of filemorph.io; left at the default it shows http://localhost:8000.
  • Compose and systemd: docker-compose.yml caps the container at 4096 processes and threads and turns off core dumps. The systemd unit in docs/self-hosting.md gains the same limits plus NoNewPrivileges=yes, PrivateTmp=yes and ProtectSystem=strict; a compose file or unit copied earlier can take them over. Where MAX_GLOBAL_CONCURRENCY was raised on a host with many cores, raise the cap too (about 200, plus 3 per CPU core for each parallel conversion; "Capacity tuning" in docs/self-hosting.md).
  • Logs: logged errors keep their type and traceback but no longer their text unless APP_DEBUG=true; the WeasyPrint, fontTools and pypdf loggers are silent; a child stopped at its CPU-time limit shows status -9. An alert rule that matched error texts needs a new pattern.
  • Image and TLS: the image moves to the python:3.14-slim build of 2026-10-21 (Python 3.14.8, a CA store without the compromised emSign root). Installed without the image, update the operating system's ca-certificates package: outgoing mail trusts it.

The full list of changes is in CHANGELOG.md.


What's Changed

  • fix(docs): serve the API explorer from our own origin — /docs works under the CSP again by @MrChengLen in #243
  • fix(logs): logged errors keep their type and location, not their text by @MrChengLen in #241
  • ci(security): check vendored browser code against OSV — blocking like pip-audit by @MrChengLen in #244
  • fix(convert): tables and PDF compression run in their own process — stopped after 90 s, 1 or 3 GiB on Linux by @MrChengLen in #242
  • deps(security): certifi 2026.10.19 + Pillow 13.0.0 — critical advisories by @MrChengLen in #245
  • fix(privacy): the privacy notice names every right, the Cloudflare transfer and a 30-day log limit by @MrChengLen in #240
  • fix(convert): the log names a failed child's error type — its text stays out, as since #241 by @MrChengLen in #246
  • feat(pricing): FileMorph is free — pricing page, navigation and account texts say so by @MrChengLen in #248
  • fix(security): emSign root gone — Stripe via certifi, base image with Debian fix by @MrChengLen in #247
  • feat(pdf): pdf_too_complex — the compress page links on to Extract PDF pages by @MrChengLen in #250
  • fix(convert): read CSV uploads as a stream — memory follows the longest row by @MrChengLen in #249
  • feat(limits): limit messages name the free way past a limit — no "upgrade your plan" by @MrChengLen in #251
  • feat(ui): every processing button says where the work runs — "via our server" by @MrChengLen in #253
  • feat(engine): hash-pinned engine mount and CSP profiles — off by default by @MrChengLen in #255
  • fix(limits): free account only where accounts exist — batch-size 400 in German by @MrChengLen in #254
  • deps: pypdf 6.21.2, opentelemetry-api 1.45.1 — unblock CI; 3 DOMPurify advisories accepted to 2026-11-02 by @MrChengLen in #257
  • fix(accounts): no sign-in links without accounts — account pages 404 there by @MrChengLen in #258
  • deps: brotli 1.2.1 — fixes CVE-2026-111497 in the decoder by @MrChengLen in #260
  • feat(convert): file size limit for conversion children — tables 2 GiB, PDF 1 GiB by @MrChengLen in #252
  • feat(convert): hard limits for the conversion children — soft and hard alike by @MrChengLen in #256
  • feat(compress): the target size is an upper bound — scale down when quality can't reach it by @MrChengLen in #261
  • docs(changelog): release 1.3.0 — fold changelog.d, bump the version by @MrChengLen in #265
  • deps: python-all batch — FastAPI 0.154, Pydantic 2.14, SQLAlchemy 2.1.5, uv 0.13.4 (supersedes #259) by @MrChengLen in #263
  • feat(convert): video → GIF — two-pass palette, 30-second limit, never cut short by @MrChengLen in #266
  • feat(convert): children isolate themselves further — no forks, no new privileges by @MrChengLen in #262
  • docs(changelog): 1.3.0 re-fold — libheif, video → GIF, child isolation by @MrChengLen in #270

Full Changelog: v1.2.1...v1.3.0