Hermes 284313f908
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
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

7.2 KiB
Raw Permalink Blame History

Alga PSA EE Extension System — Out-of-Process, MultiTenant

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

Reference

Purpose

The extension system enables:

  1. Serverside handlers executed outofprocess via a Runner (WASM-first)
  2. UI extensions rendered exclusively via sandboxed iframes with a bridge SDK
  3. 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-runtime behave 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 Runner POST /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-runtime for 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.js ext-ui route 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_REGION
    • STORAGE_S3_ACCESS_KEY, STORAGE_S3_SECRET_KEY
    • STORAGE_S3_BUCKET, STORAGE_S3_FORCE_PATH_STYLE
    • STORAGE_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

Getting Started

Host Embedding Quickstart (iframe)

  • Construct src with the canonical helper: buildExtUiSrc(extensionId, contentHash, path, { tenantId? }) from server/src/lib/extensions/ui/iframeBridge.ts.
  • If RUNNER_PUBLIC_BASE is absolute, set allowedOrigin to 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.js ext-ui route only gates/redirects when rust-host mode is enabled.