Data Compliance Assistance

Data Compliance Assistance shows application owners where an application's data lives, how it is protected, and what they still need to do. It combines four inputs:

  • Platform posture: facts Backify observes or guarantees, such as region, encryption, TLS, isolation, backups, retention, secrets, managed identities and the audit log.
  • Data inventory: every place the application stores or sends data, built from its deployed capabilities and source scan and confirmed by the owner.
  • Owner answers (attestations): what Backify cannot observe, such as lawful basis, privacy notice and DPIA.
  • Code scan: the opt-in GDPR source analysis described below.

It is advisory. It is not legal advice, certification, a complete security audit, or a guarantee that an application meets GDPR or any other requirement. Controls are mapped to GDPR articles, ISO 27001:2022 Annex A controls and SOC 2 trust services criteria to show which evidence supports which requirement. ISO 27001 and SOC 2 certify organizations, not applications. The mappings are indicative and do not imply certification.

Compliance posture

Open an application and choose Data compliance. The Overview tab evaluates a versioned catalog of controls (compliance-controls-v2) as soon as the page is opened; no setup is required. Each control has:

  • a status: Met, Needs attention, Action required, Not applicable or Unknown
  • a responsibility: Backify handles, Shared or You need to
  • evidence with its source and time
  • framework mappings
  • a suggested fix

Evidence sources are:

  • Observed: read from live platform state.
  • Platform baseline: guaranteed by platform configuration, such as storage infrastructure encryption, TLS 1.2 minimum, 7-day blob soft delete, 90-day Key Vault soft delete with purge protection, and 7-day runtime log retention.
  • Policy: organization or workspace settings.
  • Your answer: an attestation.
  • Source scan.

A control is never Met without evidence. When Backify cannot observe something, for example transfers before the first deployment, the control is Unknown, which counts as needing attention.

Group controls by responsibility, area or framework. Select a control to see its evidence, mappings and fix. Export evidence downloads a JSON evidence pack of the current controls, inventory, answers and latest scan, for your own auditor or DPO. It is not a public link.

The page also shows known platform limits rather than hiding them. Database backups are taken on demand, and the events capability has no automatic retention, so these show Needs attention until you act. Successful database backups are recorded as DATABASE_BACKUP_CREATED audit events, and backup age is read from them. Databases, files, events, jobs and backups are all kept until you delete them, so the Stored data has a retention period control asks you to confirm your retention periods. It is Not applicable once a deployed application stores none of these.

Data inventory

The Data inventory tab lists these data stores:

  • user accounts, when sign-in is enabled
  • databases and file storage per environment
  • database backups, once one has been taken (kept in the hosting cloud account's backup storage until you delete them)
  • events
  • background jobs: job runs and their payloads, which have no automatic retention
  • runtime logs
  • application audit events
  • secrets
  • each outbound transfer: AI models, outbound HTTP, mail and integrations

Each store shows its location, protection and retention. Stores the scan suggests hold personal data are marked Likely. Managers with apps.update confirm whether each store holds personal data and tag its categories from the organization's list. Confirming a transfer also acknowledges it when the organization has no approved-transfer list. Tagging special-category or children data makes evaluation stricter: a DPIA becomes required, and special-category data on a shared database server needs attention.

Obligations

The Obligations tab asks the questions Backify cannot observe for this application:

  • lawful basis
  • privacy notice
  • records of processing
  • DPIA
  • data subject requests
  • erasure
  • retention periods for stored data
  • safeguards for transfers outside the EU/EEA (adequacy or standard contractual clauses)
  • consent, including parental consent for children
  • consent for non-essential cookies, browser storage and tracking scripts (ePrivacy)
  • safeguards for solely automated decisions, including AI (GDPR Art. 22)

Answers are Yes, No or Not applicable where allowed, with an optional https evidence link and note. Do not include personal data or secrets. No means action is required. Answers expire after the organization's renewal period (365 days by default) and then need attention again. Organization-wide questions are shown read-only with the organization's answer. Every answer is recorded as a COMPLIANCE_ATTESTATION_RECORDED audit event.

Organization and workspace baseline

Much of compliance belongs to the organization, not to one application. Data compliance in the organization sidebar has three tabs:

  • Overview: an applications × areas matrix, the top actions across applications, and organization answers that are missing or expire within 30 days. It requires audit.read. Export evidence downloads the organization evidence pack.
  • Baseline: frameworks, the deploy gate, whether vulnerable dependencies require action, approved transfers, maximum backup age, answer renewal period, required source scanning, controls marked not applicable, data categories and the compliance contact. It requires organization.manage.
  • Shared obligations: answered once and inherited by every application. These are the DPA with Backify, privacy contact, incident response, security training, vendor management and access reviews.

Workspaces have the same Overview and a Baseline that can only tighten the organization's:

  • frameworks and required scanning can only be added
  • approved transfers and not-applicable controls can only be narrowed
  • limits can only be lowered
  • the deploy gate and the vulnerable-dependency severity can only become stricter

Loosening is rejected. Data categories and the contact are organization-only. For residency, the allowed regions are the intersection of the organization's and the workspace's lists, so an application in a region only the workspace allows is reported as Action required.

Deploy gate

The deploy gate controls what happens when someone publishes to production while an application has an Action required control:

  • Inform only (default): the status is shown but publishing is not affected.
  • Require approval: the release needs approval, with the reasons included in the approval request.
  • Block production publishing: publishing is refused with compliance_action_required.

The gate is evaluated against live state when publication is requested and again before the release is applied. Decisions are recorded as policy evaluations.

Re-evaluation and notifications

Posture is re-evaluated:

  • after each successful deployment or scan
  • when inventory or answers change
  • when the page is opened
  • by the worker, at least daily and after organization baseline or answer changes

A snapshot is kept when the result changes. When a control gets worse, Backify records a COMPLIANCE_REGRESSION audit event and sends a compliance-regression notification to people with apps.update.

API

Historical evidence and review accountability

In organization Data compliance → Evidence history, an administrator with organization.manage can accept the versioned 12-month evidence expiry policy. This explicit acceptance enables complete evidence collection for the organization's applications. Acceptance is audited; new archives expire 12 calendar months after capture. Existing source reports, audit events and status-only snapshots retain their existing policies. The expiry worker only removes new, eligible archives covered by the accepted policy, in bounded batches, and audits each batch.

In application Evidence history, complete captures preserve the evaluated controls and their evidence provenance and observation dates, effective baseline, inventory confirmations, application and inherited answers, catalog and question definitions, and latest source report including its commit and collection date. The compliance-evidence-v1 export schema identifies the pack format. Baselines, source observations, scans and human assertions remain distinct. Evidence links are recorded as links: Backify does not fetch linked documents or copy their contents.

The existing worker collects evidence at least daily while healthy. Deployment, scan, answer, inventory and finding-review changes also trigger collection; changed evidence produces a capture even when control statuses are unchanged. Equivalent observations within 24 hours are deduplicated. PostgreSQL collectors use an application-scoped transaction advisory lock across worker/API instances. Collection failures produce a safe failure marker; unavailable collection and missing intervals remain visible as Collection gaps. A capture is a point-in-time observation; the daily coverage display does not establish uninterrupted control operation.

Historical packs are append-only through the application and never rebuilt from current settings. Old status-only snapshots are labeled Status only and cannot be downloaded as complete evidence. History shows changed evidence sections, capture and expiry dates, collection gaps, and current answer review deadlines. Complete captures can be downloaded as JSON or a standalone HTML summary. The organization export takes an inclusive UTC range of at most 366 days and includes per-application captures and coverage gaps, including retained evidence for deleted applications. After expiry, absent evidence is reported as a gap rather than recreated.

Application managers can assign an active organization member with apps.update in that application as reviewer. Organization administrators can assign a shared-obligation reviewer with organization.manage. Assignment grants no permissions. Without an eligible assigned reviewer, reminders go to eligible application managers or organization administrators respectively. The existing notification outbox delivers reminders 30 days and 7 days before expiry, at expiry and weekly while overdue. Delayed workers send the currently applicable reminder stage. Reminders are deduplicated for the answer version and expiry stage, and both expansion and delivery re-check renewal and current recipient permissions. Renewing an answer cancels its queued reminders; it does not automatically confirm other obligations.

Historical access checks current permissions and the same detailed-compliance billing entitlement as existing evidence exports. Reviewer settings require the scope's management permission. Collection never starts AI review, source scanning, backup creation or application-data deletion, and does not change production deployment gates.

Historical evidence endpoint Permission
GET /api/apps/{id}/compliance/evidence?from={UTC}&to={UTC} apps.read, detailed compliance entitlement
GET /api/apps/{id}/compliance/evidence/{captureId}?format=json|html apps.read, detailed compliance entitlement
GET /api/apps/{id}/compliance/evidence/settings apps.update
PUT /api/apps/{id}/compliance/evidence/reviewer (userId or null for fallback) apps.update
GET /api/organizations/{org}/compliance/evidence/settings organization.manage
PUT /api/organizations/{org}/compliance/evidence/reviewer organization.manage
POST /api/organizations/{org}/compliance/evidence/policy (policyVersion: "evidence-retention-v1") organization.manage
GET /api/organizations/{org}/compliance/evidence/export?from={UTC}&to={UTC} audit.read, detailed compliance entitlement

For authenticated JSON-only clients, format=html&envelope=true returns { html } so the client can download the summary without exposing credentials in a link. HTML output escapes evidence text and fetches no remote resources. Omitted application-history dates default to the last calendar month through now; organization exports require both dates. Use Z or zero-offset UTC timestamps.

Apply the additive AutomatedComplianceEvidence migration with the API and worker before enabling policy acceptance. It creates archive, reviewer and policy tables and deletes no existing records. PostgreSQL expiry and locking behavior require PostgreSQL integration verification; in-memory local development does not emulate PostgreSQL locking and loses its data on restart.

Validation commands:

dotnet test tests/Backify.Tests/Backify.Tests.csproj --filter "FullyQualifiedName~ComplianceEvidence"
npm run test:portal -- compliance.spec.ts
npm run build -w @backify/portal

Set BACKIFY_TEST_COMPLIANCE_POSTGRES privately in the test environment to a disposable PostgreSQL database to run the concurrent-collector integration test. The test creates and drops only its own randomly named schema and never migrates an existing schema. Without this setting, that test is explicitly skipped.

For a local smoke test, use the repository's seeded demo-user identity (demo@backify.local) in Development authentication. No production data or provider credentials are needed. From the repository root, start the API in one PowerShell terminal:

$env:ASPNETCORE_ENVIRONMENT='Development'
$env:DOTNET_ENVIRONMENT='Development'
$env:UseInMemoryDatabase='true'
$env:Authentication__Mode='Development'
$env:DatabaseName='backify-compliance-evidence-local'
$env:Cors__Origins__0='http://localhost:5187'
dotnet run --no-launch-profile --project apps/api/Backify.Api.csproj --urls http://localhost:5087 -- --DataProtection:KeyVaultUri= --Billing:Stripe:Enabled=false

In a second PowerShell terminal:

$env:VITE_API_URL='http://localhost:5087/api'
$env:VITE_ENTRA_CLIENT_ID=''
$env:VITE_ENTRA_AUTHORITY=''
$env:VITE_API_SCOPE=''
npm run dev -w @backify/portal -- --port 5187

Open http://localhost:5187/organization/org-compliance?tab=evidence, explicitly accept the local expiry policy, then open an application's Data compliance → Evidence history. Confirm a complete capture, download JSON and HTML, update an application answer, and confirm the original JSON remains unchanged. API requests trigger immediate capture; the API-only setup does not run scheduled maintenance. Run the worker against the same PostgreSQL database as the API to verify daily collection, reminder delivery and expiry end to end. Separate in-memory API/worker processes do not share data. The local in-memory state resets on API restart; notification outbox tests use a recording sender and do not send real mail.

Endpoint Permission
GET /api/apps/{id}/compliance/posture apps.read
GET /api/apps/{id}/compliance/export apps.read
PUT /api/apps/{id}/compliance/inventory/{itemId} apps.update
PUT /api/apps/{id}/compliance/attestations/{key} apps.update
GET/PUT /api/organizations/{org}/compliance organization.manage
PUT /api/organizations/{org}/compliance/attestations/{key} organization.manage
GET /api/organizations/{org}/compliance/overview, /export audit.read
GET/PUT /api/workspaces/{id}/compliance workspaces.manage
GET /api/workspaces/{id}/compliance/overview audit.read

Apply the ComplianceGovernance migration with the API and worker. It adds tables and an inventory column; no existing records are removed.

Code scan

Enable assistance

In the portal, open an application, choose Data compliance and then the Code scan tab, or use the entry in application Settings. Application managers with apps.update can enable assistance. Users with apps.read can read reports. Both local scanning and AI-assisted review are disabled by default; no changes to application instructions or SDK declarations are required.

When enabled, source is scanned during the existing GitHub deployment analysis step. Findings are advisory and do not block deployment. Rescan deployed code queues a read-only source scan for the most recent successful deployment, without building or publishing a new release. A connected GitHub repository and a successfully deployed revision are required. Uncommitted changes and production database contents are not analyzed.

Reports identify the exact checked commit, source file/line anchors, confidence, recommendations, coverage limits, and optional AI-review status. The portal shows the latest 20 reports. Older reports remain stored; disabling the feature does not delete historical reports. Failed scans are clearly identified and never presented as clean results.

Local analysis

The deterministic scanner supports JavaScript/TypeScript and looks for potential personal-data form fields, sensitive/high-impact identifiers, personal information or raw requests in logs, browser persistence, and personal fields sent to absolute HTTP URLs. These are bounded syntactic heuristics, not whole-program data-flow analysis. Names alone cannot establish that personal data is processed; an absolute URL can be first-party.

Tests, fixtures, generated declarations, dependencies and common generated-output directories are excluded. Unsupported languages, dynamic behavior, external dependencies, runtime configuration, actual hosting/access controls, contracts, processing purposes and operating practices are outside the analysis. Coverage counts refer to source files supplied to the scanner, not every file in the repository. Applications without eligible source still need human review.

Each report includes human-review questions about lawful basis, notices, retention, rights, authorization/security, processors, international transfers and DPIAs. Questions do not assert that a control is absent or that a legal obligation applies in every case.

Dependency vulnerabilities

Scan dependencies for known vulnerabilities is a separate opt-in in the Code scan tab, available once assistance is enabled. On each scan, Backify reads the lockfile at the repository root (package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml or yarn.lock, classic or Berry) and checks every resolved package against the OSV advisory database, which includes GitHub security advisories and OpenSSF malicious-package reports. Nothing is installed and no install scripts run.

Only public npm package names and versions are sent to OSV.dev, a service run by Google. No source code, file contents or application identifiers are sent. Packages are not sent when they resolve to a non-public registry, a local path, a git source, a workspace or a patch, or when .npmrc or .yarnrc.yml assigns their scope to another registry. When a custom default registry is configured, only packages with an explicit public registry URL in the lockfile are checked. The report counts skipped packages. Up to 5,000 packages are checked per scan.

Each affected package becomes a finding anchored to its lockfile line, with the advisory ID and aliases, severity, the lowest fixed version above the installed one and whether npm records it as development-only. Malicious-package reports are rated critical; advisories without a rating rank as moderate. Development dependencies are included because build tools run during deployment. Whether the vulnerable code is reachable is not analyzed.

The Dependencies have no known vulnerabilities control (GDPR Art. 32, ISO 27001 A.8.8, SOC 2 CC7.1) summarizes the latest scan. By default, vulnerabilities are advisory and the control shows Needs attention. In the organization or workspace Baseline, Require action for vulnerabilities rated can be set to critical, high and above, or moderate and above. Unresolved vulnerabilities at or above that rating make the control Action required, which the deploy gate acts on. With a rating set, an application with dependency scanning turned off is also Action required.

A vulnerability is resolved by upgrading the package and redeploying, or by reviewing the finding as Mitigated or False positive. Acknowledged keeps it unresolved. Reviews carry over while the package, version and advisory stay the same. Missing lockfiles, failed lookups and scans older than 30 days show Needs attention, never Met. New advisories appear on the next deployment or rescan.

Dependency scanning uses https://api.osv.dev by default and needs no credentials. The worker needs outbound HTTPS access to it. Operators can set Compliance__Osv__Enabled=false on the API and worker to turn it off, or Compliance__Osv__Endpoint to an HTTPS OSV-compatible mirror. Apply the DependencyVulnerabilityScanning migration with the API and worker. It adds one column and removes no records.

Optional Foundry review

Enable AI-assisted review using Foundry is a separate opt-in. The manager must accept a versioned source-processing disclosure. Backify records who accepted it and when. Disabling AI prevents new requests after the worker observes the change; requests already sent cannot be recalled.

Before sending code, the local analyzer removes comments, string/number/regex literals, template text and JSX text. It selects at most 16 relevant sections, up to 3,000 characters each and 24,000 characters in total. Identifiers, relative filenames and code structure remain, so this is data minimization, not an anonymity or secret-detection guarantee. Never put personal records or credentials in identifiers or filenames. No production records, review notes, tenant/user identifiers, or raw source excerpts are sent as separate context. The selected sanitized code is transient and is not stored in Backify reports or audit metadata.

Foundry uses a stateless chat-completions request with store: false, no tools and a strict JSON schema. Comments/code are treated as untrusted evidence rather than instructions. Output is checked against supplied evidence IDs, field limits and an allowlisted set of GDPR articles. File/line locations and legal links are assigned by Backify, not trusted from the model. AI findings are potential risks, not legal determinations or verified fixes. Model errors, refusals, truncated outputs or invalid evidence leave local findings available and mark AI review incomplete.

Recommendations are grounded in the bundled gdpr-core-v1 reference-summary library (Articles 5, 6, 9, 12, 15, 16, 17, 20, 25, 28, 32, 35 and 44). These reviewed summaries are not the full law or automatically updated legal advice. A qualified reviewer must maintain the library and assess jurisdiction, applicability, national rules and operational context. The report records the configured model deployment and reference-library version for completed AI reviews.

Platform operator configuration

By default, review reuses Backify's shared platform Foundry provider, endpoint and model. It does not provision a separate AI resource. Operator approval and per-application source-processing consent are still required; enabling platform AI alone never authorizes sending source code.

For the shared provider, enable platform Foundry and set Compliance__Foundry__ApprovedForCodeReview to true on both the API and worker only after reviewing customer-code processing. Keep the dedicated compliance endpoint and deployment unset. Both services need PlatformAi__Foundry__Enabled, PlatformAi__Foundry__Endpoint and PlatformAi__Foundry__Deployment. The provider uses managed identity with the https://cognitiveservices.azure.com/.default scope; PlatformAi__Foundry__ManagedIdentityClientId optionally selects an identity, otherwise AZURE_CLIENT_ID selects each service's own identity. Production infrastructure supplies these settings and grants the worker account-scoped inference access when shared code review is approved.

In the active azd environment, set COMPLIANCE_FOUNDRY_APPROVED_FOR_CODE_REVIEW=true after approval, leave COMPLIANCE_FOUNDRY_ENDPOINT and COMPLIANCE_FOUNDRY_DEPLOYMENT empty, then run azd provision to apply configuration and worker access. Deploy the updated API and worker, refresh the portal, enable AI review and accept the disclosure before saving and rescanning. No browser credentials or new application AI capability are needed.

A dedicated Foundry/Azure OpenAI deployment remains supported through the following server-side settings on both services. Setting either a dedicated endpoint or deployment selects this route; incomplete or invalid dedicated configuration is unavailable and never silently falls back to a different source processor:

Environment variable Purpose
Compliance__Foundry__Endpoint HTTPS resource root, such as https://YOUR-RESOURCE.openai.azure.com or https://YOUR-RESOURCE.services.ai.azure.com; /openai/v1/ is also accepted. Project endpoints are not accepted.
Compliance__Foundry__Deployment Existing deployment name for a model supporting chat completions and strict structured outputs.
Compliance__Foundry__ApprovedForCodeReview Set true only after reviewing processing location, access, retention, contracts and customer-code handling. Default is disabled.
Compliance__Foundry__UseManagedIdentity Set true to authenticate using managed identity. Grant the worker's identity the appropriate Azure OpenAI inference role on the existing Foundry resource. Uses the https://ai.azure.com/.default scope.
Compliance__Foundry__ManagedIdentityClientId Optional user-assigned identity client ID; defaults to AZURE_CLIENT_ID when set, otherwise system-assigned identity. The production worker uses its existing user-assigned identity.
Compliance__Foundry__ApiKey Optional server-side secret instead of managed identity. Never put it in portal/browser code or commit it.
Deployments__ComplianceAnalyzerPath Optional absolute path to the built compliance-cli.bundle.cjs. Defaults to packages/analyzer/dist/compliance-cli.bundle.cjs relative to the worker working directory.

The API needs matching endpoint, deployment, approval and authentication-mode settings to advertise AI availability and accept AI opt-in; it does not perform inference. With managed identity, the API does not need an inference role. With API keys, configure keys only through server-side secret configuration. Keep API/worker configuration aligned.

For production, set the following in the active azd environment before provisioning:

azd env set COMPLIANCE_FOUNDRY_ENDPOINT https://YOUR-RESOURCE.openai.azure.com
azd env set COMPLIANCE_FOUNDRY_DEPLOYMENT YOUR-DEPLOYMENT
azd env set COMPLIANCE_FOUNDRY_USE_MANAGED_IDENTITY true

After explicitly approving this deployment's customer-code processing, set COMPLIANCE_FOUNDRY_APPROVED_FOR_CODE_REVIEW to true. Leave it false until that review is complete. Grant the worker's user-assigned identity the Cognitive Services OpenAI User role on the Foundry resource, then run azd provision to apply settings to both services. These settings do not create a model deployment or assign its inference role. Alternatively, set COMPLIANCE_FOUNDRY_API_KEY using protected server-side configuration; Bicep treats it as a secure parameter and stores it in Container App secrets rather than plain environment values. Do not commit the azd environment or print its credentials.

For local Docker Compose, put the shared provider's PlatformAi__Foundry__* settings and separate code-review approval in the ignored .env file, or use the table's Compliance__Foundry__* values for a dedicated deployment. Recreate both services with docker compose up -d --build api worker. The shared provider requires access to a managed identity endpoint; use a dedicated deployment with a server-side API key when local containers cannot obtain managed identity tokens. Compose passes the same configuration to both services. Once configured, refresh the portal, enable AI review, accept the source-processing disclosure and save before rescanning. Configuration availability does not verify inference permissions or model compatibility; failed reviews still preserve local findings.

Run npm run build -w @backify/analyzer when packaging the worker. The existing worker Docker build includes both analyzer bundles. Build artifacts and migrations must be deployed with the API/worker change. Apply the generated DataComplianceAssistance migration through the normal deployment process; no existing application records or stateful resources are removed.

Use an appropriate regional or EU Data Zone deployment when your policy requires its processing boundary; a resource's address or location alone does not establish inference residency. Configure Foundry Prompt Shields/guardrails for document-based prompt injection as appropriate. Backify also uses a read-only prompt and validates evidence, but these are not a guarantee against model mistakes or prompt injection. HTTP redirects are disabled, provider request logging is removed, response size/time limits are enforced, and provider error bodies are not written to reports.

Review Microsoft's Foundry data-processing documentation, including abuse monitoring and stateful features. store: false is not a promise of zero provider retention. Foundry usage can incur Azure charges; there is no automatic model selection or new resource creation.

Human review

Managers can mark findings Open, Acknowledged, Mitigated (self-assessed) or False positive (self-assessed) with a short note. Do not include personal data or secrets in notes. Reviews use optimistic concurrency, and audit events record the action without duplicating the note.

Unchanged finding fingerprints may retain earlier reviews across scans. Changes to the relevant source expression produce new static fingerprints. Review statuses are not automatic proof of remediation: reassess them when processing, permissions, contracts or operating practices change. Historical reports are read-only in the portal; current findings can be reviewed.

Authoritative legal source: Regulation (EU) 2016/679. Obtain qualified legal advice where necessary.