GDPR Procedures

GDPR data subject request handling: access, erasure, and portability procedures for each SanMarcSoft service

Overview

SanMarcSoft services operate under EU jurisdiction (infrastructure in Scaleway fr-par). All services must comply with GDPR requirements for data subject rights. This runbook covers handling data subject requests (DSRs) for access, erasure, and portability.

Data Subject Rights

RightArticleDeadline
Right of AccessArt. 1530 days
Right to ErasureArt. 1730 days
Right to PortabilityArt. 2030 days
Right to RectificationArt. 1630 days
Right to RestrictionArt. 1830 days

Service-by-Service Data Inventory

Verifieddit (verifieddit.com)

Data stored:

  • Clerk user profile (email, name, auth provider)
  • D1 badges table (URLs, domains, timestamps)
  • D1 badge_images (image URLs, AI scores)
  • D1 users table (clerk_id, email, plan)

Data locations:

  • Clerk (US-hosted, EU data processing agreement)
  • Cloudflare D1 (edge, geo-distributed)
  • Scaleway Container Registry (fr-par, EU sovereign)

Phenom Drop

Data stored:

  • Submitted media files (S3)
  • Firestore drop payloads (email, consent records)
  • C2PA signed media

Data locations:

  • AWS S3 (us-east-1)
  • Google Firestore
  • AWS App Runner (ephemeral)

Badges Worker

Data stored:

  • Badge records (D1)
  • Cached AI detection results

Data locations:

  • Cloudflare D1 (edge)
  • Cloudflare KV (edge, if caching enabled)

Procedure: Right of Access (Art. 15)

Step 1: Identify the Data Subject

Verify the requester’s identity. Require government-issued ID or verification via their registered email.

Step 2: Collect Data from Each Service

From Clerk

1
2
3
4
5
6
CLERK_SECRET=$(pass verifieddit/clerk/secret-key)

# Search by email
curl -s -H "Authorization: Bearer ${CLERK_SECRET}" \
  "https://api.clerk.com/v1/users?email_address=user@example.com" | \
  jq '.data[] | {id, email: .email_addresses, first_name, last_name, created_at, last_sign_in_at}'

From D1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Get user record
npx wrangler d1 execute verifieddit-badges \
  --command "SELECT * FROM users WHERE email = 'user@example.com'" --json

# Get associated badges
CLERK_ID="<clerk-id-from-above>"
npx wrangler d1 execute verifieddit-badges \
  --command "SELECT * FROM badges WHERE user_id = '${CLERK_ID}'" --json

# Get badge images
npx wrangler d1 execute verifieddit-badges \
  --command "SELECT bi.* FROM badge_images bi JOIN badges b ON bi.badge_id = b.id WHERE b.user_id = '${CLERK_ID}'" --json

From Phenom Drop (Firestore)

Query Firestore for records matching the user’s email:

1
2
# This requires Firebase Admin SDK access
# Query: firestore.collection('drops').where('submitterEmail', '==', 'user@example.com')

Step 3: Compile and Send

  1. Compile all data into a structured format (JSON or PDF)
  2. Send to the requester via their verified email
  3. Log the access request and response date

Procedure: Right to Erasure (Art. 17)

Step 1: Verify Identity

Same as Right of Access.

Step 2: Delete from Each Service

Delete from Clerk

1
2
3
4
5
6
CLERK_SECRET=$(pass verifieddit/clerk/secret-key)
USER_ID="<clerk-user-id>"

curl -s -X DELETE \
  -H "Authorization: Bearer ${CLERK_SECRET}" \
  "https://api.clerk.com/v1/users/${USER_ID}"

Delete from D1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CLERK_ID="<clerk-id>"

# Delete badge images first (foreign key)
npx wrangler d1 execute verifieddit-badges \
  --command "DELETE FROM badge_images WHERE badge_id IN (SELECT id FROM badges WHERE user_id = '${CLERK_ID}')"

# Delete badges
npx wrangler d1 execute verifieddit-badges \
  --command "DELETE FROM badges WHERE user_id = '${CLERK_ID}'"

# Delete user record
npx wrangler d1 execute verifieddit-badges \
  --command "DELETE FROM users WHERE clerk_id = '${CLERK_ID}'"

Delete from Phenom Drop

  1. Delete S3 media files:
1
aws s3 rm s3://phenom-drop-media/drops/<drop-id>/ --recursive
  1. Delete Firestore records:
1
2
# Via Firebase Admin SDK or console
# Delete all documents where submitterEmail matches

Step 3: Confirm Deletion

  1. Verify deletion by re-querying each service
  2. Send confirmation to the data subject
  3. Log the erasure request and completion date

Exceptions to Erasure

Data may be retained if required for:

  • Legal compliance (e.g., tax records)
  • C2PA provenance chain integrity (content credentials are permanent by design – inform the user)
  • Ongoing legal proceedings

Procedure: Right to Data Portability (Art. 20)

Step 1: Export Data

Follow the same collection steps as Right of Access, but export in a machine-readable format (JSON).

Step 2: Package and Deliver

1
2
3
4
# Create export package
mkdir -p export-${USER_EMAIL}
# Save each service's data as separate JSON files
# Zip and send via secure channel

Request Tracking

Log all DSR requests with:

  • Request date
  • Data subject identifier
  • Request type (access/erasure/portability)
  • Services affected
  • Completion date
  • Notes

Response Timeline

  • Acknowledge receipt within 3 business days
  • Complete the request within 30 calendar days
  • If complex, extend to 60 days with notification to the data subject

Troubleshooting

  • Cannot find user data: Search by all possible identifiers (email, Clerk ID, Firestore document ID).
  • Clerk user already deleted: Data may still exist in D1 and S3. Clean up orphaned records.
  • S3 objects not deletable: Check bucket versioning. If enabled, objects are soft-deleted and must be permanently removed.