diff --git a/README.md b/README.md
index a8d30bbd..04350c37 100644
--- a/README.md
+++ b/README.md
@@ -28,4 +28,4 @@ Please refer to the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) file in this reposi
## Licensing
-Copyright 2024 SAP SE or an SAP affiliate company and openMFP contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/openmfp/portal-server-lib).
+Copyright 2024 SAP SE or an SAP affiliate company and openMFP contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/openmfp/portal).
diff --git a/docs/ACD.md b/docs/ACD.md
new file mode 100644
index 00000000..9f743203
--- /dev/null
+++ b/docs/ACD.md
@@ -0,0 +1,994 @@
+# Architecture & Code Documentation (ACD)
+
+## Component Structure and Key Modules
+
+### Frontend Structure
+
+```
+frontend/
+├── src/
+│ ├── app/
+│ │ └── app.routes.ts # Route configuration (empty - handled by Luigi)
+│ ├── environments/
+│ │ ├── environment.ts # Development environment
+│ │ └── environment.prod.ts # Production environment
+│ ├── main.ts # Application bootstrap
+│ ├── index.html # HTML entry point
+│ └── styles.scss # Global styles
+├── proxy.config.json # Development proxy configuration
+├── angular.json # Angular CLI configuration
+└── package.json # Dependencies and scripts
+```
+
+#### Key Frontend Files
+
+**main.ts**
+```typescript
+import { bootstrapApplication } from '@angular/platform-browser';
+import {
+ PortalComponent,
+ PortalOptions,
+ providePortal,
+} from '@openmfp/portal-ui-lib';
+
+const portalOptions: PortalOptions = {};
+
+bootstrapApplication(PortalComponent, {
+ providers: [providePortal(portalOptions)],
+}).catch((err) => console.error(err));
+```
+
+The frontend is intentionally minimal. All portal functionality is provided by the `@openmfp/portal-ui-lib` external library, including:
+- Luigi Framework integration
+- Portal component rendering
+- Navigation management
+- Authentication flows
+
+**app.routes.ts**
+```typescript
+import { Routes } from '@angular/router';
+
+export const routes: Routes = [];
+```
+
+Routes are defined as an empty array because Luigi Framework handles all routing through its configuration system, not Angular Router.
+
+**proxy.config.json**
+```json
+{
+ "/rest/**": {
+ "target": "http://localhost:3000",
+ "secure": false,
+ "logLevel": "debug",
+ "changeOrigin": true
+ }
+}
+```
+
+Development proxy forwards all `/rest/**` requests from the Angular dev server (port 4300) to the NestJS backend (port 3000).
+
+### Backend Structure
+
+```
+backend/
+├── src/
+│ ├── app.module.ts # Root application module
+│ ├── main.ts # NestJS bootstrap
+│ ├── entity-context-provider/
+│ │ └── account-entity-context-provider.service.ts
+│ └── service-providers/
+│ ├── kubernetes-service-providers.service.ts
+│ ├── localServiceProviders.ts
+│ ├── portal-context-provider.ts
+│ └── provider-jsons/
+│ └── service-providers.ts
+├── test/
+│ ├── app.e2e-spec.ts # End-to-end tests
+│ └── jest-e2e.json # E2E test configuration
+└── package.json # Dependencies and scripts
+```
+
+#### Key Backend Modules
+
+**main.ts**
+```typescript
+import { NestFactory } from '@nestjs/core';
+import { AppModule } from './app.module';
+
+async function bootstrap() {
+ const app = await NestFactory.create(AppModule);
+ await app.listen(process.env.PORT || 3000);
+}
+bootstrap();
+```
+
+Standard NestJS bootstrap that creates the application and listens on port 3000 (or PORT environment variable).
+
+**app.module.ts**
+```typescript
+import { Module } from '@nestjs/common';
+import { PortalModule, PortalModuleOptions } from '@openmfp/portal-server-lib';
+import { join } from 'path';
+import { AccountEntityContextProvider } from './entity-context-provider/account-entity-context-provider.service';
+import { KubernetesServiceProvidersService } from './service-providers/kubernetes-service-providers.service';
+import { OpenmfpPortalProvider } from './service-providers/portal-context-provider';
+import { config } from 'dotenv';
+
+config({ path: './.env' });
+
+const portalOptions: PortalModuleOptions = {
+ frontendDistSources: join(__dirname, '../..', 'frontend/dist/frontend'),
+ entityContextProviders: {
+ account: AccountEntityContextProvider,
+ },
+ additionalProviders: [AccountEntityContextProvider],
+ serviceProviderService: KubernetesServiceProvidersService,
+ portalContextProvider: OpenmfpPortalProvider,
+};
+
+@Module({
+ imports: [PortalModule.create(portalOptions)],
+})
+export class AppModule {}
+```
+
+**Configuration breakdown:**
+- **frontendDistSources**: Path to built Angular application for static file serving
+- **entityContextProviders**: Maps entity types to context providers (e.g., "account" → AccountEntityContextProvider)
+- **additionalProviders**: Services to inject into NestJS dependency injection
+- **serviceProviderService**: Implementation that fetches service provider configurations
+- **portalContextProvider**: Provides global portal context (like API URLs)
+
+## State Management Patterns
+
+### Frontend State Management
+
+The portal frontend does not implement custom state management. All state is managed by:
+
+1. **Luigi Framework**: Maintains navigation state, current route, and micro-frontend lifecycle
+2. **@openmfp/portal-ui-lib**: Manages portal configuration and user context
+3. **RxJS**: Observable streams for asynchronous operations
+
+```mermaid
+graph LR
+ subgraph "State Management"
+ Luigi[Luigi Framework
Navigation State]
+ PortalLib[Portal UI Lib
Configuration State]
+ RxJS[RxJS Observables
Async State]
+ end
+
+ subgraph "Data Flow"
+ Backend[Backend API]
+ MicroFE[Micro-frontends]
+ end
+
+ Luigi --> MicroFE
+ PortalLib --> Luigi
+ Backend -->|API Responses| RxJS
+ RxJS --> PortalLib
+
+ style Luigi fill:#e3f2fd
+ style PortalLib fill:#f3e5f5
+ style Backend fill:#fff3e0
+```
+
+### Backend State Management
+
+The backend is stateless and does not maintain session state. Each request:
+1. Receives authentication token
+2. Queries Kubernetes for current ContentConfiguration CRDs
+3. Returns fresh data
+4. No caching implemented (consider adding for production)
+
+**Service Provider State Flow:**
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant API
+ participant K8s
+
+ Client->>API: Request service providers
+ API->>K8s: Query ContentConfiguration CRDs
+ K8s-->>API: Return latest CRDs
+ API->>API: Parse and transform
+ API-->>Client: Return providers
+ Note over API: No state retained
+```
+
+## API Integration
+
+### REST API Endpoints
+
+The portal backend exposes endpoints through the `@openmfp/portal-server-lib`:
+
+**Base URL**: `/rest`
+
+**Key endpoints provided by PortalModule:**
+- `GET /rest/service-providers` - Fetch dynamic navigation and content configuration
+- `GET /rest/entity-context` - Get entity-specific context and policies
+- `GET /rest/portal-context` - Get global portal configuration
+
+### Service Provider API
+
+**KubernetesServiceProvidersService Implementation:**
+
+```typescript
+export class KubernetesServiceProvidersService implements ServiceProviderService {
+ private k8sApi: CustomObjectsApi;
+
+ constructor() {
+ const kc = new k8s.KubeConfig();
+ kc.loadFromDefault();
+ this.k8sApi = kc.makeApiClient(k8s.CustomObjectsApi);
+ }
+
+ async getServiceProviders(
+ token: string,
+ entities: string[],
+ context: Record
+ ): Promise {
+ try {
+ const response = await this.k8sApi.listNamespacedCustomObject(
+ 'core.openmfp.io', // API Group
+ 'v1alpha1', // Version
+ 'openmfp-system', // Namespace
+ 'contentconfigurations' // Resource plural
+ );
+
+ if (!response.body['items']) {
+ return { serviceProviders: [] };
+ }
+
+ const responseItems = response.body['items'] as any[];
+
+ let contentConfigurations = responseItems
+ .filter((item) => !!item.status.configurationResult)
+ .map((item) =>
+ JSON.parse(item.status.configurationResult) as ContentConfiguration
+ );
+
+ return {
+ serviceProviders: [
+ {
+ contentConfiguration: contentConfigurations,
+ } as RawServiceProvider,
+ ],
+ };
+ } catch (error) {
+ console.error(error);
+ }
+ }
+}
+```
+
+**API Flow:**
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant PortalModule
+ participant K8sService
+ participant K8sAPI
+
+ Client->>PortalModule: GET /rest/service-providers
+ PortalModule->>K8sService: getServiceProviders(token, entities, context)
+ K8sService->>K8sAPI: listNamespacedCustomObject()
+ Note over K8sAPI: core.openmfp.io/v1alpha1
openmfp-system/contentconfigurations
+ K8sAPI-->>K8sService: Response { items: [...] }
+ K8sService->>K8sService: Filter items with status.configurationResult
+ K8sService->>K8sService: Parse JSON from configurationResult
+ K8sService-->>PortalModule: ServiceProviderResponse
+ PortalModule-->>Client: JSON response
+```
+
+### Entity Context API
+
+**AccountEntityContextProvider Implementation:**
+
+```typescript
+@Injectable()
+export class AccountEntityContextProvider implements EntityContextProvider {
+ async getContextValues(
+ token: string,
+ context?: Record
+ ): Promise> {
+ return {
+ id: context.account,
+ policies: [
+ 'create',
+ 'delete',
+ 'get',
+ 'list',
+ 'update',
+ 'watch',
+ 'gardener_project_create',
+ 'gardener_project_list',
+ 'gardener_shoot_create',
+ 'gardener_shoot_list',
+ ],
+ };
+ }
+}
+```
+
+**Returns:**
+- **id**: The account identifier from the request context
+- **policies**: Array of permission strings that control UI visibility and actions
+
+### Portal Context API
+
+**OpenmfpPortalProvider Implementation:**
+
+```typescript
+@Injectable()
+export class OpenmfpPortalProvider implements PortalContextProvider {
+ constructor() {}
+
+ getContextValues(): Promise> {
+ const context: Record = {
+ crdGatewayApiUrl: process.env.CRD_GATEWAY_API_URL,
+ };
+ return Promise.resolve(context);
+ }
+}
+```
+
+**Returns:**
+- **crdGatewayApiUrl**: API endpoint for CRD Gateway service (from environment variable)
+
+## Routing and Navigation
+
+### Luigi Framework Navigation
+
+All routing is handled by Luigi Framework through dynamic configuration loaded from Kubernetes CRDs.
+
+**Navigation Structure:**
+
+```mermaid
+graph TB
+ Root[Luigi Shell]
+ Home[Home /home]
+ Overview[Overview /home/overview]
+ Gardener[Gardener Dashboard /gardener]
+ DemoPages[Demo Pages Category]
+ Empty[Empty Page /empty]
+ Table[Table /table]
+ Tree[Tree /tree]
+
+ Root --> Home
+ Home --> Overview
+ Root --> Gardener
+ Root --> DemoPages
+ DemoPages --> Empty
+ DemoPages --> Table
+ DemoPages --> Tree
+
+ style Root fill:#e1f5ff
+ style Home fill:#fff3e0
+ style Gardener fill:#e8f5e9
+ style DemoPages fill:#f3e5f5
+```
+
+### Luigi Configuration Structure
+
+**Node Configuration Example:**
+
+```typescript
+{
+ entityType: 'global', // Entity scope
+ pathSegment: 'home', // URL segment
+ label: 'Overview', // Display name
+ icon: 'home', // SAP icon name
+ hideFromNav: true, // Hide from side navigation
+ defineEntity: {
+ id: 'example' // Define entity context
+ },
+ viewUrl: '/home', // Relative URL
+ url: 'https://...', // Absolute URL (for external micro-frontends)
+ context: { // Data passed to micro-frontend
+ title: 'Welcome',
+ content: '...'
+ },
+ children: [...] // Nested routes
+}
+```
+
+### Navigation Types
+
+**Internal Routes (viewUrl):**
+```typescript
+{
+ pathSegment: 'overview',
+ viewUrl: '/overview',
+ label: 'Overview'
+}
+```
+Served by the portal's own frontend application.
+
+**External Micro-frontends (url):**
+```typescript
+{
+ pathSegment: 'demo',
+ url: 'https://fiddle.luigi-project.io/examples/...',
+ label: 'Demo Page'
+}
+```
+Loaded in iframe from external source.
+
+**Virtual Trees (virtualTree):**
+```typescript
+{
+ pathSegment: 'gardener',
+ virtualTree: true,
+ url: 'https://gardener-dashboard.example.com',
+ label: 'Gardener Dashboard'
+}
+```
+Embeds entire external application with its own routing.
+
+### Entity-based Navigation
+
+Navigation nodes can be scoped to entity types:
+
+```typescript
+{
+ entityType: 'account', // Only visible in account context
+ pathSegment: 'resources',
+ label: 'Resources'
+}
+```
+
+The entity context is resolved by backend providers and passed to Luigi, which filters navigation based on current entity.
+
+## Configuration Options
+
+### Environment Variables
+
+**Backend Configuration (.env):**
+
+```bash
+# Server Configuration
+PORT=3000 # Backend server port
+
+# Kubernetes Configuration
+KUBECONFIG=/path/to/kubeconfig # Kubernetes config file (optional, defaults to ~/.kube/config)
+
+# OpenMFP Configuration
+CRD_GATEWAY_API_URL=https://... # CRD Gateway API endpoint
+```
+
+**Frontend Configuration:**
+
+Frontend configuration is minimal. The `environment.ts` and `environment.prod.ts` files only define the `production` flag:
+
+```typescript
+export const environment = {
+ production: false // or true for production
+};
+```
+
+All runtime configuration comes from the backend via API endpoints.
+
+### Portal Module Options
+
+**PortalModuleOptions Interface:**
+
+```typescript
+interface PortalModuleOptions {
+ // Path to built frontend assets
+ frontendDistSources: string;
+
+ // Map of entity types to context providers
+ entityContextProviders: {
+ [entityType: string]: Type;
+ };
+
+ // Additional services to inject
+ additionalProviders: Provider[];
+
+ // Service that fetches dynamic content
+ serviceProviderService: Type;
+
+ // Provider for global portal context
+ portalContextProvider: Type;
+}
+```
+
+**Example Configuration:**
+
+```typescript
+const portalOptions: PortalModuleOptions = {
+ frontendDistSources: join(__dirname, '../..', 'frontend/dist/frontend'),
+ entityContextProviders: {
+ account: AccountEntityContextProvider,
+ // Add more entity types as needed
+ },
+ additionalProviders: [AccountEntityContextProvider],
+ serviceProviderService: KubernetesServiceProvidersService,
+ portalContextProvider: OpenmfpPortalProvider,
+};
+```
+
+### ContentConfiguration CRD Format
+
+**CRD Structure:**
+
+```yaml
+apiVersion: core.openmfp.io/v1alpha1
+kind: ContentConfiguration
+metadata:
+ name: my-content
+ namespace: openmfp-system
+ creationTimestamp: "2022-05-17T11:37:17Z"
+status:
+ configurationResult: |
+ {
+ "name": "my-content",
+ "creationTimestamp": "2022-05-17T11:37:17Z",
+ "luigiConfigFragment": {
+ "data": {
+ "nodes": [...],
+ "nodeDefaults": {...}
+ }
+ }
+ }
+```
+
+**Luigi Config Fragment Fields:**
+
+- **nodes**: Array of navigation node configurations
+- **nodeDefaults**: Default properties applied to all nodes
+ - `entityType`: Default entity scope
+ - `loadingIndicator`: Loading UI configuration
+ - `icon`: Default icon
+
+### Luigi Node Configuration
+
+**Complete Node Example:**
+
+```typescript
+{
+ // Navigation
+ pathSegment: 'my-feature', // URL segment
+ label: 'My Feature', // Display name
+ icon: 'add', // SAP icon
+ hideFromNav: false, // Show in navigation
+
+ // Categorization
+ category: {
+ label: 'Feature Category',
+ icon: 'group',
+ collapsible: true
+ },
+
+ // Entity Context
+ entityType: 'account', // Entity scope
+ defineEntity: {
+ id: 'entity-id'
+ },
+
+ // Content Loading
+ viewUrl: '/internal-path', // Internal route
+ url: 'https://external.com', // External micro-frontend
+ virtualTree: true, // Full app embedding
+
+ // UI Behavior
+ loadingIndicator: {
+ enabled: true
+ },
+
+ // Data Passing
+ context: {
+ customData: 'value'
+ },
+
+ // Nested Navigation
+ children: [...]
+}
+```
+
+## Development Workflow
+
+### Local Development Setup
+
+**Prerequisites:**
+- Node.js 22+
+- npm 9+
+- Access to Kubernetes cluster with ContentConfiguration CRDs
+- Valid kubeconfig
+
+**Setup Steps:**
+
+```bash
+# Install dependencies
+npm run prepare # Installs both frontend and backend
+
+# Start development servers (runs concurrently)
+npm start # Starts both frontend:4300 and backend:3000
+
+# Or start individually
+npm run start:ui # Frontend only
+npm run start:server # Backend only
+```
+
+### Development Architecture
+
+```mermaid
+graph LR
+ subgraph "Developer Machine"
+ Browser[Browser
localhost:4300]
+ AngularCLI[Angular CLI
Dev Server]
+ NestJS[NestJS
Debug Mode]
+ end
+
+ subgraph "External"
+ K8s[Kubernetes Cluster]
+ MicroFE[External Micro-frontends]
+ end
+
+ Browser -->|HTTP| AngularCLI
+ Browser -->|Embed| MicroFE
+ AngularCLI -->|Proxy /rest/**| NestJS
+ NestJS --> K8s
+
+ style Browser fill:#e1f5ff
+ style AngularCLI fill:#fff3e0
+ style NestJS fill:#e8f5e9
+```
+
+### Build and Test
+
+**Build Commands:**
+
+```bash
+# Build both frontend and backend
+npm run build
+
+# Build individually
+npm run build:ui # Frontend production build
+npm run build:server # Backend compilation
+```
+
+**Test Commands:**
+
+```bash
+# Run all tests
+npm test
+
+# Run with coverage
+npm run test:cov
+
+# Test individually
+npm run test:ui # Frontend tests
+npm run test:server # Backend tests
+```
+
+**Lint Commands:**
+
+```bash
+# Lint all code
+npm run lint
+
+# Fix linting issues
+npm run lint:fix
+```
+
+### Docker Build
+
+**Local Docker Build:**
+
+```bash
+# Create secret file with GitHub token
+mkdir -p .secret
+echo -n $OPENMFP_GITHUB_TOKEN > .secret/gh-token
+
+# Build with secrets
+docker build --secret id=NODE_AUTH_TOKEN,src=.secret/gh-token .
+
+# Cleanup secret on failure
+docker build --secret id=NODE_AUTH_TOKEN,src=.secret/gh-token . || rm .secret/gh-token
+```
+
+**Build Stages:**
+
+```mermaid
+graph TB
+ subgraph "Stage 1: Build"
+ S1[node:22.12]
+ Copy1[Copy package files]
+ Install[npm ci]
+ Build[npm run build]
+ end
+
+ subgraph "Stage 2: Runtime"
+ S2[node:22.12-alpine]
+ Copy2[Copy dist files]
+ Cmd[CMD node dist/main]
+ end
+
+ S1 --> Copy1
+ Copy1 --> Install
+ Install --> Build
+ Build -->|Copy artifacts| Copy2
+ S2 --> Copy2
+ Copy2 --> Cmd
+
+ style Build fill:#fff3e0
+ style S2 fill:#e8f5e9
+```
+
+### Debugging
+
+**Frontend Debugging:**
+- Use Angular DevTools browser extension
+- Chrome DevTools for debugging
+- Source maps enabled in development
+
+**Backend Debugging:**
+```bash
+npm run start:debug # Starts with --debug flag
+```
+Attach debugger to `localhost:9229`
+
+**VS Code Launch Configuration:**
+
+```json
+{
+ "type": "node",
+ "request": "attach",
+ "name": "Attach to Backend",
+ "port": 9229,
+ "restart": true,
+ "skipFiles": ["/**"]
+}
+```
+
+## Testing Strategy
+
+### Frontend Tests
+
+**Test Framework:** Jest with Angular testing utilities
+
+**Test File Pattern:** `*.spec.ts`
+
+**Run Tests:**
+```bash
+cd frontend
+npm test # Run tests
+npm run test:cov # Run with coverage
+```
+
+### Backend Tests
+
+**Test Framework:** Jest with NestJS testing utilities
+
+**Unit Tests:** `src/**/*.spec.ts`
+**E2E Tests:** `test/**/*.e2e-spec.ts`
+
+**Run Tests:**
+```bash
+cd backend
+npm test # Unit tests
+npm run test:e2e # E2E tests
+npm run test:cov # Coverage
+```
+
+### Test Coverage
+
+Both frontend and backend track test coverage in `./coverage` directories.
+
+## Common Tasks
+
+### Adding a New Entity Context Provider
+
+1. Create provider service:
+```typescript
+import { Injectable } from '@nestjs/common';
+import { EntityContextProvider } from '@openmfp/portal-server-lib';
+
+@Injectable()
+export class MyEntityContextProvider implements EntityContextProvider {
+ async getContextValues(
+ token: string,
+ context?: Record
+ ): Promise> {
+ return {
+ id: context.myEntity,
+ policies: ['read', 'write']
+ };
+ }
+}
+```
+
+2. Register in `app.module.ts`:
+```typescript
+const portalOptions: PortalModuleOptions = {
+ // ...
+ entityContextProviders: {
+ account: AccountEntityContextProvider,
+ myEntity: MyEntityContextProvider, // Add here
+ },
+ additionalProviders: [
+ AccountEntityContextProvider,
+ MyEntityContextProvider, // Add here
+ ],
+ // ...
+};
+```
+
+### Adding New ContentConfiguration
+
+1. Create CRD in Kubernetes:
+```bash
+kubectl apply -f - <
+ ): Promise {
+ // Custom logic - fetch from database, external API, etc.
+ return {
+ serviceProviders: [...]
+ };
+ }
+}
+```
+
+Update `app.module.ts`:
+```typescript
+const portalOptions: PortalModuleOptions = {
+ // ...
+ serviceProviderService: CustomServiceProvidersService,
+};
+```
+
+## Troubleshooting
+
+### Frontend Not Loading
+
+**Check:**
+1. Backend is running on port 3000
+2. Proxy configuration is correct
+3. No CORS errors in browser console
+
+### Backend Cannot Connect to Kubernetes
+
+**Check:**
+1. `KUBECONFIG` environment variable is set correctly
+2. Kubeconfig file exists and has valid credentials
+3. `openmfp-system` namespace exists
+4. User/service account has permission to list ContentConfiguration CRDs
+
+### No Navigation Items Appearing
+
+**Check:**
+1. ContentConfiguration CRDs exist in `openmfp-system`
+2. CRDs have `status.configurationResult` populated
+3. JSON in `configurationResult` is valid
+4. Backend logs for errors fetching CRDs
+
+### Micro-frontend Not Loading
+
+**Check:**
+1. External URL is accessible from browser
+2. External service allows iframe embedding (X-Frame-Options)
+3. CORS headers are set correctly on external service
+4. Luigi node configuration has correct `url` field
+
+## Performance Optimization
+
+### Backend Optimization
+
+**Caching ContentConfiguration:**
+```typescript
+@Injectable()
+export class CachedServiceProvidersService implements ServiceProviderService {
+ private cache: ServiceProviderResponse | null = null;
+ private cacheTime = 60000; // 1 minute
+ private lastFetch = 0;
+
+ async getServiceProviders(
+ token: string,
+ entities: string[],
+ context: Record
+ ): Promise {
+ const now = Date.now();
+ if (this.cache && (now - this.lastFetch) < this.cacheTime) {
+ return this.cache;
+ }
+
+ // Fetch fresh data
+ const result = await this.fetchFromKubernetes();
+ this.cache = result;
+ this.lastFetch = now;
+ return result;
+ }
+}
+```
+
+**Watch for CRD Changes:**
+```typescript
+// Use Kubernetes watch API to invalidate cache
+const watch = new k8s.Watch(kc);
+watch.watch(
+ '/apis/core.openmfp.io/v1alpha1/namespaces/openmfp-system/contentconfigurations',
+ {},
+ (type, obj) => {
+ if (type === 'MODIFIED' || type === 'ADDED' || type === 'DELETED') {
+ this.cache = null; // Invalidate cache
+ }
+ }
+);
+```
+
+### Frontend Optimization
+
+- Luigi handles lazy loading automatically
+- Micro-frontends load only when navigated to
+- Use `loadingIndicator` to show loading state
+
+## Security Best Practices
+
+### Environment Variables
+
+- Never commit `.env` files with secrets
+- Use Kubernetes secrets for production deployments
+- Rotate credentials regularly
+
+### API Security
+
+- Always validate tokens on backend
+- Implement rate limiting for production
+- Use HTTPS in production
+- Set appropriate CORS policies
+
+### Kubernetes RBAC
+
+Ensure service account has minimal required permissions:
+
+```yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: portal-backend
+ namespace: openmfp-system
+rules:
+ - apiGroups: ["core.openmfp.io"]
+ resources: ["contentconfigurations"]
+ verbs: ["get", "list", "watch"]
+```
+
+## External Library Documentation
+
+- **NestJS**: https://docs.nestjs.com
+- **Angular**: https://angular.io/docs
+- **Luigi Framework**: https://docs.luigi-project.io
+- **Kubernetes Client**: https://github.com/kubernetes-client/javascript
+- **@openmfp/portal-ui-lib**: Internal OpenMFP library
+- **@openmfp/portal-server-lib**: Internal OpenMFP library
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
new file mode 100644
index 00000000..c1bfd8ac
--- /dev/null
+++ b/docs/ARCHITECTURE.md
@@ -0,0 +1,544 @@
+# Architecture
+
+## Overview
+
+The OpenMFP Portal is a web-based management interface for the OpenMFP platform. It provides a unified interface for accessing and managing various OpenMFP resources, services, and integrations through a micro-frontend architecture powered by Luigi Framework.
+
+### Purpose
+
+The portal serves as the central entry point for users to:
+- Access and manage OpenMFP resources
+- Navigate between different micro-frontends and services
+- Interact with Kubernetes custom resources through a user-friendly interface
+- Integrate with external services like Gardener Dashboard
+
+### Technology Stack
+
+**Frontend:**
+- Angular 18 with standalone components
+- Luigi Framework for micro-frontend orchestration
+- @openmfp/portal-ui-lib for UI components
+- TypeScript 5.5
+
+**Backend:**
+- NestJS 10 framework
+- @openmfp/portal-server-lib for portal services
+- Kubernetes client-node for CRD interaction
+- Node.js 22+
+
+## High-Level Architecture
+
+```mermaid
+graph TB
+ subgraph "Client Browser"
+ UI[Angular Frontend
Luigi Shell]
+ end
+
+ subgraph "Portal Backend"
+ API[NestJS API Server
Port 3000]
+ SPService[Service Provider Service]
+ EntityContext[Entity Context Provider]
+ PortalContext[Portal Context Provider]
+ end
+
+ subgraph "Kubernetes Cluster"
+ CRD[ContentConfiguration CRDs
openmfp-system namespace]
+ K8sAPI[Kubernetes API]
+ end
+
+ subgraph "External Services"
+ Gardener[Gardener Dashboard]
+ MicroFE[Micro-frontends]
+ end
+
+ UI -->|HTTP Requests /rest/**| API
+ API --> SPService
+ API --> EntityContext
+ API --> PortalContext
+ SPService -->|List ContentConfigurations| K8sAPI
+ K8sAPI --> CRD
+ UI -->|Embed iframes| MicroFE
+ UI -->|Embed virtualTree| Gardener
+
+ style UI fill:#e1f5ff
+ style API fill:#fff4e1
+ style CRD fill:#e8f5e9
+ style Gardener fill:#f3e5f5
+```
+
+## Component Architecture
+
+### Frontend Architecture
+
+```mermaid
+graph LR
+ subgraph "Frontend Application"
+ Main[main.ts
Bootstrap]
+ Portal[PortalComponent
from portal-ui-lib]
+ Luigi[Luigi Framework
Micro-frontend Shell]
+ Config[Portal Configuration]
+ end
+
+ subgraph "External Libraries"
+ PUIL[@openmfp/portal-ui-lib]
+ LuigiCore[@luigi-project/core]
+ OAuth[@luigi-project/plugin-auth-oauth2]
+ end
+
+ Main -->|bootstrapApplication| Portal
+ Main -->|providePortal| Config
+ Portal --> Luigi
+ Portal --> PUIL
+ Luigi --> LuigiCore
+ Luigi --> OAuth
+
+ style Main fill:#e3f2fd
+ style Portal fill:#f3e5f5
+ style Luigi fill:#fff3e0
+```
+
+The frontend is minimal by design, delegating most functionality to the `@openmfp/portal-ui-lib` library. This library provides:
+- The main `PortalComponent` that serves as the Luigi shell
+- Navigation configuration
+- Micro-frontend integration
+- Authentication handling
+
+### Backend Architecture
+
+```mermaid
+graph TB
+ subgraph "NestJS Application"
+ Main[main.ts
Bootstrap]
+ AppModule[AppModule]
+ PortalModule[PortalModule
from portal-server-lib]
+
+ subgraph "Custom Providers"
+ SPService[KubernetesServiceProvidersService]
+ EntityProvider[AccountEntityContextProvider]
+ ContextProvider[OpenmfpPortalProvider]
+ end
+ end
+
+ subgraph "Kubernetes Integration"
+ K8sClient[Kubernetes Client
@kubernetes/client-node]
+ CustomAPI[CustomObjectsApi]
+ end
+
+ subgraph "Portal Server Library"
+ PSL[@openmfp/portal-server-lib]
+ Interfaces[ServiceProviderService
EntityContextProvider
PortalContextProvider]
+ end
+
+ Main --> AppModule
+ AppModule -->|imports| PortalModule
+ AppModule -->|configures| SPService
+ AppModule -->|configures| EntityProvider
+ AppModule -->|configures| ContextProvider
+
+ SPService -->|implements| Interfaces
+ EntityProvider -->|implements| Interfaces
+ ContextProvider -->|implements| Interfaces
+
+ SPService --> K8sClient
+ K8sClient --> CustomAPI
+
+ PortalModule --> PSL
+
+ style Main fill:#e8f5e9
+ style AppModule fill:#fff3e0
+ style SPService fill:#e1f5ff
+ style K8sClient fill:#f3e5f5
+```
+
+## Key Workflows
+
+### Service Provider Discovery Flow
+
+```mermaid
+sequenceDiagram
+ participant Client as Browser
+ participant Backend as NestJS API
+ participant K8s as Kubernetes API
+ participant CRD as ContentConfiguration CRDs
+
+ Client->>Backend: GET /rest/service-providers
+ Backend->>K8s: listNamespacedCustomObject()
+ Note over K8s: API Group: core.openmfp.io
Version: v1alpha1
Namespace: openmfp-system
Resource: contentconfigurations
+ K8s->>CRD: Query CRDs
+ CRD-->>K8s: Return items[]
+ K8s-->>Backend: Response with items
+ Backend->>Backend: Filter items with status.configurationResult
+ Backend->>Backend: Parse JSON configurations
+ Backend-->>Client: Return serviceProviders array
+ Client->>Client: Render navigation and micro-frontends
+```
+
+### Entity Context Resolution Flow
+
+```mermaid
+sequenceDiagram
+ participant Client as Browser
+ participant Backend as NestJS API
+ participant EntityProvider as AccountEntityContextProvider
+
+ Client->>Backend: GET /rest/entity-context?entity=account
+ Backend->>EntityProvider: getContextValues(token, context)
+ EntityProvider->>EntityProvider: Extract account from context
+ EntityProvider->>EntityProvider: Define policies array
+ Note over EntityProvider: Policies: create, delete, get,
list, update, watch,
gardener_project_create, etc.
+ EntityProvider-->>Backend: Return context values
+ Backend-->>Client: Return { id, policies }
+ Client->>Client: Apply policies to UI elements
+```
+
+### Content Configuration Loading
+
+```mermaid
+sequenceDiagram
+ participant Portal as Luigi Portal Shell
+ participant Backend as Backend API
+ participant K8s as Kubernetes
+
+ Portal->>Backend: Initialize - Request service providers
+ Backend->>K8s: Fetch ContentConfiguration CRDs
+ K8s-->>Backend: Return CRD items
+ Backend->>Backend: Extract luigiConfigFragment from each CRD
+ Note over Backend: Each CRD contains:
- name
- creationTimestamp
- luigiConfigFragment.data.nodes[]
+ Backend-->>Portal: Return content configurations
+ Portal->>Portal: Merge Luigi config fragments
+ Portal->>Portal: Build navigation tree
+ Portal->>Portal: Register micro-frontend nodes
+ Note over Portal: Nodes include:
- pathSegment
- label
- icon
- viewUrl or url
- entityType
- context
+```
+
+## Dependencies and Integrations
+
+### Frontend Dependencies
+
+**Core Framework:**
+- **@angular/core (^18.0.0)**: Primary application framework
+- **@angular/router (^18.0.0)**: Routing capabilities
+- **rxjs (~7.8.0)**: Reactive programming library
+
+**Portal Libraries:**
+- **@openmfp/portal-ui-lib (^0.82.0)**: OpenMFP portal UI components and Luigi shell
+- **@luigi-project/core (^2.18.1)**: Micro-frontend framework for navigation and composition
+- **@luigi-project/plugin-auth-oauth2 (^2.18.1)**: OAuth2 authentication plugin
+
+**Utilities:**
+- **jwt-decode (^4.0.0)**: JWT token parsing
+- **jmespath (0.16.0)**: JSON query language
+- **lodash (4.17.21)**: JavaScript utility library
+
+### Backend Dependencies
+
+**Core Framework:**
+- **@nestjs/core (^10.4.6)**: NestJS application framework
+- **@nestjs/common (^10.4.6)**: Common NestJS utilities
+- **@nestjs/platform-express (^10.4.6)**: Express platform adapter
+
+**Portal Libraries:**
+- **@openmfp/portal-server-lib (^0.93.0)**: OpenMFP portal server components and services
+
+**Kubernetes Integration:**
+- **@kubernetes/client-node (^0.22.0)**: Official Kubernetes client for Node.js
+ - Used to interact with Kubernetes API
+ - Reads ContentConfiguration custom resources
+ - Loads kubeconfig from default locations
+
+**HTTP and Utilities:**
+- **@nestjs/axios (^3.0.1)**: HTTP client module
+- **axios (^1.6.3)**: Promise-based HTTP client
+- **cookie-parser (1.4.7)**: Cookie parsing middleware
+- **dotenv (^16.4.5)**: Environment variable management
+
+### External Service Integrations
+
+**Gardener Dashboard:**
+- Integrated as a virtual tree node in Luigi navigation
+- Accessed via iframe embedding
+- URL configured in ContentConfiguration CRDs
+- Example: `https://d.ing.gardener-op.mfp-dev.shoot.canary.k8s-hana.ondemand.com`
+
+**Luigi Fiddle Examples:**
+- Demo micro-frontends for testing
+- Hosted at `https://fiddle.luigi-project.io`
+- Include table, tree, and empty page examples
+
+### Kubernetes Custom Resources
+
+**ContentConfiguration CRD:**
+- **API Group**: `core.openmfp.io`
+- **Version**: `v1alpha1`
+- **Namespace**: `openmfp-system`
+- **Purpose**: Dynamic configuration of portal content and navigation
+
+**CRD Structure:**
+```yaml
+apiVersion: core.openmfp.io/v1alpha1
+kind: ContentConfiguration
+metadata:
+ name: example-config
+ namespace: openmfp-system
+spec:
+ # Specification details
+status:
+ configurationResult: |
+ {
+ "name": "example",
+ "creationTimestamp": "2022-05-17T11:37:17Z",
+ "luigiConfigFragment": {
+ "data": {
+ "nodes": [...]
+ }
+ }
+ }
+```
+
+### Configuration Flow
+
+```mermaid
+graph LR
+ subgraph "Configuration Sources"
+ ENV[Environment Variables
.env file]
+ K8sCRD[Kubernetes CRDs
ContentConfiguration]
+ end
+
+ subgraph "Backend Configuration"
+ PortalOpts[PortalModuleOptions]
+ ContextProv[OpenmfpPortalProvider]
+ end
+
+ subgraph "Frontend Configuration"
+ PortalFEOpts[PortalOptions]
+ LuigiConfig[Luigi Configuration]
+ end
+
+ ENV -->|CRD_GATEWAY_API_URL| ContextProv
+ ContextProv --> PortalOpts
+ K8sCRD -->|Dynamic content| PortalOpts
+
+ PortalOpts -->|API response| PortalFEOpts
+ PortalFEOpts --> LuigiConfig
+
+ style ENV fill:#fff3e0
+ style K8sCRD fill:#e8f5e9
+ style PortalOpts fill:#e3f2fd
+ style LuigiConfig fill:#f3e5f5
+```
+
+## Deployment Architecture
+
+### Development Environment
+
+```mermaid
+graph TB
+ subgraph "Development Machine"
+ FE[Angular Dev Server
Port 4300]
+ BE[NestJS Dev Server
Port 3000]
+ Proxy[Proxy Config
/rest/** → :3000]
+ end
+
+ subgraph "Kubernetes Cluster"
+ K8s[Kubernetes API
via kubeconfig]
+ NS[openmfp-system namespace]
+ end
+
+ FE -->|Proxied requests| Proxy
+ Proxy --> BE
+ BE --> K8s
+ K8s --> NS
+
+ style FE fill:#e3f2fd
+ style BE fill:#fff3e0
+ style K8s fill:#e8f5e9
+```
+
+**Development Setup:**
+- Frontend runs on port 4300 with hot reload
+- Backend runs on port 3000 with debug mode
+- Proxy configuration forwards `/rest/**` from frontend to backend
+- Backend connects to Kubernetes using default kubeconfig
+
+### Production Deployment
+
+```mermaid
+graph TB
+ subgraph "Docker Container"
+ Node[Node.js Runtime
Port 3000]
+ Static[Serve Static
Frontend Assets]
+ API[NestJS API
Backend Services]
+ end
+
+ subgraph "Kubernetes Cluster"
+ Service[Portal Service]
+ Pod[Portal Pod]
+ CRD[ContentConfiguration CRDs]
+ end
+
+ Client[Client Browser] -->|HTTP| Service
+ Service --> Pod
+ Pod --> Node
+ Node --> Static
+ Node --> API
+ API --> CRD
+
+ style Client fill:#e1f5ff
+ style Node fill:#fff3e0
+ style Pod fill:#e8f5e9
+```
+
+**Production Deployment:**
+- Multi-stage Docker build
+- Frontend built to static assets in `frontend/dist`
+- Backend built to JavaScript in `backend/dist`
+- Single Node.js process serves both static frontend and API
+- Alpine-based runtime image for minimal size
+- Exposed on port 3000
+
+### Docker Build Process
+
+```mermaid
+graph LR
+ subgraph "Build Stage"
+ Source[Source Code]
+ NPM[npm install]
+ Build[npm run build]
+ end
+
+ subgraph "Runtime Stage"
+ Alpine[node:22.12-alpine]
+ BackendDist[backend/dist]
+ FrontendDist[frontend/dist]
+ end
+
+ Source --> NPM
+ NPM --> Build
+ Build --> BackendDist
+ Build --> FrontendDist
+
+ BackendDist --> Alpine
+ FrontendDist --> Alpine
+
+ style Build fill:#fff3e0
+ style Alpine fill:#e8f5e9
+```
+
+## Security Considerations
+
+### Authentication
+
+- OAuth2 authentication via Luigi plugin (`@luigi-project/plugin-auth-oauth2`)
+- JWT token handling via `jwt-decode`
+- Tokens passed to backend for service provider requests
+- Entity context resolution based on authenticated user
+
+### Authorization
+
+The `AccountEntityContextProvider` defines policies for authenticated users:
+- **Resource operations**: create, delete, get, list, update, watch
+- **Gardener operations**: gardener_project_create, gardener_project_list, gardener_shoot_create, gardener_shoot_list
+
+### Kubernetes Access
+
+- Backend uses Kubernetes client with kubeconfig authentication
+- Reads ContentConfiguration CRDs from `openmfp-system` namespace
+- Service account or user credentials loaded from default kubeconfig location
+- No direct client-to-Kubernetes communication (all proxied through backend)
+
+## Extensibility
+
+### Adding New Content
+
+New portal content can be added dynamically by creating ContentConfiguration CRDs in Kubernetes:
+
+```yaml
+apiVersion: core.openmfp.io/v1alpha1
+kind: ContentConfiguration
+metadata:
+ name: my-new-content
+ namespace: openmfp-system
+status:
+ configurationResult: |
+ {
+ "name": "my-new-content",
+ "luigiConfigFragment": {
+ "data": {
+ "nodes": [
+ {
+ "pathSegment": "my-feature",
+ "label": "My Feature",
+ "icon": "add",
+ "url": "https://my-micro-frontend.example.com",
+ "entityType": "account"
+ }
+ ]
+ }
+ }
+ }
+```
+
+### Custom Entity Context Providers
+
+Additional entity types can be supported by implementing the `EntityContextProvider` interface and registering in `AppModule`:
+
+```typescript
+export class CustomEntityContextProvider implements EntityContextProvider {
+ async getContextValues(
+ token: string,
+ context?: Record
+ ): Promise> {
+ // Custom logic
+ return {
+ id: context.entityId,
+ customData: {}
+ };
+ }
+}
+```
+
+### Service Provider Customization
+
+The `KubernetesServiceProvidersService` can be replaced or extended to fetch configurations from different sources (databases, external APIs, etc.) by implementing the `ServiceProviderService` interface.
+
+## Performance Considerations
+
+### Micro-frontend Loading
+
+- Luigi Framework handles lazy loading of micro-frontends
+- Only active routes load their corresponding micro-frontends
+- Virtual trees (like Gardener) load external applications in isolated iframes
+
+### Backend Caching
+
+- ContentConfiguration CRDs are fetched on each request
+- Consider implementing caching for production deployments
+- Watch Kubernetes events for cache invalidation
+
+### Build Optimization
+
+- Production builds use Angular's optimization
+- Tree-shaking removes unused code
+- Multi-stage Docker build separates build and runtime dependencies
+- Alpine Linux runtime image minimizes container size
+
+## Monitoring and Observability
+
+### Logging
+
+- Backend uses NestJS built-in logging
+- Errors logged to console with stack traces
+- Frontend errors logged to browser console
+
+### Health Checks
+
+- Backend exposes health endpoints via NestJS
+- Kubernetes readiness/liveness probes can be configured
+- Frontend availability depends on backend serving static assets
+
+## Related Repositories
+
+- **@openmfp/portal-ui-lib**: Core portal UI components and Luigi configuration
+- **@openmfp/portal-server-lib**: Backend portal services and interfaces
+- **luigi-project/luigi**: Micro-frontend framework
+- **kubernetes/client-node**: Official Kubernetes client for Node.js