Enterprise & Compliance · Chapter 1 of 2

GDPR Subject Access Request Export

Article 15 of the GDPR (and § 15 BDSG, the German implementation) gives every natural person whose personal data is processed the right to receive a copy of that data on request. For a packaging tool, the relevant personal data is the DOMAIN\user actor identifier that the Memory Database stores against every change — who built which package, who pushed which distribution, who renamed which software identity.

10 min read
Applies to v2.0+
Last updated 2026-07-19
PUBLISHED

Article 15 of the GDPR (and § 15 BDSG, the German implementation) gives every natural person whose personal data is processed the right to receive a copy of that data on request. For a packaging tool, the relevant personal data is the DOMAIN\user actor identifier that the Memory Database stores against every change — who built which package, who pushed which distribution, who renamed which software identity.

MAS turns the Article 15 reply into a five-click workflow: enter the subject, preview the bundle, save the JSON, archive it with the request record. The bundle is signed (audit-chain integrity), self-contained (no external lookups needed to interpret it), and deterministic (the same subject against the same database produces a byte-equivalent bundle).

This chapter covers what the bundle contains, how to run the export, how to interpret the result, and how the workflow fits into the wider SAR response process.


When this matters #

Every employee whose Windows account has touched the Memory Database appears as a DOMAIN\user actor in at least one of:

  • software_identities.LastModifiedBy — last edit to an app-vendor row
  • package_records.PackagedBy — author of a specific build
  • distribution_records.DistributedBy — operator of a specific push
  • audit_log.Actor — every security-relevant write

That data is personal data under the GDPR. A current or former employee can request a copy of it. Under § 15 BDSG, MAS' role is the data processor's tool — your organisation is the controller and owes the reply within one month of the request.

The GDPR Export feature ships in every MAS 2.0 install — the workflow is small, the compliance value is high, and there is no reason to gate it behind a tier. Even a single-operator install can produce useful SAR bundles when the operator's own account has to be documented.


What the bundle contains #

The bundle is a single UTF-8 JSON file with the following structure (v2, since MAS 1.26.5.35):

Section Content
Header Subject identifier, export-time UTC timestamp, operator who triggered the export, MAS installation hardware fingerprint, schema version.
softwareIdentitiesAsLastModifier Every SoftwareIdentity row where the subject was the most recent editor. Includes app name, vendor, upgrade code, first-seen, last-seen, and the optimistic-lock row version.
auditEntriesAsActor Every AuditEntry row produced by the subject. Each entry carries event type, entity type, entity id, timestamp, and the optional payload JSON that describes the change (before/after snapshots, deletion records, etc.).
relatedPackageRecords (v2) Every PackageRecord that the subject's audit entries reference. Joined via AuditEntry.EntityId for entries with EntityType = "PackageRecord". Surfaces the actual build snapshots the subject worked on, not just the audit-trail-pointers.
relatedDistributionRecords (v2) Every DistributionRecord referenced by the subject's audit entries (EntityType = "DistributionRecord"). Same logic as related package records.
summary Counts per section, plus the earliest and latest activity timestamps that bracket all returned data.

Why the v2 cross-reference matters. The v1 bundle proved the audit trail (the subject did X at time Y). The v2 bundle additionally attaches the current state of the records the subject touched, so the compliance officer can answer "and what was X actually about?" without a second tool. Records that have been deleted between the original audit-emit and the export are silently skipped from the related sections — the audit row's payloadJson still carries a forensic snapshot, so the SAR trail stays intact even when the live row is gone. The summary counts reflect what was actually loaded, not what was referenced, so a delta between auditEntryCount (with EntityType = "PackageRecord") and relatedPackageRecordCount is the count of records the subject acted on that have since been removed.

What the bundle does NOT contain. No personal data about other operators (their actor strings appear only when the requested subject edited a row that another operator had previously last-touched, in which case that other operator's DOMAIN\user shows up as LastModifiedBy on a different snapshot — that's intrinsic to the data, not an export choice). No license keys, no email addresses, no SCCM domain credentials. No PSADT package binaries — the bundle references package output folders by path, but the disk contents are out of scope.


How to run an export #

Navigation: side-rail → Settings → Database → Subject access export.

  1. Enter the subject. The field is an auto-suggest combo populated from audit_log.distinct(actor) — start typing a domain prefix and the matching actors surface. The match against the database is case-sensitive verbatim, because Windows allows both CONTOSO\user and contoso\user as legal forms and silently merging them would risk attaching the wrong person's data to the reply.

  2. Preview. Click Preview and MAS runs the collect step against the live database. The preview card shows the bundle header, the row counts per section, and the earliest/latest activity timestamp. No file has been written yet — this step is read-only and safe to run repeatedly while you confirm you have the right subject.

  3. Save. Click Save bundle and a Windows file picker opens with a pre-filled name like contoso-user-sar-20260608.gdpr.json. The picker enforces the .json extension. Pick the destination folder (see § Compliance storage path) and confirm.

  4. Archive the request and the reply together. The bundle is the reply artefact. Store it next to the subject's request letter or ticket — your retention policy for SAR documents covers both.

  5. Verify the audit trail. The export itself is audit-logged as a GdprExport entry (event type and entity type both GdprExport, entity id 00000000-0000-0000-0000-000000000000 because the export spans many rows). The hash chain (chapter 650) carries the proof that no row was added or removed between the export and the verification.

An empty bundle is a valid Article 15 reply: it documents that you searched and found no data on file for the named subject. The Save button stays armed even when the preview shows zero rows, so an empty bundle can be produced as a proof-of-search artefact.


Bundle format spec (for compliance teams) #

The bundle is plain UTF-8 JSON, indented for readability, camelCase keys. The producer code lives in the open in src/MAS.Core/Models/GdprExportBundle.cs and src/MAS.Infrastructure/Compliance/GdprExportService.cs, so an external reviewer can verify the bundle shape against the source.

Top-level keys:

jsonc
{
  "schemaVersion": 2,
  "subject": "CONTOSO\\jdoe",
  "generatedAt": "2026-06-08T14:23:11.4521234+00:00",
  "generatedBy": "CONTOSO\\compliance-officer",
  "masInstallationFingerprint": "a4f2e1b3...c8d9",
  "softwareIdentitiesAsLastModifier": [ /* SoftwareIdentity rows */ ],
  "auditEntriesAsActor":              [ /* AuditEntry rows */ ],
  "relatedPackageRecords":            [ /* PackageRecord rows, v2+ */ ],
  "relatedDistributionRecords":       [ /* DistributionRecord rows, v2+ */ ],
  "summary": {
    "softwareIdentityCount":          12,
    "auditEntryCount":                47,
    "relatedPackageRecordCount":      9,
    "relatedDistributionRecordCount": 14,
    "earliestActivity":               "2026-01-04T08:11:00+00:00",
    "latestActivity":                 "2026-06-07T16:42:11+00:00"
  }
}

Schema versioning follows the same additive rule as the rest of MAS' persisted formats: a v2-aware reader gets the enriched view, a v1-aware reader sees the new fields as unknown JSON properties and ignores them. A future v3 would do the same — add new top-level sections without removing or renaming existing ones. A breaking change would bump to a major version and ship with a migration tool.


Compliance storage path #

The GDPR-export file ends up in the path the operator picks. There is no MAS-managed compliance vault — the file is yours from the moment it's saved. Recommendation, based on what audit-friendly customers do:

Aspect Recommendation
Location A dedicated subfolder under your compliance share, structured by year and SAR-ID: \\compliance.lab\sar\2026\REQ-0042\.
Naming Keep the MAS-suggested filename (<subject-slug>-sar-YYYYMMDD.gdpr.json) so the file is self-documenting in directory listings.
Retention Aligns with your SAR-retention policy. The bundle is the documented evidence of the Article 15 reply; under § 41 BDSG and standard data-protection-officer guidance, three years from the date of the reply is the typical floor.
Access control Read-restricted to the data protection team. The bundle contains personal data about the subject — same handling as the original SAR request letter.
Integrity check Optional but recommended: compute the SHA-256 of the file at save time and store it in the SAR ticket. If the file is ever modified, the hash diverges and the tamper is visible.
Backup Covered by your standard compliance-share backup policy. The bundle is small (typically under 100 KB for a single active operator), so backup volume is not a concern.

What the operator sees vs. what gets recorded #

The export workflow itself is audit-logged. Two audit rows are produced per export:

  1. GdprExport — written by the export service every time the Save step completes. Payload contains the subject identifier, the per-section counts, and the activity-span timestamps. This is the row a compliance auditor uses to prove that a specific SAR was actually fulfilled on a specific date by a specific operator.

  2. Hash-chain continuation — the GdprExport row is hashed into the audit chain like any other event (chapter 650), so removing it later breaks the chain at that row index. An auditor can verify from the chain that no GdprExport rows have been deleted to hide that an export happened.

The operator who runs the export sees a confirmation toast with the output path and the row counts. The operator does not see and cannot suppress the audit row — emission is built into the service.


Limits and known caveats #

Case-sensitive subject match. Mentioned above; worth repeating because it's the single most likely operator mistake. If the subject combo shows CONTOSO\jdoe but the data was originally written as contoso\jdoe, the preview returns an empty bundle. Always pick from the auto-suggest list rather than hand-typing.

Single-database scope. An export covers exactly the database MAS is currently connected to. If your organisation runs MAS instances against multiple databases (one per region, one per department), the operator must run the export once per database and concatenate the bundles into the SAR reply. The bundle header identifies the source database via the masInstallationFingerprint, so de-duplication on the receiving side is unambiguous.

Restored databases reset the chain. If the database has been restored from a portable backup (chapter 680), the audit-chain integrity verifies clean from the restore boundary forward, not all the way back. An auditor reviewing a GdprExport row that pre-dates the restore should cross-check the .masdb archive that captured the state before the restore.

Cross-reference drift. A relatedPackageRecordCount lower than the count of PackageRecord-typed audit entries reflects records that have been deleted since the original audit event. This is expected — the audit row's payloadJson is the canonical forensic record. If the request explicitly asks for the full state of every record the subject worked on, the operator should mention the drift in the SAR reply ("X records have been deleted in the normal course of operations; the audit log preserves a snapshot of each deletion").


External verification #

A compliance team that doesn't want to trust MAS' own output can verify the bundle's integrity independently:

  1. Audit chain. The bundle ships with the auditEntriesAsActor section that includes each row's prevHash and rowHash bytes (when the producing database has been backfilled — chapter 650 covers backfill). The Python reference verifier in docs/user-docs/A-Appendices/A4-Audit-Chain-Verify-Reference.py re-computes the chain over those bytes; the bundle is intact when the verifier reports IsValid = True.

  2. Subject filter. Every row in auditEntriesAsActor must have Actor == subject. Every row in softwareIdentitiesAsLastModifier must have LastModifiedBy == subject. A trivial grep over the JSON confirms the filter held.

  3. Cross-reference join. For each entry in auditEntriesAsActor with EntityType = "PackageRecord", the corresponding relatedPackageRecords[].id either exists or the row is documented as deleted (count drift, see above). Same for DistributionRecord.

  4. Producer source. The producer code is open in the MAS distribution (src/MAS.Core/Models/GdprExportBundle.cs and src/MAS.Infrastructure/Compliance/GdprExportService.cs). A compliance team can read it, build it locally, and confirm the bundle would have been identical if they had run the export themselves. This is the same logic as the Audit Chain chapter's "external Python verifier" — vendor-lock-in counter-evidence.


Up next · Enterprise & Compliance 2 of 2
Pipeline Health Reports
The Reports page surfaces what the Memory Database has recorded across a time range: build volume, distribution channel split, top software, compliance pre-check verdicts, and audit-chain integrity. T…
→