Help us shape the future of Go! Take the 2026 Go Developer Survey

erasure

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: AGPL-3.0 Imports: 24 Imported by: 0

Documentation

Overview

Package erasure is the one place a Contact's personal data is erased (GDPR Art. 17, ADR 0021). Deleting a Contact is Erasure: there is no softer variant. Every surface (the /site SPA API, the external /api and so MCP) hands it a Workspace-scoped client and an Identifier and gets back nil or a domain error; none re-implements what is removed, anonymized or kept.

The whole erasure is one transaction, opened through events.Bus.WithinScopedPurgeTx so the Contact change and the domain-event outbox commit together. Inside it the work is an ordered list of steps (see Module.steps), each a small function over the resolved Target; extending Erasure (another identifier kind, another dependent table, another signal published in the same transaction) means resolving the new identifier into a Target or adding one step, not a second code path.

Index

Constants

View Source
const (
	RequesterUser     = "user"
	RequesterAPIToken = "api_token"
)

Requester kinds, as recorded on the contact.erased Event.

Variables

View Source
var ErrInvalidEmail = errors.New("erasure: invalid email address")

ErrInvalidEmail means the address of a Privacy request is not a plain email address.

View Source
var ErrNotFound = errors.New("erasure: not found")

ErrNotFound means the identifier resolves to nothing in the Workspace.

Functions

func Hash

func Hash(value string) string

Hash is how Erasure names an address or a phone number to a consumer of the Erasures Export stream (ADR 0045): the lowercase hex SHA-256 of the value, and empty for an empty value. Erasure hashes before it publishes, so no address or phone reaches the outbox; a consumer hashes its own copy of a value the same way to find the rows to delete.

func NormalizeEmail

func NormalizeEmail(raw string) (string, error)

NormalizeEmail returns the canonical form of an address and rejects anything that is not a single plain address (no display name, no list). Callers hand the raw input to Lookup and ByEmail, which normalize themselves; a handler calls this only to answer 400 for an invalid address before it acts.

Types

type Holdings

type Holdings struct {
	ContactHeld     bool
	Events          int
	DeliveryRecords int
	Unsubscribes    int
	Suppressions    int
}

Holdings is what a Workspace holds for one email address, as counts (a Privacy request lookup). Nothing held is the zero value.

func Lookup

func Lookup(ctx context.Context, s *ent.Scoped, email string) (Holdings, error)

Lookup counts what the Workspace of s holds for the address: the Contact with it, that Contact's Events and the Events carrying it, the delivery records to it, and the Unsubscribes and Suppressions on it. It reads only through s, so another Workspace's rows never match, and changes nothing.

type Identifier

type Identifier struct {
	// contains filtered or unexported fields
}

Identifier names the data subject to erase. Build one with ByContactID, ByEmail or ByVisitorID.

func ByContactID

func ByContactID(id int64) Identifier

ByContactID identifies a Contact by its id.

func ByEmail

func ByEmail(email string) Identifier

ByEmail identifies a Contact by its email address; with no such Contact it identifies the address itself, so delivery records to it are anonymized.

func ByVisitorID

func ByVisitorID(visitorID string) Identifier

ByVisitorID identifies the Contact the visitor is bound to, or, for an anonymous Visitor with no Contact, the Visitor and its Events.

type Module

type Module struct {
	// contains filtered or unexported fields
}

Module erases data subjects.

func New

func New(bus *events.Bus) *Module

New builds the module over the bus whose transaction every erasure runs in.

func (*Module) Erase

func (m *Module) Erase(ctx context.Context, s *ent.Scoped, id Identifier, by Requester) error

Erase erases the identified data subject in one transaction: either all of it happens or none. ErrNotFound means the identifier resolves to nothing in the Workspace (including a row of another Workspace).

type Requester

type Requester struct {
	// Kind is RequesterUser or RequesterAPIToken.
	Kind string
	ID   int64
}

Requester is who asked for the Erasure: a User (Site) or an API token (External, MCP).

type Target

type Target struct {
	// ContactID is the Contact being erased; zero for an address or an anonymous
	// Visitor that has no Contact.
	ContactID int64
	// VisitorIDs are the anonymous devices bound to the Contact; their Events are
	// erased with it.
	VisitorIDs []string
	// Destinations are the Contact's addresses.
	Destinations []string
	// Phone is the Contact's phone number; empty when it has none or there is no Contact.
	Phone string
	// SubjectID is the customer's own id for the person (the Contact's subject_id, or
	// its sphericon id when it has none); only the contact.erased delivery carries it.
	// Empty for an address alone: the address is never echoed into the message.
	SubjectID string
	// IdentifierKind is what the Identifier was.
	IdentifierKind string
	// Requester is who asked.
	Requester Requester
}

Target is a resolved data subject: what every step erases. Resolving an Identifier into a Target is the only place the identifier kind matters.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL