WEB / WEB-17.04
WebSocket, SSE ve webhooks
Açık kanal ve doğrulanmış event hangi permission ve iş sonucunu henüz kanıtlamaz?
1. Core · Mekanizma
Hareket olmadan bütün senaryoları oku
İzinli message
- Message hazır
Browser, kurulmuş channel üzerinden book-read action’ı ve book-A nesnesi için mesajını hazırlar. API’nin session context’i reader-A/tenant-A olarak önceden tanımlıdır; bu mesaj henüz API’ye ulaşmamıştır.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Channel hazır; message yok
- Book service
- Request yok; effect yok
- Event provider
- Bu akışta kullanılmaz
- Message geldi
book-read/book-A mesajı API’ye ulaştı. API, mesajdaki nesne seçimini kendi reader-A/tenant-A session context’iyle değerlendirir; object tenant ve read izni henüz karara bağlanmamıştır.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Message geldi; policy bekliyor
- Book service
- Request yok; effect yok
- Event provider
- Bu akışta kullanılmaz
- Local message gate
Güncel session geçerlidir ve book-A tenant-A’ya aittir. reader-A için book-read izni verilir; API, subject/object/action context’iyle service read request’ini hazırlar.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Message allowed; service request hazır
- Book service
- Request yok; effect yok
- Event provider
- Bu akışta kullanılmaz
- Service request geldi
Book service read request’ini teslim alır; local policy ve lookup henüz değerlendirilmemiştir.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Message allowed; service request hazır
- Book service
- Read request geldi; lookup bekliyor
- Event provider
- Bu akışta kullanılmaz
- Local service read
Service, verilen read context’ini kendi policy’siyle kabul eder; book-A için Atlas Notları başlığını yerel lookup ile okur ve response’u hazırlar. Henüz API bu response’u almamıştır.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Message allowed; service request hazır
- Book service
- book-A title hazır; effect yok
- Event provider
- Bu akışta kullanılmaz
- Service result geldi
Service response API’ye ulaştı; browser message’ı henüz hazırlanmadı veya gönderilmedi.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Service result geldi; mapping bekliyor
- Book service
- book-A title hazır; effect yok
- Event provider
- Bu akışta kullanılmaz
- Local channel mapping
API result schema’sı ile correlation’ı kontrol eder; Browser’a iletilecek title message’ı hazırlar.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Browser title message hazır
- Book service
- book-A title hazır; effect yok
- Event provider
- Bu akışta kullanılmaz
- Title message geldi
Atlas Notları başlığını taşıyan WebSocket message Browser’a ulaştı. Başlık artık Browser’da bilinir; application message’ın alınması DOM’a uygulandığını veya handshake/frame parsing’in burada çalıştığını kanıtlamaz.
- Browser
- Başlık alındı: Atlas Notları; DOM ayrı
- Atlas API
- Browser title message hazır
- Book service
- book-A title hazır; effect yok
- Event provider
- Bu akışta kullanılmaz
Message reddi
- Normal read hazır
Browser, kurulmuş channel üzerinden book-read action’ı ve book-A nesnesi için mesajını hazırlar. API’nin session context’i reader-A/tenant-A olarak önceden tanımlıdır; bu mesaj henüz API’ye ulaşmamıştır.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Channel hazır; message yok
- Book service
- Request yok; effect yok
- Event provider
- Bu akışta kullanılmaz
- Message geldi
book-read/book-A mesajı API’ye ulaştı. API, mesajdaki nesne seçimini kendi reader-A/tenant-A session context’iyle değerlendirir; object tenant ve read izni henüz karara bağlanmamıştır.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Message geldi; policy bekliyor
- Book service
- Request yok; effect yok
- Event provider
- Bu akışta kullanılmaz
- Local deny
Session geçerli olsa da reader-A tenant-A içindedir, book-A ise bu dalda tenant-B’ye aittir. Object read reddedilir; service’e hiçbir request hazırlanmaz ve yalnız denial message hazırlanır.
- Browser
- book-read / book-A hazır; title yok
- Atlas API
- Policy reddi; denial hazır
- Book service
- Request yok; effect yok
- Event provider
- Bu akışta kullanılmaz
- Denial geldi
Denial response Browser’a ulaşır; service’e request çıkmamış ve Browser’a resource title verilmemiştir.
- Browser
- Denial alındı; title yok
- Atlas API
- Policy reddi; denial hazır
- Book service
- Request yok; effect yok
- Event provider
- Bu akışta kullanılmaz
SSE duplicate
- Resume sonrası
Örnekte reconnect/history seçimi tamamlanmıştır; API event-2 block’unu hazırlar, Browser henüz almaz.
- Browser
- lastID event-1; apply=0
- Atlas API
- SSE established; event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
- Block geldi
Tam SSE text block Browser’a ulaşır; parse ve dispatch henüz gerçekleşmediğinden app apply count değişmez.
- Browser
- Block geldi; parse bekliyor; apply=0
- Atlas API
- SSE established; event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
- Local event parse
Örneğin block parser’ı blank line sonrasında event-2/data/retry alanlarını okur; app apply henüz yapılmamıştır.
- Browser
- lastID event-2; event hazır; apply=0
- Atlas API
- SSE established; event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
- Local apply
Application seen-ID set’ine event-2 ekler ve title’ı bir kez uygular; bu durable transaction kanıtı değildir.
- Browser
- event-2 seen; apply=1
- Atlas API
- SSE established; event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
- Normal duplicate hazır
Delivery history aynı event-2 block’unu tekrar hazırlar; ilk delivery ikinci arrival’ı engelleyen exactly-once garantisi değildir.
- Browser
- event-2 seen; apply=1
- Atlas API
- Duplicate event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
- Duplicate block geldi
İkinci block Browser’a ulaşır; ilk title apply korunur ve duplicate için dedupe henüz değerlendirilmemiştir.
- Browser
- Duplicate block geldi; apply=1; bekliyor
- Atlas API
- Duplicate event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
- Local parse tekrar
Parser tekrar event-2 üretir; aynı last ID business acknowledgement veya unique event garantisi sayılmaz.
- Browser
- event-2 tekrar parse edildi; apply=1
- Atlas API
- Duplicate event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
- Local dedupe
App seen-ID set’i duplicate’i tanır ve ikinci apply yapmaz; bu sabit örneğin final apply count değeri 1 kalır.
- Browser
- Duplicate atlandı; apply=1
- Atlas API
- Duplicate event-2 block hazır
- Book service
- Bu akışta kullanılmaz
- Event provider
- Bu akışta kullanılmaz
Webhook duplicate
- Receipt hazır
Provider event-W delivery’sini hazırlar; receiver bu event’i daha önce processed sayar, yeni receipt henüz gelmemiştir.
- Browser
- Bu akışta kullanılmaz
- Atlas API
- event-W processed; effect=1; receipt yok
- Book service
- Yeni job yok; effect=1
- Event provider
- event-W delivery hazır
- Webhook geldi
HTTP event receipt API’ye ulaşır; exact-byte integrity ve delivery freshness koşulları henüz değerlendirilmemiştir.
- Browser
- Bu akışta kullanılmaz
- Atlas API
- Raw receipt geldi; verification bekliyor
- Book service
- Yeni job yok; effect=1
- Event provider
- event-W delivery hazır
- Local receipt gate
Bu örnekte güvenilen endpoint key’i ve tam raw body üzerindeki signature sonucu olumlu verilmiştir. now=1000 ile signedTime=970 arasındaki 30 birim, tolerance=60 içindedir; duplicate kararı henüz değerlendirilmez.
- Browser
- Bu akışta kullanılmaz
- Atlas API
- Receipt koşulu olumlu; dedupe bekliyor
- Book service
- Yeni job yok; effect=1
- Event provider
- event-W delivery hazır
- Local idempotency
Ledger consumer-A/event-W anahtarını zaten processed bulur; ikinci iş açılmaz, effect count 1 kalır ve 200 receipt hazırlanır.
- Browser
- Bu akışta kullanılmaz
- Atlas API
- Duplicate kabul edildi; effect=1; 200 hazır
- Book service
- Yeni job yok; effect=1
- Event provider
- event-W delivery hazır
- Receipt ack geldi
Provider receiver’ın 200 receipt’ini alır; bu sonuç yeni business job completion veya Browser update değildir.
- Browser
- Bu akışta kullanılmaz
- Atlas API
- Duplicate kabul edildi; effect=1; 200 hazır
- Book service
- Yeni job yok; effect=1
- Event provider
- 200 receipt alındı; effect sonucu bilinmez
ROLLER · STATE · VERİ AKIŞI
WebSocket message, frame ve permission
WebSocket uzun ömürlü, iki yönlü bir message kanalıdır; Browser ile server birbirinden bağımsız application message gönderebilir. HTTP/1.1 profile opening handshake’ı GET/Upgrade ve başarılı 101 ile kurar. HTTP/2’de RFC 8441 desteği negotiate edilmişse extended CONNECT ve :protocol=websocket kullanılır; Connection/Upgrade header’ları ve H1’in 101 cevabı buraya kopyalanmaz. Bir H2 stream WebSocket taşırken aynı connection’daki diğer stream’ler HTTP işine devam edebilir. Her Browser/proxy kombinasyonunun bu profile’ı desteklediği varsayılmaz.
Bir text message bir veya daha fazla WebSocket frame’e bölünebilir; frame sınırı TCP segment veya application JSON field sınırı değildir. Masking payload confidentiality sağlamaz; wss ve doğru TLS peer kontrolü ayrı konudur. Atlas örneği authenticated connection ile başlar, fakat book-read message geldiğinde subject/object/action policy yeniden değerlendirilir. İzinli mesaj API’den book service’e read request üretir; izin reddinde service’e request çıkmaz. Sahne handshake yerine kurulmuş kanalın message kararını izler.
SSE: event almak, işi onaylamak değildir
Server-Sent Events HTTP response body’sinde server’dan Browser’a text/event-stream taşır; EventSource aynı stream’de Browser→server application message göndermez. UTF-8 satırlarda data event payload’ını, event listener seçimini, id reconnect bağlamını, sayısal retry ise yeniden bağlanma zamanını etkiler. Blank line birikmiş data’nın dispatch edilmesini tetikler; yalnız TCP chunk gelmesi tam event oluştuğunu göstermez. Örnekteki block id:event-2 ve book title datası içerir; parser line buffer’ı ile UI state ayrı tutulur.
EventSource reconnect sırasında nonempty last event ID string’ini Last-Event-ID header’ıyla bildirir. Server’ın history’den ne kadarını tekrar sunacağı application contract’ıdır. Event ID almak business transaction commit’ini kanıtlamaz; duplicate event için app dedupe gerekir. Retry sonrası aynı event-2 gelebilir; örnekte UI apply count 1 kalır. History süresi dolduysa full snapshot refresh gibi recovery gerekir. Sahne reconnect/history seçimi tamamlandıktan sonra başlar; gerçek EventSource ve retry timer davranışını göstermez.
Webhook: provider’dan receiver’a ayrı request
Webhook Browser’da açılan stream değildir; provider server event’i receiver’a ayrı HTTP request olarak yollar. Receipt response’unun 2xx olması event’in kabul edildiğini bildirir, downstream business işinin bittiğini otomatik söylemez. Stripe provider profile duplicate delivery ve sıra dışı arrival ihtimalini açıklar. Atlas örneği generic book.updated event alias’ı kullanır; gerçek Stripe event veya account verisi içermez. Browser’ın API’den book okumasıyla provider’ın webhook teslimi direction, authentication ve lifetime bakımından farklıdır.
Receipt integrity için provider profile’ın exact raw bytes doğrulaması ve trusted endpoint key provenance’ı gerekir. Parse edilip yeniden serialize edilen eşdeğer JSON aynı bytes değildir. Signed delivery timestamp freshness kontrolü event created zamanından ayrıdır; retry yeni delivery timestamp taşıyabilir. Valid receipt yine duplicate olabilir. Idempotency ledger consumer-A/event-W anahtarını processed sayar ve ikinci business effect üretmeden receipt’i kabul eder. Crypto sonucu başlangıç koşulu olarak verilmiştir; bu ledger signature verification sonucu değildir.
2. Red · Güven ve etki
Hareket olmadan bütün senaryoları oku
Message yetkisi
- Channel ve message context ayrıldı
Channel kurulmuş ve session valid olarak tanımlıdır. policy-denied message row’unda reader-A tenant-A içindedir ama istenen object tenant-B’ye aittir.
- Channel/body kaydı
- Channel established
- Resource/verification gate
- Object tenant-B
- Delivery/effect sınırı
- Service call yok
- Action ve object sınırı seçildi
Action book-read’tir. Message’ın doğru shape’te olması veya açık connection’dan gelmesi foreign-tenant object için permission vermez.
- Channel/body kaydı
- Action: book-read
- Resource/verification gate
- Subject tenant-A ≠ object tenant-B
- Delivery/effect sınırı
- Policy bekliyor
- Per-message policy reddi belirlendi
Row expected=false bildirir. Gate object/action context’ini kontrol eder; handshake’in kabul edilmiş olması bu ret kararını allow’a dönüştürmez.
- Channel/body kaydı
- Session valid=true
- Resource/verification gate
- Object permission=false
- Delivery/effect sınırı
- Service call withheld
- Downstream transfer üretilmedi
Foreign object için book data hazırlanmaz ve service’e read dispatch yapılmaz. Denial sonrası service’in unchanged kalması bu branch’in somut sınırıdır.
- Channel/body kaydı
- Message received in ledger
- Resource/verification gate
- Policy deny known
- Delivery/effect sınırı
- No downstream read
- Connection lifetime izinle eşitlenmedi
Long-lived channel session koşullarının değişmesini engellemez. Bu row bilinen tenant mismatch’ini gösterir; logout, expiry veya heartbeat sonucu ayrıca değerlendirilir.
- Channel/body kaydı
- Tenant mismatch known
- Resource/verification gate
- Lifetime policy separate
- Delivery/effect sınırı
- No object body
- Handshake protokolü ayrı bırakıldı
HTTP/1.1 Upgrade ile negotiated HTTP/2 extended CONNECT farklı bootstrap profilleridir. Bu application permission row’u frame parsing veya bu bootstrap’ların çalıştığını kanıtlamaz.
- Channel/body kaydı
- Application deny known
- Resource/verification gate
- Bootstrap profile separate
- Delivery/effect sınırı
- No broader protocol claim
Signed byte bağı
- Webhook body iki biçimde okundu
rawBody ile reserializedBody aynı eventId, type ve bookId değerlerini taşıyabilir. Field sırası ve whitespace değiştiği için byte representation’ları aynı değildir.
- Channel/body kaydı
- Event: event-W
- Resource/verification gate
- Two byte representations
- Delivery/effect sınırı
- Verification bekliyor
- Verification girdisi sınırlandı
Exact byte verification signed body’ye bağlıdır. JSON değerlerini okuyup yeniden serialize etmek, imzalanan byte dizisini koruduğu varsayımıyla kullanılmaz.
- Channel/body kaydı
- Values may match
- Resource/verification gate
- Bytes differ
- Delivery/effect sınırı
- Raw body gerekli
- Key provenance ayrı kontrol sayıldı
untrusted-key row’unda signatureValidDeclared=true olsa bile keyProvenanceDeclared=false’tur. Matematiksel signature sonucu trusted endpoint key kaynağının yerine geçmez.
- Channel/body kaydı
- Signature declared=true
- Resource/verification gate
- Key provenance=false
- Delivery/effect sınırı
- Receipt pending
- Receipt gate reddi belirlendi
Bu row receiptAccepted=false ve newEffect=0 bildirir. Fresh signed time ve eşleşen bytes, güvenilmeyen key kaynağıyla birlikte acceptance üretmez.
- Channel/body kaydı
- Integrity condition declared
- Resource/verification gate
- Receipt accepted=false
- Delivery/effect sınırı
- New effect=0
- Signature uniqueness sayılmadı
Trusted normal signature bile aynı event’in daha önce işlenmediğini söylemez. Signature, freshness ve consumer/event dedupe üç ayrı karardır.
- Channel/body kaydı
- Signature ≠ uniqueness
- Resource/verification gate
- Freshness separate
- Delivery/effect sınırı
- Dedupe separate
- Kavram byte fixture’ında tutuldu
Burada iki inert string ve declared receipt tablosu karşılaştırılır. Gerçek SDK verification, key yönetimi veya provider delivery’si gözlenmiş sayılmaz.
- Channel/body kaydı
- Bytes comparison known
- Resource/verification gate
- Key denial known
- Delivery/effect sınırı
- No live delivery claim
VARSAYIM · SINIR · KONTROL
Established channel’ı sınırsız izin sanmak
WebSocket handshake kabulünden sonra her message’ı aynı yetkiyle geçirmek yanlış varsayımdır. Session’ın süresi dolabilir, object başka tenant’a ait olabilir veya action channel profile’da bulunmayabilir. Message schema’sı geçse bile resource kararı gerekir. Policy-denied akışı book-read message’ı teslim alır; object permission koşulu sağlanmadığında service call üretmez ve denial döndürür. Bu durum channel kabulüyle message authority’sinin neden ayrı tutulduğunu gösterir.
Service’in yalnız API connection’ına güvenmesi de aynı sorunu downstream’e taşır. API’den gelen request doğru subject/object/context’i taşımalı; service kendi policy’sini uygulamalıdır. Browser Origin header’ı handshake review’inde yararlıdır, fakat her client için signed identity veya business permission kanıtı değildir. WSS kullanımı bütün application işlerini onaylamaz. İncelemede mesajı hazırlayan taraf, trusted context’i kuran bileşen ve deny sonrasında gerçekleşmeyen transfer ayrı izlenir.
Teslim edildi ile bir kez işlendi ayrımı
SSE lastEventId bir client acknowledgement protokolü değildir; handler’ın UI’a uygulaması ile parser’ın ID güncellemesi farklı olaylardır. Webhook signature-valid sonucu da exactly-once delivery vaat etmez. Duplicate receipt ikinci business effect’e dönüşüyorsa signature idempotency yerine kullanılmış olabilir. Signature kontrolünden önce JSON normalize etmek başka bir sorundur: eşdeğer değer ağacı signed byte representation’ı değiştirebilir. Örneğin iki inert raw-body string’i arasındaki fark, değer eşitliği ile byte eşitliğinin ayrımını gösterir.
Receipt’leri event created timestamp sırasına dizmek business order garantisi vermez; ayrı event’ler aynı saniyeyi paylaşabilir. Freshness kabulü consumer’ın event ID’yi hiç görmediğini de söylemez. Duplicate retention window, durable queue ve transaction sınırı birbiriyle uyumlu olmalıdır. Bellekte processed alias’ın doğru çalışması crash sonrası veya concurrent worker’larda tek etki garantisi değildir. Artifact first, duplicate ve stale receipt kararlarını ayırır; delivery history ile business effect history ayrı tutulur.
3. Blue · Güvenli geliştirme ve savunma
Hareket olmadan bütün senaryoları oku
SSE duplicate
- Event stream alanları okundu
SSE block id=event-2, event=book, retry=1000 ve data=Atlas Notları içerir. retry değeri reconnect tercihi bildirir; kesin teslim süresi veya application ack değildir.
- Delivery observation
- id=event-2
- ID/time/consumer bağı
- retry=1000
- İnceleme sonucu
- Apply count bekliyor
- Teslim geçmişi kayda bağlandı
eventDeliveries içinde event-2 iki kez vardır. Bu önceden kurulmuş history, stream transport’ın exactly-once olduğu iddiasıyla okunmaz.
- Delivery observation
- Deliveries: event-2 × 2
- ID/time/consumer bağı
- Exactly-once=false
- İnceleme sonucu
- App dedupe inceleniyor
- Parser ID ve effect ayrıldı
Last-Event-ID state’i event’in işlenip UI’a uygulandığını tek başına kanıtlamaz. Seen-ID dedupe kararı ayrı application state’idir.
- Delivery observation
- Parser/event ID state
- ID/time/consumer bağı
- Application seen-ID state
- İnceleme sonucu
- Ack=false
- Beklenen apply sayısı eşleştirildi
expectedApplyCount=1 seçilen duplicate history için beklenen sonuçtur. İki delivery ile tek effect arasındaki fark application dedupe’dan gelir, stream’in duplicate üretmemesinden değil.
- Delivery observation
- Delivery count=2
- ID/time/consumer bağı
- Expected apply count=1
- İnceleme sonucu
- Dedupe boundary known
- Resume garantisi genişletilmedi
Bu history retained event’lerin nasıl bulunacağını bütünüyle göstermez. Expired cursor recovery ve server history retention için ayrı contract ve gözlem gerekir.
- Delivery observation
- Duplicate history known
- ID/time/consumer bağı
- Retention evidence separate
- İnceleme sonucu
- No universal resume guarantee
- İnceleme sonucu application’a bağlandı
İki ordinary event kaydını tek apply’a indiren policy incelenir. Gerçek browser reconnect, wall-clock delay veya durable effect garantisi bu tuple’dan çıkarılmaz.
- Delivery observation
- Expected apply=1
- ID/time/consumer bağı
- Transport not exactly-once
- İnceleme sonucu
- Runtime recovery unobserved
Receipt ve effect
- Signed time ve event zamanı ayrıldı
eventCreated=800, deliverySignedTime=970 ve now=1000’dır. Freshness bu örnekte delivery signature zamanına bağlanır; event oluşturma zamanı business order garantisi değildir.
- Delivery observation
- Signed time=970
- ID/time/consumer bağı
- now=1000; tolerance=60
- İnceleme sonucu
- Receipt review pending
- Freshness koşulu değerlendirildi
Normal signed time 30 birim önce olup seçilen 60 birim tolerance içindedir. Bu tolerance örneğin policy’sidir; provider’ın default değeri diye okunmaz.
- Delivery observation
- Age=30
- ID/time/consumer bağı
- Selected tolerance=60
- İnceleme sonucu
- Freshness acceptable
- İşlenmiş event bağı bulundu
initialProcessed içinde consumer-A/event-W vardır ve initialEffectCount=1’dir. Bu kayıt duplicate receipt’ten önce kurulmuş history’dir.
- Delivery observation
- Consumer/event already retained
- ID/time/consumer bağı
- Initial effect count=1
- İnceleme sonucu
- Duplicate branch selected
- Receipt kabulü yeni effect yaratmadı
duplicate row receiptAccepted=true ve newEffect=0 bildirir. duplicateReceiptStatus=200 olabilirken finalEffectCount=1 kalır; ack ile yeni business effect aynı şey değildir.
- Delivery observation
- Receipt accepted=true
- ID/time/consumer bağı
- New effect=0
- İnceleme sonucu
- Final effect count=1
- Stale receipt farklı nedenle ayrıldı
stale-declared row signedTime=900 ile now=1000 arasındaki 100 birim nedeniyle seçilen tolerance’ı aşar. Bu ret duplicate dedupe kararıyla karıştırılmaz.
- Delivery observation
- Duplicate: accepted, no effect
- ID/time/consumer bağı
- Stale: not accepted
- İnceleme sonucu
- Causes separate
- Durability ve concurrency bilinmez bırakıldı
Bu ledger receipt ve effect kararlarını ayırır. concurrencyExecuted=false olduğu için crash recovery veya paralel worker’larda tek effect garantisi çıkarılamaz.
- Delivery observation
- Receipt/effect split known
- ID/time/consumer bağı
- Concurrency unobserved
- İnceleme sonucu
- No durability guarantee
UYGULAMA · GÜVENLİK KONTROLÜ · DOĞRULAMA
Message sınırında karar ve lifetime
Channel profile message type, action, allowed object scope, size ve rate limit’i tanımlar. Server session/context’i trusted kaynaktan kurar, her message’ın object/action iznini değerlendirir ve protected data’yı ancak allow sonrasında üretir. Session expiry/logout sonrası channel invalidation davranışı belirlenir; yalnız handshake’te login kontrolü uzun connection’a yetmez. Proxy timeout, application heartbeat ve protocol ping/pong farklı liveness sinyalleridir; pong book-read permission kanıtı değildir.
API→service request gerekli minimum subject/resource context’ini taşır; service return shape’i ayrıca doğrulanır. Browser message schema ve correlation alias’ını eşleştirir; eski reply yeni read sonucu sayılmaz. Modelde local policy deny sonrasında service kartı değişmez: request çıkmaması kontrolün durduğu boundary’yi gösterir. Handshake, TLS, frame parsing ve flow control farklı katmanlardır; application state gözlemi bu protocol katmanlarının doğru çalıştığını tek başına kanıtlamaz.
Resume, receipt ve durable effect ledger
SSE contract retained history, Last-Event-ID yorumu, expired-cursor recovery ve app-level dedupe davranışını açıklar. Örnekte event-2 iki kez teslim edilse de UI apply count 1 kalır; parser ID state ile app seen-ID set farklıdır. Client close ve server’ın HTTP 204 ile reconnect’i durdurması recovery tasarımına girer. Sayısal retry alanı delivery guarantee değildir; Browser backoff ve network koşulları nedeniyle kesin wall-clock schedule sonucu çıkarılmaz.
Webhook receiver raw body’yi verification için korur, approved endpoint key’ini trusted configuration’dan seçer ve signed delivery time’ı tolerance policy’siyle değerlendirir. Artifact’teki 60 saniye eğitim için seçilmiştir; Stripe library’nin varsayılan 5 dakika değeriyle karıştırılmaz. Integrity/freshness sonrasında consumer/event key’i durable acceptance ve business effect ile aynı tutarlılık sınırına bağlanır; erken 2xx kayıp işi gizlememelidir. Duplicate receipt başarılı ack alabilir, fakat effect count değişmez. Crash recovery ve concurrent storage atomicity için kalıcı ledger ve commit davranışı ayrıca gerekir.
ŞİMDİ VAKAYA UYGULA
Açık channel veya accepted receipt hangi iş kararını kanıtlamaz?
WebSocket object policy, SSE event dedupe ve webhook receipt farklı gate’lerdir. Normal delivery history, signed bytes ve effect sayısı kendi bağlamıyla okunur.
Vakadaki API contract’ı ile browser’ın okuyabildiği representation’ı ayrı incele. Envelope, data, error ve resource permission için hangi gözlemin yeterli olduğunu tartış.
Bağlı vaka dosyasını aç ↗ÖRNEĞİ BİRLİKTE OKUYALIM
Atlas channel ve receipt karar tablosu
Bir alan seç; hangi sonucu desteklediğini ve nerede yetersiz kaldığını incele.
Ham kaydı ve alan açıklamalarını oku
{
"profile": {
"actualNetwork": false,
"actualProtocol": false,
"actualCrypto": false,
"channelEstablishedDeclared": true,
"sessionValidDeclared": true,
"serviceTransport": "ordinary owned read request",
"webhookProviderExample": "Stripe documentation only; generic Atlas aliases"
},
"channelComparison": [
{
"kind": "WebSocket",
"direction": "browser <-> API",
"lifetime": "established bidirectional message channel"
},
{
"kind": "SSE",
"direction": "API -> browser",
"lifetime": "HTTP event stream"
},
{
"kind": "Webhook",
"direction": "provider -> owned API receiver",
"lifetime": "separate HTTP deliveries"
}
],
"websocketProfiles": [
{
"http": "HTTP/1.1",
"method": "GET",
"bootstrap": "Upgrade",
"success": 101,
"stream": "connection"
},
{
"http": "HTTP/2 with RFC8441 negotiated support",
"method": "CONNECT",
"protocol": "websocket",
"success": 200,
"stream": "single H2 stream",
"connectionUpgradeHeaders": false
}
],
"messageLayers": {
"application": "one JSON book-read message",
"websocketFrames": "one or more; boundaries not fixed by app",
"tcpSegments": "byte stream segmentation independent",
"maskingIsEncryption": false
},
"messageRows": [
{
"case": "normal",
"subject": "reader-A",
"tenant": "tenant-A",
"objectTenant": "tenant-A",
"action": "book-read",
"sessionValid": true,
"expected": true
},
{
"case": "policy-denied",
"subject": "reader-A",
"tenant": "tenant-A",
"objectTenant": "tenant-B",
"action": "book-read",
"sessionValid": true,
"expected": false
}
],
"sse": {
"block": "id: event-2\nevent: book\nretry: 1000\ndata: Atlas Notları\n\n",
"initialLastId": "event-1",
"parsed": {
"id": "event-2",
"event": "book",
"retry": 1000,
"data": "Atlas Notları"
},
"eventDeliveries": [
"event-2",
"event-2"
],
"expectedApplyCount": 1,
"selectedHistoryEstablishedBeforeScene": true,
"exactlyOnceTransport": false,
"appAck": false
},
"webhook": {
"rawBody": "{\"eventId\":\"event-W\",\"type\":\"book.updated\",\"bookId\":\"book-A\"}",
"reserializedBody": "{\"bookId\": \"book-A\", \"eventId\": \"event-W\", \"type\": \"book.updated\"}",
"configuredKeyAlias": "trusted-endpoint-key-A",
"consumer": "consumer-A",
"eventId": "event-W",
"eventCreated": 800,
"deliverySignedTime": 970,
"now": 1000,
"tolerance": 60,
"toleranceIsProviderDefault": false,
"signatureValidDeclared": true,
"exactBytesVerifiedDeclared": true,
"keyProvenanceDeclared": true,
"initialProcessed": [
"consumer-A/event-W"
],
"initialEffectCount": 1,
"receiptRows": [
{
"case": "first",
"signedTime": 970,
"now": 1000,
"signatureValidDeclared": true,
"exactBytesVerifiedDeclared": true,
"keyProvenanceDeclared": true,
"previouslyProcessed": false,
"receiptAccepted": true,
"newEffect": 1
},
{
"case": "duplicate",
"signedTime": 970,
"now": 1000,
"signatureValidDeclared": true,
"exactBytesVerifiedDeclared": true,
"keyProvenanceDeclared": true,
"previouslyProcessed": true,
"receiptAccepted": true,
"newEffect": 0
},
{
"case": "stale-declared",
"signedTime": 900,
"now": 1000,
"signatureValidDeclared": true,
"exactBytesVerifiedDeclared": true,
"keyProvenanceDeclared": true,
"previouslyProcessed": false,
"receiptAccepted": false,
"newEffect": 0
},
{
"case": "untrusted-key",
"signedTime": 970,
"now": 1000,
"signatureValidDeclared": true,
"exactBytesVerifiedDeclared": true,
"keyProvenanceDeclared": false,
"previouslyProcessed": false,
"receiptAccepted": false,
"newEffect": 0
}
],
"finalEffectCount": 1,
"duplicateReceiptStatus": 200,
"concurrencyExecuted": false
},
"boundaries": {
"connectionGrantsObjectPermission": false,
"receiptEqualsBusinessCompletion": false,
"eventCreatedOrdersBusiness": false,
"signatureMeansUnique": false
}
}- channelComparison
- Üç farklı actor direction ve lifetime.
- websocketProfiles
- H1 Upgrade ile negotiated H2 extended CONNECT farklıdır.
- messageRows
- Established connection sonrası seçilmiş per-message resource gate.
- sse
- Normal event block, parser ID ve ayrı app apply count.
- webhook
- Raw bytes ve declared integrity/freshness/idempotency receipt ledger.
Inert event/read verisi ve başlangıç koşulu olarak verilen crypto sonucu kullanılır. Raw-body farkı ve gate kararları, gerçek channel/reconnect/SDK/signature/queue veya concurrent durable storage çalışması değildir.
İki yanlış kanı
Authenticated WebSocket her message’a izin verir.
Her action/object için trusted session/context policy gerekir; established channel yalnız taşıma durumudur.
Signed webhook ve SSE ID exactly-once iş sağlar.
Integrity ve resume bağlamı duplicate teslimi kaldırmaz; application idempotency ve durable effect sınırı ayrıdır.
SIRA SENDE
Bu hikâyeyi sen çöz.
Sonucu tahmin et, kanıtın sınırını bul ve nedenini kendi cümlenle anlat.
WebSocket sessionValid=true. Message içindeki object tenant-B’ye, subject tenant-A’ya ait. Message kabul edilir mi?
- Evet. Opening handshake sonraki bütün object’lere izin verir.
- Hayır. Message policy-denied olur.
Gerekçeli cevabı oku
Hayır. Message policy-denied olur. — Kurulmuş channel her message için object izni sağlamaz. Güncel tenant ve action koşulları ayrıca doğrulanır.
Duplicate receipt için accepted=true, fakat newEffect=0. Bu çelişki mi?
Dersten seçilen artifact alanları; öğretim örneği
- webhook.receiptRows[1]
- {"case":"duplicate","signedTime":970,"now":1000,"signatureValidDeclared":true,"exactBytesVerifiedDeclared":true,"keyProvenanceDeclared":true,"previouslyProcessed":true,"receiptAccepted":true,"newEffect":0}
- webhook.finalEffectCount
- 1
- Hayır. Duplicate kabul edilir; effect tekrar uygulanmaz.
- Evet. Kabul edilen her delivery yeni write gerektirir.
Gerekçeli cevabı oku
Hayır. Duplicate kabul edilir; effect tekrar uygulanmaz. — previouslyProcessed=true, consumer’ın event dedupe context’ini gösterir. Geçerli signature, event’in ilk kez geldiğini garanti etmez.
SSE retry/id, WebSocket frame’leri ve webhook’ın alınan body byte’larını doğrulaması hangi ayrı sınırları açıklar?
Gerekçeli cevabı oku
SSE’nin server→client stream’inde id, reconnect sırasında history ile ilişki kurmaya yardım eder; exactly-once veya application ACK sağlamaz. WebSocket message birden fazla frame’e bölünebilir; TCP segment sınırları ayrıdır ve masking encryption değildir. Webhook doğrulaması güvenilen endpoint key’iyle tam olarak alınan byte’lar üzerinden yapılır. Aynı JSON object’i yeniden serialize etmek aynı signed byte’ları üreteceğini garanti etmez.
□ SSE id’yi exactly-once garantisi saymadım.
□ WebSocket framing ile webhook byte doğrulamasını ayırdım.
Uygulama konsolunu aç · request / response / browser
Request, URL, cookie ve DOM örneklerini ayrıca aç
Kaynaklar ve kapsam
- RFC 6455 — The WebSocket Protocol§1.2 protocol overview; §1.3 opening handshake · §5.4 fragmentation; §5.3 masking
- RFC 8441 — Bootstrapping WebSockets with HTTP/2§3 SETTINGS_ENABLE_CONNECT_PROTOCOL; §4 extended CONNECT · §5 bootstrap differences; §5.1 example H2 200
- WHATWG HTML — Server-sent events§9.2.1 direction/UTF-8/204; §9.2.2 EventSource · §9.2.4 Last-Event-ID; §9.2.5 parsing; §9.2.6 interpreting
- Stripe — Webhooks provider documentationEvent delivery behaviors: Automatic retries; Event ordering · Handle duplicate events; Handle events asynchronously · Verify webhook signatures with official libraries: exact raw body/endpoint secret · Preventing replay attacks: signed delivery timestamp/tolerance/retry freshness; Quickly return 2xx
- OWASP — WebSocket Security Cheat SheetAuthentication and Authorization: Session Management; Message-Level Authorization · Input Validation: message shape and bounds