From 26b353f03449acef4d72a1f6d5da9141e469ae87 Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 3 Sep 2025 21:27:45 -0400 Subject: [PATCH 1/7] fix: open search pw hash bug --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 55a49398..3131f7c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,7 @@ RUN echo '#!/bin/bash' > /usr/share/opensearch/setup-security.sh && \ echo 'until curl -s -k -u admin:${OPENSEARCH_INITIAL_ADMIN_PASSWORD} https://localhost:9200; do sleep 1; done' >> /usr/share/opensearch/setup-security.sh && \ echo 'echo "Generating admin hash from OPENSEARCH_INITIAL_ADMIN_PASSWORD..."' >> /usr/share/opensearch/setup-security.sh && \ echo 'if [ -z "${OPENSEARCH_INITIAL_ADMIN_PASSWORD}" ]; then echo "[ERROR] OPENSEARCH_INITIAL_ADMIN_PASSWORD not set"; exit 1; fi' >> /usr/share/opensearch/setup-security.sh && \ - echo 'HASH=$(/usr/share/opensearch/plugins/opensearch-security/tools/hash.sh -p "${OPENSEARCH_INITIAL_ADMIN_PASSWORD}" | sed -n '\''s/^hash: //p'\'')' >> /usr/share/opensearch/setup-security.sh && \ + echo 'HASH=$(/usr/share/opensearch/plugins/opensearch-security/tools/hash.sh -p "${OPENSEARCH_INITIAL_ADMIN_PASSWORD}")' >> /usr/share/opensearch/setup-security.sh && \ echo 'if [ -z "$HASH" ]; then echo "[ERROR] Failed to generate admin hash"; exit 1; fi' >> /usr/share/opensearch/setup-security.sh && \ echo 'sed -i "s|^ hash: \".*\"| hash: \"$HASH\"|" /usr/share/opensearch/securityconfig/internal_users.yml' >> /usr/share/opensearch/setup-security.sh && \ echo 'echo "Updated internal_users.yml with runtime-generated admin hash"' >> /usr/share/opensearch/setup-security.sh && \ From fabfc6f407bbbc4677f4cabdb3e21d82a8b73def Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 3 Sep 2025 21:59:28 -0400 Subject: [PATCH 2/7] speed up ci --- .github/workflows/build-multiarch.yml | 199 ++++++++++++++------------ 1 file changed, 105 insertions(+), 94 deletions(-) diff --git a/.github/workflows/build-multiarch.yml b/.github/workflows/build-multiarch.yml index 7b0c0264..360d29d8 100644 --- a/.github/workflows/build-multiarch.yml +++ b/.github/workflows/build-multiarch.yml @@ -6,115 +6,126 @@ on: jobs: build: strategy: + fail-fast: false matrix: include: - - platform: linux/amd64 + # backend + - image: backend + file: ./Dockerfile.backend + tag: phact/openrag-backend + platform: linux/amd64 + arch: amd64 runs-on: ubuntu-latest - arch-suffix: amd64 - - platform: linux/arm64 - runs-on: self-hosted - arch-suffix: arm64 - + - image: backend + file: ./Dockerfile.backend + tag: phact/openrag-backend + platform: linux/arm64 + arch: arm64 + runs-on: self-hosted + + # frontend + - image: frontend + file: ./Dockerfile.frontend + tag: phact/openrag-frontend + platform: linux/amd64 + arch: amd64 + runs-on: ubuntu-latest + - image: frontend + file: ./Dockerfile.frontend + tag: phact/openrag-frontend + platform: linux/arm64 + arch: arm64 + runs-on: self-hosted + + # opensearch + - image: opensearch + file: ./Dockerfile + tag: phact/openrag-opensearch + platform: linux/amd64 + arch: amd64 + runs-on: ubuntu-latest + - image: opensearch + file: ./Dockerfile + tag: phact/openrag-opensearch + platform: linux/arm64 + arch: arm64 + runs-on: self-hosted + runs-on: ${{ matrix.runs-on }} - + steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4 - - name: Extract version from pyproject.toml - id: version - run: | - VERSION=$(grep '^version = ' pyproject.toml | cut -d '"' -f 2) - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Version: $VERSION" + - name: Extract version from pyproject.toml + id: version + run: | + VERSION=$(grep '^version = ' pyproject.toml | cut -d '"' -f 2) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Version: $VERSION" - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub - if: github.event_name != 'pull_request' - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build and push backend - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile.backend - platforms: ${{ matrix.platform }} - push: ${{ github.event_name != 'pull_request' }} - tags: phact/openrag-backend:${{ steps.version.outputs.version }}-${{ matrix.arch-suffix }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Build and push frontend - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile.frontend - platforms: ${{ matrix.platform }} - push: ${{ github.event_name != 'pull_request' }} - tags: phact/openrag-frontend:${{ steps.version.outputs.version }}-${{ matrix.arch-suffix }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Build and push OpenSearch - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: ${{ matrix.platform }} - push: ${{ github.event_name != 'pull_request' }} - tags: phact/openrag-opensearch:${{ steps.version.outputs.version }}-${{ matrix.arch-suffix }} - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Build and push ${{ matrix.image }} (${{ matrix.arch }}) + uses: docker/build-push-action@v5 + with: + context: . + file: ${{ matrix.file }} + platforms: ${{ matrix.platform }} + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ matrix.tag }}:${{ steps.version.outputs.version }}-${{ matrix.arch }} + cache-from: type=gha,scope=${{ matrix.image }}-${{ matrix.arch }} + cache-to: type=gha,mode=max,scope=${{ matrix.image }}-${{ matrix.arch }} manifest: needs: build runs-on: ubuntu-latest if: github.event_name != 'pull_request' steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4 - - name: Extract version from pyproject.toml - id: version - run: | - VERSION=$(grep '^version = ' pyproject.toml | cut -d '"' -f 2) - echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Extract version from pyproject.toml + id: version + run: | + VERSION=$(grep '^version = ' pyproject.toml | cut -d '"' -f 2) + echo "version=$VERSION" >> $GITHUB_OUTPUT - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} - - name: Create and push multi-arch manifests - run: | - VERSION=${{ steps.version.outputs.version }} - - # Backend manifest - docker buildx imagetools create -t phact/openrag-backend:$VERSION \ - phact/openrag-backend:$VERSION-amd64 \ - phact/openrag-backend:$VERSION-arm64 - docker buildx imagetools create -t phact/openrag-backend:latest \ - phact/openrag-backend:$VERSION-amd64 \ - phact/openrag-backend:$VERSION-arm64 - - # Frontend manifest - docker buildx imagetools create -t phact/openrag-frontend:$VERSION \ - phact/openrag-frontend:$VERSION-amd64 \ - phact/openrag-frontend:$VERSION-arm64 - docker buildx imagetools create -t phact/openrag-frontend:latest \ - phact/openrag-frontend:$VERSION-amd64 \ - phact/openrag-frontend:$VERSION-arm64 - - # OpenSearch manifest - docker buildx imagetools create -t phact/openrag-opensearch:$VERSION \ - phact/openrag-opensearch:$VERSION-amd64 \ - phact/openrag-opensearch:$VERSION-arm64 - docker buildx imagetools create -t phact/openrag-opensearch:latest \ - phact/openrag-opensearch:$VERSION-amd64 \ - phact/openrag-opensearch:$VERSION-arm64 + - name: Create and push multi-arch manifests + run: | + VERSION=${{ steps.version.outputs.version }} + + docker buildx imagetools create -t phact/openrag-backend:$VERSION \ + phact/openrag-backend:$VERSION-amd64 \ + phact/openrag-backend:$VERSION-arm64 + docker buildx imagetools create -t phact/openrag-backend:latest \ + phact/openrag-backend:$VERSION-amd64 \ + phact/openrag-backend:$VERSION-arm64 + + docker buildx imagetools create -t phact/openrag-frontend:$VERSION \ + phact/openrag-frontend:$VERSION-amd64 \ + phact/openrag-frontend:$VERSION-arm64 + docker buildx imagetools create -t phact/openrag-frontend:latest \ + phact/openrag-frontend:$VERSION-amd64 \ + phact/openrag-frontend:$VERSION-arm64 + + docker buildx imagetools create -t phact/openrag-opensearch:$VERSION \ + phact/openrag-opensearch:$VERSION-amd64 \ + phact/openrag-opensearch:$VERSION-arm64 + docker buildx imagetools create -t phact/openrag-opensearch:latest \ + phact/openrag-opensearch:$VERSION-amd64 \ + phact/openrag-opensearch:$VERSION-arm64 From 0f2502bed61a7487f4996381a07216df9551d591 Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 3 Sep 2025 22:09:01 -0400 Subject: [PATCH 3/7] chown --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 3131f7c3..de4c86f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,7 @@ RUN echo y | opensearch-plugin install repository-s3 # Copy OIDC and DLS security configuration COPY securityconfig/ /usr/share/opensearch/securityconfig/ +RUN chown -R opensearch:opensearch /usr/share/opensearch/securityconfig/ # Create a script to apply security configuration after OpenSearch starts RUN echo '#!/bin/bash' > /usr/share/opensearch/setup-security.sh && \ From 6a642902d6a28f901a1222c732254f2e48491246 Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 3 Sep 2025 22:22:20 -0400 Subject: [PATCH 4/7] chown --- Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index de4c86f6..039053d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,10 @@ RUN echo "asprof \$@" >> /usr/share/opensearch/profile.sh RUN chmod 777 /usr/share/opensearch/profile.sh +# Copy OIDC and DLS security configuration (as root) +COPY securityconfig/ /usr/share/opensearch/securityconfig/ +RUN chown -R opensearch:opensearch /usr/share/opensearch/securityconfig/ + USER opensearch RUN opensearch-plugin remove opensearch-neural-search @@ -46,10 +50,6 @@ RUN echo y | opensearch-plugin install repository-gcs RUN echo y | opensearch-plugin install repository-azure RUN echo y | opensearch-plugin install repository-s3 -# Copy OIDC and DLS security configuration -COPY securityconfig/ /usr/share/opensearch/securityconfig/ -RUN chown -R opensearch:opensearch /usr/share/opensearch/securityconfig/ - # Create a script to apply security configuration after OpenSearch starts RUN echo '#!/bin/bash' > /usr/share/opensearch/setup-security.sh && \ echo 'echo "Waiting for OpenSearch to start..."' >> /usr/share/opensearch/setup-security.sh && \ From 53b7fb27d23b84deb7f58864b13b7da392d3e96d Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 3 Sep 2025 22:29:00 -0400 Subject: [PATCH 5/7] images list --- src/tui/screens/monitor.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/tui/screens/monitor.py b/src/tui/screens/monitor.py index 9ceb9931..071bbcce 100644 --- a/src/tui/screens/monitor.py +++ b/src/tui/screens/monitor.py @@ -166,10 +166,22 @@ class MonitorScreen(Screen): return services = await self.container_manager.get_service_status(force_refresh=True) - # Fetch image info independent of service state - project_images = await self.container_manager.get_project_images_info() - digest_map = {img: dig for img, dig in project_images} - images = [img for img, _ in project_images] + # Collect images actually reported by running/stopped containers so names match runtime + images_set = set() + for svc in services.values(): + img = (svc.image or "").strip() + if img and img != "N/A": + images_set.add(img) + # Ensure compose-declared images are also shown (e.g., langflow when stopped) + try: + for img in self.container_manager._parse_compose_images(): # best-effort, no YAML dep + if img: + images_set.add(img) + except Exception: + pass + images = list(images_set) + # Lookup digests/IDs for these image names + digest_map = await self.container_manager.get_images_digests(images) # Clear existing rows self.services_table.clear() @@ -188,13 +200,9 @@ class MonitorScreen(Screen): service_info.image or "N/A", digest_map.get(service_info.image or "", "-") ) - # Populate images table (unique images) + # Populate images table (unique images as reported by runtime) if self.images_table: - seen=set() - for image in images: - if not image or image in seen: - continue - seen.add(image) + for image in sorted(images): self.images_table.add_row(image, digest_map.get(image, "-")) # Update controls based on overall state self._update_controls(list(services.values())) From 650d4c1e59e1fe0f978d46c19d890c05cb6a896a Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 3 Sep 2025 22:52:17 -0400 Subject: [PATCH 6/7] langflow env vars --- .env.example | 7 +++++++ docker-compose-cpu.yml | 2 ++ docker-compose.yml | 2 ++ 3 files changed, 11 insertions(+) diff --git a/.env.example b/.env.example index 63a8865a..dbce2bf8 100644 --- a/.env.example +++ b/.env.example @@ -19,3 +19,10 @@ AWS_SECRET_ACCESS_KEY= # OPTIONAL url for openrag link to langflow in the UI LANGFLOW_PUBLIC_URL= + +# Langflow auth +LANGFLOW_AUTO_LOGIN=False +LANGFLOW_SUPERUSER= +LANGFLOW_SUPERUSER_PASSWORD= +LANGFLOW_NEW_USER_IS_ACTIVE=False +LANGFLOW_ENABLE_SUPERUSER_CLI=False diff --git a/docker-compose-cpu.yml b/docker-compose-cpu.yml index 359c93cf..6f27042a 100644 --- a/docker-compose-cpu.yml +++ b/docker-compose-cpu.yml @@ -101,3 +101,5 @@ services: - LANGFLOW_AUTO_LOGIN=${LANGFLOW_AUTO_LOGIN} - LANGFLOW_SUPERUSER=${LANGFLOW_SUPERUSER} - LANGFLOW_SUPERUSER_PASSWORD=${LANGFLOW_SUPERUSER_PASSWORD} + - LANGFLOW_NEW_USER_IS_ACTIVE=${LANGFLOW_NEW_USER_IS_ACTIVE} + - LANGFLOW_ENABLE_SUPERUSER_CLI=${LANGFLOW_ENABLE_SUPERUSER_CLI} diff --git a/docker-compose.yml b/docker-compose.yml index 85586b6b..8e2fdee2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -102,3 +102,5 @@ services: - LANGFLOW_AUTO_LOGIN=${LANGFLOW_AUTO_LOGIN} - LANGFLOW_SUPERUSER=${LANGFLOW_SUPERUSER} - LANGFLOW_SUPERUSER_PASSWORD=${LANGFLOW_SUPERUSER_PASSWORD} + - LANGFLOW_NEW_USER_IS_ACTIVE=${LANGFLOW_NEW_USER_IS_ACTIVE} + - LANGFLOW_ENABLE_SUPERUSER_CLI=${LANGFLOW_ENABLE_SUPERUSER_CLI} From cad271350bbfd19ef1da64658879c875812b7d48 Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 3 Sep 2025 23:03:05 -0400 Subject: [PATCH 7/7] langflow auth env vars in tui --- src/tui/managers/env_manager.py | 15 +++++++++++ src/tui/screens/config.py | 45 +++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/tui/managers/env_manager.py b/src/tui/managers/env_manager.py index 90648849..61ec2f07 100644 --- a/src/tui/managers/env_manager.py +++ b/src/tui/managers/env_manager.py @@ -40,6 +40,11 @@ class EnvConfig: aws_secret_access_key: str = "" langflow_public_url: str = "" + # Langflow auth settings + langflow_auto_login: str = "False" + langflow_new_user_is_active: str = "False" + langflow_enable_superuser_cli: str = "False" + # Document paths (comma-separated) openrag_documents_paths: str = "./documents" @@ -98,6 +103,9 @@ class EnvManager: 'AWS_SECRET_ACCESS_KEY': 'aws_secret_access_key', 'LANGFLOW_PUBLIC_URL': 'langflow_public_url', 'OPENRAG_DOCUMENTS_PATHS': 'openrag_documents_paths', + 'LANGFLOW_AUTO_LOGIN': 'langflow_auto_login', + 'LANGFLOW_NEW_USER_IS_ACTIVE': 'langflow_new_user_is_active', + 'LANGFLOW_ENABLE_SUPERUSER_CLI': 'langflow_enable_superuser_cli', } if key in attr_map: @@ -193,6 +201,13 @@ class EnvManager: f.write(f"OPENRAG_DOCUMENTS_PATHS={self.config.openrag_documents_paths}\n") f.write("\n") + # Langflow auth settings + f.write("# Langflow auth settings\n") + f.write(f"LANGFLOW_AUTO_LOGIN={self.config.langflow_auto_login}\n") + f.write(f"LANGFLOW_NEW_USER_IS_ACTIVE={self.config.langflow_new_user_is_active}\n") + f.write(f"LANGFLOW_ENABLE_SUPERUSER_CLI={self.config.langflow_enable_superuser_cli}\n") + f.write("\n") + # OAuth settings if self.config.google_oauth_client_id or self.config.google_oauth_client_secret: f.write("# Google OAuth settings\n") diff --git a/src/tui/screens/config.py b/src/tui/screens/config.py index d31b02dc..1a0fb683 100644 --- a/src/tui/screens/config.py +++ b/src/tui/screens/config.py @@ -282,6 +282,47 @@ class ConfigScreen(Screen): self.inputs["openrag_documents_paths"] = input_widget yield Static(" ") + # Langflow Auth Settings + yield Static("Langflow Auth Settings", classes="tab-header") + yield Static(" ") + + # Langflow Auto Login + yield Label("Langflow Auto Login") + current_value = getattr(self.env_manager.config, "langflow_auto_login", "False") + input_widget = Input( + placeholder="False", + value=current_value, + id="input-langflow_auto_login" + ) + yield input_widget + self.inputs["langflow_auto_login"] = input_widget + yield Static(" ") + + # Langflow New User Is Active + yield Label("Langflow New User Is Active") + current_value = getattr(self.env_manager.config, "langflow_new_user_is_active", "False") + input_widget = Input( + placeholder="False", + value=current_value, + id="input-langflow_new_user_is_active" + ) + yield input_widget + self.inputs["langflow_new_user_is_active"] = input_widget + yield Static(" ") + + # Langflow Enable Superuser CLI + yield Label("Langflow Enable Superuser CLI") + current_value = getattr(self.env_manager.config, "langflow_enable_superuser_cli", "False") + input_widget = Input( + placeholder="False", + value=current_value, + id="input-langflow_enable_superuser_cli" + ) + yield input_widget + self.inputs["langflow_enable_superuser_cli"] = input_widget + yield Static(" ") + yield Static(" ") + # Langflow Secret Key removed from UI; generated automatically on save # Add optional fields only in full mode @@ -379,7 +420,7 @@ class ConfigScreen(Screen): new_value = getattr(self.env_manager.config, field_name) input_widget.value = new_value - self.notify("Generated secure passwords", severity="info") + self.notify("Generated secure passwords", severity="information") def action_save(self) -> None: """Save the configuration.""" @@ -398,7 +439,7 @@ class ConfigScreen(Screen): # Save to file if self.env_manager.save_env_file(): - self.notify("Configuration saved successfully!", severity="success") + self.notify("Configuration saved successfully!", severity="information") # Switch to monitor screen from .monitor import MonitorScreen self.app.push_screen(MonitorScreen())