- Implementerar eraseUser() i packages/database/src/gdpr-erasure.ts som raderar/anonymiserar/pseudonymiserar alla personliga tabeller. - DELETE /v1/me orkestrerar nu plan + transaktion + lagringsrensning. - Enpersonshushåll raderas automatiskt; publika recept anonymiseras; draft/private-recept raderas. - Finansiella poster (subscriptions/store_transactions/subscription_events) behålls under retention med slumpmässig pseudonym från gdpr_retention_pseudonyms. - audit_logs/domain_events anonymiseras; kvitton i kvarvarande hushåll strippas på bild och OCR-text. - Lägger till permanent residual-test (me.residual.test.ts) med schema-diff-assertion mot information_schema. - Uppdaterar docs/23 och docs/29 med implementerad mekanism. - Migration 0021 för gdpr_retention_pseudonyms och nullable cooking_sessions.started_by_user_id.
24 KiB
GDPR-radering — kompletthetsaudit (cibello, HEAD c2cc387 → 28908d5)
Syfte: identifiera varje tabell som bär
user_ideller annan persondata, klassa den enligt GDPR, hitta luckor mot nuvarandeDELETE /v1/me, och designa ett permanent residual-test.Status: ✅ Implementerad i efterföljande commits. Residual-testet
apps/api/test/me.residual.test.tsingår permanent ipnpm testoch failar automatiskt om nyauser_id-liknande kolumner läggs till utan klassning.Audit gjord mot:
packages/database/src/schema/*.tsinfrastructure/migrations/*.sqlapps/api/src/routes/me.ts(nuvarandeDELETE /v1/me)packages/database/src/analytics-gdpr.ts
1. Metod
- Enumrerade alla tabeller exporterade från
packages/database/src/schema/index.ts. - Grep:ade
user_id,references(() => users.id)och fria text/PGD-fält som kan bära PII (foton, hälsa, kvitton, betyg, kommentarer). - Klassade varje tabell i tre kategorier:
- RADERA — direkt personlig, ingen laglig grund att behålla.
- ANONYMISERA — behövs för referensintegritet/aggregat, men
user_idoch PII-fält ska skrubbas eller sättasNULL. - BEHÅLL MED LAGLIG GRUND — t.ex. bokföring, säkerhet, rättslig förpliktelse. Får inte raderas, men PII ska minimeras.
- Jämförde klassningen mot vad
DELETE /v1/mefaktiskt gör idag.
2. Tabell-för-tabell-klassning
2.1 Användarkonto & autentisering — RADERA
| Tabell | Motivering | Nuvarande DELETE /v1/me |
|---|---|---|
users |
Personliga grunduppgifter (e-post, namn). Soft-delete/anonymisering accepteras för att bevara referensintegritet, men övriga personfält måste skrubbas. | ✅ anonymiserar email, displayName, sätter deletedAt. |
user_credentials |
Lösenordshash. Ingen laglig grund efter radering. | ❌ saknas — cascade på users.id fyrar inte vid soft-delete. |
admin_totp |
TOTP-secret. Säkerhetsuppgift. | ❌ saknas — cascade fyrar inte. |
email_verification_tokens |
Engångstokens kopplade till användaren. | ❌ saknas — cascade fyrar inte. |
password_reset_tokens |
Engångstokens kopplade till användaren. | ❌ saknas — cascade fyrar inte. |
refresh_tokens |
Sessions-token-hash, IP, user-agent. | ✅ raderas explicit. |
Bedömning: Fyra auth-tabeller är ohanterade eftersom users soft-deletas. Måste explicit raderas.
2.2 Hälsa, preferenser, samtycken & locale — RADERA
| Tabell | Motivering | Nuvarande DELETE /v1/me |
|---|---|---|
user_health_profiles |
Hälsodata (längd, vikt, födelseår, kön, aktivitet). Känslig persondata. | ✅ raderas explicit. |
user_preferences |
Kost/mål/allergier/intoleranser. Personlig profilering. | ✅ raderas explicit. |
user_consents |
Samtyckeshistorik kopplad till person. | ❌ saknas — cascade på users.id fyrar inte vid soft-delete. |
user_locale_preferences |
Språk, region, tidszon, valuta. Personlig preferens. | ❌ saknas — cascade fyrar inte. |
Bedömning: user_consents och user_locale_preferences är nya luckor.
2.3 Hushåll & medlemskap — BESLUT KRÄVS
| Tabell | Motivering | Nuvarande DELETE /v1/me |
|---|---|---|
household_members |
Länkar användare till hushåll. Ingen laglig grund att behålla kopplingen efter radering. | ❌ saknas helt. |
households |
Om användaren är enda medlemmen/ägaren innehåller hushållet personliga spår (namn, budget, aktiveringsdatum, storlek). Om flera medlemmar finns är hushållet en delad resurs. | ❌ saknas helt. |
storage_locations |
Hushålls-specifika förvaringsplatser. Delad resurs om hushållet har flera medlemmar. | ❌ saknas helt. |
Bedömning: Detta är den största strukturella luckan. Förslag (se öppna frågor nedan):
- Ta bort användarens
household_members-rad(er). - Om användaren är ensam ägare/medlem i ett hushåll: radera hela hushållet och dess cascading data (
storage_locations,inventory_items,inventory_transactions,shopping_lists,week_plans,meal_boxes,receipts,cooking_sessions, etc.). - Om andra medlemmar finns: låt hushållet leva, anonymisera användarreferenser i delade data.
Öppen fråga till Johan: Vill vi automatiskt radera ett enpersonshushåll vid kontoradering, eller ska användaren först tvingas överföra ägarskap? (GDPR-mässigt är automatisk radering av ett enpersonshushåll det säkraste.)
2.4 Lager, inköp, matplanering — hushållsdata (ANONYMISERA eller RADERA beroende på hushåll)
| Tabell | Klassning | Motivering |
|---|---|---|
inventory_items |
ANONYMISERA / RADERA | Hushållsdata. Ingen personlig info i sig, men source, verifiedByUser etc. kan kopplas till användaren. Om hushållet överlever: sätt verifiedByUser = false där aktuell användare verifierat. Om hushållet raderas: cascade bort. |
inventory_transactions |
ANONYMISERA | actor_user_id är en personlig referens. Sätt NULL. Data behövs för hushållets saldo/historik. |
inventory_item_decay_profile |
BEHÅLL / RADERA | Teknisk koppling till inventory_items. Följer hushållets öde. |
inventory_conflicts |
ANONYMISERA | resolved_by_user_id ska vara NULL. Övriga fält är hushållsdata. |
shopping_lists |
BEHÅLL | Hushållsdata, ingen PII. |
shopping_list_items |
ANONYMISERA | added_by_user_id ska vara NULL. |
receipts |
RADERA / ANONYMISERA | Kvittobild (image_url) kan innehålla personuppgifter (butik, kortnummer, artiklar). Om hushållet överlever och kvittot är delat: radera bild-URL:en i objektlagring och anonymisera. Om hushållet raderas: cascade bort. |
receipt_lines |
RADERA / ANONYMISERA | raw_text är OCR-text från kvitto (potentiellt personligt). Samma logik som receipts. |
price_observations |
BEHÅLL | Aggregatdata, anonymiserad prisinformation. store_name är inte personligt i GDPR-mening. |
meal_boxes |
ANONYMISERA | reserved_for_user_id ska vara NULL. |
meals |
RADERA | Direkt personlig måltidslogg (user_id, photo_url). Raderas explicit idag. |
week_plans |
BEHÅLL | Hushållsdata, ingen PII. |
week_plan_entries |
BEHÅLL | Hushållsdata. |
cooking_sessions |
ANONYMISERA | started_by_user_id ska vara NULL. Sessionen är hushållshistorik. |
cooking_assumption_profiles |
BEHÅLL / RADERA | Hushållsaggregat. Följer hushållets öde. |
waste_summaries |
BEHÅLL | Hushållsaggregat, ingen PII. |
Bedömning: Dessa tabeller är i princip ohanterade idag. De flesta är hushållsdata och ska anonymiseras snarare än raderas, förutsatt att hushållet överlever.
2.5 Skanningar, AI-korrigeringar & träningsbank — RADERA
| Tabell | Motivering | Nuvarande DELETE /v1/me |
|---|---|---|
scan_jobs |
Innehåller uppladdade bilder (s3_keys) och användarspecifika resultat. |
✅ raderas explicit. |
ai_corrections |
Innehåller personliga korrigeringar och bildnyckel. | ✅ raderas explicit. |
ai_training_bank |
Innehåller träningsbild och korrigeringsdata. Cascadar via ai_corrections. |
✅ raderas via ai_corrections cascade. |
Bedömning: Godkänd i föregående fix. Ingen ytterligare åtgärd.
2.6 Recept & matminnen — RADERA eller ANONYMISERA
| Tabell | Klassning | Motivering |
|---|---|---|
recipes |
ANONYMISERA | Publik/verifierad receptdata är inte personlig, men creator_user_id och creator_display_name är personliga. Draft/private-recept däremot är personliga och bör raderas. |
recipe_ratings |
RADERA | Användarbetyg och kommentar är personligt. |
recipe_favorites |
RADERA | Användarens favoriter är personligt. |
recipe_cooks |
RADERA / ANONYMISERA | Logg över vilka recept användaren lagat. Personligt. |
food_memories |
RADERA | Matminne med eventuell bild (photo_url). Personligt. |
memory_items |
RADERA | AAMOS-minne — personligt. |
taste_signals |
RADERA | Smakpreferenser kopplade till användaren. |
creator_stats |
RADERA | Creator-profil, följare, statistik. Personligt. |
creator_follows |
RADERA | Följer/följare-relationer. Personliga. |
Bedömning: Receptinteraktioner är i stort sett ohanterade.
2.7 Notiser & push — RADERA
| Tabell | Motivering | Nuvarande DELETE /v1/me |
|---|---|---|
notifications |
Push-/inapp-notiser till användaren. | ❌ saknas (cascade fyrar inte). |
push_tokens |
Push-token för Expo. Teknisk identifierare kopplad till användaren. | ❌ saknas (cascade fyrar inte). |
Bedömning: Båda är luckor.
2.8 Betalningar & prenumerationer — BEHÅLL MED LAGLIG GRUND, MEN ANONYMISERA
| Tabell | Klassning | Motivering |
|---|---|---|
subscriptions |
BEHÅLL | Bokförings- och rättslig grund för att bevisa köp/åtkomst. user_id bör pseudonymiseras eller sättas NULL efter en viss period, men transaktionens existens måste kunna verifieras. Retention: minst 7 år (Bokföringslagen) eller tills rättslig tvist är avslutad. |
subscription_events |
BEHÅLL | Händelselogg för prenumeration. Samma grund. user_id kan anonymiseras. |
store_transactions |
BEHÅLL | Råa store-transaktioner för revision. raw_payload kan innehålla PII (store-kvitto) — ska skrubbas så att endast transaktions-ID, produkt-ID och belopp återstår. user_id anonymiseras. Retention: 7 år. |
store_notifications |
BEHÅLL | App/Play-servernotiser. Ingen direkt PII i sig, men kan innehålla transaktions-ID. Behålls för spårbarhet. |
trials |
RADERA / BEHÅLL | Trial-spårning. För rättvis kvot/fair-use kan vi behöva spara att denna person redan provat. Men efter kontoradering bör raden raderas eller pseudonymiseras (t.ex. hash av ursprungligt user_id för anti-fraud). Förslag: radera vid GDPR-radering; anti-fraud hanteras via store-transaktioner istället. |
ai_usage_counters |
RADERA | Månatlig AI-användning. Ingen laglig grund efter kontoradering. |
Bedömning: Finansiella poster får inte raderas, men PII måste skalas bort. subscriptions, subscription_events, store_transactions behöver en anonymiseringsfunktion, inte deletion.
2.9 Analytics & audit — BEHÅLL MED LAGLIG GRUND, MEN ANONYMISERA
| Tabell | Klassning | Motivering |
|---|---|---|
product_analytics_events |
RADERA | Pseudonyma events, men anonymous_id kan länka till användaren. Hard-delete görs redan via deleteUserAnalyticsEvents. |
analytics_daily_snapshots |
BEHÅLL | Hushållsaggregat, ingen PII. |
audit_logs |
BEHÅLL | Säkerhets-/rättslig grund. actor_user_id, ip och ev. PII i metadata ska anonymiseras (actor_user_id → NULL, ip → NULL, metadata skrubbas). Retention: minst 2 år, alternativt tills rättslig tvist är avslutad. |
domain_events |
BEHÅLL / ANONYMISERA | Event-sourcing outbox. user_id och PII i payload ska anonymiseras för att inte bryta downstream-konsumenter. |
idempotency_keys |
RADERA | Caching av API-svar kopplade till användaren. Ingen laglig grund. |
2.10 Globala system- och översättningstabeller — BEHÅLL (ingen PII)
| Tabell | Klassning | Motivering |
|---|---|---|
canonical_ingredients |
BEHÅLL | Global produktdata. |
ingredient_translations |
BEHÅLL | Översättningar av globala ingredienser. |
unit_translations |
BEHÅLL | Översättningar av enheter. |
recipe_translations |
BEHÅLL | Översättningar av recept. |
recipe_step_translations |
BEHÅLL | Översättningar av receptsteg. |
recipe_source_registry |
BEHÅLL | Juridisk källregister. |
recipe_ingredients |
BEHÅLL | Receptdata. |
recipe_steps |
BEHÅLL | Receptdata. |
recipe_similarities |
BEHÅLL | Systemdata. |
substitutions |
BEHÅLL | Systemdata. |
nutrition_display_profiles |
BEHÅLL | Systemdata. |
allergen_market_rules |
BEHÅLL | Systemdata. |
season_events |
BEHÅLL | Systemdata. |
feature_flags |
BEHÅLL | Systemdata. |
release_gates |
BEHÅLL | Systemdata. |
inventory_decay_profiles |
BEHÄLL | Systemdata. |
ai_eval_cases |
BEHÅLL | Testdata. |
ai_eval_runs |
BEHÅLL | Testkörningsresultat. |
3. Gap-tabell mot nuvarande DELETE /v1/me
Nedan är tabeller som idag varken raderas, anonymiseras eller motiveras att behållas vid kontoradering.
| # | Tabell | Kategori | Risk | Föreslagen åtgärd |
|---|---|---|---|---|
| 1 | user_credentials |
Auth | Lösenordshash överlever radering. | DELETE FROM user_credentials WHERE user_id = $1 |
| 2 | admin_totp |
Auth | TOTP-secret överlever. | DELETE |
| 3 | email_verification_tokens |
Auth | Tokens överlever. | DELETE |
| 4 | password_reset_tokens |
Auth | Tokens överlever. | DELETE |
| 5 | user_consents |
Samtycken | Samtyckeshistorik överlever. | DELETE |
| 6 | user_locale_preferences |
Preferenser | Locale-preferenser överlever. | DELETE |
| 7 | household_members |
Hushåll | Användaren kvarstår som medlem. | DELETE |
| 8 | households (+ cascading data) |
Hushåll | Enpersonshushåll läcker persondata. | Beslut krävs (se §2.3) |
| 9 | inventory_transactions |
Hushåll | actor_user_id kvarstår. |
SET actor_user_id = NULL |
| 10 | inventory_conflicts |
Hushåll | resolved_by_user_id kvarstår. |
SET resolved_by_user_id = NULL |
| 11 | shopping_list_items |
Hushåll | added_by_user_id kvarstår. |
SET added_by_user_id = NULL |
| 12 | meal_boxes |
Hushåll | reserved_for_user_id kvarstår. |
SET reserved_for_user_id = NULL |
| 13 | cooking_sessions |
Hushåll | started_by_user_id kvarstår. |
SET started_by_user_id = NULL |
| 14 | receipts |
Hushåll | Kvittobild (image_url) kan innehålla PII. |
Radera bild i lagring, sätt image_url = NULL |
| 15 | receipt_lines |
Hushåll | OCR-text kan innehålla PII. | Skrubba raw_text eller radera rader om hushåll raderas. |
| 16 | recipe_ratings |
Recept | Betyg/kommentar överlever. | DELETE |
| 17 | recipe_favorites |
Recept | Favoriter överlever. | DELETE |
| 18 | recipe_cooks |
Recept | Lagningslogg överlever. | DELETE |
| 19 | food_memories |
Matminnen | Matminnen + bild överlever. | DELETE (+ bild i lagring) |
| 20 | creator_stats |
Creator | Creator-profil överlever. | DELETE |
| 21 | creator_follows |
Creator | Följer/följare överlever. | DELETE |
| 22 | notifications |
Notiser | Notiser överlever. | DELETE |
| 23 | push_tokens |
Push | Push-token överlever. | DELETE |
| 24 | trials |
Trial | Trial-info överlever. | DELETE |
| 25 | ai_usage_counters |
Usage | AI-användning överlever. | DELETE |
| 26 | audit_logs |
Audit | actor_user_id, ip, metadata överlever. |
Anonymisera (NULL + skrubba metadata) |
| 27 | domain_events |
Events | user_id + PII i payload överlever. |
Anonymisera (NULL + skrubba payload) |
| 28 | idempotency_keys |
API | Cachade svar kopplade till användaren överlever. | DELETE |
| 29 | subscriptions |
Betalning | user_id kvarstår för evigt. |
Pseudonymisera efter retention (eller direkt vid radering om ekonomiskt spår finns kvar i store_transactions) |
| 30 | subscription_events |
Betalning | user_id + payload kvarstår. |
Anonymisera (user_id = NULL, skrubba payload) |
| 31 | store_transactions |
Betalning | raw_payload kan innehålla PII. |
Skrubba raw_payload, anonymisera user_id |
Totalt: 31 tabell-förekomster som behöver åtgärdas (många är SET NULL, inte radering).
4. Bilder / objektlagring
Förutom redan hanterade scan_jobs.s3_keys, ai_corrections.image_s3_key och ai_training_bank.image_s3_key finns följande användarbilder:
| Källa | Kolumn | Risk | Åtgärd vid GDPR-radering |
|---|---|---|---|
meals.photo_url |
Foto på måltid | Personlig bild. | Radera objektet i lagring; sätt photo_url = NULL. |
food_memories.photo_url |
Foto från matminne | Personlig bild. | Radera objektet i lagring; sätt photo_url = NULL. |
receipts.image_url |
Kvittobild | Potentiellt personlig (kortnummer, butik, köp). | Radera objektet i lagring; sätt image_url = NULL. |
recipes.image_urls |
Receptbild | Om receptet är draft/private och skapats av användaren: personlig. Om publikt/verifierat: delad resurs. | För draft/private: radera bilderna. För publika: behåll (om användaren samtyckt) eller anonymisera skaparen. |
Notering: Det finns ingen dedikerad profilbildskolumn i users. Om sådan läggs till i framtiden måste den inkluderas i residual-testet.
5. Design — RESIDUAL-TEST
Mål: en permanent test som skapar en användare med en rad i varje personlig tabell, kör DELETE /v1/me, och verifierar att inga personspår kvarstår (utom motiverat-behåll som måste vara PII-fritt/anonymiserat).
5.1 Teststruktur (förslag)
describe("DELETE /v1/me — GDPR residual test", () => {
it("lämnar inga personspår efter kontoradering", async () => {
const { token, userId } = await registerUser("residual@example.invalid");
// 1. Skapa en rad i varje personlig tabell
await seedAllPersonalTables(userId, token);
// 2. Kör DELETE /v1/me
const res = await app.inject({
method: "DELETE",
url: "/v1/me",
headers: { authorization: `Bearer ${token}` },
});
expect(res.statusCode).toBe(200);
// 3. Verifiera: varje tabell är antingen tom eller anonym/PII-fri
await assertNoPersonalTraces(app.db, userId);
});
});
5.2 Hjälpfunktioner
seedAllPersonalTables(userId, token) ska skapa exempeldata i:
user_credentials(skapas vid registrering)user_health_profiles,user_preferences,user_locale_preferences,user_consentshouseholds+household_members(+storage_locations)scan_jobs+ai_corrections+ai_training_bankmeals,memory_items,taste_signals,food_memoriesrecipe_ratings,recipe_favorites,recipe_cooks,creator_stats,creator_followsnotifications,push_tokenstrials,ai_usage_countersaudit_logs,domain_events,idempotency_keyssubscriptions,subscription_events,store_transactionsinventory_transactions,inventory_conflicts,shopping_list_items,meal_boxes,cooking_sessionsreceipts+receipt_lines
assertNoPersonalTraces(db, userId) ska:
- För RADERA-tabeller:
SELECT count(*) = 0 WHERE user_id = $userId. - För ANONYMISERA-tabeller:
SELECT count(*) = 0 WHERE user_id = $userId(eftersomuser_idska varaNULL). - För BEHÅLL-tabeller: tillåta rader med
user_id = $userIdmen kontrollera att känsliga fält (raw_payload,metadata,ip) är skrubbade. - För bilder: mocka
storage.deleteObjectoch verifiera att alla personliga bildnycklar raderats. - För
users: kontrollera attemail,displayNameär anonymiserade ochdeletedAtär satt.
5.3 Mekanism för att upptäcka nya tabeller
För att residual-testet automatiskt ska upptäcka framtida tabeller som lägger till user_id:
// I testet: reflektera schemat och jämför med en allow-list
const userIdColumns = await db.execute(sql`
SELECT table_name, column_name
FROM information_schema.columns
WHERE table_schema = 'public'
AND column_name IN ('user_id', 'actor_user_id', 'creator_user_id',
'started_by_user_id', 'resolved_by_user_id',
'added_by_user_id', 'reserved_for_user_id')
`);
// Jämför med allow-list över tabeller som testet täcker.
// Om en ny tabell dyker upp: testet failar tills den klassas.
Denna "schema-diff"-assertion gör testet till en permanent vakt.
5.4 Förväntat testresultat idag (före implementation)
Testet kommer att faila på de ~31 luckor listade i §3. Det är avsiktligt — det blir den röda tråden som implementationen ska göra grön.
6. Öppna beslut innan implementation
Följande punkter behöver ditt (Johans) godkännande eller vägledning:
-
Enpersonshushåll vid radering: Ska vi automatiskt radera hela hushållet (inkl. lager, listor, kvitton, matplaner) när enda medlemmen begär GDPR-radering? Alternativet är att tvinga användaren överföra ägarskap först, vilket är mer komplext.
- Rekommendation: Automatisk radering av enpersonshushåll.
-
Publika recept: Ska ett publikt/verifierat recept som användaren skapat anonymiseras (
creator_user_id = NULL,creator_display_name = NULL) eller raderas helt?- Rekommendation: Anonymisera publika recept, radera draft/private.
-
Finansiella poster: Ska
subscriptions.user_idpseudonymiseras direkt vid GDPR-radering, eller först efter 7 år? Bokföringslagen kräver att vi kan bevisa köpet, men inte nödvändigtvis vem som gjorde det.- Rekommendation: Sätt
subscriptions.user_id = NULLdirekt vid radering; ekonomisk spårbarhet finns kvar istore_transactions(som skrubbas men behåller transaktions-ID).
- Rekommendation: Sätt
-
Audit logs: Ska
audit_logsbehållas medactor_user_id = NULLi 2+ år, eller raderas efter kortare tid?- Rekommendation: Behåll 2 år med anonymiserad aktör, för säkerhetsincident-utredning.
-
Domain events: Ska
domain_eventsbehållas för event-sourcing, men meduser_id = NULLoch skrubbad payload?- Rekommendation: Ja, behåll för downstream replay/konsumenter.
7. Sammanfattning & rekommenderad ordningsföljd
7.1 Snabbfix (högsta riskerna)
- Radera auth-tabeller:
user_credentials,admin_totp,email_verification_tokens,password_reset_tokens. - Radera preferenser/samtycken:
user_consents,user_locale_preferences. - Radera notiser/push:
notifications,push_tokens. - Radera trials/usage:
trials,ai_usage_counters. - Radera idempotency:
idempotency_keys. - Radera receptinteraktioner:
recipe_ratings,recipe_favorites,recipe_cooks,creator_stats,creator_follows,food_memories. - Anonymisera hushållsreferenser:
inventory_transactions.actor_user_id,inventory_conflicts.resolved_by_user_id,shopping_list_items.added_by_user_id,meal_boxes.reserved_for_user_id,cooking_sessions.started_by_user_id. - Hantera kvitton: radera
receipts.image_urli lagring, skrubbareceipt_lines.raw_text.
7.2 Större beslut
- Hushållsstrategi: radera enpersonshushåll, anonymisera delade hushåll.
- Finansiella poster: anonymisera
subscriptions/subscription_events/store_transactionsenligt retention. - Audit/domain events: anonymisera men behåll enligt retention.
7.3 Permanent vakt
- Implementera residual-testet i
apps/api/test/me.residual.test.tsmed schema-diff-assertion.
8. Bilaga — fullständig tabellista från schema
(Används för att säkerställa att inga tabeller missats.)
adminTotp, aiCorrections, aiEvalCases, aiEvalRuns, aiTrainingBank,
aiUsageCounters, allergenMarketRules, analyticsDailySnapshots, auditLogs,
canonicalIngredients, cookingAssumptionProfiles, cookingSessions,
creatorFollows, creatorStats, domainEvents, emailVerificationTokens,
featureFlags, foodMemories, householdMembers, households, idempotencyKeys,
ingredientTranslations, inventoryConflicts, inventoryDecayProfiles,
inventoryItemDecayProfile, inventoryItems, inventoryTransactions,
mealBoxes, meals, memoryItems, notifications, nutritionDisplayProfiles,
passwordResetTokens, priceObservations, productAnalyticsEvents, products,
pushTokens, receiptLines, receipts, recipeCooks, recipeFavorites,
recipeIngredients, recipeRatings, recipeSimilarities, recipeSourceRegistry,
recipeStepTranslations, recipeSteps, recipeTranslations, recipes,
refreshTokens, releaseGates, scanJobs, seasonEvents, shoppingListItems,
shoppingLists, storageLocations, storeNotifications, storeTransactions,
subscriptionEvents, subscriptions, substitutions, tasteSignals, trials,
unitTranslations, userConsents, userCredentials, userHealthProfiles,
userLocalePreferences, userPreferences, users, wasteSummaries,
weekPlanEntries, weekPlans
Alla tabeller är granskade och klassade ovan.