Install & infrastructure
Provision the Azure side.
For: operators and platform engineers
Cardinal's Azure footprint comes in two parts: a shared substrate stood up once for the whole programme, and a thin per-engagement slice created each time you onboard a customer. This page is the provisioning recipe for both — see Infrastructure for the reference catalogue of what each resource is.
The simple path
Setting up your own environment? Install Scout — a desktop app that runs AI models locally and can drive your tools — sign in to the Azure subscription you want to use, and give it the prompt below. Scout provisions the shared substrate, then onboards your first engagement, pausing for you to review before anything is applied.
Paste this to Scout:
You are provisioning Cardinal's Azure footprint. I am NOT using a shared environment — set up my own. Work in two parts, always dry-run before you apply, and stop for my explicit approval at each apply. Part 1 — shared substrate (once): deploy the infrastructure-as-code (Bicep) template into a resource group. Ask me for an application name and an Azure region, then derive every resource name from them. It provisions a Postgres flexible server, a Key Vault, a storage account for the corpus mirror, an Azure OpenAI endpoint for embeddings, Application Insights + Log Analytics, and a user-assigned managed identity with its role assignments. Use keyless, managed-identity access throughout — never provision a database password. Part 2 — onboard one engagement: run the onboarding ceremony to carve out a per-engagement slice on top of the substrate — a dedicated schema, three Postgres roles (read, app, owner), engagement-scoped Key Vault secrets, the schema objects replayed from canonical SQL, and a GENESIS audit row. Ask me for the allow-list of users who may sign in. It is idempotent and reversible: on failure, resume or roll back — never leave a half-built engagement. Gates: run all three independent gates — secret scan, kernel-purity scan, config validator — and report each. They always all run. Do not mark onboarding complete until all three are green; then stop and ask me to ratify. Finish with a summary of what was provisioned and the exact next step (deploy the app, or bootstrap the ring).
The shape of an Azure setup
Provision the shared substrate first; it is engagement-agnostic and only happens once. Then, for each customer, run the onboarding ceremony to carve out their slice. Customer identity exists only in that per-engagement slice — the substrate beneath it stays neutral.
flowchart TB BICEP(["📜 Infrastructure-as-code
az deployment sub create"]):::iac subgraph P1 ["☁️ PHASE 1 — Shared substrate (once per programme)"] direction LR PG[("🐘 Postgres")]:::azure KV["🔐 Key Vault"]:::azure ST["📦 Storage"]:::azure OAI["🧠 Azure OpenAI"]:::azure OBS["📊 App Insights
+ Log Analytics"]:::azure UAMI["🆔 Managed Identity"]:::azure end CEREMONY(["🎯 Onboarding ceremony
per customer"]):::cere subgraph P2 ["🧩 PHASE 2 — Per-engagement slice"] direction LR SCHEMA[("📂 Schema
+ 3 PG roles")]:::engagement SECRETS["🗝️ KV secrets"]:::engagement DDL[("📜 SP/view replay")]:::engagement end GENESIS{{"🏁 GENESIS audit row
chain seq = 1"}}:::genesis APP["🌐 Cardinal app
SWA + Functions"]:::app BICEP ==> P1 BICEP ==> APP P1 ==> CEREMONY CEREMONY ==> P2 P2 ==> GENESIS style P1 fill:#f0f7ff,stroke:#0078d4,stroke-width:2px,color:#003966 style P2 fill:#f0fbf8,stroke:#0d9488,stroke-width:2px,color:#134e4a classDef iac fill:#fef3c7,stroke:#d97706,stroke-width:2.5px,color:#78350f classDef cere fill:#fce4ec,stroke:#b11f4b,stroke-width:2.5px,color:#7a1132 classDef azure fill:#dbeafe,stroke:#0078d4,stroke-width:2px,color:#003966 classDef engagement fill:#ccfbf1,stroke:#0d9488,stroke-width:2px,color:#134e4a classDef genesis fill:#fce4ec,stroke:#b11f4b,stroke-width:3px,color:#7a1132 classDef app fill:#ede9fe,stroke:#7c3aed,stroke-width:2px,color:#4c1d95
1 · Stand up the shared substrate
The shared substrate is deployed from infrastructure-as-code (Bicep) into a resource group. It is provisioned once and reused by every engagement. The template takes a small number of parameters and derives every resource name from them.
What the template provisions
- A Postgres flexible server (the system of record)
- A Key Vault for programme secrets and signing keys
- A storage account for the corpus mirror
- An Azure OpenAI endpoint for embeddings
- Application Insights + a Log Analytics workspace
- A user-assigned managed identity + its role assignments
Parameters, not hard-coding
The template derives names from a small set of parameters — an application name and a region — using a consistent pattern:
swa-<app>-<region>— the Static Web Appfunc-<app>-api-<region>— the Function Appkv-<orchestrator>-<region>— the Key Vault
Set the parameters for your deployment; the names follow.
2 · Onboard an engagement
Onboarding a customer is a single, checkpointed ceremony. It provisions the per-engagement slice on top of the shared substrate, replays the canonical database objects, seeds the audit chain, and stops for the operator to ratify before going live. It is idempotent and reversible — a failure resumes or rolls back, it does not leave a half-built engagement.
What it provisions
- A dedicated database schema for the engagement
- Three Postgres roles (read, app, owner)
- A set of engagement-scoped Key Vault secrets
- The schema's tables, stored procedures and triggers (replayed from canonical SQL)
- A GENESIS audit row that seeds the tamper-evident chain
The ceremony, step by step
- Dry-run — the operator reviews exactly what will be created
- Apply — an explicit, separate step provisions the resources
- Replay — canonical SQL builds the schema objects
- Seed — the GENESIS row is written; the chain begins
- Ratify — the operator signs off; the engagement flips mobilising → live
user@example.com) controls who can later sign into the app for
that engagement. The shared substrate beneath stays engagement-neutral.
3 · The gates
Onboarding does not complete on a green deployment alone. Three independent checks must all pass — and they always all run, so one failure never hides another. Until they are green, the engagement stays in mobilising and the operator must remediate and re-run.
Secret scan
Confirms no secret material leaked into any artifact — skills, briefs, configs, or the audit columns.
Kernel-purity scan
Confirms the engagement-agnostic "kernel" carries no customer-specific values — that lives only in the pack overlay.
Config validator
Validates the layered configuration end-to-end and probes that the database, Key Vault, and tool servers are reachable.
4 · Deploy the app
Cardinal's front end and back end deploy independently onto the resources the substrate template created. Both write into the same database through the same stored procedures as the agents.
Front end (SPA)
- Build the React SPA
- Publish it to the Static Web App
- Routing + auth come from the app's static configuration
Back end (API)
- Build and bundle the tRPC server
- Publish it to the Function App (Flex Consumption)
- The Static Web App reaches it through a linked backend