17 Eylül 2025 Çarşamba

Docker Compose ile Yerel LLM Kurulumu: Ollama ve Open WebUI (Llama 3.1 8B Rehberi)

Özet ve Amaç

Yerel büyük dil modeli (LLM) çalıştırmak artık sadece veri bilimi ekiplerinin değil, geliştiricilerin ve meraklı kullanıcıların da gündeminde. Bu rehberde, Docker Compose kullanarak Ollama ve Open WebUI ile bilgisayarınızda Llama 3.1 8B modelini nasıl çalıştıracağınızı adım adım anlatıyorum. Kurulum tamamen yerel olduğu için gizlilik avantajı sağlar, gecikme süresini düşürür ve internet erişimi olmadan üretken yapay zeka deneyimi yaşatır.

Gereksinimler

- İşletim sistemi: Linux, macOS veya Windows (Windows için WSL2 önerilir).
- Donanım: En az 16 GB RAM önerilir. GPU şart değil; olsa da performans artar. NVIDIA GPU hızlandırması için Linux + NVIDIA Container Toolkit gerekir.
- Yazılım: Docker ve Docker Compose (Docker Desktop içerir).
- İnternet: İlk model indirme için gerekli.

Adım 1: Docker’ı Hazırlayın

Docker yüklü değilse Docker Desktop’ı kurun. Kurulum sonrası aşağıdaki komutlarla sürümü doğrulayın:
docker --version
docker compose version
Her iki komut da sürüm döndürüyorsa hazırsınız.

Adım 2: Proje Klasörü ve Compose Dosyası

Boş bir klasör oluşturun ve kök dizine docker-compose.yml dosyasını ekleyin. Aşağıdaki içerik, Ollama ve Open WebUI’yi tek komutla ayağa kaldırır. WebUI, Ollama’yı dahili ağ üzerinden görecek şekilde ayarlanmıştır.

version: "3.9"
services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama:/root/.ollama
    restart: unless-stopped

  open-webui:
    image: ghcr.io/open-webui/open-webui:latest
    container_name: open-webui
    ports:
      - "3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    depends_on:
      - ollama
    volumes:
      - openwebui:/app/backend/data
    restart: unless-stopped

volumes:
  ollama:
  openwebui:

Not: GPU hızlandırması istiyorsanız Linux’ta NVIDIA Container Toolkit kurup Ollama servisini GPU ile çalıştırabilirsiniz. Compose tarafında farklı varyasyonlar olsa da en sorunsuz yol genellikle tek servis için docker run --gpus all ... kullanmaktır. Bu rehber CPU’da sorunsuz çalışan kurulum üzerine odaklanır.

Adım 3: Servisleri Başlatın

Proje klasörünüzde şu komutu çalıştırın:
docker compose up -d
İlk kurulumda imajlar indirileceği için biraz zaman alabilir. Durumu görmek için:
docker compose ps ve docker compose logs -f ollama komutlarını kullanabilirsiniz.

Adım 4: Modeli İndirin (Llama 3.1 8B)

Ollama konteynerine girip modeli çekin:
docker exec -it ollama bash
ollama pull llama3.1:8b
İlk indirme birkaç GB olabilir. Sisteminizin RAM ve disk durumuna göre indirip hazırlayacaktır. Daha az bellek kullanan kuantize sürümler için Ollama içindeki etiketleri inceleyebilir veya ollama run llama3.1:8b ile doğrudan test edebilirsiniz.

Adım 5: Open WebUI ile Görsel Arayüz

Tarayıcınızda http://localhost:3000 adresine gidin. İlk açılışta basit bir kayıt formu çıkabilir. Ardından sol alttaki model listesinden llama3.1:8b seçin. Eğer listede görünmüyorsa OLLAMA_BASE_URL ayarının http://ollama:11434 olduğundan ve Ollama servisinin çalışma durumundan emin olun. Alternatif olarak “Add Model” bölümünden ismi elle girebilirsiniz.

Performans İpuçları

- Kuantize modeller: 8B modellerde q4 veya q5 gibi kuantizasyonlar RAM kullanımını ciddi azaltır. Doğruluk bir miktar düşse de masaüstü sistemlerde büyük fark yaratır.
- Bağlam penceresi: Gerekli değilse yüksek context length kullanmayın. Daha az bellek tüketir ve yanıtlar hızlanır.
- Paralellik: Aynı anda çok istek göndermeyin. OLLAMA_NUM_PARALLEL=1 bırakmak çoğu sistemde daha kararlı sonuç verir.

Sorun Giderme

- Port çakışması: 11434 veya 3000 kullanımda ise compose dosyasındaki portları değiştirin (ör. 3001:8080).
- Yavaş ilk yanıt: Model ilk yüklemede belleğe alınır; ikinci istekler daha hızlı olacaktır.
- Bellek yetersiz: Kuantize sürüme geçin veya daha küçük bir model seçin (ör. 7B/8B yerine 3B-4B familyası).
- Güncelleme: docker compose pull ardından docker compose up -d ile imajları güncel tutun.

Güvenlik ve Ağ

Bu kurulum yerel geliştirme içindir. Portları internetten erişilebilir yapmayın. Uzak erişim gerekiyorsa ters proxy ve kimlik doğrulama koyun. Verileriniz container volume’larında tutulur; hassas içerik paylaşıyorsanız yedekleme ve şifreleme politikalarınızı belirleyin.

Sonuç

Dakikalar içinde Docker Compose ile Ollama ve Open WebUI’yi kurup Llama 3.1 8B gibi güncel bir LLM’i yerelde çalıştırabilirsiniz. Bu yaklaşım, gizlilik, kontrol ve düşük gecikme avantajları sunar. İhtiyaçlarınıza göre kuantize modellerle bellek kullanımını düşürüp verimi artırabilir, ileride GPU desteği ekleyerek daha hızlı sonuçlar alabilirsiniz. Yerel LLM ile prototipleme, dahili araç geliştirme ve offline kullanım senaryoları çok daha erişilebilir hale geliyor.

16 Eylül 2025 Salı

Kubernetes’te GitOps: Argo CD ile Sıfırdan Kurulum, Yapılandırma ve En İyi Uygulamalar

GitOps Nedir ve Neden Argo CD?

GitOps, uygulama ve altyapı durumunu tek gerçek kaynak olarak Git deposunda tutup teslimatı otomatikleştiren bir yaklaşım. Kubernetes dünyasında bu modelin en pratik karşılığı Argo CD. Argo CD, Git’teki deklaratif manifestleri izler, kümeye uygular ve sapmaları sürekli olarak düzeltir. Bu sayede manuel kubectl komutları azalır, güvenlik artar ve geri dönüş (rollback) işlemleri kolaylaşır. Bu yazıda Argo CD’yi sıfırdan kurup ilk “Application” nesnemizi oluşturacak, ardından pratik ipuçları ve sorun giderme önerileriyle süreci üretim standartlarına yaklaştıracağız.

Önkoşullar

Başlamadan önce aşağıdaki araçlara ihtiyacınız olacak: Kubernetes kümesi (Minikube, Kind veya bulut), kubectl (1.25+), Helm (3.x) ve örnek bir Git deposu (GitHub/GitLab). Lokal bir deneme için Minikube idealdir: minikube start. Kümede cluster-admin yetkinizin olduğundan emin olun.

Argo CD Kurulumu (Helm ile)

Helm kullanarak Argo CD’yi kurmak, yapılandırmayı sürdürmek ve güncellemeleri yönetmek için en esnek yollardan biri. Aşağıdaki adımları izleyin:

1) Argo repo’yu ekleyin:
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

2) Namespace oluşturup yükleyin:
kubectl create namespace argocd
helm install argocd argo/argo-cd -n argocd

3) Arayüze erişim: Hızlı denemeler için port yönlendirme yapabilirsiniz:
kubectl port-forward svc/argocd-server -n argocd 8080:443
Arayüz: https://localhost:8080

4) İlk parola: Varsayılan kullanıcı admin’dir. Parolayı alın:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo

İlk Uygulamanızı (Application) Tanımlayın

Argo CD’nin kalbi Application CRD’sidir. Git deposunu, hedef namespace’i ve senkronizasyon politikasını burada tanımlarsınız. Basit bir NGINX dağıtımı için örnek bir Application yaratabilirsiniz. Aşağıdaki içeriği app-nginx.yaml olarak kaydedip uygulayın:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo-nginx
  namespace: argocd
spec:
  project: default
  source:
   repoURL: https://github.com/kullanici/manifestler.git
   targetRevision: main
   path: k8s/nginx
  destination:
   server: https://kubernetes.default.svc
   namespace: demo
  syncPolicy:
   automated:
    prune: true
    selfHeal: true

Uygulamak için: kubectl apply -f app-nginx.yaml. Ardından Argo CD arayüzünde “demo-nginx” uygulamasını göreceksiniz. Automated sync sayesinde Git’e her push’ta küme otomatik güncellenecektir. “prune: true” ile Git’te çıkarılan kaynaklar kümeden de temizlenir; “selfHeal: true” ise drift (sapma) olduğunda durumu geri alır.

CLI ile Yönetim (Opsiyonel ama Önerilir)

Argo CD CLI, betiklenebilir ve hızlı yönetim için idealdir. Kurulum: macOS için brew install argocd. Giriş:
argocd login localhost:8080 --username admin --password <parola> --insecure
Uygulama oluşturma örneği:
argocd app create demo-nginx --repo https://github.com/kullanici/manifestler.git --path k8s/nginx --dest-server https://kubernetes.default.svc --dest-namespace demo --sync-policy automated --self-heal --auto-prune

Depo Yapısı ve Manifest Stratejileri

Üretimde depo düzeni, sürdürülebilirliğin anahtarıdır. Sıklıkla app-of-apps paterni tercih edilir: tek bir “root” uygulama, alt uygulamaları referans eder. Ortam ayrımı için Kustomize overlay’leri (dev/stage/prod) veya Helm chart’ları kullanın. “Config” ile “kod”u ayırın; hassas verileri Sealed Secrets veya External Secrets Operator ile yönetin.

En İyi Uygulamalar

- Şube stratejisi: Prod için korumalı branch kullanın; korumalı gözden geçirme (PR) olmadan birleşim yapmayın.
- RBAC ve SSO: Argo CD’yi OIDC/SAML ile kimlik sağlayıcınıza bağlayın; takım ve proje bazlı yetki verin.
- Sadece Git yazar: Manuel kubectl ile müdahaleyi sınırlayın; drift yaratır. Operasyonlar Git üzerinden ilerlesin.
- Health checks: Özel CRD’ler için sağlık kuralları tanımlayın; bekleme ve time-out eşiklerini ayarlayın.
- Namespace izolasyonu: Her ekip/ürün için ayrık namespace ve proje kullanın.
- Kaynak kotası ve limitler: Pod crash-loop ve aşırı kaynak kullanımını böyle önleyin.
- Audit ve izleme: Argo CD Event’lerini ve Kubernetes denetim izlerini toplayın; Prometheus/Grafana ve OpenTelemetry ile görünürlük sağlayın.

Yaygın Sorunlar ve Çözüm İpuçları

- OutOfSync ama Apply olmuyor: RBAC, mutating webhook veya admission policy engelliyor olabilir. kubectl describe ile olayları inceleyin.
- Image pull hatası: Registry kimlik bilgilerini Secret olarak tanımlayıp ServiceAccount’a bağlayın; repo başvurularını güncelleyin.
- Helm values uyuşmazlığı: Her ortam için ayrı values dosyası kullanın; Argo CD’de spec.source.helm.valueFiles ile belirtin.
- Finalizer takılması: Silinemeyen kaynaklarda finalizer’ları kaldırmadan önce bağımlılıkları temizleyin; Argo CD’nin “prune” davranışını kontrol edin.
- Sync dalgası (sync wave): Kaynak sırala(niyet)ması için argocd.argoproj.io/sync-wave anotasyonunu kullanın; CRD’ler CR’lardan önce uygulanmalı.

Güvenlik ve Güncelleme

Argo CD’yi düzenli olarak güncelleyin: helm upgrade argocd argo/argo-cd -n argocd --version <hedef>. Admin varsayılan parolasını kurulumdan sonra hemen değiştirin, mümkünse SSO zorunlu hale getirin. Arayüzü doğrudan internete açmak yerine Ingress + WAF arkasında ve mTLS/oidc ile koruyun.

Sonuç

GitOps ile operasyonel borç azalır, dağıtım tekrarlanabilirliği artar ve denetlenebilirlik kazanırsınız. Argo CD, Kubernetes üzerinde bu yaklaşımı benimsemek için olgun, topluluk tarafından desteklenen bir araç. İlk kurulumdan sonra depo yapınızı sağlamlaştırın, otomasyonu politikalarla çerçeveleyin ve her değişikliği Git üstünden yürütün. Böylece hem geliştirici deneyimi hem de üretim güvenilirliği belirgin şekilde iyileşir.

15 Eylül 2025 Pazartesi

Docker Buildx ile Çok Mimarili İmaj Oluşturma ve GitHub Actions ile Otomatik Yayınlama (2025 Rehberi)

Giriş

Apple Silicon (arm64) makineler, Raspberry Pi gibi ARM tabanlı cihazlar ve geleneksel x86_64 sunucuların aynı anda hedeflenmesi artık modern projeler için kaçınılmaz. Çok mimarili (multi-arch) Docker imajları, tek bir etiket altında hem amd64 hem de arm64 varyantlarını barındırarak bu ihtiyacı şık biçimde çözer. Bu rehberde, Docker Buildx ile yerelde çok mimarili imaj üretmeyi ve GitHub Actions üzerinden Docker Hub’a otomatik yayınlamayı adım adım anlatıyorum. Hedefimiz: sürtünmesiz bir CI/CD hattı, hızlı önbellekleme ve güvenilir üretim dağıtımları.

Ön Gereksinimler

Yerel ortamda Docker Engine veya Docker Desktop’ın güncel bir sürümünü (tercihen 24+) kullanın. BuildKit artık varsayılan, ancak gerekirse DOCKER_BUILDKIT=1 ile etkinleştirildiğinden emin olun. Bir Docker Hub hesabı ve GitHub deposu hazırlayın. Reponuzda hassas bilgileri GitHub Secrets ile saklayacağız (DOCKERHUB_USERNAME, DOCKERHUB_TOKEN vb.).

Yerelde Buildx ile Çok Mimarili İmaj Oluşturma

Önce Buildx sürümünü kontrol edin: docker buildx version. Özel bir builder oluşturup devreye alın: docker buildx create --name multi --use. Ardından QEMU emülasyonunu etkinleştirin ki yerel mimariniz dışında da derleme yapabilesiniz: docker run --privileged --rm tonistiigi/binfmt --install all. Builder’ı hazırlayın: docker buildx inspect --bootstrap. Artık çok mimarili derleme komutu hazır: docker buildx build --platform linux/amd64,linux/arm64 -t KULLANICI/IMAJ:1.0 --push . Bu komut Docker Hub’a doğrudan push yapacağından, önce docker login ile oturum açmayı unutmayın.

Dockerfile İpuçları (Mimariden Bağımsız ve Hızlı)

Temel imaj seçimi kritik: Hem amd64 hem arm64 desteği olan resmi imajları tercih edin (ör. node:20-alpine, python:3.12-slim, golang:1.22-alpine). Çok aşamalı (multi-stage) Dockerfile kullanarak build bağımlılıklarını runtime’dan ayırın; bu boyutu küçültür ve güvenliği artırır. Mimariye özel binary indiriyorsanız TARGETARCH veya TARGETOS argümanlarını kullanın. Örnek: ARG TARGETARCH satırından sonra, curl -L https://example.com/bin-$TARGETARCH -o /usr/local/bin/app gibi bir strateji uygulayabilirsiniz. Node.js projelerinde native modüller (ör. Sharp) için build araçlarını sadece builder aşamasında yükleyip, final aşamada minimal tabana geçmek iyi bir pratik.

Örnek Build Komutu ve Hızlı Doğrulama

Yerel denemede aşağıdaki tek satır oldukça pratik çalışır: docker buildx build --platform linux/amd64,linux/arm64 -t KULLANICI/IMAJ:latest --push -f Dockerfile . Yayın sonrası manifesti doğrulamak için: docker buildx imagetools inspect KULLANICI/IMAJ:latest. Çıktıda iki platformu birden görmelisiniz. Ayrıca farklı makinelerde docker pull yaparak gerçek koşullarda test edin (ör. bir M2 Mac ve bir x86_64 bulut sunucusu).

GitHub Actions ile Otomatik Yayınlama (CI/CD)

Depo köküne .github/workflows/docker.yml oluşturun ve şu adımları içeren bir iş akışı kurgulayın: kaynak kodu checkout, QEMU kurulumu, Buildx kurulumu, Docker Hub’a giriş, imaj etiketlerini otomatik üreten metadata, çok mimarili build ve push. Özet akış şöyle olmalı: checkout → setup-qemu → setup-buildx → login → metadata → build-push.

Önerilen ana adımlar: actions/checkout@v4 ile kodu çekin. docker/setup-qemu-action@v3 QEMU’yu kurar. docker/setup-buildx-action@v3 Buildx’i etkinleştirir. docker/login-action@v3 ile Docker Hub’a giriş yapın (secrets kullanın). docker/metadata-action@v5 commit SHA, branch, semver tag’lerden akıllı etiketler üretir. docker/build-push-action@v6 ile platforms: linux/amd64,linux/arm64, push: true parametreleriyle build ve yayın yapın.

Önbellek (cache) çok önemlidir. docker/build-push-action içinde cache-from: type=gha ve cache-to: type=gha,mode=max kullanarak GitHub’ın yerleşik önbelleğini devreye alın. Bu, aynı bağımlılıkları tekrar tekrar derlemeyi önleyerek build süresini ciddi ölçüde azaltır.

Sık Karşılaşılan Sorunlar ve Çözümleri

1) QEMU kaynaklı derleme hataları: setup-qemu-action adımının çalıştığını ve en güncel binfmt’nin kurulduğunu doğrulayın. Gerekirse yerelde tonistiigi/binfmt konteyneriyle tazeleyin.

2) Temel imajda arm64 desteği yok: Kullanılan base image’ın manifestini kontrol edin veya alternatif bir resmi imaja geçin (-alpine sürümleri genellikle çok mimarili gelir).

3) Native modül derleme sorunları: Build aşamasında gerekli paketleri (ör. build-base, python3, libvips-dev) kurup final aşamada temizleyin. Node projelerinde npm ci --omit=dev ve npm rebuild --arch=$TARGETARCH gibi çözümler işe yarar.

4) Büyük imaj boyutu: Multi-stage yapı, --chown ile katman optimizasyonu, gereksiz dosyaları .dockerignore ile hariç tutma ve distroless/slim tabanlar boyutu ciddi şekilde düşürür.

Performans ve Güvenlik İpuçları

- CI’da cache’i etkin kullanın; bağımlılıkların hash’lenmesi ve doğru katmanlama build’i hızlandırır. - Tek bir manifest altında birden çok mimari barındırmak, çekme (pull) sırasında istemcinin doğru varyantı otomatik seçmesini sağlar; bu da kullanıcı deneyimini iyileştirir. - Minimum yetkili kullanıcı oluşturun (USER node gibi) ve kök kullanıcıyla çalışmaktan kaçının. - Sürüm etiketlerine özen gösterin: latest yanında 1.2.3, 1.2 gibi semantik etiketler yayınlamak geriye dönük uyumluluk ve geri dönüş kolaylığı sağlar.

Sonuç

Docker Buildx ve GitHub Actions kombinasyonu, çok mimarili imajları üretme ve dağıtma işini otomatikleştirerek geliştirme ekiplerine önemli hız kazandırır. Bu rehberdeki adımları izleyerek hem yerelde hem de CI ortamında linux/amd64 ve linux/arm64 için tek komutta güvenilir, hızlı ve ölçeklenebilir bir yayın akışı kurabilirsiniz. Doğru taban imajı, akıllı önbellek, temiz Dockerfile ve iyi etiket yönetimiyle, 2025’te çok mimarili dağıtım stratejinizi gönül rahatlığıyla standart hale getirebilirsiniz.

14 Eylül 2025 Pazar

WebGPU ile Tarayıcıda Yapay Zeka: ONNX Runtime Web ile Hızlı Görüntü Sınıflandırma Rehberi

Giriş

Tarayıcıda yapay zeka çalıştırmak artık bir deney olmaktan çıktı; WebGPU ve ONNX Runtime Web gibi teknolojiler sayesinde ciddi performans elde etmek mümkün. Bu rehberde, WebGPU hızlandırmasıyla bir ONNX görüntü sınıflandırma modelini tamamen tarayıcı içinde, ek bir sunucu tarafı altyapı olmadan nasıl koşturabileceğinizi adım adım anlatıyorum. Hedefimiz düşük gecikme, daha iyi gizlilik (veri cihazı terk etmez) ve çevrimdışı çalışma imkânı.

Neden WebGPU?

WebGPU, WebGL’in ötesine geçen, modern GPU mimarilerinden doğrudan faydalanmayı kolaylaştıran bir tarayıcı API’sidir. Hesaplama (compute) kabiliyetleri, daha düşük sürücü ek yükü ve daha öngörülebilir performans sunar. ONNX Runtime Web de bu yeteneklerden yararlanarak modeli GPU üzerinde çalıştırabilir; GPU desteklenmiyorsa otomatik olarak WASM (CPU) yoluna düşer.

Önkoşullar ve Ortam

• Tarayıcı: Chrome/Edge 113+ sürümlerinde WebGPU varsayılan olarak açıktır. Safari’de kısmi, Firefox’ta Nightly ve bayrak ile destek var. En güncel Chrome önerilir.
• Temel JS/HTML bilgisi ve statik bir sunucu (ör. npx serve veya Vite).
• Model dosyası: ONNX Model Zoo’dan MobileNetV2-7 küçük ve hızlı bir başlangıçtır. Alternatif olarak SqueezeNet veya EfficientNet-Lite tercih edilebilir.

Proje İskeleti

Basit bir index.html dosyasıyla başlayın. Aşağıdaki minimal iskelet, bir dosya girişi, bir tuş ve sonucu göstermek için alan içerir:

<!doctype html>
<html lang="tr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>WASM + WebGPU Sınıflandırma</title>
</head>
<body>
  <input id="file" type="file" accept="image/*">
  <button id="run">Çalıştır</button>
  <canvas id="canvas" width="224" height="224"></canvas>
  <div id="out"></div>
  <script type="module" src="./app.js"></script>
</body>
</html>

ONNX Runtime Web’i Entegre Etme

CDN üzerinden ORT’yi kullanabilirsiniz. app.js dosyasında ORT’yi içe alın ve WebGPU’yu tercih eden bir oturum oluşturun. WASM fallback’i yedekte kalsın:

import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js";

// WASM dosyalarının yolu (CDN veya yerel) - doğru ayarlayın
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/";
// Daha yüksek performans için çoklu iş parçacığı (COOP/COEP gerektirebilir)
// ort.env.wasm.numThreads = navigator.hardwareConcurrency || 4;

const preferGPU = !!navigator.gpu;
const session = await ort.InferenceSession.create("./models/mobilenetv2-7.onnx", {
  executionProviders: preferGPU ? ["webgpu", "wasm"] : ["wasm"],
  graphOptimizationLevel: "all"
});

Ön İşleme: Görseli Tensore Dönüştürme

MobileNet türü modeller genelde 224x224 giriş, RGB kanal sırası ve kanal başta (NCHW) düzeni bekler. Pixel değerlerini 0-1 aralığına getirip normalize etmek gerekir. Örnek bir dönüştürme:

async function imageToTensor(file) {
  const img = new Image();
  img.src = URL.createObjectURL(file);
  await img.decode();
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0, 224, 224);
  const { data } = ctx.getImageData(0, 0, 224, 224);
  const chw = new Float32Array(1 * 3 * 224 * 224);
  const mean = [0.485, 0.456, 0.406];
  const std = [0.229, 0.224, 0.225];
  let p = 0;
  for (let y = 0; y < 224; y++) {
    for (let x = 0; x < 224; x++) {
      const i = (y * 224 + x) * 4;
      const r = data[i] / 255;
      const g = data[i + 1] / 255;
      const b = data[i + 2] / 255;
      // NCHW: önce tüm R, sonra G, sonra B kanalı
      chw[0 * 224 * 224 + p] = (r - mean[0]) / std[0];
      chw[1 * 224 * 224 + p] = (g - mean[1]) / std[1];
      chw[2 * 224 * 224 + p] = (b - mean[2]) / std[2];
      p++;
    }
  }
  return new ort.Tensor("float32", chw, [1, 3, 224, 224]);
}

Çalıştırma ve Sonuçları Yorumlama

Girdi tensörünü oluşturduktan sonra oturumu çalıştırın. Çoğu sınıflandırma modelinde çıktı tek bir tensördür; en büyük olasılığı seçip etikete dönüştürürsünüz. ImageNet etiketlerini küçük bir JSON dosyası olarak projeye ekleyebilirsiniz:

import labels from "./labels-imagenet.json" assert { type: "json" };

document.getElementById("run").addEventListener("click", async () => {
  const file = document.getElementById("file").files[0];
  if (!file) return;
  const input = await imageToTensor(file);
  const feeds = { input: input }; // Modelin gerçek girdi ismini kontrol edin
  const out = await session.run(feeds);
  const outputName = session.outputNames[0];
  const data = out[outputName].data;
  let idx = 0, max = -Infinity;
  for (let i = 0; i < data.length; i++) { if (data[i] > max) { max = data[i]; idx = i; } }
  document.getElementById("out").textContent = labels[idx] + " (" + max.toFixed(3) + ")";
});

Performans İpuçları

• İlk yüklemede bir “ısındırma” (warm-up) çağrısı yapın; modelin derlenmesi ve GPU kaynaklarının hazırlanması süre alabilir.
• WebGPU aktifleştirilmişse IOBinding ile giriş/çıkışları GPU üzerinde tutmak ek hız kazandırır (ileri kullanım).
• Modellerin quantized (INT8) sürümlerini tercih ederek dosya boyutunu ve gecikmeyi azaltın.
• Model dosyasını Brotli/Gzip ile sıkıştırıp uygun cache header’ları ayarlayın; tarayıcı önbelleğini ve Service Worker ile Cache Storage’ı kullanın.
• Görüntü ön işlemesini OffscreenCanvas veya Web Worker içinde yaparak ana iş parçacığını serbest bırakın.

Güvenlik ve Uyumluluk Notları

WASM tarafında çoklu iş parçacığı ve SharedArrayBuffer kullanmak istiyorsanız COOP/COEP başlıklarını etkinleştirmeniz gerekir. Bu mümkün değilse tek iş parçacığı da gayet iş görür; WebGPU varsa GPU hızlandırması zaten ciddi bir fark yaratır. Tarayıcı uyumluluğunda problem yaşıyorsanız, geçici olarak executionProviders: ["wasm"] ile çalışmaya devam edebilirsiniz.

Sonuç

Bu rehberle, yalnızca statik dosyalardan oluşan basit bir web projesinde ONNX Runtime Web ve WebGPU kullanarak gerçek zamanlıya yakın görüntü sınıflandırma kurdunuz. Avantajları açık: düşük gecikme, veri gizliliği ve dağıtım kolaylığı. Bir sonraki adım olarak kamera akışı (getUserMedia) ile canlı sınıflandırma, farklı model mimarileri (ResNet, EfficientNet, ViT), otomatik etiketleme veya yerleşik önbellekle çevrimdışı kullanım gibi özellikleri ekleyebilirsiniz. WebGPU ekosistemi hızla olgunlaşıyor; bugün attığınız adımlar, yarının tarayıcı tabanlı yapay zeka uygulamalarının temelini oluşturacak.

3.

13 Eylül 2025 Cumartesi

Passkey (WebAuthn) ile Şifresiz Giriş Entegrasyonu: Adım Adım Rehber

Passkey nedir ve neden önemli?

Geleneksel parolalar hem kullanıcı deneyimi açısından sorunlu hem de güvenlik zaafları nedeniyle verimsizdir. Passkey, FIDO2 ve WebAuthn standartlarına dayalı, phishing’e dayanıklı ve cihaz tabanlı bir kimlik doğrulama yöntemidir. Kullanıcılar artık “123456” gibi zayıf parolalarla uğraşmak yerine, platformlarının (iOS, Android, Windows, macOS) yerleşik biyometrik doğrulamasını veya donanım anahtarını kullanır. Bu yazıda, modern bir web uygulamasına passkey ile şifresiz girişin nasıl ekleneceğini, mimari prensipleri ve dikkat edilmesi gereken güvenlik detaylarını anlatıyorum.

Temel mimari ve kavramlar

WebAuthn iki temel akıştan oluşur: Kayıt (Registration) ve Giriş (Authentication). Kayıt sırasında tarayıcı bir anahtar çifti üretir ve sunucuya sadece public key ve credentialId gibi meta veriler iletilir. Girişte, sunucu tarafından üretilen tek kullanımlık challenge imzalanarak doğrulama yapılır.

Önemli alanlar: rpId (Relying Party ID, genellikle alan adınız), origin (tam köken, ör. https://app.siteniz.com), userHandle (kullanıcının uygulama içi benzersiz kimliği), signCount (anti-rollback sayacı). Her doğrulamada challenge rastgele üretilir ve tek seferliktir.

Önkoşullar ve teknoloji seçimi

Üretimde HTTPS zorunludur. Geliştirme için localhost istisnası vardır. Sunucu tarafında doğrulama için hazır kütüphaneler tercih edin: Node.js için SimpleWebAuthn, Go için duo-labs/webauthn, Java için WebAuthn4J, Python için webauthn. İstemci tarafında tarayıcı API’si navigator.credentials üzerinden erişilir. Veritabanında her kullanıcı için birden fazla credential saklamaya hazır olun (cihaz kaybı senaryosu).

Kayıt (Registration) akışı

1. Sunucu hazırlığı: Kullanıcı hesabı oluşturulduğunda veya “Passkey ekle” tıklandığında sunucu bir PublicKeyCredentialCreationOptions üretir. İçerik: rp (name, id), user (id, name, displayName), challenge (kriptopratik rastgele dizi), pubKeyCredParams (genelde -7 ES256), authenticatorSelection (ör. residentKey: "preferred", userVerification: "required"), attestation ("none" çoğu senaryo için yeterli).

2. İstemci isteği: Tarayıcıda navigator.credentials.create({ publicKey: options }) çağrılır. Kullanıcı, biyometrik veya PIN ile onaylar. Tarayıcı size attestation yanıtını döner.

3. Sunucu doğrulaması: İstemciden dönen cevabı sunucuda kütüphane ile doğrulayın. Origin ve rpId eşleşmeli, challenge doğru olmalı. Başarılıysa şu verileri saklayın: credentialId (base64url), publicKey, signCount, transports (ör. "internal", "usb", "ble"), userId. attestation politikanıza göre sertifika zinciri doğrulamasını atlayabilir veya sıkı modda açabilirsiniz.

Giriş (Authentication) akışı

1. Sunucu challenge üretimi: Giriş sayfasında kullanıcı e-posta veya kullanıcı adını girince sunucu PublicKeyCredentialRequestOptions döner: challenge, rpId, allowCredentials (ilgili credentialId listesi) ve userVerification ("required" önerilir).

2. İstemci doğrulaması: navigator.credentials.get({ publicKey: options }) çağrılır. Tarayıcı kullanıcıdan biyometrik onay ister ve assertion döner.

3. Sunucu doğrulaması: İmza ve authenticatorData kontrol edilir; rpIdHash ve origin eşleşmeli. signCount daha büyükse güncellenir, geriye düşüyorsa potansiyel klon/hatalı cihaz işareti olarak reddedilir. Başarılıysa güvenli bir session veya token üretin.

Kullanıcı deneyimi: Conditional UI ve otomatik doldurma

Conditional UI (Chromium) ile kullanıcı adını sormadan doğrudan mediation: "conditional" parametresiyle passkey önerisini tarayıcıda tetikleyebilirsiniz. iOS ve Safari tarafında sistem otomatik doldurma panelindeki “Anahtarlar” akışı devreye girer. Parola yerine “Devam etmek için cihazınızı doğrulayın” gibi net, güven veren metinler kullanın ve OTP/eposta doğrulamayı sadece kurtarma senaryosu olarak bırakın.

Güvenlik ve en iyi uygulamalar

- HTTPS zorunlu; Secure, HttpOnly, SameSite cookie bayraklarını doğru ayarlayın. CSRF için same-site veya token stratejisi uygulayın.

- rpId alan adınızla birebir ilişkili olmalı. Üretimde app.example.com kullanıyorsanız origin tam olarak bu olmalı; ters proxy kullanıyorsanız X-Forwarded-Proto/Host başlıklarını doğru iletin.

- Discoverable credentials (resident keys) açıldığında kullanıcı adı girmeden giriş mümkün olur. Ancak veritabanında kullanıcı başına çoklu credential yönetimini kurgulayın.

- Cihaz kaybı ve kurtarma: Kullanıcının birden fazla passkey ekleyebilmesini sağlayın (telefon, laptop, güvenlik anahtarı). Eski credential’ları listeden kaldırma (revoke) imkanı verin.

- Hata yönetimi: Timeout, NotAllowedError, InvalidStateError gibi hataları kullanıcı dostu mesajlarla ele alın. Geri dönüş yolunda asla parola istemeyin; kurtarma akışını ayrı doğrulamalarla çalıştırın.

Test, uyumluluk ve dağıtım

Geliştirme sırasında Chrome DevTools’taki Virtual Authenticator Environment ile farklı platformları simüle edin. iOS 17+, Android 14+, Windows Hello ve macOS/iCloud Keychain ile pratik testler yapın. Google Password Manager ve iCloud senkronizasyon farklarını (cihazlar arası passkey paylaşımı) göz önünde bulundurun.

Üretime geçerken alan adı stratejinizi belirleyin: rpId olarak kök alan adını (example.com) seçip alt alanlarda tutarlılık sağlayın. CDN ve WAF arkasında origin doğru raporlanıyor mu kontrol edin. İzleme için kayıt/giriş başarı oranı, kullanıcı başına credential sayısı, hataya düşen challenge sayısı gibi metrikler tutun.

Sonuç

Passkey (WebAuthn) ile şifresiz giriş, hem güvenliği yükselten hem de kullanıcı deneyimini sadeleştiren bir yaklaşım. Doğru rpId/origin eşlemesi, sıkı challenge doğrulaması ve sağlam bir oturum yönetimiyle phishing’e dirençli bir kimlik doğrulama katmanı elde edersiniz. Üstelik conditional UI ve discoverable credentials sayesinde “parola” kavramını kullanıcıdan tamamen gizleyerek tek dokunuşla giriş deneyimi sunabilirsiniz. Küçük bir POC ile başlayıp, çoklu credential ve kurtarma senaryolarını da kapsayacak şekilde adım adım genellemek en pragmatik yol olacaktır.

12 Eylül 2025 Cuma

Next.js 14’te Server Actions ile Güvenli Form ve Dosya Yükleme: Adım Adım Rehber

Next.js 14 ile birlikte gelen Server Actions, form gönderimlerini sunucu tarafında güvenle işlemek ve istemci kodunu sadeleştirmek için güçlü bir seçenek sunar. Bu rehberde, e-posta alanı içeren basit bir formu ve güvenli dosya yükleme akışını, Zod ile doğrulama yaparak adım adım kuracağız. Hedefimiz, gereksiz API katmanları oluşturmadan, tek bir action ile temiz ve ölçeklenebilir bir çözüm elde etmek.

Server Actions yaklaşımı; XHR/fetch kodlarını azaltır, React Server Components ile uyumlu çalışır ve SSR/ISR stratejilerine zarar vermeden form iş akışlarını basitleştirir. Dosya işlemlerinde ise tür ve boyut kontrolüyle güvenlik yüzeyini daraltır. Vercel gibi platformlarda çalışırken /tmp gibi geçici dizinlerin kullanımı mümkündür; kalıcı depolama için S3 benzeri servisler önerilir.

Gereksinimler

- Node.js 18+ ve Next.js 14+

- Paket yöneticisi (pnpm, npm veya yarn)

- (İsteğe bağlı) Zod ile şema tabanlı doğrulama

Kurulum ve Hazırlık

Yeni bir proje başlatalım:

npx create-next-app@latest next14-server-actions
cd next14-server-actions
pnpm add zod

Server Actions Next.js 14’te varsayılan olarak desteklenir. Projenizde gerekirse aşağıdaki gibi bir yapılandırma ile istek boyutunu kontrol edebilirsiniz (opsiyonel):

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: {
      bodySizeLimit: '6mb' // FormData sınırı (opsiyonel)
    }
  }
};
export default nextConfig;

Server Action: Doğrulama ve Yükleme

Aşağıdaki action, e-postayı Zod ile doğrular, dosya türünü ve boyutunu kontrol eder, ardından dosyayı bir hedef dizine yazar. Geliştirme ve Vercel üzerinde test için varsayılan olarak /tmp kullanılabilir.

// app/actions.ts
'use server';

import { z } from 'zod';
import { writeFile } from 'fs/promises';
import path from 'path';
import crypto from 'crypto';

const schema = z.object({
  email: z.string().email()
});

// Node.js API'larına ihtiyacımız olduğundan runtime'ı Node olarak zorlayalım (opsiyonel).
export const runtime = 'nodejs';

export async function uploadFile(formData: FormData) {
  const email = String(formData.get('email') || '');
  const file = formData.get('file');

  const parsed = schema.safeParse({ email });
  if (!parsed.success) {
    return { ok: false, error: 'Geçersiz e-posta adresi.' };
  }

  if (!(file instanceof File)) {
    return { ok: false, error: 'Dosya bulunamadı.' };
  }

  const allowed = ['image/png', 'image/jpeg', 'application/pdf'];
  if (!allowed.includes(file.type)) {
    return { ok: false, error: 'İzin verilmeyen dosya türü.' };
  }

  const MAX = 5 * 1024 * 1024; // 5MB
  if (file.size > MAX) {
    return { ok: false, error: 'Dosya boyutu 5MB sınırını aşıyor.' };
  }

  const arrayBuffer = await file.arrayBuffer();
  const buffer = Buffer.from(arrayBuffer);

  const filename =
    crypto.randomBytes(8).toString('hex') + '-' + (file.name || 'upload');
  const destDir = process.env.UPLOAD_DIR || '/tmp';
  const destPath = path.join(destDir, filename);

  await writeFile(destPath, buffer);

  return { ok: true, path: destPath };
}

Form Bileşeni ve Kullanım

Formun action özelliğine doğrudan server action’ı vererek kolayca bağlayabiliriz. Bu sayede client tarafında ekstra fetch mantığına gerek kalmaz.

// app/page.tsx
import { uploadFile } from './actions';

export default function Page() {
  return (
    <main style={{ maxWidth: 560, margin: '40px auto', fontFamily: 'sans-serif' }}>
      <h1>Dosya Yükleme Formu</h1>
      <form action={uploadFile}>
        <label>E-posta</label>
        <input type="email" name="email" required placeholder="[email protected]" />

        <label style={{ display: 'block', marginTop: 12 }}>Dosya (PNG/JPEG/PDF)</label>
        <input type="file" name="file" accept="image/png,image/jpeg,application/pdf" required />

        <button type="submit" style={{ marginTop: 16 }}>Gönder</button>
      </form>
      <p style={{ fontSize: 12, color: '#666', marginTop: 12 }}>
        Not: Demo amaçlı yüklemeler geçici dizine kaydedilir.
      </p>
    </main>
  );
}

İsterseniz kullanıcı deneyimini iyileştirmek için Client bileşeninde submit durumunu gösterebilir, form sonucunu Server Action’dan dönen nesneye göre ekrana yazdırabilirsiniz. Daha gelişmiş bir deneyim için useFormStatus ve useFormState yardımcılarını değerlendirebilirsiniz.

Güvenlik ve En İyi Uygulamalar

- Dosya türlerini beyaz listeyle sınırlandırın (PNG, JPEG, PDF gibi). Gerekirse MIME kontrolüne ek olarak sihirli bayt (magic bytes) kontrolü uygulayın.

- Boyut limiti belirleyin ve Next.js yapılandırmasında body limiti ile eşleştirin.

- Kalıcı depolama için S3 veya benzeri bir objeler deposu kullanın; dosya adlarını çakışmaları önlemek için hashleyin.

- İşlem süresi ve ortam kısıtları için Node.js runtime tercih edin; kenar (Edge) iş yüklerinde dosya manipülasyonu yerine imzalı URL yaklaşımı kullanın.

Çalıştırma ve Dağıtım

Geliştirme ortamında başlatmak için:

pnpm dev

Vercel’e dağıtırken ek bir API katmanına ihtiyaç duymadan aynı kod çalışır. Yalnızca kalıcı depolama kullanacaksanız ilgili servis kimlik bilgilerini çevresel değişkenlerle (ör. S3_ACCESS_KEY, S3_BUCKET) eklemeyi unutmayın.

Sonuç olarak, Next.js 14’te Server Actions ile güvenli ve performanslı bir dosya yükleme akışını, sade bir mimariyle hayata geçirebilirsiniz. Zod tabanlı doğrulama, sınırlı dosya türleri ve boyut kontrolleri ile riskleri minimize ederken, kullanıcıya yalın bir deneyim sunarsınız.

11 Eylül 2025 Perşembe

Passkey (WebAuthn) ile Şifresiz Giriş: Node.js Üzerinden Adım Adım Kurulum ve İpuçları

Passkey (WebAuthn) nedir? Neden şimdi?

Passkey, FIDO2 ve WebAuthn standartlarını temel alan, phishing’e dayanıklı ve kullanıcı dostu bir şifresiz kimlik doğrulama yöntemidir. Tarayıcılar ve işletim sistemleri 2024 itibarıyla passkey’leri yerel olarak destekler hale geldi; 2025’te ise iCloud Anahtar Zinciri, Google Password Manager ve üçüncü parti yöneticiler ile çok cihazlı senkronizasyon yaygınlaştı. Sonuç: Kullanıcılar tek dokunuşla (parmak izi, Face ID, Windows Hello) giriş yapabiliyor, geliştiriciler ise veri ihlallerine açık parola depolama yükünden kurtuluyor.

SEO açısından bakarsak, “şifresiz giriş”, “WebAuthn”, “FIDO2” ve “passkey” gibi terimler özellikle güvenlik ve kullanıcı deneyimi odaklı aramalarda öne çıkıyor. Bu rehberde Node.js ile sıfırdan bir passkey akışını nasıl kurabileceğinizi, istemci ve sunucu tarafındaki kritik ayarları ve üretim ortamında karşılaşabileceğiniz hataları ele alacağım.

Temel mimari ve akış

Kayıt (Registration): Sunucu bir “challenge” üretir, istemci WebAuthn API üzerinden biyometrik/PIN ile bir anahtar çifti oluşturur ve attestation ile sunucuya döner. Sunucu bu veriyi doğrulayıp kullanıcının public key’ini kalıcı olarak kaydeder.

Giriş (Authentication): Sunucu yine bir challenge üretir, istemci kayıtlı credential’ı kullanarak imza (assertion) üretir ve döndürür. Sunucu imzayı public key ile doğrular; eşleşirse oturum açılır.

Hazırlık: Alan, HTTPS ve kütüphaneler

WebAuthn üretimde yalnızca HTTPS üzerinde çalışır (localhost hariç). Relying Party ID (rpId), tarayıcının algıladığı etkin alanla uyuşmalıdır. Örneğin site adresiniz “app.ornek.com” ise rpId “ornek.com” ya da “app.ornek.com” olabilir. Node.js tarafında @simplewebauthn/server veya fido2-lib gibi kütüphaneler doğrulama işini büyük ölçüde kolaylaştırır.

Node.js ile uçtan uca akış (özet)

1) Kayıt için challenge üretin: Kullanıcının henüz passkey’i yoksa sunucu tarafında bir kayıt endpoint’i (ör. /webauthn/registration/options) oluşturun. rp.name, rp.id, user.id (rastgele GUID), user.name ve authenticatorSelection (residentKey: “preferred” veya “required”, userVerification: “preferred”) gibi alanları belirleyin. Challenge’ı sunucu oturumunda saklayın.

2) İstemci tarafında create() çağırın: Tarayıcıda navigator.credentials.create({ publicKey: options }) ile kullanıcıdan biyometrik/PIN onayı alın. Dönüşte attestationObject ve clientDataJSON gelecektir. Bu verileri JSON olarak sunucuya gönderin.

3) Attestation doğrulaması: Sunucuda gelen attestation verisini kütüphaneyle doğrulayın. Başarılıysa credentialId, publicKey (COSE veya JWK), signCount ve AAGUID gibi alanları kullanıcıya bağlı olarak veritabanına kaydedin. Aynı kullanıcıya birden fazla credential tanımlanabilir.

4) Giriş için challenge üretin: /webauthn/authentication/options ile kayıtlı credentialId’leri referans alarak yeni bir challenge üretin. userVerification: “preferred” veya “required” ile MFA benzeri güvenlik seviyesi sağlayabilirsiniz.

5) İstemci tarafında get() çağırın: navigator.credentials.get({ publicKey: requestOptions }) ile kullanıcıdan onay alın. Dönüşte authenticatorData, signature ve clientDataJSON gelir; sunucuya gönderin.

6) Assertion doğrulaması ve oturum: İmza doğrulanırsa oturumu başlatın. JWT veya HttpOnly, Secure, SameSite=strict bir çerezle oturumu yönetin. Replay saldırılarını önlemek için challenge tek kullanımlık olmalı ve kısa süreli (örn. 60 sn) geçerli tutulmalıdır.

Veri modeli: Ne saklamalı?

- Kullanıcı: userId (UUID), email/username, oluşturulma tarihi.

- Credential: userId, credentialId (base64url), publicKey (COSE/JWK), signCount, transports (usb, nfc, ble, internal), aaguid, createdAt.

- Güvenlik: Her doğrulamada signCount karşılaştırması yaparak kopyalanmış authenticator tespitine yardımcı olun (bazı platformlarda sabit kalabilir; kütüphanenin notlarını okuyun).

Üretim ipuçları (Güvenlik ve UX)

- Tam HTTPS: Şifreli bağlantı zorunludur. HSTS etkinleştirilebilir.

- Origin ve rpId uyumu: Hata mesajlarının çoğu burada çıkar. Subdomain değişikliklerinde dikkatli olun.

- Discoverable credentials: Kullanıcı adı öncesi doğrudan passkey ile giriş için residentKey’i “required” yapabilirsiniz. Bu, “şifresiz ve kullanıcı adısız” akış sağlar.

- Conditional UI (Chrome/Safari): Giriş formunuzda kullanıcı adı alanı göstermeden passkey istemek için conditional UI kullanın; özellikle mobilde dönüşüm oranını artırır.

- Oturum yönetimi: HttpOnly + Secure çerez kullanın, SameSite=strict iyidir. Token tabanlı sistemlerde kısa ömürlü access token ve yenileyici mekanizma kurun.

- FallBack ve hesap kurtarma: Yedek kodlar, e-posta linki veya TOTP gibi alternatifleri dengeli biçimde sunun. Amaç passkey’e öncelik verip erişilebilirliği korumaktır.

- Attestation politikası: Çoğu tüketici uygulaması “none” kullanır. Kurumsal gereksinimlerde belirli üreticilere kısıtlama getirmek için attestation “direct” değerlendirilebilir.

- Rate limit ve anti-automation: Kayıt ve giriş uç noktalarınızı hız sınırlayın, bot koruması ekleyin.

Sık karşılaşılan hatalar ve çözümleri

“The relying party ID is not a registrable domain suffix...” rpId alan adınızla uyuşmuyor olabilir. Geliştirmede localhost kullanın; üretimde tam alan adıyla test edin.

“NotAllowedError” Kullanıcı işlemi iptal etmiş olabilir veya conditional UI tetiklenmemiştir. get()/create() çağrısını kullanıcı etkileşimi (click) bağlamında başlatın.

Attestation doğrulama başarısız Sertifika zinciri veya format hatası olabilir. @simplewebauthn gibi kütüphanelerin güncel sürümünü kullanın; ham binary verileri doğru base64url kodladığınızdan emin olun.

Safari farklılıkları iOS’ta üçüncü parti iframe içinde WebAuthn kısıtlı olabilir. Mümkünse üst alan üzerinde akışı çalıştırın.

Sonuç

Passkey (WebAuthn) ile şifresiz giriş, güvenliği artırırken kullanıcı deneyimini sadeleştirir. Node.js tarafında doğru rpId/origin ayarları, tek kullanımlık challenge’lar, güvenli oturum yönetimi ve iyi bir veri modeli ile üretim seviyesinde sağlam bir akış kurabilirsiniz. 2025 itibarıyla tarayıcı ve işletim sistemi desteği olgunlaştığı için, yeni projelerde parolayı varsayılan kabul etmek yerine passkey’i birincil kimlik doğrulama olarak düşünmek artık pratik ve rekabetçidir. Küçük başlayın, conditional UI ile sürtünmeyi azaltın ve kademeli geçiş stratejisiyle mevcut kullanıcı tabanınızı passkey’e taşıyın.

3.