---
url: 'https://dxpr.com/hc/documentation/learn-dxpr-builder/getting-started/behind-a-firewall'
title: 'Firewall and data residency | DXPR'
author:
  name: Jurriaan
  url: 'https://dxpr.com/users/jurriaan'
date: '2023-11-06T05:34:28+00:00'
updated: '2026-10-23T14:31:17+00:00'
copyright: 'Copyright DXPR. All rights reserved.'
type: article
summary: 'Every DXPR host the module contacts, what each request carries, the EU-only AI model and air-gapped operation'
tags:
  - 'Meet compliance requirements'
image: 'https://dxpr.com/themes/custom/dxpr_com/images/dxpr-share-card.jpg'
published: true
og:
  site_name: DXPR
  updated_time: '2026-10-23T14:31:17+0000'
  'image:alt': 'DXPR, powered by Drupal: AI-powered Drupal for marketers.'
schema:
  '@context': 'https://schema.org'
  '@graph':
    -
      mainEntity: {  }
    -
      '@type': TechArticle
      headline: 'Firewall and data residency'
      description: 'Every DXPR host the module contacts, what each request carries, the EU-only AI model and air-gapped operation'
      image:
        '@type': ImageObject
        url: 'https://dxpr.com/themes/custom/dxpr_com/images/dxpr-share-card.jpg'
        width: '1200'
        height: '630'
      datePublished: '2023-11-06T05:34:28+0000'
      dateModified: '2026-10-23T14:31:17+0000'
      author:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
        url: 'https://dxpr.com/'
        logo:
          '@type': ImageObject
          url: 'https://dxpr.com/themes/custom/dxpr_com/dxpr-logo-dark.svg'
          width: '1348'
          height: '501'
      publisher:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
        url: 'https://dxpr.com/'
        logo:
          '@type': ImageObject
          url: 'https://dxpr.com/themes/custom/dxpr_com/dxpr-logo-dark.svg'
          width: '1348'
          height: '501'
      mainEntityOfPage: 'https://dxpr.com/hc/documentation/learn-dxpr-builder/getting-started/behind-a-firewall'
      articleSection: 'Meet compliance requirements'
    -
      '@type': WebPage
      '@id': 'https://dxpr.com/hc/documentation/learn-dxpr-builder/getting-started/behind-a-firewall'
      description: 'Every DXPR host the module contacts, what each request carries, the EU-only AI model and air-gapped operation'
      breadcrumb:
        '@type': BreadcrumbList
        itemListElement:
          -
            '@type': ListItem
            position: 1
            name: Home
            item: 'https://dxpr.com/'
      publisher:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
      inLanguage: en
---
[Help Center](/hc)/[Documentation](/hc/documentation) /[Learn DXPR Builder](/hc/documentation/learn-dxpr-builder) /[Manage the site](/hc/documentation/learn-dxpr-builder/manage) /[Meet compliance requirements](/hc/documentation/learn-dxpr-builder/manage/compliance) # Firewall and data residency.

Every DXPR host the module contacts, what each request carries, the EU-only AI model and air-gapped operation

 Learn DXPR BuilderManage the siteMeet compliance requirements

   ## In this guide

 Related guides

- [Installation](/hc/documentation/learn-dxpr-builder/getting-started/installation)
- [Security](/hc/documentation/learn-dxpr-builder/advanced/security)
- [Licence and product key errors](/hc/documentation/learn-dxpr-builder/troubleshooting/licence-and-product-key)

DXPR Builder stores your pages in your own Drupal database. It contacts DXPR services for licence checks, editor assets and AI requests. This page lists every connection, what each one carries, where the DXAI Kavya models run, how the product key is stored, and what works when outbound access is restricted.

## Connections at a glance

| Host | Made by | Purpose | Needed for |
|---|---|---|---|
| `dxpr.com` | Drupal server | Licence status, domain check, licensed user sync | Editing |
| `cdn2.dxpr.com` | Editor's browser | Editor JavaScript and CSS (primary) | Editing |
| `cdn.dxpr.com` | Editor's browser | Editor JavaScript and CSS (fallback) | Editing |
| `kavya.dxpr.com` | Drupal server | AI requests and AI credit balance | AI features |
| `html-extract.dxpr.com` | Editor's browser | AI "clone from URL" | AI features |
| `r.jina.ai` (third party) | Editor's browser | Reads web pages whose URLs appear in a prompt | AI features |
| `tuur.dxpr.com` | Editor's browser | Editor usage analytics | Optional |

All connections use HTTPS on port 443. Site visitors never load anything from DXPR servers: published pages are rendered from HTML stored in the database, and the front-end script (`dxpr_builder/dxpr_frontend.min.js`) ships inside the module. `app.dxpr.com` appears only in links shown to administrators.

## Licence checks (`dxpr.com`)

The Drupal server calls four endpoints defined in `src/Service/DxprBuilderLicenseServiceInterface.php`, each with the product key as an `Authorization: Bearer` header.

| Endpoint | When | Data sent |
|---|---|---|
| `GET /api/user-license` | Every 24 hours when authorised, every 30 minutes when not | `gba` and `users_tier` (number of accounts with the `edit with dxpr builder` permission and the tier it falls in), `values_count` (content items using a builder field), `site_base_url`, `site` (an HMAC of the site's hash salt; the salt itself is not sent), `site_mail` (from `system.site`), `project`, `version` |
| `GET /api/domain-blacklist/{hostname}` | Hourly | The hostname only; no authorisation header |
| `POST /api/central-user-storage/{add,remove,sync}` | When accounts, permissions or the product key change; follow-ups are queued and run on cron | Hostname plus, for each account with the editing permission, its email address and role names. Accounts without a valid email address are skipped |
| `GET /api/user-license/users` | When the User Licenses page is opened | Nothing beyond the key |

Page content is never part of a licence request. The module also prints a hidden `<div>` on the front page containing a fixed marker string, the licence tier, the editor count and the save count (`dxpr_builder_page_bottom()` in `dxpr_builder.module`). It is readable in the page source.

## Editor assets (`cdn2.dxpr.com`, `cdn.dxpr.com`)

When **Editor Assets** is **Cloud** (the default), the editor's browser loads the builder JavaScript and CSS from `https://cdn2.dxpr.com/<version>/`, falling back to `https://cdn.dxpr.com/<version>/`after a failed `/health` probe. The product key is appended to each asset URL as a `jwt` query parameter (`dxpr_builder_library_info_build()`). The CDN checks that key: a 401 response makes `js/editor_validation.js` show "Your product key is not valid".

The **Local (minified)** and **Local (unminified)**options only appear on the settings form when a built copy of the editor exists in the module's `dxpr_builder/` directory, and such a copy is used automatically even when Cloud is selected. The git repository does not track these built files, so a release downloaded from drupal.org loads the editor from the CDN.

## AI requests (`kavya.dxpr.com`)

The browser never contacts `kavya.dxpr.com`. The editor posts to routes on your own site (`/dxpr_builder/ajax/ai/chat`, `/dxpr_builder/ajax/ai/image`, `/dxpr_builder/ajax/ai/image/edit`; all require the `edit with dxpr builder` permission, a CSRF token and a licensed account). The site relays each request through the Drupal AI module's DXPR provider (`ai_provider_dxpr`) to `https://kavya.dxpr.com/v1/chat/completions`, `/v1/images/generations` or `/v1/images/edits`, authenticated with the DXPR API key stored in the Key module. The provider's `host` setting (`ai_provider_dxpr.settings`) can point requests at a different host. Separately, the AI settings page calls `GET https://kavya.dxpr.com/v1/account/balance`with the product key to show your credit balance.

### What an AI request contains

Only AI features send page content off the site, and only when an editor triggers them (`dxpr_builder/build/dxpr-global/ai-service.js`).

| Request | Content sent |
|---|---|
| Text and page generation | System prompt, the editor's prompt, chat history |
| With an element selected | The element's outer HTML: whole if 500 characters or fewer, otherwise its first and last 200 characters |
| Page-level operations | The existing page HTML, up to 50,000 characters; the complete page when the whole page is being replaced |
| Optional fields | Model name, enabled provider order, a predicted HTML result, allowed tags and classes, a `web_search` flag, a response format |
| Image generation | Prompt, size, quality, background |
| Image editing | The same plus the source image, base64 encoded |

Two conveniences fetch web pages from the editor's browser (`ai-service-url-fetch.js`): a URL in a prompt is requested with `.md` appended and, if that fails, through `https://r.jina.ai/<url>`, a third-party reader service not operated by DXPR; a prompt that asks to clone a page sends the URL to `https://html-extract.dxpr.com/?url=...`. In both cases only the URL is sent.

AI output is filtered on the way back. Allowed domains, blocked tags and allowed tags are set at `/admin/dxpr_studio/dxpr_builder/ai_settings`under **AI Output Filtering**.

## Where the DXAI Kavya models run

DXPR Builder offers three models (`src/Constants/AiModelConstants.php`):

| Model | API name | Description shown in the settings form |
|---|---|---|
| DXAI Kavya M1 | `kavya-m1` | Best performance (OpenAI, Google Gemini, Anthropic) |
| DXAI Kavya M1 EU | `kavya-m1-eu` | Privacy-focused (MistralAI) |
| DXAI Kavya M1 Fast | `kavya-m1-fast` | Fastest responses, lower quality |

There is no region setting in either module. Data residency is chosen by model and by which upstream providers you enable:

- `kavya-m1` fails over between five providers, listed with their country in the DXPR AI Provider settings form: Anthropic (United States), Google Gemini (United States), MistralAI (France), OpenAI (United States), XAI (United States). In **Manual priority** mode you drag providers between **Enabled** and **Disabled**and set their order.
- `kavya-m1-eu` uses MistralAI only. The DXPR AI Provider README states it "keeps data within European boundaries (servers in Netherlands)"; its documentation states that the Kavya server runs in the Netherlands with a backup in Germany, that requests go through MistralAI servers in France, and that it never falls back to a non-EU provider. These locations are documented by DXPR, not enforced by anything in the Drupal code.

With `ai_provider_dxpr` installed, the model is chosen at `/admin/config/ai/providers/dxpr`, and its `allowed_models` setting controls which models editors may pick. Without it, model and provider order are set at `/admin/dxpr_studio/dxpr_builder/ai_settings`.

## How the product key is stored

The **License** section of `/admin/dxpr_studio/dxpr_builder/settings` offers two **Storage method** options (`src/Form/DxprBuilderSettingsForm.php`, `src/Service/DxprBuilderKeyService.php`):

- **Key module**: the key is read from the Key entity chosen under **Key**. `dxpr_builder_update_9020()` moves a key found in `dxpr_builder.settings` into the Key entity `dxpr_builder_jwt`, whose **Configuration**provider still exports it. Only the **File** or **Environment** provider keeps the key out of configuration exports.
- **Configuration storage**: the key is saved in clear text as `dxpr_builder.settings.json_web_token` and is included in configuration exports.

The product key is a JSON Web Token that authenticates licence calls, the credit balance call and the CDN asset URLs; treat it as a credential.

## Firewall configuration

| From | Allow outbound HTTPS to | Notes |
|---|---|---|
| Drupal server | `dxpr.com` | Licence |
| Drupal server | `kavya.dxpr.com` | AI only |
| Editors' workstations | `cdn2.dxpr.com`, `cdn.dxpr.com` | Editor assets |
| Editors' workstations | `html-extract.dxpr.com`, `r.jina.ai` | AI only; blocking them only disables URL research and cloning |
| Editors' workstations | `tuur.dxpr.com` | Analytics; blocking has no effect on editing |

Analytics (`dxpr_builder/build/dxpr-analytics/`, PostHog) identify events by the editor's email address and the site hostname. Turn them off with `drush config:set dxpr_builder.settings record_analytics 0`; the key has no form field. Two further keys without a form field are read from `dxpr_builder.settings`: `cloud_url` replaces the CDN base URL (`VERSION` is substituted with the module version) and `license_endpoint` replaces the central user storage endpoint. Set them with a configuration override in `settings.php`.

## Proxy configuration

Licence and AI calls use Drupal's HTTP client, so a proxy is configured in `settings.php`:

```php
$settings['http_client_config']['proxy'] = [
  'http' => 'http://proxy.example.com:8080',
  'https' => 'http://proxy.example.com:8080',
  'no' => ['localhost', '127.0.0.1'],
];

```

Editors' browsers load the CDN assets and call the analytics and reader services directly, so those requests follow the workstation's proxy settings.

## Air-gapped and on-premise deployments

DXPR Builder runs on your own server; no content passes through a hosted component. When neither the server nor the editors can reach DXPR:

| Capability | Without any DXPR access |
|---|---|
| Serving published pages | Works; nothing is loaded from DXPR |
| Content storage | Works; pages are field values in your database |
| Loading the editor | Fails unless a local build of the editor is present in `dxpr_builder/` |
| Licence validation | Fails. The module logs the error, shows "We are having trouble connecting to the DXPR servers" to administrators and retries every 30 minutes. Editing is not blocked while a product key is configured |
| User Licenses page | Empty; it needs `dxpr.com` |
| AI features | Unavailable; every request goes through `kavya.dxpr.com` |
| Analytics, URL research, clone from URL | Silently skipped |

Without a product key at all, the module applies the free tier limits in code: one editor and 100 content items.

A fully on-premise deployment therefore works for serving and storing content; editing needs CDN access or a local editor build, and licence validation needs `dxpr.com`. Contact DXPR if you need an editor build to host internally.

## What's next?

- [Installation](/hc/documentation/learn-dxpr-builder/getting-started/installation): configure the product key and storage method
- [AI features](/hc/documentation/learn-dxpr-builder/ai): enable AI and choose a model
- [Security](/hc/documentation/learn-dxpr-builder/advanced/security): permissions and output filtering
- [Licence and product key](/hc/documentation/learn-dxpr-builder/troubleshooting/licence-and-product-key): troubleshooting licence errors