Add cost-minimized prod eval infrastructure#81
Conversation
…-0027
Two coupled changes in one PR (per follow-up to audit + rename plan):
1. Project rename: bicep projectName default + scripts + workflows flip
from 'whatssummarize' to 'convolens'.
2. Resource naming convention migrates from the legacy
`<type>-<project>-<env>` (`rg-whatssummarize-dev`, `kvwhatssummarizedev`,
`cosmos-whatssummarize-dev`, etc.) to the NL Azure Naming Standard
with the region suffix dropped per mystira ADR-0027 — applied here
for cross-project consistency:
{org}-{env}-{project}-{type} (hyphens for most resources)
{org}{env}{project}{type} (alphanumeric only — Storage)
Region is now expressed by the resource group's `location` property
and the `region` resource tag.
Surfaces touched:
- `infra/bicep/main.bicep` — added `org` parameter (default `'nl'`,
allowed `nl|pvc|tws|mys`); replaced `resourcePrefix`/`resourcePrefixClean`
with `base`/`baseAlphanumeric`; rewrote every module's `name`
interpolation to the new pattern (`${base}-kv`, `${baseAlphanumeric}st`,
`${base}-oai`, `${base}-cosmos`, `${base}-redis`, `${base}-appi`,
`${base}-cae`, `${base}-api`, `${base}-swa`, `${base}-budget`).
Tags object grew an `org` field.
- `infra/parameters/{dev,staging,prod}.bicepparam` — `projectName` flipped
to `convolens`; `org = 'nl'` added explicitly; tags include `org`;
`adminEmail` defaults updated to `convolens.com` placeholders (note:
the convolens.* domain is **not yet registered** — these are inert
until the domain question is resolved, same as the previous
`whatssummarize.com` placeholders were).
- `infra/scripts/deploy.{sh,ps1}` — added `ORG`/`Org`, derived `BASE` /
`Base` and `BASE_ALPHANUMERIC` / `BaseAlphanumeric`; resource group
+ key-vault names rebuilt from `BASE`.
- `infra/scripts/validate-resources.{sh,ps1}` — same pattern; every
resource lookup retargets to the NL-pattern names.
- `.github/workflows/infrastructure.yml` — `AZURE_RESOURCE_GROUP_*`
env vars + dynamic `RG_NAME` use `nl-${env}-convolens-rg`.
- `.github/workflows/release-validation.yml` — full rebuild of resource
name construction inside the validate step (`BASE`, `${BASE}-kv`,
`${ORG}${ENV}${PROJECT}st`, etc.).
- `infra/README.md` — leading note rewritten to describe both renames;
Naming Convention section rewritten with the new pattern + 10
worked examples; Azure CLI examples (`az group create --name
nl-dev-convolens-rg`, `az keyvault set-policy --name
nl-dev-convolens-kv`) and post-deployment endpoints updated.
Cutover plan (separate manual ops, not in this PR):
1. Land this PR — code retargets; no Azure resources are touched.
2. Fix the AAD federated-credential subjects (`AADSTS700213` failure on
`Preview Changes (dev)` since 2025-12-07 — same bug as on omnipost
PR #128). See org-meta handoff
`2026-05-10-azure-rename-plan-omnipost-convolens.md`.
3. Run the dev infrastructure workflow → creates `nl-dev-convolens-rg`
and child resources cleanly.
4. Verify with `infra/scripts/validate-resources.sh dev`.
5. No old resources to decommission — convolens has nothing deployed
under any historical name (verified across all subscriptions).
Out of scope (deferred):
- `.github/settings.yml` repo `name` field — handled with the GitHub
repo rename, not this PR.
- `.github/whatssummarize-mcp-app.json` — webhook live/stale decision.
- `convolens.*` domain registration — open question 2 in the rename
audit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- release-validation.yml: fix CAE/CA names to use ${BASE}-cae and
${BASE}-api so they match what bicep deploys (nl-{env}-convolens-*);
align MISSING tags (container-app-api -> container-apps-api) with
the report-table 'container-apps' substring lookup.
- deploy.sh, infrastructure.yml: add org tag to RG creation;
standardise managedBy=bicep across all paths (workflow was using
managedBy=github-actions).
- deploy.sh, deploy.ps1: remove dead BASE_ALPHANUMERIC /
$BaseAlphanumeric variable (unused after storage name moved into
bicep template).
- validate-resources.sh: support COSMOS_DATABASE_NAME env override
so the Cosmos check no longer assumes databaseName == projectName.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the bicep tree (main + 9 modules + 3 .bicepparam + 4 deploy /
validate scripts) with an equivalent Terraform stack at
infra/terraform/env/dev/.
Why: bicep deployment in eastus repeatedly failed with ServiceUnavailable
on Cosmos DB AZ-redundant accounts despite the template requesting
non-AZ mode. Rather than chase a region-specific bicep workaround,
switch to the AzureRM Terraform pattern already used by sluice; easier
cross-project state hygiene and a single tool to teach.
Stack (env/dev/main.tf):
- RG, Log Analytics, App Insights, Storage Account + 3 blob containers,
Cosmos serverless + 3 SQL containers, Key Vault + 5 secrets,
Container Apps Environment + API Container App, Static Web App.
- Redis and OpenAI flag-gated (off by default in dev). Budget alerts
auto-skip when admin_email is empty.
- Naming follows ADR-0027: {org}-{env}-{project}-{type}, no region suffix.
- Region: eastus2 (healthier Cosmos capacity than eastus).
Remote state:
- nl-tfstate-rg / nltfstateconvolens / tfstate container.
- Key convolens-dev.tfstate. Default azurerm backend (access keys via
Contributor RBAC).
Auth (configured in tenant 9530cd32-9e33-47f0-9247-ed964730b580):
- AAD app convolens-sp (clientId 8f57a349-eb88-40fd-81eb-1065f84e668b).
- Federated cred subject: repo:neuralliquid/convolens:environment:dev.
- Sub-scoped Contributor for the SP on bb4e3882-...
- env-scoped AZURE_CREDENTIALS GitHub secret on environment dev.
Workflow:
- .github/workflows/infrastructure.yml rewritten: validate (fmt-check +
validate), plan (with PR comment), apply (push to main / dispatch),
verify.
- release-validation.yml: comment-only update referencing TF instead
of bicep.
Verified locally: terraform apply succeeded; all 23 resources up in
nl-dev-convolens-rg.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Terraform Plan (
|
Terraform Plan (
|
Terraform Plan (
|
There was a problem hiding this comment.
💡 Codex Review
convolens/.github/workflows/infrastructure.yml
Lines 25 to 27 in 4167de4
Selecting staging in a manually dispatched plan or apply expands the matrix to staging and runs terraform -chdir=infra/terraform/env/staging (.github/workflows/infrastructure.yml:126), but this commit creates only infra/terraform/env/dev and infra/terraform/env/prod. Thus every staging dispatch fails before Terraform initialization despite being offered by the workflow UI. Remove the option until an environment exists, or add the staging Terraform directory and backend.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| min_tls_version = "TLS1_2" | ||
| https_traffic_only_enabled = true | ||
| allow_nested_items_to_be_public = false | ||
| shared_access_key_enabled = false |
There was a problem hiding this comment.
Provide a usable Azure Blob authentication path
With AZURE_STORAGE_ACCOUNT_NAME set on the API, the application selects Azure Blob storage, but its configuration only accepts a connection string, account key, or SAS token (apps/api/src/config/azure/index.ts:163-178) and signs requests with SharedKey (apps/api/src/services/storage/storage.service.ts:576-629). This disables SharedKey while the Container App receives none of those credentials; the managed-identity role assignments are not consumed by that implementation. As a result, production chat-export uploads will receive storage authorization failures. Use an identity-capable Blob client/user-delegation SAS or supply a supported credential.
Useful? React with 👍 / 👎.
| name = "DB_TYPE" | ||
| value = var.enable_postgres ? "postgres" : "sqlite" |
There was a problem hiding this comment.
Ship the migrations needed by the PostgreSQL deployment
When enable_postgres is turned on, this selects the new PostgreSQL path, but the runtime still discovers migrations only from dist/db/migrations/*.js (apps/api/src/config/database.ts:18). The API Dockerfile builds only tsup src/index.ts and copies only /app/dist; src/db/migrations/1710806000000-UpdateEntities.ts is not imported by that entry point and is therefore absent from the image. Since production also disables synchronization, a newly provisioned PostgreSQL database has no tables and CRUD requests fail after deployment. Compile/copy migrations into dist or register the migration class directly.
Useful? React with 👍 / 👎.
Summary
Adds the first production Azure Terraform environment for Convolens, scoped as a cost-minimized internal evaluation apply rather than the durable production data shape.
Key defaults in
infra/terraform/env/prod:southafricanorthwithnl-prod-convolens-*namingenable_postgres = falseenable_container_registry = falseF1api_target_port = 80; set this to3001with the real API imageAlso wires the API TypeORM configuration so it can select SQLite or Postgres by env var and adds the
pgdriver for the later durable-data phase.Validation
terraform fmt -recursive infra/terraform/env/prodterraform validatepnpm --filter @convolens/api buildpassedterraform apply -auto-approve -input=falsecompleted for the eval profileterraform plan -refresh=false -input=false -compact-warningsreportsNo changesApply Results
Created eval resources in
nl-prod-convolens-rg.Live endpoints:
Both endpoints returned
200 OKafter the eval ingress correction.Outputs of note:
nl-prod-convolens-kvnlprodconvolensstingestionbaton-publishenable_postgres = falseenable_container_registry = falseApply Notes
This is an internal eval foundation. With current defaults it does not create PostgreSQL, ACR, or Redis, and the API uses the Azure helloworld placeholder image. Flip
enable_postgres = true, setapi_target_port = 3001, and select paid frontend/API sizing before treating this as durable production.