diff --git a/DSL/Liquibase/changelog/rag-search-script-v5-prompt-config.sql b/DSL/Liquibase/changelog/rag-search-script-v5-prompt-config.sql new file mode 100644 index 0000000..8d29f94 --- /dev/null +++ b/DSL/Liquibase/changelog/rag-search-script-v5-prompt-config.sql @@ -0,0 +1,8 @@ +-- liquibase formatted sql + +-- changeset Erangi Ariyasena:rag-script-v5-changeset1 +CREATE TABLE public.prompt_configuration ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + prompt TEXT +); + diff --git a/DSL/Liquibase/master.yml b/DSL/Liquibase/master.yml index a1c31eb..a17a248 100644 --- a/DSL/Liquibase/master.yml +++ b/DSL/Liquibase/master.yml @@ -6,4 +6,6 @@ databaseChangeLog: - include: file: changelog/rag-search-script-v3-configuration.sql - include: - file: changelog/rag-search-script-v4-authority-data.xml \ No newline at end of file + file: changelog/rag-search-script-v4-authority-data.xml + - include: + file: changelog/rag-search-script-v5-prompt-config.sql \ No newline at end of file diff --git a/DSL/Resql/rag-search/GET/get-prompt-configuration.sql b/DSL/Resql/rag-search/GET/get-prompt-configuration.sql new file mode 100644 index 0000000..36b3f36 --- /dev/null +++ b/DSL/Resql/rag-search/GET/get-prompt-configuration.sql @@ -0,0 +1,5 @@ +SELECT + id, + prompt +FROM prompt_configuration +LIMIT 1 diff --git a/DSL/Resql/rag-search/POST/insert-prompt-configuration.sql b/DSL/Resql/rag-search/POST/insert-prompt-configuration.sql new file mode 100644 index 0000000..d60bd70 --- /dev/null +++ b/DSL/Resql/rag-search/POST/insert-prompt-configuration.sql @@ -0,0 +1,3 @@ +INSERT INTO prompt_configuration (prompt) +VALUES (:prompt) +RETURNING id, prompt diff --git a/DSL/Resql/rag-search/POST/update-prompt-configuration.sql b/DSL/Resql/rag-search/POST/update-prompt-configuration.sql new file mode 100644 index 0000000..1d3fb20 --- /dev/null +++ b/DSL/Resql/rag-search/POST/update-prompt-configuration.sql @@ -0,0 +1,4 @@ +UPDATE prompt_configuration +SET prompt = :prompt +WHERE id = :id +RETURNING id, prompt diff --git a/DSL/Ruuter.private/rag-search/GET/prompt-configuration/get.yml b/DSL/Ruuter.private/rag-search/GET/prompt-configuration/get.yml new file mode 100644 index 0000000..69e513c --- /dev/null +++ b/DSL/Ruuter.private/rag-search/GET/prompt-configuration/get.yml @@ -0,0 +1,39 @@ +declaration: + call: declare + version: 0.1 + description: "Get prompt configuration" + method: get + accepts: json + returns: json + namespace: rag-search + +get_prompt_configuration: + call: http.get + args: + url: "[#RAG_SEARCH_RESQL]/get-prompt-configuration" + result: prompt_result + next: check_prompt_exists + +check_prompt_exists: + switch: + - condition: "${prompt_result.response.body.length > 0}" + next: transform_response + next: transform_empty_response + +transform_response: + assign: + data: ${prompt_result.response.body} + next: return_success + +transform_empty_response: + assign: + emptyData: [] + next: return_empty + +return_success: + return: ${data} + next: end + +return_empty: + return: ${emptyData} + next: end diff --git a/DSL/Ruuter.private/rag-search/POST/prompt-configuration/save.yml b/DSL/Ruuter.private/rag-search/POST/prompt-configuration/save.yml new file mode 100644 index 0000000..ad90875 --- /dev/null +++ b/DSL/Ruuter.private/rag-search/POST/prompt-configuration/save.yml @@ -0,0 +1,58 @@ +declaration: + call: declare + version: 0.1 + description: "Update or insert prompt configuration" + method: post + accepts: json + returns: json + namespace: rag-search + allowlist: + body: + - field: prompt + type: string + description: "Prompt text to save" + +extract_request_data: + assign: + prompt: ${incoming.body.prompt ?? ""} + next: get_existing_prompt + +get_existing_prompt: + call: http.get + args: + url: "[#RAG_SEARCH_RESQL]/get-prompt-configuration" + result: existing_prompt + next: check_if_exists + +check_if_exists: + switch: + - condition: "${existing_prompt.response.body.length > 0}" + next: update_prompt + next: insert_prompt + +update_prompt: + call: http.post + args: + url: "[#RAG_SEARCH_RESQL]/update-prompt-configuration" + body: + id: ${existing_prompt.response.body[0].id} + prompt: ${prompt} + result: update_result + next: return_update_success + +insert_prompt: + call: http.post + args: + url: "[#RAG_SEARCH_RESQL]/insert-prompt-configuration" + body: + prompt: ${prompt} + result: insert_result + next: return_insert_success + +return_update_success: + return: ${update_result.response.body[0]} + next: end + +return_insert_success: + return: ${insert_result.response.body[0]} + next: end diff --git a/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml b/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml index 3fa2f46..b6a533d 100644 --- a/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml +++ b/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml @@ -5,7 +5,7 @@ declaration: method: post accepts: json returns: json - namespace: classifier + namespace: rag-search allowlist: body: - field: connectionId diff --git a/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml b/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml index 7cf146f..f0a7220 100644 --- a/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml +++ b/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml @@ -5,7 +5,7 @@ declaration: method: post accepts: json returns: json - namespace: classifier + namespace: rag-search allowlist: body: - field: connectionId diff --git a/GUI/src/App.tsx b/GUI/src/App.tsx index ceb8d83..5839b18 100644 --- a/GUI/src/App.tsx +++ b/GUI/src/App.tsx @@ -13,6 +13,7 @@ import ViewLLMConnection from 'pages/LLMConnections/ViewLLMConnection'; import UserManagement from 'pages/UserManagement'; import TestLLM from 'pages/TestModel'; import TestProductionLLM from 'pages/TestProductionLLM'; +import PromptConfigurations from 'pages/PromptConfigurations'; const App: FC = () => { const navigate = useNavigate(); @@ -62,6 +63,7 @@ const App: FC = () => { } /> } /> } /> + } /> } /> } /> diff --git a/GUI/src/components/MainNavigation/index.tsx b/GUI/src/components/MainNavigation/index.tsx index 90dccb4..070c4b9 100644 --- a/GUI/src/components/MainNavigation/index.tsx +++ b/GUI/src/components/MainNavigation/index.tsx @@ -25,9 +25,19 @@ const MainNavigation: FC = () => { }, { id: 'llmConnections', - label: t('menu.llmConnections'), - path: '/llm-connections', + label: t('menu.llmConnections._self'), + path: '', icon: , + children: [ + { + label: t('menu.llmConnections.overview'), + path: '/llm-connections', + }, + { + label: t('menu.llmConnections.promptConfigurations'), + path: '/prompt-configurations', + } + ], }, { id: 'testLLM', @@ -37,7 +47,7 @@ const MainNavigation: FC = () => { }, { id: 'testProductionLLM', - label: 'Test Production LLM', + label: t('menu.testProductionLLM'), path: '/test-production-llm', icon: } diff --git a/GUI/src/pages/PromptConfigurations/PromptConfigurations.scss b/GUI/src/pages/PromptConfigurations/PromptConfigurations.scss new file mode 100644 index 0000000..edbeba1 --- /dev/null +++ b/GUI/src/pages/PromptConfigurations/PromptConfigurations.scss @@ -0,0 +1,39 @@ +.prompt-configurations { + padding: 2rem; + + .container { + max-width: 1200px; + margin: 0 auto; + } + + .title-container { + margin-bottom: 2rem; + + .title { + font-size: 2rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: #1a1a1a; + } + + .subtitle { + font-size: 1rem; + color: #666; + margin: 0; + } + } + + .prompt-form { + background: #fff; + border-radius: 8px; + padding: 2rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + .form-actions { + margin-top: 1.5rem; + display: flex; + justify-content: flex-end; + gap: 1rem; + } + } +} diff --git a/GUI/src/pages/PromptConfigurations/index.tsx b/GUI/src/pages/PromptConfigurations/index.tsx new file mode 100644 index 0000000..0c7b511 --- /dev/null +++ b/GUI/src/pages/PromptConfigurations/index.tsx @@ -0,0 +1,100 @@ +import { FC, useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { Button, FormTextarea } from 'components'; +import { ButtonAppearanceTypes, ToastTypes } from 'enums/commonEnums'; +import CircularSpinner from 'components/molecules/CircularSpinner/CircularSpinner'; +import { getPromptConfiguration, savePromptConfiguration } from 'services/promptConfiguration'; +import { promptConfigurationQueryKeys } from 'utils/queryKeys'; +import { useToast } from 'hooks/useToast'; +import './PromptConfigurations.scss'; + +const PromptConfigurations: FC = () => { + const { t } = useTranslation(); + const toast = useToast(); + const queryClient = useQueryClient(); + const [promptText, setPromptText] = useState(''); + const [isUpdating, setIsUpdating] = useState(false); + + // Fetch prompt configuration + const { data: promptConfig, isLoading } = useQuery({ + queryKey: promptConfigurationQueryKeys.current(), + queryFn: getPromptConfiguration, + }); + + + // Update promptText when data is loaded + useEffect(() => { + if (promptConfig && promptConfig.length > 0) { + setPromptText(promptConfig[0].prompt || ''); + setIsUpdating(true); + } + }, [promptConfig]); + + // Save prompt mutation + const saveMutation = useMutation({ + mutationFn: savePromptConfiguration, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: promptConfigurationQueryKeys.current() }); + toast.open({ + type: ToastTypes.SUCCESS, + title: t('toast.success.title'), + message: t('promptConfigurations.submitSuccess'), + }); + }, + onError: (error: any) => { + console.error('Error saving prompt:', error); + toast.open({ + type: ToastTypes.ERROR, + title: t('toast.error.title'), + message: t('promptConfigurations.submitError'), + }); + }, + }); + + const handleSubmit = () => { + if (!promptText.trim()) { + return; + } + saveMutation.mutate(promptText); + }; + + if (isLoading) { + return ; + } + + return ( +
+
+
+
{t('promptConfigurations.title')}
+
+ +
+ setPromptText(e.target.value)} + minRows={10} + /> + +
+ +
+
+
+
+ ); +}; + +export default PromptConfigurations; diff --git a/GUI/src/services/promptConfiguration.ts b/GUI/src/services/promptConfiguration.ts new file mode 100644 index 0000000..09dc6d4 --- /dev/null +++ b/GUI/src/services/promptConfiguration.ts @@ -0,0 +1,23 @@ +import apiDev from './api-dev'; +import { promptConfigurationEndpoints } from 'utils/endpoints'; + +export interface PromptConfiguration { + id: number | null; + prompt: string; +} + +export interface PromptConfigurationResponse { + response: PromptConfiguration[]; +} + +export const getPromptConfiguration = async (): Promise => { + const { data } = await apiDev.get(promptConfigurationEndpoints.GET_PROMPT_CONFIGURATION()); + return data?.response || []; +}; + +export const savePromptConfiguration = async (prompt: string): Promise => { + const { data } = await apiDev.post(promptConfigurationEndpoints.SAVE_PROMPT_CONFIGURATION(), { + prompt, + }); + return data?.response; +}; diff --git a/GUI/src/utils/endpoints.ts b/GUI/src/utils/endpoints.ts index a6b203d..386db29 100644 --- a/GUI/src/utils/endpoints.ts +++ b/GUI/src/utils/endpoints.ts @@ -34,3 +34,8 @@ export const vaultEndpoints = { CREATE_VAULT_SECRET: (): string => `/rag-search/vault/secret/create`, DELETE_VAULT_SECRET: (): string => `/rag-search/vault/secret/delete`, } + +export const promptConfigurationEndpoints = { + GET_PROMPT_CONFIGURATION: (): string => `/rag-search/prompt-configuration/get`, + SAVE_PROMPT_CONFIGURATION: (): string => `/rag-search/prompt-configuration/save`, +} diff --git a/GUI/src/utils/queryKeys.ts b/GUI/src/utils/queryKeys.ts index e10462e..ebc3c1a 100644 --- a/GUI/src/utils/queryKeys.ts +++ b/GUI/src/utils/queryKeys.ts @@ -38,3 +38,8 @@ export const inferenceQueryKeys = { results: () => [...inferenceQueryKeys.all(), 'results'] as const, result: (request: InferenceRequest) => [...inferenceQueryKeys.results(), request] as const, }; + +export const promptConfigurationQueryKeys = { + all: () => ['prompt-configuration'] as const, + current: () => [...promptConfigurationQueryKeys.all(), 'current'] as const, +}; diff --git a/GUI/translations/en/common.json b/GUI/translations/en/common.json index 0341108..abba391 100644 --- a/GUI/translations/en/common.json +++ b/GUI/translations/en/common.json @@ -62,7 +62,12 @@ "menu": { "userManagement": "User management", "testLLM": "Test LLM", - "llmConnections": "LLM connections" + "testProductionLLM": "Test Production LLM", + "llmConnections": { + "_self": "LLM connections", + "overview": "Overview", + "promptConfigurations": "Prompt Configurations" + } }, "userManagement": { "title": "User management", @@ -399,5 +404,17 @@ "aws": "AWS Bedrock", "azure": "Azure OpenAI" } + }, + "promptConfigurations": { + "title": "Prompt Configurations", + "subtitle": "Configure and manage your prompt templates", + "promptLabel": "Prompt Template", + "promptPlaceholder": "Enter your prompt template here...", + "submitButton": "Save", + "updateButton": "Update", + "saving": "Saving...", + "updating": "Updating...", + "submitSuccess": "Prompt configuration saved successfully", + "submitError": "Failed to save prompt configuration. Please try again." } } \ No newline at end of file diff --git a/GUI/translations/et/common.json b/GUI/translations/et/common.json index bd2d550..2cf5b3c 100644 --- a/GUI/translations/et/common.json +++ b/GUI/translations/et/common.json @@ -62,7 +62,12 @@ "menu": { "userManagement": "Kasutajate haldus", "testLLM": "Testi mudelit", - "llmConnections": "Mudelite ühendused" + "testProductionLLM": "Testi toodangu mudelit", + "llmConnections": { + "_self": "Mudelite ühendused", + "overview": "Ülevaade", + "promptConfigurations": "Viiba Seaded" + } }, "userManagement": { "title": "Kasutajate haldus", @@ -399,5 +404,17 @@ "aws": "AWS Bedrock", "azure": "Azure OpenAI" } + }, + "promptConfigurations": { + "title": "Viiba Seaded", + "subtitle": "Seadista ja halda oma viiba malle", + "promptLabel": "Viiba Mall", + "promptPlaceholder": "Sisesta siia oma viiba mall...", + "submitButton": "Salvesta", + "updateButton": "Uuenda", + "saving": "Salvestan...", + "updating": "Uuendan...", + "submitSuccess": "Viiba seadistus salvestati edukalt", + "submitError": "Viiba seadistuse salvestamine ebaõnnestus. Palun proovi uuesti." } } \ No newline at end of file diff --git a/README.md b/README.md index 9e7dd82..ad5edce 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The **BYK-RAG Module** is part of the Burokratt ecosystem, designed to provide * - Models searchable via dropdown with cache-enabled indicators. - **Enhanced Security with RSA Encryption** - - LLM credentials encrypted with RSA-2048 asymmetric encryption before storage. + - LLM credentials encrypted with RSA-2048 asymmetric encryption before storage. - GUI encrypts using public key; CronManager decrypts with private key. - Additional security layer beyond HashiCorp Vault's encryption. diff --git a/docker-compose-ec2.yml b/docker-compose-ec2.yml index 130a348..26c1906 100644 --- a/docker-compose-ec2.yml +++ b/docker-compose-ec2.yml @@ -128,7 +128,7 @@ services: - REACT_APP_RUUTER_API_URL=https://est-rag-rtc.rootcode.software/ruuter-public - REACT_APP_RUUTER_PRIVATE_API_URL=https://est-rag-rtc.rootcode.software/ruuter-private - REACT_APP_CUSTOMER_SERVICE_LOGIN=https://est-rag-rtc.rootcode.software/authentication-layer/et/dev-auth - - REACT_APP_CSP=upgrade-insecure-requests; default-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; object-src 'none'; connect-src 'self' http://localhost:8086 http://localhost:8088 http://localhost:3004 http://localhost:3005 ws://localhost https://est-rag-rtc.rootcode.software; + - REACT_APP_CSP=upgrade-insecure-requests; default-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; object-src 'none'; connect-src 'self' http://localhost:8086 http://localhost:8088 http://localhost:3004 http://localhost:3005 ws://localhost https://vault-agent-gui:8202 https://est-rag-rtc.rootcode.software; - DEBUG_ENABLED=true - CHOKIDAR_USEPOLLING=true - PORT=3001 @@ -174,25 +174,25 @@ services: cron-manager: container_name: cron-manager image: cron-manager-python:latest - user: "root" + user: root volumes: - ./DSL/CronManager/DSL:/DSL - ./DSL/CronManager/script:/app/scripts - ./src/vector_indexer:/app/src/vector_indexer + - ./src/utils/decrypt_vault_secrets.py:/app/src/utils/decrypt_vault_secrets.py:ro # Decryption utility (read-only) - cron_data:/app/data - shared-volume:/app/shared # Access to shared resources for cross-container coordination - ./datasets:/app/datasets # Direct access to datasets folder for diff identifier operations - ./grafana-configs/loki_logger.py:/app/src/vector_indexer/loki_logger.py - ./.env:/app/.env:ro - - vault-agent-token:/agent/out:ro # Mount vault token for accessing vault secrets environment: - server.port=9010 - PYTHONPATH=/app:/app/src/vector_indexer - - VAULT_ADDR=http://vault:8200 + - VAULT_AGENT_URL=http://vault-agent-cron:8203 ports: - 9010:8080 depends_on: - - vault-agent-llm + - vault-agent-cron networks: - bykstack @@ -496,10 +496,8 @@ services: - vault-data:/vault/file - ./vault/config:/vault/config:ro - ./vault/logs:/vault/logs - expose: - - "8200" networks: - - bykstack + - vault-network # Only on vault-network for security restart: unless-stopped healthcheck: test: ["CMD", "sh", "-c", "wget -q -O- http://127.0.0.1:8200/v1/sys/health || exit 0"] @@ -520,14 +518,74 @@ services: volumes: - vault-data:/vault/data - vault-agent-creds:/agent/credentials - - vault-agent-token:/agent/out + - vault-agent-gui-token:/agent/gui-token + - vault-agent-cron-token:/agent/cron-token + - vault-agent-llm-token:/agent/llm-token - ./vault-init.sh:/vault-init.sh:ro networks: - - bykstack + - vault-network # Access vault + - bykstack # Access to write agent tokens entrypoint: ["/bin/sh"] - command: ["-c", "apk add --no-cache curl jq && chmod -R 755 /agent/credentials && chmod -R 770 /agent/out && chown -R vault:vault /agent/credentials /agent/out && su vault -s /bin/sh /vault-init.sh"] + command: + - -c + - | + apk add --no-cache curl jq uuidgen openssl + # Create and set permissions for all agent directories + mkdir -p /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out + chown -R vault:vault /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out + chmod 755 /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out + # Run vault initialization as vault user + su vault -s /bin/sh /vault-init.sh restart: "no" + vault-agent-gui: + image: hashicorp/vault:1.20.3 + container_name: vault-agent-gui + command: ["vault", "agent", "-config=/agent/config/gui-agent.hcl", "-log-level=info"] + depends_on: + vault-init: + condition: service_completed_successfully + cap_add: + - IPC_LOCK + volumes: + - ./vault/agents/gui/gui-agent.hcl:/agent/config/gui-agent.hcl:ro + - vault-agent-creds:/agent/credentials:ro + - vault-agent-gui-token:/agent/gui-token + networks: + - vault-network # Access vault + - bykstack # Accessible by GUI service + restart: unless-stopped + healthcheck: + test: ["CMD", "sh", "-c", "test -f /agent/gui-token/token && test -s /agent/gui-token/token"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s + + vault-agent-cron: + image: hashicorp/vault:1.20.3 + container_name: vault-agent-cron + command: ["vault", "agent", "-config=/agent/config/cron-agent.hcl", "-log-level=info"] + depends_on: + vault-init: + condition: service_completed_successfully + cap_add: + - IPC_LOCK + volumes: + - ./vault/agents/cron/cron-agent.hcl:/agent/config/cron-agent.hcl:ro + - vault-agent-creds:/agent/credentials:ro + - vault-agent-cron-token:/agent/cron-token + networks: + - vault-network # Access vault + - bykstack # Accessible by CronManager service + restart: unless-stopped + healthcheck: + test: ["CMD", "sh", "-c", "test -f /agent/cron-token/token && test -s /agent/cron-token/token"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s + vault-agent-llm: image: hashicorp/vault:1.20.3 container_name: vault-agent-llm @@ -540,10 +598,17 @@ services: volumes: - ./vault/agents/llm/agent.hcl:/agent/config/agent.hcl:ro - vault-agent-creds:/agent/credentials:ro - - vault-agent-token:/agent/out + - vault-agent-llm-token:/agent/llm-token networks: - - bykstack + - vault-network # Access vault + - bykstack # Accessible by LLM service restart: unless-stopped + healthcheck: + test: ["CMD", "sh", "-c", "test -f /agent/llm-token/token && test -s /agent/llm-token/token"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s # LLM Orchestration Service llm-orchestration-service: @@ -558,24 +623,22 @@ services: - .env environment: - ENVIRONMENT=production - - VAULT_ADDR=http://vault:8200 - - VAULT_TOKEN=/agent/out/token + - VAULT_ADDR=http://vault-agent-llm:8201 + # VAULT_TOKEN not set - vault-agent-llm proxy handles authentication volumes: - ./src/llm_config_module/config:/app/src/llm_config_module/config:ro - ./src/optimization/optimized_modules:/app/src/optimization/optimized_modules - llm_orchestration_logs:/app/logs - - vault-agent-token:/agent/out:ro networks: - bykstack depends_on: - - vault - vault-agent-llm - # healthcheck: - # test: ["CMD", "curl", "-f", "http://llm-orchestration-service:8100/health"] - # interval: 30s - # timeout: 10s - # start_period: 40s - # retries: 3 + healthcheck: + test: ["CMD", "curl", "-f", "http://llm-orchestration-service:8100/health"] + interval: 30s + timeout: 10s + start_period: 40s + retries: 3 volumes: loki-data: @@ -602,8 +665,12 @@ volumes: name: cron_data vault-agent-creds: name: vault-agent-creds - vault-agent-token: - name: vault-agent-token + vault-agent-gui-token: + name: vault-agent-gui-token + vault-agent-cron-token: + name: vault-agent-cron-token + vault-agent-llm-token: + name: vault-agent-llm-token opensearch-data: name: opensearch-data @@ -611,3 +678,7 @@ networks: bykstack: name: bykstack driver: bridge + vault-network: + name: vault-network + driver: bridge + internal: true # No external access - isolated network \ No newline at end of file