---
id: 6e0bce9e22a4bf7d9672c59e67bcf5ec
author: Lemon Mint
title: 'gosuda.org/chromenet: Stack Jaringan Chromium untuk Go, Dibangun Secara Reproducible dari Rilis yang Dipatok'
description: CGO_ENABLED=0 Go http.RoundTripper yang mengirimkan permintaan melalui stack //net milik Chromium sendiri (HTTP/1.1, HTTP/2, QUIC/HTTP/3, BoringSSL) di dalam worker HQQ yang terisolasi, dengan kode sumber, toolchain, dan enam binary tertanam yang dikunci menggunakan SHA-256 serta dibangun di dalam sandbox gvx.
language: id
date: 2026-11-09T00:00:00Z
path: /chromenet
go_package: gosuda.org/chromenet
go_repourl: https://github.com/gosuda/chromenet.git
category: network
---

`gosuda.org/chromenet` adalah `http.RoundTripper` Go yang mengirim permintaan melalui stack jaringan milik Chromium sendiri. Stack tersebut adalah `//net` dari rilis resmi Chromium: implementasi HTTP/1.1, HTTP/2, dan QUIC/HTTP/3, BoringSSL, konfigurasi TLS Chrome, serta Chrome Root Store. Stack ini berjalan dalam proses worker terpisah, `chrome_net_server`, dan sisi Go tetap murni Go (`CGO_ENABLED=0`), berkomunikasi dengan worker melalui RPC shared-memory [HQQ](https://gosuda.org/hqq).

```bash
go get gosuda.org/chromenet@v0.1.10157081033800
```

## Mengapa Menjalankan Stack Jaringan Chromium

Program Go yang berkomunikasi dengan web menggunakan `net/http` menampilkan dirinya sebagai klien Go kepada server. Itu adalah default yang tepat untuk lalu lintas antarlayanan. Namun, itu menjadi pilihan yang salah ketika perilaku server bergantung pada jenis klien yang dikiranya sedang dihadapi: endpoint yang hanya dapat diakses dari browser, middleware anti-bot, CDN yang menyajikan versi protokol berbeda untuk sidik jari TLS dan HTTP/2 yang berbeda, serta pengujian yang harus mereproduksi pengalaman browser sungguhan.

Pustaka standar Go tidak dapat memberikan perilaku wire browser. `crypto/tls` memiliki urutan cipher-suite dan kumpulan ekstensi sendiri, dan `net/http` memiliki pengaturan HTTP/2 sendiri. Pustaka yang meniru browser dengan menyalin byte ClientHello-nya harus terus disalin ulang setiap rilis. `chromenet` mengambil jalur lain: ia tidak meniru stack jaringan Chromium, melainkan menjalankannya.

Diukur pada engine `darwin/arm64` terhadap layanan fingerprint publik (`tls.peet.ws`), konfigurasi default melaporkan:

| Properti                            | Nilai yang teramati                                                     |
| :---------------------------------- | :---------------------------------------------------------------------- |
| Protokol yang dinegosiasikan        | `h2`                                                                    |
| JA4                                 | `t13d1516h2_8daaf6152771_806a8c22fdea`                                  |
| Fingerprint HTTP/2 (format Akamai)  | `1:65536;2:0;4:6291456;6:262144\|15663105\|0\|m,a,s,p`                  |
| User-Agent                          | Chrome desktop dengan versi mayor engine, misalnya `Chrome/157.0.0.0`   |

Dengan `ForceHTTP3`, permintaan ke `cloudflare-quic.com` dan `www.google.com` dijawab melalui HTTP/3. Angka-angka ini berasal dari satu platform pada satu hari. Engine adalah stack itu sendiri, sehingga angka tersebut berubah ketika rilis Chromium yang di-pin berubah.

## Cara Menggunakan

`NewTransport` mengembalikan `http.RoundTripper`. Gunakan di mana pun Anda akan menggunakan `http.Transport`:

```go
package main

import (
	"fmt"
	"io"
	"net/http"

	"gosuda.org/chromenet"
)

func main() {
	client := &http.Client{Transport: chromenet.NewTransport(chromenet.Options{})}

	resp, err := client.Get("https://example.com/")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.Proto, resp.StatusCode, len(body))
}
```

Pada permintaan pertama, modul menulis binary worker tertanam untuk platform Anda ke direktori cache pengguna, memverifikasi digest SHA-256-nya dengan `gosuda.org/hqq/secureexec`, menjalankannya dengan lifeline induk (`--parent-stdin`, sehingga worker keluar ketika proses Anda keluar), dan membuka session pool. Tidak ada yang perlu diinstal.

Beberapa perilaku mengikuti `http.Transport` agar `http.Client` dapat bekerja tanpa perubahan:

- Transport tidak mengikuti redirect dan tidak menyimpan cookie. `http.Client` melakukan keduanya, sebagaimana halnya dengan `http.Transport`.
- Respons yang dikompresi dengan gzip, deflate, Brotli, atau zstd diterima dalam keadaan sudah didekode, dan `resp.Uncompressed` bernilai true.
- Sebuah permintaan menahan satu session engine hingga body respons-nya ditutup. Tutup body.
- Membatalkan context permintaan akan membatalkan permintaan di dalam Chromium.
- Kegagalan jaringan direpresentasikan sebagai nilai `*client.NetError` yang membawa kode dan nama error Chromium:

```go
var ne *client.NetError
if errors.As(err, &ne) {
	fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
}
```

### Opsi

| Field                         | Efek                                                                                                                           |
| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------- |
| `UserAgent`                   | Menggantikan User-Agent Chrome default. Header permintaan `User-Agent` tetap diutamakan.                                      |
| `AcceptLanguage`              | Menggantikan `en-US,en;q=0.9`.                                                                                                 |
| `Proxy`                       | Aturan proxy Chromium seperti `http://127.0.0.1:8080` atau `socks5://127.0.0.1:1080`. Nilai kosong berarti koneksi langsung. |
| `DisableHTTP2`, `DisableQUIC` | Menonaktifkan protokol tersebut.                                                                                               |
| `ForceHTTP3`                  | Menggunakan QUIC untuk setiap origin tanpa menunggu advertisement `Alt-Svc`.                                                  |
| `InsecureSkipVerify`          | Menerima sertifikat server apa pun.                                                                                            |
| `Pool`                        | Session engine yang digunakan. Nil memakai `client.DefaultPool()`, yang mengizinkan 64 permintaan bersamaan.                  |

Transport dengan opsi yang sama berbagi satu `URLRequestContext` Chromium, sehingga connection pool, session HTTP/2, dan session QUIC digunakan ulang di antara permintaan. Mengatur `HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server` menggantikan worker tertanam, misalnya dengan build lokal.

## Cara Kerjanya

| Lapisan             | Yang berjalan di sana                                                                                                                                                                                                       |
| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Program Anda        | `http.Client` dan `chromenet.Transport`, murni Go.                                                                                                                                                                          |
| Batas proses        | RPC shared-memory HQQ. Antarmukanya adalah `chrome_net.idl`, yang dikompilasi oleh `hqq-bindgen` menjadi klien Go dan glue server C.                                                                                       |
| `chrome_net_server` | Lapisan C++ kecil di atas Chromium: satu network thread yang memiliki setiap objek `//net`, dan session thread yang memblokir permintaan hingga network thread memberikan jawaban.                                           |
| Chromium `//net`    | `URLRequestContext`, `URLRequest`, session HTTP/2 dan QUIC, BoringSSL.                                                                                                                                                      |

IDL memiliki enam operasi yang penting: membuat engine dari opsi `key=value`, memulai permintaan dengan head-nya (`METHOD URL` ditambah baris header) dan panjang body, menulis potongan body permintaan, menunggu head respons, membaca body yang sudah didekode, dan menghancurkan handle. Handle permintaan mendeklarasikan `@cancel`, sehingga panggilan yang ditinggalkan akan menginterupsi permintaan di dalam Chromium alih-alih membiarkannya berjalan hingga selesai.

Mengisolasi stack dalam proses worker adalah properti keamanan sekaligus properti pengemasan. Kode jaringan Chromium mem-parsing input berbahaya dari internet terbuka. Jika ia crash, layanan Go Anda tidak ikut crash: pemanggilan gagal dengan error, session dibuang, dan klien memulai worker baru saat dibutuhkan lagi.

## Dibangun Secara Reproducible, Di-pin dengan SHA-256

Binary 25 MB yang berisi Chromium hanya dapat dipercaya jika Anda dapat menyatakan dengan tepat apa yang masuk ke dalamnya. `chromenet` mengikuti Chromium Stable sebagaimana `gosuda.org/sqlite3` mengikuti SQLite: sumbernya tidak pernah di-vendor, hanya di-pin.

| Pin                                                                                                                                      | Lokasi                         |
| :--------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------- |
| Rilis Chromium dan arsip sumber `-lite` resminya                                                                                         | `third_party/chromium/VERSION` |
| SHA-256 dari arsip dan setiap toolchain yang di-pin oleh rilis tersebut: clang, Rust, GN, ninja, runtime clang Windows, dan xwin          | `third_party/chromium/SHA256`  |
| Versi MSVC CRT dan Windows SDK yang diambil melalui xwin                                                                                 | `third_party/chromium/WINSDK`  |
| Rilis runtime sandbox (`rungv`), digest guest image, tanggal snapshot Debian                                                             | `scripts/engine/BUILDENV`      |
| Runtime C HQQ                                                                                                                            | `go.mod` dan `go.sum`          |

Pin saat ini adalah Chromium `157.0.8103.38`. Unduhan yang tidak cocok dengan pin-nya akan menghentikan build. Rilis ini menerapkan tepat satu patch ke Chromium: di Linux, `chrome_net_server` dibangun tanpa NSS dan memverifikasi sertifikat server hanya terhadap Chrome Root Store, sehingga binary tidak membutuhkan apa pun selain libc.

Engine Linux dan Windows dibangun di dalam sandbox [gvx](https://gosuda.org/blog/posts/gvx-cross-platform-linux-containers-z48916d01): proses tanpa hak istimewa dengan guest image yang di-pin dan tanpa daemon host. Engine Windows dikompilasi silang di Linux menggunakan clang-cl dan lld-link milik Chromium terhadap Windows SDK yang diambil xwin dari Microsoft. Engine macOS dibangun secara native pada runner macOS, karena build macOS Chromium membutuhkan Xcode.

Membangun commit yang sama menghasilkan binary yang identik byte demi byte di antara CI run yang terpisah. Binary rilis identik byte dengan binary yang dibangun oleh CI run sebelumnya untuk commit yang memasukkan engine ke monorepo, di keenam platform.

| Platform        | Ukuran binary |
| :-------------- | ------------: |
| `linux/amd64`   |       27.9 MB |
| `linux/arm64`   |       27.4 MB |
| `darwin/amd64`  |       26.2 MB |
| `darwin/arm64`  |       23.9 MB |
| `windows/amd64` |       27.3 MB |
| `windows/arm64` |       25.8 MB |

Engine Linux hanya bergantung pada `libc`, `libm`, `libdl`, `libpthread`, dan `libgcc_s`, serta membutuhkan glibc 2.31 atau yang lebih baru.

### Melacak Chromium dan Rilis

Workflow terjadwal memeriksa Chromium Stable setiap enam jam. Untuk rilis baru, ia menulis ulang pin, membangun dan menguji keenam engine pada branch `chromium/<version>`, mengattest binary, lalu melakukan commit pin bersama binary tertanam, digest-nya, dan teks lisensi ke `main` dalam satu commit. Di monorepo, `OWNERS` per file hanya memberikan bot workflow akses ke file-file tersebut, sehingga pembaruan masuk tanpa langkah manusia, sementara perubahan pada resep build itu sendiri (pin Windows SDK dan patch) tetap membutuhkan owner.

Setiap binary rilis membawa build provenance attestation dari workflow yang membangunnya:

```bash
gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet
```

Teks lisensi untuk setiap komponen yang terdapat dalam binary (Chromium, BoringSSL, QUICHE, dan sisa build graph) dihasilkan oleh `tools/licenses/licenses.py` milik Chromium sendiri dan dikirimkan bersama rilis sebagai `NOTICE_<os>_<arch>.txt`.

## Batasan

- `DialWebSocket` adalah klien RFC 6455 murni Go dan tidak menggunakan stack Chromium.
- `DialWebTransport` adalah implementasi in-process. Ia tidak menjangkau server.
- Fingerprint JA4 dan HTTP/2 default adalah milik rilis Chromium yang di-pin. Chromium mengubahnya dari rilis ke rilis, dan `chromenet` mengikutinya.
- Binary macOS ditandatangani secara ad-hoc dan tidak dinotarisasi.
- Sebuah proses worker memakan memori. Setiap binary engine berukuran 24 hingga 28 MB di disk, dan runtime Chromium memulai thread pool serta network thread.
- Session engine ditahan hingga body respons ditutup. Program yang membocorkan body respons akan menghabiskan pool.

## Sumber

Repositori berada di [github.com/gosuda/chromenet](https://github.com/gosuda/chromenet), dicerminkan dari `//net/chromenet` di monorepo gosuda. Resep build berada di `scripts/engine`, glue engine di `engine/`, dan antarmuka di `chrome_net.idl`.
