Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions content/configuration/1.general.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ See [Licensing](/licensing/overview) for a full explanation of license keys, lic

| Variable | Description | Default Value |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `LICENSE_KEY` | License key in the format `DXXXX-XXXXX-XXXXX-XXXXX-XXXXC`. Activated against the Directus licensing service on first use and revalidated periodically. Takes precedence over `LICENSE_TOKEN` and any key set through the Studio. | |
| `LICENSE_TOKEN` | Pre-issued license token for offline use. Validated locally on each startup and never contacts the licensing service. Mutually exclusive with `LICENSE_KEY` — setting both is a configuration error and Directus will refuse to start. | |
| `LICENSE_KEY` | License key in the format `DXXXX-XXXXX-XXXXX-XXXXX-XXXXC`. Activated against the Directus licensing service on first use and revalidated periodically. Takes precedence over `LICENSE_TOKEN` and any key set through the Studio. If activation or revalidation fails, Directus keeps running on its current license, or on the core tier if none is in effect, and retries automatically. | |
| `LICENSE_TOKEN` | Pre-issued license token for offline use. Validated locally on startup and on each scheduled license check, and never contacts the licensing service. Mutually exclusive with `LICENSE_KEY`. Setting both is a configuration error and Directus will refuse to start. | |
| `LICENSE_KEY_MANAGEMENT_ENABLED` | Whether the license key can be managed in the Studio and via the API. Set to `false` to prevent the license from being activated, updated, or deactivated. | `true` |

::callout{icon="i-lucide-info"}
Expand Down
3 changes: 1 addition & 2 deletions content/configuration/cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ than you would cache database content. To learn more, see [Files](/configuration
| `CACHE_AUTO_PURGE_IGNORE_LIST`<sup>[3]</sup> | List of collections that prevent cache purging when `CACHE_AUTO_PURGE` is enabled. | `directus_activity,directus_presets` |
| `CACHE_SYSTEM_TTL`<sup>[4]</sup><sup>[5]</sup> | How long `CACHE_SCHEMA` is persisted. | -- |
| `CACHE_SCHEMA`<sup>[4]</sup> | Whether or not the database schema is cached. One of `false`, `true` | `true` |
| `CACHE_SCHEMA_MAX_ITERATIONS`<sup>[4]</sup> | Safe value to limit max iterations on get schema cache. This value should only be adjusted for high scaling applications. | `100` |
| `CACHE_SCHEMA_SYNC_TIMEOUT` | How long to wait for other containers to message before trying again | `10000` |
| `CACHE_SCHEMA_SYNC_TIMEOUT` | Maximum time, in milliseconds, to wait for the schema to finish building. Requests fail if the timeout is exceeded. | `60000` |
| `CACHE_SCHEMA_FREEZE_ENABLED` | Whether or not to freeze the schema to improve memory efficiency | false |
| `CACHE_NAMESPACE`<sup>[5]</sup> | How to scope the cache data. | `system-cache` |
| `CACHE_STORE`<sup>[5]</sup> | Where to store the cache data. Either `memory`, `redis`. | `memory` |
Expand Down
4 changes: 2 additions & 2 deletions content/configuration/security-limits.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ This page documents environment variables. For in-app security configuration (pe
| `USER_REGISTER_URL_ALLOW_LIST` | List of URLs that can be used as `verification_url` in the `/users/register` endpoint. | |
| `IP_TRUST_PROXY` | Settings for the Express.js trust proxy setting. | false |
| `IP_CUSTOM_HEADER` | What custom request header to use for the IP address. | false |
| `IMPORT_IP_DENY_LIST`<sup>[2]</sup> | Deny importing files from these IP addresses / IP ranges / CIDR blocks. Use `0.0.0.0` to match any local IP address. | `0.0.0.0,169.254.169.254` |
| `IMPORT_IP_DENY_LIST`<sup>[2]</sup> | Deny outbound requests, such as file imports and the **Request URL** flow operation, to these IP addresses, IP ranges (`10.0.0.1-10.0.0.50`), or CIDR blocks (`10.0.0.0/8`). Use `0.0.0.0` to match any local IP address. | `0.0.0.0,169.254.169.254` |
| `HSTS_ENABLED` | Enable the Strict-Transport-Security policy header. When enabled, Directus will send the `Strict-Transport-Security: max-age=15552000; includeSubDomains` header on all responses. | `false` |
| `HSTS_*` | Custom overrides for the Strict-Transport-Security header. See [helmet's documentation](https://helmetjs.github.io). Example: `HSTS_MAX_AGE=63072000` | |

<sup>[1]</sup> When `SECRET` is not set, a random value will be used. This means sessions won't persist across system
restarts or horizontally scaled deployments. Must be explicitly set to a secure random value in production.

<sup>[2]</sup> localhost can get resolved to `::1` as well as `127.0.0.1` depending on the system - ensure to include
both if you want to specifically block localhost.
both if you want to specifically block localhost. Wildcards (`10.0.0.*`), netmasks (`10.0.0.0/255.255.255.0`), short ranges (`10.0.0.1-50`), and malformed prefixes (`10.0.0.0/ 24`) are invalid. Directus checks `IMPORT_IP_DENY_LIST` each time it makes an outbound request. If any entry is invalid, Directus logs a warning and denies every outbound request.

Browsers are pretty strict when it comes to third-party cookies. If you're running into unexpected problems when running your project and API on different domains, make sure to verify your configuration for `REFRESH_TOKEN_COOKIE_NAME`, `REFRESH_TOKEN_COOKIE_SECURE`, and `REFRESH_TOKEN_COOKIE_SAME_SITE`.

Expand Down
2 changes: 1 addition & 1 deletion content/getting-started/10.accessibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Special shortcuts for the WYSIWYG editor:

- Use `Tab` to move focus into and through the toolbar, and arrow keys to move between adjacent tools.
- Press `Esc` to return focus to the editor content area.
- Standard formatting shortcuts apply in the content area, for example `meta` + `b` (bold), `meta` + `i` (italic), and `meta` + `k` (insert link).
- Standard formatting shortcuts apply in the content area, for example `meta` + `b` (bold), `meta` + `i` (italic), and `meta` + `k` (insert a link, or link a selected image).

<!-- TODO(CMS-2851): confirm the exact toolbar keyboard-navigation keys and the release version for the Tiptap-based editor before publishing. -->

Expand Down
22 changes: 22 additions & 0 deletions content/guides/01.data-model/4.rich-text.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,28 @@ Behavior worth knowing:
- Deleting the image out of a figure removes the orphaned caption too.
- Stored `<figure>` and `<figcaption>` markup round-trips, including a caption placed before the image and a figure holding only a caption.

## Linked images

To link an image, select it and use the **Add/Edit Link** toolbar button or `meta` + `k`. In the link drawer, enter the **URL**. **Tooltip** sets the image's `title`, and **New Tab** opens the link in a new tab. The drawer hides **Display Text** for images. To remove the link, use **Remove Link**.

The image is stored wrapped in an anchor. This also works inside a captioned `<figure>`:

```html
<a href="https://example.com" target="_blank" rel="noopener noreferrer"><img src="https://example.com/assets/2b1a…" alt="A wind turbine" /></a>
```

The editor keeps `href`, `target`, and `rel` on the wrapping `<a>`. Other attributes on that anchor, such as `class` or `data-*`, are not supported and trigger the [read-only normalization notice](/releases/breaking-changes/version-12#content-is-normalized-on-first-edit). Clicking a linked image in the editor selects it rather than following the link.

## Pasting content

Some apps, such as Word and Figma, wrap copied content in markup the editor does not support. When you paste content like this, the editor inserts a cleaned version that keeps your text and supported formatting, and shows a notice above the field. Select **See what was removed** to view a diff and choose an option:

- **Keep Cleaned** keeps the cleaned paste.
- **Undo Paste** removes the paste.
- **Paste Raw** replaces the cleaned paste with the HTML exactly as copied and switches the field to the raw HTML editor. If the cursor was inside a paragraph, the paragraph is split around the pasted HTML.

**Undo Paste** and **Paste Raw** are only available until you make another change. Pasting plain text, or HTML the editor supports, never shows the notice.

## Styling the output on your frontend

Directus stores the HTML. Rendering and styling it is your frontend's job.
Expand Down
4 changes: 4 additions & 0 deletions content/guides/03.auth/2.access-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ An admin can set the user status. Only the active state is able to authenticate,

A policy can also have a specific allowlist of IP addresses, IP ranges, and CIDR blocks which allow access. Leave this empty to allow all IP addresses.

Each entry must be an IPv4 or IPv6 address (`192.168.1.10`), a range of full addresses (`10.0.0.1-10.0.0.50`), or a CIDR block (`10.0.0.0/24`). Directus rejects wildcards (`10.0.0.*`), netmasks, short ranges (`10.0.0.1-50`), and malformed prefixes such as `10.0.0.0/ 24`.

IP access is configured at the individual policy level, meaning each policy maintains its own independent set of IP restrictions.
This granular approach provides several advantages:
- Each policy's IP allowlist operates independently without affecting other policies in your system.
Expand Down Expand Up @@ -163,6 +165,8 @@ The combined rule becomes: `(user_id = current_user.id) OR (department = current

IP access restrictions follow a subtractive model, meaning that if a user's IP address doesn't match the allowlist, that entire policy is removed from the evaluation chain.

This also applies to **Require 2FA**. A policy that requires two-factor authentication only requires it when the user logs in from an IP address on that policy's allowlist.

**How It Works**: When a request arrives, the system first checks the requesting IP address against each policy's IP allowlist. Any policy whose IP restrictions are not satisfied is immediately excluded from further evaluation. Only policies that pass the IP access check remain active for that request.

**Practical Example**: Consider a user assigned three policies:
Expand Down
4 changes: 3 additions & 1 deletion content/guides/03.auth/5.2fa.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ navigation:
title: Two-Factor Auth
---

Two-factor authentication (2FA) in Directus is a security measure that requires a generated one-use code to be provided after log in to complete authentication. 2FA for the Data Studio can be enabled or enforced in the user page. A one-time password (OTP) is required when logging in via the Data Studio or API.
Two-factor authentication (2FA) in Directus is a security measure that requires a generated one-use code to be provided after log in to complete authentication. Users can enable 2FA from their user page. A one-time password (OTP) is required when logging in via the Data Studio or API.

To require 2FA, enable **Require 2FA** on a [policy](/guides/auth/access-control). Directus enforces 2FA if at least one of the user's policies has it enabled. A policy with an IP allowlist only requires 2FA when the user logs in from an allowed IP address. Users who have not set up 2FA are redirected to set it up after logging in, including after SSO logins.

To enable 2FA, you will need an external authenticator app or support for OTPs in your password manager.

Expand Down
1 change: 1 addition & 0 deletions content/guides/04.connect/5.errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ The `code` value in `extensions` lets you handle errors programmatically without
| `INVALID_PROVIDER_CONFIG` | 503 | The authentication provider is misconfigured. |
| `INVALID_QUERY` | 400 | The query parameters can't be used as provided. |
| `INVALID_TOKEN` | 403 | The access token is malformed or invalid. |
| `LICENSE_INVALID` | 400 | The license key is invalid. `extensions.failure` gives the cause. |
| `LIMIT_EXCEEDED` | 403 | A configured limit (relations, depth, etc.) was exceeded. |
| `METHOD_NOT_ALLOWED` | 405 | The HTTP method isn't allowed on this endpoint. |
| `NOT_NULL_VIOLATION` | 400 | A required field was submitted as null. |
Expand Down
2 changes: 1 addition & 1 deletion content/guides/05.files/1.upload.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ The file contents has to be provided in a property called `file`. All other prop
the file object can be provided as well, except `filename_disk` and `filename_download`.

::callout{icon="i-lucide-info" color="info"}
If `storage` is not specified, it defaults to the first location listed in [`STORAGE_LOCATIONS`](/configuration/files#storage-locations).
If `storage` is not specified, it defaults to the first location listed in [`STORAGE_LOCATIONS`](/configuration/files#storage-locations). A `storage` value must match one of the configured locations exactly, including case. Other values are rejected with an `INVALID_PAYLOAD` error.
::
12 changes: 10 additions & 2 deletions content/guides/06.flows/5.manage-flows.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
stableId: a953ba99-dea5-44a2-8c7b-176180759fe9
title: Manage Flows
description: Organize, find, duplicate, import, and export Flows from the Flows module.
description: Organize, find, activate, duplicate, import, and export Flows from the Flows module.
---

Flows have their own module in the module bar. The Flows page lists every Flow in a table with its folder, status, trigger type, name, and description. From here you can organize Flows into folders, search and filter the list, duplicate a Flow, and import and export Flows.
Flows have their own module in the module bar. The Flows page lists every Flow in a table with its folder, status, trigger type, name, and description. From here you can organize Flows into folders, search and filter the list, activate or deactivate Flows, duplicate a Flow, and import and export Flows.

::callout{icon="i-lucide-info"}
Flows used to live in the Settings module. Links to the old location redirect to the Flows module, so existing bookmarks keep working.
Expand All @@ -30,6 +30,14 @@ Deleting a folder never deletes the Flows inside it. Choose **Delete this folder

Use the search box in the header bar to find Flows by name. Select the filter icon to add conditions on Flow fields such as **Status**, **Trigger**, or the folder's **Name**.

## Activate or Deactivate Flows

Only active Flows run. To change the status of one Flow, open its context menu and select **Set Flow to Active** or **Set Flow to Inactive**.

To change several Flows at once, select them in the table. The header bar shows **Set Flows to Active** when at least one selected Flow is inactive, and **Set Flows to Inactive** when at least one is active. A mixed selection shows both buttons. Directus only updates the Flows whose status changes.

Both buttons require update permission on `directus_flows`.

## Duplicate a Flow

To copy a Flow together with its trigger configuration and all of its operations, open the Flow's context menu and select **Duplicate Flow**. Enter a name for the copy and select **Duplicate**. The copy is placed in the same folder as the original.
Expand Down
Loading
Loading