A GitHub Actions incident left the 1.8.0 release build queued indefinitely, so no Docker image was ever published for that tag. 1.8.1 re-runs the release with the same code, plus one plugin slot that merged in the meantime.
Security: Manage S/MIME and PGP public keys and configure Stalwart encryption at rest from account security settings
Notifications: Background notification onboarding, sequenced after the PWA install prompt
Navigation: Deep links for mail, calendar, contacts, files, and settings, with screen-reader improvements
Settings: Always show the Unified Mailbox switch in Layout settings
i18n: Catalan translation
i18n: Localized editor toolbar across every locale
Plugins: Contact API β contact.get, contact.create, contact.update, contact.search
Plugins: contact-cryptokeys UI slot, behind the ui:contact-cryptokeys permission, so a plugin can render a contact's crypto keys in place of the built-in list
Plugins: user.getAccounts and user.getIdentities
Plugins: user.logout method and logout hook
Plugins: Crypto API β public-key management and encryption-at-rest control on the privileged tier
Plugins: onBeforeBlobUpload can offload an attachment to external storage
Plugins: Binary Blob/File bodies for api.http.post
Plugins: upfiles.get moved behind email:blob-read, off the privileged tier
Dev: Mock JMAP defaults now include nested tags
Changes
Mail: The "Reset to defaults" button is gone from tag settings β one stray click wiped a carefully built tag list, with no confirmation and no undo
Fixes
Send: Send through the identity's own account client so DKIM matches the From domain (#461)
Send: Split Name <addr> recipients into the JMAP name and email fields (#672)
Send: Time out stalled JMAP requests so a send can't hang forever (#702)
Mail: Keep inline images when replying to application/octet-stream cid parts (#543)
Mail: Reply on your own thread message no longer re-addresses the original recipients (#703)
Mail: Empty folder no longer stops after 500 emails (#711)
Mail: Move messages across accounts from the "Move to" context menu, preserving read state and deferring source removal to Stalwart
Mail: Stop resurrecting deleted rows in the mailbox refresh merge
Mail: Keep the message/rfc822 attachment visible after inline unwrapping
Mail: Strip sender and recipient names from forward-as-attachment filenames
Mail: Open mailto: links in the built-in composer
Mail: Spell out the full tag path in drag-and-drop toasts, so Personal/Receipts and Work/Receipts no longer read as the same tag
Mail: Act on current email state in the context menu's mark-as-read instead of a stale copy
Mail: Match the selected-row tint between dark and light mode
Mail: Restore lost animations after the Tailwind config move
Drafts: Restore the sender identity when reopening a draft
Calendar: Stop re-adding the organizer to the attendee list on every save (#731)
Calendar: Stop re-probing shared accounts that have no calendar access
Calendar: Route the parse dump through the debug logger
Project variable resolution now matches the Docker Compose CLI
Previously, environment variables set on Arcane's own container (such as PORT) could be picked up when resolving ${VARIABLE} references in your projects' compose files. This could cause surprising results β for example, a project using ${PORT:-8191} could end up binding to Arcane's own port instead of its default (#3499).
Starting with this release, variable references in a project's compose file resolve only from:
your global Variables (.env.global)
the project's own .env file
defaults in the compose file itself (${VAR:-default})
timezone and locale from Arcane's environment (TZ, LANG, LANGUAGE, LC_ALL)
This means a project deployed through Arcane now resolves its variables the same way as running docker compose up in the project directory, and projects can no longer accidentally pick up Arcane's own configuration.
If a project referenced a variable that was only defined on Arcane's container, add it under Customization β Variables to share it with all projects, or to that project's .env file. No other action is needed.
New features
simplify build registry image references (#3243 by @traeli)
add gated admin password reset to interal CLI (#3470 by @kmendell)
per user passkey mfa / passwordless login support (#3493 by @kmendell)
custom payload generic webhooks and google chat notifications (#3417 by @khanhx)
bump docker/compose to v5.4.0, gate diverged-volume recreation behind deploy option, pull pre_start hook and image-volume images (#3502 by @kmendell)
Bug fixes
synchronize structured log toggle state (#3418 by @Kstateag)
users-permissions: move server code into server/src (#26105)
π Enhancement
content-type-builder: support required on relation attributes (#27080)
core: look models up on the registries in getModel (#27143)
database: reduce per-column work when mapping rows to entities (#27144)
β οΈ Changes to be aware of
Filter operators: literal wildcards and true case-insensitive equality
$eqi / $nei now do real case-insensitive equality (= LOWER(?)), not LIKE, so values with %, _, or a trailing \ no longer act as wildcards or crash some databases. Substring operators ($contains, $startsWith, $endsWith, and case-insensitive variants) now treat %, _, and \ in the filter value as literal characters. If you relied on % / _ inside those filters as SQL wildcards, update filters to match the new literal semantics.
(#26476)
If you are using Part-DB it would be helpful if you fill out this short survey on your usage of Part-DB (Google Forms): https://forms.gle/Q15twx3YYq3qCNfe8
Restricted the settings fields readable with minimal app access to those actually needed by non-admin users, no longer exposing admin-only and sensitive AI configuration fields (#27996)
The minimal app permissions now grant read access to only a subset of directus_settings fields. This applies to new policies, existing policies are untouched.
Replaced the TinyMCE editor powering the WYSIWYG with Tiptap... (#27754) The WYSIWYG interface now runs on Tiptap instead of TinyMCE
tinymceOverrides no longer has any effect. Stored values are kept and a console warning is logged, but the editor ignores them. Use the fontsize/fontfamily toolbar menus and customFormats instead.
TinyMCE is no longer bundled with the app, so anything depending on it (custom plugins, skins, content CSS, the global tinymce object) no longer applies.
Existing content that contains markup the editor would normalize now locks the field read-only until the warning dialog is confirmed. Editing and autosave are blocked while locked, including raw-value editing.
Fixed deployment webhooks resolving a project from the wrong provider when external IDs collide (#27816)
The DeploymentProjectsService.readByExternalId method now takes the deployment ID as its first argument (i.e. readByExternalId(deploymentId, externalId))
Added support for multi-collection flat data imports (#27984) Import file size is now capped by default
A new IMPORT_MAX_FILE_SIZE environment variable (default: 50mb) limits the size of uploaded import files and schema snapshots. Previously, imports were effectively unrestricted, allowing files larger than 50mb to be processed. With this change, imports exceeding the configured limit will be rejected. Increase IMPORT_MAX_FILE_SIZE to restore the previous behavior.
Updated background query flag handling for POST /utils/import/:collection
The background query flag now treats a valueless indicator (i.e. ?background) as true. If you previously relied on a valueless background flag being interpreted as false, pass an explicit value instead (i.e. ?background=false).
Added a mode parameter and partial snapshot support to the schema diff endpoint (#27984)
The SDK schemaDiff command now takes its options as an object (schemaDiff(snapshot, { force, mode }))
Added support for restricting image transformation output size via ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION (#27995) Image transformation output is now restricted
Image transformations that project an output larger than ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION (default 3000 px) on either axis are now rejected with an IllegalAssetTransformationError.
@directus/app
Replaced the TinyMCE editor powering the WYSIWYG with Tiptap (#27754 by @alvarosabu)
To avoid data loss, the editor preserves attributes (class, id, title, role, lang, dir, data-*, aria-*) and non-schema semantic tags. If stored HTML still contains markup the editor would normalize, the field is locked read-only with a warning dialog, so no edit or autosave can rewrite it before you confirm; raw-value editing is disabled while locked so the warning can't be bypassed.
@directus/api
Restricted the settings fields readable with minimal app access to those actually needed by non-admin users, no longer exposing admin-only and sensitive AI configuration fields (#27996 by @br41nslug)
Fixed deployment webhooks resolving a project from the wrong provider when external IDs collide (#27816 by @MahinAnowar)
Fixed the translations split view hiding the second language when resizing the window (#27681 by @valerkahere)
Fixed geometry fields losing their subtype on schema changes (#27828 by @rajkumar0932)
Fixed stale dynamic permission presets after editing current account (#27899 by @scarab-systems)
Fixed a type error in the module bar default configuration (#27944 by @kheiner)
Amended app's save-as-copy logic to not create new items when only adjusting order on relationals (#27871 by @AlexGaillard)
Fixed the auth module registering a permanent cookie polling interval (#27851 by @dstockton)
Fixed presentation fields allowing required and/or readonly to be set (#27688 by @sourav-18)
Fixed missing translations for the Datetime display timezone options (#28000 by @lazerg)
Fixed dropdown menus shifting position when flipped above their trigger. (#27958 by @Harshith-muddasani)
Updated the remaining *.io references to the current *.com domains where possible (#27948 by @kheiner)
Updated the onboarding flow to replace the Privacy Policy link with the Data Processing Agreement (#27934 by @JamesW1)
Fixed live preview requesting a draft version before it exists, which caused a forbidden error (#27848 by @dstockton)
Fixed issue causing singleton primary key mismatch (#27919 by @robluton)
Restricted the settings fields readable with minimal app access to those actually needed by non-admin users, no longer exposing admin-only and sensitive AI configuration fields (#27996 by @br41nslug)
Fixed many-to-one fields to display the saved key when the referenced item is inaccessible due to permissions (#27899 by @scarab-systems)
Fixed a Forbidden error when publishing an itemless content version without delete permission on directus_versions (#27892 by @alex-hsieh)
@directus/api
Updated the remaining *.io references to the current *.com domains where possible (#27948 by @kheiner)
Added global setting for default save action (#27993 by @robluton)
Fixed OpenAPI spec error schema to match API error format (#27885 by @kheiner)
Restricted license key previews to administrators after initial project setup (#27886 by @ComfortablyCoding)
Stopped logging the missing custom IP header warning on /server/ping and /server/info, which are commonly hit directly (health checks) (#27903 by @dstockton)
Updated axios, sharp, liquidjs, js-yaml, minimatch, adm-zip, brace-expansion, linkify-it, fast-xml-parser and tar to address CVEs (#27990 by @br41nslug)
Fixed parsing of the deep query parameter, GraphQL nested arguments, and CSV import headers so keys dont collide with built-in object property names (#27992 by @br41nslug)
Fixed aliased relational fields returning null in GraphQL when nested inside a Many-to-Any field (#27864 by @apoorva-01)
Fixed IP denylist not enforced for AI chat file downloads (#27994 by @br41nslug)
Fixed background imports (POST /utils/import/:collection?background=true) intermittently hanging and importing nothing when running behind a streaming proxy or CDN. (#27862 by @dstockton)
Added an IMPORT_MAX_FILE_SIZE environment variable that caps the size of an uploaded import file, returning 413 Content Too Large when exceeded. Unset (unlimited) by default.
Fixed requests referencing duplicate primary keys resulting in forbidden error (#27882 by @lazerg)
Fixed manual flows triggerable by non authenticated users (#27997 by @br41nslug)
Fixed count, countAll, and PK counts being inflated when filtering across relations (#27926 by @ComfortablyCoding)
Fixed TUS uploads not respecting FILES_MIME_TYPE_ALLOW_LIST (#27793 by @amitmishra11)
Fixed WebSocket handlers not validating query parameters (#27845 by @tsushanth)
Fixed unnecessary schema cache rebuilds on permission-related changes (#27876 by @dstockton)
@directus/sdk
Updated the remaining *.io references to the current *.com domains where possible (#27948 by @kheiner)
Fixed nested filters on relational fields losing type inference, so filtering a related collection's field (e.g. filter: { o2m: { id: { _eq: 5 } } }) is now type-checked instead of silently accepting any value (#27815 by @MahinAnowar)
Removed phantom timestamp from directus_operations (#27942 by @kheiner)
Fixed an unhandled rejection in the sdk realtime client when the connection closed during a heartbeat ping (#27846 by @apoorva-01)
@directus/specs
Updated the remaining *.io references to the current *.com domains where possible (#27948 by @kheiner)
Fixed OpenAPI spec error schema to match API error format (#27885 by @kheiner)
Fixed background imports (POST /utils/import/:collection?background=true) intermittently hanging and importing nothing when running behind a streaming proxy or CDN. (#27862 by @dstockton)
Added an IMPORT_MAX_FILE_SIZE environment variable that caps the size of an uploaded import file, returning 413 Content Too Large when exceeded. Unset (unlimited) by default.
Added missing /users registration and 2FA endpoint openapi specs (#27857 by @kheiner)
Removed OpenAPI query parameters that the underlying controllers never honor (#27922 by @kheiner)
Added missing id path parameter to the /comments/{id} OpenAPI spec (#27884 by @kheiner)
@directus/constants
Updated the onboarding flow to replace the Privacy Policy link with the Data Processing Agreement (#27934 by @JamesW1)
@directus/system-data
Restricted the settings fields readable with minimal app access to those actually needed by non-admin users, no longer exposing admin-only and sensitive AI configuration fields (#27996 by @br41nslug)
@directus/env
Fixed background imports (POST /utils/import/:collection?background=true) intermittently hanging and importing nothing when running behind a streaming proxy or CDN. (#27862 by @dstockton)
Added an IMPORT_MAX_FILE_SIZE environment variable that caps the size of an uploaded import file, returning 413 Content Too Large when exceeded. Unset (unlimited) by default.
@directus/utils
Fixed background imports (POST /utils/import/:collection?background=true) intermittently hanging and importing nothing when running behind a streaming proxy or CDN. (#27862 by @dstockton)
Added an IMPORT_MAX_FILE_SIZE environment variable that caps the size of an uploaded import file, returning 413 Content Too Large when exceeded. Unset (unlimited) by default.
@directus/schema
Fixed MSSQL schema introspection reporting the byte size as max_length for non-character types (#27825 by @BIGSUS24)
@directus/storage-driver-cloudinary
Fixed Cloudinary uploads failing when the configured root contains whitespace (#27841 by @itsabhay1)
deps: upgrade handlebars, axios, tar, and related transitive deps (#27091)
deps: bump @radix-ui/react-toolbar from 1.0.4 to 1.1.11 (#27059)
email-nodemailer: migrate unit tests from jest to vitest (#27074)
email-sendmail: migrate unit tests from jest to vitest (#27075)
upload-local: migrate unit tests from jest to vitest (#27073)
β οΈ Changes to be aware of
Required media and relations: opt-in strictRelations
New config api.documents.strictRelations enforces required media and relations on publish (drafts can still be empty). On by default for new projects; existing apps are unchanged until you set it. To opt in, set documents.strictRelations: true in config/api.
(#27028)
Empty multiple media / morphMany now returns []
Populated empty morphMany relations (including type: 'media', multiple: true) serialize as [] instead of null, matching other to-many relations. This is unconditional and not gated by strictRelations. If clients, webhooks, or integrations check field === null for empty galleries / morphMany, treat [] as empty instead (e.g. !field?.length).
(#27090)
This is a security release to address a range of vulnerabilities:
External Authentication Use (OIDC/SAML2/LDAP) could potentially mismatch external authentication system users to BookStack users upon login, where unique IDs are very similar (same ID text but different casing, or accented characters).
The login form could be abused to use timing to gain information about if a user exists in the system. This was already limited by request rate-limits, but could still have assisted targeted scenarios.
Certain editor content could be used to load interactive content over file links when exported, which could then pose a risk after being exported in Windows environments which use NTLM.
API errors could include debug details by default, and therefore potentially expose some system details like file paths.
With our default PDF rendering option, certain content could be used to access/check for files on the BookStack host beyond the scope of what we'd expect.
Upgrading is generally advised, but more so for instances using OIDC, SAML2 or LDAP authentication.
Thanks to Tanner Marks (GitHub), Gurmandeep Deol (LinkedIn) and whale120 (Blog, X, Working with DEVCORE Internship Program) for responsibly reporting issues addressed in this release.
Full List of Changes
Updated PHP package versions.
Updated translations with the latest Crowdin changes.
Updated login with fake hash and random delay on failed login.
Updated allow list content filter to force the use of schemes.
Updated allow list content filter with smarter srcset attribute parsing.
Updated user external authentication ID queries, and database column collation, to ensure an exact match.
Updated API exception handler to follow a more controlled error message approach.
Fixed image delete API endpoint to avoid an error scenario.
there is a discord server with an @everyone in case of future important updates, such as vulnerabilities (most recently 2026-07-27)
β οΈ ATTN: this release fixes a vulnerability in FTP and FTPS (not SFTP)
the FTP-server (default-disabled) would allow uploading to any folder that the copyparty process had permission to write to, but with certain limitations; see GHSA-phv8-wgjp-g4p9
After upgrade, you need to run php bin/console doctrine:migrations:migrate (or equivalent) as webserver user after upgrade.. If you are running a docker container, use sudo docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate, or sudo -E inside the docker container, to ensure that the migrations are applied to the correct database.
Important
If you are using Part-DB it would be helpful if you fill out this short survey on your usage of Part-DB (Google Forms): https://forms.gle/Q15twx3YYq3qCNfe8
New features
Provide MCP tools to allow AI agents access Part-DB's data (read-only for now)
UpSnap is, and always will be, free and open source software.
If someone is asking you to pay money for access to UpSnap binaries, source code, or licenses, you are being scammed.
The official and only trusted source for UpSnap is this repository (and its linked releases).
Do not pay third parties for something that is provided here for free.
If you are using Part-DB it would be helpful if you fill out this short survey on your usage of Part-DB (Google Forms): https://forms.gle/Q15twx3YYq3qCNfe8
Changes
Made it more clear that TRUSTED_HOST value must not be quoted in docker-compose file, both in documentation and messages (#1453)
Import: document EDA/KiCad columns, add regression tests, fix eda_invisible inversion by @Sebbeben in #1436