Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b7055ea
Bump gcp measure script version
alexhulbert May 28, 2026
60561c7
Merge pull request #156 from flashbots/ah/bump-gcp-measure
niccoloraspa Jun 3, 2026
70f2d20
Prometheus metrics setup (#125)
pablin-10 Jun 3, 2026
16f419f
Update readme for flashbox-l1 with GCP pricing (#159)
pablin-10 Jun 10, 2026
09abcd1
Decouple ssh-pubkey-server from the searcher container
MoeMahhouk Jun 10, 2026
d3b6c91
Merge pull request #160 from flashbots/moe/fix-attestation-tofu-decou…
niccoloraspa Jun 10, 2026
7e4eeeb
Add measurements format conversion helper (#161)
pablin-10 Jun 11, 2026
edea6da
Use attested-tls-proxy rather than cvm-reverse-proxy
ameba23 Jun 15, 2026
ea0ee53
Use 2.0.0 pre-release of attested-tls-proxy
ameba23 Jun 15, 2026
d8458c1
flashbox-l1: allowlist BuilderNet bottom-of-block endpoints (#163)
MoeMahhouk Jun 16, 2026
d006fa2
Bump gcp measure script version (#164)
alexhulbert Jun 17, 2026
11d403f
flashbox-l1: drop BuilderNet endpoints in maintenance mode (#165)
MoeMahhouk Jun 17, 2026
d894f1a
Use latest release of attested-tls-proxy
ameba23 Jun 19, 2026
757c832
Update firewall port naming and readme
ameba23 Jun 22, 2026
80be581
Revert change to mkosi.conf used for testing
ameba23 Jun 22, 2026
7a17c13
Typo, tidy readme
ameba23 Jun 22, 2026
52ebc50
flashbox-l1: document BuilderNet bottom-of-block in readme (#166)
MoeMahhouk Jun 23, 2026
2e3a88c
flashbox-l1: remove Titan Builder bottom-of-block endpoints (#167)
MoeMahhouk Jun 23, 2026
322227a
Merge pull request #168 from flashbots/peg/flashbox-use-attested-tls-…
ameba23 Jun 29, 2026
7bc5e48
Add reusable build+publish workflow for flashbox-l1 (#173)
pablin-10 Jul 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 276 additions & 0 deletions .github/workflows/_build-and-publish-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
# Reusable GitHub Actions workflow for building and publishing mkosi images.
#
# Builds the specified image, uploads the artifacts to R2, and creates a draft
# GitHub Release when the build is driven by a release tag.
#
# Behavior:
# - Release tag (<image-id>-vX.Y.Z, pushed or passed as `ref`):
# - Builds the image at that tag
# - Uploads artifacts to R2 under <image-id>-images/vX.Y.Z/
# - Creates a draft GitHub Release with measurements
#
# - Any other ref (branch, SHA, or empty):
# - Builds a dev image at that ref
# - Uploads artifacts to R2 under <image-id>-images/dev/<branch>.<sha>/
# - No release is created

name: Build and publish image

on:
workflow_call:
inputs:
image-id:
description: "Image to build (images/<id>.conf)."
type: string
required: true
ref:
description: "Tag, SHA, or branch to build. A release tag selects release mode; anything else is a dev build. Empty = the triggering ref."
type: string
default: ""
dev-image:
description: "Build a dev image (devtools profile) via `make build-dev`. Produces <image-id>-dev_* artifacts."
type: boolean
default: false

permissions:
contents: write

jobs:
build:
name: Build ${{ inputs.image-id }}
runs-on: warp-ubuntu-latest-x64-32x
env:
R2_BUCKET: flashbots-public-artifacts
R2_PUBLIC_BASE_URL: "https://builder-artifacts.flashbots.net"
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ inputs.ref }}

- name: Install Nix
uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
experimental-features = nix-command flakes

# Use Nix to build image
- name: Bypass Lima wrapper
run: touch .bypass-lima

- name: Enable user namespaces
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

# A build is release when an <image-id>-vX.Y.Z tag is pushed or passed as `ref`
# Everything else is a dev build.
- name: Determine build mode
env:
IMAGE: ${{ inputs.image-id }}
REF_INPUT: ${{ inputs.ref }}
DEV_IMAGE: ${{ inputs.dev-image }}
run: |
set -euo pipefail
TAG_RE="^${IMAGE}-v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$"

RELEASE_TAG=""
if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF_TYPE}" == "tag" ]]; then
if ! [[ "${GITHUB_REF_NAME}" =~ ${TAG_RE} ]]; then
echo "::error::pushed tag '${GITHUB_REF_NAME}' doesn't match ${IMAGE}-vX.Y.Z[-suffix]"
exit 1
fi
RELEASE_TAG="${GITHUB_REF_NAME}"
elif [[ "${REF_INPUT}" =~ ${TAG_RE} ]]; then
RELEASE_TAG="${REF_INPUT}"
fi

if [[ -n "${RELEASE_TAG}" && "${DEV_IMAGE}" == "true" ]]; then
echo "::error::dev-image builds cannot be released"
exit 1
fi

# build-dev builds under image id <image-id>-dev, which changes the
# artifact filenames.
if [[ "${DEV_IMAGE}" == "true" ]]; then
ARTIFACT_PREFIX="${IMAGE}-dev"
else
ARTIFACT_PREFIX="${IMAGE}"
fi

if [[ -n "${RELEASE_TAG}" ]]; then
VERSION_DIR="v${RELEASE_TAG#"${IMAGE}"-v}"
IMAGE_VERSION="" # mkosi.version supplies the version
else
# GITHUB_SHA differs from the checked-out HEAD when a custom ref is passed.
HEAD_SHA="$(git rev-parse HEAD)"
# Lowercase to [a-z0-9-] so the GCS image name and R2 path stay valid.
BRANCH="$(printf '%s' "${REF_INPUT:-${GITHUB_REF_NAME}}" \
| tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+|-+$//g')"
# env_wrapper.sh's latest.* symlink handling requires a version
# containing a dot.
IMAGE_VERSION="${BRANCH}.${HEAD_SHA:0:8}"
VERSION_DIR="dev/${IMAGE_VERSION}"
fi

{
echo "IMAGE=${IMAGE}"
echo "ARTIFACT_PREFIX=${ARTIFACT_PREFIX}"
echo "RELEASE_TAG=${RELEASE_TAG}"
echo "R2_PATH_PREFIX=${IMAGE}-images"
echo "VERSION_DIR=${VERSION_DIR}"
echo "IMAGE_VERSION=${IMAGE_VERSION}"
} >> "$GITHUB_ENV"
echo "mode: RELEASE_TAG=${RELEASE_TAG:-<dev build>}, VERSION_DIR=${VERSION_DIR}"

# MKOSI_EXTRA_ARGS is picked up and appended to mkosi by env_wrapper.sh.
- name: Build image
env:
DEV_IMAGE: ${{ inputs.dev-image }}
run: |
set -euo pipefail
umask 022
if [[ -n "${IMAGE_VERSION}" ]]; then
export MKOSI_EXTRA_ARGS="--image-version=${IMAGE_VERSION}"
fi
if [[ "${DEV_IMAGE}" == "true" ]]; then
make build-dev IMAGE="${IMAGE}"
else
make build IMAGE="${IMAGE}"
fi

- name: Fix permissions
run: sudo chown -R "$(id -u):$(id -g)" build/

- name: Export measurements (GCP TDX)
run: |
set -euo pipefail
make measure-gcp

- name: Show build artifacts
run: ls -lh build/

- name: Install rclone
run: |
set -euo pipefail
RCLONE_DEB=rclone-v1.74.2-linux-amd64.deb
curl -fsSL -o "${RCLONE_DEB}" "https://downloads.rclone.org/v1.74.2/${RCLONE_DEB}"
echo "d2232f57e47ff4f9b30f1b575ce95567b1c51f22526c4e98814641dfd835c103 ${RCLONE_DEB}" | sha256sum -c -
sudo apt-get install -y "./${RCLONE_DEB}"
rm -f "${RCLONE_DEB}"

- name: Upload artifacts to R2
env:
R2_ACCESS_KEY: ${{ secrets.R2_FLASHBOTS_PUBLIC_ARTIFACTS_ACCESS_KEY }}
R2_SECRET_KEY: ${{ secrets.R2_FLASHBOTS_PUBLIC_ARTIFACTS_SECRET_KEY }}
R2_ENDPOINT: ${{ secrets.R2_FLASHBOTS_PUBLIC_ARTIFACTS_ENDPOINT }}
run: |
set -euo pipefail
mkdir -p ~/.config/rclone
# use_data_integrity_protections=false: R2 returns 501 NotImplemented
# for the CRC32 checksum trailers aws-sdk-go-v2 sends by default.
cat > ~/.config/rclone/rclone.conf <<EOF
[r2]
type = s3
provider = Cloudflare
access_key_id = ${R2_ACCESS_KEY}
secret_access_key = ${R2_SECRET_KEY}
region = auto
endpoint = ${R2_ENDPOINT}
acl = private
use_data_integrity_protections = false
EOF
# Upload only the image artifacts and measurements.
rclone copy -P --retries 3 --retries-sleep 20s --error-on-no-transfer \
--s3-upload-concurrency=8 --transfers=8 \
--include "${ARTIFACT_PREFIX}_*.{efi,tar.gz,qcow2}" \
--include "gcp_measurements.json" \
build "r2:${R2_BUCKET}/${R2_PATH_PREFIX}/${VERSION_DIR}/"

- name: Create draft GitHub Release
if: ${{ env.RELEASE_TAG != '' }}
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

shopt -s nullglob
HEAD_SHA="$(git rev-parse HEAD)"
COMMIT_SHORT="${HEAD_SHA:0:7}"
BUILT="$(date -u '+%Y-%m-%d %H:%M UTC')"
VERSION="${RELEASE_TAG#"${IMAGE}"-v}"
{
echo "## 🚀 \`${IMAGE}\`: ${RELEASE_TAG}"
echo ""
echo "| | |"
echo "|--:|:--|"
echo "| 📦 **Image** | \`${IMAGE}\` |"
echo "| 🏷️ **Version** | \`${VERSION}\` |"
echo "| 🔖 **Commit** | [\`${COMMIT_SHORT}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${HEAD_SHA}) |"
echo "| 📅 **Built** | ${BUILT} |"
echo ""
echo "### 💿 Images"
echo ""
echo "| Target | File | Size |"
echo "|---|---|--:|"
for f in build/"${ARTIFACT_PREFIX}"_*.efi build/"${ARTIFACT_PREFIX}"_*.tar.gz build/"${ARTIFACT_PREFIX}"_*.qcow2; do
n="$(basename "$f")"
sz="$(du -h "$f" | cut -f1)"
case "$n" in
*.efi) t="🐧 UKI (EFI)" ;;
*.tar.gz) t="☁️ GCP disk" ;;
*.qcow2) t="🧪 QEMU qcow2" ;;
*) t="📄 artifact" ;;
esac
echo "| ${t} | [\`${n}\`](${R2_PUBLIC_BASE_URL}/${R2_PATH_PREFIX}/${VERSION_DIR}/${n}) | ${sz} |"
done
echo ""
echo "> [!NOTE]"
echo "> **\`gcp_measurements.json\`** (attached, also on R2): the expected TDX measurements (MRTD, RTMR0-3) for this image on GCP, from [\`dstack-mr-gcp\`](https://github.com/flashbots/dstack-mr-gcp). Reproduce them yourself with the steps below."
echo ">"
echo "> 📖 Details: [attestation walkthrough](https://github.com/flashbots/flashbots-images/blob/main/modules/flashbox/flashbox-l1/readme.md#attestation-walkthrough) · [cvm-reverse-proxy](https://github.com/flashbots/cvm-reverse-proxy)"
echo ""
echo "### 🔁 Reproduce the measurements"
echo ""
echo "1. Clone and check out this release:"
echo ' ```bash'
echo " git clone ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} && cd ${GITHUB_REPOSITORY#*/}"
echo " git checkout ${RELEASE_TAG}"
echo ' ```'
echo "2. Build the image (~30 min):"
echo ' ```bash'
echo " make build IMAGE=${IMAGE}"
echo ' ```'
echo "3. Reproduce the measurements:"
echo ' ```bash'
echo " make measure-gcp"
echo ' ```'
echo " Your \`build/gcp_measurements.json\` should be identical to the attached one."
echo ""
echo "### 🔐 Attest your running VM"
echo ""
echo "To verify your running VM, generate an Intel-signed TDX quote and validate it against expected measurements using [\`cvm-reverse-proxy\`](https://github.com/flashbots/cvm-reverse-proxy) over attested TLS."
echo ""
echo "1. Set your instance IP and build the client:"
echo ' ```bash'
echo " VM_IP=<your-instance-ip>"
echo " git clone https://github.com/flashbots/cvm-reverse-proxy && make -C cvm-reverse-proxy build-proxy-client"
echo ' ```'
echo "2. Convert your measurements to the proxy's format (temporary [jq shim](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/blob/${RELEASE_TAG}/scripts/gcp_measurements_to_dcap.sh)):"
echo ' ```bash'
echo " scripts/gcp_measurements_to_dcap.sh > gcp-server-measurements.json"
echo ' ```'
echo "3. Attest:"
echo ' ```bash'
echo " cvm-reverse-proxy/build/proxy-client --server-measurements gcp-server-measurements.json --target-addr=https://\${VM_IP}:8745 &"
echo " curl -si http://127.0.0.1:8080 | grep -q X-Flashbots-Measurement && echo '✅ VM matches YOUR build' || echo '❌ mismatch'"
echo ' ```'
} > release-notes.md

# Attach the measurements and the .efi UKI; the larger tar.gz/qcow2
# artifacts stay in R2 and are linked in the release body.
assets=()
for f in build/gcp_measurements.json build/"${ARTIFACT_PREFIX}"_*.efi; do
[ -e "$f" ] && assets+=("$f")
done

gh release create "${RELEASE_TAG}" --draft \
--title "${RELEASE_TAG}" --notes-file release-notes.md "${assets[@]}"
5 changes: 2 additions & 3 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# - Reproducibility test (default: false)
# - Adds a second build on a separate runner and compares SHA256 hashes


name: Build mkosi images

on:
Expand Down Expand Up @@ -87,7 +86,7 @@ jobs:
name: build ${{ matrix.image }} image
runs-on: warp-ubuntu-latest-x64-32x
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7
with:
ref: ${{ inputs.branch || github.ref }}

Expand Down Expand Up @@ -150,7 +149,7 @@ jobs:
name: reprotest ${{ matrix.image }}
runs-on: warp-ubuntu-latest-x64-32x
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7
with:
ref: ${{ inputs.branch || github.ref }}

Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/flashbox-l1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# GitHub Actions workflow for building and publishing the flashbox-l1 image.
#
# Triggers on:
# - Pushes of a `flashbox-l1-v*` tag: builds the image, uploads the artifacts
# to R2, and creates a draft GitHub Release
#
# - Manual dispatch: allows specifying:
# - Ref to build (default: the current branch)
# - Branch or SHA → dev build, uploaded to R2 only (no release)
# - Existing release tag → deterministic rebuild and a new draft Release
# - Dev image (default: false)
# - Builds with the devtools profile (e.g. SSH access); never released
#
# All build and publish logic lives in the reusable workflow
# _build-and-publish-image.yaml; this file only sets the image id.

name: flashbox-l1

on:
push:
tags:
- "flashbox-l1-v*"
workflow_dispatch:
inputs:
ref:
description: "Tag, SHA, or branch to build (empty = this branch)."
type: string
default: ""
dev-image:
description: "Build a dev image (devtools profile)"
type: boolean
default: false

jobs:
release:
permissions:
contents: write
uses: ./.github/workflows/_build-and-publish-image.yaml
with:
image-id: flashbox-l1
# Inputs are empty on tag pushes; fall back to the defaults.
ref: ${{ inputs.ref || '' }}
dev-image: ${{ inputs.dev-image || false }}
secrets: inherit
4 changes: 2 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
src = pkgs.fetchFromGitHub {
owner = "flashbots";
repo = "dstack-mr-gcp";
rev = "ecf3284b72a507fd005de91d49f7372490cf6995";
sha256 = "sha256-1rcm9sIZuvCojNN2HMPrsECYn9sd8eVChsgwleo8nFY=";
rev = "b16e08b32b3dc8f1af7087e12f9970dc91a0b9a0";
sha256 = "sha256-3KIKgWsDzmLXuRK9YVxX2zJ6jAlZSmRm/bLYE1kJY7k=";
};
vendorHash = "sha256-glOyRTrIF/zP78XGV+v58a1Bec6C3Fvc5c8G3PglzPM=";
};
Expand Down
1 change: 1 addition & 0 deletions images/flashbox-l1.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Include=shared/mkosi.conf
Include=modules/flashbox/common/mkosi.conf
Include=modules/flashbox/flashbox-l1/mkosi.conf
Include=modules/flashbox/observability/mkosi.conf

[Config]
Profiles=azure,gcp
Expand Down
15 changes: 8 additions & 7 deletions modules/flashbox/common/mkosi.build
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@ make_git_package \
# Build ssh-pubkey-server
make_git_package \
"ssh-pubkey-server" \
"multi-key" \
"v0.1.0" \
"https://github.com/flashbots/ssh-pubkey-server" \
'go build -trimpath -ldflags "-s -w -buildid= -X github.com/flashbots/go-template/common.Version=v1.0.0" -o ./build/ssh-pubkey-server cmd/httpserver/main.go' \
"build/ssh-pubkey-server:/usr/bin/ssh-pubkey-server"

make_git_package \
"cvm-reverse-proxy" \
"v0.1.8" \
"https://github.com/flashbots/cvm-reverse-proxy" \
"make build-proxy-server" \
"build/proxy-server:/usr/bin/cvm-reverse-proxy"

# Install attested-tls-proxy
VERSION="v1.1.3"
EXPECTED_SHA256=3d3f43203bd51be399fe90ab5c633716cbecba0cb30beff291052b748610b65b
curl -sSfL https://github.com/flashbots/attested-tls-proxy/releases/download/${VERSION}/attested-tls-proxy_1.${VERSION}_amd64.deb \
-o $PACKAGEDIR/attested-tls-proxy.deb
echo "${EXPECTED_SHA256}" $PACKAGEDIR/attested-tls-proxy.deb | sha256sum --check

# Build input-only-proxy
build_rust_package \
Expand Down
Loading