effaced-supabase
effaced-supabase — first-party Supabase resolvers for effaced.
PostgrestColumn
Section titled “PostgrestColumn”class PostgrestColumn(BaseModel): category: PiiCategory name: str = Field(min_length=1)One PII-bearing column the PostgREST resolver exports and erases.
A column declares the name PostgREST exposes it under and the
PiiCategory the value holds. Declaration is explicit
and auditable on purpose: the resolver never discovers a table’s
schema, so a column not named here is neither exported nor counted in
the resolver’s covered surface.
Fields:
- category (
PiiCategory): ThePiiCategorythe column’s value is declared to hold; carried onto every exported record and into the resolver’s covered surface. - name (
str): The column name as PostgREST exposes it (aselectkey).
PostgrestTable
Section titled “PostgrestTable”class PostgrestTable(BaseModel): columns: tuple[PostgrestColumn, ...] = Field(min_length=1) name: str = Field(min_length=1) subject_column: str = Field(min_length=1)One PostgREST-exposed table holding a subject’s PII.
A table declares the column that carries the subject id and the
PII-bearing columns to export and erase. The resolver filters on
subject_column = <ref value> and never discovers the schema, so
the declaration is the auditable record of which rows and columns the
resolver reaches.
Fields:
- columns (
tuple[PostgrestColumn, ...]): The PII-bearing columns to export; at least one. Thesubject_columnneed not appear here unless it is itself PII to export. - name (
str): The table (or view) name PostgREST exposes at/rest/v1/{name}. - subject_column (
str): The column whose value equals the subject id — the resolver matchessubject_column=eq.<ref value>.
SupabaseAuthResolver
Section titled “SupabaseAuthResolver”class SupabaseAuthResolver: def __init__(base_url: str, service_role_key: str, *, transport: httpx.BaseTransport | None = None, timeout: float = _DEFAULT_TIMEOUT) -> NoneExports and erases a subject’s Supabase Auth (auth.users) record.
Expects refs of kind "supabase_auth" (refs are routed to the
resolver whose name equals their kind — ADR 0008) whose value is the
GoTrue user id. Talks to the Admin API (/auth/v1/admin), which
only accepts the service-role key — server-side use only.
Idempotency: a user GoTrue no longer knows yields
already_absent=True — success, never an error.
Error taxonomy (see effaced_supabase.errors): 4xx responses
other than 404 and 429 raise
ResolverError; rate limits, 5xx, and
connection faults propagate so the saga runner retries. A fresh
httpx client is built per call — nothing loop- or
connection-bound is cached on the instance (ADR 0006).
Fields:
- covered_surface (
CoveredSurface): The GoTrue PII this resolver claims to reach (AttestingResolver). Returns:AUTH_COVERED_SURFACE, built from the exporter’s field tuple so it cannot drift. - name (
str): Stable resolver name recorded in manifests and audits.
SupabaseAuthResolver.erase_subject
Section titled “SupabaseAuthResolver.erase_subject”async def erase_subject(ref: SubjectRef) -> ResolverErasureHard-delete the user in GoTrue (Art. 17).
Args:
- ref (
SubjectRef):kind="supabase_auth",value=<gotrue user id>.
Returns:
ResolverErasure— The outcome;already_absent=Trueif GoTrue already had noResolverErasure— such user.
Raises:
ResolverError— The key is invalid, lacks admin access, or the request was malformed — retrying cannot succeed.
SupabaseAuthResolver.export_subject
Section titled “SupabaseAuthResolver.export_subject”async def export_subject(ref: SubjectRef) -> ResolverExportCollect the user’s GoTrue-held contact fields (Art. 15).
Args:
- ref (
SubjectRef):kind="supabase_auth",value=<gotrue user id>.
Returns:
ResolverExport— The user’semailandphonewhen populated (the fieldResolverExport— set lives ineffaced_supabase.auth_export_records);ResolverExport— empty when GoTrue holds no such user.
Raises:
ResolverError— The key is invalid, lacks admin access, or the request was malformed — retrying cannot succeed.
SupabasePostgrestResolver
Section titled “SupabasePostgrestResolver”class SupabasePostgrestResolver: def __init__(base_url: str, service_role_key: str, tables: Sequence[PostgrestTable], *, transport: httpx.BaseTransport | None = None, timeout: float = _DEFAULT_TIMEOUT) -> NoneExports and erases a subject’s PII held in PostgREST-exposed tables.
Expects refs of kind "supabase_postgrest" (refs are routed to the
resolver whose name equals their kind — ADR 0008) whose value is the
subject id. The resolver is configured with an explicit list of
PostgrestTable declarations
— the tables and columns holding the subject’s PII and the column that
carries the subject id. It performs no schema discovery: a table
or column not declared is neither exported nor erased.
For each table, export issues
GET /rest/v1/{table}?{subject_column}=eq.{id}&select=... and emits
one record per populated declared column; erasure issues
DELETE /rest/v1/{table}?{subject_column}=eq.{id} with
Prefer: return=representation. PostgREST answers a no-match delete
with an empty representation (not a 404); a subject whose every
declared table deletes nothing yields already_absent=True —
success, never an error.
Talks to the data API with the service-role key, which bypasses
row-level security — server-side use only. A fresh httpx client is
built per call; nothing loop- or connection-bound is cached on the
instance (ADR 0006). Error taxonomy is shared with the other Supabase
resolvers (see effaced_supabase.errors): 4xx other than 429
raise ResolverError; rate limits, 5xx,
and connection faults propagate so the saga runner retries.
Fields:
- covered_surface (
CoveredSurface): The declared PII this resolver reaches (AttestingResolver). Returns: ACoveredSurfacewith one field per declared column, built from the same table list the exporter walks so the declaration and the export cannot drift. - name (
str): Stable resolver name recorded in manifests and audits.
SupabasePostgrestResolver.erase_subject
Section titled “SupabasePostgrestResolver.erase_subject”async def erase_subject(ref: SubjectRef) -> ResolverErasureDelete the subject’s rows from the declared tables (Art. 17).
Args:
- ref (
SubjectRef):kind="supabase_postgrest",value=<subject id>.
Returns:
ResolverErasure— The outcome;already_absent=Truewhen no declared tableResolverErasure— held a row for the subject.
Raises:
ResolverError— The key is invalid, lacks access, a declared table is missing, or the request was malformed — retrying cannot succeed.
SupabasePostgrestResolver.export_subject
Section titled “SupabasePostgrestResolver.export_subject”async def export_subject(ref: SubjectRef) -> ResolverExportCollect the subject’s PII across the declared tables (Art. 15).
Args:
- ref (
SubjectRef):kind="supabase_postgrest",value=<subject id>.
Returns:
ResolverExport— One record per populated declared column of every matchingResolverExport— row, sourced under each table’s name; empty when no declaredResolverExport— table holds the subject.
Raises:
ResolverError— The key is invalid, lacks access, a declared table is missing, or the request was malformed — retrying cannot succeed.