Handle data subject erasure (DSAR)
Erase one user’s data across a workspace and confirm the request finished.
A request erases one workspace. To erase a person from several workspaces, compute their pseudonym in each workspace with that workspace’s key, and submit one request to each workspace’s privacy-api.
The request lifecycle and every field are in the Privacy API reference.
Prerequisites
Section titled “Prerequisites”-
Enterprise installed (
enterprise.enabled: true). The DSAR routes are served in dev mode, so a license is not required. See Licensing & Features. -
spec.privacyset on the Workspace, so the operator deploys privacy-api. -
The user’s pseudonymized id (
virtualUserId) in this workspace. Sessions, memories and requests are keyed by it, so a raw email or user id matches nothing. Each workspace has its own pseudonym key, so the same person has a different id in each workspace. The id is the first 16 hex characters of a hash of the identifier the facade hashed.With no pseudonym key, the hash is SHA-256, the same in every workspace:
Terminal window printf %s '<raw-user-id>' | shasum -a 256 | cut -c1-16With a key, the hash is HMAC-SHA256 keyed with the workspace’s key. The operator keeps it in the Secret
omnia-workspace-pseudonym-keyin the workspace namespace:Terminal window export OMNIA_PSEUDONYM_HMAC_KEY="$(kubectl --context <ctx> -n <workspace-namespace> \get secret omnia-workspace-pseudonym-key -o jsonpath='{.data.key}' | base64 -d)"printf %s '<raw-user-id>' | openssl dgst -sha256 -hmac "$OMNIA_PSEUDONYM_HMAC_KEY" -r | cut -c1-16If you cannot read Secrets in the workspace namespace, derive the key from the install key and the scope pinned on the workspace namespace instead. See Compute a workspace key. An install with no key has no
omnia-workspace-pseudonym-keySecret.Identifiers that start with
system:are always hashed with plain SHA-256.session-api, memory-api and privacy-api each bind their database to their workspace key’s fingerprint and refuse to start under another. privacy-api sends its fingerprint with every erasure, and a session-api or memory-api bound to another key refuses it with
409 pseudonym_key_mismatch, so the request fails instead of reporting success with nothing deleted. See Pseudonym key. -
A way to reach privacy-api. Its Service is ClusterIP only:
privacy-<workspace>on port 8080, in the namespace named by the Workspace’sspec.namespace.name. Forward it locally:Terminal window kubectl port-forward -n <workspace-namespace> svc/privacy-<workspace> 8087:8080 -
A bearer token, when
internalServiceAuth.enabledis true (the chart default). privacy-api then requires a ServiceAccount token with audienceomnia-session-apifrom a ServiceAccount in the workspace namespace:Terminal window export SA_TOKEN=$(kubectl create token default -n <workspace-namespace> --audience omnia-session-api)With
internalServiceAuth.enabledset to false, drop theAuthorizationheader from the commands below.
Submit the request
Section titled “Submit the request”-
Send
POST /api/v1/privacy/deletion-request. The call returns202 Acceptedand processing continues in the background.Terminal window curl -sS -X POST http://localhost:8087/api/v1/privacy/deletion-request \-H "Authorization: Bearer $SA_TOKEN" \-H "Content-Type: application/json" \-d '{"virtualUserId": "<pseudonymized-user-id>","reason": "gdpr_erasure","scope": "all"}'The request erases all of the user’s sessions, media and memories in the workspace. privacy-api cannot erase by date, so a
date_rangescope or adateFromordateTofield returns400. See the request fields. -
Copy the
idfrom the response. Itsstatusispending.
Track the request
Section titled “Track the request”Send GET /api/v1/privacy/deletion-request/{id}:
curl -sS http://localhost:8087/api/v1/privacy/deletion-request/<id> \ -H "Authorization: Bearer $SA_TOKEN"status moves from pending to in_progress and ends at completed or failed. The response includes sessionsDeleted and an errors array.
To list every request for one user, newest first, send GET /api/v1/privacy/deletion-requests?virtualUserId=<id>. The alias virtual_user_id also works.
Verify
Section titled “Verify”The request is done when status is completed and errors is empty. Illustrative response:
{ "id": "<request-id>", "virtualUserId": "<pseudonymized-user-id>", "status": "completed", "sessionsDeleted": 12, "errors": []}privacy-api writes deletion_requested when it accepts the request and deletion_completed or deletion_failed when it ends. The rows are in the audit_log table of the privacy-api database, with source_service privacy-api-dsar. See the Privacy API reference for the rest of the privacy surface.
Troubleshooting
Section titled “Troubleshooting”The request is failed
Section titled “The request is failed”Read errors. Each entry names the service group and the tier that failed, for example group <name> sessions: ... or group <name> memory: .... Data erased before the failure stays erased. Fix the cause, then submit a new request for the same user; a finished request is not retried.
If errors reads no service group to erase from, privacy-api found no session-api or memory-api URL in the Workspace status when it started. Check status.services on the Workspace, then restart privacy-api so it reads the status again.
The request is completed with sessionsDeleted 0
Section titled “The request is completed with sessionsDeleted 0”Check that virtualUserId is the pseudonym and not the raw identifier.
The API returns 401
Section titled “The API returns 401”internalServiceAuth.enabled is true and the token is missing, expired, has the wrong audience, or belongs to a ServiceAccount outside the workspace namespace.
The API returns 404
Section titled “The API returns 404”privacy-api serves the DSAR routes only when Enterprise is installed. Check that the privacy-<workspace> Service exists, which needs spec.privacy on the Workspace.