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

174 lines
9.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
- Runner project (Rust): [ee/runner/Cargo.toml](ee/runner/Cargo.toml)
- Iframe bootstrap and URL builder in host UI: [bootstrapIframe()](../../../server/src/lib/extensions/ui/iframeBridge.ts:45), [buildExtUiSrc()](../../../server/src/lib/extensions/ui/iframeBridge.ts:38)
- Registry v2 service (types and scaffold): [ExtensionRegistryServiceV2](../../server/src/lib/extensions/registry-v2.ts:48)
- Gateway handler (current implementation): [server/src/app/api/ext/[extensionId]/[[...path]]/route.ts](../../../server/src/app/api/ext/%5BextensionId%5D/%5B%5B...path%5D%5D/route.ts)
## 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](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):
```json
{
"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):
```json
{
"status": 200,
"headers": {
"content-type": "application/json",
"x-ext-request-id": "3d2c3f27-5b9c-4ad0-90f5-4c5ec8e844be"
},
"body_b64": "eyAic3RhdHVzIjogIm9rIiB9"
}
```
- The Gateway normalizes the inbound Next.js request, attaches install metadata, and forwards the envelope. See [server/src/app/api/ext/[extensionId]/[[...path]]/route.ts](../../../server/src/app/api/ext/%5BextensionId%5D/%5B%5B...path%5D%5D/route.ts).
### 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()](../../../server/src/lib/extensions/ui/iframeBridge.ts:38) to construct the iframe URL for the Runners public base and [bootstrapIframe()](../../../server/src/lib/extensions/ui/iframeBridge.ts:45) 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()](../../../server/src/lib/extensions/ui/iframeBridge.ts:45)).
- 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](../../server/src/lib/extensions/registry-v2.ts:48) for service scaffolding used by the Gateway and Registry layer.