2 poin oleh GN⁺ 2026-01-15 | 1 komentar | Bagikan ke WhatsApp
  • Saat menggunakan GitHub API, muncul masalah pada fitur pembuatan tautan komentar PR karena ketidakcocokan ID sehingga tautan tidak berfungsi
  • Hasil investigasi menunjukkan bahwa GitHub menggunakan dua sistem ID secara bersamaan: node ID di GraphQL dan database ID di REST API
  • Setelah base64 decoding pada node ID, ditemukan bahwa 32 bit terbawah berisi database ID, sehingga dapat dikonversi dengan operasi bitmask sederhana
  • Analisis lanjutan mengungkap bahwa GitHub mencampur format ID baru berbasis MessagePack dan format lama berbasis string
  • Struktur ini menunjukkan dualisme sistem identifikasi objek internal GitHub, dan pengembang perlu berhati-hati saat mengintegrasikan API

Penemuan sistem ID ganda GitHub

  • Saat mengembangkan fitur pada alat AI code review milik Greptile, muncul masalah ketika tautan komentar PR GitHub tidak berfungsi
    • ID komentar yang disimpan telah disambungkan ke URL, tetapi saat diklik tidak berpindah ke halaman GitHub
  • Setelah memeriksa dokumentasi GitHub, ditemukan bahwa node ID pada GraphQL API dan database ID pada REST API merupakan dua sistem yang berbeda
    • Contoh node ID: PRRC_kwDOL4aMSs6Tkzl8
    • Contoh database ID: 2475899260
  • node ID adalah string hasil encoding base64 untuk mengidentifikasi objek secara global di seluruh GitHub, sedangkan database ID digunakan sebagai pengenal URL berbentuk bilangan bulat

Analisis hubungan antara node ID dan database ID

  • Dengan membandingkan node ID dan database ID dari beberapa komentar PR, terlihat bahwa kedua nilai meningkat dengan pola interval yang konsisten
  • Ketika bagian base64 dari node ID didekode, dihasilkan integer 96-bit, dan 32 bit terbawah dari nilai ini cocok dengan database ID
    • Contoh: PRRC_kwDOL4aMSs6Tkzl8 → 32 bit terbawah = 2475899260
  • database ID dapat diekstrak dengan operasi bitmask sederhana
    • Konversi dilakukan dengan operasi berbentuk decoded & ((1 << 32) - 1)

Format ID lama GitHub

  • Ketika node ID dari repositori lama (torvalds/linux) didekode, muncul string dengan format berbeda
    • Contoh: MDEwOlJlcG9zaXRvcnkyMzI1Mjk4010:Repository2325298
  • Format ini memiliki struktur [nomor tipe objek]:[nama objek][Database ID] dan merupakan identifier eksplisit berbasis string
  • Untuk objek tree, bentuknya seperti 04:Tree2325298:7201bfb9..., yang juga memuat ID repositori dan nilai SHA
  • GitHub menggunakan format lama dan format baru secara bersamaan, dan formatnya berbeda tergantung jenis objek serta waktu pembuatannya

Struktur format node ID baru

  • Panduan migrasi GraphQL GitHub menyatakan bahwa node ID harus diperlakukan sebagai string opak, tetapi tetap ada struktur internal di dalamnya
  • Setelah base64 decoding lalu di-unpack dengan MessagePack, muncul data berbentuk array
    • Contoh: [0, 47954445, 2475899260]
  • Susunan array tersebut
    • Elemen pertama (0): diduga sebagai identifier versi
    • Elemen kedua (47954445): database ID repositori
    • Elemen ketiga (2475899260): database ID objek
  • Panjang array berbeda menurut jenis objek; commit menyertakan SHA, sedangkan repositori hanya memiliki dua elemen

Pemanfaatan praktis dan kesimpulan

  • Contoh kode Python untuk mengekstrak database ID dari node ID baru
    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]
    
  • Dengan cara ini, database ID komentar PR dapat diekstrak langsung untuk menyelesaikan masalah tautan URL
  • GitHub saat ini tetap mempertahankan sistem ID baru berbasis MessagePack dan sistem lama berbasis string secara bersamaan
  • Struktur ini menunjukkan proses transisi internal GitHub dan upaya menjaga kompatibilitas, sehingga pengembang yang menggunakan API perlu memperhatikan perbedaan format ID

1 komentar

 
GN⁺ 2026-01-15
Komentar Hacker News
  • GitHub global node ID terbaru bisa dipaksa digunakan lewat header 'X-Github-Next-Global-ID'
    ID tersebut terdiri dari prefiks tipe objek dan payload msgpack yang di-encode dengan base64
    Misalnya, ID pengguna saya "U_kgDOAAhEkg" ter-decode menjadi [0, 541842], yang cocok dengan databaseId di REST API
    Namun, sebaiknya jangan bergantung pada implementasi internal seperti ini; lebih baik langsung meminta field databaseId dari GraphQL API
    Dokumentasi terkait: panduan migrasi GraphQL global node ID, info pengguna GitHub saya, contoh decoding CyberChef, implementasi GitHub ETag

  • Mendekode dengan cara seperti ini menurut saya rapuh
    Global node ID di GraphQL pada dasarnya harus bersifat opaque
    Berbagai tipe GitHub (seperti PullRequest) menyediakan field databaseId, jadi itulah yang seharusnya dipakai
    Sebagian besar GraphQL API memang meng-encode nama tipe dan DB ID dengan base64, tetapi tidak ada jaminan pola ini akan selalu dipertahankan
    Referensi: dokumentasi objek PullRequest, spesifikasi GraphQL global ID

    • Tipe GraphQL GitHub punya field seperti permalink, url, dan interface UniformResourceLocatable, jadi tidak perlu menyusun URL secara manual
    • Struktur internal seperti ini kemungkinan besar akan rusak seiring waktu
      Itulah sebabnya API menyediakan permalink. Pola ID atau tautan bisa berubah kapan saja
    • Jika ingin memasukkan metadata ke dalam identifier, lebih baik dienkripsi agar pengguna tidak bergantung pada struktur internal
      Pendekatan seperti ini juga sering dipakai pada token pagination
  • ID seperti 010:Repository2325298 punya struktur yang jelas
    010 adalah enum tipe, Repository adalah nama, dan 2325298 adalah DB ID
    Jadi, ini berbentuk length prefix. Repository panjangnya 10 karakter, Tree panjangnya 4 karakter

    • Mengingatkan pada protokol BitTorrent
    • Hampir terlihat seperti URN
  • Opus 4.5 mengetahui trik decoding ID GitHub seperti ini, dan otomatis menulis kode untuk mendekodenya

  • Apa yang ditemukan penulis memang benar secara teknis, tetapi tidak terdokumentasi dan tidak didukung
    GitHub juga pernah diam-diam mengubah struktur internal node ID di masa lalu
    Jika mereka menambahkan field ke array MessagePack, mengubah encoding, mengenkripsi, atau beralih ke UUID,
    sistem yang bergantung pada struktur internal seperti ini akan langsung rusak

  • Identifier GitHub yang saya simpan secara eksplisit paling banter hanyalah kunci URL yang tidak berubah (nomor issue/PR atau hash commit)
    ID komentar saya masukkan begitu saja ke dalam blob JSON
    Tidak perlu mencoba menormalkan semuanya. JSON sudah cukup cepat
    Kecuali Anda melakukan kueri lintas-komentar, hampir tidak akan muncul sebagai masalah performa

    • Tetapi URL issue/PR tidak benar-benar tidak berubah
      Jika repositori berganti nama atau dipindahkan ke organisasi lain, URL-nya bisa berubah
  • Di API v3 lama, tidak ada ID, jadi jika seseorang mengganti nama pengguna atau nama repositori, sulit melacak siapa mereka
    Karena itu saya pernah membuat sendiri sistem manajemen kepemilikan berbasis tim
    Terraform provider-nya kurang bagus, sehingga saat offboarding sering muncul masalah seperti “orang yang keluar ternyata satu-satunya admin”
    Semua repositori dimiliki oleh tim, dan hak akses juga hanya diberikan per tim

    • Cara berpikir “beri izin ke tim, lalu pengguna adalah anggota tim itu” jauh lebih efisien daripada “beri akses ke pengguna”
      Kontrol akses berbasis tim seperti ini berguna bukan hanya di GitHub, tetapi juga di sistem lain
  • Ini contoh klasik Hyrum’s Law — ketika orang mulai bergantung pada perilaku yang tidak terdokumentasi, pada akhirnya pasti akan rusak

  • Dalam desain basis data, biasanya ke pihak luar diberikan natural key yang opaque, sementara di internal digunakan ID integer yang bertambah

    • Ada dua alasannya
      1. agar jumlah objek tidak terekspos ke luar
      2. agar orang tidak bisa menelusuri semua objek hanya dengan menaikkan ID
        Tetapi jika memakai ID komposit, masalah ini berkurang.
        Misalnya, jika ID repositori mencakup ID objek, maka menaikkan ID hanya akan menjelajahi objek dalam repositori yang sama
        Jika ditambah entropi atau timestamp, penyalahgunaannya nyaris mustahil
    • Namun natural key bisa berubah
      Karena itu lebih aman mengekspos surrogate key yang tidak bermakna
      Misalnya, YouTube mungkin memakai nomor indeks secara internal, tetapi ke luar memberikan ID berbentuk kode yang tidak bermakna
  • Sekarang jadi masuk akal kenapa tim GitHub dalam beberapa tahun terakhir sangat memperluas dukungan sharded/multi-database di Rails