2 puan yazan GN⁺ 2026-01-15 | 1 yorum | WhatsApp'ta paylaş
  • GitHub API kullanılırken PR yorum bağlantısı oluşturma özelliğinde kimlik uyuşmazlığı nedeniyle bağlantıların çalışmadığı bir sorun ortaya çıktı
  • Araştırma sonucunda GitHub’ın GraphQL node ID ile REST API database ID olmak üzere iki ayrı ID sistemi kullandığı görüldü
  • node ID base64 ile decode edildiğinde, alt 32 bitte database ID’nin yer aldığı doğrulandı ve bunun basit bir bit maskesi işlemiyle dönüştürülebildiği anlaşıldı
  • Ek analizler, GitHub’ın MessagePack tabanlı yeni bir ID biçimi ile string tabanlı legacy biçimi birlikte kullandığını ortaya koydu
  • Bu yapı, GitHub’ın dahili nesne kimliklendirme sistemindeki ikiliği gösteriyor ve API entegrasyonu yapan geliştiricilerin dikkatli olmasını gerektiriyor

GitHub’ın çift ID sisteminin keşfi

  • Greptile’ın yapay zeka kod inceleme aracı özelliği geliştirilirken, GitHub PR yorum bağlantılarının çalışmaması sorunu yaşandı
    • Kaydedilen yorum ID’si URL’ye eklendi, ancak tıklandığında GitHub sayfasına gidilmedi
  • GitHub belgeleri incelendiğinde, GraphQL API’nin node ID’si ile REST API’nin database ID’sinin birbirinden farklı iki sistem olduğu görüldü
    • node ID örneği: PRRC_kwDOL4aMSs6Tkzl8
    • database ID örneği: 2475899260
  • node ID, GitHub genelinde nesneleri küresel olarak tanımlamak için kullanılan base64 encode edilmiş bir string, database ID ise URL’de kullanılan tamsayı türünde bir tanımlayıcı

node ID ile database ID arasındaki ilişkinin analizi

  • Birden fazla PR yorumunun node ID ve database ID değerleri karşılaştırıldığında, iki değerin de düzenli aralıklarla arttığı görüldü
  • node ID’nin base64 kısmı decode edildiğinde 96 bitlik bir tamsayı elde edildi ve bu değerin alt 32 bitinin database ID ile eşleştiği doğrulandı
    • Örnek: PRRC_kwDOL4aMSs6Tkzl8 → alt 32 bit = 2475899260
  • Basit bir bit maskesi işlemiyle database ID çıkarılabiliyor
    • Dönüşüm, decoded & ((1 << 32) - 1) biçimindeki işlemle yapılıyor

GitHub’ın legacy ID biçimi

  • Eski bir depodaki (torvalds/linux) node ID decode edildiğinde farklı biçimde bir string ortaya çıktı
    • Örnek: MDEwOlJlcG9zaXRvcnkyMzI1Mjk4010:Repository2325298
  • Bu biçim, [nesne türü numarası]:[nesne adı][Database ID] yapısında, açık string tabanlı bir tanımlayıcı
  • Ağaç nesnelerinde 04:Tree2325298:7201bfb9... biçimi görülüyor ve burada depo ID’si ile SHA değeri birlikte yer alıyor
  • GitHub, legacy biçim ile yeni biçimi birlikte kullanıyor; biçim, nesne türüne ve oluşturulma zamanına göre değişiyor

Yeni node ID biçiminin yapısı

  • GitHub’ın GraphQL migration guide dokümanı, node ID’nin opak bir string olarak ele alınmasını söylese de, içinde bir yapı bulunuyor
  • base64 decode işleminden sonra MessagePack ile unpack edildiğinde dizi biçiminde veri elde ediliyor
    • Örnek: [0, 47954445, 2475899260]
  • Dizinin yapısı
    • İlk öğe (0): sürüm tanımlayıcısı olduğu tahmin ediliyor
    • İkinci öğe (47954445): deponun database ID’si
    • Üçüncü öğe (2475899260): nesnenin database ID’si
  • Nesne türüne göre dizinin uzunluğu değişiyor; commit nesneleri SHA içeriyor, repository nesneleri ise yalnızca iki öğe içeriyor

Pratik kullanım ve sonuç

  • Yeni node ID’den database ID çıkarmak için Python kodu örneği
    import base64, msgpack
    def node_id_to_database_id(node_id):
        prefix, encoded = node_id.split('_')
        packed = base64.b64decode(encoded)
        array = msgpack.unpackb(packed)
        return array[-1]
    
  • Bu yöntemle PR yorumunun database ID’si doğrudan çıkarılarak URL bağlantısı sorunu çözülebiliyor
  • GitHub şu anda MessagePack tabanlı yeni ID sistemi ile string tabanlı legacy sistemi aynı anda sürdürüyor
  • Bu yapı, GitHub’ın geçiş sürecini ve uyumluluğu koruma çabasını gösteriyor; API kullanan geliştiricilerin ID biçimi farklarına dikkat etmesi gerekiyor

1 yorum

 
GN⁺ 2026-01-15
Hacker News yorumları
  • En yeni GitHub global node ID'si 'X-Github-Next-Global-ID' başlığı üzerinden zorla kullanılabiliyor
    ID, nesnenin tip öneki ve base64 ile kodlanmış bir msgpack payload'undan oluşuyor
    Örneğin benim kullanıcı ID'm "U_kgDOAAhEkg", [0, 541842] olarak decode ediliyor; bu da REST API'deki databaseId ile eşleşiyor
    Ancak böyle iç uygulama detaylarına dayanmak yerine, GraphQL API'deki databaseId alanını doğrudan sorgulamak daha iyi
    İlgili belgeler: GraphQL global node ID migration guide, GitHub kullanıcı bilgilerim, CyberChef decode örneği, GitHub ETag implementasyonu

  • Bu şekilde decode etmenin kırılgan olduğunu düşünüyorum
    GraphQL'in global node ID'leri aslında opaque olmalı
    GitHub'daki birçok tip (PullRequest vb.) databaseId alanı sağlıyor; doğru olan onu kullanmak
    Çoğu GraphQL API'si tip adını ve DB ID'sini base64 ile kodlar, ama bu kuralın hep korunacağı garanti edilemez
    Referans: PullRequest nesnesi dokümantasyonu, GraphQL global ID spec

    • GitHub'ın GraphQL tiplerinde permalink, url gibi alanlar ve UniformResourceLocatable arayüzü var; bu yüzden URL'yi elle kurmak gerekmiyor
    • Bu tür iç yapılar zamanla bozulmaya çok açık
      API'nin permalink vermesinin nedeni de bu. ID ya da link desenleri her an değişebilir
    • Tanımlayıcıya metadata koymak istiyorsanız, kullanıcıların iç yapıya bağımlı hale gelmemesi için bunu şifrelemek daha iyi olur
      Bu yaklaşım pagination token'larında da sık görülür
  • 010:Repository2325298 gibi ID'lerin belirgin bir yapısı var
    010 tip enum'u, Repository adı, 2325298 ise DB ID'si
    Yani bu bir length prefix biçimi. Repository 10 karakter, Tree ise 4 karakter

    • BitTorrent protokolünü hatırlatıyor
    • Neredeyse bir URN gibi görünüyor
  • Opus 4.5 bu GitHub ID decode numarasını biliyor ve otomatik olarak decode kodu yazıyor

  • Yazarın bulduğu şey teknik olarak doğru ama dokümante edilmemiş ve desteklenmiyor
    GitHub geçmişte de node ID'nin iç yapısını sessizce değiştirmişti
    MessagePack dizisine alan eklenirse, encoding değişirse, şifrelenirse ya da UUID tabanlı hale gelirse
    bu iç yapıya dayanan sistemler anında bozulur

  • Benim açıkça sakladığım GitHub tanımlayıcıları en fazla değişmez URL anahtarları (issue/pr numaraları veya commit hash'leri)
    Yorum ID'lerini de JSON blob'un içine olduğu gibi koyuyorum
    Her şeyi normalize etmeye çalışmak gerekmiyor. JSON yeterince hızlı
    Yorum düzeyinde çapraz sorgular yapmadığınız sürece, bunun bir performans sorunu olarak ortaya çıkması pek olası değil

    • Ama issue/pr URL'leri de değişmez değil
      Depo ad değiştirirse ya da başka bir organizasyona taşınırsa URL değişebilir
  • Eski v3 API'de ID yoktu; bu yüzden biri kullanıcı adını ya da depo adını değiştirince kimin kim olduğunu takip etmek zordu
    Bu yüzden takım bazlı bir sahiplik yönetim sistemini kendim yazmıştım
    Terraform provider pek iyi değildi; bu nedenle offboarding sırasında “tek admin olan kişi ayrıldı” gibi sorunlar sık yaşanıyordu
    Tüm depoların sahibi bir takım olur ve erişim izinleri de yalnızca takım bazında verilir

    • “Kullanıcıya erişim vermek” yerine “takıma yetki verilir, kullanıcı da o takımın üyesidir” diye düşünmek çok daha verimli
      Bu tür takım tabanlı erişim kontrolü sadece GitHub için değil, başka sistemlerde de faydalı
  • Bu, Hyrum’s Law için klasik bir örnek — insanlar dokümante edilmemiş davranışlara bağımlı olmaya başlayınca eninde sonunda her şey bozulur

  • Veritabanı tasarımında genelde dışarıya opaque doğal anahtarlar verilir, içeride ise artan tamsayı ID'ler kullanılır

    • Bunun iki nedeni var
      1. Dışarıya nesne sayısını göstermemek
      2. ID'leri basitçe artırarak tüm nesnelerin taranmasını engellemek
        Ama bileşik ID kullanıldığında bu sorunlar azalır.
        Örneğin depo ID'sinin içinde nesne ID'si de varsa, ID artırılarak ancak aynı depo içindeki nesneler taranabilir
        Buna entropi veya timestamp da eklenirse, kötüye kullanım neredeyse imkansız hale gelir
    • Ama doğal anahtarlar değişebilir
      Bu yüzden anlamsız bir surrogate key'i dışarıya açmak daha güvenlidir
      Örneğin YouTube içeride indeks numarası kullanıyor olsa bile, dışarıya anlamsız kod biçiminde ID'ler verir
  • GitHub ekibinin son birkaç yılda Rails'e sharded/multi-database desteğini neden ciddi biçimde genişlettiği şimdi daha anlaşılır geliyor