# Configuration & Environment

This section explains how the **BaSyx AAS Web UI** is configured in development and production environments. It describes the different configuration layers, their precedence, and how they interact at runtime.

Understanding this configuration model is essential when running the UI in complex infrastructures, multi-environment setups, or secured deployments.

## Configuration Layers and Precedence

The Web UI supports multiple configuration mechanisms that are intentionally layered to maintain backward compatibility and runtime flexibility.

The effective precedence (highest first) is:

1. Infrastructure configuration stored in `localStorage`
(only if `ENDPOINT_CONFIG_AVAILABLE=true`)
2. Legacy infrastructure environment variables
(kept for backward compatibility)
3. Environment variables
    * env.development in development mode
    * Docker-injected environment variables in production
4. `public/config/basyx-infra.yml`
5. Application defaults

```{note}
Legacy infrastructure-related environment variables intentionally have higher precedence than the YAML configuration to avoid breaking existing deployments.
```

## Infrastructure Configuration

The primary and recommended way to configure backend infrastructures is the YAML file:

```bash
public/config/basyx-infra.yml
```

This file defines:

* One or more AAS infrastructures
* The infrastructure template for each backend topology
* Component endpoints (registry, repository, discovery, etc.)
* Authentication configuration per infrastructure

### Characteristics

* The file name is currently fixed and cannot be changed
* It is loaded at runtime, not build time
* It supports multi-infrastructure setups
* Each infrastructure can select a topology template

```{warning}
Changes made via the UI infrastructure editor are stored in `localStorage` only and are **not written back** to `basyx-infra.yml`.
```

## Environment Variables

Environment variables are used for **build-agnostic configuration** such as branding, feature flags, authentication toggles, and (for legacy reasons) infrastructure endpoints.

### Development Mode

In development mode:

* Configuration is read from `env.development`
* Variables must be prefixed with `VITE_`
* Values are injected by Vite at runtime

### Production Mode

In production:

* Environment variables are injected via Docker
* Variables are **not prefixed** with `VITE_`
* The `entrypoint.sh` script replaces placeholders at container startup

```{hint}
The same configuration keys are used in development and production. Only the prefix handling differs.
```

## Supported Environment Variables

### General

* `BASE` – application base path (runtime replaced) (**only available in dev mode!**, use `BASE_PATH` in production)
* `VITE_LOGO_LIGHT_PATH`
* `VITE_LOGO_DARK_PATH`
* `VITE_PRIMARY_LIGHT_COLOR`
* `VITE_PRIMARY_DARK_COLOR`
* `VITE_COPYRIGHT_NAME`
* `VITE_LEGAL_NOTICE_URL`
* `VITE_PRIVACY_POLICY_URL`

The company copyright name and both legal links are optional. `VITE_COPYRIGHT_NAME` is shown with the current year in the center of the footer. The legal notice and privacy policy values must be HTTP(S) URLs. An entry is hidden when its value is empty or not configured. In production, use the same names without the `VITE_` prefix: `COPYRIGHT_NAME`, `LEGAL_NOTICE_URL`, and `PRIVACY_POLICY_URL`.

### Infrastructure (Legacy – Deprecated)

The following variables are deprecated in favor of `basyx-infra.yml`, but still supported:

* `VITE_AAS_DISCOVERY_PATH`
* `VITE_AAS_REGISTRY_PATH`
* `VITE_SUBMODEL_REGISTRY_PATH`
* `VITE_AAS_REPO_PATH`
* `VITE_SUBMODEL_REPO_PATH`
* `VITE_CD_REPO_PATH`

```{warning}
These variables remain available to avoid breaking existing deployments but should not be used for new setups.
```

### Authentication & Security

* `VITE_KEYCLOAK_ACTIVE` (**deprecated**; automatically set in production if Keycloak variables are present)
* `VITE_KEYCLOAK_URL` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_KEYCLOAK_REALM` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_KEYCLOAK_CLIENT_ID` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_OIDC_ACTIVE` (**deprecated**; automatically set in production if OIDC variables are present)
* `VITE_OIDC_URL` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_OIDC_SCOPE` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_OIDC_CLIENT_ID` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_PRECONFIGURED_AUTH` (**deprecated**; use `basyx-infra.yml` instead with `client_Credentials` flow)
* `VITE_PRECONFIGURED_AUTH_CLIENT_SECRET` (**deprecated**; use `basyx-infra.yml` instead with `client_Credentials` flow)
* `VITE_BASIC_AUTH_ACTIVE` (**deprecated**; automatically set in production if basic auth variables are present)
* `VITE_BASIC_AUTH_USERNAME` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_BASIC_AUTH_PASSWORD` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_AUTHORIZATION_HEADER_PREFIX` (**deprecated**; use `basyx-infra.yml` instead)
* `VITE_AUTHORIZATION_HEADER_DESCRIPTION_ENDPOINT_EXEMPTION`

```{danger}
Never commit OAuth client secrets or credentials into version control. Frontend applications expose configuration to end users and secrets can be extracted easily.
```

### Feature Flags & Behavior

* `VITE_ENDPOINT_CONFIG_AVAILABLE`
* `VITE_SINGLE_AAS`
* `VITE_SINGLE_AAS_REDIRECT`
* `VITE_SM_VIEWER_EDITOR`
* `VITE_SINGLE_SM`
* `VITE_SINGLE_SM_REDIRECT`
* `VITE_ALLOW_EDITING`
* `VITE_ALLOW_UPLOADING`
* `VITE_ALLOW_LOGOUT`
* `VITE_START_PAGE_ROUTE_NAME`
* `VITE_FEATURE_CONTROL_CLAIM_MAPPINGS`

#### Claim-based feature control

Feature control can map claims from an OIDC access token to temporary UI feature overrides. It is identity-provider agnostic: claim locations are configured explicitly as [RFC 6901 JSON Pointers](https://www.rfc-editor.org/rfc/rfc6901), so the UI does not assume Keycloak roles or any other provider-specific token shape.

Use `FEATURE_CONTROL_CLAIM_MAPPINGS` in production or `VITE_FEATURE_CONTROL_CLAIM_MAPPINGS` in development. The value follows the BaSyx Go claim-mapping shape:

```json
[
  {
    "target": "features",
    "mode": "list",
    "sources": ["/basyx_features"]
  }
]
```

The mapping has the following rules:

* `target` must be `features` and may occur only once.
* `mode` must be `list`.
* `sources` must contain at least one valid JSON Pointer.
* A source may contain a string or an array of strings. Values from all sources are merged and deduplicated.
* Missing sources contribute no values. A source that is present with another value type invalidates the feature set for that token.
* Unsupported feature values invalidate the feature set instead of being ignored. This makes misspelled restrictive values visible in the browser console and prevents a partially applied set.
* Malformed JSON, duplicate targets, unsupported modes, and invalid pointers invalidate the configuration.
* An invalid or empty feature set restores the deployment defaults. Feature decisions do not depend on mapping or value order.
* If the setting is absent, claim-based overrides are disabled.

Token-derived values are reevaluated when authentication is restored, a user logs in or out, an access token is refreshed or removed, or the selected infrastructure changes. Expired tokens and infrastructures marked unauthenticated no longer provide overrides. The configured deployment values remain unchanged and are restored when an override is unavailable.

| Feature value | Result |
| --- | --- |
| `endpoint-config-available` / `endpoint-config-unavailable` | Show or hide endpoint configuration |
| `single-aas` / `multiple-aas` | Use the single- or multiple-AAS presentation |
| `sm-viewer-editor` | Enable separate Submodel viewer and editor routes |
| `single-sm` / `multiple-sm` | Use the single- or multiple-Submodel presentation |
| `allow-editing` / `forbid-editing` | Show or hide editing controls |
| `allow-uploading` / `forbid-uploading` | Show or hide upload controls |
| `allow-logout` / `forbid-logout` | Show or hide logout controls |

If a token contains both values from a pair, the restrictive value wins: `endpoint-config-unavailable`, `single-aas`, `single-sm`, `forbid-editing`, `forbid-uploading`, or `forbid-logout`.

##### Keycloak

Create a multivalued `basyx_features` user attribute and add an OIDC **User Attribute** protocol mapper to the UI client. Configure the mapper to include the attribute as a multivalued string claim named `basyx_features` in access tokens. The mapping above then reads it from `/basyx_features`.

Keep backend authorization separate. For example, map a different user attribute or client role to the permission claims consumed by BaSyx Go. Feature values only control UI presentation and never grant API permissions.

##### Microsoft Entra ID

For Microsoft Entra ID, create a multivalued string directory extension through Microsoft Graph. Use the object ID of the application which owns the extension definition:

```http
POST https://graph.microsoft.com/v1.0/applications/{extension-owner-object-id}/extensionProperties
Content-Type: application/json

{
  "name": "basyx_features",
  "dataType": "String",
  "isMultiValued": true,
  "targetObjects": ["User"]
}
```

The response contains the full property name `extension_<appid-without-dashes>_basyx_features`. Populate that property on each user through Microsoft Graph or provisioning. Add the full property name to the BaSyx resource application's access-token optional claims:

```json
{
  "optionalClaims": {
    "accessToken": [
      {
        "name": "extension_<appid-without-dashes>_basyx_features",
        "source": "user",
        "essential": false
      }
    ]
  }
}
```

Entra emits the directory extension in the JWT as `extn.basyx_features`. Set the UI environment variable to the complete mapping array:

```json
[
  {
    "target": "features",
    "mode": "list",
    "sources": ["/extn.basyx_features"]
  }
]
```

BaSyx Go can independently consume the same raw claim with this provider entry in its trust list:

```json
{
  "issuer": "https://login.microsoftonline.com/{tenant-id}/v2.0",
  "audience": "{basyx-resource-application-id}",
  "claimMappings": [
    {
      "target": "features",
      "mode": "list",
      "sources": ["/extn.basyx_features"]
    }
  ]
}
```

Alternatively, use a custom claims provider which emits a string array named `basyx_features` and change both source pointers to `/basyx_features`.

Access-token optional claims belong to the resource application whose audience the UI requests, not only to the SPA registration. BaSyx Go validates the token and creates the canonical `basyx.features` claim internally. The identity provider must not emit `basyx.features` directly because the `basyx.*` namespace is reserved and protected by BaSyx Go. Backend policies should use separately mapped permission attributes rather than treating UI feature values as authorization.

A runnable Keycloak and BaSyx Go setup is available in the [ClaimBasedFeatureControl example](https://github.com/eclipse-basyx/basyx-aas-web-ui/tree/main/examples/ClaimBasedFeatureControl).

### Miscellaneous

* `VITE_INFLUXDB_TOKEN`
* `VITE_EDITOR_ID_PREFIX`

## Base Path Configuration

The Web UI supports deployment under a custom base path (e.g. `/basyx-ui/`).

* Controlled via the `BASE` environment variable
* Replaced at runtime by `entrypoint.sh` in production (`BASE_PATH`)

This affects:

* Routing
* Static asset resolution
* Mounted configuration files (e.g. `basyx-infra.yml`, logos)

```{hint}
When deploying under a non-root base path, all mounted static files must be available under the same base path.
```

## Start Page Configuration

The Web UI supports configuring which page is shown when the application is first loaded.

* Controlled via the `VITE_START_PAGE_ROUTE_NAME` environment variable (`START_PAGE_ROUTE_NAME` in production)
* The value must be a valid **named route** defined in the application router (e.g. `AASViewer`, `AASEditor`, `SubmodelViewer`)
* Validated at startup: the configured route must exist, its associated feature flag must be enabled, and the corresponding module must be visible
* If the value is invalid or the route is not accessible, the application falls back to the default start page
* After authentication, the application automatically redirects to the configured start page

```{note}
The main viewer route has moved from `/` to `/aasviewer`. Navigating to `/` now shows a 404 page unless an explicit start page redirect is configured. The default start page route is `AASViewer`.
```

### Behavior

* On initial load: the root path (`/`) redirects to the resolved start page route
* After authentication: the user is redirected to the configured start page
* Invalid route names: fall back to the application default
* Feature flag gating: if the target route's feature flag is disabled, the fallback is used

## Infrastructure Editing via UI

If enabled, the Web UI allows users to edit infrastructure configurations directly.

* Controlled by `ENDPOINT_CONFIG_AVAILABLE=true`
* Enabled by default in development and production

Edits:

* Are stored in `localStorage`
* Override YAML and environment configuration
* Are local to the browser instance

```{note}
Infrastructure editing does not modify backend configuration or YAML files.
```

## Docker and Runtime Injection

In containerized deployments:

* Configuration is injected via environment variables
* `entrypoint.sh` performs:
  * Base path replacement
  * Environment variable injection into Pinia stores
  * Configuration file placement

This allows the same image to be reused across environments without rebuilding.

## Development vs Production Summary

| **Aspect** | **Development** | **Production** |
|------------|-----------------|----------------|
| Env vars | `env.development` (`VITE_*`) | Docker env vars |
| Infra config | `basyx-infra.yml` | `basyx-infra.yml` |
| Overrides | localStorage | localStorage |
| Base path | runtime | runtime |

## Common Pitfalls

* Committing local changes to `basyx-infra.yml`
* Assuming UI-edited infrastructure is persisted globally
* Using OAuth client secrets in frontend configuration
* Selecting a template that does not match the actual backend topology
* Forgetting to adjust mounted paths when changing `BASE`/`BASE_PATH`
* Setting `START_PAGE_ROUTE_NAME` to a route whose feature flag is disabled (the app will fall back silently)
* Using a path (e.g. `/aasviewer`) instead of a named route (e.g. `AASViewer`) for `START_PAGE_ROUTE_NAME`

## YAML Infrastructure Configuration

The primary and recommended method for configuring infrastructure connections is through the `basyx-infra.yml` file. This section provides comprehensive technical details for system administrators and developers.

### File Locations

* **Development Mode**: `/aas-web-ui/public/config/basyx-infra.yml`
* **Production Mode (Docker)**: Mount to `/basyx-infra.yml` in the container

The configuration file is copied to the application's config directory at container startup and parsed client-side by the browser using the `js-yaml` library.

### Basic Structure

```yaml
infrastructures:
  default: <default-infra-key>

  <infra-key>:
    name: <infrastructure-name>
    template: <template-name>
    components:
      aasDiscovery:
        baseUrl: "<url>"
      aasRegistry:
        baseUrl: "<url>"
        hasDiscoveryIntegration: <true|false>  # optional
      submodelRegistry:
        baseUrl: "<url>"
      aasRepository:
        baseUrl: "<url>"
        hasRegistryIntegration: <true|false>   # optional
      submodelRepository:
        baseUrl: "<url>"
        hasRegistryIntegration: <true|false>   # optional
      conceptDescriptionRepository:
        baseUrl: "<url>"
    security:
      type: <auth-type>
      config:
        # Authentication-specific configuration
```

The `template` field is optional for backward compatibility. If it is omitted or contains an unknown value, the Web UI uses `full`.

### Infrastructure Templates

Infrastructure templates describe which endpoint fields are active for a backend topology. The selected template controls the endpoint fields shown in the UI, the YAML component keys that are read, and whether grouped endpoints are mapped to multiple internal components.

| Template | YAML endpoint keys | Description |
|----------|--------------------|-------------|
| `full` | `aasDiscovery`, `aasRegistry`, `submodelRegistry`, `aasRepository`, `submodelRepository`, `conceptDescriptionRepository` | Separate discovery, registries, repositories, and concept description repository |
| `mono-repo` | `aasDiscovery`, `aasRegistry`, `submodelRegistry`, `aasEnvironment` | Separate discovery and registries with one AAS Environment endpoint for AAS, Submodel, and Concept Description repository APIs |
| `mono-all` | `aasEnvironment` | One AAS Environment endpoint exposes all full-template component APIs |
| `identifiable` | `aasRepository` | AAS Repository only; Submodels are resolved through shell paths such as `/shells/{aasId}/submodels/{submodelId}` |
| `catena-x` | `digitalTwinRegistry`, `submodelService` | Digital Twin Registry plus Submodel Service |

```{note}
Grouped endpoint keys are expanded internally. For example, `aasEnvironment` in the `mono-repo` template sets the AAS Repository, Submodel Repository, and Concept Description Repository URLs to the same base URL.
```

### Configuration Examples

#### No Authentication

```yaml
infrastructures:
  default: local

  local:
    name: Local Development Environment
    template: full
    components:
      aasDiscovery:
        baseUrl: "http://localhost:9084"
      aasRegistry:
        baseUrl: "http://localhost:9082"
        hasDiscoveryIntegration: true
      submodelRegistry:
        baseUrl: "http://localhost:9083"
      aasRepository:
        baseUrl: "http://localhost:9081"
        hasRegistryIntegration: true
      submodelRepository:
        baseUrl: "http://localhost:9081"
        hasRegistryIntegration: true
      conceptDescriptionRepository:
        baseUrl: "http://localhost:9081"
    security:
      type: none
```

#### OAuth2 Authorization Code Flow

```yaml
infrastructures:
  default: production

  production:
    name: Production Environment
    template: full
    components:
      aasDiscovery:
        baseUrl: "https://discovery.basyx.example.com"
      aasRegistry:
        baseUrl: "https://aasreg.basyx.example.com"
      submodelRegistry:
        baseUrl: "https://smreg.basyx.example.com"
      aasRepository:
        baseUrl: "https://aasenv.basyx.example.com"
      submodelRepository:
        baseUrl: "https://aasenv.basyx.example.com"
      conceptDescriptionRepository:
        baseUrl: "https://aasenv.basyx.example.com"
    security:
      type: oauth2
      config:
        flow: auth_code
        issuer: "https://keycloak.example.com/auth/realms/BaSyx"
        clientId: "basyx-web-ui"
        scope: "openid profile email"
```

#### OAuth2 Client Credentials Flow

```{danger}
Client Credentials Flow exposes the client secret in the browser. Only use for internal/trusted environments.
```

```yaml
infrastructures:
  default: service

  service:
    name: Service Account Environment
    template: mono-repo
    components:
      aasDiscovery:
        baseUrl: "https://discovery.internal.basyx.com"
      aasRegistry:
        baseUrl: "https://aasreg.internal.basyx.com"
      submodelRegistry:
        baseUrl: "https://smreg.internal.basyx.com"
      aasEnvironment:
        baseUrl: "https://aasenv.internal.basyx.com"
    security:
      type: oauth2
      config:
        flow: client_credentials
        issuer: "https://keycloak.internal.basyx.com/auth/realms/BaSyx"
        clientId: "basyx-service-client"
        clientSecret: "your-client-secret-here"
        scope: ""
```

#### Basic Authentication

```yaml
infrastructures:
  default: staging

  staging:
    name: Staging Environment
    template: catena-x
    components:
      digitalTwinRegistry:
        baseUrl: "https://dtr.staging.basyx.com"
      submodelService:
        baseUrl: "https://submodel-service.staging.basyx.com"
    security:
      type: basic
      config:
        username: "admin"
        password: "admin123"
```

#### Bearer Token Authentication

```yaml
infrastructures:
  default: test

  test:
    name: Test Environment
    template: identifiable
    components:
      aasRepository:
        baseUrl: "https://aasenv.test.basyx.com"
    security:
      type: bearer
      config:
        token: "your-bearer-token-here"
```

#### Multiple Infrastructures

```yaml
infrastructures:
  default: production

  production:
    name: Production Environment
    template: full
    components:
      # ... production components
    security:
      type: oauth2
      config:
        flow: auth_code
        issuer: "https://auth.prod.example.com/realms/basyx"
        clientId: "basyx-web-ui"
        scope: "openid profile email"

  development:
    name: Development Environment
    template: mono-repo
    components:
      # ... development components
    security:
      type: none

  partner-a:
    name: Partner A Infrastructure
    template: catena-x
    components:
      # ... partner A components
    security:
      type: oauth2
      config:
        flow: auth_code
        issuer: "https://auth.partner-a.com/realms/basyx"
        clientId: "basyx-integration"
        scope: "openid"
```

### Docker Deployment

#### Mounting the Configuration File

```yaml
services:
  aas-web-ui:
    image: eclipsebasyx/aas-gui:latest
    ports:
      - '3000:3000'
    volumes:
      - ./basyx-infra.yml:/basyx-infra.yml:ro
    environment:
      # Control whether users can edit infrastructures
      ENDPOINT_CONFIG_AVAILABLE: "false"  # Locked mode
```

#### Configuration Precedence

Environment variables take precedence over YAML for backward compatibility:

1. **Environment Variables** (highest): If any infrastructure-related env vars are set (e.g., `AAS_REGISTRY_PATH`, `KEYCLOAK_URL`), the YAML file is ignored
2. **YAML Configuration**: Only used when no env vars are configured
3. **User Edits (localStorage)**: User modifications in the UI (if `ENDPOINT_CONFIG_AVAILABLE=true`)

#### Configuration Locking

Control user ability to modify infrastructures with `ENDPOINT_CONFIG_AVAILABLE`:

* **`false`** (Locked Mode):
  - Configuration source (env vars or YAML) takes full precedence
  - Users cannot add or edit infrastructures
  - Recommended for production

* **`true`** (Editable Mode - Default):
  - Configurations from source are loaded as templates
  - Users can edit and create infrastructures
  - Changes stored in browser localStorage
  - Recommended for development

### Security Configuration Reference

#### Authentication Types

The `security.type` field accepts:

* **`none`**: No authentication
* **`oauth2`**: OAuth2/OIDC authentication
* **`basic`**: HTTP Basic authentication
* **`bearer`**: Bearer token authentication

#### OAuth2 Fields

**Common Fields (Both Flows):**

* `flow` (required): `auth_code` or `client_credentials`
* `issuer` (required): OAuth2 authorization server URL
* `clientId` (required): Client identifier
* `scope` (optional): Space-separated OAuth2 scopes (default: `""`)

**Client Credentials Flow Only:**

* `clientSecret` (required): Client secret for authentication

```{warning}
Client secrets in YAML files are visible in the browser. Never use client credentials flow in production environments with external access.
```

#### Basic Authentication Fields

* `username` (required): Username for authentication
* `password` (required): Password for authentication

#### Bearer Token Fields

* `token` (required): Bearer token string

#### Component Integration Options

These optional boolean flags control who is responsible for synchronizing descriptors and asset links when AAS or submodels change.

**`aasRegistry`:**

* `hasDiscoveryIntegration` (optional, default: `true`): When `true`, the backend is expected to handle Discovery Service synchronization itself — the Web UI will not create, update, or delete asset links in the Discovery Service. When `false`, the Web UI takes over this responsibility and directly manages asset links based on changes to AAS.

**`aasRepository` and `submodelRepository`:**

* `hasRegistryIntegration` (optional, default: `true`): When `true`, the backend is expected to handle Registry synchronization itself — the Web UI will not create, update, or delete descriptors in the corresponding registry. When `false`, the Web UI takes over this responsibility and directly manages descriptors based on changes to AAS and submodels.

**Grouped endpoints:**

* `aasEnvironment.hasRegistryIntegration` can be used in grouped templates such as `mono-repo`. It is applied to both the internal AAS Repository and Submodel Repository compatibility slots.
* Integration options are only relevant when the corresponding registry or discovery component is active for the selected template.

```{note}
Set these flags to `false` when your backend components do **not** have built-in registry or discovery integration. In that case, the Web UI automatically keeps registries and discovery in sync whenever AAS or submodels are created, updated, or deleted.
```

### Implementation Details

#### Infrastructure IDs

* YAML-based: `yaml_<yamlKey>` (stable across reloads)
* User-created: `infra_<timestamp>` (unique per creation)

#### YAML to Internal Format Mapping

* YAML `baseUrl` → Internal `url`
* YAML `template` → Internal infrastructure template (`full` if missing or invalid)
* Individual YAML endpoint keys map to internal component slots
* Grouped YAML endpoint keys, such as `aasEnvironment`, `digitalTwinRegistry`, and `submodelService`, map to the active internal component slots for the selected template
* Security types map: `none` → `No Authentication`, `oauth2` → `OAuth2`, etc.

#### Loading Process

1. **Container Startup** (`entrypoint.sh`):
   ```bash
   if [ -f "/basyx-infra.yml" ]; then
     cp /basyx-infra.yml /usr/src/app/dist/config/basyx-infra.yml
   fi
   ```

2. **Application Startup** (`useInfrastructureConfigLoader`):
   - Determine base path (dev: `import.meta.env.BASE_URL`, prod: `envStore.getEnvBasePath`)
   - Fetch from `${basePath}/config/basyx-infra.yml`
   - Return null if file doesn't exist (404)

3. **YAML Parsing** (`useInfrastructureYamlParser`):
   - Normalize the infrastructure template
   - Convert YAML structure to internal format
   - Transform `baseUrl` to `url` for each component
   - Expand grouped endpoint fields to internal component URLs
   - Map security types to internal enums
   - Generate infrastructure IDs: `yaml_<yamlKey>`

4. **Configuration Merging** (`useInfrastructureStorage`):
   - Check if environment variables are configured
   - Load YAML file and parse infrastructures
   - Merge with localStorage based on `ENDPOINT_CONFIG_AVAILABLE`
   - Authenticate client credentials flows automatically

#### OAuth2 Client Credentials Auto-Authentication

When `client_credentials` flow is detected:
* Application automatically authenticates on load
* Token refresh handled automatically
* No user interaction required

### Best Practices

1. **Use YAML for Production**: Avoid deprecated environment variables for infrastructure config
2. **Lock Configuration**: Set `ENDPOINT_CONFIG_AVAILABLE=false` in production
3. **Secure Secrets**: Never use client credentials flow with external access
4. **Multiple Infrastructures**: Define all environments in one YAML file
5. **Select the Right Template**: Use `mono-repo`, `mono-all`, `identifiable`, or `catena-x` when your deployment does not expose separate full-template endpoints
6. **Default Infrastructure**: Always set a sensible `default` key
7. **Version Control**: Commit YAML templates, not files with real credentials
8. **Read-Only Mount**: Mount YAML files as read-only (`:ro`) in Docker

## Introducing new Environment Variables

When introducing new environment variables, the following files need to be updated accordingly:

* `env.development` – for development mode (including dev defaults)
* `entrypoint.sh` – for production mode (including prod defaults)
* `src/store/EnvironmentStore.ts` – to define placeholders that will be replaced at runtime (including application defaults)

## Next Steps

* [Design Guidelines](design_guidelines.md)
* [Creating Submodel Plugins](creating_submodel_plugins.md)
* [Developing Custom Modules](developing_custom_modules.md)
