# BuilderHub [![Goreport status](https://goreportcard.com/badge/github.com/flashbots/builder-hub)](https://goreportcard.com/report/github.com/flashbots/builder-hub) [![Test status](https://github.com/flashbots/builder-hub/actions/workflows/checks.yml/badge.svg?branch=main)](https://github.com/flashbots/builder-hub/actions?query=workflow%3A%22Checks%22) [![Docker hub](https://badgen.net/docker/size/flashbots/builder-hub?icon=docker&label=image)](https://hub.docker.com/r/flashbots/builder-hub/tags) BuilderHub is the central data source for BuilderNet builder registration and configuration. Docs here: https://buildernet.org/docs/flashbots-infra BuilderHub has these responsibilities: 1. Builder identity management 2. Provisioning of secrets and configuration 3. Peer discovery --- ![Architecture](https://buildernet.org/assets/ideal-img/flashbots-infra-dataflow.7377b1f.3909.png) --- ## Getting started ### Admin authentication The Admin API (port 8081) requires HTTP Basic Auth. Configure via env vars or flags: - `ADMIN_BASIC_USER` (default: `admin`) - `ADMIN_BASIC_PASSWORD_BCRYPT` (bcrypt hash of the password; required) Generate a bcrypt hash (example using htpasswd): ```bash htpasswd -nbBC 10 "" 'secret' | cut -d: -f2 ``` Run with: ```bash export ADMIN_BASIC_USER=admin export ADMIN_BASIC_PASSWORD_BCRYPT='$2y$12$...' go run cmd/httpserver/main.go ``` Use Basic Auth when calling admin endpoints, e.g.: ```bash curl -u admin:secret http://localhost:8081/api/admin/v1/measurements ``` Local development only: you can disable Admin API auth with `--disable-admin-auth` or `DISABLE_ADMIN_AUTH=1`. This is unsafe; never use in production. ### Manual setup **Start the database and the server:** ```bash # Start a Postgres database container docker run -d --name postgres-test -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres postgres # Apply the DB migrations for file in schema/*.sql; do psql "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" -f $file; done # Start the server go run cmd/httpserver/main.go ``` ### Using Docker - See instructions on using Docker to run the full stack at [`docs/devenv-setup.md`](./docs/devenv-setup.md) - Also check out the [`docker-compose.yaml`](../docker/docker-compose.yaml) file, which sets up the BuilderHub, a mock proxy, and a Postgres database. - Finally, there's an e2e api spec test suite you can run: [`./scripts/ci/integration-test.sh`](./scripts/ci/integration-test.sh) ### Example requests ```bash # Public endpoints curl localhost:8080/api/l1-builder/v1/measurements # client-aTLS secured endpoints curl localhost:8080/api/l1-builder/v1/builders curl localhost:8080/api/l1-builder/v1/configuration curl -X POST localhost:8080/api/l1-builder/v1/register_credentials/rbuilder ``` ### Testing Run test suite with database tests included: ```bash RUN_DB_TESTS=1 make test ``` --- ## Contributing **Install dev dependencies** ```bash go install mvdan.cc/gofumpt@v0.4.0 go install honnef.co/go/tools/cmd/staticcheck@v0.4.2 go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3 go install go.uber.org/nilaway/cmd/nilaway@v0.0.0-20240821220108-c91e71c080b7 go install github.com/daixiang0/gci@v0.11.2 ``` **Lint, test, format** ```bash make lint make test make fmt ``` --- ## API Documentation `BuilderHub` exposes a JSON+REST API with these methods: | API | Exposure | Authentication | Requested by | Served by | | ------------------------ | -------- | ---------------------------- | ----------------------------- | ---------------------- | | Get Secrets + Config | TDX Node | IP + Client-ATLS Attestation | Builder (via cvm-proxy) | cvm-proxy → BuilderHub | | Register Credentials | TDX Node | IP + Client-ATLS Attestation | Builder (via cvm-proxy) | cvm-proxy → BuilderHub | | Get Active Builders | TDX Node | IP + Client-ATLS Attestation | Builder (via cvm-proxy) | cvm-proxy → BuilderHub | | Get Active Builders | Internal | HTTP Basic Auth | MEV-Share, block processor, … | BuilderHub | | Get Allowed Measurements | Internal | HTTP Basic Auth | Users, Builders | nginx → BuilderHub | | Admin Endpoints | Internal | HTTP Basic Auth | | | See also a [Bruno collection](https://www.usebruno.com/) (Postman alternative) in [`docs/api-docs/`](./docs/api-docs/). --- ### Get Secrets + Configuration `GET /api/l1-builder/v1/configuration` Auth: - IP + Client-ATLS Attestation Response: [testdata/get-configuration.json](https://github.com/flashbots/builder-config-hub/blob/main/testdata/get-configuration.json) --- ### Register Credentials `POST /api/l1-builder/v1/register_credentials/` Auth: - IP + Client-ATLS Attestation Request: - [service: `instance`]: TLS cert ```json { "tls_cert": string (\n instead of newlines) } ``` - [service: `orderflow_proxy`]: ECDSA pubkey address (for orderflow) ```json { "ecdsa_pubkey_address": string } ``` - [service: `rbuilder`]: ECDSA pubkey address (for bids) ```json { "ecdsa_pubkey_address": string } ``` Response: 200 OK --- ### Get Active Builders `GET /api/l1-builder/v1/builders` (external, requests from builder via cvm-proxy) `GET /api/internal/l1-builder/v1/builders` (internal, no auth, uses `production` network by default) `GET /api/internal/l1-builder/v2/network/{network}/builders` (internal, no auth, for specific peer network) Response: [testdata/get-builders.json](https://github.com/flashbots/builder-config-hub/blob/main/testdata/get-builders.json) --- ### Get Allowed Measurements Auth: public `GET /api/l1-builder/v1/measurements` Response: Array with currently allowed measurement JSONs [testdata/get-measurements.json](https://github.com/flashbots/builder-config-hub/blob/main/testdata/get-measurements.json) --- ## Admin Endpoints ### Add measurements (created disabled by default) `POST /api/admin/v1/measurements` Payload ```json { "measurement_id": "v1.2.3-20241010-rc1", "attestation_type": "azure-tdx", "measurements": { "11": { "expected_any": ["efa43e0beff151b0f251c4abf48152382b1452b4414dbd737b4127de05ca31f7", "abc123..."] }, "4": { "expected": "ea92ff762767eae6316794f1641c485d4846bc2b9df2eab6ba7f630ce6f4d66f" } } } ``` Two fields are available for specifying expected values: - `expected` (string): A single expected value - `expected_any` (array): Multiple expected values - any matching value is accepted (OR semantics) Use `expected_any` when multiple firmware versions or configurations should be accepted for a single measurement key. Note that only the measurements given are expected, and any non-present will be ignored. To allow _any_ measurement, use an empty measurements field: `"measurements": {}`. ```json { "measurement_id": "test-blanket-allow", "attestation_type": "azure-tdx", "measurements": {} } ``` ### Enable/disable measurements `POST /api/admin/v1/measurements/activation/{measurement_id}` ```json { "enabled": true } ``` ### Adding a new builder instance (created inactive by default) `POST /api/admin/v1/builders/` Payload ```json { "name": string, "ip_address": string } ``` ### Enable/disable builder instance `POST /api/admin/v1/builders/activation/{builder_name}` ```json { "enabled": true } ``` Errors: - if no active configuration available ### Get builder configuration `GET /api/admin/v1/builders/configuration/{builder_name}/active` gets always the latest/active configuration ### Get builder configuration with secrets `GET /api/admin/v1/builders/configuration/{builder_name}/full` gets always the latest/active configuration ### Update builder configuration if valid JSON, will create a new active configuration and disable the old configuration `POST /api/admin/v1/builders/configuration/{builder_name}` ```json { ... } ``` ### Update secrets configuration POST `/api/admin/v1/builders/secrets/{builderName}` Payload: JSON with secrets, both flattened/unflattened ```json { ... } ```