
Define what remains when personal data is erased
Removing a person’s data can involve several kinds of record. Some may be deleted; others may need to remain for a defined business purpose, with personal values removed or transformed.
Wildo lets the application express that treatment on its resources and fields. A retained row is marked, hidden from ordinary reads and protected from ordinary changes. The resource policy distinguishes keeping a row as it is from retaining it with declared field treatments.
This gives the application a consistent execution model for its chosen policy. The responsible team still decides which records should remain, why they are retained and for how long.
Example — Preserve a record without keeping it in daily work
Wonder Todos declares a retain-only policy for tasks. When a task becomes retained through the erasure process, ordinary task lists and charts exclude it, while controlled internal access can still account for the stored record. A resource containing personal values can instead declare field treatments with retain-and-impersonalize.
For engineers
Declare what the resource means on erasure
This excerpt from Wonder Todos’ todos.resources-config.ts includes the resource’s retention declaration and the operations required by its internal retention path. Source comments are omitted; the operation configurations follow later:
mainSchema: Todos_Schema,
resourceIdentifier: TasksManager_ResourceType.TODOS,
resourceFieldIdentifier: TasksManager_ResourceFieldIdentifier[TasksManager_ResourceType.TODOS],
resourceRelationships: resourcesRelationships,
inheritenceSchemaDefinition: TodosSchemaFamily.inheritenceSchemaDefinition,
isSystemResource: false,
systemAccessPolicy: {
list: ResourceSystemAccessMode.ALLOWED,
exportSubject: ResourceSystemAccessMode.ALLOWED,
},
retentionPolicy: { mode: ErasureRetentionMode.RETAIN_ONLY },
dataSubjectPolicy: { kind: ResourceDataSubjectKind.NO_PRINCIPAL },
coreOperations: [
CoreResourceOperation.READ,
CoreResourceOperation.LIST,
CoreResourceOperation.SEARCH,
CoreResourceOperation.CREATE,
CoreResourceOperation.UPDATE,
CoreResourceOperation.UPDATE_MANY,
CoreResourceOperation.DELETE,
CoreResourceOperation.COUNT
],
RETAIN_ONLY says that this resource is retained without field scrubbing. NO_PRINCIPAL separately says that the task itself is not a login identity whose sessions should be revoked. An assignee or creator reference does not make a task into that person’s account.
Choose field treatments where the row holds personal values
A resource using RETAIN_AND_IMPERSONALIZE declares what happens to its own personal fields. Treatments include MASK, REMOVE, RESET and explicit KEEP. HASH is not supported by this static scrub path, and reversible ENCRYPT is not an erasure treatment. The factory checks the relationship between the mode and the treatments so the policy describes work the writer can actually perform.
Wonder CRM’s contact provides the connected example. Its resource configuration selects:
retentionPolicy: { mode: ErasureRetentionMode.RETAIN_AND_IMPERSONALIZE },
dataSubjectPolicy: { kind: ResourceDataSubjectKind.NO_PRINCIPAL },
Its contact.schemas.ts declares the treatment of employer and name. Comments and other contact fields are omitted:
companyId: z.string().min(1).optional().isDBIndexed().isForeignKey().isSummaryField()
.impersonalizeWith(RedactionType.REMOVE)
.dataCategories(PersonalDataCategory.PROFESSIONAL),
name: z.string().min(1).max(120).isSummaryField()
.impersonalizeWith(RedactionType.MASK, { maskValue: '[erased]' })
.dataCategories(PersonalDataCategory.IDENTITY),
| Declared field | Scrub result | Why the choice fits |
|---|---|---|
Optional companyId · REMOVE | Clear the stored reference. | Stop recording who this person works for. |
Required name · MASK | Replace the value with [erased]. | Keep a usable retained-row shape without preserving the name. |
The contact is not a login principal, but it still holds personal data. NO_PRINCIPAL does not exempt it from erasure; it separates field treatment from revoking a user’s sessions. The full schema declares treatments for its other personal values as well.
Transforming a value is not by itself a claim that the remaining record is anonymous. Keep the resource’s meaning, its person references and the chosen treatment explicit. Relationship lifecycle rules independently decide how a child participates when its parent is erased.
Use the retained state consistently
The factory adds the retention marker and derives the internal permissions needed for the retention writer and controlled reads. The ordinary read path uses a shared hide predicate, including the database query paths used for listings and aggregation. Retained rows therefore stop participating in ordinary application work rather than merely disappearing from one screen.
The internal writer uses the required resource operations declared above, while the public operation variants keep their own access rules. This separation lets the application account for retained data without exposing it through its ordinary views.
The resource-level policy is about erasure behavior. A retention period, a row-expiry rule and a policy document are separate declarations. Use each for its own purpose, and describe the chosen record treatment in the application’s information and operating procedures.