From c8f90135ace84c0bf3852ca82b28b1cd983a7568 Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 24 Aug 2026 15:09:50 +0200 Subject: [PATCH] tap: attesto 0.5.0 + attesto-local-vault 2.0.1 Mirrored from attesto-v1 ops/get-host/homebrew/ (commit f21339ce); hashes from the KMS-signed channel manifests on get.attesto.eu. Co-Authored-By: Claude Fable 5 --- Formula/attesto-local-vault.rb | 178 +++++++++++++++++++++++++++++++++ Formula/attesto.rb | 68 +++++++++++++ README.md | 64 ++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 Formula/attesto-local-vault.rb create mode 100644 Formula/attesto.rb create mode 100644 README.md diff --git a/Formula/attesto-local-vault.rb b/Formula/attesto-local-vault.rb new file mode 100644 index 0000000..c56d508 --- /dev/null +++ b/Formula/attesto-local-vault.rb @@ -0,0 +1,178 @@ +# Attesto Local Vault — container-wrapper formula. +# +# The Local Vault ships ONLY as a container image, pinned by digest. This +# formula does not install a native vault; it installs a small POSIX-sh +# wrapper that runs every CLI subcommand inside that image via Docker. +# +# PROVENANCE OF THE sha256 VALUE: the url below is the signed installer copy +# from the versioned channel, and its hash is copied verbatim from the SIGNED +# manifest https://get.attesto.eu/local-vault/2.0.1/SHA256SUMS after +# verifying its cosign signature: +# +# curl -fsSLO https://get.attesto.eu/cosign.pub +# curl -fsSLO https://get.attesto.eu/local-vault/2.0.1/SHA256SUMS +# curl -fsSLO https://get.attesto.eu/local-vault/2.0.1/SHA256SUMS.sig +# cosign verify-blob --key cosign.pub --insecure-ignore-tlog \ +# --signature SHA256SUMS.sig SHA256SUMS +# +# (--insecure-ignore-tlog: KMS release key, no Rekor entry — the project's +# documented verification flow.) +# +# SYNC CONTRACT: the wrapper heredoc below must stay behaviorally identical +# (same docker argv) to the wrapper embedded in the channel installer +# (ops/get-host/local-vault/install.sh in the main repo, served as +# https://get.attesto.eu/local-vault/install.sh). The check +# tests/wrapper_sync_check.sh in the tap source tree runs both against a +# stub docker and diffs the argv; it extracts the heredoc textually, so keep +# the <<~'WRAPPER' markers and the 6-space body indentation. +class AttestoLocalVault < Formula + desc "Container wrapper for the Attesto Local Vault (digest-pinned image)" + homepage "https://attesto.eu" + url "https://get.attesto.eu/local-vault/2.0.1/install.sh" + sha256 "35770b22886f04a7021d6b854d21f1208092fa02eca5d9c8762218ced4861ae5" + license "Apache-2.0" + + def install + # The staged download is the signed channel installer. It is kept for + # reference/out-of-band verification only — never executed; the wrapper + # written below is the Homebrew-native equivalent of the wrapper that + # installer would write. + pkgshare.install "install.sh" + + (bin/"attesto-local-vault").write wrapper_script + chmod 0755, bin/"attesto-local-vault" + end + + def caveats + <<~EOS + attesto-local-vault is a container wrapper: it needs a working Docker + engine at runtime (Docker Desktop on macOS, Docker Engine on Linux). + Homebrew does not and cannot install Docker for you. + + Honesty note for macOS: the Local Vault and its Linux-only provider + sandbox run inside Docker Desktop's Linux VM. Attesto's isolation + claims apply within that VM — a native macOS process would not carry + them, which is exactly why this install is a container wrapper and + never claims native isolation. + + The first run pulls the digest-pinned image and prepares the state and + config directories under + ${XDG_DATA_HOME:-~/.local/share}/attesto-local-vault + (override the root with ATTESTO_LOCAL_VAULT_HOME). The state directory + holds the vault's keys, spool, and provenance capsules — deleting it + destroys evidence that may exist nowhere else. + + Get started (mint a single-use enrollment token in the console): + attesto-local-vault init --base-url https://verify.attesto.eu --enrollment-token lvet_... + attesto-local-vault doctor + EOS + end + + test do + # The wrapper must at least parse. + system "sh", "-n", bin/"attesto-local-vault" + + # Run it against a stub docker and inspect the argv it produces: the + # digest-pinned image, the state/config mounts, and our arguments must + # all be there. + (testpath/"stub").mkpath + (testpath/"stub/docker").write <<~STUB + #!/bin/sh + printf '%s\\n' "$@" >> "#{testpath}/docker-argv.log" + exit 0 + STUB + chmod 0755, testpath/"stub/docker" + ENV.prepend_path "PATH", testpath/"stub" + ENV["ATTESTO_LOCAL_VAULT_HOME"] = (testpath/"vault-home").to_s + + system bin/"attesto-local-vault", "--help" + + log = (testpath/"docker-argv.log").read + assert_match "git.attesto.eu/attesto/local-vault@sha256:" \ + "5799e7f0669c9b42a55a338f4233cfa5afb5aa6a2627c2c50f7545a41bff1551", log + assert_match "--help", log + assert_match "/var/lib/attesto", log + assert_match "/etc/attesto/local-vault", log + end + + private + + # Kept in sync with the wrapper written by install.sh — see the SYNC + # CONTRACT comment at the top of this file. The single-quoted chown script + # inside the bootstrap block must stay byte-identical to install.sh's + # (including its 4-space continuation line): it is a single docker argv + # element and the sync check compares argv exactly. + def wrapper_script + <<~'WRAPPER' + #!/usr/bin/env sh + # attesto-local-vault — container wrapper installed by Homebrew + # (attesto/attesto tap). Every argument is passed to the CLI inside the + # image; stdin/stdout and the exit code pass through. + # ATTESTO_LOCAL_VAULT_IMAGE overrides the image for one invocation; all + # other ATTESTO_LOCAL_VAULT_* environment variables are forwarded into + # the container. Kept behaviorally identical (same docker argv) to the + # wrapper written by https://get.attesto.eu/local-vault/install.sh. + set -eu + + IMAGE="${ATTESTO_LOCAL_VAULT_IMAGE:-git.attesto.eu/attesto/local-vault@sha256:5799e7f0669c9b42a55a338f4233cfa5afb5aa6a2627c2c50f7545a41bff1551}" + ROOT_DIR="${ATTESTO_LOCAL_VAULT_HOME:-${XDG_DATA_HOME:-$HOME/.local/share}/attesto-local-vault}" + STATE_DIR="$ROOT_DIR/data" # mounted at /var/lib/attesto + CONFIG_DIR="$ROOT_DIR/config" # mounted at /etc/attesto/local-vault + + if ! command -v docker >/dev/null 2>&1; then + echo "attesto-local-vault: docker is required but was not found on PATH." >&2 + echo "Install Docker Desktop (macOS) or Docker Engine (Linux) and retry." >&2 + exit 1 + fi + + # First run: create the state+config dirs and hand them to the image's + # unprivileged user, exactly as the channel installer does at install + # time. On Docker Desktop (macOS) the chown is a harmless no-op. The + # quoted -c script must stay byte-identical to install.sh's. + if [ ! -d "$STATE_DIR" ] || [ ! -d "$CONFIG_DIR" ]; then + mkdir -p "$STATE_DIR" "$CONFIG_DIR" + docker run --rm --user root --entrypoint /bin/sh \ + -v "$STATE_DIR:/var/lib/attesto" \ + -v "$CONFIG_DIR:/etc/attesto/local-vault" \ + "$IMAGE" -c 'chown attesto:attesto-provider /var/lib/attesto /etc/attesto/local-vault \ + && install -d -o attesto -g attesto-provider -m 2770 /var/lib/attesto/provider-work /var/lib/attesto/run' + fi + + tty_flags="" + if [ -t 0 ] && [ -t 1 ]; then tty_flags="-t"; fi + + # Forward ATTESTO_LOCAL_VAULT_* env vars (after the wrapper defaults, so an + # explicit host value wins). The wrapper's own image knob stays on the host, + # and the container config dir is always the mounted path. + env_flags="" + for name in $(env | sed -n 's/^\(ATTESTO_LOCAL_VAULT_[A-Za-z0-9_]*\)=.*/\1/p'); do + case "$name" in + ATTESTO_LOCAL_VAULT_IMAGE|ATTESTO_LOCAL_VAULT_CONFIG_DIR) continue ;; + esac + env_flags="$env_flags -e $name" + done + + run() { + # shellcheck disable=SC2086 # tty_flags/env_flags are token-safe + exec docker run --rm -i $tty_flags \ + -v "$STATE_DIR:/var/lib/attesto" \ + -v "$CONFIG_DIR:/etc/attesto/local-vault" \ + -e ATTESTO_LOCAL_VAULT_CONFIG_DIR=/etc/attesto/local-vault \ + -e ATTESTO_LOCAL_VAULT_SPOOL_DB=/var/lib/attesto/spool.sqlite3 \ + -e ATTESTO_LOCAL_VAULT_FINALIZED_QUEUE_DB=/var/lib/attesto/finalized-evidence.sqlite3 \ + -e ATTESTO_LOCAL_VAULT_CAPSULE_STORE_DB=/var/lib/attesto/provenance-capsules.sqlite3 \ + -e ATTESTO_LOCAL_VAULT_WITNESS_DB=/var/lib/attesto/witness.sqlite3 \ + $env_flags \ + "$@" + } + + # Mount the caller's working directory so file arguments (attestation + # files, JSON payloads) resolve transparently. Skipped for / and for + # paths a -v flag cannot express. + case "$PWD" in + /|*:*) run "$IMAGE" "$@" ;; + *) run -v "$PWD:/workdir" -w /workdir "$IMAGE" "$@" ;; + esac + WRAPPER + end +end diff --git a/Formula/attesto.rb b/Formula/attesto.rb new file mode 100644 index 0000000..a5d705b --- /dev/null +++ b/Formula/attesto.rb @@ -0,0 +1,68 @@ +# Attesto CLI — offline verifier for Attesto evidence. +# +# PROVENANCE OF THE sha256 VALUES: every hash below is copied verbatim from +# the SIGNED channel manifest https://get.attesto.eu/0.5.0/SHA256SUMS after +# verifying its cosign signature: +# +# curl -fsSLO https://get.attesto.eu/cosign.pub +# curl -fsSLO https://get.attesto.eu/0.5.0/SHA256SUMS +# curl -fsSLO https://get.attesto.eu/0.5.0/SHA256SUMS.sig +# cosign verify-blob --key cosign.pub --insecure-ignore-tlog \ +# --signature SHA256SUMS.sig SHA256SUMS +# +# (--insecure-ignore-tlog: the release key is a KMS key and signatures are +# not uploaded to Rekor; this matches the project's documented verification +# flow.) Do not replace these hashes with locally computed ones. +class Attesto < Formula + desc "Offline verifier CLI for Attesto evidence (receipts, bundles, anchors)" + homepage "https://attesto.eu" + version "0.5.0" + license "Apache-2.0" + + # KNOWN AUDIT WAIVER: `brew audit --strict` reports "Use `url :stable`" for + # the livecheck url below. That is a false positive in the + # FormulaAudit/LivecheckUrlSymbol cop: with every stable `url` inside + # on_macos/on_linux blocks, the cop takes the FIRST `url` call in the class + # body as the stable URL — which is this livecheck url itself, so it always + # self-matches, whatever string it holds. Following the suggestion would be + # wrong (livecheck would scan a binary download instead of the version + # feed), and moving `livecheck` after the on_* blocks trips ComponentsOrder + # instead. Canonical order is kept; the finding is waived. + livecheck do + url "https://get.attesto.eu/latest-version.txt" + regex(/^v?(\d+(?:\.\d+)+)$/i) + end + + on_macos do + on_arm do + url "https://get.attesto.eu/0.5.0/attesto_0.5.0_darwin_arm64" + sha256 "8fd7a0044d5d042cd63f7972d80c78a099f5880050933a718bcdae9429e76815" + end + on_intel do + url "https://get.attesto.eu/0.5.0/attesto_0.5.0_darwin_amd64" + sha256 "ddc4d82ac9ecf8c85abee0f6300e305288d590f5b76723445ca76137a6d17dbf" + end + end + + on_linux do + on_arm do + url "https://get.attesto.eu/0.5.0/attesto_0.5.0_linux_arm64" + sha256 "bce90fd4714e61fc2b95813b6935cceadf8b5346764c5ab4c3c968aab511d5c6" + end + on_intel do + url "https://get.attesto.eu/0.5.0/attesto_0.5.0_linux_amd64" + sha256 "65ec45c16096556eea86604ba61a15f5934874e5f92dc24de194657609e45bc8" + end + end + + def install + os = OS.mac? ? "darwin" : "linux" + arch = Hardware::CPU.arm? ? "arm64" : "amd64" + bin.install "attesto_#{version}_#{os}_#{arch}" => "attesto" + chmod 0755, bin/"attesto" + end + + test do + assert_match version.to_s, shell_output("#{bin}/attesto version") + end +end diff --git a/README.md b/README.md new file mode 100644 index 0000000..d5aaf98 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# attesto/attesto — Homebrew tap + +Homebrew tap for [Attesto](https://attesto.eu): the `attesto` verifier CLI and +the `attesto-local-vault` container wrapper. Artifacts are served from the +signed download host [get.attesto.eu](https://get.attesto.eu). + +## Install + +```sh +brew tap attesto/attesto https://git.rotz.ai/attesto/homebrew-attesto.git +brew install attesto # the CLI (offline verifier) +brew install attesto-local-vault # the Local Vault container wrapper +``` + +## What is signed, and how to verify independently + +Every release channel on get.attesto.eu carries a `SHA256SUMS` manifest and a +cosign signature over it (`SHA256SUMS.sig`). The `sha256` stanzas in the +formulas of this tap are copied verbatim from those **signed** manifests — +never computed locally. Homebrew then enforces the same hashes on every +install. + +You do not have to trust this tap. Verify the manifests yourself: + +```sh +curl -fsSLO https://get.attesto.eu/cosign.pub + +# CLI channel +curl -fsSLO https://get.attesto.eu/0.5.0/SHA256SUMS +curl -fsSLO https://get.attesto.eu/0.5.0/SHA256SUMS.sig +cosign verify-blob --key cosign.pub --insecure-ignore-tlog \ + --signature SHA256SUMS.sig SHA256SUMS + +# Local Vault channel +curl -fsSLO https://get.attesto.eu/local-vault/2.0.1/SHA256SUMS +curl -fsSLO https://get.attesto.eu/local-vault/2.0.1/SHA256SUMS.sig +cosign verify-blob --key cosign.pub --insecure-ignore-tlog \ + --signature SHA256SUMS.sig SHA256SUMS +``` + +then compare the listed hashes with the `sha256` values in `Formula/*.rb`. +(`--insecure-ignore-tlog` is required because the release key is a KMS key +and signatures are not uploaded to the Rekor transparency log.) + +The Local Vault container image itself is referenced **by digest**, not by a +tag, so the wrapper can only ever run the exact image +`git.attesto.eu/attesto/local-vault@sha256:5799e7f0669c9b42a55a338f4233cfa5afb5aa6a2627c2c50f7545a41bff1551`. + +## macOS honesty note + +`attesto-local-vault` requires Docker (Docker Desktop on macOS) at runtime — +Homebrew does not install it for you. On macOS the vault and its Linux-only +provider sandbox run **inside Docker Desktop's Linux VM**. Attesto's +isolation claims apply within that VM; a native macOS process would not carry +them, which is exactly why the macOS install is a container wrapper and never +claims native isolation. + +## Where this tap comes from + +The contents of this repository are mirrored from `ops/get-host/homebrew/` in +the main Attesto repository — that directory is the source of truth; changes +land there first and are synced here at publish time (`sync_tap.sh`). The +behavioral parity between the brew wrapper and the `install.sh` wrapper is +enforced there by `tests/wrapper_sync_check.sh`.