[recipe_wrapper] Delete BCID-related dead code

BCID attestion is not being used and the work is paused indefinitely.

Bug: 333766437
Change-Id: I9bdd165d9a8c8d84c3aeb4ea49d012017b24d206
Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/infra/+/1024833
Fuchsia-Auto-Submit: Oliver Newman <olivernewman@google.com>
Reviewed-by: Vinicius Felizardo <felizardo@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
diff --git a/cmd/recipe_wrapper/bcid/bcid.go b/cmd/recipe_wrapper/bcid/bcid.go
deleted file mode 100644
index 29d420d..0000000
--- a/cmd/recipe_wrapper/bcid/bcid.go
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package bcid
-
-import (
-	"bytes"
-	"context"
-	"crypto/sha256"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"net/http"
-	"net/http/httputil"
-	"os/exec"
-	"runtime"
-
-	spb "github.com/in-toto/attestation/go/v1"
-	"go.chromium.org/luci/auth"
-	"go.chromium.org/luci/cipd/common"
-	"go.chromium.org/luci/common/logging"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/env"
-	"golang.org/x/oauth2"
-	"google.golang.org/protobuf/encoding/protojson"
-	structpb "google.golang.org/protobuf/types/known/structpb"
-)
-
-const (
-	ProdKeyID     = "projects/fuchsia-bcid/locations/global/keyRings/bcid/cryptoKeys/attestation_signer/cryptoKeyVersions/1"
-	TestKeyID     = "projects/fuchsia-bcid-dev/locations/global/keyRings/tests/cryptoKeys/recipe_wrapper_attestation_signer/cryptoKeyVersions/1"
-	StmtType      = "https://in-toto.io/Statement/v1"
-	PredicateType = "https://slsa.dev/provenance/v1"
-	sciloEndpoint = "https://bcidsoftwareverifier-pa.googleapis.com/v1/software-artifact-verification-requests"
-)
-
-type attestOutputter struct{}
-
-func (*attestOutputter) CombinedOutput(ctx context.Context, stdin []byte, args ...string) ([]byte, error) {
-	cmd := exec.CommandContext(ctx, "attestation_tool", args...)
-	if stdin != nil {
-		pipe, err := cmd.StdinPipe()
-		if err != nil {
-			return nil, fmt.Errorf("unable to pipe to stdin: %v", err)
-		}
-		pipe.Write(stdin)
-		// This close must occur before CombinedOutput() is called otherwise the command will wait forever.
-		pipe.Close()
-	}
-
-	logging.Infof(ctx, "Executing %+v\n", cmd)
-	return cmd.CombinedOutput()
-}
-
-func CanAttestPlatform() bool {
-	return runtime.GOOS == "linux" && runtime.GOARCH == "amd64"
-}
-
-func Attest(ctx context.Context, stmt *spb.Statement, keyID string) (*InTotoBundle, error) {
-	if !CanAttestPlatform() {
-		return nil, fmt.Errorf("platform %v/%v is unsupported for attestation", runtime.GOOS, runtime.GOARCH)
-	}
-	return attestWithOutputter(ctx, &attestOutputter{}, stmt, keyID)
-}
-
-type CombinedOutputer interface {
-	CombinedOutput(context.Context, []byte, ...string) ([]byte, error)
-}
-
-func attestWithOutputter(ctx context.Context, cmd CombinedOutputer, stmt *spb.Statement, keyID string) (*InTotoBundle, error) {
-	stmtJSON, err := protojson.Marshal(stmt)
-	if err != nil {
-		return nil, err
-	}
-
-	out, err := cmd.CombinedOutput(ctx, stmtJSON, "sign_intoto_statement",
-		"--input", "-",
-		"--key_id", keyID,
-		"--output", "-")
-	if err != nil {
-		return nil, fmt.Errorf("attestation_tool failed: %v / %q", err, out)
-	}
-	return UnmarshalBundle(bytes.NewReader(out))
-}
-
-type Signature struct {
-	KeyID string `json:"keyid"`
-	Sig   string `json:"sig"`
-}
-
-// InToToBundle represents the attestation bundle.
-// TODO(https://github.com/in-toto/attestation/issues/280): This struct should ideally be provided upstream.
-type InTotoBundle struct {
-	ContentType string      `json:"payloadType"`
-	Payload     string      `json:"payload"`
-	Signatures  []Signature `json:"signatures"`
-}
-
-func UnmarshalBundle(data io.Reader) (*InTotoBundle, error) {
-	all, err := io.ReadAll(data)
-	if err != nil {
-		return nil, fmt.Errorf("can't read input data: %v", err)
-	}
-
-	var itt InTotoBundle
-	if err := json.Unmarshal(all, &itt); err != nil {
-		return nil, fmt.Errorf("unable to pass in-toto payload: %v", err)
-	}
-
-	return &itt, nil
-}
-
-type Subject struct {
-	Pkg  *common.Pin
-	Data io.Reader
-}
-
-func NewStmt(subject *Subject, buildEnv *env.Build) (*spb.Statement, error) {
-	if subject == nil {
-		return nil, errors.New("subject cannot be nil")
-	}
-	h := sha256.New()
-	if _, err := io.Copy(h, subject.Data); err != nil {
-		return nil, err
-	}
-
-	// The statement proto uses this struct proto which is dynamically typed.
-	// It then uses reflection to try and figure out what it all is.
-	// The only real way to work through this is by using raw types, not
-	// real Go structs.
-	// It's possible to just dump raw JSON here, but that feels even less
-	// idomatic than this does.
-	// Unless tagged as required, fields are optional.
-	// Any changes to this specification should be reflected in the external
-	// documentation at //slsa/buildConfig/v1
-	// Do not remove fields once populated unless BCID For Software team is
-	// consulted.
-	pred, err := structpb.NewStruct(map[string]any{
-		// Required.
-		"buildDefinition": map[string]any{
-			// Required.
-			"buildType": "https://fuchsia.googlesource.com/infra/infra/slsa/buildConfig/v1",
-			// Required, but fields within may be best-effort.
-			// The spec is unclear whether this field can be included empty.
-			"externalParameters": map[string]any{
-				"buildConfigSource": map[string]any{
-					"repository": fmt.Sprintf("git+%s@%s", buildEnv.RecipesExe.Repo(), buildEnv.RecipesExe.SHA1()),
-					"path":       "recipes.py",
-				},
-			},
-		},
-		// Required.
-		"runDetails": map[string]any{
-			// Required.
-			"builder": map[string]any{
-				// Required.
-				"id": "//bcid.corp.google.com/builders/luci/fuchsia/l1",
-				// TODO(cflewis): Add the version here.
-			},
-		},
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	return &spb.Statement{
-		Type: StmtType,
-		Subject: []*spb.ResourceDescriptor{
-			{
-				Name: subject.Pkg.PackageName,
-				// cipd_instance_id is required by SCILo to understand what is being uploaded.
-				// This key isn't arbitrary chosen and _mustn't be changed_.
-				Digest: map[string]string{"cipd_instance_id": subject.Pkg.InstanceID},
-			},
-		},
-		PredicateType: PredicateType,
-		Predicate:     pred,
-	}, nil
-}
-
-type sciloContext struct {
-	VerificationPurpose  string `json:"verificationPurpose"`
-	EnforcementPointName string `json:"enforcementPointName"`
-	OccurrenceStage      string `json:"occurrenceStage"`
-}
-
-type sciloArtifactInfo struct {
-	Digests map[string]string `json:"digests"`
-	// Attestations are _strings_ of InTotoBundles.
-	// Passing bundles as JSON is not accepted by the endpoint.
-	Attestations []string `json:"attestations"`
-}
-
-type sciloReq struct {
-	Context          *sciloContext      `json:"context"`
-	ResourceToVerify string             `json:"resourceToVerify"`
-	ArtifactInfo     *sciloArtifactInfo `json:"artifactInfo"`
-}
-
-type SCILoResp struct {
-	Allowed             bool   `json:"allowed"`
-	RejectionMessage    string `json:"rejectionMessage"`
-	VerificationSummary string `json:"verificationSummary"`
-}
-
-// UploadToSCILo uploads the bundle to SCILo using default values.
-func UploadToSCILo(ctx context.Context, stmt *spb.Statement, bundle *InTotoBundle) (*SCILoResp, error) {
-	ts, err := LUCITokenSource(ctx)
-	if err != nil {
-		return nil, err
-	}
-	return UploadToSCILoEndpoint(ctx, sciloEndpoint, ts, stmt, bundle)
-}
-
-func UploadToSCILoEndpoint(ctx context.Context,
-	endpoint string, tokenSource oauth2.TokenSource, stmt *spb.Statement, bundle *InTotoBundle) (*SCILoResp, error) {
-	token, err := tokenSource.Token()
-	if err != nil {
-		return nil, fmt.Errorf("couldn't get auth token: %v", err)
-	}
-	if subjs := len(stmt.GetSubject()); subjs != 1 {
-		return nil, fmt.Errorf("expected 1 subject in statement, got %v", subjs)
-	}
-	subj := stmt.GetSubject()[0]
-
-	jsonBundle, err := json.Marshal(bundle)
-	if err != nil {
-		return nil, fmt.Errorf("couldn't marshal bundle to JSON: %v", err)
-	}
-
-	sReq := &sciloReq{
-		// These values are documented at go/scilo-server#artifact-occurrence-stage
-		Context: &sciloContext{
-			VerificationPurpose: "VERIFY_FOR_ENFORCEMENT",
-			// This is an arbitrary name to identify what is attesting.
-			EnforcementPointName: "fuchsia-recipe-wrapper",
-			// This means that the attestation has been made after the build has completed
-			// and will no longer be modified.
-			OccurrenceStage: "AS_VERIFIED",
-		},
-		ResourceToVerify: "cipd_package://" + subj.GetName(),
-		ArtifactInfo: &sciloArtifactInfo{
-			Digests:      subj.GetDigest(),
-			Attestations: []string{string(jsonBundle)},
-		},
-	}
-	jsonReq, err := json.Marshal(sReq)
-	if err != nil {
-		return nil, fmt.Errorf("couldn't marshal SCILo Request to JSON: %v", err)
-	}
-	logging.Infof(ctx, "Uploading attestations to SCILo:\n%s", jsonBundle)
-
-	httpReq, _ := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(jsonReq))
-	httpReq.Header.Set("Content-Type", "application/json")
-	httpReq.Header.Set("Accept", "application/json")
-	httpReq.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token.AccessToken))
-	// The dump needs to happen _before_ the httpclient.Do() call.
-	// Do() probably consumes the request and then dump is empty.
-	// DumpRequestOut() will copy back the data it consumes so the
-	// Do() call works correctly.
-	dump, err := httputil.DumpRequestOut(httpReq, true)
-	if err != nil {
-		return nil, fmt.Errorf("unable to dump http request: %v", err)
-	}
-
-	// TODO(cflewis): This might need to be retried if the BCID server is observed to flake out.
-	resp, err := http.DefaultClient.Do(httpReq)
-	if err != nil {
-		return nil, fmt.Errorf("unable to contact BCID endpoint: %v", err)
-	}
-	defer resp.Body.Close()
-	body, err := io.ReadAll(resp.Body)
-	if err != nil {
-		return nil, fmt.Errorf("couldn't read HTTP body: %v", err)
-	}
-	// Check if the request was blocked by lack of a good token.
-	// If so, give a more informative error.
-	if resp.StatusCode == http.StatusUnauthorized {
-		return nil, fmt.Errorf("GFE blocked access attempt, probably due to bad auth token. Output:\n%s", body)
-	}
-	// If there was no good status there really isn't anything meaningful to be done at
-	// this point. There's no recovery that can be taken.
-	if resp.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("bad HTTP status code %v: body:\n%s\noriginal request:\n%s", resp.StatusCode, body, dump)
-	}
-
-	var sResp SCILoResp
-	err = json.Unmarshal(body, &sResp)
-	if err != nil {
-		return nil, fmt.Errorf("unable to marshal HTTP body to SCILoResp: %v", err)
-	}
-	return &sResp, nil
-}
-
-func LUCITokenSource(ctx context.Context) (oauth2.TokenSource, error) {
-	authenticator := auth.NewAuthenticator(ctx, auth.SilentLogin, auth.Options{
-		Scopes: []string{"https://www.googleapis.com/auth/bcid_verify"},
-	})
-	return authenticator.TokenSource()
-}
diff --git a/cmd/recipe_wrapper/bcid/bcid_test.go b/cmd/recipe_wrapper/bcid/bcid_test.go
deleted file mode 100644
index ad17afa..0000000
--- a/cmd/recipe_wrapper/bcid/bcid_test.go
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// TODO(b/299967645): Test errors, not just happy path.
-package bcid
-
-import (
-	"archive/zip"
-	"bytes"
-	"context"
-	"encoding/json"
-	"fmt"
-	"io"
-	"net/http"
-	"net/http/httptest"
-	"os"
-	"os/exec"
-	"strings"
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	"github.com/google/go-cmp/cmp/cmpopts"
-	spb "github.com/in-toto/attestation/go/v1"
-	"go.chromium.org/luci/cipd/common"
-	"go.chromium.org/luci/common/logging/gologger"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/env"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/recipes"
-	"golang.org/x/oauth2"
-)
-
-func ctx() context.Context {
-	ctx := context.Background()
-
-	if testing.Verbose() {
-		return gologger.StdConfig.Use(ctx)
-	}
-	return ctx
-}
-
-// These are read from a test data file because the line lengths are enormous.
-var fakeInTotoOutput []byte
-var fakeVerificationResponse []byte
-
-var buildEnv = &env.Build{RecipesExe: &recipes.Repo{}}
-
-type testAttestOutputter struct{}
-
-func (tao *testAttestOutputter) CombinedOutput(ctx context.Context, _ []byte, args ...string) ([]byte, error) {
-	if args[0] != "sign_intoto_statement" {
-		panic("only sign_intoto_statement is supported")
-	}
-	return fakeInTotoOutput, nil
-}
-
-// outputter chooses a real version of a tool if it is available and the test
-// isn't running in t.Short. Otherwise, returns a stubbed outputter.
-func outputter(t *testing.T, ctx context.Context, tool string, args ...string) CombinedOutputer {
-	t.Helper()
-
-	// TODO(cflewis): Find a way to make this work on a LUCI builder.
-	// Tools on LUCI usually aren't provided inside a path but via
-	// argument/envvar.
-	_, pathErr := exec.LookPath(tool)
-	// Check that the test isn't running in a sandbox.
-	resp, _ := http.Get("https://example.com")
-
-	var real, stub CombinedOutputer
-
-	switch tool {
-	case "attestation_tool":
-		real = &attestOutputter{}
-		stub = &testAttestOutputter{}
-	default:
-		t.Fatalf("no definition for real/stub commander for %v", tool)
-	}
-
-	if !testing.Short() && pathErr == nil && resp.StatusCode == http.StatusOK {
-		return real
-	}
-
-	return stub
-}
-
-func TestAttest(t *testing.T) {
-	ctx := ctx()
-	stmt, err := NewStmt(&Subject{Pkg: &common.Pin{
-		PackageName: "fuchsia/sdk/core/linux-amd64",
-		InstanceID:  "NubrCS3MOEEOGG1aPZJnyK3AjZnPMTVC6Of5leb7vHkC"},
-		Data: strings.NewReader("")}, buildEnv)
-	if err != nil {
-		t.Fatalf("can't initialize statement")
-	}
-
-	bundle, err := attestWithOutputter(ctx, outputter(t, ctx, "attestation_tool"), stmt, TestKeyID)
-	if err != nil {
-		t.Fatalf("attestWithCommander() error %v, want nil error", err)
-	}
-
-	// A real invocation of the attestation_tool is non-deterministic because it appears
-	// to rely on the clock time as an input.
-	// Instead of relying on the data itself, just check that the data was formatted as
-	// expected. This validates that attestation_tool did work.
-	if bundle.ContentType != "application/vnd.in-toto+json" ||
-		bundle.Signatures[0].KeyID != "gcpkms://"+TestKeyID || bundle.Signatures[0].Sig == "" {
-		t.Errorf("InToto struct did not follow expected format\nContentType = %q\nKey ID = %q\nSig = %q",
-			bundle.ContentType, bundle.Signatures[0].KeyID, bundle.Signatures[0].Sig)
-	}
-}
-
-func fakeZip(t *testing.T, buf *bytes.Buffer, data string) *zip.Writer {
-	w := zip.NewWriter(buf)
-	f, err := w.Create(data)
-	if err != nil {
-		t.Fatalf("can't add to zip: %v", err)
-	}
-	_, err = f.Write([]byte("bar"))
-	if err != nil {
-		t.Fatalf("can't write to zip: %v", err)
-	}
-	return w
-}
-
-// CIPD packages are zip files so that functionality needs its own test.
-func TestNewStmtWithZip(t *testing.T) {
-	buf := new(bytes.Buffer)
-	// Note that changing these input values will change the expected SHA and fail the test.
-	w := fakeZip(t, buf, "foo")
-	if err := w.Close(); err != nil {
-		t.Fatalf("can't close writer to zip: %v", err)
-	}
-
-	const name = "alice/bob.pkg"
-	const instanceID = "instance_id"
-	stmt, err := NewStmt(&Subject{
-		Pkg: &common.Pin{
-			PackageName: name,
-			InstanceID:  "instance_id",
-		},
-		Data: buf,
-	}, buildEnv)
-	if err != nil {
-		t.Errorf("got %q, want nil err", err)
-	}
-
-	want := &spb.Statement{
-		Type:          StmtType,
-		PredicateType: PredicateType,
-		Subject: []*spb.ResourceDescriptor{
-			{
-				Name: name,
-				Digest: map[string]string{
-					"cipd_instance_id": instanceID,
-				},
-			},
-		},
-	}
-	if diff := cmp.Diff(stmt, want,
-		// Predicate is a bunch of dynamically typed values.
-		// It's a pain to write a comparer for this, so just ignore it and make sure
-		// it's not nil.
-		cmpopts.IgnoreFields(spb.Statement{}, "Predicate"),
-		cmpopts.IgnoreUnexported(spb.Statement{}),
-		cmpopts.IgnoreUnexported(spb.ResourceDescriptor{})); diff != "" || stmt.Predicate == nil {
-		t.Errorf("got diff, want no diff: Diff:\n%v", diff)
-	}
-}
-
-type repo struct {
-	repo, sha string
-}
-
-func (r *repo) LuciexeCommand() ([]string, error) {
-	return nil, nil
-}
-func (r *repo) Repo() string { return r.repo }
-func (r *repo) SHA1() string { return r.sha }
-
-func TestNewStmtConfigSource(t *testing.T) {
-	got, err := NewStmt(&Subject{
-		Pkg:  &common.Pin{PackageName: "Foo", InstanceID: "Bar"},
-		Data: strings.NewReader("foo"),
-	}, &env.Build{RecipesExe: &repo{repo: "foo", sha: "123456"}})
-	if err != nil {
-		t.Fatalf("unable to construct new statement: %v", err)
-	}
-
-	want := "git+foo@123456"
-	// No, there is no better way to navigate through this structure.
-	// Yes, it sucks.
-	if got.Predicate.GetFields()["buildDefinition"].
-		GetStructValue().GetFields()["externalParameters"].
-		GetStructValue().GetFields()["buildConfigSource"].
-		GetStructValue().GetFields()["repository"].GetStringValue() != want {
-		t.Errorf("got %v, want %v", got, want)
-	}
-}
-
-const fakeLUCIToken = "ya29.a0AfB_byAdIhZ9KWIh6vYkODl3VDfOHi1fNNmblLb9yp_W2CH7T23C0RTmpspLZ-EMgKO-UWGIMkm8ivmqF6j8iFPsEkW8Xywld4yI3AnFhCM6G7k433FRoUVfQkNUCOC03j_P1Q8gqK37VKPnuGmjQl_1RcPBzlv5XJpdaCgYKASISAQ8SFQGOcNnCKCsyK-Oe4wRl_HNBMAk2Rg0171"
-
-type testTokenSource struct {
-}
-
-func (tts *testTokenSource) Token() (*oauth2.Token, error) {
-	return &oauth2.Token{AccessToken: fakeLUCIToken}, nil
-}
-
-func TestLUCIToken(t *testing.T) {
-	ctx := context.Background()
-	var tokenSource oauth2.TokenSource
-
-	// Attempt to get a real token if possible.
-	if !testing.Short() {
-		var err error
-		tokenSource, err = LUCITokenSource(ctx)
-		if err != nil || tokenSource == nil {
-			t.Logf("LUCI unable to get a real token source, this is expected when run non-locally and can be ignored: %v",
-				err.Error())
-			// There's no real token source, so use the test one instead.
-			tokenSource = &testTokenSource{}
-		}
-	}
-
-	// All Google access tokens seem to start with ya29.
-	token, err := tokenSource.Token()
-	if err != nil {
-		t.Fatalf("unable to get token from source")
-	}
-	wantPrefix := "ya29"
-	if !strings.HasPrefix(token.AccessToken, wantPrefix) {
-		t.Errorf("got %v, want start of ya29 %v", token, wantPrefix)
-	}
-}
-
-func TestUploadBundle(t *testing.T) {
-	// It's simply too difficult to test hitting the BCID API
-	// given the allowlists, and OAuth token exchanges (with private scopes) required.
-	// Bring up a test server and just check that the requests are reasonably
-	// formatted.
-	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		body, err := io.ReadAll(r.Body)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("can't read body: %v", err), http.StatusBadRequest)
-		}
-		defer r.Body.Close()
-
-		var req sciloReq
-		if err := json.Unmarshal(body, &req); err != nil {
-			http.Error(w, fmt.Sprintf("can't unmarshal request to valid SCILo request: %v\nBody: %s", err, body), http.StatusBadRequest)
-		}
-
-		fmt.Fprintln(w, string(fakeVerificationResponse))
-	}))
-	defer ts.Close()
-
-	fakeBundle, err := UnmarshalBundle(bytes.NewReader(fakeInTotoOutput))
-	if err != nil {
-		t.Fatalf("couldn't marshal fake bundle: %v", err)
-	}
-
-	tests := []struct {
-		name         string
-		subject      *Subject
-		bundle       *InTotoBundle
-		wantAccepted bool
-	}{
-		{
-			name: "A good statement and bundle should produce a good response",
-			subject: &Subject{
-				Pkg: &common.Pin{
-					PackageName: "foopkg",
-					InstanceID:  "bar_instance",
-				},
-				Data: strings.NewReader("bazdata"),
-			},
-			bundle:       fakeBundle,
-			wantAccepted: true,
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			stmt, err := NewStmt(test.subject, buildEnv)
-			if err != nil {
-				t.Fatalf("couldn't create statement from subject: %v", err)
-			}
-			resp, err := UploadToSCILoEndpoint(
-				context.Background(),
-				ts.URL,
-				&testTokenSource{},
-				stmt,
-				test.bundle,
-			)
-			if err != nil {
-				t.Errorf("got error %v, want nil error", err)
-			}
-			if resp == nil {
-				t.Error("got nil response, want valid response")
-			}
-			if test.wantAccepted && !(resp.Allowed && resp.RejectionMessage == "") {
-				t.Errorf("wanted accepted response with no rejection message, got %+v", resp)
-			}
-		})
-	}
-}
-
-func mustRead(filename string) []byte {
-	f, err := os.Open(filename)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "unable to open testdata: %v", err)
-		os.Exit(1)
-	}
-	data, err := io.ReadAll(f)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "unable to read in-toto testdata: %v", err)
-		os.Exit(1)
-	}
-	return data
-}
-
-func TestMain(m *testing.M) {
-	fakeInTotoOutput = mustRead("testdata/in-toto-output.json")
-	fakeVerificationResponse = mustRead("testdata/verification-response.json")
-
-	os.Exit(m.Run())
-}
diff --git a/cmd/recipe_wrapper/bcid/testdata/in-toto-output.json b/cmd/recipe_wrapper/bcid/testdata/in-toto-output.json
deleted file mode 100644
index 60097a1..0000000
--- a/cmd/recipe_wrapper/bcid/testdata/in-toto-output.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "payloadType": "application/vnd.in-toto+json",
-  "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCAic3ViamVjdCI6W3siZGlnZXN0Ijp7InNoYTI1NiI6IjQzNzVhNTBiYjJhODFkNTQwMGU2NmEzMWMwNzRkN2U3ODRjNjE2OTMzMzFlNzFhNzhmN2MwNzk5ZTc0YzhhNTcifX1dLCAicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MSIsICJwcmVkaWNhdGUiOnsiYnVpbGREZWZpbml0aW9uIjp7ImV4dGVybmFsUGFyYW1ldGVycyI6eyJidWlsZENvbmZpZ1NvdXJjZSI6eyJwYXRoIjoicmVjaXBlcy5weSIsICJyZXBvc2l0b3J5IjoiZ2l0K2h0dHBzOi8vZnVjaHNpYS5nb29nbGVzb3VyY2UuY29tL2luZnJhL3JlY2lwZXMifX19LCAicnVuRGV0YWlscyI6eyJidWlsZGVyIjp7ImlkIjoiLy9iY2lkLmNvcnAuZ29vZ2xlLmNvbS9idWlsZGVycy9sdWNpL2Z1Y2hzaWEvbDEifX19fQ==",
-  "signatures": [
-    {
-      "keyid": "gcpkms://projects/fuchsia-bcid-dev/locations/global/keyRings/tests/cryptoKeys/recipe_wrapper_attestation_signer/cryptoKeyVersions/1",
-      "sig": "MEUCIQDr4B4IXsKYfqRjn7uXJQdWHwYk1K+YBkQzr1gwjMtKfAIgSRUbv+sfBzsEebkt32K+Zoil/GYbu9Fu0W1n1CF8IiI="
-    }
-  ]
-}
\ No newline at end of file
diff --git a/cmd/recipe_wrapper/bcid/testdata/verification-response.json b/cmd/recipe_wrapper/bcid/testdata/verification-response.json
deleted file mode 100644
index 2801042..0000000
--- a/cmd/recipe_wrapper/bcid/testdata/verification-response.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "allowed": true,
-  "verificationSummary": "{\"payload\":\"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9zbHNhLmRldi92ZXJpZmljYXRpb25fc3VtbWFyeS92MC4yIiwicHJlZGljYXRlIjp7InBvbGljeSI6eyJ1cmkiOiJnb29nbGVmaWxlOi9nb29nbGVfc3JjL2ZpbGVzLzU2NzM5NTQ4MS9kZXBvdC9nb29nbGUzL3Byb2R1Y3Rpb24vc2VjdXJpdHkvYmNpZC9zb2Z0d2FyZS9taXNjX3NvZnR3YXJlL2RhcnQvc2RrLnN3X3BvbGljeS50ZXh0cHJvdG8ifSwidGltZV92ZXJpZmllZCI6IjIwMjMtMDktMjFUMjA6Mzg6NDEuNDM2NDY1WiIsInJlc291cmNlX3VyaSI6Im1pc2Nfc29mdHdhcmU6Ly9kYXJ0L3Nkay9tYWNvcyIsInBvbGljeV9sZXZlbCI6IlNMU0FfTEVWRUxfMiIsInZlcmlmaWVyIjp7ImlkIjoiaHR0cHM6Ly9iY2lkLmNvcnAuZ29vZ2xlLmNvbS92ZXJpZmllci9iY2lkX3BhY2thZ2VfZW5mb3JjZXIvdjAuMSJ9LCJ2ZXJpZmljYXRpb25fcmVzdWx0IjoiUEFTU0VEIn0sInN1YmplY3QiOlt7Im5hbWUiOiJfIiwiZGlnZXN0Ijp7InNoYTI1NiI6IjRkNzAwYmZlMTVhYTM2NGZiMDYzMTUzNDRmODA2NjY4MzE0NDVlZTlkNDMzZDkxMDJiOTMyNjJiZWU5YTMxNWMifX1dfQ==\",\"payloadType\":\"application/vnd.in-toto+json\",\"signatures\":[{\"sig\":\"Pvs6oNhpUKSW/B+tgeqaM2lJBmlFkeuDvuegf5GGaBTJb0Uy4Ote7uaY4CG2YA45C5Mg9DDlURO+RiE54yfslw==\",\"keyid\":\"keystore://76574:prod:vsa_signing_public_key_staging\"}]}"
-}
\ No newline at end of file
diff --git a/cmd/recipe_wrapper/cipd/cipd.go b/cmd/recipe_wrapper/cipd/cipd.go
index aca896c..c8ba2ac 100644
--- a/cmd/recipe_wrapper/cipd/cipd.go
+++ b/cmd/recipe_wrapper/cipd/cipd.go
@@ -11,17 +11,8 @@
 	"os/exec"
 	"path/filepath"
 
-	v1 "github.com/in-toto/attestation/go/v1"
-	"go.chromium.org/luci/cipd/client/cipd"
 	"go.chromium.org/luci/cipd/common"
 	"go.chromium.org/luci/common/logging"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/bcid"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/env"
-)
-
-const (
-	SERVICE_URL     = "https://chrome-infra-packages.appspot.com"
-	SERVICE_DEV_URL = "https://chrome-infra-packages-dev.appspot.com"
 )
 
 type Package struct {
@@ -47,10 +38,6 @@
 		version: "version:2@2.7.18.chromium.44",
 		binDir:  "bin",
 	}
-	AttestToolPkg = Package{
-		name:    "fuchsia/infra/attestation_tool/${platform}",
-		version: "A5A21ls2KKN_H59cvEI2IKtIT3-TnNnNY49sguw56BoC",
-	}
 )
 
 func Install(ctx context.Context, dir string, packages ...Package) ([]string, error) {
@@ -76,90 +63,3 @@
 
 	return binDirs, nil
 }
-
-type PkgFile struct {
-	PackageName string `json:"cipd_package"`     //`json:"package"`
-	InstanceID  string `json:"cipd_instance_id"` //`json:"instance_id"`
-	Path        string `json:"path"`
-}
-
-func (pf *PkgFile) AsPin() *common.Pin {
-	return &common.Pin{
-		PackageName: pf.PackageName,
-		InstanceID:  pf.InstanceID,
-	}
-}
-
-type StmtBundle struct {
-	Package   *PkgFile
-	Statement *v1.Statement
-	Bundle    *bcid.InTotoBundle
-}
-
-// AttestPkgs searches for packages that were registered to CIPD and
-// generates attestations for them.
-func AttestPkgs(pkgs []*PkgFile, keyID string, buildEnv *env.Build) ([]*StmtBundle, []error) {
-	var stmtBundles []*StmtBundle
-	var errs []error
-	for _, pkg := range pkgs {
-		data, err := os.Open(pkg.Path)
-		if err != nil {
-			errs = append(errs, fmt.Errorf("can't open package file %q: %v", pkg, err))
-			continue
-		}
-		defer data.Close()
-
-		stmt, err := bcid.NewStmt(&bcid.Subject{
-			Pkg:  pkg.AsPin(),
-			Data: data,
-		}, buildEnv)
-		if err != nil {
-			errs = append(errs, fmt.Errorf("can't create statement for package %q: %v", pkg, err))
-			continue
-		}
-		b, err := bcid.Attest(context.TODO(), stmt, keyID)
-		if err != nil {
-			errs = append(errs, fmt.Errorf("can't attest package %q: %v", pkg, err))
-			continue
-		}
-		stmtBundles = append(stmtBundles, &StmtBundle{
-			Package:   pkg,
-			Statement: stmt,
-			Bundle:    b,
-		})
-	}
-
-	return stmtBundles, errs
-}
-
-type CombinedOutputer interface {
-	CombinedOutput(context.Context, ...string) ([]byte, error)
-}
-
-type cipdOutputter struct{}
-
-func (*cipdOutputter) CombinedOutput(ctx context.Context, args ...string) ([]byte, error) {
-	return exec.CommandContext(ctx, "cipd", args...).CombinedOutput()
-}
-
-// AddMetadata wraps `cipd set-metadata`.
-// The value is assumed to be a text/plain content type.
-// It's named `Add` and not `Set` because using the same key multiple times
-// will result in the key appearing multiple times, not once with the last updated
-// value.
-// Package and version name examples can be found at
-// https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/cipd/README.md
-func AddMetadata(ctx context.Context, pkg *common.Pin, md []cipd.Metadata) error {
-	return addMetadataWithOutputter(ctx, &cipdOutputter{}, SERVICE_URL, pkg, md)
-}
-
-func addMetadataWithOutputter(ctx context.Context, cmd CombinedOutputer, serviceURL string, pkg *common.Pin, md []cipd.Metadata) error {
-	// TODO(cflewis): This code would be faster if the client was kept around rather than remade each time.
-	c, err := cipd.NewClientFromEnv(ctx, cipd.ClientOptions{ServiceURL: serviceURL})
-	if err != nil {
-		return err
-	}
-	defer c.Close(ctx)
-
-	return c.AttachMetadataWhenReady(ctx, *pkg, md)
-}
diff --git a/cmd/recipe_wrapper/cipd/cipd_test.go b/cmd/recipe_wrapper/cipd/cipd_test.go
deleted file mode 100644
index 8375a51..0000000
--- a/cmd/recipe_wrapper/cipd/cipd_test.go
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package cipd
-
-import (
-	"context"
-	"encoding/json"
-	"os"
-	"os/exec"
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	"go.chromium.org/luci/auth"
-	api "go.chromium.org/luci/cipd/api/cipd/v1"
-	"go.chromium.org/luci/cipd/client/cipd"
-	"go.chromium.org/luci/cipd/common"
-	"go.chromium.org/luci/grpc/prpc"
-	"go.chromium.org/luci/hardcoded/chromeinfra"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/bcid"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/env"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/recipes"
-)
-
-var buildEnv = &env.Build{RecipesExe: &recipes.Repo{}}
-
-func TestAttestPkgs(t *testing.T) {
-	_, pathErr := exec.LookPath("attestation_tool")
-	if testing.Short() || pathErr != nil {
-		t.Skip("no attestation_tool or test ran with -short, skipping")
-	}
-	tmpfile, err := os.CreateTemp("", "cipd_test")
-	if err != nil {
-		t.Fatalf("can't create tempfile: %v", err)
-	}
-	pkgName := "foo"
-
-	stmtBundles, errs := AttestPkgs([]*PkgFile{
-		{
-			PackageName: pkgName,
-			InstanceID:  "bar",
-			Path:        tmpfile.Name(),
-		},
-	}, bcid.TestKeyID, buildEnv)
-	if errs != nil {
-		t.Errorf("got errors: %+v, want nil error", errs)
-	}
-
-	for _, sb := range stmtBundles {
-		// A real invocation of the attestation_tool is non-deterministic because it appears
-		// to rely on the clock time as an input.
-		// Instead of relying on the data itself, just check that the data was formatted as
-		// expected. This validates that attestation_tool did work.
-		if sb.Statement.GetSubject()[0].GetName() != pkgName ||
-			sb.Bundle.ContentType != "application/vnd.in-toto+json" ||
-			sb.Bundle.Signatures[0].KeyID != "gcpkms://"+bcid.TestKeyID || sb.Bundle.Signatures[0].Sig == "" {
-			t.Errorf("InToto struct did not follow expected format\nName = %q\nContentType = %q\nKey ID = %q\nSig = %q",
-				sb.Statement.GetSubject()[0].GetName(), sb.Bundle.ContentType, sb.Bundle.Signatures[0].KeyID, sb.Bundle.Signatures[0].Sig)
-		}
-	}
-}
-
-type testCIPDOutputter struct{}
-
-func (*testCIPDOutputter) CombinedOutput(ctx context.Context, args ...string) ([]byte, error) {
-	cmd := exec.CommandContext(ctx, "cipd", args...)
-	cmd.Env = append(cmd.Environ(), "CIPD_SERVICE_URL="+SERVICE_DEV_URL)
-	return cmd.CombinedOutput()
-}
-
-const pkgName = "experimental/fuchsia/recipe_wrapper/cipd_test"
-
-type CIPDResult struct {
-	Package    string `json:"package"`
-	InstanceID string `json:"instance_id"`
-}
-type CIPDResults struct {
-	Result *CIPDResult `json:"result"`
-}
-
-func containsMetadata(s []*api.InstanceMetadata, md cipd.Metadata) bool {
-	for _, im := range s {
-		if im.GetKey() == md.Key && string(im.GetValue()) == string(md.Value) {
-			return true
-		}
-	}
-
-	return false
-}
-
-func newRepoClient(ctx context.Context, t *testing.T) api.RepositoryClient {
-	c, err := auth.NewAuthenticator(ctx, auth.OptionalLogin, chromeinfra.DefaultAuthOptions()).Client()
-	if err != nil {
-		t.Fatalf("couldn't initialize authenticator: %v", err)
-	}
-	prpcC := &prpc.Client{
-		C:    c,
-		Host: "chrome-infra-packages-dev.appspot.com",
-	}
-
-	return api.NewRepositoryClient(prpcC)
-}
-
-func TestAddMetadata(t *testing.T) {
-	// This test reaches out to the CIPD service which assumes some base ACLs
-	// that are not available on all bots that run the test.
-	t.Skip("skipping non-hermetic test")
-
-	_, err := exec.LookPath("cipd")
-	if err != nil {
-		// Testing without cipd is so trivial there's no point in doing anything.
-		// Even if the code is only using the client library, it does assume that
-		// the required auth flow for cipd has already been completed (e.g. `cipd auth-login`).
-		// If `cipd` isn't here, then the auth flow can't have happened.
-		t.Skip("`cipd` not on path")
-	}
-	ctx := context.Background()
-
-	// An executed command is used instead of using the luci-go cipd client
-	// because the package creation code included in the `cipd` executable
-	// is non-trivial. It's more reliable to reuse the executable than attempt
-	// to recreate the logic here.
-	tmpdir, err := os.MkdirTemp("", "cipd_test")
-	if err != nil {
-		t.Fatalf("unable to create tempdir: %v", err)
-	}
-	f, err := os.CreateTemp(tmpdir, "")
-	if err != nil {
-		t.Fatalf("unable to create temp file: %v", err)
-	}
-	f.WriteString("Hello, cipd_test!")
-	f.Close()
-	defer os.RemoveAll(tmpdir)
-
-	// Make a temporary file to output results to.
-	// This is necessary so the created version can be inspected.
-	// The version ID is a requirement for adding metadata.
-	outF, err := os.CreateTemp("", "cipd_test_output")
-	if err != nil {
-		t.Fatalf("unable to create JSON output file: %v", err)
-	}
-	defer os.Remove(outF.Name())
-
-	cmd := exec.Command(
-		"cipd",
-		"create",
-		"-in", tmpdir,
-		"-name", pkgName,
-		"-json-output", outF.Name(),
-	)
-	cmd.Env = append(cmd.Environ(), "CIPD_SERVICE_URL="+SERVICE_DEV_URL)
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		t.Fatalf("unable to create test package: %s/%v", out, err)
-	}
-	outF.Close()
-
-	// Retrieve the instance ID to add metadata to.
-	resultData, err := os.ReadFile(outF.Name())
-	if err != nil {
-		t.Fatalf("couldn't read CIPD create JSON results file: %v", err)
-	}
-	var results CIPDResults
-	err = json.Unmarshal(resultData, &results)
-	if err != nil {
-		t.Fatalf("couldn't get JSON results out of CIPD create results file: %v", err)
-	}
-
-	want := []cipd.Metadata{
-		{
-			Key:         "foo",
-			Value:       []byte("bar"),
-			ContentType: "text/plain",
-		},
-		{
-			Key:         "alice",
-			Value:       []byte("bob"),
-			ContentType: "text/plain",
-		},
-	}
-
-	err = addMetadataWithOutputter(
-		ctx, &testCIPDOutputter{}, SERVICE_DEV_URL, &common.Pin{PackageName: pkgName, InstanceID: results.Result.InstanceID}, want)
-	if err != nil {
-		t.Fatalf("couldn't add metadata: %v", err)
-	}
-
-	repoC := newRepoClient(ctx, t)
-	resp, err := repoC.ListMetadata(ctx, &api.ListMetadataRequest{
-		Package:  pkgName,
-		Instance: common.InstanceIDToObjectRef(results.Result.InstanceID),
-	})
-	if err != nil {
-		t.Fatalf("couldn't get metadata back: %v", err)
-	}
-
-	for _, md := range want {
-		if !containsMetadata(resp.GetMetadata(), md) {
-			t.Errorf("returned metadata does not contain %v", md)
-		}
-	}
-
-	// The package can't be deleted here for cleanup as that requires admin privileges
-	// on the package registry. It's OK for the test package to just pile up multiple
-	// versions.
-}
-
-// Regression test.
-func TestPackageFileMarshal(t *testing.T) {
-	data := `{"cipd_instance_id": "xUAOSrGYJ3OcV88evjHVfFOPo_Yxd4aoQOfUP8BpnMAC", "cipd_package": "fuchsia_internal/assembly/inputs/hsp.x64", "path": "/b/s/w/ir/x/w/recipe_cleanup/cipd-util-build99nbs7su/inputs.pkg"}`
-
-	pkgFile := &PkgFile{}
-	if err := json.Unmarshal([]byte(data), pkgFile); err != nil {
-		t.Fatalf("unable to unmarshal package JSON: %v", err)
-	}
-	if diff := cmp.Diff(pkgFile, &PkgFile{
-		PackageName: "fuchsia_internal/assembly/inputs/hsp.x64",
-		InstanceID:  "xUAOSrGYJ3OcV88evjHVfFOPo_Yxd4aoQOfUP8BpnMAC",
-		Path:        "/b/s/w/ir/x/w/recipe_cleanup/cipd-util-build99nbs7su/inputs.pkg",
-	}); diff != "" {
-		t.Errorf("got diff: %v", diff)
-	}
-}
diff --git a/cmd/recipe_wrapper/env/env.go b/cmd/recipe_wrapper/env/env.go
deleted file mode 100644
index c11e940..0000000
--- a/cmd/recipe_wrapper/env/env.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package env
-
-import (
-	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/recipes"
-)
-
-type Build struct {
-	Environ                 []string
-	RecipesExe              recipes.Checkout
-	Build                   *buildbucketpb.Build
-	DeferredStepsDir        string
-	AttestationShouldUpload bool
-}
diff --git a/cmd/recipe_wrapper/main.go b/cmd/recipe_wrapper/main.go
index d0eda2f..ad28ac7 100644
--- a/cmd/recipe_wrapper/main.go
+++ b/cmd/recipe_wrapper/main.go
@@ -7,17 +7,13 @@
 import (
 	"bytes"
 	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"os"
 	"os/exec"
-	"path/filepath"
-	"runtime"
 	"strings"
 
 	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
-	cipdclient "go.chromium.org/luci/cipd/client/cipd"
 	"go.chromium.org/luci/common/logging"
 	"go.chromium.org/luci/common/logging/gologger"
 	"go.chromium.org/luci/logdog/client/butlerlib/bootstrap"
@@ -25,13 +21,18 @@
 	"go.chromium.org/luci/luciexe"
 	"google.golang.org/protobuf/proto"
 
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/bcid"
 	"go.fuchsia.dev/infra/cmd/recipe_wrapper/cipd"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/env"
 	"go.fuchsia.dev/infra/cmd/recipe_wrapper/props"
 	"go.fuchsia.dev/infra/cmd/recipe_wrapper/recipes"
 )
 
+// environment represents the environment within which a recipe is run.
+type environment struct {
+	environ    []string
+	recipesExe recipes.Checkout
+	buildProto *buildbucketpb.Build
+}
+
 // outputBuildSummary outputs error details to the current build's
 // summary_markdown in case of failure.
 func outputBuildSummary(ctx context.Context, buildErr error) error {
@@ -70,7 +71,7 @@
 // initEnvironment sets up the environment with recipes, Python 2 etc.
 // Returns particulars about the environment created as well as a cleanup function
 // that the caller should run when the environment is no longer needed.
-func initEnvironment(ctx context.Context) (*env.Build, func(), error) {
+func initEnvironment(ctx context.Context) (*environment, func(), error) {
 	cwd, err := os.Getwd()
 	if err != nil {
 		return nil, nil, err
@@ -103,9 +104,6 @@
 	if err != nil {
 		return nil, nil, err
 	}
-	if bcid.CanAttestPlatform() {
-		pkgsToInstall = append(pkgsToInstall, cipd.AttestToolPkg)
-	}
 	if noPy2 {
 		logging.Infof(ctx, "no_python2=true, not installing python 2")
 	} else {
@@ -119,38 +117,21 @@
 	path := strings.Join(append(binDirs, os.Getenv("PATH")), string(os.PathListSeparator))
 	os.Setenv("PATH", path)
 
-	deferredStepsDir, err := os.MkdirTemp("", "bcid")
-	if err != nil {
-		return nil, nil, fmt.Errorf("couldn't create a deferred steps temp dir: %v", err)
-	}
-	if err := props.SetBuildInputProperty(build, "$fuchsia/recipe_wrapper",
-		map[string]any{"deferred_steps_dir": deferredStepsDir}); err != nil {
-		return nil, nil, fmt.Errorf("couldn't set `deferred_steps_dir` property: %v", err)
-	}
-	shouldUpload, err := props.Bool(build, "upload_bcid_attestation")
-	if err != nil {
-		logging.Infof(ctx, "couldn't find upload_bcid_attestation property, assuming false")
-		shouldUpload = false
-	}
-
 	logging.Infof(ctx, "Initialized execution environment to:\n%+v", os.Environ())
 
-	return &env.Build{
-			Environ:                 os.Environ(),
-			RecipesExe:              exe,
-			Build:                   build,
-			DeferredStepsDir:        deferredStepsDir,
-			AttestationShouldUpload: shouldUpload,
+	return &environment{
+			environ:    os.Environ(),
+			recipesExe: exe,
+			buildProto: build,
 		}, func() {
 			// No need to check errors here, trashing temp files is best effort.
 			os.RemoveAll(recipesDir)
 			os.RemoveAll(rootBinDir)
-			os.RemoveAll(deferredStepsDir)
 		}, err
 }
 
-func runRecipe(ctx context.Context, buildEnv *env.Build) error {
-	commandLine, err := buildEnv.RecipesExe.LuciexeCommand()
+func runRecipe(ctx context.Context, env *environment) error {
+	commandLine, err := env.recipesExe.LuciexeCommand()
 	if err != nil {
 		return err
 	}
@@ -160,8 +141,8 @@
 	cmd := exec.CommandContext(ctx, commandLine[0], commandLine[1:]...)
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
-	cmd.Env = buildEnv.Environ
-	inputData, err := proto.Marshal(buildEnv.Build)
+	cmd.Env = env.environ
+	inputData, err := proto.Marshal(env.buildProto)
 	if err != nil {
 		return fmt.Errorf("could not marshal input build: %w", err)
 	}
@@ -181,111 +162,6 @@
 	return strings.Join(errStrs, ",")
 }
 
-func pkgsInDir(ctx context.Context, dir string) ([]*cipd.PkgFile, error) {
-	pkgs := []*cipd.PkgFile{}
-	files, err := os.ReadDir(dir)
-	if err != nil {
-		return nil, err
-	}
-
-	logging.Infof(ctx, "Found %v deferred step files in %v", len(files), dir)
-	for _, f := range files {
-		// Annoyingly f is not an *os.File but an *fs.DirEntry,
-		// and *fs.DirEntries do not contain the filepath.
-		// It has to be joined to the directory it was read from.
-		path := filepath.Join(dir, f.Name())
-		data, err := os.ReadFile(path)
-		if err != nil {
-			return nil, fmt.Errorf("couldn't open %q: %v", path, err)
-		}
-		logging.Infof(ctx, "Read %v: %s", path, data)
-
-		pkgFile := &cipd.PkgFile{}
-		if err := json.Unmarshal(data, pkgFile); err != nil {
-			return nil, err
-		}
-		logging.Infof(ctx, "Unmarshaled to %+v", pkgFile)
-		pkgs = append(pkgs, pkgFile)
-	}
-
-	return pkgs, nil
-}
-
-// uploadAttestations will upload attestations to SCILo, then attach them as metadata to the CIPD packages.
-func uploadAttestations(ctx context.Context, buildEnv *env.Build, stmtBundles []*cipd.StmtBundle) error {
-	if !buildEnv.AttestationShouldUpload {
-		logging.Infof(ctx, "Attestation skipped, builder does not have `upload_bcid_attestation` set")
-		return nil
-	}
-
-	if err := uploadToSCILo(ctx, stmtBundles); err != nil {
-		return fmt.Errorf("unable to upload attestations to SCILo: %v", err)
-	}
-	// Attestations aren't valid if they didn't go in to SCILo, so only attach them as metadata _after_
-	// SCILo has accepted them.
-	if err := attachAttestations(ctx, stmtBundles); err != nil {
-		return fmt.Errorf("unable to attach attestations: %v", err)
-	}
-
-	return nil
-}
-
-func uploadToSCILo(ctx context.Context, stmtBundles []*cipd.StmtBundle) error {
-	for _, sb := range stmtBundles {
-		logging.Infof(ctx, "Uploading attestation for %v | %v:\n%+v",
-			sb.Package.PackageName, sb.Package.InstanceID, sb.Bundle)
-
-		_, err := bcid.UploadToSCILo(ctx, sb.Statement, sb.Bundle)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func attachAttestations(ctx context.Context, stmtBundles []*cipd.StmtBundle) error {
-	for _, sb := range stmtBundles {
-		logging.Infof(ctx, "Attaching attestation for %v | %v:\n%+v",
-			sb.Package.PackageName, sb.Package.InstanceID, sb.Bundle)
-		jsb, err := json.Marshal(sb.Bundle)
-		if err != nil {
-			return fmt.Errorf("unable to marshal %+v: %v", sb.Bundle, err)
-		}
-		cipd.AddMetadata(ctx, sb.Package.AsPin(), []cipdclient.Metadata{
-			{
-				Key:         "provenance",
-				Value:       jsb,
-				ContentType: "text/plain",
-			},
-		})
-	}
-
-	return nil
-}
-
-func attest(ctx context.Context, buildEnv *env.Build) error {
-	if !bcid.CanAttestPlatform() {
-		logging.Infof(ctx, "Unsupported attestation platform: %v/%v", runtime.GOOS, runtime.GOARCH)
-		return nil
-	}
-	pkgs, err := pkgsInDir(ctx, buildEnv.DeferredStepsDir)
-	if err != nil {
-		return err
-	}
-
-	logging.Infof(ctx, "Inspecting %v packages for attestation...", len(pkgs))
-	stmtBundles, errs := cipd.AttestPkgs(pkgs, bcid.ProdKeyID, buildEnv)
-	if len(errs) != 0 {
-		return fmt.Errorf("AttestPkgs returned errors: %v", joinErrs(errs))
-	}
-	if err := uploadAttestations(ctx, buildEnv, stmtBundles); err != nil {
-		return fmt.Errorf("unable to upload attestations: %v", err)
-	}
-
-	return nil
-}
-
 func main() {
 	ctx := context.Background()
 	ctx = gologger.StdConfig.Use(ctx)
@@ -303,10 +179,4 @@
 		logging.Errorf(ctx, fmt.Errorf("build failed: %v", err).Error())
 		os.Exit(1)
 	}
-
-	logging.Infof(ctx, "Running attestation step")
-	if err := attest(ctx, env); err != nil {
-		logging.Errorf(ctx, fmt.Errorf("build was successful, but couldn't attest: %v", err).Error())
-		os.Exit(1)
-	}
 }
diff --git a/cmd/recipe_wrapper/main_test.go b/cmd/recipe_wrapper/main_test.go
deleted file mode 100644
index 0a5aa59..0000000
--- a/cmd/recipe_wrapper/main_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package main
-
-import (
-	"context"
-	"encoding/json"
-	"os"
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	"github.com/google/go-cmp/cmp/cmpopts"
-	"go.fuchsia.dev/infra/cmd/recipe_wrapper/cipd"
-)
-
-func TestOpenFilesInDir(t *testing.T) {
-	tests := []struct {
-		name string
-		// One step to one file.
-		// This makes the test simpler to reason about.
-		files []*cipd.PkgFile
-	}{
-		{
-			name: "Steps should be read back",
-			files: []*cipd.PkgFile{
-				{
-					PackageName: "foo/bar",
-					InstanceID:  "baz",
-					Path:        "/tmp/foo/bar.pkg",
-				},
-				{
-					PackageName: "alice/bob/charlie",
-					InstanceID:  "dave",
-					Path:        "/alice/bob/charlie.pkg",
-				},
-			},
-		},
-		{
-			name: "Empty files should be acceptable",
-			files: []*cipd.PkgFile{
-				{},
-				{
-					PackageName: "foo/bar",
-					InstanceID:  "baz",
-					Path:        "/tmp/foo/bar.pkg",
-				},
-				{},
-			},
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			tmpdir, err := os.MkdirTemp("", "main_test")
-			if err != nil {
-				t.Fatalf("couldn't make tempfile: %v", err)
-			}
-
-			for _, pkgFile := range test.files {
-				f, err := os.CreateTemp(tmpdir, "")
-				if err != nil {
-					t.Fatalf("couldn't create tempfile: %v", err)
-				}
-				pkgJSON, err := json.Marshal(pkgFile)
-				if err != nil {
-					t.Fatalf("couldn't marshal to JSON: %v", err)
-				}
-				_, err = f.WriteString(string(pkgJSON))
-				if err != nil {
-					t.Fatalf("couldn't write JSON: %v", err)
-				}
-				t.Logf("wrote to %q\n", f.Name())
-				f.Close()
-			}
-
-			got, err := pkgsInDir(context.Background(), tmpdir)
-			if err != nil {
-				t.Errorf("got error %q, want nil", err)
-			}
-			if diff := cmp.Diff(got, test.files, cmpopts.SortSlices(func(x, y *cipd.PkgFile) bool {
-				return x.PackageName > y.PackageName
-			})); diff != "" {
-				t.Errorf("got diff: %v", diff)
-			}
-		})
-	}
-
-}
diff --git a/slsa/buildConfig/v1/README.md b/slsa/buildConfig/v1/README.md
deleted file mode 100644
index c8f232d..0000000
--- a/slsa/buildConfig/v1/README.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Fuchsia SLSA v1 spec
-
-This document is a loose description of the SLSA v1 spec. The spec may well _grow_, but it will not shrink to less than this size.
-
-## Statement (Proto)
-
-| Field           | Description            | Default                           |
-| --------------- | ---------------------- | --------------------------------- |
-| `Type`          | Statement type/version | <https://in-toto.io/Statement/v1> |
-| `Subject`       | See Resource Descriptor | See Resource Descriptor            |
-| `PredicateType` | Predicate type/version | <https://slsa.dev/provenance/v1>  |
-| `Predicate`     | See Predicate          | See Predicate                     |
-
-## Resource Descriptor (Proto)
-
-| Field    | Description           | Default |
-| -------- | --------------------- | ------- |
-| `Name`   | The CIPD package name |         |
-| `Digest` | The CIPD instance ID  |         |
-
-## Predicate (JSON)
-
-| Field             | Description          | Default              |
-| ----------------- | -------------------- | -------------------- |
-| `buildDefinition` | See Build Definition | See Build Definition |
-| `runDetails`      | See Run Details      | See Run Details      |
-
-## Build definition (JSON)
-
-| Field                                             | Description                                | Default                                                                            |
-| ------------------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------- |
-| `buildType`                                       | Link to this document                      | <https://fuchsia.googlesource.com/infra/infra/+/refs/heads/main/slsa/buildConfig/v1> |
-| `externalParameters.buildConfigSource.repository` | Location of recipes repository             | `git+https://fuchsia.googlesource.com/infra/recipes`                               |
-| `externalParameters.buildConfigSource.path`       | Recipes entry point                        | `recipes.py`                                                                       |
-
-## Run details (JSON)
-
-| Field     | Description                                   | Default                                           |
-| --------- | --------------------------------------------- | ------------------------------------------------- |
-| `builder` | Chosen ID registered with BCID (currently L1) | `//bcid.corp.google.com/builders/luci/fuchsia/l1` |