# Extension UI Loading Fix for Docker Backend ## Problem Statement The current extension UI loading system has two different implementations: 1. **Knative Backend (Production)**: Uses custom domains (`*.apps.algapsa.com`) with Temporal provisioning 2. **Docker Backend (Local Dev)**: Intended to use path-based URLs (`/runner/ext-ui/...`) but **incomplete** ### Current Issues 1. The EE extension page component (`packages/product-extensions/ee/entry.tsx`) only supports Knative-style custom domains 2. It requires a `runner_domain` field in the database, which doesn't make sense for Docker local development 3. The `ExtensionIframe` component has hardcoded domain validation that only allows `*.apps.algapsa.com` 4. There's no implementation for content-hash based URLs in the Docker backend path ### What Works - ✅ Docker runner container runs successfully on port 8085 - ✅ Extension registry, version, and install records in database - ✅ Extension shows in sidebar menu ("Hello World") - ✅ Backend abstraction exists (`RunnerBackend` interface) - ✅ Proxy route exists (`/runner/[...path]`) - ✅ `buildExtUiSrc()` function supports both modes ### What Doesn't Work - ❌ Extension page tries to load `https://hello-world.apps.algapsa.com/` instead of `/ext-ui/...` - ❌ No implementation to serve UI assets for Docker backend - ❌ Extension iframe refuses to load non-allowed domains - ❌ Content hash in database is fake (`sha256:1234...`) - ❌ No actual bundle uploaded to storage ## Root Cause Analysis The implementation is **incomplete**. The plan document shows: - Phase 1 (Abstraction) ✅ DONE - Phase 2 (Proxy Routing) ✅ DONE - Phase 3 (Tooling) ✅ MOSTLY DONE - **Missing**: Alternative extension page component for Docker backend that uses content-hash URLs ## Solution Design ### Architecture Decision For Docker backend (`RUNNER_BACKEND=docker`), we need: 1. **Content-hash based URLs** instead of custom domains 2. **Same-origin serving** via Next.js proxy at `/ext-ui/...` 3. **No `runner_domain` requirement** in database ### Implementation Approach #### Option A: Conditional Rendering in Extension Page (RECOMMENDED) Modify `packages/product-extensions/ee/entry.tsx` to: 1. Check `RUNNER_BACKEND` environment variable 2. If `docker`: Use content-hash based iframe URL with `buildExtUiSrc()` 3. If `knative`: Use existing custom domain approach **Pros:** - Single component, clear conditional logic - Leverages existing `buildExtUiSrc()` function - No duplicate code **Cons:** - Mixes two approaches in one component #### Option B: Separate Page Components Create `packages/product-extensions/docker/entry.tsx` for Docker mode **Pros:** - Clean separation of concerns - Each mode has its own optimized implementation **Cons:** - Need routing logic to select correct component - More code duplication ### Decision: Go with Option A It's simpler and the logic is straightforward. ## Implementation Plan ### Step 1: Fix Extension Bundle Content Hash **Problem**: Bundle has fake hash `sha256:1234...` **Solution**: Generate real SHA256 hash from extension directory ```bash # Calculate real content hash cd ee/extensions/samples/hello-world tar -czf - . | sha256sum ``` **Update database** with real hash ### Step 2: Upload Bundle to Storage **Current**: `storage_url` points to local filesystem (`file://...`) **For Docker backend**: - Either keep filesystem path (Runner can access it) - Or upload to MinIO (running on port 4569) **Decision**: Keep filesystem for now, ensure Runner can access it ### Step 3: Create Docker-Compatible Extension Page Component **File**: `packages/product-extensions/ee/entry.tsx` **Changes needed**: ```typescript import { buildExtUiSrc } from 'server/src/lib/extensions/assets/url.shared'; export default async function Page({ params }: { params: { id: string } }) { const id = params.id; const runnerBackend = process.env.RUNNER_BACKEND || 'knative'; // Fetch install info const info = await getInstallInfo(id); if (runnerBackend === 'docker') { // Docker mode: Use content-hash based URLs if (!info?.content_hash) { return