Kanun m.7 ve Kişisel Verilerin Silinmesi, Yok Edilmesi veya Anonim Hale Getirilmesi Hakkında Yönetmelik uyarınca hazırlanmıştır. 🔴 Bu politika CEO'nun iki isteğini uzlaştırır: "her şeyi loglayalım, cevapsız kalmayalım" ve "bizi hukuken koru". KVKK veri minimizasyonu ilkesi taşır — "her şeyi süresiz sakla" denetimde ceza sebebidir. Çözüm: katmanlı saklama.
1. Saklama süreleri
| Katman | İçerik | Süre | Dayanak |
|---|---|---|---|
| Mali | Tutar, KDV, servis bedeli, bahşiş, masa etiketi, tarih-saat, ödeme referansı, adisyon kalemi kopyası | 10 yıl | TTK m.82 / VUK m.253 saklama yükümlülüğü — KVKK'nın meşru istisnası |
| İşlemsel | Kim onayladı, hangi bildirim gitti, mutabakat izi, ikram/indirim/iade kaydı | 2 yıl | Uyuşmazlık/zamanaşımı süresi |
| İşlem güvenliği | Hız sınırı kayıtları — IP ham hâliyle saklanmaz, salt+SHA-256 özeti tutulur (§4) | 90 gün (üst sınır; işlevsel ömrü 15 dk) | Meşru menfaat (m.5/2-f) — güvenlik amacı sona erince silinir |
| Cihaz bildirim kimliği | Push token, platform | Kullanıcı bildirimleri kapatana / hesabı silene kadar | Sözleşmenin ifası |
| Kimlik bağı | E-posta, görünen ad | Hesap silinene kadar | Sözleşmenin ifası |
| Ödemedeki "kim ödedi" bağı | payments.user_id | Masa kapandıktan 3 gün sonra otomatik kopar — kullanıcı hiçbir şey yapmasa bile | Veri minimizasyonu (m.4/2-d) |
2. Hesap silindiğinde ne olur
🔴 Mali kayıt silinmez — kanunen silinemez. Silinen şey kimlik bağıdır:
`` ÖNCE: ödeme #123 → user_id: <kullanıcı> → e-posta: ali@ornek.com SONRA: ödeme #123 → user_id: (bağ koparıldı) → kimlik yok ``
Ödeme tutarı, tarihi ve restoranın mutabakat kaydı yerinde kalır; kullanıcı ile bağı kalmaz. Bu, hem KVKK'nın silme hakkını hem TTK/VUK saklama yükümlülüğünü aynı anda karşılar.
Tam döküm — ne siliniyor, ne kalıyor
| İşlem | Veri |
|---|---|
| DELETE | auth.users kaydı (e-posta, kimlik), session_participants, item_claims, user_push_tokens, receipt_visibility, session_tokens, payment_reminder_queue, staff_pins, restaurant_staff |
| Kimlik bağı NULL | payments.user_id, table_sessions.opened_by, audit_log.actor_user_id, shift_closures.closed_by |
| Dokunulmaz | Ödeme tutarı/tarihi/yöntemi, adisyon kalemleri, denetim defterindeki işlemin kendisi |
⚠️ Restoran sahibi hesabını doğrudan silemez. restaurants.owner_id yabancı anahtarı ON DELETE CASCADE'dir; sahip hesabını silmek restoranın tüm verisini (masalar, adisyonlar, personel, geçmiş ciro) birlikte götürürdü. Bu, sahibin kendi verisi değil başkalarının verisidir. Akış: önce devir ya da purge_restaurant (§2.1).
🔴 2026-08-10 — bu mekanizma canlıda ÖLÜ bulundu ve diriltildi
20260803190000_delete_account_preserve_payments fonksiyonu public şemasına yazılmıştı; istemci ise splittable şemasını kullanıyor ve taşımada yalnız o şema geldi. Sonuç: "Hesabı Sil" düğmesi canlıda çalışmıyordu — KVKK m.7 silme hakkı fiilen sağlanamıyordu (App Store Review Guideline 5.1.1(v) ihlali de aynı kapıya çıkıyordu).
✅ Düzeltme: 20260810110000_e11_hesap_silme_dirilt.sql — splittable.delete_my_account(), imha izi yazan ve sahip kapısı olan sürüm. Kanıt: scripts/guvenlik/e11_dogrula.py.
📌 Politikadan çıkan kural: bu belgede "mekanizma kurulu" yazan her satırın arkasında canlıdan alınmış bir kanıt olmalı. Migration dosyasının varlığı kanıt değildir.
2.1 Restoran silme talebi (veri sorumlusu restoran ise)
Bir restoran kendi verisinin silinmesini isterse purge_restaurant(restoran, gerekçe) çalıştırılır: restoran ve tüm bağlı kayıtları silinir, denetim defterindeki satırlar kimlikten arındırılıp purged=true işaretiyle anonim arşiv olarak kalır. Yalnız restoran sahibi çağırabilir ve gerekçe zorunludur.
3. Müşterinin "listemden kaldır" işlemi (silme değildir)
Uygulamadaki "Listemden Kaldır" seçeneği ödeme kaydını silmez, yalnız müşterinin kendi görünümünden gizler (receipt_visibility). Restoranın mutabakat raporunda kayıt durmaya devam eder ve olay defterine receipt_hidden_by_customer yazılır.
🟢 Bu davranış teknik olarak doğrulanıyor: scripts/guvenlik/d_dogrula.py T6 her koşumda "müşteri gizledi, restoranın raporunda duruyor" şartını ölçüyor. 🟢 Kullanıcıya da dürüstçe söyleniyor: "Ödeme kaydı silinmez; yalnız senin listenden kaldırılır. Restoranın muhasebe kayıtlarında durmaya devam eder."
4. ✅ TESPİT EDİLEN TEKNİK EKSİKLER — ÜÇÜ DE KAPATILDI (2026-08-08)
Bu politikanın tam uygulanmadığı üç nokta ölçülerek tespit edilmiş ve aynı gün kapatılmıştı. Migration: 20260808500000_s1_s2_s3_data_retention.sql · kanıt: scripts/guvenlik/s_dogrula.py → 10/10.
| # | Eksikti | Bugünkü hâli | Kanıt |
|---|---|---|---|
| S1 | ~~IP adresi MASKELENMEMİŞ saklanıyor (92.44.118.223)~~ | ✅ Anahtar artık salt + SHA-256 özeti (ipx_…), ham IP hiç yazılmıyor | s_dogrula T1–T4 |
| S2 | ~~IP kayıtları için otomatik silme YOK~~ | ✅ splittable-purge-join-attempts cron işi, günlük 04:45 UTC, 90 gün | s_dogrula T6–T7 |
| S3 | ~~data_retention_purge olayı hiç yazılmamış~~ | ✅ Hem IP temizliği hem ödeme anonimleştirmesi deftere yazıyor | s_dogrula T8–T9 |
🔴 S1'de neden maskeleme değil özet seçildi
Politikanın ilk hâli "maskelenmiş IP" diyordu (92.44.118.x). Uygulanmadı, çünkü attempt_key aynı zamanda kaba kuvvet hız sınırının anahtarıdır; /24'e indirmek aynı kovaya farklı kişileri düşürürdü:
- yanlış pozitif: aynı operatörün iki abonesi birbirini kilitler
- yanlış negatif: saldırgan komşu adreslerle 255 ücretsiz deneme kazanır
Salt + SHA-256 ikisini de çözer: aynı IP → aynı anahtar (hız sınırı çalışır), farklı IP → farklı anahtar (çakışma yok), anahtardan IP geri elde edilemez. 🟢 Hız sınırının gerçekten sağlam kaldığı gerçek REST turunda ölçülüyor (g1_g2_g3_dogrula.py T6 — 10. denemede kilit geldi).
⚠️ Dürüst sınır: salt veritabanında (splittable.security_salts, RLS açık + politika yok + GRANT yok) durur. Veritabanına tam erişen biri salt'ı da görür. Özet, tabloyu okuyanın IP öğrenmesini engeller — DB'yi ele geçirene karşı değil. KVKK açısından belirleyici olan birincisidir (veri minimizasyonu).
🟢 Zaten çalışıyordu: splittable-anonymize-payments cron işi (30 4 *) günlük anonimleştirme yapıyor — mali kayıtla kimlik bağını koparan mekanizmanın bir parçası. Artık bu koşum da restoran bazında imha izi yazıyor, böylece restoran kendi verisinin imhasını payment_events üzerinden görebiliyor.
5. İmha yöntemleri
| Veri tipi | Yöntem |
|---|---|
| Veritabanı kaydı (kimlik bağı) | Silme (DELETE) veya anonim hale getirme (bağ koparma) |
| Mali kayıt | 🔴 Silinmez — 10 yıl sonunda anonim hâlde kalır veya imha edilir |
Değiştirilemez defter (payment_events, audit_log) | Append-only; silinemez. Kimlik alanları anonimleştirilir |
| Yedekler | ~/SplitTable-Yedekler/ — rotasyon: son 14 yedek, eskiler otomatik düşer |
⚠️ Append-only defterlerin sınırı dürüstçe kayda geçiriliyor: payment_events ve audit_log tasarım gereği silinemez (mali denetim izinin değiştirilemez olması için). Bu tablolarda imha, kimlik alanlarının anonimleştirilmesi yoluyla yapılır.
📌 Ayrıca defterde, eski test sürümlerinden kalan ve append-only olduğu için silinemeyen teknik kayıtlar bulunmaktadır (5 b1_test + 36 yetim olay). Bunlar kişisel veri içermez; raporlarda ayıklanır.
6. Periyodik imha
Yönetmelik gereği periyodik imha 6 ayda birden fazla aralıklarla yapılamaz.
| Ne | Sıklık | Durum |
|---|---|---|
| Mali kayıt anonimleştirme | Günlük (30 4 *) | 🟢 kurulu |
| İşlem güvenliği (IP) temizliği | Günlük tarama, 90 gün eşiği (45 4 *) | 🟢 kuruldu (S2) |
| İşlemsel kayıt temizliği | 2 yıl | ⏳ süre henüz dolmadı |
| Politika gözden geçirme | Yılda 1 | ⏳ takvime bağlanacak |