---
id: 6e0bce9e22a4bf7d9672c59e67bcf5ec
author: Lemon Mint
title: 'gosuda.org/chromenet: Chromiums Network Stack for Go, bygget reproduserbart fra en Pinned Release'
description: CGO_ENABLED=0 Go http.RoundTripper som sender forespørsler gjennom Chromiums egen //net-stack (HTTP/1.1, HTTP/2, QUIC/HTTP/3, BoringSSL) i en isolert HQQ-worker, med kildekode, toolchains og seks innebygde binærfiler låst til SHA-256-verdier og bygget i gvx-sandboxes.
language: "no"
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` er en Go-`http.RoundTripper` som sender forespørsler gjennom Chromiums egen nettverksstakk. Stakken er `//net` fra en offisiell Chromium-utgivelse: dens implementasjoner av HTTP/1.1, HTTP/2 og QUIC/HTTP/3, BoringSSL, Chromes TLS-konfigurasjon og Chrome Root Store. Den kjører i en egen arbeidsprosess, `chrome_net_server`, og Go-siden forblir ren Go (`CGO_ENABLED=0`), og kommuniserer med arbeidsprosessen via [HQQ](https://gosuda.org/hqq) shared-memory RPC.

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

## Hvorfor kjøre Chromiums nettverksstakk

Et Go-program som kommuniserer med nettet via `net/http` fremstår for serveren som en Go-klient. Dette er riktig standard for trafikk mellom tjenester. Det er feil når serverens oppførsel avhenger av hva slags klient den tror den kommuniserer med: endepunkter som er låst til nettleser, anti-bot-mellomvare, CDN-er som leverer ulike protokollversjoner til ulike TLS- og HTTP/2-fingeravtrykk, og tester som må gjenskape det en ekte nettleser opplever.

Go sitt standardbibliotek kan ikke gi deg en nettleser sin nettverksoppførsel. `crypto/tls` har sin egen rekkefølge for chiffersuiter og sitt eget sett med utvidelser, og `net/http` har sine egne HTTP/2-innstillinger. Biblioteker som etterligner en nettleser ved å kopiere dens ClientHello-bytes, må fortsette å kopiere, utgivelse etter utgivelse. `chromenet` tar en annen vei: den etterligner ikke Chromiums nettverksstakk, den kjører den.

Målt med `darwin/arm64`-motoren mot en offentlig tjeneste for fingeravtrykk (`tls.peet.ws`) rapporterer standardkonfigurasjonen:

| Egenskap                           | Observert verdi                                                       |
| :--------------------------------- | :-------------------------------------------------------------------- |
| Forhandlet protokoll               | `h2`                                                                  |
| JA4                                | `t13d1516h2_8daaf6152771_806a8c22fdea`                                |
| HTTP/2-fingeravtrykk (Akamai-format) | `1:65536;2:0;4:6291456;6:262144\|15663105\|0\|m,a,s,p`                |
| User-Agent                         | desktop-Chrome med motorens hovedversjon, f.eks. `Chrome/157.0.0.0`   |

Med `ForceHTTP3` ble forespørsler til `cloudflare-quic.com` og `www.google.com` besvart over HTTP/3. Disse tallene stammer fra én plattform på én dag. Motoren er stakken, så tallene endres når den fastlåste Chromium-utgivelsen endres.

## Bruk

`NewTransport` returnerer en `http.RoundTripper`. Bruk den der du ellers ville brukt `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))
}
```

Ved den første forespørselen skriver modulen den innebygde arbeidsbinærfilen for din plattform til brukerens cachekatalog, verifiserer SHA-256-digesten med `gosuda.org/hqq/secureexec`, starter den med en forelderlivline (`--parent-stdin`, slik at den avsluttes når prosessen din avsluttes), og åpner en sesjonspool. Det trenger ikke installeres noe.

Noen atferder følger `http.Transport`, slik at `http.Client` fungerer uendret:

- Transporten følger ikke omdirigeringer og lagrer ingen informasjonskapsler. `http.Client` gjør begge deler, slik den gjør med `http.Transport`.
- Svar komprimert med gzip, deflate, Brotli eller zstd kommer dekomprimert, og `resp.Uncompressed` er true.
- En forespørsel holder én motorsesjon til responskroppen er lukket. Lukk kroppene.
- Å avbryte forespørselskonteksten avbryter forespørselen inne i Chromium.
- Nettverksfeil er `*client.NetError`-verdier som inneholder Chromiums feilkode og feilnavn:

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

### Alternativer

| Felt                          | Effekt                                                                                                                                  |
| :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- |
| `UserAgent`                   | Erstatter standard Chrome-User-Agent. En `User-Agent`-forespørselsheader har fortsatt forrang.                                          |
| `AcceptLanguage`              | Erstatter `en-US,en;q=0.9`.                                                                                                             |
| `Proxy`                       | Proxy-regler for Chromium, for eksempel `http://127.0.0.1:8080` eller `socks5://127.0.0.1:1080`. Tom verdi betyr direkte tilkobling.   |
| `DisableHTTP2`, `DisableQUIC` | Slår av protokollene.                                                                                                                   |
| `ForceHTTP3`                  | Bruker QUIC for alle opprinnelser uten å vente på en `Alt-Svc`-annonsering.                                                             |
| `InsecureSkipVerify`          | Godtar ethvert serversertifikat.                                                                                                        |
| `Pool`                        | Motorsesjoner som skal brukes. Nil bruker `client.DefaultPool()`, som tillater 64 samtidige forespørsler.                              |

Transporter med like alternativer deler én Chromium-`URLRequestContext`, slik at tilkoblingspooler, HTTP/2-sesjoner og QUIC-sesjoner gjenbrukes på tvers av forespørsler. Å sette `HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server` erstatter den innebygde arbeidsprosessen, for eksempel med en lokal build.

## Slik fungerer det

| Lag                 | Hva som kjører der                                                                                                                                                                                                              |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Ditt program        | `http.Client` og `chromenet.Transport`, ren Go.                                                                                                                                                                                  |
| Prosessgrense       | HQQ shared-memory RPC. Grensesnittet er `chrome_net.idl`, kompilert av `hqq-bindgen` til Go-klienten og C-serverlimet.                                                                                                          |
| `chrome_net_server` | En liten C++-lag oppå Chromium: én nettverkstråd som eier alle `//net`-objekter, og sesjonstråder som blokkerer på en forespørsel til nettverkstråden svarer.                                                                 |
| Chromium `//net`    | `URLRequestContext`, `URLRequest`, HTTP/2- og QUIC-sesjoner, BoringSSL.                                                                                                                                                         |

IDL-en har seks operasjoner som er viktige: opprette en motor fra `key=value`-alternativer, starte en forespørsel med dens hode (`METHOD URL` pluss headerlinjer) og kroppslengde, skrive forespørselskroppens biter, vente på responshodet, lese den dekomprimerte kroppen og destruere håndtakene. Et forespørselshåndtak deklarerer `@cancel`, så et forlatt kall avbryter forespørselen inne i Chromium i stedet for å la den kjøre til ende.

Å isolere stakken i en arbeidsprosess er like mye en sikkerhetsegenskap som en pakkeegenskap. Chromiums nettverkskode analyserer fiendtlig input fra det åpne internett. Hvis den krasjer, krasjer ikke Go-tjenesten din med: kallet feiler med en feil, sesjonen kasteres, og klienten starter en ny arbeidsprosess neste gang den trenger en.

## Reproduserbart bygget, fastlåst med SHA-256

En 25 MB stor binærfil som inneholder Chromium er bare pålitelig hvis du kan si nøyaktig hva som gikk inn i den. `chromenet` følger Chromium Stable på samme måte som `gosuda.org/sqlite3` følger SQLite: kildekoden blir aldri vendorisert, bare fastlåst.

| Fastlåsing                                                                                                                                  | Plassering                     |
| :------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------- |
| Chromium-utgivelse og dens offisielle `-lite`-kildearkiv                                                                                    | `third_party/chromium/VERSION` |
| SHA-256 for arkivet og for hver verktøykjede som utgivelsen fastlåser: clang, Rust, GN, ninja, Windows clang-runtime, xwin                 | `third_party/chromium/SHA256`  |
| MSVC CRT- og Windows SDK-versjoner hentet via xwin                                                                                          | `third_party/chromium/WINSDK`  |
| Sandkassens kjøretids (`rungv`) utgivelse, gjesteavbildningens digest, Debian-snapshotdatoen                                                | `scripts/engine/BUILDENV`      |
| HQQ C-kjøretiden                                                                                                                            | `go.mod` og `go.sum`           |

Den gjeldende fastlåsingen er Chromium `157.0.8103.38`. En nedlasting som ikke samsvarer med fastlåsingen stopper bygget. Utgivelsen bruker nøyaktig ett plaster på Chromium: på Linux bygges `chrome_net_server` uten NSS og verifiserer serversertifikater kun mot Chrome Root Store, slik at binærfilen ikke trenger noe utover libc.

Motorene for Linux og Windows bygges i [gvx](https://gosuda.org/blog/posts/gvx-cross-platform-linux-containers-z48916d01)-sandkasser: prosesser uten privilegier med et fastlåst gjesteavbilde og ingen vertsdaemon. Windows-motorene krysskompileres på Linux med Chromiums clang-cl og lld-link mot Windows SDK som xwin henter fra Microsoft. macOS-motorene bygges naturlig på en macOS-runner, fordi Chromiums macOS-bygg krever Xcode.

Ny bygging av samme commit ga byte-identiske binærfiler på tvers av separate CI-kjøringer. Utgivelsesbinærfilene er byte-identiske med dem en tidligere CI-kjøring bygget for commit-en som innførte motoren i monorepoet, på alle seks plattformer.

| Plattform       | Binærstørrelse |
| :-------------- | -------------: |
| `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 |

Linux-motorene avhenger bare av `libc`, `libm`, `libdl`, `libpthread` og `libgcc_s`, og krever glibc 2.31 eller nyere.

### Følge Chromium og utgivelser

En planlagt arbeidsflyt sjekker Chromium Stable hver sjette time. For en ny utgivelse skriver den om fastlåsingene, bygger og tester alle seks motorene på en `chromium/<version>`-gren, attesterer binærfilene, og committer fastlåsingene sammen med de innebygde binærfilene, deres digester og lisenstekstene til `main` i én commit. I monorepoet gir `OWNERS` per fil arbeidsflytens bot nøyaktig disse filene, slik at oppdateringen går inn uten et menneskelig steg, mens endringer i selve byggeoppskriften (Windows SDK-fastlåsingen og plasteret) fortsatt krever en eier.

Hver utgivelsesbinærfil bærer en byggeprovenienssertifisering fra arbeidsflyten som bygget den:

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

Lisenstekstene for det hver binærfil inneholder (Chromium, BoringSSL, QUICHE og resten av byggegrafen) genereres av Chromiums egen `tools/licenses/licenses.py` og leveres med utgivelsen som `NOTICE_<os>_<arch>.txt`.

## Begrensninger

- `DialWebSocket` er en ren Go-klient for RFC 6455 og bruker ikke Chromium-stakken.
- `DialWebTransport` er en implementasjon i samme prosess. Den når ikke en server.
- Standard JA4- og HTTP/2-fingeravtrykk er de for den fastlåste Chromium-utgivelsen. Chromium endrer dem fra utgivelse til utgivelse, og `chromenet` følger med.
- macOS-binærfilene er ad hoc-signert og ikke notarisert.
- En arbeidsprosess koster minne. Hver motorbinærfil er 24 til 28 MB på disk, og Chromiums kjøretid starter en trådpool og en nettverkstråd.
- En motorsesjon holdes til responskroppen er lukket. Et program som lekker responskropper vil tømme poolen.

## Kilde

Repositoriet er [github.com/gosuda/chromenet](https://github.com/gosuda/chromenet), speilet fra `//net/chromenet` i gosuda-monorepoet. Byggeoppskriften ligger i `scripts/engine`, motorlimet i `engine/`, og grensesnittet i `chrome_net.idl`.
