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
samSessionIdprovided in the payload, the code obtains the DO viaenv.SAM_CHATand invokesdestroyForErasion()(lines 191–198). This permanently destroys the session state stored in the Durable Object. - Audit scratchpad DOs: For each
auditId, the system callsenv.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
r2Keyspayload array usingenv.R2.delete(lines 218–220). - Prompt-cache cleanup: For each
organizationIdin the payload,deleteOrganizationPromptCacheslists all objects under theAI_SEARCH_PROMPT_CACHE_NAMESPACEprefix, filters them by organization membership, and removes them in bulk (lines 221–224). The function returns the total count of removed cache objects viapromptCacheObjects(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:
deleteOauthGrantsiterates over all keys matching the prefixgrant:{userId}:(lines 96–108). - Token cascade: For each grant discovered, the system calls
deleteKvPrefixto wipe associated token keys matchingtoken:{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:
terminateWorkflowsis invoked onenv.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 coreeraseStoragefunction, workflow termination logic, and coordinated deletion orchestration.src/shared/gdpr-erasure.ts: Defines thegdprStorageErasurePayloadSchema, HMAC signing helper (signGdprErasureRequest), and the endpoint path constant (GDPR_STORAGE_ERASURE_PATH).src/lib/oauth-resource.ts: Provides theenv.OAUTH_KVnamespace used by thedeleteOauthGrantsfunction.src/lib/r2-cache.ts: Supplies theAI_SEARCH_PROMPT_CACHE_NAMESPACEconstant referenced bydeleteOrganizationPromptCaches.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/storageprocesses deletion requests throughhandleGdprStorageErasureinsrc/server/gdpr/storage-erasure.ts. - Durable Objects are purged via
destroyForErasion()for chat sessions anddestroyScratchpad()for audit engines. - R2 storage cleanup uses
env.R2.deletefor 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
signGdprErasureRequestfromsrc/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.
How are OAuth tokens and grants related to a user deleted?
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →