For technical evaluators
The technical posture, in plain English.
The architectural decisions, the security model, the AI governance, the compliance posture — and the honest gaps. Written for CTOs, IT directors, MSP partners considering reselling, and technical evaluators at procurement. Read it before you ask us; bring the questions it doesn’t cover.
The stack
The stack — no surprises.
Microsoft-native by binding rule. Every layer is a deliberate, defensible choice.
| Layer | What we use | Why this choice |
|---|---|---|
| Identity | Microsoft Entra ID + MSAL | Enterprise SSO; MFA and conditional access through the same directory the client already operates. Multi-portal app registrations under a single API audience. |
| Application | C# / .NET (LTS releases only) | Long-term-supported runtimes; large talent pool; first-class Azure SDK integration. |
| Frontend | TypeScript (strict) + React + Vite + Tailwind | TypeScript types generated from the OpenAPI spec — no hand-written DTOs. Strict mode mandatory. |
| Data access | Raw ADO.NET against Azure SQL | No ORM. Query behaviour stays explicit and predictable. (Reasoned in the FAQ below.) |
| Hosting | Azure App Service (Linux) + Azure Front Door | Auto-scale, slot-swap zero-downtime deployment, WAF + DDoS at the edge. Static Web Apps is never used. |
| AI | Azure OpenAI (managed identity) + Azure AI Document Intelligence | Runs in the customer tenant. No API keys. No training on customer data. |
| Comms | Azure Communication Services (email / SMS / push) | Native to Azure; no third-party messaging providers in the data path. |
| DevOps | Azure DevOps Repos + Bicep IaC | Source and infrastructure-as-code in one place; audit trail; pipeline gates. |
| Storage | Azure Blob (SAS-token, managed identity) | No file uploads through the API; short-lived SAS tokens for direct browser uploads. |
| Background work | Hosted services + Azure Functions | Right tool for the cadence; queue-triggered Functions for scale-out work. |
Why no ORM?
Direct SQL keeps query behaviour explicit and predictable, eliminates the "what query did the ORM just run?" surprise class, and works with stored procedures and complex joins without object-relational impedance. We accept the boilerplate cost for behaviour transparency.
Why no Azure Static Web Apps?
Static Web Apps is a separate product with different deployment, identity, and runtime characteristics from App Service. Standardising on App Service across both portals keeps the operational surface uniform.
Why .NET, not Node for the backend?
Long-term support, strong typing, mature data-access tooling, and easier to staff at the seniority level the work needs.
Portal separation
Three portals, separated by deployment.
Admin Portal
Internal staff, behind your Entra ID. The full operational surface.
Client Portal
External customers. Self-service, document access, status tracking, online payments. Public marketing pages and authenticated client pages co-located in one Vite SPA.
Employee Self-Service
Staff self-service — leave, timesheets, expenses, payslips, profile updates.
Each portal is a separate Vite project with its own build, deployment, URL, and Entra ID App Registration. They share the backend API and shared workspace packages — @yourorg/ui and @yourorg/api-client — but not deployments.
Combining client-facing and internal functionality into a single deployable frontend is a binding architectural defect under our framework. The API is the security boundary; the portals are the audience boundary. Mixing them is what causes admin-tools-leaked-to-clients incidents in the wild.
Security model
Security, in layers.
- Authentication
- Microsoft Entra ID, OAuth 2.0 / OpenID Connect via MSAL.js on the frontend; JWT bearer-token validation on every API request (issuer, audience, expiration, signature). The API accepts tokens from each portal’s app registration via configured trusted issuers.
- Authorisation
- Role-based access control via Entra ID App Roles. Roles arrive as claims in the JWT; the API enforces with [Authorize(Roles = "…")] or policy-based authorisation. Frontend role checks are UX-only — the API is the enforcement boundary.
- CORS
- Allowed origins listed explicitly, per environment, per portal. AllowAnyOrigin() is never used in staging or production.
- HTTP headers
- HSTS, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, a tailored Content-Security-Policy (no unsafe-inline without justification), Referrer-Policy: strict-origin-when-cross-origin, minimised Permissions-Policy, Cache-Control: no-store on authenticated API responses.
- Rate limiting
- ASP.NET Core rate-limiting middleware; tiered per endpoint category (public 30/min, authenticated 100/min, admin 200/min, auth endpoints 5 per 5 minutes); 429 responses carry Retry-After.
- Managed identity
- Every Azure-internal hop — App Service to SQL, Storage, Key Vault, Cache, Service Bus, App Configuration — uses system-assigned managed identity in staging and production. No API keys stored anywhere except Key Vault.
- Secrets
- All third-party secrets via Azure Key Vault references in App Service configuration. Never in source, environment variables, or app-settings literals.
- Audit logging
- Security-sensitive operations write to a dedicated, append-only audit-log table (separate from operational logs) with a minimum 12-month retention.
- WAF / DDoS
- Azure Front Door Standard with a WAF policy in prevention mode (not detection) is mandatory for every production deployment.
Data
Every field has a tier.
| Tier | Examples | Handling |
|---|---|---|
| 1 — Public | Marketing content, published help | No restrictions. |
| 2 — Internal | Operational metrics, dashboards | Authenticated access; encryption at rest via Azure SQL TDE. |
| 3 — Confidential | Customer names, emails, business data, invoices | Authorised roles only; encrypted at rest; access logged. |
| 4 — Restricted | Passwords, tokens, financial account numbers, health data, gov IDs | Field-level encryption via Key Vault; access logged and alerted; never stored if avoidable. |
PII handling
Every spec classifies fields. A binding rule requires the AI to flag suspected PII and confirm classification before generating code. PII never appears in URLs, logs, error messages, cache keys, or test data.
Optimistic concurrency
Every mutable table carries a ROWVERSION column; updates filter on it and conflicts return 409 with an RFC 7807 problem-details body. No silent last-write-wins.
Soft delete + retention
A standard audit-column set on every table. Soft-deleted records are purged after a defined retention period (default 90 days; longer where a sector regulates it).
Right of erasure
Per-user data export (machine-readable) and anonymisation/purge are supported. Each PII-bearing table is tracked so an erasure request can be fulfilled across the whole dataset.
The AI model
AI bound by enforceable rules.
Your platform’s runtime AI runs under enforceable controls — read-versus-mutate separation, tier limits with autonomous action prohibited, per-invocation audit, and user-facing labelling, backed in code. The refusal and anti-hallucination discipline is what the delivery tooling is built under — upstream of what ships.
- Read vs mutate, by hard rule
- The AI may invoke read-only functions freely. Mutating functions (create, update, delete, send a communication) require a human approval step — the AI prepares the action, a human reviews and approves, and the application (not the AI) executes the mutation.
- Tier classification
- Every AI feature is classified Tier 1 (minimal), Tier 2 (limited — AI drafts for human review), or Tier 3 (high — affects user-facing experience). Tier 4 (autonomous action affecting users, transactions, or external parties) is prohibited and rejected at spec time.
- Audit logging
- Every AI invocation produces an audit record: agent version, input summary (PII sanitised), output summary, human-review outcome, timestamp, acting user.
- Transparency to end users
- Tier 2 and Tier 3 outputs are clearly labelled in the UI; Tier 3 includes an opt-out mechanism.
- Built to refuse non-compliance
- The AI delivery tooling — the assistants that build your platform — is bound to refuse non-compliant output rather than ship it. This is the discipline your platform is built under, upstream of what runs.
- Hallucination is a defect
- Invented APIs, SDK methods, platform capabilities, or fields that don’t exist are treated as a build defect: the delivery tooling states the limitation rather than guessing. A build-time discipline, distinct from the runtime controls above.
Models run on Azure OpenAI in the customer’s tenant via managed identity. Inputs and outputs never leave the customer’s Azure compliance boundary, and are stored in Australia for Australian clients. Foundation models only — no fine-tuning unless explicitly contracted with a separate ADR for data sources and bias evaluation.
CI/CD
The pipeline that gets in the way — in a good way.
Every PR to main runs
- Build (warnings-as-errors)
- Lint (Roslyn analysers for C#, ESLint strict for TypeScript)
- Unit tests (xUnit + Vitest + jest-axe)
- Contract tests (generated TypeScript types must match the OpenAPI spec)
- Type-generation staleness check (regenerate; fail if uncommitted)
- Accessibility audit (axe-core; critical or serious violations fail the build)
- Security scans (SAST, dependency vulnerability scan, secret scanning)
- Frontend pre-push: pnpm build, tsc --noEmit, eslint --max-warnings 0
On merge
- Integration tests against a real test database
- Build artefacts for backend, admin portal, and client portal separately
- Auto-deploy to Dev, then smoke tests
- Staging requires dev-manager approval; Production requires client approval (24-hour expiry)
- Each component (API, admin, client) deploys and rolls back independently
Production uses Azure App Service deployment slots for zero-downtime swap-based deployment.
Infrastructure & operations
Reproducible and observable.
- Infrastructure as code
- All Azure resources defined in Bicep. No manual Portal configuration in staging or production — resource group, environment parameter file, repeatable.
- Three environments
- Development (synthetic data), Staging (anonymised/synthetic for UAT), Production (real data, restricted access). Configuration via Azure App Configuration / App Service settings — never in code.
- Observability
- Azure Application Insights for request, dependency, exception, and custom-metric telemetry. Correlation IDs propagate frontend → API → database. PII never in operational logs.
- Health endpoints
/health/live(process running) and/health/ready(dependencies reachable). Both unauthenticated; both excluded from rate limiting.- Alerts
- A baseline set: error-rate spike, response-time degradation, failed-requests spike, health-check failure, SQL DTU exhaustion, auth-failure spike, deployment failure, certificate expiry, budget forecast.
- Disaster recovery
- A per-project DR runbook. RTO 4 hours and RPO 24 hours as framework defaults (tightenable with client agreement). Quarterly point-in-time restore tests; an annual region-failover walkthrough.
Compliance posture
Compliance, evidenced — not promised.
XCentral-Framework is engineered to align with ISO 9001, ISO/IEC 27001, and ISO/IEC 42001:2023. XCentral Solutions Pty Ltd — the framework vendor — is not itself ISO-certified; the certifications are held by the delivery partner.
ISO 9001
Quality Management
Certified — held by Conscierra
ISO/IEC 27001
Information Security Management
Certified — held by Conscierra
ISO/IEC 42001:2023
AI Management Systems
Framework-aligned
Conscierra — the independent MSP that delivers XCentral platforms (a trading name of B & A Technologies Pty Ltd) — holds ISO 9001 (Quality Management) and ISO/IEC 27001 (Information Security Management) certification — certificates 5991-3435-01 and 5992-3435-01, issued by Compass Assurance Services (JAS-ANZ accredited). Engagements delivered through Conscierra run under those certified processes and inherit those controls.
Compliance evidence is generated as a byproduct of normal platform operation rather than assembled separately. Audit-log retention, change-control history (Azure DevOps), pipeline-gate records (build artefacts), and Architecture Decision Records all serve as evidence sources.
Ownership & exit
You own what should be yours.
What transfers on production deployment + full payment
- Source code (mirrored to your Azure DevOps; clone rights yours from day one).
- Database content (your Azure SQL instance; daily backups; exportable on request).
- Infrastructure as code (Bicep templates; reproducible in another Azure subscription).
- Architecture documentation, data model, API reference (OpenAPI), integration runbooks.
- Security and compliance evidence pack.
- Handover documentation — the path to running the platform without us, documented from day one.
What we retain
XCentral-Framework itself — the binding rules, slash commands, Bicep templates, governance models, and the framework’s own evolution history. The proprietary IP of XCentral Solutions Pty Ltd that built and continues to improve your platform.
The migration path
Azure resources transfer between subscriptions or tenants; Bicep reproduces the environment in any Azure account; source code moves to any Azure DevOps organisation or GitHub; database content exports to any SQL Server destination. The path to running the platform without us is in the contract — not a discretionary policy, a written one.
You own your production code (mirrored to your Azure DevOps), your data, and your Azure infrastructure. We retain XCentral-Framework — the proprietary IP of XCentral Solutions Pty Ltd that built and continues to improve your platform.
The honest gaps
What we don’t have (yet).
We don’t ship perfection. We ship discipline with known edges. Here’s what sophisticated buyers ask about that isn’t in the framework yet.
Supply-chain integrity artefacts
SLSA provenance, SBOM (CycloneDX / SPDX), and artifact signing (cosign) are tracked but not yet implemented in the binding pipeline. On the roadmap.
OpenTelemetry explicit SDK
Application Insights is the observability proxy today. Migration to an explicit OpenTelemetry SDK is on the roadmap, not yet shipped.
SLO / error-budget discipline
Quality objectives are documented (for example, a ≥99.5% uptime target). Formal SLI / SLO / burn-rate alerting language is not yet codified.
Chaos / fault-injection testing
Not currently part of the binding test pyramid.
DPIA tooling
Privacy-impact-assessment templates exist; an automated DPIA workflow does not.
If a gap above is decision-relevant for your engagement, we’ll discuss it at discovery and either work around it, sequence it into your phase plan, or honestly tell you it’s a deal-breaker. What we don’t have yet, we tell you up front — that’s the difference.
FAQ
Common objections, answered.
- Why no ORM?
- Behaviour transparency. ADO.NET in a base-repository pattern with parameterised queries.
- Why no Azure Static Web Apps?
- Standardising the runtime and deployment surface on App Service across all frontends.
- Why React + Vite, not Next.js / Remix?
- A Vite SPA on App Service is the binding architecture; SSR is unnecessary for the audience (public pages pre-render at build).
- How does the AI not leak data?
- Azure OpenAI in your tenant; no training on your data; managed identity; PII flagged before generation; Tier 4 (autonomous) prohibited.
- What if I want to bring it in-house?
- Source + IaC + database transfer is contractual; the migration path is documented from day one.
- How is multi-tenancy handled?
- Default is single-tenant. Multi-tenant is specced explicitly with a TenantId at row level, resolved from the JWT claim — never from a request parameter.
- What’s the test-coverage target?
- 80% line coverage on application services and domain logic. Coverage is a guide, not a goal.
- How are secrets rotated?
- Key Vault references; rotation triggers a managed-identity refresh; no app restart needed.
- What about migrations?
- Forward-only, sequentially numbered, idempotent-safe, backwards-compatible with the current API version for one deployment window.
Bring the questions this page didn’t answer.
If your objections are already addressed here, the next conversation is about delivery. If they’re not, that’s the conversation we want to have.
