Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
Alga PSA EE Extension System — Out-of-Process, Multi‑Tenant
This directory contains the Enterprise Edition documentation for the Alga PSA Extension System. The system is v2-only: out-of-process execution, signed/reproducible bundles, an API Gateway that proxies to the Runner, and iframe-only UI served by the Runner.
Documentation Index
Core Architecture
- Overview — Goals, isolation model, components
- Runner — Responsibilities, configuration, and integration
- Implementation Plan — Current plan and acceptance criteria
- Development Guide — Building componentized extensions (WASM handler + iframe UI)
Technical Guides
- API Routing Guide — Gateway pattern
/api/ext/[extensionId]/[[...path]]→ Runner/v1/execute - Client UI Template/SDK Guide — Iframe SDK and UI kit usage
- DataTable Integration Guide — Using the UI kit DataTable in iframe apps
- Enterprise Build Workflow — EE build, packaging, and publish
Reference
- Manifest Schema — Manifest v2 (runtime, capabilities, api.endpoints, ui.iframe, precompiled, assets)
- Registry Implementation — Data model and services
- Security & Signing — Signed, content‑addressed bundles (sha256:...), verification, quotas
- Sample Extension — Server handler + iframe UI example
- Index — Topical map
Purpose
The extension system enables:
- Server‑side handlers executed out‑of‑process via a Runner (WASM-first)
- UI extensions rendered exclusively via sandboxed iframes with a bridge SDK
- Controlled integrations with external systems via capability-based Host APIs
Design goals:
- No tenant code executes in the core app process
- Per-tenant isolation for compute, storage, and egress
- Signed, content-addressed bundles with verified provenance (sha256:…)
- Least-privilege host APIs with quotas and auditable execution
- Component-model execution so extensions produced via
componentize-js+@alga-psa/extension-runtimebehave consistently across languages
Architecture Snapshot
- Runner (Rust + Wasmtime components): executes handlers produced by
componentize-js, enforces capability-scoped host APIs, and serves static iframe UI assets at${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/[...]. - Registry + Bundle Store (S3-compatible): content-addressed artifacts, install-scoped config, provider grants, and sealed secret envelopes.
- API Gateway (Next.js):
/api/ext/[extensionId]/[[...path]]looks up the tenant install via@ee/lib/extensions/installConfig, forwards{context, http, limits, config, providers, secret_envelope}to RunnerPOST /v1/execute, and proxies the response. Manifest endpoint lists are advisory; enforcement is undecided (see alignment plan). - Client SDKs:
@alga/extension-iframe-sdk(postMessage bridge) and@alga/ui-kit(components + theming), plus@alga-psa/extension-runtimefor component handlers.
Correctness Rules
- All extension API calls go through
/api/ext/[extensionId]/[[...path]]and are proxied to Runner/v1/execute. Reference the Next.js handler at server/src/app/api/ext/[extensionId]/[[...path]]/route.ts. - UI assets are served by the Runner only at
${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/[...]; the Next.jsext-uiroute is a gate that returns 404/redirect when rust-host mode is active. - Iframe src is constructed by buildExtUiSrc() and bootstrapped via bootstrapIframe()
- Tenant install metadata (config, providers, secret envelopes) flows through @ee/lib/extensions/installConfig and is attached to each execute request so the Runner can unlock capabilities.
- Registry v2 and signing integrate with ExtensionRegistryServiceV2
Runner Configuration (host environment)
- RUNNER_BASE_URL — internal URL used by the Gateway to call
POST /v1/execute - RUNNER_PUBLIC_BASE — public base used to construct ext-ui iframe src
- EXT_GATEWAY_TIMEOUT_MS — gateway → runner request timeout
- SIGNING_TRUST_BUNDLE — trust anchors for signature verification
- EXT_EGRESS_ALLOWLIST — optional comma-separated host list used by the Wasmtime HTTP capability guardrail
Configuration Summary
Server and Runner use these environment variables:
-
Gateway/Runner
RUNNER_BASE_URL(Gateway → Runner execute API)RUNNER_PUBLIC_BASE(iframe UI base)EXT_GATEWAY_TIMEOUT_MS(Gateway timeout)SIGNING_TRUST_BUNDLE(signature verification trust anchors)DEBUG_STREAM_REDIS_URL,RUNNER_DEBUG_REDIS_STREAM_PREFIX,RUNNER_DEBUG_REDIS_MAXLEN(live debug stream fan-out)
-
Object Storage (S3/MinIO)
STORAGE_S3_ENDPOINT,STORAGE_S3_REGIONSTORAGE_S3_ACCESS_KEY,STORAGE_S3_SECRET_KEYSTORAGE_S3_BUCKET,STORAGE_S3_FORCE_PATH_STYLESTORAGE_S3_BUNDLE_BUCKET(optional override for bundles)
Refer to this list from other docs to avoid drift. See Runner S3 guide for runtime-specific notes.
Live Debug Console
- Runner emits stdout/stderr/log events as
ExtDebugEventrecords via Redis Streams whenRUNNER_DEBUG_REDIS_URLis configured. See server/src/lib/extensions/debugStream/redis.ts. - EE exposes
/api/ext-debug/stream(SSE) and the MSP UI at/msp/extensions/[id]/debugso authorized users can watch events in real time. - Feature flags, tenant scoping, and capability gating are tracked in 2025-11-12-extension-system-alignment-plan.
Getting Started
- Read the Overview
- Review the Manifest Schema and Security & Signing
- Follow the Development Guide to build:
- Server handlers targeting the Runner (WASM-first)
- An iframe UI that uses the Client SDK and UI kit
- See Sample Extension for an end‑to‑end example
Host Embedding Quickstart (iframe)
- Construct src with the canonical helper:
buildExtUiSrc(extensionId, contentHash, path, { tenantId? })fromserver/src/lib/extensions/ui/iframeBridge.ts. - If
RUNNER_PUBLIC_BASEis absolute, setallowedOriginto that origin before bootstrapping the iframe. - Bootstrap once the iframe element exists:
import { buildExtUiSrc, bootstrapIframe } from 'server/src/lib/extensions/ui/iframeBridge';
const src = buildExtUiSrc(extId, contentHash, '/');
iframe.src = src;
bootstrapIframe({
iframe,
extensionId: extId,
contentHash,
initialPath: '/',
session: { token, expiresAt },
themeTokens,
allowedOrigin: process.env.RUNNER_PUBLIC_BASE, // required when absolute
});
- ext-ui is always served by the Runner at
${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/...; the Next.jsext-uiroute only gates/redirects when rust-host mode is enabled.