Skip to content

Conversation

@asithade
Copy link
Contributor

Summary

Implements secure build-time secret injection for LaunchDarkly client IDs following the pattern established in PR #156. Secrets are retrieved from AWS Secrets Manager during GitHub Actions builds and injected at build time using Docker BuildKit secret mounts and Angular's define feature.

Key Changes

Infrastructure

  • Created reusable workflow .github/workflows/get-launchdarkly-secrets.yml for AWS secret retrieval
  • Updated Dockerfile with BuildKit secret mounts for secure build-time injection
  • Modified all three Docker build workflows (main, tag, PR) to use reusable secret workflow

Application

  • Implemented OpenFeature integration with LaunchDarkly provider
  • Added feature flag service and provider with SSR support
  • Configured Angular define feature for build-time constant replacement
  • Added fallback values for local development with ng serve

Documentation

  • Created comprehensive build-time secrets guide at docs/build-time-secrets.md
  • Added feature flags documentation at docs/architecture/frontend/feature-flags.md

Security Features

  • ✅ AWS Secrets Manager with OIDC authentication (no long-lived credentials)
  • ✅ BuildKit secret mounts (secrets don't persist in image layers)
  • ✅ GitHub Actions log masking
  • ✅ Build-time injection (no runtime environment variable exposure)
  • ✅ Validation steps that fail fast if secrets are missing

Local Development

  • Supports yarn start with hardcoded dev client ID fallback
  • Allows testing with yarn build --define for build-time injection
  • Dev client ID in source code is acceptable trade-off (client-side SDK, public value)

Prerequisites

Before merging, ensure AWS Secrets Manager secret exists:

  • Path: /cloudops/managed-secrets/launchdarkly/lfx_one/ld_client_id
  • Format: {"ld_client_id": "your-client-id"}
  • GitHub Actions role has read access

Test Plan

  • Build completes successfully locally
  • Linting and type checking pass
  • Verify workflow runs successfully in CI
  • Confirm LaunchDarkly client ID is injected correctly in deployed environment
  • Test feature flag functionality in browser

LFXV2-750

- Add reusable workflow for AWS Secrets Manager integration
- Update Dockerfile with BuildKit secret mounts for secure injection
- Configure Angular define feature for build-time constant replacement
- Implement OpenFeature with LaunchDarkly provider
- Add feature flag service and provider with SSR support
- Update all Docker build workflows to use reusable secret workflow
- Add comprehensive documentation for build-time secrets pattern
- Support local development with fallback values for ng serve

LFXV2-750

Signed-off-by: Asitha de Silva <[email protected]>
Copilot AI review requested due to automatic review settings November 17, 2025 22:40
@asithade asithade requested a review from jordane as a code owner November 17, 2025 22:40
@coderabbitai
Copy link

coderabbitai bot commented Nov 17, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Integrates LaunchDarkly feature flags via OpenFeature into the Angular frontend. Adds build-time secret injection from AWS Secrets Manager through GitHub Actions OIDC and Docker to the application bundle. Implements FeatureFlagService with signal-based reactive flag accessors. Feature-gates sidebar Projects item and organization selector via flags. Includes architecture and secrets management documentation.

Changes

Cohort / File(s) Summary
GitHub Actions Workflows — OIDC & LaunchDarkly Secret Injection
.github/workflows/docker-build-main.yml, docker-build-pr.yml, docker-build-tag.yml
Added top-level permissions for id-token and packages write. Replaced per-job permissions with workflow-level permissions. Introduced OIDC authentication steps to configure AWS credentials. Added steps to fetch LaunchDarkly client ID from AWS Secrets Manager and set it as an environment variable with validation. Propagate LAUNCHDARKLY_CLIENT_ID via secret-envs to Docker build step. Tag workflow now exports chart_version as job output.
Docker & Build Configuration
Dockerfile, apps/lfx-one/angular.json, apps/lfx-one/.env.example
Dockerfile now builds shared package first before Angular UI. Build step mounts LAUNCHDARKLY_CLIENT_ID secret and injects it via --define flag. Angular config adds define block with LAUNCHDARKLY_CLIENT_ID: "''" across all build configurations. .env.example documents new LD_CLIENT_ID environment variable with build-time injection guidance.
Dependencies & Environment Configuration
apps/lfx-one/package.json, apps/lfx-one/src/environments/environment.ts, environment.dev.ts, environment.prod.ts, environment.staging.ts
Added OpenFeature and LaunchDarkly client SDK dependencies (@openfeature/core, @openfeature/launchdarkly-client-provider, @openfeature/web-sdk, launchdarkly-js-client-sdk). All environment files now declare build-time constant LAUNCHDARKLY_CLIENT_ID and expose launchDarklyClientId property. Dev environment uses fallback dev client ID; prod/staging/main use build-time injected value.
Feature Flag Infrastructure
apps/lfx-one/src/app/shared/providers/feature-flag.provider.ts, shared/services/feature-flag.service.ts
New provideFeatureFlags() function initializes OpenFeature with LaunchDarkly provider at app startup. New FeatureFlagService with signal-based API: initialize(user), getBooleanFlag(), getStringFlag(), getNumberFlag(), getObjectFlag(). Reactive context signal triggers flag signal recomputation. Event handlers manage real-time provider updates with error recovery.
Application Integration
apps/lfx-one/src/app/app.config.ts, app.component.ts, layouts/main-layout/main-layout.component.ts, layouts/main-layout/main-layout.component.html, modules/dashboards/board-member/board-member-dashboard.component.ts, board-member-dashboard.component.html
Registered provideFeatureFlags() in app config. App component injects and initializes FeatureFlagService after user identification. MainLayout component uses computed sidebarItems signal that filters Projects item based on showProjectsInSidebar flag. Sidebar template changed from property binding [items]="sidebarItems" to function binding [items]="sidebarItems()". BoardMemberDashboard conditionally renders organization selector via showOrganizationSelector() flag.
Shared Types
packages/shared/src/interfaces/auth.interface.ts
Added optional preferred_username?: string property to User interface.
Documentation
docs/architecture/frontend/feature-flags.md, docs/build-time-secrets.md
New architecture guide covering OpenFeature integration, signal-based reactivity, initialization flow, error handling, configuration, best practices, and troubleshooting. New secrets management guide detailing AWS Secrets Manager storage, GitHub Actions retrieval, Docker BuildKit mounts, Angular build-time injection, local development, and security considerations.

Sequence Diagrams

sequenceDiagram
    participant GHA as GitHub Actions
    participant AWS as AWS Secrets Manager
    participant Docker as Docker Build
    participant Bundle as App Bundle
    participant Runtime as Runtime

    GHA->>GHA: OIDC Auth (configure credentials)
    GHA->>AWS: Retrieve LaunchDarkly Client ID
    AWS-->>GHA: Return secret value
    GHA->>Docker: Pass LAUNCHDARKLY_CLIENT_ID via secret-env
    Docker->>Docker: Mount secret at /run/secrets/LAUNCHDARKLY_CLIENT_ID
    Docker->>Bundle: Inject via --define LAUNCHDARKLY_CLIENT_ID
    Bundle->>Bundle: Build-time constant embedded in JS
    Runtime->>Runtime: Loaded in browser at startup
Loading
sequenceDiagram
    participant App as App Startup
    participant FeatureFlag as FeatureFlagService
    participant Provider as OpenFeature Provider
    participant LD as LaunchDarkly

    App->>FeatureFlag: initialize(user)
    FeatureFlag->>Provider: Create LaunchDarklyClientProvider
    Provider->>LD: Connect with streaming
    LD-->>Provider: Flag values + subscribe to updates
    Provider-->>FeatureFlag: Provider ready
    FeatureFlag->>FeatureFlag: Store context in signal
    FeatureFlag-->>App: Promise resolved
    
    App->>FeatureFlag: getBooleanFlag('showProjects')
    FeatureFlag->>Provider: Evaluate flag with context
    Provider-->>FeatureFlag: Return value
    FeatureFlag-->>App: Return Signal<boolean>
    
    Note over LD,FeatureFlag: On flag change, provider emits event
    LD-->>Provider: FlagChanged event
    Provider-->>FeatureFlag: Trigger event handler
    FeatureFlag->>FeatureFlag: refreshFlags() → update context signal
    FeatureFlag->>FeatureFlag: All flag Signals recompute
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

  • CI/CD & Infrastructure changes — Three GitHub Actions workflows modified with OIDC authentication, AWS Secrets Manager integration, and secret-env propagation patterns that require verification of security posture and workflow correctness.
  • Build-time secret injection — Multi-stage injection (GitHub Actions → Docker → Angular define → runtime constant) demands careful review of the injection points, fallback logic, and error handling across Docker and build configs.
  • Signal-based reactive service — FeatureFlagService combines signals, reactive context, computed flag accessors, and event-driven refreshes; requires careful reasoning about signal dependencies, memory leaks, and timing of initialization.
  • Component integrations — Multiple component modifications (main-layout, board-member-dashboard) with computed signal bindings and conditional rendering; verify correct signal dereferencing and change detection triggers.
  • Environment configuration sprawl — Build-time constants duplicated across four environment files; verify consistency and fallback logic.
  • New dependencies — OpenFeature and LaunchDarkly SDKs introduce external security and behavioral dependencies requiring validation.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: adding build-time secret injection for LaunchDarkly, which aligns with the primary objective and changes in the changeset.
Description check ✅ Passed The PR description is comprehensive and related to the changeset, covering infrastructure changes, application changes, security features, local development, and prerequisites.
Linked Issues check ✅ Passed The PR successfully implements secure build-time secret injection for LaunchDarkly, directly addressing the objective in LFXV2-750 to support build-time secrets in lfx-v2-ui.
Out of Scope Changes check ✅ Passed All changes are within scope: infrastructure updates for secret injection, OpenFeature/LaunchDarkly integration, Angular configuration, environment setup, and documentation. One minor change adds an optional field to the User interface (preferred_username), which appears to be an incidental but related change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/LFXV2-750

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c0c6f7c and 20001f9.

📒 Files selected for processing (4)
  • .github/workflows/docker-build-main.yml (3 hunks)
  • .github/workflows/docker-build-pr.yml (3 hunks)
  • .github/workflows/docker-build-tag.yml (3 hunks)
  • docs/build-time-secrets.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/build-time-secrets.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-push
🔇 Additional comments (10)
.github/workflows/docker-build-tag.yml (4)

11-14: Permissions scope is appropriate for OIDC and container registry operations.

The top-level permissions correctly include id-token: write for OIDC token generation and packages: write for GHCR operations. Both are required and scoped appropriately.


34-39: OIDC configuration is sound.

The AWS credentials configuration uses OpenID Connect (no long-lived credentials), targeting the correct role with the appropriate audience.


41-54: LaunchDarkly secret retrieval and validation are correctly implemented.

The workflow properly:

  • Retrieves the secret from AWS Secrets Manager using OIDC-authenticated credentials
  • Extracts the ld_client_id field from the JSON response using jq
  • Validates the value is present and fails the build with a clear error message if missing
  • Exports the variable to $GITHUB_ENV for use in subsequent steps

The secret-envs parameter format (LAUNCHDARKLY_CLIENT_ID=LAUNCHDARKLY_CLIENT_ID) is correct—it reads the value from the environment variable LAUNCHDARKLY_CLIENT_ID that was set in the previous step.


109-110: Secret mounting configuration is appropriate for build-time injection.

The secret is correctly mounted as a file at /run/secrets/LAUNCHDARKLY_CLIENT_ID within the build container, aligning with the Dockerfile expectations. This keeps the secret out of image layers and GitHub Actions logs.

.github/workflows/docker-build-pr.yml (3)

15-15: Added OIDC permission aligns with infrastructure pattern.

The id-token: write permission enables OIDC-based AWS credential retrieval, consistent with the other build workflows.


36-56: Secret retrieval and validation follow established workflow pattern.

The OIDC authentication, secret retrieval, and validation steps mirror the tag and main build workflows, maintaining consistency across all build triggers (release, PR preview, and main branch).


99-100: Secret mounting configuration is consistent with other workflows.

The secret-envs parameter correctly mounts the LAUNCHDARKLY_CLIENT_ID from the workflow environment into the Docker build.

.github/workflows/docker-build-main.yml (3)

14-15: Top-level permissions enable both OIDC and container registry operations.

Both id-token: write and packages: write are necessary and correctly scoped at the workflow level.


29-49: OIDC-based secret retrieval with validation is correctly implemented.

The workflow consistently implements secure build-time secret injection:

  1. Retrieves AWS credentials via OIDC (no long-lived keys)
  2. Fetches the LaunchDarkly client ID from AWS Secrets Manager
  3. Validates presence and exits with clear error messaging if missing
  4. Exports to $GITHUB_ENV for downstream steps

This pattern is now consistent across all three build workflows (release, PR preview, main branch).


91-92: Build-time secret mounting is correctly configured.

The secret-envs parameter properly mounts the client ID into the Docker build without persisting it as a build argument or environment variable in the final image.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive build-time secret injection system for LaunchDarkly client IDs, enabling secure feature flag management in the LFX One application. The implementation follows a defense-in-depth approach using AWS Secrets Manager, GitHub OIDC authentication, Docker BuildKit secret mounts, and Angular's build-time constant replacement.

Key Changes:

  • Implemented OpenFeature-based feature flag system with LaunchDarkly provider and signal-based reactivity
  • Created reusable GitHub Actions workflow for secure AWS secret retrieval with validation and masking
  • Modified Docker build process to inject secrets at build time without persisting them in image layers

Reviewed Changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
yarn.lock Added OpenFeature and LaunchDarkly SDK dependencies for feature flag support
packages/shared/src/interfaces/feature-flags.interface.ts Defined TypeScript interfaces for evaluation context and LaunchDarkly configuration
packages/shared/src/interfaces/index.ts Exported feature flag interfaces for use across packages
docs/build-time-secrets.md Comprehensive documentation for build-time secret injection architecture and workflows
docs/architecture/frontend/feature-flags.md Detailed guide on feature flag implementation, usage patterns, and best practices
apps/lfx-one/src/environments/environment*.ts Added LaunchDarkly client ID configuration with build-time constant injection
apps/lfx-one/src/app/shared/services/feature-flag.service.ts Signal-based feature flag service with OpenFeature integration and real-time updates
apps/lfx-one/src/app/shared/providers/feature-flag.provider.ts APP_INITIALIZER provider for OpenFeature/LaunchDarkly setup with SSR guards
apps/lfx-one/src/app/modules/dashboards/board-member/* Example usage: conditional organization selector rendering based on feature flag
apps/lfx-one/src/app/layouts/main-layout/* Example usage: dynamic sidebar navigation filtering using computed signals
apps/lfx-one/src/app/app.config.ts Registered feature flag provider in application configuration
apps/lfx-one/src/app/app.component.ts Initialized feature flags with authenticated user context
apps/lfx-one/package.json Added OpenFeature and LaunchDarkly client SDK dependencies
apps/lfx-one/angular.json Configured define option for build-time constant replacement across all build configurations
apps/lfx-one/.env.example Added LaunchDarkly configuration example for local development
Dockerfile Implemented BuildKit secret mount for secure build-time secret injection
.github/workflows/get-launchdarkly-secrets.yml Reusable workflow for AWS Secrets Manager retrieval with OIDC auth and validation
.github/workflows/docker-build-*.yml Updated all Docker build workflows to use secret retrieval workflow and pass secrets to builds

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
.github/workflows/get-launchdarkly-secrets.yml (1)

24-30: Consider making the IAM role ARN configurable.

The OIDC authentication is implemented securely. However, the hardcoded IAM role ARN at Line 29 may require updates if the AWS account or role changes. Consider using a GitHub environment variable or repository secret for the role ARN to improve maintainability.

Example refactor:

-          role-to-assume: arn:aws:iam::788942260905:role/github-actions-deploy
+          role-to-assume: ${{ secrets.AWS_GITHUB_ACTIONS_ROLE_ARN }}
apps/lfx-one/src/app/shared/providers/feature-flag.provider.ts (1)

28-33: Consider timeout implications.

The LaunchDarkly provider is configured correctly. The 5-second initialization timeout (Line 30) is reasonable but may cause feature flags to be unavailable on slow connections. Consider whether this timeout aligns with your UX requirements, especially for users with slower network conditions.

apps/lfx-one/src/app/layouts/main-layout/main-layout.component.ts (1)

52-62: Consider more robust item identification.

The computed signal correctly implements reactive filtering based on the feature flag. However, filtering by item.label !== 'Projects' couples the logic to the display string. If the label changes (e.g., for i18n), the filter will break.

Consider adding an id property to SidebarMenuItem for more robust identification:

-    // Filter out Projects if feature flag is disabled
-    if (!this.showProjectsInSidebar()) {
-      return items.filter((item) => item.label !== 'Projects');
-    }
+    // Filter out Projects if feature flag is disabled
+    if (!this.showProjectsInSidebar()) {
+      return items.filter((item) => item.routerLink !== '/projects');
+    }

Alternatively, use routerLink as shown above, which is less likely to change than display text.

apps/lfx-one/angular.json (1)

82-84: Consider removing redundant configuration-level defines.

Each build configuration duplicates the root-level define with identical values. Since Angular merges configuration with base options, the root-level define (lines 57-59) should suffice.

You can simplify by removing the define blocks from individual configurations and relying on the root-level definition:

           "fileReplacements": [
             {
               "replace": "src/environments/environment.ts",
               "with": "src/environments/environment.prod.ts"
             }
-          ],
-          "define": {
-            "LAUNCHDARKLY_CLIENT_ID": "''"
-          }
+          ]
         },

Apply this pattern to all configurations (staging, development, local) to reduce duplication. The Dockerfile can still override via --define flag.

Also applies to: 106-108, 120-122, 128-130

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8b2114e and d41c385.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • .github/workflows/docker-build-main.yml (2 hunks)
  • .github/workflows/docker-build-pr.yml (3 hunks)
  • .github/workflows/docker-build-tag.yml (3 hunks)
  • .github/workflows/get-launchdarkly-secrets.yml (1 hunks)
  • Dockerfile (1 hunks)
  • apps/lfx-one/.env.example (1 hunks)
  • apps/lfx-one/angular.json (4 hunks)
  • apps/lfx-one/package.json (1 hunks)
  • apps/lfx-one/src/app/app.component.ts (3 hunks)
  • apps/lfx-one/src/app/app.config.ts (2 hunks)
  • apps/lfx-one/src/app/layouts/main-layout/main-layout.component.html (2 hunks)
  • apps/lfx-one/src/app/layouts/main-layout/main-layout.component.ts (3 hunks)
  • apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts (2 hunks)
  • apps/lfx-one/src/app/shared/providers/feature-flag.provider.ts (1 hunks)
  • apps/lfx-one/src/app/shared/services/feature-flag.service.ts (1 hunks)
  • apps/lfx-one/src/environments/environment.dev.ts (2 hunks)
  • apps/lfx-one/src/environments/environment.prod.ts (2 hunks)
  • apps/lfx-one/src/environments/environment.staging.ts (2 hunks)
  • apps/lfx-one/src/environments/environment.ts (2 hunks)
  • docs/architecture/frontend/feature-flags.md (1 hunks)
  • docs/build-time-secrets.md (1 hunks)
  • packages/shared/src/interfaces/feature-flags.interface.ts (1 hunks)
  • packages/shared/src/interfaces/index.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-21T21:19:13.599Z
Learnt from: andrest50
Repo: linuxfoundation/lfx-v2-ui PR: 125
File: apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts:345-350
Timestamp: 2025-10-21T21:19:13.599Z
Learning: In the Angular meeting card component (apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts), when selecting between `summary.summary_data.edited_content` and `summary.summary_data.content`, the logical OR operator (`||`) is intentionally used instead of nullish coalescing (`??`) because empty string edited_content should fall back to the original content rather than being displayed as empty.

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html
🧬 Code graph analysis (4)
apps/lfx-one/src/app/shared/providers/feature-flag.provider.ts (4)
apps/lfx-one/src/environments/environment.dev.ts (1)
  • environment (7-20)
apps/lfx-one/src/environments/environment.prod.ts (1)
  • environment (7-20)
apps/lfx-one/src/environments/environment.staging.ts (1)
  • environment (7-20)
apps/lfx-one/src/environments/environment.ts (1)
  • environment (7-21)
apps/lfx-one/src/app/app.config.ts (1)
apps/lfx-one/src/app/shared/providers/feature-flag.provider.ts (1)
  • provideFeatureFlags (46-46)
apps/lfx-one/src/app/shared/services/feature-flag.service.ts (1)
packages/shared/src/interfaces/feature-flags.interface.ts (1)
  • EvaluationContext (8-17)
apps/lfx-one/src/app/layouts/main-layout/main-layout.component.ts (1)
packages/shared/src/interfaces/components.interface.ts (1)
  • SidebarMenuItem (267-288)
🪛 GitHub Actions: Markdown Lint
docs/build-time-secrets.md

[error] 22-22: MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]

🪛 GitHub Check: markdown-lint
docs/build-time-secrets.md

[failure] 111-111: Fenced code blocks should have a language specified [Context: ""] docs/build-time-secrets.md:111 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: ""]


[failure] 22-22: Fenced code blocks should have a language specified [Context: ""] docs/build-time-secrets.md:22 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: ""]

🪛 LanguageTool
docs/build-time-secrets.md

[uncategorized] ~36-~36: The official name of this software platform is spelled with a capital “H”.
Context: ...use a reusable GitHub Actions workflow (.github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)


[uncategorized] ~211-~211: The official name of this software platform is spelled with a capital “H”.
Context: ... Step 2: Update Reusable Workflow Edit .github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)

docs/architecture/frontend/feature-flags.md

[grammar] ~561-~561: Ensure spelling is correct
Context: ...y**: Updates typically arrive within 50-200ms - Automatic Reconnection: Provider handl...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~1337-~1337: Use a hyphen to join words.
Context: ...g Solution: - Verify browser check in feature-flag.provider.ts - Initiali...

(QB_NEW_EN_HYPHEN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (50)
docs/build-time-secrets.md (1)

1-345: Excellent documentation for build-time secrets management.

This document provides comprehensive coverage of the build-time secret injection architecture, including security considerations, troubleshooting, and practical examples. The step-by-step guide for adding new secrets will be valuable for future maintenance.

docs/architecture/frontend/feature-flags.md (1)

1-1460: Outstanding feature flags documentation.

This is exceptionally comprehensive documentation that covers architecture, implementation details, usage patterns, best practices, error handling, and troubleshooting. The real-world examples and ASCII diagrams make it easy to understand the system. This will be an excellent resource for developers working with feature flags.

apps/lfx-one/src/app/app.component.ts (1)

10-10: LGTM: Feature flag initialization is correctly placed.

The initialization happens after authentication is confirmed and passes the user context for targeting. Error handling is graceful, allowing the app to continue with default flag values if initialization fails. This matches the recommended pattern from the feature flags documentation.

Also applies to: 23-23, 57-61

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html (1)

13-30: LGTM: Conditional rendering with feature flag is correctly implemented.

The organization selector is now controlled by a feature flag with proper signal invocation. This enables progressive rollout and quick rollback capabilities for this UI element.

apps/lfx-one/.env.example (1)

69-73: LGTM: Clear documentation for LaunchDarkly configuration.

The comments clearly explain that this value is injected at build time via Docker and provide guidance for local development. This aligns with the build-time secrets architecture documented in the PR.

apps/lfx-one/src/app/layouts/main-layout/main-layout.component.html (1)

7-7: LGTM: Signal invocation is correctly updated.

The sidebar items binding now correctly invokes the computed signal to get the filtered navigation items. This enables dynamic filtering based on feature flags as described in the documentation.

Also applies to: 26-26

apps/lfx-one/src/app/app.config.ts (1)

18-18: LGTM: Feature flags provider is correctly registered.

The provideFeatureFlags() provider is properly imported and added to the application configuration. This initializes the OpenFeature provider during application bootstrap via APP_INITIALIZER.

Also applies to: 49-49

packages/shared/src/interfaces/index.ts (1)

52-53: LGTM: Feature flags interface export follows established patterns.

The export for feature flag interfaces is correctly structured and maintains consistency with other interface exports in this index file.

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts (2)

11-11: LGTM: Clean service integration.

The FeatureFlagService import and injection follow Angular best practices with proper visibility and readonly modifier.

Also applies to: 27-27


36-37: The implementation is correct and follows sound architectural patterns.

The code uses a graceful fallback design where if the 'organization-selector' flag is unavailable in LaunchDarkly or the flag service is uninitialized, the default value of true ensures the organization selector remains visible. The feature flag service is built with error handling that logs issues but continues execution, making the system resilient to flag configuration issues.

Flag key validation occurs at runtime through LaunchDarkly; the codebase intentionally doesn't maintain a central registry of keys to allow flexibility. This is appropriate for the OpenFeature abstraction pattern being used.

.github/workflows/docker-build-main.yml (3)

12-15: LGTM: Correct workflow-level permissions.

The workflow-level permissions correctly grant id-token: write for AWS OIDC authentication and packages: write for container registry operations.


22-29: LGTM: Proper secret retrieval and propagation.

The workflow correctly orchestrates secret retrieval through the reusable workflow and propagates the LaunchDarkly client ID to the build job via outputs and environment variables.


75-76: Verification complete—the code is correct.

docker/build-push-action@v5 supports the secret-envs input, and the syntax correctly maps the environment variable to a Docker secret for BuildKit secret mounts.

apps/lfx-one/src/environments/environment.staging.ts (2)

4-5: LGTM: Correct ambient declaration.

The ambient declaration properly types the build-time constant that will be injected via Angular's define feature.


17-19: LGTM: Safe fallback for missing client ID.

The implementation correctly falls back to an empty string when the build-time constant is not provided, which aligns with the provider's falsy check to disable feature flags gracefully.

.github/workflows/get-launchdarkly-secrets.yml (4)

1-15: LGTM: Proper reusable workflow structure.

The workflow is correctly configured as a reusable workflow with appropriate permissions and output definition.


32-37: LGTM: Correct AWS Secrets Manager integration.

The secret retrieval step properly uses the official AWS action and matches the documented secret path from the PR prerequisites.


39-61: LGTM: Robust secret validation.

The validation step properly checks for the presence of required secrets, parses JSON correctly, and provides clear error messages with fail-fast behavior.


63-74: LGTM: Defensive masking approach.

The output step correctly parses and masks the client ID. Note that LaunchDarkly client-side SDK IDs are typically public values (visible in browser requests), but the explicit masking is a safe defensive practice that aligns with the PR's security-focused approach.

Dockerfile (1)

27-30: LGTM: Secure BuildKit secret injection.

The implementation correctly uses BuildKit secret mounts to avoid persisting secrets in image layers. The define flag format is correct for Angular's build-time constant replacement.

Note: If the secret file is empty, the build will proceed with an empty string, which is handled gracefully by the environment fallback logic. The validation step in get-launchdarkly-secrets.yml ensures the secret exists before reaching this point.

apps/lfx-one/src/app/shared/providers/feature-flag.provider.ts (4)

17-20: LGTM: Proper SSR handling.

The server-side guard correctly prevents OpenFeature initialization during server-side rendering using the standard typeof window === 'undefined' check.


22-26: LGTM: Graceful fallback for missing client ID.

The implementation correctly handles missing client IDs with a warning message and allows the app to continue without feature flags, which is appropriate for this scenario.


35-39: LGTM: Proper error handling.

The provider initialization correctly uses async/await and handles errors gracefully by logging and allowing the app to continue without feature flags.


46-46: LGTM: Correct Angular 19 provider pattern.

The provider export properly uses Angular 19's provideAppInitializer API with the correct return type.

apps/lfx-one/src/environments/environment.dev.ts (1)

4-5: LGTM: Consistent environment configuration.

The implementation matches the pattern in environment.staging.ts with proper ambient declaration and safe fallback behavior.

Also applies to: 17-19

apps/lfx-one/package.json (1)

43-45: All dependency versions are current and free from known vulnerabilities.

Verification confirms that all four dependencies are pinned to their latest available versions on npm, with no security advisories detected. The use of caret ranges (^) is appropriate for stable versions. No action required.

apps/lfx-one/src/environments/environment.prod.ts (2)

4-5: LGTM: Ambient declaration for build-time constant.

The ambient declaration appropriately types the build-time constant as string | undefined, which aligns with the Angular define mechanism where the value may not be provided during local development.


17-19: LGTM: Build-time constant with appropriate fallback.

The property correctly uses the build-time constant with an empty string fallback for production builds. This ensures the application won't crash if the secret injection fails, though feature flags will be disabled.

apps/lfx-one/src/environments/environment.ts (2)

4-5: LGTM: Consistent ambient declaration.

The ambient declaration matches the production environment, maintaining consistency across the codebase.


17-20: LGTM: Development environment with local fallback.

The hardcoded dev client ID fallback enables local development without requiring secret injection. Since LaunchDarkly client IDs are public client-side values, this approach is secure and appropriate for the development workflow.

apps/lfx-one/src/app/layouts/main-layout/main-layout.component.ts (3)

5-5: LGTM: Feature flag service integration.

The imports and service injection follow Angular best practices for integrating the feature flag system into the layout component.

Also applies to: 9-9, 25-25


30-31: LGTM: Feature flag for conditional sidebar rendering.

The boolean flag with a default value of true ensures the Projects item remains visible when the feature flag service is uninitialized or the flag is not configured, maintaining backward compatibility.


33-50: LGTM: Base sidebar items refactoring.

Renaming to baseSidebarItems clearly distinguishes the static configuration from the computed, feature-flag-aware sidebarItems, improving code clarity.

packages/shared/src/interfaces/feature-flags.interface.ts (2)

8-17: LGTM: Well-documented evaluation context interface.

The interface appropriately models OpenFeature's evaluation context with optional properties, enabling flexible user targeting for LaunchDarkly flag evaluation.


22-34: LGTM: Comprehensive LaunchDarkly configuration interface.

The interface correctly models LaunchDarkly provider configuration with clientId as the only required property, providing sensible optional configuration for timeouts, streaming, and logging.

.github/workflows/docker-build-pr.yml (4)

15-15: LGTM: OIDC authentication permission.

The id-token: write permission enables GitHub Actions to authenticate with AWS using OIDC, eliminating the need for long-lived credentials.


22-30: LGTM: Secure secret retrieval with fork protection.

The conditional execution ensures secrets are only retrieved for legitimate preview deployments from trusted sources, preventing secret exposure to forked repositories.


33-33: LGTM: Proper job dependency and secret propagation.

The dependency chain ensures secrets are retrieved before the build starts, and the environment variable correctly propagates the LaunchDarkly client ID from the get-secrets job output.

Also applies to: 42-43


89-90: LGTM: BuildKit secret mount configuration.

The secret-envs mapping correctly passes the LaunchDarkly client ID to the Docker build as a secret mount, ensuring it doesn't persist in the image layers.

.github/workflows/docker-build-tag.yml (3)

13-14: LGTM: Top-level permissions configuration.

Moving permissions to the workflow level is cleaner and ensures both OIDC authentication and package publishing are properly authorized.


23-24: LGTM: Consistent secret retrieval pattern.

The workflow correctly implements the same secret retrieval pattern as other workflows, ensuring consistent handling of build-time secrets across all build types.

Also applies to: 27-27, 29-30


93-94: LGTM: BuildKit secret mount for release builds.

The secret injection mechanism is consistently implemented across all Docker build workflows.

apps/lfx-one/angular.json (1)

57-59: LGTM: Root-level define configuration.

The base-level define configuration ensures LAUNCHDARKLY_CLIENT_ID is defined for all build configurations with a safe empty string default.

apps/lfx-one/src/app/shared/services/feature-flag.service.ts (7)

11-16: LGTM: Well-structured service fields.

The service correctly uses private signals for internal state management and exposes a readonly signal for external consumption, following Angular best practices.


22-45: LGTM: Robust initialization with proper error handling.

The initialization method correctly handles the OpenFeature client setup, user context configuration, and error cases. The fallback chain for targetingKey (preferred_username || username || sub) provides good resilience for different authentication providers.


51-67: LGTM: Reactive boolean flag accessor.

The computed signal correctly establishes a reactive dependency on the context signal, ensuring automatic re-evaluation when flags change. The error handling and default value fallback are appropriate.


73-89: LGTM: Consistent flag accessor pattern.

The string and number flag accessors follow the same robust pattern as the boolean accessor, with appropriate type-specific default values.

Also applies to: 95-111


117-133: LGTM: Type-safe object flag accessor.

The generic object flag accessor correctly constrains the type to JsonValue from OpenFeature, ensuring type safety for JSON-serializable flag values.


138-157: Consider implementing event handler cleanup.

The event handlers are properly set up for real-time flag updates. However, since the service is provided in root, the handlers will persist for the application lifetime, which is acceptable. If the service scope changes in the future, you'll need to implement cleanup (e.g., in ngOnDestroy or via DestroyRef).

Current implementation is fine for a root-scoped service, but document this assumption or add cleanup if the service scope might change.


162-167: LGTM: Signal refresh mechanism.

Creating a new object reference with the spread operator is an effective way to trigger re-evaluation of all computed flag signals. This approach leverages Angular's signal change detection correctly.

- Remove duplicate EvaluationContext and LaunchDarklyConfig interfaces from shared package (available from @openfeature/web-sdk)
- Update feature flags documentation to show provideAppInitializer instead of deprecated APP_INITIALIZER pattern
- Replace hardcoded LaunchDarkly client IDs with placeholders in documentation
- Add preferred_username field to User interface for feature flag targeting
- Improve type safety in feature-flag.service.ts by using User type

LFXV2-750

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
docs/build-time-secrets.md (1)

199-207: Minor path placeholder inconsistency in AWS Secrets Manager example

In Step 1 you describe the pattern as /cloudops/managed-secrets/[service]/[app]/[secret_name], but the CLI example hardcodes service:

--name /cloudops/managed-secrets/service/lfx_one/new_secret

Consider keeping it clearly templated to avoid copy‑paste confusion, e.g.:

-  --name /cloudops/managed-secrets/service/lfx_one/new_secret \
+  --name /cloudops/managed-secrets/<service>/lfx_one/new_secret \

or mirror the actual service name you expect.

docs/architecture/frontend/feature-flags.md (1)

568-588: Environment configuration section should reflect build-time LAUNCHDARKLY_CLIENT_ID flow

This section still suggests hardcoding launchDarklyClientId values in environment.ts / environment.prod.ts:

export const environment = {
  production: false,
  launchDarklyClientId: 'your-dev-client-id-here',
};

Given the new build-time injection via --define LAUNCHDARKLY_CLIENT_ID (documented in docs/build-time-secrets.md), it would be clearer to either:

  • Show the current pattern with the injected constant and dev fallback, e.g.:
+declare const LAUNCHDARKLY_CLIENT_ID: string | undefined;
+
 export const environment = {
   production: false,
-  launchDarklyClientId: 'your-dev-client-id-here',
+  launchDarklyClientId:
+    typeof LAUNCHDARKLY_CLIENT_ID !== 'undefined'
+      ? LAUNCHDARKLY_CLIENT_ID
+      : 'dev-client-id-fallback',
 };

(and the analogous prod variant), or

  • Explicitly reference docs/build-time-secrets.md here and note that the preferred configuration in CI is via build-time secrets rather than checked-in client IDs.

That keeps the architecture doc aligned with the actual deployment story.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d41c385 and 75331d7.

📒 Files selected for processing (5)
  • Dockerfile (1 hunks)
  • apps/lfx-one/src/app/shared/services/feature-flag.service.ts (1 hunks)
  • docs/architecture/frontend/feature-flags.md (1 hunks)
  • docs/build-time-secrets.md (1 hunks)
  • packages/shared/src/interfaces/auth.interface.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile
🧰 Additional context used
🧬 Code graph analysis (1)
apps/lfx-one/src/app/shared/services/feature-flag.service.ts (1)
packages/shared/src/interfaces/auth.interface.ts (1)
  • User (8-43)
🪛 LanguageTool
docs/architecture/frontend/feature-flags.md

[grammar] ~560-~560: Ensure spelling is correct
Context: ...y**: Updates typically arrive within 50-200ms - Automatic Reconnection: Provider handl...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~1336-~1336: Use a hyphen to join words.
Context: ...g Solution: - Verify browser check in feature-flag.provider.ts - Initiali...

(QB_NEW_EN_HYPHEN)

docs/build-time-secrets.md

[uncategorized] ~36-~36: The official name of this software platform is spelled with a capital “H”.
Context: ...use a reusable GitHub Actions workflow (.github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)


[uncategorized] ~211-~211: The official name of this software platform is spelled with a capital “H”.
Context: ... Step 2: Update Reusable Workflow Edit .github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)

🔇 Additional comments (3)
packages/shared/src/interfaces/auth.interface.ts (1)

37-38: preferred_username field addition looks correct and aligns with usage

The optional preferred_username claim matches how the feature-flag user context is built and keeps the shared User interface in sync with Auth0/OIDC-style profiles.

apps/lfx-one/src/app/shared/services/feature-flag.service.ts (2)

4-18: Typed user context + signal-based API look solid

Using the shared User type here (including preferred_username) and exposing read‑only signals for initialization status and flag values is a clean, type‑safe integration of OpenFeature with Angular signals.


23-45: Initialization is one-shot; flags won’t adapt if the active user changes

initialize short‑circuits when this.isInitialized() is true, so if your app ever supports changing the authenticated user without a full reload (logout/login, account switch, etc.), the evaluation context and targetingKey will remain tied to the first user.

If user switching is a realistic scenario, consider either:

  • Allowing initialize to update the context when the user identity changes, or
  • Adding a dedicated setUserContext(user: User)/reset() method that updates context (and, if needed, re-registers handlers) without duplicating setup.

If the app is guaranteed single-user per browser session, the current behavior is fine but worth documenting in the service JSDoc.

- Fix Dockerfile to use --define=KEY=VALUE without -- separator
- Update documentation with verified working syntax
- Remove incorrect -- separator from all examples
- Tested build completes successfully and value is injected

The Angular CLI requires --define=KEY=VALUE format directly on yarn build
commands without the -- separator. String values must be quoted within
quotes: --define=KEY="'value'"

Verified with test build that value is properly injected into bundle.

LFXV2-750

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
docs/build-time-secrets.md (1)

1-349: ✅ Comprehensive documentation with all past issues resolved.

This is well-structured documentation that clearly explains the build-time secrets architecture. Past review concerns have been addressed:

  • ✅ All fenced code blocks now include language specifiers (lines 22, 46, 111, etc.)
  • ✅ Placeholder values used throughout instead of hardcoded credentials (e.g., 'dev-client-id-fallback', 'your-client-id-here')
  • ✅ Strong security guidance emphasizing client-side exposure and appropriate use cases (lines 191–195)

The content provides a clear end-to-end flow (Overview → Architecture → Data Flow → Local Dev → Workflow Integration → Troubleshooting) with practical examples and step-by-step guidance for extending to new secrets. References to related docs are provided.

For readers seeking the complete workflow implementation details (jq extraction, masking steps, etc.), consider adding a note in the "Reusable Workflow" section (lines 34–52) to reference .github/workflows/get-launchdarkly-secrets.yml in the PR for the full implementation details.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 75331d7 and 565869a.

📒 Files selected for processing (2)
  • Dockerfile (1 hunks)
  • docs/build-time-secrets.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile
🧰 Additional context used
🪛 LanguageTool
docs/build-time-secrets.md

[uncategorized] ~36-~36: The official name of this software platform is spelled with a capital “H”.
Context: ...use a reusable GitHub Actions workflow (.github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)


[uncategorized] ~211-~211: The official name of this software platform is spelled with a capital “H”.
Context: ... Step 2: Update Reusable Workflow Edit .github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)

- Build shared package separately before UI to avoid TypeScript compiler errors
- Use yarn workspace commands for targeted builds
- Update Dockerfile to build @lfx-one/shared then lfx-one-ui
- Update documentation with workspace build approach
- Change --define syntax to use space (matches Angular CLI docs)

This resolves the issue where running build from root would fail because
the shared package's TypeScript compiler doesn't understand --define flags.
Building them separately allows each package to use appropriate build flags.

LFXV2-750

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
Dockerfile (1)

30-33: Consider adding explicit secret validation for clearer error messages.

While the current implementation will fail if the secret is missing (via Docker's BuildKit), adding an explicit validation step would provide clearer error messages and fail faster during builds.

The past review suggested defensive validation. Consider this defensive approach:

  RUN --mount=type=secret,id=LAUNCHDARKLY_CLIENT_ID \
+     if [ ! -f /run/secrets/LAUNCHDARKLY_CLIENT_ID ]; then \
+       echo "Error: LAUNCHDARKLY_CLIENT_ID secret not found" && exit 1; \
+     fi && \
      LAUNCHDARKLY_CLIENT_ID=$(cat /run/secrets/LAUNCHDARKLY_CLIENT_ID) && \
      yarn workspace lfx-one-ui build:${BUILD_ENV} --define LAUNCHDARKLY_CLIENT_ID="'${LAUNCHDARKLY_CLIENT_ID}'"

This provides fail-fast behavior with a clear error message if the secret mount fails, improving the developer experience during troubleshooting.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 565869a and c0c6f7c.

📒 Files selected for processing (2)
  • Dockerfile (1 hunks)
  • docs/build-time-secrets.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/build-time-secrets.md

[uncategorized] ~36-~36: The official name of this software platform is spelled with a capital “H”.
Context: ...use a reusable GitHub Actions workflow (.github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)


[uncategorized] ~221-~221: The official name of this software platform is spelled with a capital “H”.
Context: ... Step 2: Update Reusable Workflow Edit .github/workflows/get-launchdarkly-secrets.yml...

(GITHUB)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-push
🔇 Additional comments (2)
Dockerfile (1)

27-28: Shared package build correctly separated.

Building the shared package separately before the Angular UI ensures compatibility with TypeScript compiler, which doesn't support the --define flag. This is the right approach.

docs/build-time-secrets.md (1)

1-363: Comprehensive and well-organized documentation.

The documentation effectively guides users through build-time secret injection, from architecture through troubleshooting. Structure is logical, examples are accurate and use proper placeholders, security considerations are clearly highlighted, and the step-by-step guide for adding new secrets is thorough. The past concerns (language specifiers, placeholder values) have been addressed.

@github-actions
Copy link

github-actions bot commented Nov 17, 2025

🚀 Deployment Status

Your branch has been deployed to: https://ui-pr-157.dev.v2.cluster.linuxfound.info

Deployment Details:

  • Environment: Development
  • Namespace: ui-pr-157
  • ArgoCD App: ui-pr-157

The deployment will be automatically removed when this PR is closed.

Remove get-launchdarkly-secrets.yml reusable workflow and implement direct
AWS Secrets Manager retrieval in each job using GITHUB_ENV. This resolves
the GitHub Actions issue where masked values cannot be used in workflow
outputs.

Changes:
- Update docker-build-main.yml to retrieve secrets directly in job
- Update docker-build-tag.yml to retrieve secrets directly in job
- Update docker-build-pr.yml to retrieve secrets directly in job
- Delete get-launchdarkly-secrets.yml reusable workflow file
- Update build-time-secrets.md documentation to reflect new approach
- LaunchDarkly client-side IDs are NOT masked (they're public values)

LFXV2-750

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <[email protected]>
@asithade asithade merged commit 951af3f into main Nov 18, 2025
7 checks passed
@asithade asithade deleted the feat/LFXV2-750 branch November 18, 2025 00:05
@github-actions
Copy link

🧹 Deployment Removed

The deployment for PR #157 has been removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants