Repository navigation
feat(core): personal data in events, forgotten by crypto-shredding - #213
Open
benatespina wants to merge 2 commits into
Open
benatespina wants to merge 2 commits into
benatespina wants to merge 2 commits into
Conversation
Events are never deleted, so a person's right to erasure needs another answer than deleting rows. An event's payload now takes personal and person next to z: a field marked personal is stored encrypted with a key of the person the event names, one per person in the adapter's database, and forget(personId) throws that key away and appends PersonalDataForgotten to the person's stream, in one transaction. The field then reads as null everywhere, and the types say so: T | null for whoever reads it, T for whoever writes it, so the compiler finds every place that assumed the data would always be there. Forgetting is an event because what the log held has leaked into read models and the outside world: a read model that projects a personal field must project PersonalDataForgotten too, which the generator checks through a type in .bounda/types.ts, and a policy on it tells the providers. The envelope carries a fingerprint of the key, so a person forgotten and registered again reads their old events as null next to the new ones. The global stream shows the fields as stored.
…orget Under read-your-writes a forget only waits for the read models that handle it; the others catch up in the alarm, which may or may not have fired by the time the test looked, so the assertion raced the runtime.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
personalin an event's payload is stored encrypted with a key of the person the event names, andforget(personId)deletes the key and appendsPersonalDataForgotten, in one transaction. The field reads asnullfrom then on, in the types too.PersonalDataForgotten; the generator checks it through a type in.bounda/types.ts. A policy on the same event tells the outside world.Changes
core:EventPayloadArgswithpersonalandperson;PersonalSchema/PersonSchematypedT | nullon read,Ton write;HasPersonalFieldsandForgetsPersonalData;PersonalKeyStoreonStorageandStorageTransaction, in memory and SQLite (core/adapter/sqlite); the event store wrapper that seals and opens fields (AES-256-GCM through WebCrypto, envelope with the person's id and the key's fingerprint);forgetonBoundaClient; theboundaaggregate andPersonalDataForgotten; storage contracts for the key store and the transaction.postgresql:personal_keystable and store.cloudflare:forgeton the object, the stub andconnect;POST /forget/<personId>on the Worker; the contracts run inworkerd.react-router:forgeton the lazy stub.cli:projections/bounda/andpolicies/bounda/,core.EventPayloadArgsfor events,<ReadModel>Forgettingin.bounda/types.ts,boundarefused as an aggregate name; fixtures regenerated, with acustomer-directoryread model and personal fields incustomer-registeredandorder-placed.concepts/forgetting-in-an-immutable-log,guides/personal-data, and conventions, limitations, reading the stream, Cloudflare, project layout, read models and adapters updated. Changesetminor.Decisions
person; an event names at most one. Marks sit on top-level fields; a nested object or array is sealed as one value.null, not a sentinel or a default: explicit, JSON-safe, and the type forces every reader to handle it.bounda, one stream per person, so no domain name is taken.Quality
pnpm checkgreen, three full runs. Stryker on the files touched: core 97 %, postgresql 97.6 %, react-router 100 %, cli and cloudflare over their thresholds./code-reviewathigh: one real bug fixed (a person forgotten and registered again had old envelopes that failed to decrypt with the new key; envelopes now carry the key's fingerprint and read asnull), plus a worker route hardened against malformed escapes, a doc example that was a no-op, and.transform()afterpersonal()keeping the mark.