Company branding setup#

Configuring branding.json in Sherpa AI Server#

Purpose#

The branding.json file lets you change the appearance and part of the behavior of the Sherpa AI Server interface without rebuilding the frontend:

  • name and logos;
  • main colors, border radius, and font;
  • user and Assistant message backgrounds;
  • default language and available languages;
  • display of the OpenID sign-in button;
  • additional links in the side menu;
  • an information button on the sign-in page.

The settings are applied in the browser when the application loads. There is no separate branding administration screen in Sherpa AI Server.

Where the file is located#

In source code#

Main file:

frontend/src/assets/branding/branding.json

It is included in the frontend build and is used as the default settings set for the distribution.

In the client distribution#

When preparing the client archive, the file is copied here:

frontend/config/branding.json

To make nginx use this file, enable a bind mount in the docker-compose.yml file for the aiserver-nginx service:

services:
  aiserver-nginx:
    volumes:
      - ./frontend/config/branding.json:/opt/SherpaAIServer/frontend/dist/assets/branding/branding.json:ro

In the distributed docker-compose.yml, this line may be commented out. After enabling the mount for the first time, recreate the container:

docker compose up -d --force-recreate aiserver-nginx

If the mount is already enabled, changes to the file on the host are visible inside the container immediately. Reloading the page is enough to apply them in the interface.

How the configuration is loaded#

When the frontend starts, it requests:

/assets/branding/branding.json

The configuration is loaded before the Angular application starts. Because of that, the language, logos, and branding CSS variables are available during the initial rendering of the interface.

Loading rules:

  1. Built-in Sherpa AI Server values are used as the base.
  2. Fields from branding.json replace the corresponding built-in values.
  3. If the file is unavailable or contains invalid JSON, the application continues to work with the built-in values.
  4. Missing top-level fields do not need to be specified: the built-in values are preserved for them.
  5. Nested objects and array items must be specified in full. Exception: the top-level fields of chat_model_picker_text are supplemented with built-in values.

The _comments field inside the standard branding.json file contains reference descriptions. This is a normal JSON helper field; the interface does not use it.

Quick start#

  1. Back up frontend/config/branding.json, then prepare or edit the working file.

  2. Make sure the bind mount is enabled in docker-compose.yml.

  3. Check the JSON syntax:

    jq empty frontend/config/branding.json
    
  4. When connecting the file for the first time, recreate aiserver-nginx:

    docker compose up -d --force-recreate aiserver-nginx
    
  5. Check which file the server is serving:

    Replace ai.example.org with your installation address.

    curl -fsS https://ai.example.org/assets/branding/branding.json | jq .
    
  6. Reload the Sherpa AI Server page. If the browser shows stale resources, perform a hard refresh.

Complete example#

{
  "app_name": "Корпоративный AI",
  "logo_url": "assets/branding/logo-light-v1.svg",
  "logo_url_light": "assets/branding/logo-light-v1.svg",
  "logo_url_dark": "assets/branding/logo-dark-v1.svg",
  "favicon_url": "favicon.ico",
  "disable_openid": false,
  "hide_chat_scope_switch": false,
  "hide_language_selector": false,
  "hide_unavailable_menu_items": false,
  "auto_expand_sidebar_sections": false,
  "default_language": "ru",
  "available_languages": ["ru", "en"],
  "additional_menu_items": [
    {
      "button_name": {
        "ru": "Поддержка",
        "en": "Support"
      },
      "url": "https://support.example.org",
      "icon": "question-mark-circle-outline"
    },
    {
      "button_name": {
        "ru": "Инструкции",
        "en": "Documentation"
      },
      "url": "/main/documents",
      "icon": "book-open-outline"
    }
  ],
  "additional_login_button": {
    "button_name": {
      "ru": "Как получить доступ?",
      "en": "How do I get access?"
    },
    "message": {
      "ru": "Для получения доступа обратитесь в службу поддержки.",
      "en": "Contact the support team to request access."
    }
  },
  "primary_color": "#2563eb",
  "primary_dark_color": "#1d4ed8",
  "primary_light_color": "#60a5fa",
  "primary_rgb": "37, 99, 235",
  "secondary_color": "#1f2937",
  "border_radius": "8px",
  "font_family": "Inter, sans-serif",
  "font_url": "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap",
  "chat_user_bg": "#eff6ff",
  "chat_assistant_bg": "#ffffff"
}

Parameter descriptions#

Parameter Format Purpose
app_name string Name in the sidebar, logo alt text and browser tab title (<title>).
logo_url URL string Fallback logo for compatibility. Used if a separate logo for the required theme is not set.
logo_url_light URL string Logo for the light theme.
logo_url_dark URL string Logo for the dark theme.
favicon_url URL string Browser tab icon. A frontend-relative path or absolute URL; defaults to favicon.ico.
disable_openid true or false When true, hides the Sign in with OpenID button on the sign-in page. The parameter controls the interface and by itself does not disable OpenID on the backend.
hide_chat_scope_switch true / false When true, hides the Shared / Personal chats switch in both regular and compact sidebars. Defaults to false; the login type still determines the chat list.
hide_language_selector true / false When true, hides the language selector on the sign-in page and in the header. Defaults to false.
hide_unavailable_menu_items true / false When true, hides menu items unavailable under the User’s role. Defaults to false, which shows them disabled.
auto_expand_sidebar_sections true / false When false, the Chat, Assistants, and API sections do not expand automatically at sign-in or when navigating between screens. They can be expanded manually. Defaults to true: the sections expand automatically.
default_language ru or en Default language. The value must be present in available_languages.
available_languages array of ru and/or en Languages available to the user. Other values are discarded.
additional_menu_items array of objects Additional permanent items in the side menu before the Chat section.
additional_login_button object or null Information button on the sign-in page. Clicking it opens a dialog with localized text. null hides the button.
primary_color #RRGGBB Main color for buttons, links, active elements, tables, and form controls.
primary_dark_color #RRGGBB Darker shade of the main color for hover and press states and some switches.
primary_light_color #RRGGBB Lighter shade for highlights and secondary states.
primary_rgb R, G, B string RGB components of primary_color for translucent backgrounds, shadows, and highlights.
secondary_color CSS color Secondary color. It is also used for card surfaces in the dark theme and the splash screen.
border_radius CSS size Element rounding, for example 0px, 6px, or 16px.
font_family CSS font-family Font family applied to the interface.
font_url CSS file URL Stylesheet from which the browser loads the font. An empty string disables the extra download.
chat_user_bg CSS color Background of user messages on the Chat screen.
chat_assistant_bg CSS color Background of Assistant responses on the Chat screen.

Hiding the chat scope switch#

To remove the Shared / Personal chats switch from the sidebar, set this in branding.json:

{
  "hide_chat_scope_switch": true
}

This changes only the visible switch. A User login still opens personal chats; an Account login opens shared chats. Set the value to false or remove it to restore the switch, then reload the page.

Configuring logos#

Logo selection priority for each theme:

logo_url_light or logo_url_dark → logo_url → built-in logo

You can use:

  • a relative path inside the frontend, for example assets/branding/logo-light-v1.svg;
  • an absolute path from the site root, for example /assets/branding/logo-light-v1.svg;
  • an absolute URL, for example https://static.example.org/logo-light-v1.svg.

If the logo is stored alongside the client configuration, mount it separately in docker-compose.yml:

services:
  aiserver-nginx:
    volumes:
      - ./frontend/config/branding.json:/opt/SherpaAIServer/frontend/dist/assets/branding/branding.json:ro
      - ./frontend/config/logo-light-v1.svg:/opt/SherpaAIServer/frontend/dist/assets/branding/logo-light-v1.svg:ro
      - ./frontend/config/logo-dark-v1.svg:/opt/SherpaAIServer/frontend/dist/assets/branding/logo-dark-v1.svg:ro

In production, images, fonts, and other static files can be cached by the browser for up to a year. When the logo content changes, use a new name, for example logo-light-v2.svg, and update the path in branding.json.

Set favicon_url for the browser tab icon. If you supply a custom file, make its URL reachable from users’ browsers and, if necessary, mount it in the nginx container as you do for logos. Use a new filename when changing the icon to avoid cached copies.

Configuring colors#

primary_color and primary_rgb describe the same color in different formats. They are not synchronized automatically.

Example:

{
  "primary_color": "#2563eb",
  "primary_rgb": "37, 99, 235"
}

For primary_color, primary_dark_color, and primary_light_color, use the full #RRGGBB HEX format. The service calculates the contrast of the main color for text and icons, so shortened HEX and rgb(...) or rgba(...) values should not be used for these three fields.

For secondary_color, chat_user_bg, and chat_assistant_bg, regular CSS colors such as HEX or rgba(...) are allowed.

After changing colors, check both themes, tables, form fields, buttons, and the readability of chat messages.

Configuring the font#

font_url must point to a CSS file with the font, and font_family must point to the family name from that CSS file:

{
  "font_family": "Inter, sans-serif",
  "font_url": "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
}

For a custom font, you can mount the CSS and font files into the frontend directory and use a relative URL. Make sure the URL is accessible from the users' browsers, not only from the Sherpa AI Server backend.

To avoid loading an extra CSS file, leave font_url empty:

{
  "font_family": "Roboto, 'Helvetica Neue', sans-serif",
  "font_url": ""
}

Interface languages#

Only ru and en values are supported.

If available_languages is empty, has an invalid format, or does not contain supported values, Sherpa AI Server enables both languages. If default_language is missing from the available languages list, the first available_languages item becomes the default language.

Example of an interface in Russian only:

{
  "default_language": "ru",
  "available_languages": ["ru"]
}

The current-language button remains visible, but the switcher menu does not open.

The actual language is selected in the following order:

  1. the lang or locale parameter in the URL;
  2. the previously selected browser language;
  3. default_language from branding.json.

The value from the URL or browser is applied only when the language is present in available_languages.

To hide the language button itself on the sign-in page and in the header, set "hide_language_selector": true. Keep it false if Users should be able to switch languages visibly.

hide_unavailable_menu_items changes only the visibility of sidebar items, not role permissions. With true, unavailable sections disappear; with false, they remain visible but disabled.

Additional menu items#

Each additional_menu_items entry must contain:

{
  "button_name": {
    "ru": "Поддержка",
    "en": "Support"
  },
  "url": "https://support.example.org",
  "icon": "question-mark-circle-outline"
}

Rules:

  • at least one non-empty translation of the name is required;
  • url must be an internal path with a single leading / or a URL with the http or https protocol;
  • values with other protocols and invalid URLs are ignored;
  • internal links open through the Sherpa AI Server router;
  • external links open in the current tab;
  • items are displayed in the same order as in the array;
  • for a standard icon, specify an Eva Icons name, for example book-open-outline;
  • a string containing / or ending in .svg is treated as a path to a custom SVG icon.

If a translation is missing, Russian is used first, then English.

Information button on the sign-in page#

Example:

{
  "additional_login_button": {
    "button_name": {
      "ru": "Забыли пароль?",
      "en": "Forgot your password?"
    },
    "message": {
      "ru": "Обратитесь в службу поддержки.",
      "en": "Contact the support team."
    }
  }
}

The button is shown if at least one name and at least one message text are filled in. The text is displayed as plain text, not HTML. If a translation is missing, Russian is used first, then English.

To hide the button:

{
  "additional_login_button": null
}

Home cards#

The home_featured_cards setting defines the cards in “Get started”. Array order determines card order; the number is not limited to four. To add, remove or rename a card, edit the array in the deployment's branding.json and reload the page.

The example defines one chat card. Add this field to the existing configuration while keeping the other settings:

{
  "home_featured_cards": [
    {
      "id": "chat",
      "title": { "ru": "Чат", "en": "Chat" },
      "description": { "ru": "Новый диалог", "en": "New conversation" },
      "icon": "message-circle-outline",
      "action": { "type": "route", "url": "/main/new-chat" }
    }
  ]
}

For each card, provide a unique id, non-empty title and description in ru and en, an icon name and an action. A route action opens an accessible internal route. An assistant action with assistant_guid opens a new chat with that Assistant; obtain the GUID from an existing accessible Assistant.

A valid array completely replaces the standard set. If the field is missing, is null, contains an empty array or invalid cards, the application uses its built-in cards. An empty array does not hide all cards. Sections unavailable under the current permissions are filtered out. A card with an assistant action appears only with read access to both Chat and Assistants; this setting does not change role permissions or Assistant access.

Pinned Assistants are added after the home_featured_cards cards automatically. This setting does not control which Assistants are pinned. If an Assistant is already configured, a second card for it is not shown.

If changes do not appear, check the JSON syntax, the file being loaded and account permissions. If an Assistant GUID is invalid, correct it in the configuration. There is no separate card editor in the interface.