# CMEM Helm chart

This chart deploys the core CMEM consisting of the following components:

- Explore
- Dataintegration
- bootstrap done with eccenca-cmemc
- GraphInsights (optional, `graphinsights.enabled`)
- Marketplace (optional, `marketplace.enabled`, enabled by default)

Please see below on how to install, upgrade or uninstall the chart.

## Prerequisites

- Kubernetes 1.23+
- Helm 3+

## Kubernetes and OpenShift compatibility

### Kubernetes

- Minimum supported version: **1.23+**. `Chart.yaml` does not set a `kubeVersion` constraint, so Helm will not enforce this — it is a documented minimum only.
- No maximum version is pinned or tested; the chart is not validated against a specific upper bound in CI.
- `templates/ingress.yaml` contains fallback logic down to `networking.k8s.io/v1beta1` and `extensions/v1beta1` (pre-1.19/pre-1.14 clusters), but this is legacy compatibility code — only 1.23+ is actively supported.

### OpenShift / OKD

This chart is **not fully validated on OpenShift** yet. Status of known gaps:

- **`Route` support added (opt-in).** `templates/route.yaml` creates one `route.openshift.io/v1` `Route` per entry in `route.paths` (a Route only supports a single backend service, unlike `Ingress`, so each path becomes its own object). Disabled by default (`route.enabled: false`) and independent of `ingress.enabled` — enable one or the other depending on your cluster. Default TLS is `edge` termination with `Redirect` and no explicit certificate, i.e. it uses the cluster's default router wildcard certificate; override `route.tls.certificate`/`key`/`caCertificate` for a custom one, or set `route.tls.enabled: false` for a plain HTTP route. See `values.yaml` for all fields.

  ```yaml
  # values-openshift.yaml
  ingress:
    enabled: false
  route:
    enabled: true
    host: cmem.apps.example.com
  ```

- **Hardcoded pod UID/GID — needs a per-component decision, not a blanket fix.** Every component sets a fixed `runAsUser`/`fsGroup` (and in some cases `runAsGroup`) in `values.yaml` under `podSecurityContext` (`bootstrap`: 999, `explore`: 10001, `dataintegration`: `runAsUser` 1213 / `fsGroup` 10001, `graphinsights`/`marketplace`: 1213). OpenShift's default `restricted` SCC assigns an arbitrary UID from the project's allocated range and rejects a fixed `runAsUser` unless it falls within that range or the deploying ServiceAccount is granted the `anyuid` SCC.

  The fields can already be dropped per component today, without any template change, by setting them to `null` in an overlay values file, e.g.:

  ```yaml
  # values-openshift.yaml
  explore:
    podSecurityContext:
      runAsNonRoot: true
      runAsUser: null
      fsGroup: null
  ```

  Whether that's *safe* depends on the image, though:
  - **Explore** does not set a `USER` in its Dockerfile, so it already runs as whatever UID it's given — dropping `runAsUser`/`fsGroup` here to let OpenShift assign an arbitrary UID (with the guaranteed primary group `0` that `restricted` SCC gives arbitrary-UID pods) is the safe, low-risk case.
  - **DataIntegration, GraphInsights, Marketplace and the bootstrap job** all bake a specific `USER` into their Dockerfiles. All containers already run with `readOnlyRootFilesystem: true`, so their actual writable paths are the mounted `emptyDir`/PVC volumes (`/tmp`, DI data, Marketplace data, python-plugin install dir, trust stores) — those are covered by Kubernetes' `fsGroup` volume-ownership mechanism regardless of the image's baked-in UID, so dropping `fsGroup` for these should be safe. Dropping `runAsUser`/`runAsGroup` is the riskier part: OpenShift will still start the container with an SCC-assigned arbitrary UID instead of the image's baked-in one, which only works cleanly if the image's *own* filesystem (installed binaries/config, not the mounted volumes) is group-owned by `0` with group read/write/execute — that has not been verified for these images. Until it is, the safer option on OpenShift for these four components is either to keep `runAsUser` as configured and grant the deploying ServiceAccount the `anyuid` SCC, or to fix the images to follow the "arbitrary UID, GID 0" convention and then drop `runAsUser` there too.

- **Container-level `securityContext` is already OpenShift-friendly.** `allowPrivilegeEscalation: false`, all Linux capabilities dropped, and `readOnlyRootFilesystem: true` are set on every container by default and are compatible with the `restricted`/`restricted-v2` SCC.
- No `hostNetwork`, `hostPath`, `hostPID`/`hostIPC`, or privileged containers are used anywhere in the chart, so no additional SCC beyond the UID/GID point above should be required.

Planned follow-up: verify (with the image maintainers) whether the DataIntegration/GraphInsights/Marketplace/bootstrap images already follow the OpenShift arbitrary-UID convention; if not, fix the Dockerfiles and then drop the hardcoded `runAsUser`/`runAsGroup` defaults chart-wide.

## Cluster requirements

This section always depends on the load to be expected for the instance. Default setup requires ~ 8 GBs RAM
- Ressources: ~ 4CPU
  - Explore: 2,5 GB RAM
  - DataIntegration: 5GB RAM
  - InitContainer cmemc: 128M RAM
- Volumes
  - Explore:
    - tempFS/emptyDir 1GB /tmp
    - optional: tempFS/emptyDir <10M trust store for custom SSL certifcatates
  - DataIntegration:
    - Volume 5GB for DI datasets, uploaded data/files.
    - tempFS/emptyDir 10GB temp (/tmp, `dataintegration.temp`)
    - optional: tempFS/emptyDir 500M pythonPluginImageInstall
    - optional: tempFS/emptyDir <10M trust store for custom SSL certifcatates
  - Marketplace (optional, enabled by default):
    - Volume 5GB for the marketplace data directory (`marketplace.persistence`)
    - optional: tempFS/emptyDir <10M trust store for custom SSL certifcatates
  - Bootstrap:
    - optional: tempFS/emptyDir <10M trust store for custom SSL certifcatatest

## Releases and Compatibility matrix

- Helm chart and CMEM releases are released independent from each other, with different version schema (you can find the current version of the Helm chart under the field `key` in the file `Chart.yaml`)
- Any given release of this Helm chart supports a specific `<major>.<minor>` release line of CMEM. Feel free to change the default value for `dataNNNN.image.tag.` in a given Helm chart release to e.g. the latest stable release.
- The default value for `dataNNNN.image.tag.` in a given Helm chart release
  should be the latest stable release, of the latest supported `<major>.<minor>` line, at the
  time the Helm chart is released.
- If we drop support for older CMEM releases in our Helm chart, we increase the `<major>` version of that Helm chart.
- The `appVersion` of a Helm chart (see `Chart.yaml`) corresponds to the latest CMEM release this Helm chart supports (in the form of `<major>.<minor>`, e.g. `v22.2`)

| Helm Chart Release | Supported CMEM Releases | Default Release |
|--------------------|-------------------------|-----------------|
| 5.x.y              | 24.3 - 25.1             | 25.1.*          |
| 5.2.y              | 25.2.*                  | 25.2.*          |
| 5.3.y              | 25.2.*                  | 25.2.*          |
| 5.4.y              | 25.3 - *                | 25.3.*          |
| 5.5.y              | 25.3 - *                | 25.3.*          |
| 5.6.y              | 26.1                    | 26.1.*          |
| 5.6.y              | 26.1                    | 26.1.*          |
| 5.7.y              | 26.1                    | 26.1.*          |
| 5.8.y              | 26.1                    | 26.1.*          |
| 6.x.y              | 26.2 - *                | 26.2.*          |


## Branch and tag naming
```
Branches
  master          Points to latest release/tag
  develop         Long-lived dev
  feature/*       Short-lived dev
  attic/*         Cool ideas

Tags
  x.y.z           Helm chart releases
```

## Getting started

### Prerequisites

Understanding this helm chart requires you to have some basic understandings in the following technologies. There you also find instructions on how to install them. Soon we also provide a repository to give a basic local setup based on k3d a k3s in docker environment.

- Docker https://docs.docker.com/get-started/
- Kubernetes concepts https://kubernetes.io/docs/concepts/
- Helm https://helm.sh/docs/intro/quickstart/

As this was already mentionend this Repository only installs Corporate Memory without Keycloak and GraphDB. Here we asume you already have Graphdb and Keycloak deployed. Please see configurations further below.

- https://github.com/Ontotext-AD/graphdb-helm
- https://github.com/bitnami/charts/tree/main/bitnami/keycloak

#### Binaries
- Install `helm`: https://helm.sh/docs/intro/install/
- Install `kubectl`: https://kubernetes.io/docs/tasks/tools/install-kubectl/
- If deploying on K3D, download a static binary from
  https://github.com/k3d-io/k3d/releases (or use the script at https://k3d.io/
  to do the same).

## Architecture
 ![CMEM Helm Chart Architecture](images/cmem-helm-architecture.png)

## Quick Start

This section provides quick deployment scenarios for common environments.

### Quick Start: K3d (Local Development)

For local development with k3d:

```yaml
# quick-start-k3d.yaml
global:
  protocol: https
  hostname: cmem.docker.localhost
  cmemClientId: cmem-service-account
  cmemClientSecret: <your-secret>
  keycloakBaseUrl: https://keycloak.docker.localhost/auth
  keycloakIssuerUrl: https://keycloak.docker.localhost/auth/realms/cmem

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: cmem.docker.localhost

explore:
  companion:
    persistence:
      storageClassName: local-path  # k3d default storage class
  store:
    graphdb:
      enabled: true
      host: graphdb.graphdb
      repository: cmem
      user: provisioner
      password: <your-password>

dataintegration:
  persistence:
    storageClassName: local-path  # k3d default storage class

graphinsights:
  enabled: false  # Optional, enable if needed
  persistence:
    storageClassName: local-path
```

Deploy:
```bash
helm upgrade --install cmem . -n cmem --create-namespace -f quick-start-k3d.yaml
```

### Quick Start: Production

For production deployments with proper TLS:

```yaml
# quick-start-production.yaml
global:
  protocol: https
  hostname: cmem.example.com
  cmemClientId: cmem-service-account
  cmemClientSecret: <your-secret>
  keycloakBaseUrl: https://keycloak.example.com/auth
  keycloakIssuerUrl: https://keycloak.example.com/auth/realms/cmem
  license: cmem-license  # Reference to license secret

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: cmem.example.com
  tls:
    - secretName: cmem-tls-cert
      hosts:
        - cmem.example.com

explore:
  resources:
    limits:
      memory: 4Gi
    requests:
      memory: 4Gi
  store:
    graphdb:
      enabled: true
      host: graphdb.production.svc
      sslEnabled: false
      user: provisioner
      password: "changeme"

dataintegration:
  resources:
    limits:
      memory: 8Gi
    requests:
      memory: 8Gi
  persistence:
    storageClassName: fast-ssd  # Use production storage class
    storageSize: 20Gi
```

## Troubleshooting

### Common Issues

#### 1. Pods Stuck in Pending State

**Symptom**: Pods show `Pending` status indefinitely.

**Cause**: Usually PersistentVolumeClaim (PVC) cannot be bound due to:
- Missing or incorrect `storageClassName`
- No available PersistentVolumes
- Insufficient storage capacity

**Solution**:
```bash
# Check PVC status
kubectl get pvc -n cmem

# Check available storage classes
kubectl get storageclass

# Update values.yaml with correct storage class
# For k3d: use "local-path"
# For AWS: use "gp3" or "ebs-sc"
# For GCP: use "standard" or "pd-ssd"
```

#### 2. StorageClassName Immutability Error

**Symptom**: Helm upgrade fails with `PersistentVolumeClaim is invalid: spec: Forbidden: spec is immutable`

**Cause**: Trying to change `storageClassName` on existing PVC.

**Solution**:
```bash
# Option 1: Delete and recreate (DATA LOSS!)
kubectl delete namespace cmem
helm install cmem . -n cmem -f values.yaml

# Option 2: Fix values.yaml to match existing PVC
kubectl get pvc -n cmem -o yaml | grep storageClassName
# Update your values.yaml to match the existing storageClassName
```

#### 3. License Issues

**Symptom**: Application starts but features are limited or show license warnings.

**Cause**: Missing or incorrect license configuration.

**Solution**:
```bash
# Create license secret
kubectl create secret generic cmem-license \
  --from-file=license.asc=/path/to/license.asc \
  -n cmem

# Update values.yaml
global:
  license: cmem-license
```

#### 4. Keycloak Authentication Failures

**Symptom**: Unable to login, OAuth errors in logs.

**Cause**: Incorrect Keycloak configuration or network issues.

**Solution**:
```bash
# Verify Keycloak URLs are accessible from pods
kubectl exec -it explore-0 -n cmem -- curl -k <keycloak-url>

# Check Keycloak realm and client configuration
# Ensure cmem-service-account has Service Accounts Enabled
# Verify client secret matches values.yaml

# For k3d: Add extraHosts to resolve .docker.localhost domains
dataintegration:
  extraHosts:
    - ip: 172.18.0.X  # Cluster endpoint IP
      hostnames:
        - keycloak.docker.localhost
```

#### 5. Image Pull Errors

**Symptom**: `ImagePullBackOff` or `ErrImagePull` status.

**Cause**: Missing or incorrect Docker registry credentials.

**Solution**:
```bash
# Recreate docker registry secret
kubectl create secret docker-registry eccenca-docker-registry-credentials \
  --docker-server=https://docker-registry.eccenca.com \
  --docker-username=<your-username> \
  --docker-password=<your-password> \
  -n cmem

# Or disable auto-creation and use manual secret
imageCredentials:
  enabled: false
```

### Debug Commands

```bash
# Check pod status and events
kubectl get pods -n cmem
kubectl describe pod <pod-name> -n cmem

# View pod logs
kubectl logs -f <pod-name> -n cmem

# Check PVC status
kubectl get pvc -n cmem
kubectl describe pvc <pvc-name> -n cmem

# Test connectivity from pod
kubectl exec -it <pod-name> -n cmem -- /bin/bash

# View Helm release status
helm list -n cmem
helm status cmem -n cmem

# Check differences before upgrade
helm diff upgrade cmem . -n cmem -f values.yaml
```

## Installation


This guide provides instructions on how to install the chart using `kubectl` and `helm`.
You need to have a keycloak instance and a supported graph database installed.

### 1. Download the chart or use our helm repository or clone the repository

```console
wget https://releases.eccenca.com/cmem-helm/latest.tgz
tar -xzf latest.tgz
cd cmem
```

```console
helm repo add --force-update cmem-helm https://helm.eccenca.com
helm repo update cmem-helm
```

```console
# this requires gitlab.eccenca.com access
git clone https://gitlab.eccenca.com/cmem/cmem-helm.git
cd cmem-helm
```

### 2. Create a namespace

It is recommended to install CMEM in its own namespace.

```console
kubectl create namespace <your-namespace>
```
Replace `<your-namespace>` with the desired namespace (e.g., `cmem`).

### 3. Create Docker registry credentials

To pull the CMEM images, you need to provide credentials to your Docker registry.

```console
kubectl create secret docker-registry eccenca-docker-registry-credentials \
  --docker-server=https://docker-registry.eccenca.com \
  --docker-username=<your-docker-username> \
  --docker-password=<your-docker-password> \
  -n <your-namespace>
```
Replace the placeholders with the provided registry details and credentials.

### 3b. (optional) Create cmem license secret

By default, Corporate Memory is subject to the eccenca free Personal, Evaluation and Development License Agreement (PEDAL), a license intended for non-commercial usage.

If you have a dedicated license file, create a secret with a `license.asc` data entry:

```console
kubectl create secret generic cmem-license \
  --from-file license.asc
  -n <your-namespace>
```

Then, add the secret name to your `values.yaml` file for the key `global.license`.

For more background on license, see also: https://documentation.eccenca.com/latest/deploy-and-configure/configuration/dataplatform/application-full/

### 4. Configure your deployment

Copy the `values.sample.yaml` to a new file, for example `my-values.yaml`.

```console
cp values.sample.yaml my-values.yaml
```

Edit `my-values.yaml` and adjust the configuration to your needs.
At a minimum, you will need to configure the hostname, and connection details for your Ingress or Route, Keycloak and GraphDB.

```yaml
ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: <cluster-issuer-if-present>
  hosts:
    - host: "<your-cmem-hostname>"
      paths:
        - path: /
          pathType: Prefix
          serviceName: explore
          servicePort: 8080
        - path: /dataintegration
          pathType: Prefix
          serviceName: dataintegration
          servicePort: 8080
        - path: /graphinsights
          pathType: Prefix
          serviceName: graphinsights
          servicePort: 8080
  tls:
    - hosts:
         - "<your-cmem-hostname>"
      secretName:  cmem-ingress-cert

global:
  protocol: "https"
  cmemClientId: cmem-service-account
  cmemClientSecret: changeme
  graphinsightsClientId: graph-insights-service-account
  graphinsightsClientSecret: changeme

  hostname: "<your-hostname>"
  keycloakBaseUrl: https://<your-keycloak-hostname>/auth/'
  keycloakIssuerUrl: https://<your-keycloak-hostname>/auth/realms/cmem'
  oauthClientId: cmem
  oauthClientIdGraphInsights: cmem
  # If you specified customCACerts, an initContainer is added to DI and EXPLORE to append your custom CA to the system-wide TrustStore.
  # Here you can optionally specify resource requests and limits for that initContainer.
  customCACerts: {}

  # (optional if 3b was created)
  # license: cmem-license

explore:
  store:
    graphdb:
      enabled: true
      repository: cmem
      user: provisioner
      password: "iHaveSuperpowers"
      host: "<your-graphdb-hostname>"
      sslEnabled: false

```

### 4.1 Configure LLM Provider (explore)


In Explore we use Spring AI to configure an LLM provider. For full reference
see [https://docs.spring.io/spring-ai/reference/api/chat/comparison.html](https://docs.spring.io/spring-ai/reference/api/chat/comparison.html).
For the most useful models and provider we added example configurations in `configuration-files/application-llm-*.yml`.
To enable the companion set `.Values.explore.companion.enabled`.
You then have to configure the Spring AI module in `.Values.explore.companion.config`.
See the examples for that.

### 4.2 LLM Provider Setup (DataIntegration)

For DataIntegrations Mapping Creator LLM support you would need to enable this `.Values.dataintegration.mappingCreator.enabled`
Further configuration for this can be found in `configuration-files/dataintegration.conf` section `com.eccenca.di.assistant`.

``` yaml
.Values.dataintegration.mappingCreator:
  enable: false
  AiApiKey: "changeme"
  AiOrgId : ""
  AiModel: "openai/gpt-5-mini"
  AiCoreUrl: "https://openrouter.ai/api/v1"
  AiReasoningEffort: "low"
  AiLogQueries: false
```

### 4.3 Marketplace

The Marketplace is an optional component that lets you install packages into your Corporate Memory
deployment. It is **enabled by default** (`marketplace.enabled: true`). Before deploying, make sure to:

- add an ingress path routing `/marketplace` to the `marketplace` service on port `5050`
  (already contained in the sample ingress above):

  ```yaml
  - path: /marketplace
    pathType: Prefix
    serviceName: marketplace
    servicePort: 5050
  ```

- create a dedicated Keycloak OAuth client and reference it via `global.marketplaceClientId`
  (defaults to `marketplace`).
- provide a valid license via `global.license` — the default Marketplace functions require a license
  (Explore itself still falls back to the free PEDAL license). Please contact Sales for a license.
- change the session signing secret `marketplace.config.sessionSecret` to a secure value, or reference
  an externally managed secret via `marketplace.config.existingSecret`.

To skip the component entirely, set:

```yaml
marketplace:
  enabled: false
```

#### Restricting a standalone Marketplace to allowed licenses

Set `marketplace.config.licenseModeAllowedLicenses` to a comma-separated string of
`checksum` or `checksum:label` entries. Labels are descriptive and must not
contain commas or colons. Empty or unset accepts any license that verifies,
preserving the behavior of existing deployments.

For a standalone service, configure the following (replace the example checksum
with the checksum of an actual client license):

```yaml
global:
  license: ""
marketplace:
  config:
    frontend: "false"
    licenseMode: LICENSE_TOKEN
    licenseFile: ""
    licenseModeAllowedLicenses: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef:example"
    mcp: false
```

The allowlist applies only to licenses received in `x-eccenca-auth`, with
`LICENSE_TOKEN` mode and neither `ECC_MARKETPLACE_LICENSE_TEXT` nor
`ECC_MARKETPLACE_LICENSE_FILE` set. The application ignores it, with a startup
warning, in anonymous mode or when using its own local license. A valid but
unlisted license receives HTTP 403; invalid or expired licenses still fail
verification before the allowlist is checked. Malformed checksums fail startup.

The MCP endpoint is not license-gated and bypasses this allowlist. Set
`marketplace.config.mcp: false` to disable it on a restricted service. Its default
`null` leaves the image's behavior unchanged; `true` explicitly enables it.
`/api/info` and public badge routes remain public.

Calculate a checksum without eccenca tooling:

```bash
grep '^licenseSignature' license.asc | cut -d= -f2- | base64 -d | sha256sum
```

Use `-f2-` to preserve any `=` padding in the base64 signature. On macOS, replace
`sha256sum` with `shasum -a 256`.

The checksum hashes the decoded signature, not the `.asc` file. Installations
sharing a license share its checksum; license renewal requires updating the
allowlist. Authenticated `/api/session` responses also expose the locally configured
license's `licenseChecksum`.

Changing the allowlist or MCP setting changes the pod's existing ConfigMap
checksum annotation, causing a rollout so the process reads the new environment.

### 5. Install the chart

Use `helm` to deploy the chart.

```console
# In case you have the chart or repostiory locally available
helm upgrade --install <release-name> .
  --namespace <your-namespace> \
  -f my-values.yaml

# or use our helm repository
helm upgrade --install <release-name> cmem-helm/cmem
  --namespace <your-namespace> \
  -f my-values.yaml
```

- `<release-name>`: A name for this release (e.g., `cmem`).
- `<your-namespace>`: The namespace created in step 2.

This command will install the chart in the specified namespace using your custom configuration.

_See [configuration](#configuration) below for more details on available options._

_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._

### 6. Verify the installation

After the installation is complete, you can check the status of the pods:

```console
kubectl get pods -n <your-namespace>
```

You can also check the rollout status of the statefulsets:

```console
kubectl rollout status statefulset/explore -n <your-namespace>
kubectl rollout status statefulset/dataintegration -n <your-namespace>
```

## Uninstall chart

```console
helm uninstall -n [RELEASE_NAMESPACE] [RELEASE_NAME]
```

This removes all the Kubernetes components associated with the chart and deletes the release.

_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._

## Preview Helm changes

The [helm-diff](https://github.com/databus23/helm-diff) plugin gives us the possibility to preview what a `helm upgrade` would change.

Install it via `helm plugin install https://github.com/databus23/helm-diff`, then you can run the following prior to an install or upgrade command:

```console
# Helm (requires helm-diff plugin)
helm diff upgrade --install -n [RELEASE_NAMESPACE] [RELEASE_NAME] [-f values.yaml] .
```

## Render Helm templates

To render plain Kubernetes manifests from the Helm chart, run:

```console
helm template -n [RELEASE_NAMESPACE] [RELEASE_NAME] .
```

## Configuration

See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments:

```console
helm show values .
```

### Custom ConfigMaps and Secrets

The chart allows you to override the default ConfigMaps and Secrets with your own custom ones. This is useful when you want to:
- Use external configuration management systems
- Share configurations across multiple deployments
- Maintain configurations separately from the Helm release

#### ConfigMap Overrides

You can provide your own ConfigMaps for application configuration files instead of using the chart's default generated ones.

**Supported ConfigMap Overrides:**

| Component | Configuration Option | ConfigMap Key(s) Required | Description |
|-----------|---------------------|---------------------------|-------------|
| Explore | `explore.config.configMapApplication` | `application.yml` | Spring Boot application configuration |
| Explore | `explore.config.configMapLogback` | `logback.xml` | Logback logging configuration |
| DataIntegration | `dataintegration.config.configMapApplication` | `dataintegration.conf` | Scala Play Framework configuration |
| DataIntegration | `dataintegration.config.configMapLogback` | `logback.xml` | Logback logging configuration |
| GraphInsights | `graphinsights.config.configMapApplication` | `cmem.integration.config.yml` | GraphInsights integration configuration |

**Example: Using Custom ConfigMaps**

```yaml
# values.yaml
explore:
  config:
    # Use your own ConfigMap for application configuration
    configMapApplication: "my-custom-explore-application"
    # Use your own ConfigMap for logging configuration
    configMapLogback: "my-custom-explore-logback"

dataintegration:
  config:
    configMapApplication: "my-custom-di-application"
    configMapLogback: "my-custom-di-logback"

graphinsights:
  config:
    configMapApplication: "my-custom-graphinsights-application"
```

**Creating Custom ConfigMaps:**

```bash
# Create a custom ConfigMap for Explore application configuration
kubectl create configmap my-custom-explore-application \
  --from-file=application.yml=/path/to/your/application.yml \
  -n cmem

# Create a custom ConfigMap for Explore logback configuration
kubectl create configmap my-custom-explore-logback \
  --from-file=logback.xml=/path/to/your/logback.xml \
  -n cmem
```

**Important Notes:**
- When you specify a custom ConfigMap, the chart will NOT create the default ConfigMap
- The custom ConfigMap must exist before deploying the chart
- The ConfigMap keys must match exactly as shown in the table above
- You can refer to the default configuration files in `configuration-files/` directory as templates

#### Secret Overrides

You can provide your own Secrets for sensitive configuration instead of using the chart's default generated ones.

**Supported Secret Overrides:**

| Component | Configuration Option | Description |
|-----------|---------------------|-------------|
| Explore | `explore.config.secret` | Contains GraphDB credentials, OAuth client secrets |
| DataIntegration | `dataintegration.config.secret` | Contains encryption keys, OAuth secrets, S3 credentials |
| GraphInsights | `graphinsights.config.secret` | Contains OAuth client credentials |
| Marketplace | `marketplace.config.existingSecret` | Contains the session signing secret and the Keycloak client secret |

**Example: Using Custom Secrets**

```yaml
# values.yaml
explore:
  config:
    # Use your own Secret
    secret: "my-custom-explore-secret"

dataintegration:
  config:
    secret: "my-custom-di-secret"

graphinsights:
  config:
    secret: "my-custom-graphinsights-secret"
```

**Creating Custom Secrets:**

```bash
# Create a custom Secret for Explore
kubectl create secret generic my-custom-explore-secret \
  --from-literal=STORE_GRAPHDB_USERNAME=provisioner \
  --from-literal=STORE_GRAPHDB_PASSWORD=changeme \
  --from-literal=EXPLORE_CMEM_SERVICE_CLIENT=graph-insights-service-account \
  --from-literal=EXPLORE_CMEM_SERVICE_CLIENT_SECRET=changeme \
  -n cmem
```

**Important Notes:**
- When you specify a custom Secret, the chart will NOT create the default Secret
- The custom Secret must exist before deploying the chart
- The Secret must contain all required keys expected by the component
- Refer to the default Secret templates (`templates/*-secret.yaml`) to understand the required keys:
  - `templates/explore-secret.yaml` for Explore secret keys
  - `templates/di-secret.yaml` for DataIntegration secret keys
  - `templates/graphinsights-secret.yaml` for GraphInsights secret keys

**Testing Your Custom Configurations:**

After deploying with custom ConfigMaps or Secrets, verify they are being used:

```bash
# Check which ConfigMap is mounted
kubectl describe statefulset explore -n cmem | grep -A 5 "config-files"

# Check which Secret is used
kubectl describe statefulset explore -n cmem | grep -A 5 "secretRef"

# View the actual mounted configuration (for debugging)
kubectl exec -it explore-0 -n cmem -- cat /data/application.yml
```

### Configuring Graphdb as backend

Please visit https://github.com/Ontotext-AD/graphdb-helm to get familiar with Ontotext GraphDB. Here we show some

```console
helm repo add --force-update ontotext http://maven.ontotext.com/repository/helm-public"
helm repo update ontotext"

kubectl create namespace graphdb || true"
kubectl -n graphdb create secret generic graphdb-license --from-file graphdb/graphdb.license"
helm upgrade \
    -i \
    -n graphdb \
    --create-namespace
    graphdb GRAPHDB-CHART-OR-PATH
    -f your-graphdb-values.yaml
    --set graphdb.node.license='graphdb-license'"
kubectl rollout status statefulset -n graphdb graphdb-node"
```

You will need to configure those values to set up GraphDB with Corporate Memory.

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| explore.store.graphdb.enabled | bool | `false` | enable this to use GraphDB as backend |
| explore.store.graphdb.repository | string | `cmem` | the repository Corporate Memory stores its data inside GraphDB |
| explore.store.graphdb.user | string | `provisioner` | the GraphDB user used by explore |
| explore.store.graphdb.password | string | `` | the GraphDB users password used by explore |
| explore.store.graphdb.host | string | `` | the GraphDB hostname of the instance (not the URL) |
| explore.store.graphdb.port | string | `7200` | the GraphDB port of the instance or service |
| explore.store.graphdb.sslEnabled | bool | `false` | in case the connections is encrypted (be aware for potential pitfalls here) |


### Configuring Keycloak as authentication provider


```console
helm repo add --force-update bitnami https://charts.bitnami.com/bitnami"
helm repo update bitnami"
kubectl create namespace keycloak --dry-run=client -o yaml | kubectl apply -f -"
# in case you have a working postgres dump
kubectl -n keycloak delete ConfigMap keycloak-postgresql-init-scripts || true "
# in case you have a working postgres dump
cat keycloak/keycloak-postgresql-init-scripts.template.json
          | jq -M --rawfile keycloak_db_sql keycloak/keycloak_db.sql '.data[\"keycloak_db.sql\"] = $keycloak_db_sql'
          | kubectl -n keycloak create -f -"
      kubectl -n keycloak apply -f keycloak/keycloak-secret.yaml"
      kubectl -n keycloak apply -f keycloak/keycloak-postgres-secret.yaml"
      kubectl -n keycloak delete --ignore-not-found=true secret eccenca-docker-registry-credentials"
      kubectl -n keycloak apply -f eccenca-docker-registry-credentials.yaml"
      - defer: rm -f eccenca-docker-registry-credentials.yaml
helm upgrade
          -i
          -n keycloak
          --create-namespace
          keycloak bitnami/keycloak
          -f keycloak/keycloak-values.yaml
          --set ingress.hostname={{ .KEYCLOAK_HOSTNAME }}
          --set initContainers[0].image={{ .KEYCLOAK_THEME_VERSION }}"
      - "cat keycloak/keycloak_separate_ingress.yml
          | sed s/KEYCLOAK_HOSTNAME/{{ .KEYCLOAK_HOSTNAME }}/g
          | sed s/CLUSTER_ISSUER_NAME/{{ .CLUSTER_ISSUER_NAME }}/g
          | kubectl apply -f -"
kubectl rollout status statefulset -n keycloak keycloak-postgresql"
kubectl rollout status statefulset -n keycloak keycloak"
```


You will need to configure those values to set up Keycloak with Corporate Memory.

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| global.cmemClientId | string | `false` | cmem client ID and secret; see also: https://documentation.eccenca.com/24.1/deploy-and-configure/configuration/keycloak/ |
| global.cmemClientSecret | string | `cmem` |  |
| global.keycloakBaseUrl | string | `cmem` | keycloak base url (e.g. https://cmem.example.com/auth) |
| global.keycloakIssuerUrl | string | `cmem` | keycloak cmem realm url (e.g. https://cmem.example.com/auth/realms/cmem)|
| global.oauthClientId | string | `cmem` | keycloak oauth client id (used for explore connection and DataIntegration)|
| global.oauthClientIdGraphInsights | string | `graph-insights` | keycloak oauth client id used by GraphInsights (must exist if GraphInsights is enabled)|
| global.marketplaceClientId | string | `marketplace` | keycloak oauth client id used by the Marketplace (must exist if Marketplace is enabled)|


To configure the installation, please specify additional helm value files via `-f values.yaml`.

We provide a (pre-filled) `values.sample.yaml` file with a minimal set of configuration parameters you would need to provide.

### Extra Manifests

You can add arbitrary Kubernetes resources to the release with `extraManifests`.
Each item can be a YAML object or a templated YAML string; both forms are rendered
with Helm `tpl`, so they can reference chart values and named templates.

```yaml
extraManifests:
  - apiVersion: v1
    kind: ConfigMap
    metadata:
      name: '{{ include "cmem.fullname" . }}-extra-config'
    data:
      release-name: '{{ .Release.Name }}'

  - |
    apiVersion: v1
    kind: Secret
    metadata:
      name: {{ include "cmem.fullname" . }}-extra-secret
    type: Opaque
    stringData:
      token: {{ .Values.global.cmemClientSecret | quote }}
```

This is useful for small companion resources that should be installed and
removed together with the CMEM release, such as RBAC bindings, NetworkPolicies,
or extra Secrets or ConfigMaps.
