From 558292030ff041847feccefddb18eb2c44cc68e0 Mon Sep 17 00:00:00 2001 From: Christopher Jennings <1398135+ChristopherJennings@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:37:46 -0400 Subject: [PATCH 1/4] Document Directus 12.2.0 release changes Verified against the v12.2.0 tag rather than the release notes, which contradict themselves on the IMPORT_MAX_FILE_SIZE default. The breaking changes page listed only the WYSIWYG migration. Added the remaining six: - App Access policies now read a limited set of directus_settings fields. Existing policies are untouched, so pre-12.2.0 policies still expose the AI provider keys - Imports capped at 50mb (IMPORT_MAX_FILE_SIZE), which also caps schema snapshot uploads to /schema/diff and /schema/apply - Image transformation output capped at 3000px - Valueless boolean query flags now count as true, affecting ?background on import and ?force on /schema/apply. /schema/diff changed the opposite way: it tested key presence, so ?force=false previously still forced - SDK schemaDiff now takes an options object rather than a positional force argument - DeploymentProjectsService.readByExternalId now takes a deployment ID Also: - Documented the new Default Save Action project setting, along with the save action menu, which was previously undocumented apart from Save as Copy - Corrected the IMPORT_MAX_FILE_SIZE default from "no limit" to 50mb - Noted read exposure of the AI provider keys in the security best practices, which previously covered only write access - Dropped the pre-publication TODOs now that v12.2.0 has shipped The schema migration tutorial updates for the new snapshot and diff parameters are on chrisjennings/fix-schema-migration-tutorial. POST /utils/import is intentionally not documented. It is API-only with no Studio UI, so it belongs in the generated API reference rather than the content module guide. Co-Authored-By: Claude Fable 5 --- content/configuration/security-limits.md | 2 +- content/guides/02.content/2.editor.md | 20 ++++++++ .../guides/13.security/1.best-practices.md | 3 ++ content/releases/2.changelog.md | 10 ++-- .../3.breaking-changes/3.version-12.md | 48 ++++++++++++++++++- 5 files changed, 77 insertions(+), 6 deletions(-) diff --git a/content/configuration/security-limits.md b/content/configuration/security-limits.md index 0fd9eee5..1cec3e56 100644 --- a/content/configuration/security-limits.md +++ b/content/configuration/security-limits.md @@ -186,6 +186,6 @@ Allows you to configure hard technical limits, to prevent abuse and optimize for | `QUERYSTRING_MAX_PARSE_DEPTH` | The maximum object depth when parsing URL query parameters using the querystring format | `10` | | `QUERYSTRING_ARRAY_LIMIT` | The array limit when parsing URL query parameters using the querystring format | `500` | | `MAX_IMPORT_ERRORS` | The maximum number of validation errors permitted while importing records before the process is cancelled and the errors returned. | `1000` | -| `IMPORT_MAX_FILE_SIZE` | The maximum size of a file uploaded to the import endpoint. Accepts number of bytes, or human readable string; no limit by default. Returns `413` when exceeded. | | +| `IMPORT_MAX_FILE_SIZE` | The maximum size of a file uploaded to the import endpoints, or of a schema snapshot uploaded to `/schema/diff` and `/schema/apply`. Accepts number of bytes, or human readable string. Returns `413` when exceeded. | `50mb` | [1] Defaults to `auth_login`, `auth_refresh`, `auth_password_request`, `auth_password_reset`, `users_register`, `users_register_verify`, `users_invite_accept`, `users_me_tfa_generate`, `users_me_tfa_enable`, `users_me_tfa_disable`, and `utils_cache_clear`. Setting your own list overrides these. diff --git a/content/guides/02.content/2.editor.md b/content/guides/02.content/2.editor.md index 07985108..eacbec39 100644 --- a/content/guides/02.content/2.editor.md +++ b/content/guides/02.content/2.editor.md @@ -26,6 +26,26 @@ settings, the App will automatically open the item page when selecting the colle :: +## Saving Items + +Click :icon{name="material-symbols:check"} in the page header to save your changes. By default this saves the item and returns you to the collection page. + +Click :icon{name="material-symbols:more-vert"} to pick a different action for the current save: + +| Action | Description | +| ----------------------- | -------------------------------------------------------------------------- | +| **Save and Quit** | Saves the item and returns to the collection page. | +| **Save and Stay** | Saves the item and keeps you on the item page. | +| **Save and Create New** | Saves the item and opens an empty item page for the same collection. | +| **Save as Copy** | Saves your changes as a new item, leaving the original untouched. | +| **Discard All Changes** | Reverts your unsaved changes and keeps you on the item page. | + +To change what the save button does across the project, set **Default Save Action** in the settings module. The action you choose becomes the button's behavior, and the remaining actions stay available in the :icon{name="material-symbols:more-vert"} menu. + +::callout{icon="i-lucide-info"} +**Save and Create New** falls back to **Save and Quit** on [singletons](/guides/data-model/collections) and wherever the user's policies do not grant create access on the collection. +:: + ## Duplicating Items ![Item duplicating](/img/3ac21f31-a1e0-4506-a2cc-86a3682d4bf6.webp) diff --git a/content/guides/13.security/1.best-practices.md b/content/guides/13.security/1.best-practices.md index 902d6bcc..0e271f59 100644 --- a/content/guides/13.security/1.best-practices.md +++ b/content/guides/13.security/1.best-practices.md @@ -25,8 +25,11 @@ Flows run arbitrary operations. They can execute custom scripts, make HTTP reque Updating `directus_settings` changes behavior for every user in the project. Fields on this collection include the project name, theming, authentication policy, and URLs used in outbound email templates. A non-administrator with update access can inject CSS into the Data Studio through the theming group, change links in password-reset emails, or disable password requirements. +Read access matters too. `directus_settings` stores the API keys for your configured AI providers in fields such as `ai_openai_api_key`, so a policy that grants read on the whole collection hands those credentials to every user it applies to. + - Grant update on `directus_settings` to administrators only. - If non-administrators need to edit a specific field, scope field permissions tightly, and pair the policy with a [Content Security Policy](/configuration/security-limits#csp) that restricts inline styles and untrusted sources. +- Scope read on `directus_settings` to the fields your users actually need. Policies created before Directus 12.2.0 grant read on every field, including the AI provider keys. See [Version 12 breaking changes](/releases/breaking-changes/version-12) for the field list applied to new policies. ### Users, Roles & Policies diff --git a/content/releases/2.changelog.md b/content/releases/2.changelog.md index b1a32537..eb995fbd 100644 --- a/content/releases/2.changelog.md +++ b/content/releases/2.changelog.md @@ -12,10 +12,14 @@ Each month, some of the Directus team talk through what’s new including core r ## July 2026 - - -- Directus 12.2.0 has a breaking change: the WYSIWYG (rich text) interface is now built on [Tiptap](https://tiptap.dev) instead of TinyMCE. Existing fields keep working, but stored HTML is normalized to supported markup on first edit and the **Options Override** (`tinymceOverrides`) option is deprecated. See the [Version 12 breaking changes](/releases/breaking-changes/version-12#wysiwyg-editor-rebuilt-on-tiptap). +- [Directus 12.2.0](https://github.com/directus/directus/releases/tag/v12.2.0) has a breaking change: the WYSIWYG (rich text) interface is now built on [Tiptap](https://tiptap.dev) instead of TinyMCE. Existing fields keep working, but stored HTML is normalized to supported markup on first edit and the **Options Override** (`tinymceOverrides`) option is deprecated. See the [Version 12 breaking changes](/releases/breaking-changes/version-12#wysiwyg-editor-rebuilt-on-tiptap). +- 12.2.0 also tightens several defaults. Imports are capped at `50mb`, image transformations are capped at 3000px of output, and new App Access policies read a limited set of `directus_settings` fields instead of all of them. Review the [full list](/releases/breaking-changes/version-12) before upgrading. - Rebuilt the WYSIWYG editor on Tiptap, an actively maintained and license-compatible library, with a redesigned toolbar that collapses overflowing tools into a Show More menu. +- Added a `POST /utils/import` endpoint for importing flat data into multiple collections in a single request. Directus orders the collections by their relationships and remaps primary keys along with the foreign keys that reference them. +- Schema snapshots can now be [scoped to a subset of collections](/tutorials/migration/promoting-changes-between-environments-in-directus), and the diff endpoint takes a `mode` parameter for producing an additive diff that leaves the target's other collections alone. +- Added a [**Default Save Action**](/guides/content/editor#saving-items) project setting, which controls what the save button does when editing an item. +- Added [JSON path filtering](/guides/content/explore#filtering-json-fields) to Studio filters, so you can filter on values inside JSON columns from the filter interface. +- Added [`LICENSE_KEY_MANAGEMENT_ENABLED`](/licensing/overview) for controlling whether license keys can be managed through the Studio. ## March 2026 diff --git a/content/releases/3.breaking-changes/3.version-12.md b/content/releases/3.breaking-changes/3.version-12.md index 25070752..b39adec9 100644 --- a/content/releases/3.breaking-changes/3.version-12.md +++ b/content/releases/3.breaking-changes/3.version-12.md @@ -6,8 +6,6 @@ description: Breaking changes may require action on your part before upgrading. ## Version 12.2.0 - - ### WYSIWYG editor rebuilt on Tiptap The WYSIWYG interface (`input-rich-text-html`) has replaced its TinyMCE editor with [Tiptap](https://tiptap.dev). The interface id, option keys, and HTML storage format are unchanged, so existing fields continue to work without migration. Two changes may require action. @@ -42,6 +40,52 @@ If you style or process stored HTML by tag, class, or attribute, review affected This option passed raw TinyMCE configuration to the editor and no longer has any effect. Existing values are ignored and log a deprecation warning in the browser console, and the option is hidden for new fields. It will be removed in a future release. Configure the toolbar, custom formats, font families, and font sizes through the interface's dedicated options instead. +### App Access policies read a limited set of settings fields + +The minimal permissions attached to a policy with **App Access** enabled previously granted read access to every field on `directus_settings`, including admin-only configuration and AI provider credentials. New policies now grant read access to only the fields a non-admin user needs: + +`id`, `project_url`, `project_logo`, `module_bar`, `storage_asset_transform`, `storage_asset_presets`, `custom_aspect_ratios`, `basemaps`, `mapbox_key`, `visual_editor_urls`, `collaborative_editing_enabled`, `report_error_url`, `ai_openai_compatible_models`, `ai_openai_allowed_models`, `ai_anthropic_allowed_models`, `ai_google_allowed_models`, `mcp_prompts_collection`, `default_save_action`, `ai_translation_default_model`, `ai_translation_glossary`, and `ai_translation_style_guide`. + +Existing policies are not modified, so upgrading does not change what your current users can read. This means any policy created before this release still exposes the full settings collection, including `ai_openai_api_key` and the other provider keys. Audit your App Access policies and scope the `directus_settings` read permission to the fields you need. + +If an extension or frontend of yours reads a settings field that a new policy no longer grants, add that field explicitly to the policy's read permission. + +### Imports are capped at 50mb by default + +`IMPORT_MAX_FILE_SIZE` limits the size of files uploaded to the import endpoints, and of schema snapshots uploaded to `/schema/diff` and `/schema/apply`. It defaults to `50mb`. + +Imports were previously unrestricted, so a file that used to be processed now fails with `413 Content Too Large` once it passes the limit. Raise [`IMPORT_MAX_FILE_SIZE`](/configuration/security-limits) to restore the previous behavior. + +### Image transformation output is capped at 3000px + +`ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION` limits the pixel dimensions a transformation is allowed to output, and defaults to `3000`. A transformation projecting a width or height above the limit is rejected with an [`ILLEGAL_ASSET_TRANSFORMATION`](/guides/connect/errors) error. + +The limit applies at every step of a transformation rather than only to the final dimensions, so a preset that scales an image up to 10000px before scaling it back down is rejected. Review your storage asset presets and the transformation parameters your frontend requests, then raise [`ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION`](/configuration/files) if you need larger output. + +### Valueless boolean query flags now count as true + +Boolean query flags are now read through a shared helper that treats a flag carrying no value (`?flag`) as true. Two endpoints change behavior as a result: + +- `POST /utils/import/:collection?background` previously evaluated to false, so the import ran synchronously. It now runs in the background. +- `POST /schema/apply?force` previously evaluated to false, so version and database vendor checks still applied. It now bypasses them. + +Pass an explicit value (`?background=false`, `?force=false`) wherever you relied on the previous behavior. + +`POST /schema/diff` changes in the opposite direction. It previously treated the mere presence of `force` as true, so even `?force=false` bypassed the checks. It now respects the value you pass. + +### SDK `schemaDiff` takes an options object + +`schemaDiff` accepted `force` as a positional second argument. It now takes an options object, so the new `mode` parameter can be passed alongside it. + +```js +schemaDiff(snapshot, true); // [!code --] +schemaDiff(snapshot, { force: true }); // [!code ++] +``` + +### `DeploymentProjectsService.readByExternalId` takes a deployment ID + +External IDs are only unique within a provider, so this method now takes the deployment ID as its first argument: `readByExternalId(deploymentId, externalId)`. Update any extension that calls it directly. + ## Version 12.1.0 ### Removal of /hash endpoints From 4c9a55034ad0d8ad3728cd81ac73d1cb0f949c44 Mon Sep 17 00:00:00 2001 From: Christopher Jennings <1398135+ChristopherJennings@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:38:11 -0400 Subject: [PATCH 2/4] Correct and complete the import environment variable reference The Imports table documented a variable that does not exist, two wrong defaults, and omitted one variable entirely. Verified against packages/env/src/constants/defaults.ts at v12.2.0. Corrections. These were also wrong in v12.1.1, so they are not regressions from a recent release: - IMPORT_CONCURRENT_MAX does not exist. The code reads IMPORT_MAX_CONCURRENCY (api/src/services/import/import.ts), and its default is 20 rather than 10. A documented name the code never reads silently does nothing when set - IMPORT_TIMEOUT defaults to 1h, not 1m. Also noted that the budget is shared across receiving and parsing an upload, so the effective wall clock is not twice the value - IMPORT_EXPORT_NAMESPACE defaults to directus:import-export, not import-export - Fixed the "simultainous" typo Addition: - IMPORT_MAX_FILE_SIZE, new in v12.2.0, was missing from this table. Added as a pointer row so the Imports section covers every import variable. Its full description, including the 413 response and the fact that it also caps schema snapshot uploads, lives in Security & Limits alongside MAX_IMPORT_ERRORS. content/releases/3.breaking-changes/2.version-11.md already documented IMPORT_TIMEOUT as 1 hour and IMPORT_MAX_CONCURRENCY as 20 under the correct name, so that page has been contradicting this table. Co-Authored-By: Claude Fable 5 --- content/configuration/files.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/content/configuration/files.md b/content/configuration/files.md index 5f064f0c..83e12867 100644 --- a/content/configuration/files.md +++ b/content/configuration/files.md @@ -181,8 +181,9 @@ The output limit applies at every step of a transformation, not only to the fina ## Imports -| Variable | Description | Default Value | -| ------------------------- | ----------------------------------------------------- | --------------- | -| `IMPORT_EXPORT_NAMESPACE` | Redis namespace for storing import/export information | `import-export` | -| `IMPORT_TIMEOUT` | Allowed duration for importing files | `1m` | -| `IMPORT_CONCURRENT_MAX` | Maximum simultainous imports | `10` | +| Variable | Description | Default Value | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `IMPORT_EXPORT_NAMESPACE` | Redis namespace for storing import/export information. | `directus:import-export` | +| `IMPORT_TIMEOUT` | Allowed duration for importing files. Shared across receiving the upload and parsing it. | `1h` | +| `IMPORT_MAX_CONCURRENCY` | Maximum simultaneous imports. | `20` | +| `IMPORT_MAX_FILE_SIZE` | Maximum size of an uploaded import file. See [Security & Limits](/configuration/security-limits). | `50mb` | From 3d319b17f07e73873852a06136667fe0226f5ee0 Mon Sep 17 00:00:00 2001 From: Christopher Jennings <1398135+ChristopherJennings@users.noreply.github.com> Date: Thu, 30 Jul 2026 10:45:43 -0400 Subject: [PATCH 3/4] Update content/configuration/security-limits.md Co-authored-by: judda <44623501+ComfortablyCoding@users.noreply.github.com> --- content/configuration/security-limits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/configuration/security-limits.md b/content/configuration/security-limits.md index 1cec3e56..4689fb62 100644 --- a/content/configuration/security-limits.md +++ b/content/configuration/security-limits.md @@ -186,6 +186,6 @@ Allows you to configure hard technical limits, to prevent abuse and optimize for | `QUERYSTRING_MAX_PARSE_DEPTH` | The maximum object depth when parsing URL query parameters using the querystring format | `10` | | `QUERYSTRING_ARRAY_LIMIT` | The array limit when parsing URL query parameters using the querystring format | `500` | | `MAX_IMPORT_ERRORS` | The maximum number of validation errors permitted while importing records before the process is cancelled and the errors returned. | `1000` | -| `IMPORT_MAX_FILE_SIZE` | The maximum size of a file uploaded to the import endpoints, or of a schema snapshot uploaded to `/schema/diff` and `/schema/apply`. Accepts number of bytes, or human readable string. Returns `413` when exceeded. | `50mb` | +| `IMPORT_MAX_FILE_SIZE` | The maximum size of a file uploaded to the data import, schema diff and schema apply endpoints . Accepts number of bytes, or human readable string. Returns `413` when exceeded. | `50mb` | [1] Defaults to `auth_login`, `auth_refresh`, `auth_password_request`, `auth_password_reset`, `users_register`, `users_register_verify`, `users_invite_accept`, `users_me_tfa_generate`, `users_me_tfa_enable`, `users_me_tfa_disable`, and `utils_cache_clear`. Setting your own list overrides these. From 8118a910e9421dab43fc4bcb8e4cdb08ab8b14e3 Mon Sep 17 00:00:00 2001 From: Christopher Jennings <1398135+ChristopherJennings@users.noreply.github.com> Date: Thu, 30 Jul 2026 10:46:02 -0400 Subject: [PATCH 4/4] Update content/guides/13.security/1.best-practices.md Co-authored-by: judda <44623501+ComfortablyCoding@users.noreply.github.com> --- content/guides/13.security/1.best-practices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/13.security/1.best-practices.md b/content/guides/13.security/1.best-practices.md index 0e271f59..ec89eef3 100644 --- a/content/guides/13.security/1.best-practices.md +++ b/content/guides/13.security/1.best-practices.md @@ -29,7 +29,7 @@ Read access matters too. `directus_settings` stores the API keys for your config - Grant update on `directus_settings` to administrators only. - If non-administrators need to edit a specific field, scope field permissions tightly, and pair the policy with a [Content Security Policy](/configuration/security-limits#csp) that restricts inline styles and untrusted sources. -- Scope read on `directus_settings` to the fields your users actually need. Policies created before Directus 12.2.0 grant read on every field, including the AI provider keys. See [Version 12 breaking changes](/releases/breaking-changes/version-12) for the field list applied to new policies. +- Scope read on `directus_settings` to the minimum fields your users actually need. Policies created before Directus 12.2.0 grant read on every field. See [Version 12 breaking changes](/releases/breaking-changes/version-12) for the field list applied to new policies. ### Users, Roles & Policies