PSA/ee/docs/extension-system/knative-app-domains.md
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

5.4 KiB

Per-Extension App Domains (Knative) — Ops & RBAC

This document captures the operational pieces needed to support per-install application domains using Knative DomainMapping, provisioned by the Temporal worker.

Scope: We track changes here; the actual Helm/Kubernetes wiring is handled in the infra repo.

Environment Variables

  • Temporal worker

    • EXT_DOMAIN_ROOT: Root wildcard domain for extension apps (e.g., ext.example.com).
    • RUNNER_NAMESPACE: Kubernetes namespace where the Runner KService lives (defaults to default).
    • RUNNER_KSERVICE: Name of the Knative Service for the Runner (defaults to runner).
    • Standard Temporal env: TEMPORAL_ADDRESS, TEMPORAL_NAMESPACE, TEMPORAL_TASK_QUEUE.
  • Runner KService

    • BUNDLE_STORE_BASE: Base URL for bundle objects (e.g., http://minio:9000/alga-ext/).
    • REGISTRY_BASE_URL: Base URL of the EE server (used for host lookup + validation).
    • EXT_CACHE_MAX_BYTES: Optional cache/file size limit for static assets.
    • EXT_STATIC_STRICT_VALIDATION: true|false to enforce strict tenant/contentHash validation.
    • EXT_EGRESS_ALLOWLIST: Optional comma-separated host allowlist for guest HTTP egress.
    • S3/MinIO creds if needed by origin access (typically not needed if using HTTP gateway with public read): S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET.

DNS / Ingress

  • Configure wildcard DNS: *.${EXT_DOMAIN_ROOT} to Knative Ingress.
  • If not using wildcard, automate DNS records for each provisioned domain.

RBAC (Temporal Worker ServiceAccount)

Grant the Temporal worker permission to manage DomainMappings in the target namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: temporal-worker-knative
  namespace: ${RUNNER_NAMESPACE}
rules:
  - apiGroups: ["serving.knative.dev"]
    resources: ["domainmappings"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: temporal-worker-knative
  namespace: ${RUNNER_NAMESPACE}
subjects:
  - kind: ServiceAccount
    name: temporal-worker
    namespace: ${TEMPORAL_WORKER_NAMESPACE}
roleRef:
  kind: Role
  apiGroup: rbac.authorization.k8s.io
  name: temporal-worker-knative

Ensure the Temporal worker Deployment/Pod uses serviceAccountName: temporal-worker and set environment variables RUNNER_NAMESPACE, RUNNER_KSERVICE, EXT_DOMAIN_ROOT.

Temporal Activity Behavior

  • computeDomain(tenantId, extensionId, EXT_DOMAIN_ROOT) returns ${t8}--${e8}.${EXT_DOMAIN_ROOT} where:
    • t8 is the first 8 hex chars if tenantId looks like a UUID, otherwise first 12 slug chars
    • e8 is calculated similarly for extensionId
    • Rationale: keep metadata.name within Kubernetes 63-char limit for DomainMapping resources.
  • ensureDomainMapping({ domain, namespace, kservice }):
    • Preflight checks:
      • Verifies Knative Service exists: serving.knative.dev/v1, resource services, name ${kservice} in ${namespace}
      • Ensures ClusterDomainClaim exists for ${domain} (networking.internal.knative.dev/v1alpha1).
        • If env KNATIVE_AUTO_CREATE_CDC=true, the worker attempts to create the CDC.
        • Otherwise, it fails with a clear message including a ready-to-apply CDC manifest.
    • Creates or patches DomainMapping:
      • apiVersion: serving.knative.dev/v1beta1, kind: DomainMapping, metadata.name: ${domain}
      • spec.ref: { apiVersion: 'serving.knative.dev/v1', kind: 'Service', name: ${kservice} }
  • On success, status in DB is updated to { state: 'ready' } and runner_ref stores a small reference object.

Runner

  • Exposes GET / which reads Host → calls REGISTRY_BASE_URL/api/installs/lookup-by-host (API wrapper around the installs.lookupByHost server action) → redirects to /ext-ui/{extensionId}/{content_hash}/index.html.
  • Continues to enforce strict validation in /ext-ui route via /api/installs/validate.

Next.js (EE Server)

  • Server actions-first (business logic):
    • installs.lookupByHost(host: string){ tenant_id, extension_id, content_hash }.
    • installs.validate(tenant: string, extension: string, hash: string){ valid: boolean }.
    • installs.provisionDomain(installId: string) / installs.reprovision(installId: string) → triggers Temporal workflow.
  • Thin API wrappers for external consumers (delegate to actions only):
    • GET /api/installs/lookup-by-host?host=...
    • GET /api/installs/validate?tenant=...&extension=...&hash=...
    • POST /api/installs/:id/reprovision

Lookup and Validate Contracts

  • LookupByHost response: { tenant_id: string, extension_id: string, content_hash: string }.
  • Validate response: { valid: boolean }.

Helm Values Hints (Infra Repo)

  • temporal-worker chart:
    • serviceAccount.name: temporal-worker
    • env:
      • RUNNER_NAMESPACE: <ns>
      • RUNNER_KSERVICE: runner
      • EXT_DOMAIN_ROOT: ext.example.com
      • KNATIVE_AUTO_CREATE_CDC: "true" (optional; requires ClusterRole to manage ClusterDomainClaims)
    • RBAC templates include Role/RoleBinding above when enabled.

RBAC for ClusterDomainClaim (optional)

To auto-create ClusterDomainClaims, grant the Temporal worker a ClusterRole with read/create/patch on clusterdomainclaims.networking.internal.knative.dev and bind it via ClusterRoleBinding to the worker ServiceAccount.


Change history: Introduced with per-install app domains (Plan 1.f).