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

9.4 KiB
Raw Permalink Blame History

Extension Runner (EE)

This document explains the Extension Runners role in the Enterprise Extension System and how it integrates with the Gateway, Registry, signing, and UI delivery.

References:

Purpose and responsibilities

  • Execute extension server handlers (Wasmtime components produced by componentize-js) with strict isolation.
  • Serve static UI assets for extensions by immutable content hash (content-addressed).
  • Enforce capability-based host APIs and guardrails (quotas, timeouts, egress policies).
  • Provide a stable HTTP interface for the Gateway to invoke extension handlers.
  • Maintain a pod-local cache of verified bundles for performance and reliability.

High-level architecture

  • Process and runtime: Rust + Wasmtime (Component Model).
    • Wasmtime is used to load/execute precompiled or interpreted WASM components with resource limits.
  • HTTP server: Axum + Tower layers (tracing, headers, static files), see dependencies in ee/runner/Cargo.toml.
  • Storage and caching:
    • Immutable content-addressed bundles (tar/zstd or similar) stored in object storage.
    • Pod-local cache keyed by content hash for hot assets and module bytes.
  • Security:
    • Signature verification at publish/install (via Registry) and prior to execution/load.
    • Capability-based host API; deny-by-default for egress and privileged operations.
    • Strict header and size limits on inbound/outbound HTTP.

Interfaces

1) Execute endpoint (Runner)

  • HTTP: POST /v1/execute
  • Caller: Gateway
  • Request (example):
  • Actual payload shape (2025-11-12):
{
  "context": {
    "request_id": "3d2c3f27-5b9c-4ad0-90f5-4c5ec8e844be",
    "tenant_id": "tenant-123",
    "extension_id": "com.example.sales",
    "version_id": "ver_abc123",
    "content_hash": "sha256:012345...abcd"
    // install_id is currently omitted; tracked in Workstream A1.
  },
  "http": {
    "method": "POST",
    "path": "/agreements/sync",
    "query": { "force": "true" },
    "headers": {
      "x-request-id": "3d2c3f27-...",
      "x-alga-tenant": "tenant-123",
      "x-alga-extension": "com.example.sales",
      "content-type": "application/json"
    },
    "body_b64": "eyAiZm9vIjogImJhciIgfQ=="
  },
  "limits": { "timeout_ms": 5000 },
  "config": { "region": "emea" },
  "providers": ["cap:http.fetch","cap:storage.kv","cap:secrets.get"],
  "secret_envelope": { "ciphertext_b64": "vault:v1:....", "algorithm": "vault-transit:v1" }
}

Earlier versions included an endpoint field pointing at dist/handlers/.... Componentized extensions no longer require the gateway to select a handler file; the component inspects request.http directly.

  • Response (example):
{
  "status": 200,
  "headers": {
    "content-type": "application/json",
    "x-ext-request-id": "3d2c3f27-5b9c-4ad0-90f5-4c5ec8e844be"
  },
  "body_b64": "eyAic3RhdHVzIjogIm9rIiB9"
}

2) Static UI asset hosting (Runner)

  • Purpose: Serve iframe UI assets for a given extension by content hash.
  • URL shape (recommended):
    • ${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/[...]
  • Behavior:
    • Content-addressed path ensures immutability. Set Cache-Control: public, max-age=31536000, immutable.
    • Assets validated (existence and content hash) before serving.
    • MIME type is derived safely (e.g., via mime_guess); only static file types served.
  • Host bootstrap:
    • The host uses buildExtUiSrc() to construct the iframe URL for the Runners public base and bootstrapIframe() to perform the secure initialization (sandbox, origin checks, postMessage protocol).

Execution model

  • Module resolution:
    • version_id + content_hash → fetch component artifact (dist/main.wasm) from object storage/cache. The handler selection happens inside the component; manifest endpoint data is advisory today.
  • Isolation and limits:
    • Memory/time/fuel limits enforced per invocation (configurable).
    • Concurrency controls per tenant/extension (global caps, per-request rate limits).
  • Capability-based host APIs (examples):
    • http.fetch with tenant/extension egress allowlists.
    • storage.kv with tenant-namespaced keys.
    • secrets.get returning handles/tokens; plaintext minimized.
    • ui_proxy.call_route bridging from components to host-approved UI proxy endpoints.
    • invoicing.create_manual_invoice to create draft manual invoices (requires cap:invoice.manual.create).
    • log, metrics, and live debug events emitting structured telemetry.

Security and signing

  • Content-addressed bundles:
    • Hash: sha256:<hex64>.
    • Bundles contain manifest.json (v2), dist/ (WASM handlers), ui/ (static assets), and optional precompiled artifacts.
  • Signatures:
    • Detached signature persisted alongside bundle metadata.
    • Verification occurs on publish/install and before load/serve.
    • Trust roots provisioned via Runner/Registry environment (e.g., PEM chain).
  • Origin and iframe safety:
    • The host enforces sandbox defaults (allow-scripts, no implicit allow-same-origin) and validates target origins in the bootstrap flow (see bootstrapIframe()).
  • Header policy:
    • Gateway strips end-user authorization and injects service-level headers (x-request-id, tenant/extension IDs).
    • Runner enforces response header allowlist (content-type, safe cache-control, custom x-ext-*).

Configuration (env)

  • RUNNER_BASE_URL: Gateways internal URL to call Runner (e.g., http://runner:8080).
  • RUNNER_DOCKER_HOST: Override Runner base URL when using the Docker backend (e.g., http://localhost:8085).
  • RUNNER_PUBLIC_BASE: Public base used in iframe src for UI assets. Accepts absolute URLs or relative paths (e.g., /runner) when the gateway proxies Runner assets.
  • SIGNING_TRUST_BUNDLE: Path or value for trusted publisher certificates/keys.
  • BUNDLE_STORE_BASE / BUNDLE_STORAGE_*: Object storage configuration for content-addressed bundle retrieval (S3 or equivalent).
  • REGISTRY_BASE_URL, ALGA_AUTH_KEY: Used to fetch install metadata/signature info from the EE server.
  • EXT_EGRESS_ALLOWLIST: Comma-separated list of hostnames allowed for alga.http.fetch.
  • RUNNER_DEBUG_REDIS_URL, RUNNER_DEBUG_REDIS_STREAM_PREFIX, RUNNER_DEBUG_REDIS_MAXLEN, RUNNER_DEBUG_MAX_EVENT_BYTES: Enable Redis-backed debug streaming (stdout/stderr/log fan-out).
  • UI_PROXY_BASE_URL, UI_PROXY_AUTH_KEY, UI_PROXY_TIMEOUT_MS: Configure the UI proxy host capability.
  • WASM_POOL_* / EXT_CACHE_ROOT: Tune Wasmtime pooling and cache directories.

Gateway → Runner flow (summary)

  1. Client calls host /api/ext/{extensionId}/{...}.
  2. Gateway resolves tenant install → (version_id, content_hash, config, provider grants, sealed secret envelope).
  3. Gateway builds normalized request (currently without install_id, see plan A1) and POST /v1/execute to Runner with timeouts and service auth.
  4. Runner executes the handler in a sandbox and returns {status, headers, body_b64}.
  5. Gateway returns filtered headers/body to the client.

Observability

  • Structured logs per request with correlation IDs.
  • Metrics exposed by Runner:
    • Invocation duration, memory usage, fuel, egress bytes, error counts.
  • Live debug stream:
    • When RUNNER_DEBUG_REDIS_URL is set, stdout/stderr/log events are published to Redis Streams (ext-debug:{tenant}:{extension}) and consumed by /api/ext-debug/stream.
    • RUNNER_DEBUG_MAX_EVENT_BYTES truncates noisy messages; the UI shows a [truncated] marker.
  • Execution logs persisted via Registry or a logging backend keyed by tenant/extension.

Local development

  • Run Runner locally (Cargo) and point the host RUNNER_BASE_URL to it.
  • Use RUNNER_PUBLIC_BASE to serve UI assets directly from Runners static asset host.
  • During early development of UI, authors may serve assets via a local dev server, but production must use content-addressed assets hosted by Runner.

Error mapping (guidance)

  • 404: Unknown endpoint in manifest or missing asset (by content hash/path).
  • 413: Request/response size exceeded configured limits.
  • 502: Runner internal error or non-OK upstream.
  • 504: Timeout reached (Gateway or Runner).
  • Always include x-request-id and x-ext-* headers where appropriate.

Registry integration

  • The Runner relies on Registry to supply metadata: versions, manifests, content hashes, and signatures (publish/install workflows).
  • See ExtensionRegistryServiceV2 for service scaffolding used by the Gateway and Registry layer.