diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 34b44b70ee6d004d5105eb7339c9ebb07bfa6886..e0819577dba83eb6996a03077dd983de24a9c3c5 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -32,10 +32,8 @@ jobs:
       - uses: actions/setup-go@v2
         with:
           go-version: ${{ env.GOVERSION }}
-      - name: download go-swagger
-        run : go install github.com/go-swagger/go-swagger/cmd/swagger@v0.27.0
       - name: Validate OpenAPI with Swagger
-        run: swagger validate openapi.yaml
+        run: make validate-openapi
       - name: Build
         run: make -C $GITHUB_WORKSPACE all
       - name: Test
diff --git a/.gitignore b/.gitignore
index 2350421419f0ea7f2c9d18599a8ccde81e16df15..0b56bd68c3142e243a0af52191ab50c5726fc605 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,5 @@ logid
 /tests/rekor-server
 /server
 swagger
+dist/*
+hack/*
diff --git a/Makefile b/Makefile
index e76f1d65ef4404038964a7a8c6118a825c9e324c..b5fedfc2e2d5a5f6d563575a7fc83b1c5740a39f 100644
--- a/Makefile
+++ b/Makefile
@@ -13,13 +13,18 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-.PHONY: all test clean clean-gen lint gosec ko sign-container
+.PHONY: all test clean clean-gen lint gosec ko sign-container cross-cli
 
 all: rekor-cli rekor-server
 
 GENSRC = pkg/generated/client/%.go pkg/generated/models/%.go pkg/generated/restapi/%.go
 OPENAPIDEPS = openapi.yaml $(shell find pkg/types -iname "*.json")
 SRCS = $(shell find cmd -iname "*.go") $(shell find pkg -iname "*.go"|grep -v pkg/generated) pkg/generated/restapi/configure_rekor_server.go $(GENSRC)
+TOOLS_DIR := hack/tools
+TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin)
+BIN_DIR := $(abspath $(ROOT_DIR)/bin)
+GO_INSTALL = ./scripts/go_install.sh
+
 
 # Set version variables for LDFLAGS
 GIT_VERSION ?= $(shell git describe --tags --always --dirty)
@@ -37,15 +42,24 @@ ifeq ($(DIFF), 1)
     GIT_TREESTATE = "dirty"
 endif
 
+# Binaries
+SWAGGER_VER := v0.27.0
+SWAGGER_BIN := swagger
+SWAGGER := $(TOOLS_BIN_DIR)/$(SWAGGER_BIN)-$(SWAGGER_VER)
+
 CLI_PKG=github.com/sigstore/rekor/cmd/rekor-cli/app
 CLI_LDFLAGS="-X $(CLI_PKG).gitVersion=$(GIT_VERSION) -X $(CLI_PKG).gitCommit=$(GIT_HASH) -X $(CLI_PKG).gitTreeState=$(GIT_TREESTATE) -X $(CLI_PKG).buildDate=$(BUILD_DATE)"
 
 SERVER_PKG=github.com/sigstore/rekor/cmd/rekor-server/app
 SERVER_LDFLAGS="-X $(SERVER_PKG).gitVersion=$(GIT_VERSION) -X $(SERVER_PKG).gitCommit=$(GIT_HASH) -X $(SERVER_PKG).gitTreeState=$(GIT_TREESTATE) -X $(SERVER_PKG).buildDate=$(BUILD_DATE)"
 
-$(GENSRC): $(OPENAPIDEPS)
-	swagger generate client -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --default-consumes application/json\;q=1
-	swagger generate server -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --exclude-main -A rekor_server --exclude-spec --flag-strategy=pflag --default-produces application/json
+$(GENSRC): $(SWAGGER) $(OPENAPIDEPS)
+	$(SWAGGER) generate client -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --default-consumes application/json\;q=1
+	$(SWAGGER) generate server -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --exclude-main -A rekor_server --exclude-spec --flag-strategy=pflag --default-produces application/json
+
+.PHONY: validate-openapi
+validate-openapi: $(SWAGGER)
+	$(SWAGGER) validate openapi.yaml
 
 # this exists to override pattern match rule above since this file is in the generated directory but should not be treated as generated code
 pkg/generated/restapi/configure_rekor_server.go: $(OPENAPIDEPS)
@@ -69,6 +83,8 @@ test:
 	go test ./...
 
 clean:
+	rm -rf dist
+	rm -rf hack/tools
 	rm -rf rekor-cli rekor-server
 
 clean-gen: clean
@@ -90,3 +106,30 @@ ko:
 
 sign-container: ko
 	cosign sign -key .github/workflows/cosign.key -a GIT_HASH=$(GIT_HASH) ${KO_DOCKER_REPO}:$(GIT_HASH)
+
+## --------------------------------------
+## Release
+## --------------------------------------
+
+.PHONY: dist-cli
+dist-cli:
+	mkdir -p dist/
+	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-linux-amd64 ./cmd/rekor-cli
+	CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-linux-arm64 ./cmd/rekor-cli
+	CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-darwin-amd64 ./cmd/rekor-cli
+	CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-darwin-arm64 ./cmd/rekor-cli
+	CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-windows-amd64.exe ./cmd/rekor-cli
+
+.PHONY: dist-server
+dist-server:
+	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(SERVER_LDFLAGS) -o rekor-server ./cmd/rekor-server
+
+.PHONY: dist
+dist: dist-server dist-cli
+
+## --------------------------------------
+## Tooling Binaries
+## --------------------------------------
+
+$(SWAGGER): ## Build swagger from tools folder.
+	GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) github.com/go-swagger/go-swagger/cmd/swagger $(SWAGGER_BIN) $(SWAGGER_VER)
diff --git a/scripts/go_install.sh b/scripts/go_install.sh
new file mode 100755
index 0000000000000000000000000000000000000000..789dd69a9abe976162e5047cfef10cff4a602538
--- /dev/null
+++ b/scripts/go_install.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# Copyright 2020 The Sigstore Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+if [ -z "${1}" ]; then
+  echo "must provide module as first parameter"
+  exit 1
+fi
+
+if [ -z "${2}" ]; then
+  echo "must provide binary name as second parameter"
+  exit 1
+fi
+
+if [ -z "${3}" ]; then
+  echo "must provide version as third parameter"
+  exit 1
+fi
+
+if [ -z "${GOBIN}" ]; then
+  echo "GOBIN is not set. Must set GOBIN to install the bin in a specified directory."
+  exit 1
+fi
+
+tmp_dir=$(mktemp -d -t goinstall_XXXXXXXXXX)
+function clean {
+  rm -rf "${tmp_dir}"
+}
+trap clean EXIT
+
+rm "${GOBIN}/${2}"* || true
+
+cd "${tmp_dir}"
+
+# create a new module in the tmp directory
+go mod init fake/mod
+
+# install the golang module specified as the first argument
+go get -tags tools "${1}@${3}"
+mv "${GOBIN}/${2}" "${GOBIN}/${2}-${3}"
+ln -sf "${GOBIN}/${2}-${3}" "${GOBIN}/${2}"