15 Aralık 2025 Pazartesi

WebAuthn ve Passkeys ile Parolasız Giriş: Node.js Üzerinde Adım Adım Entegrasyon Rehberi

Giriş

Parolalar hem kullanıcılar hem de geliştiriciler için yıllardır en zayıf halka. Güvenlik ihlalleri, kimlik avı ve kullanım zorluğu derken şifrelerin yerini alan yeni standartlar artık olgunlaştı: WebAuthn ve Passkeys. Apple, Google ve Microsoft ekosistemlerinde yerleşik olan Passkey’ler; biyometrik doğrulama (Face ID, Touch ID), cihaz PIN’i veya güvenlik anahtarlarıyla kullanıcıyı güvenli ve hızlı şekilde doğrular. Bu yazıda, Node.js üzerinde modern bir Passkey (FIDO2/WebAuthn) entegrasyonunu adım adım ele alacağız.

Neden Passkey?

Passkey, kullanıcı tarafında kriptografik anahtar çifti üretir ve yalnızca kayıt olduğunuz alan adıyla (relying party) çalışır. Bu sayede kimlik avı saldırılarına karşı dirençlidir, veri sızıntılarında şifre yeniden kullanımı riski yoktur ve kullanıcı deneyimi önemli ölçüde iyileşir. iCloud Anahtar Zinciri ve Google Password Manager gibi yöneticiler, passkey’leri cihazlar arasında senkronize edebilir; güvenlik anahtarları (YubiKey, Titan Key) ise kurumsal ortamda donanımsal güvence sağlar.

Mimari ve Akış

WebAuthn entegrasyonu iki ana adımdan oluşur: Kayıt ve Giriş. Kayıtta sunucu bir challenge üretir, tarayıcı üzerinden kullanıcının cihazında anahtar çifti oluşturulur ve sunucuya attestation verisiyle beraber kimlik bilgisi (credential) kaydedilir. Girişte sunucu yine bir challenge üretir, cihaz özel anahtarla imzalar ve sunucu imzayı, sayacı (signCount) ve relying party değerlerini doğrular.

Ön Koşullar ve Güvenlik

Üretimde mutlaka HTTPS kullanın; WebAuthn tarayıcılar tarafından güvenli bağlam gerektirir. rpID alan adınız (ör. example.com), origin ise tam köken (ör. https://example.com) olmalıdır. Oturum yönetimi için HttpOnly, Secure ve SameSite cookie ayarlarına dikkat edin. Kullanıcı deneyimini iyileştirmek için mümkünse User Verification değerini required seçin; böylece biyometrik veya PIN doğrulaması zorunlu olur.

Sunucu Kurulumu (Node.js)

Sunucuda Express veya benzeri bir framework kullanabilirsiniz. WebAuthn doğrulama ve dönüşüm adımlarında kendiniz sıfırdan implementasyon yazmak yerine toplulukta yaygın kütüphanelerden yararlanmak geliştirme hızını artırır. Örneğin @simplewebauthn/server ve @simplewebauthn/browser ikilisi, kurulum ve doğrulama mantığını açık biçimde sunar. Kayıt için iki uç nokta tanımlayın: /register/options (kayıt seçenekleri üretir) ve /register/verify (kayıt yanıtını doğrular). Giriş için benzer şekilde /login/options ve /login/verify oluşturun.

Kayıt seçeneklerini üretirken rpID ve rpName (marka adınız), user.id (stabil benzersiz kimlik), user.name (kullanıcı adı/e‑posta), user.displayName, authenticatorSelection ve attestationType gibi alanları doldurun. Çoğu senaryo için attestationType = none, residentKey = preferred veya passkey zorunlu ise required, userVerification = required iyi varsayılanlardır. Challenge değerini kriptografik olarak güçlü bir rastgelelik ile üretin ve oturumda kısa süreli saklayın.

Kullanıcı tarayıcıda kayıt yanıtı döndüğünde sunucu tarafında doğrulama yapın. Başarılı doğrulamada şu bilgileri veritabanında tutun: credentialID (base64url), credentialPublicKey, counter (signCount), transports ve tercihen cihaz adına ilişkin meta. Bu kayıtlar kullanıcı hesabıyla ilişkilendirilir ve çoklu cihaz/passkey senaryolarında birden fazla credential desteklenir.

İstemci (Tarayıcı) Entegrasyonu

Tarayıcı tarafında navigator.credentials.create() çağrısı kayıt için, navigator.credentials.get() ise giriş için kullanılır. Modern tarayıcılarda Conditional UI desteği ile kullanıcı “Parolayı yazmadan” doğrudan giriş önerisi alabilir. Özellikle mobilde bu deneyim, geleneksel şifre formuna göre çok daha hızlıdır. İstemci kodunda sunucudan gelen PublicKeyCredentialCreationOptions veya PublicKeyCredentialRequestOptions nesnelerini doğru şekilde dönüştürmeyi (ArrayBuffer <-> base64url) unutmayın.

Passkey’lerin bulut senkronizasyonu açıksa (iCloud/Google Password Manager), kullanıcı yeni cihazında da sorunsuz giriş yapabilir. Kurumsal katmanda donanımsal güvenlik anahtarlarıyla (USB/NFC/BLE) aynı akışı takip edersiniz; sadece transports alanı farklılık gösterir.

Giriş Doğrulaması ve Sayaç Yönetimi

Girişte sunucu challenge üretir ve tarayıcı assertion döndürür. Sunucu, imzayı credentialPublicKey ile doğrular, rpIDHash ve origin kontrolünü yapar ve signCount değerini karşılaştırır. Donanım bazı senaryolarda sayacı artıramayabilir; bu durumda kütüphanenin “signCount kludge” dokümantasyonuna bakın. Başarılı doğrulamada kullanıcı oturumunu açın ve sayacı güncelleyin.

UX, Erişilebilirlik ve Geri Dönüş Planı

Kullanıcıya net talimatlar verin: “Cihazınızın biyometrisini kullanın” gibi. Geri dönüş planı olarak e‑posta tabanlı sihirli bağlantı veya tek kullanımlık kurtarma kodları tanımlayın. Güvenlik anahtarlarını tercih eden kullanıcılar için “harici anahtar ekle” seçeneği sunun. Çoklu cihaz desteğinde, kullanıcı hesabı ayarlarına eklenen/çıkarılan tüm kimlik bilgilerini listelemeniz önemlidir.

Uyumluluk ve Test

Geliştirme sürecinde webauthn.io veya tarayıcı geliştirici araçlarının “Security” ve “WebAuthn” panellerinden yararlanın. iOS Safari, Android Chrome, macOS ve Windows ortamlarında çapraz test yapın. Alan adı ve köken uyuşmazlığı en yaygın hatadır; yerelde test ederken localhost için HTTPS kullanmayı veya tünel servisleriyle geçici bir alan adı oluşturmayı düşünün.

Sonuç

Passkey ve WebAuthn, parolasız geleceğin pratik ve güvenli yolu. Node.js üzerinde birkaç uç nokta ve doğru yapılandırma ile güncel tarayıcılar ve işletim sistemleri arasında kusursuz bir deneyim sunabilirsiniz. Doğru varsayılanlar (attestation none, userVerification required), güçlü bir TLS yapılandırması, sağlam oturum yönetimi ve iyi bir geri dönüş planı ile projenizi bugün üretime taşımanız mümkün.

14 Aralık 2025 Pazar

Next.js ile Passkey (WebAuthn) Entegrasyonu: Şifresiz Giriş İçin Adım Adım Rehber

Giriş

Şifresiz kimlik doğrulama, web uygulamalarında güvenliği yükseltirken kullanıcı deneyimini de ciddi biçimde iyileştiriyor. Passkey (FIDO2/WebAuthn) tabanlı giriş, parolaları ortadan kaldırarak kimlik doğrulamayı cihazın güvenli donanımına emanet eder. Bu yazıda, Next.js ile Passkey entegrasyonunu adım adım ele alacak; sunucu ve istemci akışlarını, doğru yapılandırmayı, veri modelini ve canlıya alma ipuçlarını paylaşacağım.

Passkey ve WebAuthn Nedir?

WebAuthn, tarayıcı ile sunucu arasında kriptografik anahtarlar kullanarak kullanıcı doğrulamayı standartlaştırır. Passkey ise bu standardın son kullanıcıya yansıyan yüzü: Apple, Google ve Microsoft ekosistemlerinde anahtarlar güvenli biçimde saklanır ve cihazlar arasında senkronize olabilir. Kullanıcı, biyometri veya cihaz kilidi ile yetkilendirme yapar; sunucu tarafında hiçbir parola tutulmaz.

Neden Passkey?

- Kimlik avı (phishing) direncine sahiptir: Anahtarlar alan adına (RP ID) bağlıdır, başka bir sitede kullanılamaz.
- Kullanıcı deneyimi yüksektir: Parola üretme, hatırlama ve reset akışları büyük ölçüde ortadan kalkar.
- Uygulama bakım maliyeti azalır: Parola karma algoritmaları, karmaşık parola politikaları gibi işler minimize olur.

Önkoşullar ve Mimari

- Next.js 13/14 (App Router) veya 12 (Pages) ile Node.js 18+.
- Üretimde HTTPS zorunludur; geliştirmede localhost desteklenir.
- RP ID, alan adınız (ör. example.com) olmalı; origin tam URL’nizdir (ör. https://example.com).
- Sunucuda kriptografik doğrulama için Node runtime kullanın; Edge runtime, bazı kütüphanelerde sınırlı kalabilir.

Kütüphaneler ve Temel Akış

Node.js ekosisteminde @simplewebauthn/server ve tarayıcı tarafında @simplewebauthn/browser ile stabil bir deneyim elde edebilirsiniz. Kayıt (registration) ve giriş (authentication) iki ayrı fakat benzer akışla ilerler: Sunucu bir challenge üretir, tarayıcı navigator.credentials üzerinden güvenli cihaz akışını tetikler ve yanıt sunucuda doğrulanır.

Kayıt (Registration) Akışı

1) Sunucu uç noktası: /api/passkey/register/options
Sunucu generateRegistrationOptions ile kullanıcıya özgü bir challenge üretir. Parametrelerde rpID (alan adınız), rpName, user.id (kalıcı ve benzersiz), user.name, attestation (çoğunlukla none) ve discoverable credentials için authenticatorSelection.residentKey="preferred", userVerification="preferred" kullanabilirsiniz. Challenge’ı ve kullanıcı kimliğini kısa süreli (örn. 5 dk) Redis gibi bir depoda saklayın.

2) İstemci adımı:
Tarafınıza dönen PublicKeyCredentialCreationOptions ile @simplewebauthn/browser kütüphanesindeki startRegistration çağrılır. Tarayıcı biyometri/donanım akışını yönetir ve yanıt üretir.

3) Sunucu doğrulaması: /api/passkey/register/verify
verifyRegistrationResponse ile gelen veriyi doğrulayın. Başarılıysa veritabanına credentialId (Base64URL), publicKey, counter, transports, backedUp gibi alanları ekleyin. Aynı kullanıcı için birden fazla cihaz desteği sağlamak üzere bir “kullanıcı-kimlik bilgisi” tablosu kullanın.

Giriş (Authentication) Akışı

1) Sunucu uç noktası: /api/passkey/login/options
generateAuthenticationOptions ile challenge üretin. Kullanıcının e-posta veya kullanıcı adı verdiği senaryoda allowCredentials ile ilgili kimlik bilgilerini sınırlayabilir; tamamen kullanıcı adı girmeden “sinyalsiz” giriş için discoverable credentials’ı destekleyebilirsiniz.

2) İstemci adımı:
startAuthentication çağrısı tarayıcıda WebAuthn akışını tetikler; kullanıcı cihaz kilidi veya biyometri ile onaylar.

3) Sunucu doğrulaması:
verifyAuthenticationResponse ile yanıtı doğrulayın. Counter değeri artmıyorsa veya geriye gidiyorsa potansiyel klonlama şüphesi doğar; kontrol edip güncelleyin. Başarılı doğrulamada oturum belirtecini (HttpOnly, Secure, SameSite=Lax) ayarlayın.

Veri Modeli ve Saklama

Veri tabanı şeması örneği: user_credentials(id, user_id, credential_id, public_key, counter, transports, backed_up, created_at). credential_id ve public_key için Base64URL normalize edin. Kullanıcı silme ve cihaz yönetimi (ör. “Bu cihazı kaldır”) arayüzü sağlayın.

Yapılandırma Ayrıntıları ve İpuçları

- Origin/RP ID eşleşmesi kritik: https://app.example.com için RP ID example.com veya alt alan adınızın köküne uygun olmalı. 127.0.0.1 yerine localhost kullanın.
- HTTPS zorunlu; yalnızca http://localhost istisnadır.
- Attestation çoğu senaryoda “none”; kurumsal güven zinciri gereksiniminde metadata doğrulamayı (MDS) değerlendirin.
- userVerification: “required”, uygulamanızın risk profiline göre zorunlu biyometri sağlar.
- Challenge tek kullanımlık ve kısa ömürlü olmalı, tekrar kullanımda reddedilmeli.
- Edge vs Node: Doğrulama kütüphanesinin Node kriptosuna ihtiyacı olabilir; Next.js API Routes’ı Node runtime’da çalıştırın.

UX Önerileri

- Yeni kullanıcı kayıt akışında parolasız varsayılanı sunun; uygun cihaz yoksa e-posta sihirli bağlantı veya tek seferlik kod ile yedek akış bırakın.
- Giriş sayfasında “Passkey ile devam et” butonu tek tıklama ile süreci başlatmalı; başarısızlıkta otomatik olarak yedek akışa yönlendirin.
- Cihaz yönetimi sayfasında kayıtlı cihaz adlarını, eklenme tarihini ve kaldırma seçeneklerini gösterin.

Hata Ayıklama ve Sık Karşılaşılan Sorunlar

- “The relying party ID is not a registrable domain suffix” uyarısı: RP ID yanlış. Geliştirmede localhost kullanın; üretimde çıplak alan adı veya uygun alt alan.
- “NotAllowedError” hatası: Kullanıcı onaylamadı veya tarayıcı akışı zaman aşımına uğradı. Zaman aşımı süresini makul tutun ve net geri bildirim verin.
- “This device doesn’t support passkeys”: Eski OS/tarayıcı olabilir; güncellemeyi önerin ve yedek kimlik doğrulamasını aktif tutun.

Güvenlik En İyi Uygulamaları

- Oturum çerezlerini HttpOnly ve Secure olarak ayarlayın; CSRF riskine karşı SameSite=Lax veya CSRF belirteci kullanın.
- Rate limiting ve IP/cihaz parmak iziyle kötüye kullanımı azaltın.
- Kök alan adından başka ortamlara (staging) dağıtırken RP ID ve origin’i doğru güncelleyin.
- Yedek kurtarma (ör. e-posta + ek doğrulama) ve cihaz kaybı senaryolarını önceden tasarlayın.

Sonuç

Passkey (WebAuthn), modern web uygulamalarında güvenlik ve kullanılabilirliği aynı anda yükselten bir yapı taşı. Next.js ekosisteminde @simplewebauthn ile kısa sürede kayıt ve giriş akışlarını hayata geçirebilir, phishing’e dirençli, hızlı ve kullanıcı dostu bir kimlik doğrulama deneyimi sunabilirsiniz. Doğru RP ID/origin eşleşmesi, kısa ömürlü challenge’lar ve sağlam bir cihaz yönetimi arayüzü ile üretime hazır bir çözüm elde etmek zor değil.

13 Aralık 2025 Cumartesi

OpenTelemetry ile Mikroservislerde Dağıtık İzleme: Grafana Tempo ve Loki ile Uçtan Uca Kılavuz

Giriş

Mikroservis mimarisine geçtikçe uygulamaların davranışını anlamak ve sorunları hızla tespit etmek, basit log takibinin çok ötesine geçmeyi gerektirir. OpenTelemetry (OTel), ölçümleme (metrics), izleme (traces) ve günlük (logs) verilerini satıcıdan bağımsız bir standartla üretip toplamanıza izin veren modern bir çatı sunar. Bu yazıda, OpenTelemetry ile Node.js ve Python (FastAPI) servislerinin enstrümantasyonunu kavramsal olarak ele alacak, verileri Grafana Tempo (tracing) ve Loki (logging) ile toplayıp Grafana’da görselleştirmenin pratik adımlarını anlatacağım.

Mimari: Hangi bileşenler bir araya geliyor?

OpenTelemetry iki ana katmandan oluşur: uygulama içine eklediğiniz SDK/Enstrümantasyon katmanı ve veriyi toplayıp yöneten Collector. Uygulamalarınız OTLP protokolü üzerinden Collector’a veri gönderir; Collector da bu veriyi işleyip farklı hedeflere (Tempo, Loki, Prometheus, vs.) dağıtır. Bu yapının avantajı, uygulama kodunuza dokunmadan hedefleri, örneğin Jaeger’den Tempo’ya veya bir APM’e, değiştirebilmenizdir.

Bu kılavuzda şu akışı hedefliyoruz: Uygulamalar (Node.js/FastAPI) → OTLP (gRPC/HTTP) → OTel Collector → Tempo (traces) + Loki (logs) + Prometheus (metrics, opsiyonel) → Grafana ile görselleştirme.

Collector: Konfigürasyon mantığı

Collector konfigürasyonu üç ana bloktan oluşur: receivers (hangi protokolden veri alacağını belirtir, örn. otlp), processors (batch, attributes, memory_limiter gibi akış içi işlemler), exporters (veriyi nereye göndereceği, örn. tempo/loki). Tipik bir senaryoda, otlp receiver’dan gelen tüm izleri batch işlemcisinden geçirir, servis adı gibi resource etiketleri ekler ve tempo exporter’a yönlendirirsiniz. Loglar için benzer akış loki exporter’a gider. Metrics verisini kullanacaksanız prometheus veya otlp exporter ekleyebilirsiniz.

Üretimde şunları eklemek iyi bir pratiktir: memory_limiter (Collector’ın stabil kalması için), batch (veri paketleme ve throughput optimizasyonu), tail_sampling (yüksek trafik altında daha anlamlı izleri seçmek için kuyruk sonu örnekleme), attributes/resource (ortam, sürüm, ekip etiketleri).

Uygulama tarafı: Node.js servisini enstrümante etmek

Node.js tarafında çekirdek bileşenler: @opentelemetry/sdk-node, otomatik enstrümantasyon paketleri (HTTP, Express, MySQL/Postgres istemcileri vb.) ve OTLP exporter. Amaç; servisiniz başlarken bir tracer sağlayıcısı başlatmak, service.name, deployment.environment, service.version gibi resource etiketlerini tanımlamak ve Collector’a OTLP üzerinden veri göndermektir. Bağlantı için OTEL_EXPORTER_OTLP_ENDPOINT gibi çevre değişkenlerini kullanabilirsiniz. Üretimde gRPC tercih etmek genellikle daha performanslıdır.

Log korelasyonu için Node.js logger’ınız (ör. pino veya winston) ile trace_id ve span_id alanlarını yapılandırılmış loglara enjekte edin. Böylece Grafana’da bir trace’i incelerken ilgili loglara tek tıkla geçebilirsiniz. Eğer logs sinyalini de OTel üzerinden gönderecekseniz OTEL_LOGS_EXPORTER=otlp ve Collector’da loki exporter’ı etkinleştirerek aynı akış içinde korelasyon sağlayabilirsiniz.

Uygulama tarafı: Python FastAPI servisinde OTel

Python’da opentelemetry-sdk, opentelemetry-instrumentation-fastapi ve opentelemetry-exporter-otlp paketleriyle hızlıca başlarsınız. Çoğu durumda opentelemetry-instrument komutu ile otomatik enstrümantasyon yeterli olur. Yine OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_RESOURCE_ATTRIBUTES (örn. deployment.environment=prod) gibi değişkenlerle konfigürasyon yapın. Asenkron çağrılarda bağlamın (context) düzgün taşındığından emin olmak için FastAPI ve HTTPX/Requests enstrümantasyonlarının etkin olduğuna dikkat edin.

Log korelasyonunda Python logging veya structlog ile trace_id’yi log kaydına dahil ederek Loki tarafında aynı etiketlerle sorgulanabilir hale getirin. Bu, “Trace → İlgili logları göster” yolculuğunu saniyelere indirir.

Grafana: Tempo ve Loki ile görselleştirme

Grafana’da veri kaynakları olarak Tempo ve Loki’yi ekleyin. Tracing için Tempo Explore sekmesinde service.name veya http.method, http.route gibi etiketlerle arama yapabilirsiniz. Eğer Collector’da exemplar desteğini ve histogram metriklerini açtıysanız, metrik panellerinde örnek izlere (trace exemplars) tıklayarak doğrudan ilgili trace’e atlayabilirsiniz.

Loki’de, log etiketlerinizi (level, service, env gibi) sade ve anlamlı tutun. Trace kimlikleri log mesajlarına etiketsiz gömülmek yerine alan olarak eklenirse sorgu performansı ve filtreleme kolaylaşır. Amacınız; “Belirli bir isteğin izini aç, aynı trace_id ile logları getir, botleneck’i gör” akışını akıcı hale getirmektir.

Gelişmiş konular: Örnekleme, maliyet ve güvenlik

Örnekleme (sampling) stratejisi maliyet ve görünürlük dengesini belirler. Giriş seviyesinde head-based (SDK tarafında sabit oranlı) örnekleme iş görür; ileri seviye üretim senaryolarında tail-based (Collector içinde karar veren), hata veya yüksek gecikmeli izlere öncelik veren kurallarla çok daha anlamlı veri tutulur. Örneğin “5xx veya p95 gecikmesi yüksek istekleri sakla, diğerlerini %5 örnekle.”

Maliyet açısından batch işlemcilerini, gzip sıkıştırmayı, histogram metriklerini (DDSketch/OTLP temporalları) ve yüksek kardinaliteli etiketlerden kaçınmayı düşünün. Güvenlik tarafında ise PII içeren alanları Collector’da attributes/transform işlemcileriyle maskeleme veya atma kuralları tanımlayarak uyumluluğu sağlayın.

Sorun giderme ve ipuçları

Çok sık görülen problem, Collector veya uygulama tarafında OTLP uç noktasının hatalı olmasıdır. Sağlık kontrolleri ve debug logları ile endpoint’in erişilebilir olduğundan emin olun. Trace zincirinin kopması genellikle bağlam yayılımı (W3C traceparent) eksikliğinden kaynaklanır; gateway/ingress, mesaj kuyrukları ve aracı servislerde propagation başlıklarını ilettiğinizden emin olun. Son olarak, container’larda saat senkronizasyonu (NTP) bozuksa iz süreleri anlamsız görünür; zaman senkronizasyonunu mutlaka doğrulayın.

Sonuç

OpenTelemetry; iz, metrik ve logları ortak bir çatı altında toplayarak mikroservislerinizi gözlemlenebilir kılar. Collector ile satıcı bağımsız mimari kurabilir, Tempo ve Loki ile uygun maliyetli ve güçlü bir görünürlük katmanı elde edebilirsiniz. Node.js ve FastAPI örnekleri üzerinden özetlediğimiz yaklaşım; üretimde örnekleme, maskeleme ve korelasyon stratejileriyle birleştirildiğinde, kök neden analizi ve performans iyileştirmelerini ciddi biçimde hızlandıracaktır.

12 Aralık 2025 Cuma

Docker ile Yerel LLM Kurulumu: Ollama + Open WebUI (GPU Hızlandırmalı Adım Adım Rehber)

Giriş

Yerel büyük dil modeli (LLM) çalıştırmak; gizliliği korumak, gecikmeyi düşürmek ve kullanım maliyetini kontrol etmek için harika bir yöntem. Son dönemde Ollama ve Open WebUI, Docker ile birlikte kullanıldığında zahmetsiz bir kurulum ve modern bir arayüz sunuyor. Bu rehberde, Ollama + Open WebUI ikilisini Docker üzerinde kurup, mümkünse GPU hızlandırma ile performansı nasıl katlayacağınızı adım adım anlatıyorum.

Neler Kuracağız?

Ollama, Llama 3, Mistral, Qwen gibi açık modelleri kolayca indirip çalıştırmanızı sağlar. Open WebUI ise sohbet, dosya yükleme, RAG eklentileri ve çoklu model yönetimi gibi özelliklerle modern bir ara yüz sunar. Docker sayesinde bu iki bileşen birbirinden izole, tekrar üretilebilir ve kolay güncellenebilir hale gelir.

Gereksinimler

- İşletim sistemi: Linux (Ubuntu 22.04+ önerilir), Windows 11 (WSL2 ile), veya macOS. CPU ile çalışır; GPU hızlandırma için donanım ve sürücü şarttır.

- Donanım: En az 16 GB RAM öneririm. 8B sınıfı modeller için 6–8 GB VRAM ile kuantize sürümler rahatlar; daha büyük modeller için 12 GB+ VRAM faydalıdır.

- Docker: Linux’ta Docker Engine + Docker Compose plugin; Windows/macOS’ta Docker Desktop.

Hızlı Başlangıç: Docker Compose ile Ollama + Open WebUI

Önce iki kalıcı disk alanı (volume) ve ortak bir ağ üzerinde iki servis ayağa kaldıracağız. Aşağıdaki dosyayı docker-compose.yml adıyla kaydedin:

version: "3.8"
services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - ollama:/root/.ollama
    environment:
      - OLLAMA_KEEP_ALIVE=24h
    # GPU için not: Aşağıdaki 'GPU Hızlandırma' bölümüne bakın.

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

volumes:
  ollama:
  openwebui:

Komutlar: Bulunduğunuz klasörde docker compose up -d deyin. Ardından tarayıcıdan http://localhost:3000 adresine giderek Open WebUI arayüzünü açın.

Model İndirme ve Test

Open WebUI içinden model menüsünde “Pull” diyerek llama3:8b, qwen2:7b veya mistral:7b gibi kuantize seçenekleri indirebilirsiniz. Terminal tercih ederseniz:

docker exec -it ollama ollama pull llama3:8b
docker exec -it ollama ollama run llama3:8b "Merhaba, nasılsın?"

Her şey yolundaysa, modelden metin çıktısı almaya hemen başlayabilirsiniz.

GPU Hızlandırma (NVIDIA, AMD, Apple Silicon)

NVIDIA (Linux/WSL2): Sunucuda uyumlu sürücü ve nvidia-container-toolkit kurulu olmalı. Özet akış:

# Sürücü kurulumu sonrası:
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
 sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -fsSL https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
 sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

Ardından Ollama’yı GPU ile başlatın:

docker run -d --name ollama --gpus all -p 11434:11434 \
  -v ollama:/root/.ollama ollama/ollama:latest

Compose kullanacaksanız, mevcut servisi durdurup bu komutla GPU’lu Ollama kapsayıcısını başlatabilir veya Docker Compose V2’de desteklenen GPU ayarlarını kullanabilirsiniz. Sürüm farklılıkları nedeniyle en sorunsuz yöntem genellikle yukarıdaki docker run satırıdır.

AMD ROCm (Linux): ROCm destekli kart ve sürücülerle Ollama GPU hızlandırma çalışır. Konteyner içinde ROCm erişimi için ROCm runtime ve /dev/kfd, /dev/dri cihazlarına erişim gerekebilir. Pratikte, GPU hızlandırma istiyorsanız ROCm tarafında host’a kurulu Ollama’yı, Open WebUI’yi ise Docker’da tutmak çoğu kullanıcı için daha az sorun çıkarır.

Apple Silicon (macOS): Ollama, Metal ile GPU hızlandırmayı yerel kurulumda otomatik kullanır. Docker konteyneri macOS’ta Metal’e doğrudan erişemeyeceğinden, Mac kullanıcılarına Ollama’yı host’a, Open WebUI’yi Docker’a kurmalarını öneririm. Open WebUI’nin OLLAMA_BASE_URL değerini host’taki Ollama adresine işaret edecek şekilde değiştirin.

Performans İpuçları

- Kuantizasyon: q4_k_m gibi kuantize modeller VRAM/RAM kullanımını dramatik biçimde azaltır. İlk denemelerde 7–8B modellerin kuantize sürümleri idealdir.

- Eşzamanlılık: OLLAMA_NUM_PARALLEL ve OLLAMA_BATCH gibi değişkenlerle paralellik ve batch boyutunu ayarlayabilirsiniz. Makinenizin sınırlarını test ederek en iyi kombinasyonu bulun.

- Disk I/O: Modeller büyük dosyalardır; SSD kullanımı ve yeterli boş alan önemlidir.

Open WebUI Özelliklerine Kısa Bakış

Open WebUI, konuşma geçmişi, istem (prompt) şablonları, çoklu model seçimi, rol tanımları ve RAG için belge yükleme gibi pratik özellikler sunar. Takım içinde kullanacaksanız, kullanıcı oturumları ve rol bazlı ayarlarla basit bir “self-hosted” sohbet platformu oluşturabilirsiniz.

Güvenlik ve Ağ

Bu kurulumu internetten erişilebilir hale getirecekseniz, ters proxy arkasında HTTPS zorunludur. Open WebUI’de dahili oturum açma mekanizmasını etkin tutun. Ollama API’sini (11434) doğrudan dış dünyaya açmayın; sadece iç ağda veya reverse proxy üzerinden eriştirin.

Sorun Giderme

- CUDA hatası: Sürücü ve nvidia-container-toolkit sürüm uyumsuz olabilir. nvidia-smi çıktısını kontrol edin, Docker daemon’ı yeniden başlatın.

- Model çekilemiyor: Ağ veya DNS sorunları; tekrar deneyin, mümkünse farklı bir ağa geçin.

- Open WebUI, Ollama’ya bağlanamıyor: OLLAMA_BASE_URL’in doğru olduğundan emin olun. Compose içinde servis adları (ollama) DNS ile çözülür; dışarıdan erişiliyorsa host IP’sini kullanın.

Sonuç

Docker ile Ollama + Open WebUI kurmak, yerel LLM deneyimini dakikalar içinde erişilebilir kılıyor. GPU hızlandırma ile yanıt süresi ve akıcılık ciddi ölçüde iyileşiyor. Küçük bir masaüstünde bile kuantize modellerle harika sonuçlar alabilir, belgelerinizi yerelde işleyebilir ve verinizi bulut servislerine göndermeden üretkenlik sağlayabilirsiniz. İhtiyacınıza göre 7–8B modellerle başlayıp, daha güçlü bir GPU’ya geçtiğinizde 13B/70B sınıfına doğru kademeli büyüyebilirsiniz.

11 Aralık 2025 Perşembe

Next.js ile WebAuthn (Passkey) Entegrasyonu: Parolasız Giriş Adım Adım

Parolasız kimlik doğrulama, kullanıcı deneyimini iyileştirirken hesap güvenliğini artırmanın en pratik yolu haline geldi. WebAuthn ve Passkey teknolojileri sayesinde kullanıcılar artık SMS kodları veya karmaşık parolalar yerine cihazlarındaki biyometrik sensörlerle giriş yapabiliyor. Bu yazıda, Next.js üzerinde WebAuthn tabanlı Passkey entegrasyonunu adım adım anlatarak, hem teknik detayları hem de üretim ortamı için ipuçlarını paylaşacağım.

Kısaca özetlemek gerekirse, WebAuthn FIDO2 standardının bir parçasıdır ve tarayıcılar aracılığıyla donanımsal (Güvenli Anahtarlar, Touch ID, Windows Hello vb.) veya platform tabanlı kimlik doğrulayıcılarla anahtar çifti üretip doğrulamayı mümkün kılar. RP (Relying Party) ID, origin ve challenge mekanizmaları doğru kurgulandığında, hem phishing dirençli hem de kullanıcı dostu bir giriş deneyimi elde edersiniz.

Önkoşullar ve Mimari

Gerekenler: Node.js 18+, Next.js 13+ (App Router tercihen), HTTPS (yerelde self-signed veya localhost), ve bir sunucu tarafı doğrulama paketi. Toplulukta yaygın kullanılan kütüphanelerden biri @simplewebauthn. Akış şu şekilde işler: Sunucu, kayıt veya giriş için tek kullanımlık bir challenge üretir ve istemciye döner. İstemci, tarayıcıdaki navigator.credentials API’sini (veya sarmalayıcı kütüphane) çağırarak kimlik doğrulayıcıyla işlem yapar ve imzalı yanıtı sunucuya gönderir. Sunucu, public key ve imza doğrulamasıyla işlemi sonuçlandırır.

Kurulum

Yeni bir Next.js projesi oluşturarak başlayalım:

npx create-next-app@latest passkey-demo --typescript
cd passkey-demo
npm i @simplewebauthn/server @simplewebauthn/browser cookie uuid

Basit bir yapı için .env dosyanıza aşağıdaki değerleri ekleyin (yerel geliştirme için):

RP_ID=localhost
RP_NAME=Passkey Demo
ORIGIN=http://localhost:3000

Kayıt (Registration) Akışı

Kullanıcı bir Passkey oluşturmak istediğinde önce sunucudan registration options alır. Bu seçenekler, kimlik doğrulayıcıya hangi parametrelerle anahtar üretmesi gerektiğini söyler. Next.js App Router kullandığınızı varsayarak endpoint’leri şu şekilde düşünebilirsiniz:

// app/api/webauthn/registration/options/route.ts
import { generateRegistrationOptions } from '@simplewebauthn/server';
import { cookies } from 'next/headers';
import { v4 as uuid } from 'uuid';

export async function GET() {
  const userId = uuid(); // Demo için rastgele. Gerçekte gerçek kullanıcı ID'si olmalı.
  const opts = await generateRegistrationOptions({
    rpName: process.env.RP_NAME!,
    rpID: process.env.RP_ID!,
    userID: userId,
    userName: '[email protected]',
    attestationType: 'none',
    authenticatorSelection: {
      residentKey: 'preferred',
      userVerification: 'preferred',
    },
  });

  // Challenge'ı oturumda saklayın
  cookies().set('regChallenge', opts.challenge, { httpOnly: true });

  return new Response(JSON.stringify({ userId, options: opts }), { status: 200 });
}

İstemci tarafında, tarayıcı API’si ile uğraşmayı kolaylaştırmak için @simplewebauthn/browser paketini kullanabiliriz:

// app/register/page.tsx
'use client';
import { startRegistration } from '@simplewebauthn/browser';

export default function Register() {
  const onRegister = async () => {
    const res = await fetch('/api/webauthn/registration/options');
    const { userId, options } = await res.json();
    const attRes = await startRegistration(options);
    await fetch('/api/webauthn/registration/verify', {
      method: 'POST',
      body: JSON.stringify({ userId, attRes }),
    });
    alert('Passkey oluşturuldu!');
  };
  return <button onClick={onRegister}>Passkey Oluştur</button>;
}

Sunucu, istemciden gelen yanıtı doğrulamalıdır. Doğrulama sonunda public key’i ve ilgili metadata’yı kullanıcı profiline kalıcı olarak kaydedersiniz.

// app/api/webauthn/registration/verify/route.ts
import { verifyRegistrationResponse } from '@simplewebauthn/server';
import { cookies } from 'next/headers';

export async function POST(req: Request) {
  const { userId, attRes } = await req.json();
  const expectedChallenge = cookies().get('regChallenge')?.value;

  const verification = await verifyRegistrationResponse({
    response: attRes,
    expectedChallenge,
    expectedOrigin: process.env.ORIGIN!,
    expectedRPID: process.env.RP_ID!,
  });

  if (!verification.verified) return new Response('Doğrulanamadı', { status: 400 });

  // verification.registrationInfo'dan publicKey ve credentialID'yi alıp DB'ye kaydedin
  return new Response('OK', { status: 200 });
}

Giriş (Authentication) Akışı

Girişte benzer şekilde bir challenge üretip istemciye gönderirsiniz. Kullanıcı cihazındaki Passkey ile imzalanan yanıtı sunucu doğrular. Burada kullanıcıya ait kayıtlı credentialID’lerinizi ve public key’lerinizi DB’den çekmeniz gerekir.

// app/api/webauthn/authentication/options/route.ts
import { generateAuthenticationOptions } from '@simplewebauthn/server';
import { cookies } from 'next/headers';

export async function GET() {
  const opts = await generateAuthenticationOptions({
    rpID: process.env.RP_ID!,
    userVerification: 'preferred',
  });
  cookies().set('authChallenge', opts.challenge, { httpOnly: true });
  return new Response(JSON.stringify({ options: opts }), { status: 200 });
}
// app/login/page.tsx
'use client';
import { startAuthentication } from '@simplewebauthn/browser';

export default function Login() {
  const onLogin = async () => {
    const res = await fetch('/api/webauthn/authentication/options');
    const { options } = await res.json();
    const assertion = await startAuthentication(options);
    await fetch('/api/webauthn/authentication/verify', {
      method: 'POST',
      body: JSON.stringify({ assertion }),
    });
    alert('Giriş başarılı!');
  };
  return <button onClick={onLogin}>Passkey ile Giriş</button>;
}
// app/api/webauthn/authentication/verify/route.ts
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import { cookies } from 'next/headers';

// Not: Burada kullanıcıya ait credential'ları DB'den çekmelisiniz.
export async function POST(req: Request) {
  const { assertion } = await req.json();
  const expectedChallenge = cookies().get('authChallenge')?.value;

  const verification = await verifyAuthenticationResponse({
    response: assertion,
    expectedChallenge,
    expectedOrigin: process.env.ORIGIN!,
    expectedRPID: process.env.RP_ID!,
    authenticator: {
      credentialID: Buffer.from('...'), // DB'den
      credentialPublicKey: Buffer.from('...'), // DB'den
      counter: 0,
    },
  });

  if (!verification.verified) return new Response('Hatalı kimlik doğrulama', { status: 401 });
  return new Response('OK', { status: 200 });
}

Üretime Hazırlık: İpuçları ve En İyi Uygulamalar

- HTTPS zorunlu: WebAuthn, localhost harici kökenlerde güvenli bağlam ister. Prod ortamda gerçek sertifika kullanın.

- RP ID ve Origin uyumu: RP_ID alan adınızla, ORIGIN ise protokol ve host’la birebir eşleşmeli. Alt alan adları ve çoklu çevrelerde yanlış yapılandırma en yaygın hatadır.

- Challenge ve oturum: Challenge değerini kısa ömürlü ve kullanıcıya bağlı saklayın. CSRF ve yeniden oynatma saldırılarına karşı dikkatli olun.

- Kullanıcı deneyimi: Platform authenticator (cihaz içi) ve cross-platform (USB/NFC) anahtarları desteklediğinizden emin olun. Cihaz değişimlerinde çoklu Passkey veya hesap kurtarma akışları tasarlayın.

- Günlükleme ve ölçüm: Başarısız doğrulama nedenlerini (origin uyuşmuyor, RP ID yanlış, kullanıcı doğrulaması başarısız vb.) log’layın; destek taleplerini azaltır.

Sonuç

WebAuthn ve Passkey ile parolasız kimlik doğrulama, hem saldırı yüzeyini küçültür hem de kullanıcı memnuniyetini artırır. Next.js üzerinde @simplewebauthn ile kurulum oldukça hızlıdır: kayıt ve giriş için challenge kurgusu, doğru RP/Origin ayarı ve güvenli oturum yönetimi yapı taşlarını oluşturur. Bu mimariyi uygulayarak uygulamanızda modern, phishing dirençli ve yüksek dönüşüm oranına sahip bir giriş deneyimine geçiş yapabilirsiniz.

10 Aralık 2025 Çarşamba

Ollama, LangChain ve ChromaDB ile Yerel RAG Chatbotu Kurulum Rehberi

Özet

Bu rehberde, internet bağlantısına ihtiyaç duymadan çalışan bir RAG (Retrieval-Augmented Generation) chatbotunu yerelde nasıl kurabileceğinizi adım adım anlatıyorum. Kullandığımız bileşenler: Ollama (yerel LLM çalıştırma), LangChain (zincirleme ve orkestrasyon), ChromaDB (vektör veritabanı) ve yerel gömlemeler. Amaç; PDF, teknik doküman veya notlarınızı indeksleyip, LLM modeline belgelerden alıntı yaparak daha doğru yanıtlar ürettirmek.

Neden Yerel RAG?

RAG yaklaşımı, dil modelinin yanıtlarını belgelerinizle zenginleştirerek halüsinasyonları azaltır ve güncel bilgi sağlar. İşin “yerel” tarafı ise verinin bilgisayarınızdan çıkmaması, maliyetin düşük ve sistemin offline çalışabilmesi demek. Özellikle kurumsal dokümanlar, Ar-Ge notları veya gizli veriler için bu yaklaşım idealdir.

Gereksinimler

- 16 GB RAM önerilir (8 GB ile küçük modeller çalışabilir).
- macOS, Linux veya Windows (WSL destekli).
- Python 3.10+.
- Temel terminal bilgisi.
- Bazı modellerde GPU varsa hız artar (Metal/CUDA desteği).

Adım 1: Ollama ve Modelleri Kurma

Ollama, LLM modellerini tek komutla indirip yerelde çalıştırmanızı sağlar. Sisteminizde kurulu değilse resmi talimatları izleyerek kurun. Linux için tipik kurulum şu şekildedir:

curl -fsSL https://ollama.com/install.sh | sh
# Servisi başlat (Linux)
ollama serve

Şimdi bir sohbet modeli ve bir gömleme modeli çekelim. Küçük ve hızlı bir sohbet modeli için Llama 3.1 8B iyi bir başlangıçtır. Gömlemeler için “nomic-embed-text” yaygın ve hızlıdır:

ollama pull llama3.1:8b
ollama pull nomic-embed-text

Model adlarını ihtiyacınıza göre değiştirebilirsiniz (örn. mistral veya phi-3). Büyük modeller daha iyi kalite, daha fazla RAM/GPU kullanımı demektir. İlk denemelerde 7B–8B aralığı pratik bir seçimdir.

Adım 2: Python Ortamını Hazırlama

Proje klasörünüzü oluşturup sanal ortam açın ve gerekli paketleri yükleyin. LangChain ile Ollama’nın entegrasyonu için “langchain-ollama” paketini kullanalım. Vektör veritabanı için Chroma’yı ekleyelim.

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install --upgrade pip
pip install langchain langchain-community langchain-ollama chromadb pydantic

Basit metin dosyalarıyla başlayacağız. PDF’ler için ekstra dönüştürücüler (örn. pypdf, unstructured) ekleyebilirsiniz.

Adım 3: Belgeleri İndeksleme ve RAG Zinciri

Aşağıdaki örnekte, “docs/” klasöründeki metin dosyalarını parçalara ayırıp Chroma’ya yükleyeceğiz. Ardından bir sorgu geldiğinde ilgili parçaları geri çağırıp modelin bağlamıyla birlikte yanıtlamasını sağlayacağız.

# rag_app.py
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain.prompts import PromptTemplate
from langchain.schema import Document

# 1) Belgeleri yükle
loader = DirectoryLoader("docs", glob="**/*.txt", loader_cls=TextLoader, show_progress=True, use_multithreading=True)
docs = loader.load()

# 2) Parçala
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=120)
chunks = splitter.split_documents(docs)

# 3) Gömleme ve vektör veritabanı
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectordb = Chroma.from_documents(chunks, embedding=embeddings, collection_name="yerel_rag")

# 4) Retriever ve LLM
retriever = vectordb.as_retriever(search_kwargs={"k": 4})
llm = ChatOllama(model="llama3.1:8b", temperature=0.2)

# 5) İstem şablonu
template = """Aşağıdaki bağlamı KESİNLİKLE dikkate alarak soruyu yanıtla.
Bağlam:
{context}

Soru: {question}
Cevap: """
prompt = PromptTemplate.from_template(template)

def ask(question: str):
    # İlgili parçaları getir
    rel_docs = retriever.get_relevant_documents(question)
    context = "\n\n".join([d.page_content for d in rel_docs])
    # İstemi oluştur ve yanıtı al
    final_prompt = prompt.format(context=context, question=question)
    return llm.invoke(final_prompt).content

if __name__ == "__main__":
    print(ask("Bu belgelerde performans optimizasyonu için öneriler neler?"))

Uygulamayı çalıştırmadan önce “docs/” klasörüne örnek metinler koymayı unutmayın. Eğer PDF kullanacaksanız “pypdf” ile yükleyebilir veya metne dönüştürüp aynı adımları uygulayabilirsiniz.

Performans ve Kalite İpuçları

Gömleme modeli seçimi: “nomic-embed-text” hızlı ve yeterli doğruluk sağlıyor. Çok dilli dokümanlarda “bge-m3” gibi çok dilli gömlemelere bakabilirsiniz.

Parça boyutu ve örtüşme: chunk_size=600–1200, chunk_overlap=80–200 aralığı pratik. Metin türüne göre ayarlayın. Uzun form teknik belgelerde daha büyük parçalar, soru-cevap notlarında daha küçük parçalar işe yarar.

Arama türü: Chroma varsayılan kosinüs benzerliği ile iyi çalışır. Farklı “k” değerlerini deneyin. Çok sayıda belge olduğunda metadata filtreleri eklemek doğruluğu artırır.

İstem mühendisliği: Şablona “Cevabın sonunda kaynak alıntısı ver” gibi kurallar eklemek kullanıcı güvenini artırır. Yanıt formatını (madde işaretleri, kısa özet + detaylar) açıkça belirtin.

Model ve nicemleme: Ollama üzerinde “:q4_0” gibi nicemlenmiş sürümler daha az RAM kullanır ve hızlanır; kalite az da olsa düşebilir. GPU mevcutsa GPU kullanımını etkinleştirmek büyük fark yaratır.

Güvenlik ve Gizlilik

Yerel RAG’in en önemli avantajı verilerin cihazınızda kalmasıdır. Ancak indeks dizininizi şifreli disk üzerinde tutmak, yedekleri güvenli almak ve işletim sistemi düzeyinde erişim izinlerini doğru ayarlamak gerekir. Paylaşılan makinelerde servis portlarını dışa açmayın.

Sorun Giderme

- “Connection refused”: Ollama servisi çalışmıyor olabilir. “ollama serve” komutunu kontrol edin.
- “CUDA/Metal hataları”: Sürücü/güncelleme gerekli olabilir. Geçici olarak CPU ile deneyin.
- “Bellek yetersiz”: Daha küçük model deneyin (ör. 3B–7B) veya nicemlenmiş varyant çekin.
- “Cevaplar ilgisiz”: chunk_size ve k değerlerini ayarlayın, gömleme modelini değiştirin, istemi netleştirin.

Sonuç

Bu kurulumla, Ollama + LangChain + Chroma üçlüsü sayesinde tamamen yerel çalışan bir RAG chatbotu elde ettiniz. Küçük boyutlu bir modelle bile belgeye dayalı arama ve yanıt üretiminde tatmin edici sonuçlar alabilirsiniz. Zamanla daha iyi gömlemeler, gelişmiş istem şablonları ve daha hızlı modellerle doğruluğu ve hızı artırabilirsiniz. Sonraki adım olarak bir REST API ekleyip (ör. FastAPI) web arayüzü veya Slack/Teams entegrasyonu kurmayı düşünebilirsiniz.

9 Aralık 2025 Salı

İnca Gaming Klavye Fabrika Ayarlarına Döndürme

 Bir süredir klavyemin tüm ışıkları gitmişti ve nasıl geri getirebileceğimi merak ediyordum. FN + ESC tuş kombinasyonuna birkaç saniye basılı tuttuktan sonra ışıklar yeniden açılmaya başladı ve renk modlarını istediğim gibi değiştirmeye başladım. Bu harika bir haber benim için, teşekkürler :)