Technical architecture overview for IT teams evaluating AccessPoint
Last updated: August 09, 2026 by Steve
Technical Architecture
This document provides a technical overview of the AccessPoint platform for solution architects, cybersecurity engineers, and IT directors evaluating the product for their organization. It is drawn from the AccessPoint Solution Architecture Guide (v2.0.67).
Platform Overview
AccessPoint is an access-to-information and privacy management platform built on Microsoft 365 and Azure. It began as a subject access request (SAR) management tool and has grown into an integrated ATIP/privacy office suite covering the full request lifecycle and the surrounding privacy program. It runs entirely within your organization's own Microsoft 365 and Azure tenant: there are no external servers, databases, or third-party cloud dependencies at runtime, and all data remains in your environment under your control.
The platform is organized into modules that share a single identity, RBAC, notification, audit, and reporting spine:
- Access requests — ATI/FOIA/GDPR intake, custodian tasking, document review and redaction, response packaging, and statutory reporting
- Privacy assessments — a configurable PIA/AIA/Security assessment engine
- Privacy incidents and breaches — intake, containment, risk-of-harm, and breach-notification workflows
- Complaints and appeals — complaint lifecycle with parties, statutory clocks, and investigation
- Privacy risk register — ISO 31000-style risks, treatment plans, and key risk indicators
- Records of processing (ROPA) — GDPR Article 30 records and register export
- Commitments register — tracked privacy commitments with cadence and check-ins
- AI Assist (optional) — Azure OpenAI-backed suggestions and drafting running entirely in the customer's own subscription: document summaries, AI redaction suggestions, grounded drafting, intake triage, assessment answer prefill, semantic search and duplicate detection, a statute-aware per-case assistant (Ask AccessPoint — clickable citations, saved conversations, a My-caseload mode), "Ask AccessPoint" data answers, a jurisdiction playbook that grounds process answers, auto-translation, and a daily brief — all opt-in per tenant, metadata-only logged, with per-record AI activity disclosure
All modules are jurisdiction-pack driven, with no static seed data.
Key characteristics:
- Tenant-native — deploys as a SharePoint Framework (SPFx) solution and Azure PaaS services within your existing tenant
- No Power Platform dependencies — no Dataverse, Power Automate, or Power Apps licensing required
- Data sovereignty — all data resides in your Azure tenant's configured geography
- No vendor runtime access — the publisher cannot access your data during normal operation
- Flat-rate licensing — departmental licensing with no per-user metering
- Privacy by design — custodian and contributor roles are structurally walled off from requestor and data-subject PII
- Configuration over code — request types, exemption codes, and choice fields are data-driven
- Multilingual by design — a three-tier translation system supporting 11 languages
Architecture Diagram
Customer's Microsoft 365 Tenant Customer's Azure Subscription
┌──────────────────────────────┐ ┌─────────────────────────────────────────┐
│ │ │ │
│ SPFx Web Part │ │ Azure App Service (Linux) │
│ (SharePoint Online / │──HTTPS──│ ASP.NET Core 10 Web API │
│ Teams Personal App / │ (JWT) │ ├─ Entra ID JWT validation │
│ Teams Tab) │ │ ├─ Role-based authorization │
│ │ │ ├─ PII filter middleware │
│ Installed from AppSource │ │ ├─ User upsert from JWT claims │
│ or Tenant App Catalog │ │ ├─ License validation middleware │
│ │ │ └─ Syncfusion document conversion │
│ │ │ │
│ SharePoint Tenant Storage │ │ Azure SQL Database │
│ (API URL override, │ │ (222 tables, 3,410 fields) │
│ optional) │ │ │
└──────────────────────────────┘ │ Azure Blob Storage │
│ (Document files) │
Microsoft Graph API │ │
┌──────────────────────┐ │ Application Insights + Log Analytics │
│ Mail.Send │◄────────│ (Telemetry, diagnostics) │
│ User.ReadBasic.All │ │ │
│ TeamsActivity.Send │ │ (opt-in) Azure AI Search │
│ TeamsAppInstall │ │ (opt-in) Azure OpenAI │
│ Calendars.Read │ │ (opt-in) Azure Document Intelligence │
│ AiEnterprise…Read │ └─────────────────────────────────────────┘
└──────────────────────┘ ← M365 record capture (delegated + app-only Copilot)
Component Inventory
| Component | Technology | Deployed To | Purpose |
|---|---|---|---|
| SPFx Web Part | TypeScript, React 17, Fluent UI 8, SPFx 1.23.2 | Customer SharePoint Online / Teams | User interface for all roles |
| Teams App | Teams manifest v1.19, version lockstep with the SPFx solution and ApiVersion.Current (stamped by CI) |
Customer Teams Admin Center | Personal app, configurable tab, activity feed notifications with deep linking |
| Web API | C# / ASP.NET Core 10, .NET 10 (~103 controllers, ~124 service interfaces) | Customer Azure App Service (Linux) | Business logic, data access, document processing |
| Database | SQL Server (DacPac) | Customer Azure SQL Database | Relational data store (222 tables, 3,410 fields) |
| Blob Storage | Azure Blob Storage | Customer Azure Storage Account | Document file storage |
| Monitoring | Application Insights + Log Analytics | Customer Azure Subscription | Telemetry, diagnostics, alerting |
| AI Search (opt-in) | Azure AI Search | Customer Azure Subscription | Full-text document content search plus semantic vector search (documents + case index); provisioned only when deployAiSearch=true |
| Azure OpenAI (opt-in) | Azure OpenAI Service | Customer Azure Subscription | AI Assist — grounded drafting, suggestions, semantic search, case assistant (Ask AccessPoint); managed-identity auth (no API keys), provisioned only when AI Assist is deployed |
| Document Intelligence (opt-in) | Azure AI Document Intelligence (prebuilt-read) | Customer Azure Subscription | OCR for image-only scans feeding content search, duplicate detection, summaries, and redaction suggestions; managed-identity auth, provisioned only when deployDocumentIntelligence=true |
What the Publisher Operates
| Service | Purpose |
|---|---|
| Realizer Platform | License validation, Teams notification proxy, SPFx API-URL discovery, the Teams personal-tab smart router, and the post-deployment Finish Setup page (no customer case data transmitted) |
| AppSource Listing | SPFx package distribution |
| Deployment Template | Bicep/ARM template for the Azure backend, deployed one-click from the Azure portal into the customer's subscription |
| Teams App Package | Teams manifest distribution (sideload or org app store) |
No customer data is transmitted to or stored by any publisher-operated service.
Functional Modules
All modules run inside the single Web API / SPFx web part and share one identity, RBAC, comment, notification, My Day, audit, hours, review-workflow, and reporting spine. Each is data-driven via Jurisdiction Packs.
| Module | Summary |
|---|---|
| Requests (ATIP/SAR) | Request intake → custodian tasking → collection → response. Configurable types, statuses, numbering, calendars, and SLAs. |
| Documents & Redaction | Faceted document workspace: tags, folders, email families, read tracking, saved views, decision-based duplicate detection, Syncfusion preview, redaction pipeline with exemptions and XFDF round-trip, find & redact patterns, rule-based and AI redaction suggestions, prior-request redaction reuse, response packaging with letterhead merge. |
| Requestors / Contacts | First-class reusable requestor identity, intake flow control, frivolous-and-vexatious conduct workspace, fees & verification, merge; consultations; correspondence composer with PII firewall. |
| Privacy Assessments | Configurable PIA/AIA/Security engine: templates, screeners, section assignment, scoring/tiering, risk register, closure/regulator summaries. |
| Privacy Incidents / Breach | Incident intake, containment, risk-of-harm, breach-notification rules by regime, remediation. |
| Complaints & Appeals | Complaint lifecycle: parties, statutory clocks, admissibility, investigation, representations review. |
| Risk & Commitments | ISO 31000 risk register with appetite, KRIs, and treatment tasks; commitments register with cadence and check-ins. |
| ROPA / Privacy Subjects | Reusable programs/systems with GDPR Article 30 records and register export. |
| Reviews | Configurable sequential/parallel review-and-approval engine reused across all case entities. |
| Reporting & Audit | Fixed reports, catalog-driven custom report builder (Report Studio dashboards), statistical annual reports, SLA/management dashboards, hash-chained audit ledger and court-ready case audit exports. |
| AI Assist (optional) | Azure OpenAI-backed suggestions and drafting: summaries, redaction suggestions, grounded drafting, intake triage, custodian suggestions, assessment prefill and consistency checks, semantic search and duplicates, a statute-aware case assistant — Ask AccessPoint (clickable citations, prepare-a-draft, saved conversations) — plus a My-caseload/portfolio assistant, a pack-seeded jurisdiction playbook, Ask AccessPoint reporting, daily brief and catch-up digest, risk portfolio and conduct analysis. Per-feature tenant toggles; metadata-only logging (the assistant may additionally save owner-private transcripts). |
| Configuration & Platform | Jurisdiction-pack import (1 Universal Baseline + 106 jurisdiction packs, including type-scoped deemed-refusal auto-statuses and optional AI grounding), tenant settings, custom roles/permissions, feature toggles, custom fields, 11-language translation tables. |
Deployment Model
AccessPoint uses a split deployment model. The SPFx web part is installed from Microsoft AppSource (or a tenant App Catalog), the Teams app package is installed via sideloading or the organization's app store, and the Azure backend is deployed into the customer's own subscription by one of two methods:
- Azure portal (recommended) — one-click deployment of all Azure resources (App Service, SQL, Blob Storage, Application Insights, plus the opt-in AI Search / Azure OpenAI / Document Intelligence resources) via ARM/Bicep. The API is deployed by
zipdeployduring the ARM deployment; code is downloaded once from the publisher CDN and stored in the customer's App Service, with no runtime external dependency. A required deployment type choice guards redeploys: New installation creates the SQL server Entra-first (the deploying principal becomes the initial Entra admin, Entra-only auth from birth — no SQL credential ever exists), while Upgrade existing installation preserves operator app settings and leaves database access untouched. The customer retains full ownership of the resource group. - Bicep template + PowerShell script (manual) — for organizations with strict change control. A Deploy to Azure button or
Deploy-AccessPoint.ps1deploys the infrastructure, re-asserts SQL Entra-only authentication (a backstop — the template applies it), grants Microsoft Graph permissions to the managed identity (the scripted alternative to the publisher's Finish Setup page), configures the optional SharePoint storage-entity API-URL override, and approves SPFx API permission requests. Each step can be skipped independently.
Graph permissions are normally granted through the publisher's Finish Setup page — the deployment emits a finishSetupUrl output that a customer Entra administrator opens to grant every managed-identity permission in one idempotent pass (re-run after upgrades to pick up new permissions).
The deployment is security-hardened by default: TLS 1.3, FTPS disabled, SCM/FTP basic authentication disabled, HTTP/2 enabled, and Microsoft Defender for SQL on by default (opt-out). The API bundles its database DacPac and applies it on startup via a migration service using DacServices.Deploy(upgradeExisting: true) — a no-op on an already-current schema, and an additive, non-destructive delta after a schema change (BlockOnPossibleDataLoss = true, DropObjectsNotInSource = false). The migration outcome is surfaced through /api/health. Post-deployment, an administrator grants consent for the Realizer enterprise app (Teams notifications), configures the sender mailbox, and imports a jurisdiction pack.
Authentication and Identity
AccessPoint uses Microsoft Entra ID as its sole identity provider. There are no application-specific user accounts or passwords. The SPFx web part acquires a JWT for the API audience via AadHttpClient; the API validates the issuer, audience, signature, and expiry on every request. MFA and Conditional Access policies configured in the tenant apply in full.
User (Browser) SharePoint Online AccessPoint API
│ │ │
│ 1. Load SPFx web part │ │
│─────────────────────────────►│ │
│ │ │
│ 2. AadHttpClient.getClient()│ │
│ (SPFx acquires token for │ │
│ api://<client-id> audience)│ │
│ │ │
│ 3. API call + Bearer token │ │
│──────────────────────────────┼─────────────────────────►│
│ │ │
│ │ 4. Validate JWT: │
│ │ - Issuer (Entra ID) │
│ │ - Audience (api://) │
│ │ - Signature │
│ │ - Expiry │
│ │ │
│ │ 5. UserUpsertMiddleware: │
│ │ Extract claims → │
│ │ Upsert Users table │
│ │ │
│ │ 6. PermissionService: │
│ │ resolve roles + │
│ │ permission grants │
│ │ │
│ ◄── JSON response ───────┼──────────────────────────│
| Parameter | Value |
|---|---|
| Identity Provider | Microsoft Entra ID (Azure AD) |
| Protocol | OAuth 2.0 / OpenID Connect |
| Token Type | JWT Bearer |
| Audience | api://<client-id> and <client-id> (both v1 and v2 tokens accepted) |
| Issuer | Any Entra tenant's issuer — https://login.microsoftonline.com/{tenantId}/v2.0 (v2) and https://sts.windows.net/{tenantId}/ (v1) formats validated; the token's tid claim drives tenant scoping |
| App Registration | Publisher-operated multi-tenant app, consented per customer tenant (no per-customer app registration to manage or rotate); exposed scope access_as_user; no client secret. Isolation between customer tenants is enforced by the validated tid claim plus a license/subscription check |
Managed Identity
The App Service uses a system-assigned managed identity to authenticate to backend services, so no client secrets or certificates are stored in the application:
- Azure SQL Database — Entra-only authentication (the server is created Entra-first; no SQL credential ever exists)
- Azure Blob Storage —
DefaultAzureCredential(managed identity in production) - Microsoft Graph API —
ManagedIdentityCredentialwith application permissions - Optional AI resources (AI Search, Azure OpenAI, Document Intelligence) — managed-identity-only, with local/key auth disabled
Managed identity credentials are rotated automatically by Azure.
Authorization and RBAC
AccessPoint implements a granular, tenant-configurable permission model stored in Azure SQL and enforced at the API layer on every request: a product-defined catalog of atomic permission codes (e.g. request.modify, document.view, redaction.approve, request.view.pii) is grouped into roles, and every controller action is gated on a permission code.
| Role type | Roles | Notes |
|---|---|---|
| System role | Administrator (the only built-in standing role) | Resolves to every catalog permission — computed from the catalog, not database seeding — so it can never be locked out; immutable and undeletable. |
| Relationship roles | Reviewer, Custodian, Contributor, Reader, Advisor | Product-defined permission bundles conferred automatically and per-target by work/collaboration relationships (custodian assignment, contributor task, review, advisor/reader membership). Not directly assignable, scoped to the related record, and never include requestor PII. |
| Tenant roles | e.g. Request Coordinator (shipped via the Universal Baseline pack), plus any custom roles | Fully editable permission bundles managed in Settings > Roles & Permissions. Coordinator/officer roles are entirely tenant-defined — the pack-shipped Request Coordinator is the de-facto access-and-privacy-officer role. |
| Role | Sees Requestor PII | Typical Access | Typical User |
|---|---|---|---|
| Administrator | Yes | Everything (all catalog permissions) | IT admin, system owner |
| Request Coordinator (tenant role) | Yes | Manages requests, assignments, redactions, correspondence | Access & privacy officer |
| Reviewer | No | Read + flag/resolve redaction concerns on requests under review | Legal counsel, QA |
| Advisor | No | Read + view/flag redactions (never apply or approve) | Consulting counsel, SME |
| Custodian | No | Own assignments and their documents | Department records officer |
| Contributor | No | Own tasks and their documents | Subject matter expert |
| Reader | No | Read-only on associated requests and shared documents | Oversight, audit |
Enforcement points:
[RequirePermission(code)]attributes — a dynamic policy provider maps each controller action to a catalog permission codePermissionService— resolves the caller's effective permissions (standing roles unioned with per-target relationship grants), with object-scoped checks at request/document/assignment/task level- Resource-level ownership checks — e.g., a Contributor can only access their own tasks; a Custodian only their own assignments
PiiFilterMiddleware— strips requestor PII fields from JSON responses for any caller who does not hold the view-requestor-PII permission
All API write endpoints enforce permissions server-side at the controller level. Document reclassification is scope-aware — the user who collected a document is the one who can reclassify it — and once a request is closed, mutations on its object graph return HTTP 409, with a small set of deliberate exceptions (post-closure correspondence, retention purge, and reopen). Standing role assignments are scoped (global, request, assignment, or task), optionally time-limited, and relationship roles are never written as standing assignments — they are derived per-target from the underlying work records. The first user to access the system is bootstrapped with the Administrator role, and a server-side guard prevents removing the last active Administrator.
Data Residency and Sovereignty
All data resides in the customer's Azure tenant in the region selected during deployment. No data is transmitted to or stored in other regions unless the customer explicitly configures Azure geo-replication.
Canadian federal departments: For GC-specific data-residency requirements, ITSG-33 control mapping, and GC Cloud Guardrails compliance, see the GC Security Controls Reference.
Customer Data Locations
| Data Type | Storage Location | Controlled By |
|---|---|---|
| Request records, user data, audit trail | Azure SQL Database | Customer's Azure subscription |
| Uploaded documents | Azure Blob Storage | Customer's Azure subscription |
| Application telemetry | Application Insights / Log Analytics | Customer's Azure subscription |
| SPFx web part assets | SharePoint CDN | Customer's M365 tenant |
| API URL override (optional) | SharePoint Tenant Storage Entity | Customer's M365 tenant (primary discovery is the publisher API-discovery endpoint) |
| AI search indexes, summaries, OCR output (opt-in) | Azure AI Search / Azure SQL | Customer's Azure subscription |
What Crosses Tenant Boundaries
| Data Flow | Direction | What Is Transmitted | Purpose |
|---|---|---|---|
| License validation | API → Publisher Platform | Entra managed-identity token (the token's tid claim identifies the tenant), API version, and the API's own base URL (self-registration for web-part discovery) |
Validate active subscription; the response also carries the latest published AccessPoint version so the Setup panel can show an "Update available" notice (version numbers only — no usage or case data) |
| API-URL discovery | SPFx → Publisher Platform | Bearer token (tenant ID claim only) | Resolve the customer API base URL when no storage-entity override is set |
| Email notifications | API → Microsoft Graph | Email content via Mail.Send |
Send notifications from shared mailbox |
| Teams notifications (optional — toggleable / eliminable) | API → Publisher Platform → Microsoft Graph | Activity type, recipient + tenant GUIDs, notification title/preview text, acting-user name, request number, record id (deep link) | Send Teams activity feed notifications (proxied through the publisher's multi-tenant enterprise app because sendActivityNotification must be called by the app that owns the Teams manifest). This is the only notification channel that leaves the tenant — email and the in-app feed deliver the same notices in-tenant. See Teams Notification Data Sharing |
| User profile lookup | SPFx → Microsoft Graph | User search queries | People picker, user resolution |
What Never Leaves the Tenant
- Request records and requestor PII
- Uploaded documents
- Audit history
- Configuration data (request types, templates, numbering)
- Role assignments
- AI Assist prompts and responses — when AI Assist is deployed, the Azure OpenAI resource runs in the customer's own subscription and region; Microsoft does not train models on the content, and prompt/response content is not stored (only usage metadata, for the budget meter and the case audit export's AI-involvement disclosure)
- In-app and email/Outlook notifications — the in-app feed is served from the customer's own API; email is sent from the customer's own shared mailbox via
Mail.Send. Neither transits the publisher. Only the optional Teams activity feed sends any data outside the tenant, and even that can be minimised or eliminated (below)
Teams Notification Data Sharing (Optional)
Consistent with the data-sovereignty promise, the only tenant data that leaves the boundary for notifications is an optional Teams activity payload — and it can be minimised or removed entirely. Every notification is delivered on up to three channels; two are always in-tenant (the in-app feed, served from the customer's API, and email/Outlook, sent from the customer's own shared mailbox). The Teams activity feed is a convenience: in its default mode the customer API POSTs a small payload to the publisher's multi-tenant app, which calls Graph sendActivityNotification (which must be invoked by the app that owns the Teams manifest). The publisher stores none of it and logs only the recipient + tenant GUIDs.
Fields in the default (publisher-relay) Teams payload — and what the Minimise toggle strips:
| Field | Contains | Personal data? | Removed by Minimise? |
|---|---|---|---|
tenantId / recipientUserId |
Tenant + recipient Entra GUIDs | No — identifiers | No (routing) |
activityType |
Fixed manifest enum (e.g. assignmentCreated) |
No | No |
previewText / topicText |
Preview + title (assignment/task name, notification text) | Potentially (free text) | Yes → placeholder |
actorName |
Acting staff member's display name | Yes — personal name | Yes → "AccessPoint" |
requestNumber |
Request reference code | No — reference | No (context) |
relatedEntity + relatedRecordId |
Record type + GUID (deep link) | No — identifiers | No (deep link) |
Requestor PII is never in the payload — there is no requestor name/email field, and Custodian/Contributor notifications are PII-blanked upstream regardless. Two controls tighten this further:
- Minimise (
Notifications:TeamsMinimalPayload, a tenant toggle) replacespreviewText,topicText, andactorNamewith neutral placeholders, so names, titles, and free text never leave the tenant — only routing/deep-link identifiers do. No manifest change required. - Eliminate (
Notifications:TeamsRelayMode=Direct) makes the customer API call GraphsendActivityNotificationitself with its managed identity, so no payload reaches the publisher at all. It requires theTeamsActivity.Sendapp-role on the API's managed identity and the Teams manifest'swebApplicationInfo.idpointed at the customer's own app — see the Deployment Guide.
| Mode | Payload to publisher | PII / free text leaves tenant | Setup effort |
|---|---|---|---|
| Default relay | Yes (fields above) | Names/titles, unless minimised | None |
| Minimise toggle | Yes (identifiers only) | None | One toggle |
| Direct (self-hosted) relay | None | None | MI grant + manifest edit |
| Teams disabled | None | None (email + in-app only) | None |
Encryption
In Transit
| Connection | Protocol | Minimum TLS |
|---|---|---|
| Browser → App Service | HTTPS (enforced, httpsOnly: true) |
TLS 1.3 |
| SPFx → App Service | HTTPS (enforced by SharePoint context) | TLS 1.3 |
| App Service → Azure SQL | TDS with encryption (Encrypt=True) |
TLS 1.2+ |
| App Service → Blob Storage | HTTPS (managed identity) | TLS 1.2+ |
| App Service → Microsoft Graph | HTTPS | TLS 1.2+ |
The App Service enforces TLS 1.3 as its minimum at the front door (TLS 1.0/1.1/1.2 rejected); outbound connections to Azure backend services negotiate TLS 1.2 or higher.
At Rest
| Data Store | Encryption | Key Management |
|---|---|---|
| Azure SQL Database | Transparent Data Encryption (TDE) | Microsoft-managed keys (default) or customer-managed keys (CMK) |
| Azure Blob Storage | Storage Service Encryption (SSE), AES-256 | Microsoft-managed keys (default) or customer-managed keys (CMK) |
| Application Insights | Platform encryption | Microsoft-managed keys |
Application-Level
| Feature | Mechanism |
|---|---|
| Document viewer tokens | ASP.NET Core Data Protection (WOPI-style token, 15-minute TTL, tenant cross-check on every anonymous viewer call) |
| Attestation e-signatures | SHA-256 hash of signatory and timestamp stored in audit fields |
Privacy by Design
AccessPoint implements structural privacy controls that are enforced at the API layer, not only in the UI.
PII Filter Middleware
A response-level middleware intercepts all JSON responses and strips PII fields for any caller who does not hold the view-requestor-PII permission (Custodians, Contributors, Readers, Reviewers, and Advisors never hold it), server-side, regardless of what the client requests. Fields stripped: requestorName, requestorEmail, requestorPhone, requestorAddress, subjectName, subjectDateOfBirth, plus representative identity/contact fields and identity-verification notes.
Notification PII Blanking
When notifications are sent to Custodians or Contributors, requestor PII is replaced with placeholder text in the notification content itself — even if the notification template includes PII merge fields.
Custodian / Contributor Isolation
- Custodians see only their assigned requests and work from sanitized instructions
- Contributors see only their assigned tasks
- Neither role can search, browse, or access requests outside their scope
Logging, Monitoring, and Audit
Application Insights
All API telemetry is sent to the customer's Application Insights instance:
| Signal | What Is Captured |
|---|---|
| Request traces | HTTP method, path, status code, duration, correlation ID |
| Dependency tracking | SQL queries, Blob operations, Graph API calls (duration, success/failure) |
| Exceptions | Unhandled exceptions with stack traces |
| Custom metrics | Request processing times, document conversion durations |
Audit Trail
AccessPoint maintains a comprehensive audit trail in the AuditHistory table (Azure SQL), recording the entity type, entity ID, action (Create, Update, Delete, StatusChange), field name, old/new values, an optional change reason (captured on status transitions such as close and reopen), the authenticated user, and a UTC timestamp. Audit records are immutable — they cannot be modified or deleted through the API.
Hash-Chained Audit Ledger
In addition to the field-level audit trail, a tamper-evident, append-only AuditLedger (SHA-256 hash chain) records actions across all modules. Integrity can be verified server-side, and a court-ready case audit export (formerly "evidence package") can be generated for a request.
Optimistic Concurrency
High-contention entities (Requests, Documents, CustodianAssignments) each carry a SQL Server ROWVERSION concurrency token. The client echoes the row version on update; if another writer has changed the row in the interim, the save is rejected with HTTP 409 ("Concurrent edit detected") rather than silently overwriting.
Retention Purge Log
One RetentionService engine disposes of aged-out records across five registers — requests, assessments, incidents, complaints, and standalone privacy risks — driven by a per-type RetentionPeriodMonths + RetentionStartPoint (NULL = keep indefinitely, the default). Records that must not age out are excluded by construction (an In Effect assessment, an Accepted risk, a risk anchored to a case), and an expired record another open case still depends on is refused server-side; eligibility is re-evaluated at purge time. When a record is purged the engine deletes document blobs, converted PDFs, annotations, and export packages from Blob Storage in addition to the database records. The RetentionPurgeLog records what was deleted, when, and by whom — EntityName identifies which register it came from, alongside a snapshot of its number, type, close date, and document count — a compliance-grade trail that survives the data removal and is itself never purged.
Log Analytics and Alerts
Application Insights is backed by a Log Analytics workspace with configurable retention (default 90 days; configurable 30–730 days). Data can be exported to Microsoft Sentinel or an existing SIEM. The Bicep deployment includes two default metric alerts on the App Service:
| Alert | Severity | Condition | Window |
|---|---|---|---|
| Server Errors | 2 (Warning) | HTTP 5xx count > 5 | 5 minutes |
| High Latency | 3 (Informational) | Average response time > 5 seconds | 15 minutes |
Customers can customize thresholds and add action groups (email, SMS, webhook) in the Azure Portal.