Spaces:
Sleeping
Production Launch Checklist — arac-hasar-v2
Hedef stack: Vercel (web) + Render (backend, free tier) + Supabase (Postgres free tier) + Cloudflare R2 (storage)
Bu doküman ne için var? Production'a basmadan ÖNCE tek tek tikleyeceğin son güvenlik ve operasyon listesi. Her madde için:
- P0 = Deploy'u BLOKLAR (yapılmadıysa canlıya çıkma)
- P1 = İlk hafta içinde bitir (canlı ama yüksek risk)
- P2 = İlk ay içinde bitir (iyileştirme)
Sorumlu rolleri: dev = sen / geliştirici · devops = altyapı / hosting · legal = KVKK / sözleşme
Tahmini süre = senin için gerçekçi efor (saat).
TOP 10 MUST-DO (deploy öncesi, P0)
Bu 10 madde TAMAMLANMADAN canlıya çıkma. Hepsi docs/PROD_LAUNCH_CHECKLIST.md'nin ilgili bölümünde detaylandırılmıştır.
| # | Madde | Süre |
|---|---|---|
| 1 | ROBOFLOW_API_KEY revoke + yenisini üret (chat geçmişine sızdı) |
15dk |
| 2 | JWT_SECRET_KEY 48-byte random üret, sadece Render dashboard'da sakla |
10dk |
| 3 | Cloudflare R2 bucket-scoped token (account-level token KULLANMA) | 30dk |
| 4 | Supabase DATABASE_URL pgbouncer pooler portu :6543 (direct :5432 DEĞİL) |
15dk |
| 5 | ENVIRONMENT=production set → in-memory fallback HARD FAIL test et |
1s |
| 6 | CORS_ORIGINS sadece prod Vercel domain'i (wildcard veya * YOK) |
10dk |
| 7 | Next.js 15.1.3 → ^15.5.16 upgrade (CVE-2025-29927 middleware bypass) |
1s |
| 8 | WebSocket /api/v1/inspect/{id}/stream auth ekle (query param ?token=... minimum) |
2s |
| 9 | .env* dosyaları repo'da DEĞİL → git log --all -p -- .env ile doğrula |
30dk |
| 10 | UptimeRobot ping kur (Render free 15dk sleep'i engelle, /health endpoint) |
20dk |
Toplam: ~7 saat çekirdek iş + test/validation süresi.
1. Secrets Management (KRİTİK)
Tüm secret'lar YALNIZCA hosting platform'un secret manager'ında (Render Environment, Vercel Environment Variables) saklanır.
.envdosyası repo'ya GİRMEZ. Local dev için.env.local(gitignored) kullan.
1.1 — Sızdırılmış secret rotation
- P0 | dev | 15dk —
ROBOFLOW_API_KEYRoboflow konsolundan revoke + yeni key üret. Önceki chat history'de plain text görüldü. - P0 | dev | 20dk — Tüm
.env*,docker-compose*.yml,services/backend/*.pyiçinde geçen değer ne isegit log -S "<value>" --allile sızıntı yok mu doğrula. Sızdıysagit filter-repoveya repo reset. - P0 | dev | 30dk —
git log --all -p -- "**/.env*"ile herhangi bir.envcommit'lenmiş mi kontrol et. Commit'lendiyse → BFG Repo Cleaner veya filter-repo + force push + GitHub destek bilet (cache invalidation). - P1 | dev | 20dk — Tüm secret'ları doğal cycle (90 gün) için takvime ekle (Sentry DSN, R2, Supabase service role).
1.2 — JWT secret
- P0 | devops | 10dk —
openssl rand -base64 48ile yeniJWT_SECRET_KEYüret. - P0 | devops | 5dk — Render dashboard →
hasarui-apiservice → Environment →JWT_SECRET_KEYekle (Renderrender.yaml'da zatengenerateValue: trueile otomatik üretiyor; manuel override yapacaksan worker da aynı değere bağlı olduğundan emin ol, render.yaml'da workerfromServiceile referans veriyor — bu OK). - P0 | dev | 15dk —
services/backend/config.py:139-155_jwt_secret_hard_failvalidator çalışıyor mu test et:ENVIRONMENT=production JWT_SECRET_KEY=short python -c "import config"→ exception fırlatmalı. - P1 | devops | 10dk — 1Password / Vault / Bitwarden gibi bir password manager'a kopya al (recovery için, ASLA Slack/email'de paylaşma).
1.3 — Database credentials (Supabase)
- P0 | devops | 15dk — Supabase Project Settings → Database → Connection Pooling (PgBouncer) sekmesinden URL al. Port
:6543, modeTransaction. Direct:5432URL KULLANMA (free tier 60 connection limit, web+worker hızla doldurur). - P0 | devops | 10dk — Render
DATABASE_URLenv'ini Supabase pooler URL ile manuel override et (render.yaml'dafromDatabaseRender-managed DB için, Supabase için manuel set). - P0 | devops | 5dk — Supabase
DATABASE_URL'in sonuna?sslmode=requireekle. SSL olmadan bağlantı reddedilsin. - P1 | devops | 10dk — Supabase Database password'ü ayda 1 rotate planı (calendar reminder).
- P2 | dev | 30dk —
database_url_async(asyncpg) için ayrıca pooler URL test et — asyncpg + pgbouncer transaction mode'da prepared statement sorunu var,?prepared_statements=falseveyastatement_cache_size=0gerekebilir.
1.4 — Object storage (Cloudflare R2)
- P0 | devops | 30dk — Cloudflare R2 dashboard → Manage R2 API Tokens → Object Read & Write scope sadece
inspectionsbucket için. Account-level token OLUŞTURMA. - P0 | devops | 10dk — Token oluştururken TTL set et (1 yıl). Süresiz token oluşturma.
- P0 | devops | 10dk — Render env:
S3_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com,S3_REGION=auto,S3_ACCESS_KEY,S3_SECRET_KEY,S3_BUCKET=inspections. - P0 | devops | 15dk —
S3_PUBLIC_ENDPOINTya R2 public bucket URL (custom domain) ya da presigned URL strategy. Bucket public yapacaksan sadecepreviews/veoverlays/prefix'i için custom CORS rule. - P1 | devops | 20dk — R2 bucket → Settings → CORS Policy:
AllowedOrigins: ["https://<vercel-domain>"],AllowedMethods: ["GET","HEAD"]. Wildcard yok. - P1 | devops | 10dk — R2 → Object Lifecycle rule:
tmp/prefix 7 gün sonra expire (worker geçici dosyalar). - P2 | devops | 30dk — R2 token rotation runbook (90 gün).
1.5 — Admin bootstrap
- P0 | dev | 15dk —
ADMIN_PASSWORDiçinopenssl rand -base64 24üret. Sadece Render env'de + password manager'da sakla. - P0 | dev | 10dk —
ADMIN_EMAILset et (örn.admin@<domain>). İlk login sonrası şifreyi web UI'dan değiştir. - P0 | dev | 5dk —
.env.exampledosyasındaADMIN_PASSWORD=placeholder'ın boş olduğunu doğrula (örnek değer KOYMA, kullanıcı yanlışlıkla aynısını kullanır). - P1 | dev | 30dk — İlk admin login + şifre değiştirme akışı test edildi.
1.6 — Repo hijyen
- P0 | dev | 20dk —
.gitignoreiçinde:.env,.env.*,!.env.example,*.pt(model weights ayrı bucket'tan fetch),login.json(repo kökünde duruyor — bu ne? incele!). - P0 | dev | 10dk —
login.jsondosyası repo kökünde (lsçıktısında görüldü). İçinde credentials var mı? Varsa rotate + sil + .gitignore'a ekle. - P1 | dev | 30dk — Pre-commit hook:
detect-secretsveyagitleakskur. CI'da da çalışsın. - P2 | dev | 1s — GitHub repo Settings → Secret scanning + Push protection aktif (public repo için ücretsiz).
2. Backend Hardening (FastAPI / Render)
2.1 — Production mode hard-fail
- P0 | dev | 1s —
services/backend/main.py:150-163_db_available()in-memory fallback: production'da DB connect fail →sys.exit(1). Şu anlogger.warningile geçiyor, sessizce yanlış data döner. - P0 | dev | 30dk — Production'da
init_db()çağrısı fail olursa app boot fail. Şu anki davranışıservices/backend/main.py:169test et. - P1 | dev | 1s —
pipeline.pymodel load fail → production'da hard fail. Dev'de fake/dummy pipeline'a fallback OK ama prod'da değil.
2.2 — CORS
- P0 | devops | 10dk — Render env
CORS_ORIGINS=https://<your-vercel-domain>.vercel.app,https://<custom-domain>. Wildcard /*/ boş YOK. - P0 | dev | 15dk —
services/backend/config.py:71-77cors_origin_regexproduction'da daraltıldı mı doğrula. Şu anvercel.appwildcard subdomain kabul ediyor (preview deploy'larda preview URL'leri eşleşir; OK ama bilinçli karar). - P0 | dev | 5dk —
middleware.py:268allow_credentials=Falsekorundu (Authorization header'da JWT, cookie yok). - P1 | dev | 30dk —
curl -H "Origin: https://evil.com" -I https://<api>/health→Access-Control-Allow-Originheader DÖNMEMELİ.
2.3 — Logging / debug
- P0 | dev | 15dk —
ENVIRONMENT=productioniken log levelINFO(DEBUG değil).services/backend/main.pylogging.basicConfigayarını doğrula. - P0 | dev | 15dk — Exception handler stack trace'i client'a göndermiyor. FastAPI default
{"detail": "Internal Server Error"}yeterli; custom handler eklediysen mesajda traceback olmadığını test et. - P0 | dev | 10dk —
middleware.py:139_SENSITIVE_PATH_FRAGMENTSlistesi/auth/,/login,/token,/refresh,/passwordiçeriyor (OK)./inspectupload body'si zaten log'a girmiyor (sadece query string). - P1 | dev | 20dk — Access log SAMPLE et: prod'da her isteğin tam JSON log'u Render'in 100GB/ay limitini hızlı tüketebilir. Sentry breadcrumb'a ya da error-only log'a düşür.
2.4 — JWT validation
- P1 | dev | 30dk —
services/backend/security.py:191JWT encode'aissclaim eklenmiş amaverify_token(security.py:226-231)issdoğrulaması yapmıyor.jwt.decode(..., issuer="arac-hasar-v2")ekle, options'darequire: ["iss"]. - P1 | dev | 20dk —
audclaim ekle (örn.aud=arac-hasar-v2:api) ve verify et. WS, web, mobile için farklı audience kullanabilirsin (ileri sürüm). - P2 | dev | 2s — JWT revocation list (jti blacklist) Redis'te. Logout endpoint refresh token'ı invalidate etsin.
- P2 | dev | 1s — Refresh token rotation: her refresh'te yeni jti üret, eskisini blacklist'e at.
2.5 — Rate limiting
- P0 | dev | 30dk —
middleware.py:80-91limiterRedis storage_uri set edilmiş mi prod'da kontrol et.RATE_LIMIT_REDIS_URLRender free'de Upstash Redis kullanabilirsin (free tier 10K req/gün). - P0 | dev | 20dk — Endpoint'lerde decorator'lar var mı doğrula:
/auth/login→5/minute,/api/v1/inspect→60/minute.grep -n "@limiter" services/backend/main.py. - P1 | dev | 1s — Test: aynı IP'den 10 hatalı login → 6. attempt 429 dönmeli.
- P1 | dev | 30dk —
/auth/registeriçin de rate limit (3/hourper IP).
2.6 — Security headers
- P0 | dev | 15dk —
middleware.py:209-237SecurityHeadersMiddlewareaktif. Prod URL'decurl -I https://<api>/healthile şu header'ları doğrula:X-Content-Type-Options: nosniffX-Frame-Options: DENYStrict-Transport-Security: max-age=31536000; includeSubDomains; preloadContent-Security-Policy: default-src 'none'; ...Referrer-Policy: strict-origin-when-cross-origin
- P1 | dev | 10dk —
Permissions-Policyheader'ıcamera=()engelliyor — mobile/desktop client kamerayı browser üzerinden değil native API ile alıyor, sorun yok. - P1 | devops | 20dk — securityheaders.com'da production API URL test et → A veya A+ skoru.
2.7 — WebSocket auth (HIGH bulgu hâlâ açık)
- P0 | dev | 2s —
services/backend/main.py:1065-1077WS endpoint auth yok. Saldırgan inspection_id bilse her kullanıcının inspection'ını dinleyebilir. Minimum:@app.websocket("/api/v1/inspect/{inspection_id}/stream") async def inspect_stream(websocket: WebSocket, inspection_id: str, token: str = Query(...)): try: payload = verify_token(token, expected_type="access") except HTTPException: await websocket.close(code=1008) # Policy Violation return # Ek: bu inspection_id payload.sub'a mı ait? await stream_inspection(websocket, inspection_id) - P0 | dev | 1s — Authorization sonrası: inspection ownership check (
inspection.user_id == payload.sub). Aksi halde IDOR (Insecure Direct Object Reference). - P1 | dev | 30dk — WS rate limit (per user, max 5 concurrent connection per user).
- P1 | dev | 20dk — WS heartbeat / idle disconnect (
ws_max_duration_sec=600zaten var, doğrula).
2.8 — REST IDOR kontrolleri
- P0 | dev | 2s —
GET /api/v1/inspect/{id},GET /api/v1/inspect/{id}/result,DELETE /api/v1/inspect/{id}her birindeinspection.user_id == current_user.subcheck'i VAR mı?services/backend/main.py:873, 905, 963, 1048satırlarını oku, ownership check yoksa ekle. Admin role bypass'i ayrı kontrol. - P1 | dev | 1s — Pytest: User A'nın inspection'ını User B token ile çağır → 403 dönmeli.
2.9 — Input validation
- P0 | dev | 10dk —
services/backend/security.py:369-434validate_image_uploadMIME sniff + decompression bomb guard + EXIF strip aktif (iyi). - P1 | dev | 30dk —
max_image_size_mb=10Render free 512MB RAM'de güvenli mi? 10MB × 5 paralel = 50MB raw + PIL decode amplification (4×) = 200MB. ML model yüklemesi varsa OOM. Limit5MB'a düşür veya max_images_sync'i 2-3'e indir. - P2 | dev | 20dk — Filename sanitization (
security.py:442-477) zaten UUID prefix + whitelist ile güvenli.
2.10 — Dependency audit
- P1 | dev | 30dk —
cd services/backend && pip-audit -r requirements.txt. Critical/High CVE varsa pinleri güncelle. - P1 | dev | 20dk —
python-jose,bcrypt,passlib(varsa) sürümlerini kontrol et — python-jose'un bilinen CVE'leri var, PyJWT'ye geçişi değerlendir.
3. Frontend Hardening (Next.js / Vercel)
3.1 — Next.js CVE upgrade (KRİTİK)
- P0 | dev | 1s —
apps/web/package.jsonnext: "15.1.3"→next: "^15.5.16"(CVE-2025-29927 middleware bypass; saldırganx-middleware-subrequestheader ile middleware'i atlatabilir,/dashboardgibi protected route'lara erişebilir). - P0 | dev | 30dk — Upgrade sonrası
pnpm install,pnpm typecheck,pnpm buildlokalde geçti. - P0 | dev | 30dk —
apps/web/middleware.tsupgrade sonrası halen çalışıyor (auth redirect, locale cookie). E2E test. - P0 | dev | 10dk —
eslint-config-nextaynı sürüme bumple (^15.5.16).
3.2 — Token storage
- P1 | dev | 4s —
apps/web/lib/api.ts:20-63access_token + refresh_token localStorage'da. XSS bir kez bulunursa attacker tüm token'ları çalar. Hedef (v0.2):- Refresh token →
HttpOnly; Secure; SameSite=Laxcookie (backend/auth/refreshcookie'den okusun) - Access token → memory'de tut (React Context), reload'da
/auth/refreshile yenile - Bu değişiklik backend
/auth/refreshendpoint'inin cookie okumasını da gerektirir.
- Refresh token →
- P1 | dev | 30dk — Şu an için minimum:
apps/web/middleware.ts:24TOKEN_COOKIE = 'access_token'cookie'sinin HttpOnly olmadığını doğrula (server-side middleware bu cookie'yi okuduğundan JS'ten set ediliyor → HttpOnly olamaz). En azındanSecure; SameSite=Laxset edildiğinden emin ol. - P2 | dev | 1s — XSS koruma: tüm user-provided content
dangerouslySetInnerHTMLile render edilmiyor (grep -r dangerouslySet apps/web→ 0 sonuç olmalı).
3.3 — Axios / API config
- P0 | dev | 10dk —
apps/web/lib/api.tsaxios instancewithCredentials: false(cross-site cookie kullanmıyoruz, JWT Authorization header'da). Doğrula. - P0 | dev | 5dk —
NEXT_PUBLIC_API_URLVercel env'dehttps://<render-app>.onrender.com(http değil). - P1 | dev | 20dk — Axios timeout (
timeout: 30000) set edildi mi — Render free cold start için 60s gerekebilir, ilk istek için ayrı timeout.
3.4 — Content Security Policy (frontend)
- P1 | dev | 1s — Vercel'de
next.config.tsheaders()ile CSP set et:async headers() { return [{ source: '/(.*)', headers: [ { key: 'Content-Security-Policy', value: "default-src 'self'; img-src 'self' data: https://*.r2.dev https://*.r2.cloudflarestorage.com; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://<render-app>.onrender.com https://*.sentry.io; frame-ancestors 'none';" }, { key: 'X-Frame-Options', value: 'DENY' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, ]}]; } - P1 | dev | 30dk — CSP'yi
Report-Onlyile başlat (1 hafta), violation log topla, sonra enforce. - P2 | dev | 20dk —
nonce-based script CSP (Next 15+ destekliyor)unsafe-inline'ı kaldırmak için.
3.5 — Vercel project hardening
- P0 | devops | 15dk — Vercel project Settings → Deployment Protection → preview deploy'larda password / Vercel auth zorunlu (preview'lar arama motorlarına düşmesin).
- P1 | devops | 10dk — Production branch sadece
main. Preview deploy'lar farklı subdomain (zaten default). - P1 | devops | 20dk — Custom domain bağladıktan sonra
Strict-Transport-Securitypreload list'e başvur (https://hstspreload.org).
3.6 — output: 'standalone' ve sızıntı
- P1 | dev | 30dk —
next.config.ts:8output: 'standalone'→.next/standaloneiçinde server-side code;NEXT_PUBLIC_*olmayan env'ler client bundle'a sızmamalı.grep -r "process.env" apps/web/app apps/web/componentsile audit. - P1 | dev | 10dk —
next.config.ts:35NEXT_PUBLIC_API_URLzaten public, OK. Başka server-only env varsaprocess.env.X'i sadece server component / route handler'da kullan.
4. Database (Supabase)
4.1 — Schema / migration
- P0 | dev | 30dk —
services/backend/migrations/Alembic head local'de Supabase'e karşıalembic upgrade headile uygulanabiliyor. - P0 | dev | 15dk — Render
RUN_MIGRATIONS=1env'i ile boot'taentrypoint.shmigration çalıştırıyor. Worker servisindeRUN_MIGRATIONS=0(çift migration race condition önler).
4.2 — Connection pooling
- P0 | devops | 10dk — Supabase free direct connections: max 60, pooler connections: 200. Backend pool:
pool_size=5, max_overflow=10. Worker pool:pool_size=3, max_overflow=5. Toplam < 30. - P0 | dev | 20dk — SQLAlchemy
create_engine(..., pool_pre_ping=True, pool_recycle=300)— Supabase idle connection'ları kapatır. - P1 | dev | 30dk — Worker idle'da connection bırakmıyor (Celery worker_max_tasks_per_child=100 → periyodik recycle).
4.3 — Row-Level Security (RLS)
NOT: Mevcut kod SQLAlchemy ORM kullanıyor, backend kendi auth check'lerini yapıyor. RLS ekstra defense-in-depth katmanı. Sadece Supabase client (
anonkey) kullanan akış varsa zorunlu — bu projede yok, ama yine de aç.
- P1 | dev | 2s — Supabase Studio → Authentication → Policies.
inspectionstablosu için RLS aç:enable row level security. Policy:auth.uid()::text = user_id(Supabase auth kullanmıyorsan policy:current_setting('app.user_id', true) = user_id::text+ backend her query öncesiSET app.user_id). - P1 | dev | 1s —
userstablosu için RLS: user kendi profilini görür, admin hepsini. - P2 | dev | 1s — Service role key (backend) RLS'i bypass eder. Backend service role kullansa bile her query'de user context'ini geçiriyor mu doğrula.
4.4 — Backup ve recovery
- P0 | devops | 10dk — Supabase free tier: 7 günlük PITR YOK, sadece daily logical backup, 7 gün retention. Pilot için yeterli ama kritik veri için Pro plan ($25/ay) gerekir.
- P0 | devops | 20dk — Manuel haftalık
pg_dumpile yerel + R2'ye yedek script'i (cron / GitHub Actions). 30 gün retention. - P1 | dev | 1s — Restore drill: staging Supabase'e yedek geri yükle, app boot olduğunu doğrula.
- P1 | devops | 15dk — Backup script Sentry'ye fail bildirsin.
4.5 — Storage limits
- P0 | devops | 5dk — Supabase free 500MB storage.
inspectionstablosu satır başına ~5KB (sonuç JSON dahil). 100K satır = 500MB. Pilot için OK, 6 ay sonra Pro'ya geçiş takvime. - P1 | devops | 30dk — Eski (60 gün+)
inspections.resultJSON'ını ayrı R2 bucket'a arşivleyen cleanup job. DB'de sadece metadata kalır.
5. Monitoring & Observability
5.1 — Sentry
- P0 | dev | 30dk — Sentry hesap aç (free 5K events/ay). Backend project + Frontend project ayrı.
- P0 | dev | 20dk — Backend:
services/backend/main.py'dasentry_sdk.init(dsn=settings.sentry_dsn, environment="production", traces_sample_rate=0.1)aktif et.render.yamlSENTRY_DSNenv zaten var. - P0 | dev | 20dk — Frontend:
@sentry/nextjspaketi ekle,sentry.client.config.ts+sentry.server.config.tsoluştur. Vercel env'eNEXT_PUBLIC_SENTRY_DSN. - P0 | dev | 15dk — Sentry → Issues → Email alerts (admin@'e) Critical + High severity için.
- P1 | dev | 30dk — Backend
before_sendhook'u: PII (email, JWT, password) scrub et. - P1 | dev | 20dk — Frontend
sourcemapupload (Vercel build hook ile). - P2 | dev | 1s — Sentry sample rate: 5K events/ay limit için
traces_sample_rate=0.05(5%) prod'da.
5.2 — Uptime monitoring
- P0 | devops | 20dk — UptimeRobot (free 50 monitor, 5dk interval) →
https://<render-app>.onrender.com/healthping. - P0 | devops | 10dk — Render free 15dk inactivity sleep → UptimeRobot 5dk ping bunu engeller (warm tutar, cold start önler).
- P1 | devops | 10dk — Vercel frontend için ayrı ping monitor.
- P1 | devops | 15dk — UptimeRobot → Alert contact: email + (opsiyonel) Telegram bot.
5.3 — Web analytics
- P1 | devops | 20dk — Cloudflare Web Analytics aktif (cookieless, GDPR/KVKK uyumlu, ücretsiz). Beacon script Vercel'de
_appveya root layout'a ekle. - P2 | devops | 30dk — Plausible / Umami self-hosted analytics alternatifi (cookie-free).
5.4 — Backend metrics
- P2 | dev | 1s —
/metricsPrometheus endpoint (FastAPIprometheus-fastapi-instrumentator). Grafana Cloud free (10K series) ile scrape. - P2 | dev | 30dk — ML inference latency histogram (p50/p95/p99 görünür olsun).
5.5 — Log aggregation
- P1 | devops | 20dk — Render log'ları default 7 gün. Critical log'lar için Better Stack (eski Logtail) free 1GB/ay → Render log drain ayarla.
6. KVKK Uyumu (Türkiye)
6.1 — Bilgilendirme metinleri
- P0 | legal | 2s —
/legal/privacyprivacy policy sayfası. Template: KVKK Kurumu sitesinden örnek + bu projeye özel: "Görseller AI işlemesi için Cloudflare R2'de saklanır, X gün sonra silinir, plaka/VIN anonimleştirilir." - P0 | legal | 1s —
/legal/termskullanım koşulları. - P0 | dev | 2s — Foto upload öncesi inline notice (modal veya checkbox): "Yüklediğiniz görseller yapay zeka ile analiz edilir. Plaka ve kişisel bilgiler otomatik anonimleştirilir. Detaylar için Gizlilik Politikası."
- P0 | dev | 1s — Kayıt formunda KVKK açık rıza checkbox (
required), link/legal/privacy'e gider.
6.2 — Veri sahibi hakları
- P0 | dev | 30dk —
DELETE /api/v1/inspect/{id}endpoint kullanıcının kendi inspection'ını silebiliyor mu doğrula (ownership check ile birlikte). DB'den + R2'den orijinal görsel + overlay'i sil. - P0 | dev | 2s —
DELETE /api/v1/users/meendpoint: hesap silme (soft delete + 30 gün sonra hard delete). Tüm inspection'lar cascade sil veya anonymize. - P1 | dev | 2s —
GET /api/v1/users/me/exportendpoint: kullanıcının tüm verisini JSON olarak indir (KVKK Madde 11 — "verilerinizin bir kopyasını talep etme"). - P1 | legal | 30dk — Veri silme talebi prosedürü dokümante et:
kvkk@<domain>email → 30 gün içinde yanıt.
6.3 — Cookie consent
- P1 | dev | 2s — Cloudflare Web Analytics cookie kullanmıyor → consent banner gerekmez. Ama GA4 / Hotjar eklersen → cookie consent (örn.
klaro,cookieconsent). - P1 | dev | 15dk —
NEXT_LOCALEcookie functional (zorunlu) → consent gerekmez, ama privacy policy'de belirt.
6.4 — VERBIS
- P2 | legal | 4s — Kayıtlı kullanıcı sayısı 100K'yı aşar veya yıllık ciro > 25M TL olursa VERBIS kaydı zorunlu. Pilot için muaf, ama takvime al.
6.5 — Veri işleyici sözleşmeleri
- P1 | legal | 1s — Cloudflare DPA (Data Processing Agreement) → Cloudflare dashboard'dan kabul.
- P1 | legal | 1s — Supabase DPA → Supabase dashboard.
- P1 | legal | 1s — Render DPA → Render dashboard / support.
- P1 | legal | 1s — Sentry DPA.
6.6 — Veri minimizasyonu
- P0 | dev | 15dk —
security.py:422-424EXIF strip aktif (GPS / camera serial / timestamp PII'ı kaldırır). Doğrula. - P1 | dev | 1s — Plaka / VIN anonymization ML pipeline'da var mı? Yoksa
output_formatter.py'da regex blur (görseli prod'da arşivlemeden önce). - P1 | dev | 30dk — Access log'da IP adresi maskeleme (
/24subnet'e indir). KVKK için IP kişisel veri sayılır.
7. Performance
7.1 — Render free cold start
- P0 | devops | 5dk — render.yaml
plan: starter($7/ay) önerilse de free tier kullanacaksan:plan: free(var mı kontrol et — Render free tier'da Docker desteği sınırlı). ML model 700MB RAM + Docker image 600MB → free 512MB RAM'de çalışmaz. Minimumstarter($7/ay) zorunlu. - P0 | devops | 20dk — UptimeRobot ping yukarıda — Render free 15dk sleep'i engeller, starter plan'da sleep yok ama yine de health check için ping kullan.
- P1 | dev | 30dk — Cold start sırasında model load süresi log'la. Sentry'de p95 boot time metric.
7.2 — Image storage / cache
- P1 | devops | 30dk — Model weights
MODEL_S3_PREFIX=models/full_20260515_044630boot'ta R2'den fetch. 140MB × 3 model = 420MB indirme. Aynı region (Frankfurt) ise saniyeler. Render restart'ta tekrar fetch (ephemeral disk) — kabul edilebilir. - P2 | devops | 1s — Model weights'i Docker image içine bake et (
Dockerfile.embeddedzaten var) — boot süresi azalır ama image 1GB+ olur, build/push yavaşlar.
7.3 — Frontend bundle size
- P1 | dev | 30dk —
pnpm buildsonrasıapps/web/.next/standaloneboyut analizi.lucide-reacticon tree-shake doğru çalışıyor (optimizePackageImportsaktif). - P1 | dev | 20dk —
next/imageile lazy load + AVIF/WebP zaten aktif. LCP < 2.5s mobile 4G.
7.4 — Database query
- P1 | dev | 1s —
inspectionstablosu üzerinde(user_id, created_at DESC)index var mı? History listesi için kritik. - P1 | dev | 30dk —
EXPLAIN ANALYZEile en sık çalışan 5 query'yi profile et.
7.5 — Supabase storage projeksiyonu
- P1 | devops | 15dk — 1000 inspection × 5KB metadata = 5MB DB; 1000 × 3 görsel × 200KB = 600MB R2 storage. R2 free 10GB → 16K inspection'a yeter.
- P2 | devops | 30dk — Storage budget alert: R2 dashboard'da %80'e gelince email.
8. Disaster Recovery
8.1 — Backup prosedürü
- P0 | devops | 30dk — Database: yukarıda 4.4'te ele alındı (Supabase otomatik + manuel weekly
pg_dump). - P0 | devops | 30dk — Model weights: R2 bucket'ta versioning aktif. Production prefix
models/full_<timestamp>(immutable). Yeni model deploy'unda eskisini silme — rollback için 3 sürüm tut. - P1 | devops | 20dk — R2 bucket'ta Cross-Region replication (R2 free buna sahip değil — kritik değil, model weights GitHub release'e de yedekle).
- P1 | dev | 1s —
services/backend/migrations/alembic version history → git'te (zaten orada). Rollback içinalembic downgrade -1test edildi mi.
8.2 — Rollback prosedürü
- P0 | devops | 20dk — Render → Deploys → her deploy'un
Rollbackbutonu var. Önceki successful deploy'a 1 tıkla dönülür. Test et: dummy commit deploy → rollback. - P0 | devops | 15dk — Vercel → Deployments → "Promote to Production" ile eski deploy'a anında dön.
- P1 | dev | 30dk — DB migration rollback playbook: yeni migration deploy edip sorun çıkarsa → 1) Render eski sürüme rollback, 2)
alembic downgrade -1, 3) Sentry'de error rate check. - P1 | dev | 30dk — Model rollback:
MODEL_S3_PREFIXenv'i Render dashboard'dan eski path'e değiştir → redeploy.
8.3 — Incident response
- P1 | dev | 1s —
docs/INCIDENT_RESPONSE.md(bu deploy sonrası yaz): kim, ne zaman, hangi karar. Sev1 (down) / Sev2 (degraded) / Sev3 (cosmetic). - P1 | devops | 20dk — Status page (Hyperping / BetterUptime free) → public.
9. Post-Launch Monitoring (ilk 30 gün)
Bu liste deploy SONRASI haftalık tikleyeceğin checkup.
Günlük (ilk 1 hafta)
- Sentry → Issues → 24h içinde yeni Critical / High issue var mı?
- UptimeRobot → 24h uptime ≥ %99.5
- Render → Metrics → CPU < %70, Memory < %80
- Supabase → Database → Active connections < 50
- R2 → Storage → kullanım trendi
- Cloudflare Analytics → traffic, error rate
Haftalık (ilk 30 gün)
- Sentry events / month → 5K limit'in altında mı (free tier)?
- DB backup başarılı mı? Restore drill ayda 1.
- Dependency scan:
pip-audit+pnpm audit - R2 storage trend → 80%'e yaklaştı mı?
- Supabase storage → 500MB free tier'a yaklaştı mı?
- Log aggregator → ERROR / WARNING en sık 10 kaynak
Aylık
- Penetration test: OWASP ZAP baseline scan, Nuclei ile bilinen CVE tarama.
- Secret rotation: JWT, R2 token, Supabase password takvim kontrolü.
- User feedback → security/privacy concern var mı?
- Cost review: Render + Supabase + R2 + Sentry toplam < bütçe?
- KVKK: silme talebi geldi mi, 30 gün içinde yanıtlandı mı?
Ek — Hızlı validation komutları
# 1. CORS test
curl -H "Origin: https://evil.com" -I https://<api>/health
# Beklenen: Access-Control-Allow-Origin header DÖNMEMELİ
# 2. Security headers
curl -I https://<api>/health | grep -iE "strict-transport|x-frame|content-security|x-content-type"
# Beklenen: HSTS + CSP + XFO + nosniff
# 3. Rate limit
for i in {1..10}; do curl -X POST https://<api>/auth/login -d '{}' -H "Content-Type: application/json"; done
# Beklenen: 6. istekten sonra 429
# 4. WS unauth (DOĞRULA - şu an FAIL döner çünkü auth yok)
wscat -c "wss://<api>/api/v1/inspect/test-id/stream"
# Hedef: 1008 Policy Violation veya 401
# 5. IDOR test (User A token ile User B inspection)
curl -H "Authorization: Bearer <user_a_token>" https://<api>/api/v1/inspect/<user_b_inspection_id>
# Beklenen: 403 Forbidden
# 6. Secret leak (git history)
git log --all -p -- "**/.env*" | grep -iE "key|secret|password|token"
# Beklenen: boş çıktı
# 7. Next.js CVE-2025-29927 test
curl -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" https://<vercel-app>/dashboard
# Beklenen: 307 /login redirect (upgrade sonrası)
İmza & Tarih
| Rol | İsim | Tarih | İmza |
|---|---|---|---|
| dev | |||
| devops | |||
| legal |
Deploy onayı: Yukarıdaki tüm P0 maddeleri tikli ve aşağıda imzalanmadan production'a basma.
Doküman sürümü: v1.0 · Oluşturuldu: 2026-05-16 · Sahibi: Security Engineer