Appearance
BYOC on Azure
This brief lists the Azure resources, foundation-model deployments, network, and identity prerequisites your IT and cloud platform team should arrange so a Nomain BYOC stamp can be deployed into your own Azure subscription. It is the conversation-starter when scoping a proof-of-concept or production rollout.
For the AWS equivalent, see BYOC on AWS. The deployment pipeline itself is covered in Azure CI/CD, and the platform-wide security model in Security.
What is Nomain BYOC?
Nomain is an AI-native platform for understanding and modernizing mainframe estates (COBOL, PL/I, JCL, DB2). The BYOC (Bring Your Own Cloud) model runs the entire application stack inside your own Azure subscription: no Nomain-operated data plane, and no customer code or AST leaves your perimeter.
There are four outbound calls to Nomain infrastructure, all against the Nomain Customers API and all authenticated with an M2M ClientId / Secret issued at customer onboarding:
| # | Call | What it does |
|---|---|---|
| 1 | License validation | At App Service startup and at a configurable refresh interval. Returns a signed JWT carrying the contract tier and expiry. |
| 2 | Heartbeat | The webapp periodically posts a small liveness signal so Nomain can detect deployments that have stopped reporting and surface tenant state in the admin portal. It also carries an aggregate, content-free chat-health summary (counts, percentiles, and each tier's effective model/region; no chat content or PII). |
| 3 | Feedback | When an end user rates an AI response (thumbs up or down, free-text comment) or reports an issue from the webapp, the rating plus the redacted prompt category is posted back so the product team can detect regressions and improve the models. Opt-out at the tenant level. |
| 4 | Telemetry | Aggregated usage signals (job counts, query latencies, error-rate buckets by category) posted on a fixed cadence for product analytics and service-quality monitoring. Aggregates only: no per-user content, no source code, no AST. |
Data boundary
All four calls carry license, tenant, or aggregated-metric metadata only. No customer code, AST, embeddings, or chat content ever crosses this boundary.
Architecture at a glance
- One Azure subscription, one region (e.g.
swedencentral,westeurope,northeurope). - A single resource group holds the entire stamp.
- All resources land inside a dedicated VNet (default
10.10.0.0/16). No public PaaS endpoints: every PaaS service uses Private Endpoints routed through private DNS zones. - Your existing spoke VNet (DevOps subnet, user-access subnet) peers into the deployment VNet so internal users and Azure DevOps can reach the webapp and Key Vault privately.
- Six application services run as containers on Premium V3 App Service Plans: webapp, graphs-api, chats-api, recipes-api, recipes-worker, and llm-proxy, the in-stamp relay that serves the VS Code coding agent, so workspace code reaches the model without leaving your subscription.
- End-user authentication is delegated to your existing OIDC IdP via WorkOS AuthKit, or WorkOS Connect bridging Okta, Entra ID, Auth0, or Keycloak, with no app-level integration changes. See Single sign-on.
Architecture diagrams (solution, components, data flow, HA, network, integrations) are delivered alongside this brief as a separate diagram pack. Each diagram covers the same three-flavour shape (Azure-native, Azure Arc-enabled, vanilla on-prem K8s) so the comparison is direct.
Solution map
A logical view of the Azure resources Nomain deploys into your subscription and the outbound calls it makes. Every resource lives inside the customer-owned resource group and is reached over Private Endpoints; only the webapp is exposed externally, via the public ingress tier the customer chooses at deploy time.
The stamp groups into these logical tiers: compute (App Service), AI (Azure OpenAI), database (PostgreSQL), storage and registry, identity and Key Vault, messaging, and observability, plus the four outbound calls to Nomain.
Network topology
Every PaaS resource is fronted by a Private Endpoint, so no Nomain service has a public endpoint. The stamp VNet peers with your existing spoke VNet for internal user access and Azure DevOps deployment. The design combines VNet peering, App Service integration, private endpoints, private DNS zones, and controlled NAT-gateway egress. See AWS networking for the equivalent shape on AWS.
Azure resources provisioned
The Bicep stamp creates the following resources inside your subscription. Sizes below are a typical production starting point; smaller PoC sizings are available.
| Category | Resource | Typical SKU / size |
|---|---|---|
| Compute | App Service Plan, graphs-api | P2V3 PremiumV3, capacity 1 |
| Compute | App Service Plan, chats-api | P2V3 PremiumV3, capacity 1 |
| Compute | App Service Plan, recipes (api + worker) | P3V3 PremiumV3, capacity 1 |
| Compute | App Service Plan, webapp | P2V3 PremiumV3, capacity 1 |
| Compute | App Service Plan, llm-proxy | P2V3 PremiumV3, capacity 1 |
| Compute | App Service Plan, generic (status, ancillary) | S1 Standard, capacity 1 |
| Compute | App Services × 6 (Linux containers) | publicNetworkAccess=Disabled |
| Registry | Azure Container Registry (dedicated to the stamp) | Standard SKU, private endpoint |
| Database | Azure Database for PostgreSQL Flexible Server | Standard_D2ds_v4 GP, 128 GB, PG 16 |
| AI | Azure OpenAI account + AI Hub + AI Project | EntraId auth from each App Service |
| Storage | Storage Account (Blob + Queue) | Standard_GRS geo-redundant |
| Messaging | Event Grid Topic | Private endpoint |
| Secrets | Key Vault (RBAC mode) | Private endpoint |
| Identity | User-assigned managed identities (6, one per App Service) | Workload Identity scopes |
| Network | Virtual Network 10.10.0.0/16 + NAT Gateway | Subnets per tier |
| Network | Private DNS zones (8), privatelink.* | Auto-linked to VNet |
| Network | Private Endpoints (≈ 10), one per PaaS resource | One per PaaS resource |
| Observability | Log Analytics Workspace + diagnostic settings | Audit-table retention |
Backups: 14-day point-in-time PG, geo-redundant backups enabled. High availability (zone-redundant) is an opt-in switch with a per-environment cost delta.
Azure OpenAI models and quota
Nomain needs access to a small set of Azure OpenAI models. The exact deployment names in your Azure OpenAI resource (and the precise wiring of which deployment serves which application code path) are configured per environment at deploy time and ship with the operational runbook: you do not need to pre-create deployments yourself. What you do need to arrange with Microsoft is model approval and TPM quota in your target region.
Required model families
Capacities below are in thousand-tokens-per-minute (TPM) units as Azure surfaces them in the quota console. Each row shows a recommended range: pick a point in the range based on your expected estate size, except the coding-agent row, which is driven by how many developers use it at once rather than by the size of the estate.
| Model | Role in the platform | Recommended capacity |
|---|---|---|
| gpt-5.6-luna | Knowledge-graph code explanation, the single highest-volume analysis path | 1,000 - 4,000 |
| gpt-5-mini | Bulk analysis paths: code parsing, unused-code-detection inspector, chat context compaction | 1,000 - 4,000 |
| gpt-5.4-mini | Mid-tier analysis and chat sub-agent search (where heavier reasoning than gpt-5-mini is required) | 1,000 - 4,000 |
| gpt-5.4 | Chat answering (QuickSearch and DeepSearch), query preprocessing, process extraction, fallback for analysis paths | 1,000 - 4,000 |
| gpt-5.6-sol | VS Code coding agent, served by the in-stamp LLM proxy, the one bursting interactive path (see the sizing note below) | 1,000 - 8,000 |
| gpt-5-nano | Lightweight utility: chat-title generation | 150 - 500 |
| text-embedding-3-large | Primary embeddings for the knowledge graph (3,072 dimensions) | 500 - 4,000 |
| text-embedding-3-small | Clustering embeddings (lower-dim, lower-cost) | 250 - 500 |
| Total TPM quota required | ~ 5,900 - 29,000 |
Size the explanation model first. Code explanation dominates analysis token spend, so gpt-5.6-luna is the row where an under-estimate is felt first: it throttles the initial analysis of an estate, which is the longest-running job a new deployment performs. The other analysis rows are comparatively forgiving.
Choosing a point in the range. The lower bound is enough for evaluation, PoCs, and small estates. The upper bound is what a production deployment serving an estate in a risk-sensitive environment typically needs. Customers running smaller workloads can also collapse gpt-5-mini and gpt-5.4-mini into a single model (the platform supports re-mapping the affected code paths to either one via configuration), which drops one model approval and a slice of TPM quota. gpt-5.6-luna is not a candidate for that collapse; the explanation path is deliberately pinned to it.
Sizing the coding agent
The coding agent is the only path on this page that bursts. An agent turn is a loop of model calls, each carrying the whole conversation so far, several per minute while a developer works. Everything else is either a batch job (paced by the app-side ceilings below) or a single chat answer.
Size it from the peak, not the average:
TPM ≥ users who may be mid-turn in the same minute × what one bursting user costs in their busiest minute
Measured on Nomain's own development stamp over 30 days of real use: a call carries a median of 33,000 tokens and a 95th-percentile of 108,000 after the client's compaction, and a developer at work makes 5 - 7 calls a minute. A single bursting user cost 62,000 TPM in a median busy minute, 280,000 at the 95th percentile and 366,000 at peak. Plan on roughly 300,000 TPM per concurrently bursting user.
Do not reach that second factor by multiplying the per-call size by the call rate. The two are anti-correlated (a call carrying 108,000 tokens takes longer to serve than one carrying 5,000, so the largest contexts and the fastest rates never coincide), and the multiplication overshoots badly: 108,000 × 7 suggests 756,000 against a measured peak of 366,000. Use a measured per-minute figure, ideally re-measured on your own stamp.
An average would mislead badly here: of those turns, 44 cost under 5,000 tokens (a fresh session asking a couple of questions) while the largest cost 140,000. The same developer is 30× cheaper in their first minute than in their thirtieth.
| Seats using the coding agent | Assumed bursting at once | Recommended capacity |
|---|---|---|
| up to 5 | 2 | 1,000 |
| 10 | 3 | 1,000 |
| 25 | 7 | 2,000 - 2,500 |
| 50 | 13 | 4,000 |
| 100 | 25 | 8,000 |
The middle column assumes a third of seats bursting together up to 25 seats and a quarter above that. Replace it with your own figure after a week of real use: every agent turn records its token consumption and the quota the deployment reported it had left, in the stamp's own Log Analytics workspace. Where the stamp is configured with alert recipients, sustained throttling also raises a "coding agent throttled" alert.
Three things that surprise people sizing this:
- Rate limiting admits on the estimated full prompt, not on what you are billed for.
StandardandGlobalStandarddeployments evaluate a request against the whole prompt they estimate it will send. Prompt caching cuts the bill substantially (in the same 30-day measurement, a typical agent call re-read ~109,000 cached tokens alongside ~14,000 new ones) but it does not reduce what the rate limiter counted. Size on the full context, and never read a low billed-token figure as spare headroom. - Give it its own deployment where you can. If the coding agent shares the chat deployment (
gpt-5.4), its bursts land on that row and compete with chat answers, which a user is sitting and waiting for. Wheregpt-5.6-solis available in the region, a dedicated deployment keeps the two apart. - Sustained load can leave
Standardaltogether.gpt-5.6-solships asGlobalProvisionedManaged, so a stamp with steady coding-agent use can buy guaranteed throughput instead of repeatedly raising a per-minute ceiling.
Per-service rate ceilings (app-side throttles)
Separate from the Azure quota above, the application enforces its own rate-limiter ceilings per service path so a runaway job cannot burst all token budget at once. The numbers below are an illustrative production-grade configuration; your deployment tunes them per environment.
| Service path | Max concurrent | Requests / min | Tokens / min |
|---|---|---|---|
| graphs-api LlmService | 390 | 3,900 | 3,900,000 |
| graphs-api AiParser | 16 | 3,900 | 3,900,000 |
| graphs-api UnusedCodeDetection | 14 | 3,900 | 4,000,000 |
| recipes-worker LlmService | 390 | 3,900 | 3,900,000 |
| recipes-worker AiParser | 80 | 3,900 | 3,900,000 |
| recipes-worker UnusedCodeDetection | 14 | 3,900 | 4,000,000 |
Quota request to file with Microsoft
Request an Azure OpenAI resource in your target region with TPM quota in the 5,900 - 29,000 range (sum across the model families above), sized to your expected estate. DataZoneStandard SKU is recommended when available; Standard SKU also works if the region has constraints. Verify availability of each model in the region before committing.
Prerequisites you must provide
| # | Item | Detail |
|---|---|---|
| 1 | Azure subscription with Owner (or Contributor + User Access Administrator) on the target RG | Needed for the initial Bicep deploy of role assignments and private DNS zones. After bootstrap a scoped service principal is sufficient. |
| 2 | Target region | Must support Premium V3 App Service Plans, PostgreSQL Flexible Server, and every Azure OpenAI model in the models section. Common picks: swedencentral, westeurope, northeurope. |
| 3 | Spoke VNet with user-access and deployment subnets | Peered to the stamp VNet 10.10.0.0/16. userAccessSubnet reaches the webapp + Key Vault; deploymentSubnet reaches App Service SCM for Azure DevOps. |
| 4 | DNS zone control over the environment domain | Customer creates A / CNAME records to the App Service custom-domain endpoint at deploy time. |
| 5 | Azure OpenAI quota (5,900 - 29,000 TPM range) | Request approval for the model families listed in the models section and TPM quota sized to your expected estate. Submit via Azure Portal, Quotas, Cognitive Services. Allow time for review on first-time requests in the region. |
| 6 | Entra ID app registrations | WorkOS AuthKit client + Nomain-issued M2M client credentials. |
| 7 | Customer IdP (optional but typical) | Okta / Entra ID / Auth0 / Keycloak wired through WorkOS Connect. |
| 8 | Subscription policy review | Confirm no Microsoft.Web/sites deny policies on the target RG that would conflict with the BYOC posture (publicNetworkAccess=Disabled is required and customer MG policies should permit it). |
| 9 | Cost-management budget | See the cost line items below. |
Identity and access
Inside the stamp
- No static credentials. Each App Service runs under a user-assigned managed identity bound to Azure OpenAI, Storage Blob, Key Vault Secrets User, and PostgreSQL (Entra-AD authentication mode).
- App-to-app authentication uses Workload Identity tokens: short-lived, automatically rotated.
- PostgreSQL admin password exists only so the first-deploy schema migration can create per-service Postgres roles; application services authenticate with Entra-AD afterwards.
End-user authentication
- WorkOS AuthKit for end-user OIDC sign-in.
- WorkOS Connect bridges your existing IdP (Okta, Entra ID, Auth0, Keycloak) without app-level changes: your users keep their normal SSO experience. See Single sign-on.
- JWTs are validated locally inside the webapp BFF and each API; no per-request callback to WorkOS.
- Outbound M2M credentials issued by Nomain at onboarding cover all four calls: license validation, webapp heartbeat, end-user feedback, and aggregated telemetry.
Security and compliance
The BYOC posture is built around three properties: your data never leaves your Azure subscription, no Nomain operator has runtime access to the stamp, and every cross-service call uses short-lived identity tokens rather than static credentials.
Defense-in-depth controls
| Layer | What's in place |
|---|---|
| Network isolation | All PaaS resources reachable only over Private Endpoints: no public endpoints. App Services run with publicNetworkAccess=Disabled. VNet integration on the Linux App Service Plan keeps East-West service-to-service traffic inside the customer's VNet. Outbound egress through a single NAT Gateway with a predictable static public IP for upstream firewall allow-lists. |
| Encryption in transit | TLS 1.2+ enforced on every ingress and outbound call. PostgreSQL connection string uses sslmode=require. Workload Identity / Entra-AD tokens validated on every cross-service call. |
| Encryption at rest | All Azure-managed services encrypt at rest by default with Microsoft-managed keys. Geo-redundant backups (Postgres + Storage GRS) ride the same encryption boundary. |
| Identity | Six user-assigned managed identities, one per App Service, bound at deploy time to the resources each service is allowed to touch (AOAI, Storage Blob, Key Vault Secrets User, PostgreSQL via Entra-AD). Zero static credentials live inside container images or app configuration. Workload Identity tokens are short-lived and rotated by the platform. |
| Secret management | Key Vault is the single source of truth for runtime secrets (Nomain-issued M2M ClientId/Secret, TLS certificate, etc.). App Services read secrets through Key Vault references: no secret value ever lands in App Service configuration logs. |
| End-user authentication | WorkOS AuthKit for OIDC, with WorkOS Connect bridging your existing IdP (Okta / Entra ID / Auth0 / Keycloak) without app-level changes. JWTs validated locally inside the webapp BFF and each API. |
| Tenant data isolation | Within a single BYOC stamp, tenant data is scoped through per-service PostgreSQL roles (nomain_chats, nomain_graphs) and row-level filters on tenant ID. Cross-tenant queries are forbidden at the data layer. For full physical isolation, deploy one stamp per tenant. |
| Audit logging | Tenant lifecycle events, admin actions, authentication events, and data-deletion events emit through a dedicated IAuditLogger path that lands in audit-category Log Analytics tables retained for 12 months (separate from the 90-day default retention for operational logs). |
| Diagnostic + activity logs | Every Azure resource emits diagnostic logs into the stamp's Log Analytics workspace. Subscription Activity Log is captured. Alerts route to a customer-specified email recipient list (your Ops alongside Nomain Ops if you want the same view). |
| Vulnerability management | Container images are scanned for CVEs at build time (Trivy). Microsoft Defender for Cloud Standard tier is supported on the App Service Plans, Key Vault, and Storage Account: recommended but not auto-enabled (your subscription policy decides). |
| Outbound dataflow | The only outbound calls to Nomain infrastructure are license validation, the webapp heartbeat, end-user feedback, and aggregated telemetry, all carrying license, tenant, or aggregated-metric metadata only, and no customer code, AST, embeddings, or chat content. |
What Nomain does not have access to
Customer-owned perimeter
- No standing operator credentials into your subscription. Initial deployment uses a service principal owned by your tenant.
- No runtime read or write access to your data plane (PostgreSQL, Storage, Key Vault). Nomain support engineers receive logs and traces only on customer request, through a controlled diagnostics export.
- No telemetry of your source code, AST, embeddings, or chat content back to Nomain.
Compliance alignment
| Area | Property | How it holds |
|---|---|---|
| Data residency | Single region | Every byte (PostgreSQL, Storage, AOAI inference) stays in the Azure region you pick at deploy time. EU customers commonly choose swedencentral or westeurope. |
| DORA / ICT third-party risk | Customer-controlled data plane | Nomain is not a critical ICT service provider for the data plane: it lives in your subscription. The four Customers API calls (license, heartbeat, feedback, telemetry) are the only Nomain runtime dependency to model. |
| GDPR / privacy | No PII processed by Nomain | Customer authentication is delegated to your IdP. Nomain stores no end-user PII on its side; tenant-level metadata only flows back via the M2M channel. |
| ISO 27001 / SOC 2 | Audit trail in your tenant | 12-month retention on audit-category Log Analytics tables. Customer SIEM ingestion supported via diagnostic-settings export. |
Cost line items
These are the variable-cost categories you should ask your Azure account team to model. Exact numbers depend on your EA / CSP pricing and your estate size.
| Line | Driver |
|---|---|
| App Service Plans (P2V3 × 4, P3V3 × 1, S1 × 1) | Compute fixed cost: biggest non-AI line |
PostgreSQL Flexible Server (Standard_D2ds_v4 + 128 GB + geo-redundant backup) | Single-AZ by default; HA option roughly doubles compute |
| Azure OpenAI tokens | Dominant variable cost, driven by ingestion + chat volume |
| Azure Container Registry (Standard SKU) | Negligible; small private registry |
| Storage Account (GRS) | GB-month + transactions |
| Private Endpoints + NAT Gateway + private DNS zones | Small fixed networking cost |
| Key Vault + secret operations | Negligible |
| Log Analytics ingestion + retention | Modest; tunable via retention parameters |
PoC kick-off checklist
When green-lighting a Nomain proof-of-concept on Azure, confirm the following are in place before deployment day:
- Subscription and region. Subscription with admin access; target region selected and reviewed against model availability.
- Azure OpenAI quota. Model approval + TPM quota in your target region, sized in the 5,900 - 29,000 range. Submit the quota request early.
- Network. Spoke VNet with non-overlapping CIDR; user-access and deployment subnets ready for peering.
- DNS and TLS. Zone control over the environment domain; certificate strategy agreed (Key Vault integration with managed certificate or customer-provided).
- Identity provider. WorkOS workspace provisioned (Nomain side); WorkOS Connect to your IdP scoped if you want SSO from day one.
- Secrets and credentials. Key Vault ready to receive the Nomain-issued M2M credentials (used for license, heartbeat, feedback, telemetry) at first deploy.
- Cost budget. Budget alerts set against the subscription; AOAI token spend modelled separately.
For questions, contact your Nomain customer success representative.