Skip to main content

Data Erasure Policy

The data-erasure policy category enforces pending erasure requests as required by GDPR Art-17 ("Right to Erasure") and CCPA §1798.105 (Right to Delete). When an end-user has filed an erasure request:

  • Agents must NOT continue to process that subject's data
  • New memory writes referencing the subject must be blocked
  • The erasure must complete within the regulatory SLA (default 30 days)

This handler is the bridge between data-access (which enforces source allowlists but knows nothing about pending requests) and memory (which writes/reads but doesn't track deadlines).

Rules

RuleTypeDefaultDescription
max_pending_daysinteger30Hard SLA -- erasure must complete within N days of request (GDPR default 30)
block_processing_for_subjectsbooleantrueBlock any agent run scoped to a subject with a pending request
block_writes_for_subjectsbooleantrueBlock new memory writes referencing a subject with a pending request
warn_threshold_daysinteger25Warn when a request has been pending more than N days (approaching SLA)
action_on_violationstring"block"Action on violation: "block" or "warn"

How It Works

The data-erasure handler runs at before_workflow, mid_execution, and after_workflow. It performs four checks in order:

  1. SLA check -- any pending request past max_pending_days violates the policy regardless of who the current run is for
  2. Run-scoped check -- if this run's subject(s) overlap with pending requests, refuse to process
  3. Write-scoped check -- block memory writes whose string repr contains a pending-request subject id
  4. Warn check -- warn on requests approaching SLA so the tenant can prioritize

Context Attributes Read

AttributePhasePurpose
context.metadata["erasure_requests"]allList of {sub_user_id, requested_at} or {user_id, requested_at} (tenant-populated)
context._sub_user_identity / context.sub_user_identityallResolve the run's subject (preferred over user_id)
context.user_idallFallback subject identifier
context.memory_writesallInspect each write for subject references

requested_at accepts ISO strings or float epoch seconds.

Example Policy

{
"max_pending_days": 30,
"block_processing_for_subjects": true,
"block_writes_for_subjects": true,
"warn_threshold_days": 25,
"action_on_violation": "block"
}

SDK Integration

import waxell_observe as waxell
waxell.init()

@waxell.observe(agent_name="support-agent", enforce_policy=True)
async def handle_ticket(ticket: dict) -> dict:
return await respond(ticket)

Tenants populate erasure requests from their PrivacyAdmin service:

ctx.metadata["erasure_requests"] = [
{"sub_user_id": "user_123", "requested_at": "2026-05-01T10:00:00Z"},
{"sub_user_id": "user_456", "requested_at": "2026-05-20T14:30:00Z"},
]

Observability

FieldExample
Categorydata-erasure
Actionblock
Reason"Subject(s) ['user_123'] have pending erasure requests; further processing of their data is prohibited under GDPR Art-17."
Metadata{"signal": "erasure_subject_processed", "subject_ids": ["user_123"], "gdpr": "Art-17"}

Possible signal values: erasure_sla_overdue, erasure_subject_processed, erasure_subject_write, erasure_sla_approaching.

Common Gotchas

  1. Write detection is substring-based. The handler does str(write).lower() and checks for any pending-subject id as a substring. If your subject ids are short or numeric ("42"), you will get false positives on writes that happen to contain "42" anywhere. Use long, unique subject ids (UUIDs, prefixed slugs).

  2. erasure_requests is tenant-populated. This handler does NOT query your privacy admin database. You must populate context.metadata["erasure_requests"] from your PrivacyAdmin service (typically via a middleware that injects it on every run).

  3. SLA violations block ALL runs, not just the affected subject's. If any pending request is past max_pending_days, the handler blocks the current run regardless of who it is for -- this is intentional pressure on the tenant to complete erasure.

  4. Sub-user identity is preferred over user_id. The handler reads _sub_user_identity first, falls back to user_id. If you have both, make sure they identify the same person -- or only set the one that matches your erasure_requests schema.

  5. Memory writes need to be on context.memory_writes. Memory writes that bypass the context (direct DB inserts, raw cache writes) will not be detected. Wire memory operations through the runtime to get coverage.

  6. warn_threshold_days >= max_pending_days is silently useless. SLA-overdue triggers first; the warn branch only fires for requests between warn_threshold_days and max_pending_days.

Next Steps