Skip to Content
Design DecisionsTS Engine for the Solution Plane

TypeScript is canonical for the solution plane

Status: Accepted · Date: 2026-07-05 · Area: Deployment

Context

Per-solution provisioning existed as two deliberately maintained twins: the bash infrastructure/azure/provision-solution.sh (declared source of truth) and the in-product deployOrbitStack() TypeScript path the Aurora agent chat used. Only the naming helper was byte-verified between them; everything else had to be kept in sync by hand, and the drift was no longer hypothetical:

  • the share-quota constant diverged (TS 5 GiB vs bash 100 GiB) and failed a live deploy with an opaque ARM InvalidHeaderValue error;
  • the TS path was Cloudflare-blind — every agent-chat deploy produced a stack without a front door, requiring a manual deploy.sh provision <id> chaser;
  • the registry write-back guessed the public URLs by string interpolation from the zone name, recording links no one had verified existed.

Meanwhile the intended operating model is the Aurora agent as the primary operator — which needs partial operations (fix just the front door, re-check just storage, roll just the apps), structured results the model cannot misparaphrase, and a toolset far wider than one monolithic “deploy” verb. The bash monolith could not be reached from the aurora container (no az, no scripts, no config file), and its complexity existed largely to work around an az-CLI defect (create --yaml silently drops identity/registries/secrets/ scale) that the ARM SDK simply does not have.

Decision

  • One engine, in TypeScript: implementation/shared/orbit-deploy (@maxq/orbit-deploy, export ./azure) owns the solution plane. It is a catalog of idempotent steps, each runnable in three modes (plan = read-only diff, apply = reconcile, verify = read-only assertions), composed into scenarios: deploy, redeploy-apps, frontdoor, storage, verify, teardown. Every step returns a structured StepResult (status, detail, evidence, remediation) and every run emits a RunLog persisted on the registry solution record (deployment.lastRun).
  • Three drivers share it: the Aurora agent tools (ten of them, from solution_status to double-confirmed teardown_solution), the ops CLI (deploy.sh solution <id> [scenario] → the package’s cli), and the createSolution auto-deploy hook.
  • Bash keeps the platform plane — the human-run singletons: bootstrap, postgres, services, aurora, and build-images.sh. Image building stays human (commit → tag → build-images.sh); the engine’s preflight.images step only validates that the pinned tags exist in ACR and stops the deploy when they don’t.
  • The engine ports the live-proven front-door recipe (asuid TXT → grey-cloud → hostname add → managed cert → SniEnabled bind → re-proxy) and the edge lockdown transform rule to TypeScript, closing the Cloudflare gap; the seed job becomes conditional (skipped when both shares already carry a .git clone, forceable via reseed).

Consequences

  • provision-solution.sh is legacy — kept only until the engine’s front door is live-proven on a real solution, then deleted. The naming scheme’s bash/TS parity is enforced by a script gate (orbit-deploy/scripts/parity-names.sh) rather than by discipline.
  • Agent-chat deploys now produce the same stack as the ops path — front door, edge lockdown, verified URLs in the registry — and the agent can run partial scenarios instead of full redeploys.
  • The aurora app’s env contract grew (Cloudflare token/zone, edge flag, per-solution agent secrets, the previously-missing Mission Control image tag), rendered by provision-aurora.sh; the aurora image build context moved to implementation/ for the shared-package file: dependency.
  • The solution-service schema gained deployment.lastRun — live registries need the rebuilt solution-service image before run logs persist in production.
  • Destructive teardown is exposed to the chat agent, but double-gated: confirm: true and a confirmId that must equal the solution id (enforced in the tool body and in canUseTool), with WorkOS user auth as the immediate follow-on.

Evidence

  • implementation/shared/orbit-deploy/ — the engine (steps, scenarios, runner, Cloudflare client, status, diagnostics, CLI, parity gate)
  • implementation/aurora-webapp/codebase/src/lib/solutions/deploy-azure.ts — now a thin adapter; src/lib/agent/deploy-tools.ts — the tool surface
  • infrastructure/azure/deploy.sh (solution subcommand) and provision-aurora.sh / app-aurora.yaml.tmpl (the env contract)
  • memory/orbit-deploy-engine.md — the decision record