How the GDPR Storage Erasure Endpoint Cascades Deletes Across D1, Hyperdrive, and OAuth KV

The GDPR storage erasure endpoint in Open SEO orchestrates a coordinated purge across Durable Objects, R2 storage, and OAuth KV by invoking eraseStorage in src/server/gdpr/storage-erasure.ts, which terminates active workflows, destroys DO sessions, batch-deletes R2 objects, and recursively wipes OAuth grants and tokens in a single atomic request.

When handling data privacy requests under GDPR, the Open SEO platform (available at every-app/open-seo) provides a comprehensive storage erasure mechanism. The /api/internal/gdpr-erasure/storage endpoint triggers a cascading deletion sequence that ensures user data is removed from every persistent layer—including Cloudflare Durable Objects, Hyperdrive-backed R2 storage, and OAuth KV namespaces.

How the GDPR Storage Erasure Endpoint Works

The erasure flow begins when the handleGdprStorageErasure handler receives a validated POST request. The handler parses the payload using gdprStorageErasurePayloadSchema, then delegates to the core eraseStorage function. This function executes deletions in a specific order to prevent data inconsistency: first terminating active workflows, then purging storage layers, and finally revoking external OAuth grants.

The coordinated approach ensures that once the endpoint returns a success response, the user’s personal data has been purged from every persisted layer used by the application.

Cascading Deletions Across Storage Layers

The eraseStorage function implements distinct deletion strategies for each storage backend, handling dependencies between systems to ensure complete data removal.

Durable Objects (D1) Cleanup

For Durable Objects, the endpoint targets two specific session types:

  • Sam-chat sessions: For each samSessionId provided in the payload, the code obtains the DO via env.SAM_CHAT and invokes destroyForErasion() (lines 191–198). This permanently destroys the session state stored in the Durable Object.
  • Audit scratchpad DOs: For each auditId, the system calls env.AUDIT_ENGINE.destroyScratchpad(auditId) (lines 199–202), removing temporary computation state associated with audit workflows.

These operations run sequentially within the erasure flow to ensure that all session artifacts are cleared before proceeding to object storage cleanup.

Hyperdrive (R2) Object Removal

The Hyperdrive-backed R2 cleanup handles both user-generated content and application cache entries:

  • User-generated objects: The endpoint batch-deletes specific R2 keys provided in the r2Keys payload array using env.R2.delete (lines 218–220).
  • Prompt-cache cleanup: For each organizationId in the payload, deleteOrganizationPromptCaches lists all objects under the AI_SEARCH_PROMPT_CACHE_NAMESPACE prefix, filters them by organization membership, and removes them in bulk (lines 221–224). The function returns the total count of removed cache objects via promptCacheObjects (lines 242–244) for audit reporting.

OAuth KV Grant and Token Purge

The OAuth cleanup requires recursive deletion to handle the grant-token relationship:

  • Grant enumeration: deleteOauthGrants iterates over all keys matching the prefix grant:{userId}: (lines 96–108).
  • Token cascade: For each grant discovered, the system calls deleteKvPrefix to wipe associated token keys matching token:{userId}:{grantId}: (lines 100–103).
  • Grant removal: After purging dependent tokens, the grant itself is deleted (line 104).

This ensures that orphaned tokens cannot persist after their parent grants are removed, maintaining referential integrity during the erasure process.

Pre-Cleanup Workflow Termination

Before storage deletion begins, the endpoint prevents data resurrection by terminating active computations:

  • Audit workflows: terminateWorkflows is invoked on env.SITE_AUDIT_WORKFLOW (lines 176–184).
  • Rank-check workflows: The same termination logic applies to env.RANK_CHECK_WORKFLOW.

Stopping these workflows prevents them from recreating data after the storage wipe initiates.

Additional Cascade Steps

The erasure process includes several ancillary cleanup operations:

  • Google account revocation: Each Google account in the payload is revoked via the Google OAuth revocation endpoint through revokeGoogleAccount (lines 138–172).
  • Dub referral cleanup: The system deletes Dub-referral keys for both the user and associated organizations using env.KV.delete (lines 213–216).

Calling the GDPR Storage Erasure Endpoint

cURL Request Example

Invoke the endpoint with proper HMAC signing headers:

curl -X POST https://your-open-seo-instance.com/api/internal/gdpr-erasure/storage \
  -H "Content-Type: application/json" \
  -H "x-gdpr-timestamp: $(date +%s%3N)" \
  -H "x-gdpr-signature: $(node -e \"const {signGdprErasureRequest}=require('./src/shared/gdpr-erasure'); signGdprErasureRequest(process.env.GDPR_ERASURE_SECRET, Date.now().toString(), JSON.stringify(<payload>)).then(s=>process.stdout.write(s))\")" \
  -d '{"userId":"user_123","organizationIds":["org_456"],"samSessionIds":["sam_abc"]}'

Payload Structure

The payload must conform to gdprStorageErasurePayloadSchema:

import { gdprStorageErasurePayloadSchema } from '@/shared/gdpr-erasure';

const payload = {
  userId: 'user_123',
  organizationIds: ['org_456'],
  activeAuditWorkflowIds: [],
  activeRankWorkflowIds: [],
  samSessionIds: ['sam_abc'],
  auditIds: ['audit_xyz'],
  r2Keys: ['uploads/user_123/file.png'],
  googleAccounts: [
    { providerId: 'google', accountId: 'google_789' }
  ]
};

gdprStorageErasurePayloadSchema.parse(payload); // validates

Programmatic Invocation

For internal scripts, use the signing utilities provided in the shared module:

import { signGdprErasureRequest, GDPR_STORAGE_ERASURE_PATH } from '@/shared/gdpr-erasure';

const response = await fetch(`${env.BASE_URL}${GDPR_STORAGE_ERASURE_PATH}`, {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'x-gdpr-timestamp': timestamp,
    'x-gdpr-signature': await signGdprErasureRequest(secret, timestamp, JSON.stringify(payload)),
  },
  body: JSON.stringify(payload),
});

Key Implementation Files

The GDPR erasure logic spans several critical files in the every-app/open-seo repository:

  • src/server/gdpr/storage-erasure.ts: Contains the core eraseStorage function, workflow termination logic, and coordinated deletion orchestration.
  • src/shared/gdpr-erasure.ts: Defines the gdprStorageErasurePayloadSchema, HMAC signing helper (signGdprErasureRequest), and the endpoint path constant (GDPR_STORAGE_ERASURE_PATH).
  • src/lib/oauth-resource.ts: Provides the env.OAUTH_KV namespace used by the deleteOauthGrants function.
  • src/lib/r2-cache.ts: Supplies the AI_SEARCH_PROMPT_CACHE_NAMESPACE constant referenced by deleteOrganizationPromptCaches.
  • src/server.ts: Registers the route handler (handleGdprStorageErasure) for the internal endpoint.
  • scripts/erase-user-data.ts: Example administrative script demonstrating payload assembly and authenticated invocation.

Summary

  • The GDPR storage erasure endpoint at /api/internal/gdpr-erasure/storage processes deletion requests through handleGdprStorageErasure in src/server/gdpr/storage-erasure.ts.
  • Durable Objects are purged via destroyForErasion() for chat sessions and destroyScratchpad() for audit engines.
  • R2 storage cleanup uses env.R2.delete for user objects and filtered prefix deletion for organization prompt caches.
  • OAuth KV requires recursive deletion of grants and their dependent tokens using prefix scanning.
  • Active workflows are terminated before storage deletion to prevent data resurrection during the erasure process.
  • All operations require HMAC-signed requests using signGdprErasureRequest from src/shared/gdpr-erasure.ts.

Frequently Asked Questions

What storage systems does the GDPR erasure endpoint target?

The endpoint targets Cloudflare Durable Objects (for session and scratchpad state), R2 object storage (for user uploads and AI prompt caches), and OAuth KV (for grant and token persistence). It also clears Dub referral tracking data from general KV storage.

How does the endpoint prevent data resurrection during erasure?

The system calls terminateWorkflows on both env.SITE_AUDIT_WORKFLOW and env.RANK_CHECK_WORKFLOW (lines 176–184) before executing any storage deletions. This ensures that active background jobs cannot recreate data after the purge begins, effectively freezing the data state before erasure.

What authentication is required to call the GDPR storage erasure endpoint?

Requests must include an x-gdpr-timestamp header and an x-gdpr-signature header generated by the signGdprErasureRequest function. The signature is computed using a shared secret and the request timestamp to prevent replay attacks and unauthorized erasure requests.

The deleteOauthGrants function iterates over all KV keys matching the prefix grant:{userId}:. For each grant found, it first deletes all associated tokens matching token:{userId}:{grantId}: via deleteKvPrefix, then removes the grant record itself. This cascade ensures no orphaned authentication tokens remain after the parent grants are removed.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →