Skip to content
Draft
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
dcff36a
cargo update
ala-mode Feb 1, 2026
2a4fa5c
add stagex util scripts
ala-mode Feb 1, 2026
886bbe1
add stagex Dockerfile
ala-mode Feb 1, 2026
3f9bc84
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 2, 2026
120d83a
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 3, 2026
1f39bce
cleanup
ala-mode Feb 13, 2026
d0c7e9b
update README
ala-mode Feb 13, 2026
065ad85
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 13, 2026
03aae29
add Makefile
ala-mode Feb 13, 2026
b28552a
remove duplicate dependency
ala-mode Feb 13, 2026
55a7aeb
cargo update
ala-mode Feb 13, 2026
4507a24
busybox runtime, network-patch for build script, force static linking…
ala-mode Feb 13, 2026
fdc0f7f
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 13, 2026
d33203d
fix spelling in README
ala-mode Feb 15, 2026
a4993db
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 16, 2026
8413b7f
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 16, 2026
6beb9fc
add ENTRYPOINT
ala-mode Feb 16, 2026
30dfa55
point ENTRYPOINT
ala-mode Feb 16, 2026
757e5b9
add entrypoint.sh
ala-mode Feb 17, 2026
e3886b4
copy entrypoint.sh from builder stage, exec mode CMD
ala-mode Feb 17, 2026
c8b4e8d
etc/ permissions, higher UID, standardize runtime dirs, USER root, co…
ala-mode Feb 17, 2026
3b8d6dd
WIP, setpriv, dir ownership, create wallet dir
ala-mode Feb 17, 2026
4078295
WIP, util-linux, setpriv
ala-mode Feb 18, 2026
c19186b
WIP, working Dockerfile, no entrypoint use
ala-mode Feb 21, 2026
2a7750f
restrict permissions, change back to use entrypoint.sh
ala-mode Feb 23, 2026
d31a00d
entrypoint.sh overhaul: simple sync, version pint, info, tail for per…
ala-mode Feb 23, 2026
2dec19e
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 23, 2026
503d9e1
update Makefile
ala-mode Feb 24, 2026
1fcb81f
add create_wallet.sh
ala-mode Feb 24, 2026
1193c34
update entrypoint.sh
ala-mode Feb 24, 2026
853cd17
update entrypoint.sh with if ./initialized
ala-mode Feb 24, 2026
17e3a47
cleanup
ala-mode Feb 24, 2026
732854e
tune ENTRYPOINT, tighten permissions, cleanup
ala-mode Feb 24, 2026
26c4d76
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 24, 2026
66cc448
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 25, 2026
9347d26
update entrypoint.sh and create_wallet
ala-mode Feb 25, 2026
7131ec3
add interact.sh and load_image.sh
ala-mode Feb 25, 2026
f98ae9e
update Makefile with load, interact
ala-mode Feb 25, 2026
51a3d14
good docker run in interact.sh
ala-mode Feb 25, 2026
2a3f426
cleanup Dockerfile
ala-mode Feb 25, 2026
6317011
cleanup
ala-mode Feb 26, 2026
6f86136
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 26, 2026
a195817
correct load_image.sh
ala-mode Feb 26, 2026
17492b2
cleanup create_wallet.sh
ala-mode Feb 26, 2026
c69fce8
Merge branch 'dev' into stagex_reproducable_build
ala-mode Feb 27, 2026
e4377f0
Merge branch 'dev' into stagex_reproducable_build
ala-mode Mar 2, 2026
3552ba1
Merge branch 'dev' into stagex_reproducable_build
ala-mode Mar 9, 2026
964c5fe
Merge branch 'dev' into stagex_reproducable_build
ala-mode Mar 13, 2026
d06126a
Merge branch 'dev' into stagex_reproducable_build
ala-mode Mar 26, 2026
cadf0c5
Merge branch 'dev' into stagex_reproducable_build
ala-mode Mar 26, 2026
5c8375d
Merge branch 'dev' into stagex_reproducable_build
ala-mode Mar 28, 2026
ea907d2
readd --quiet
ala-mode Mar 28, 2026
00fcb0c
update README
ala-mode Mar 28, 2026
fcacead
CMD defaults to simple --help
ala-mode Mar 28, 2026
2ab4171
expand README
ala-mode Mar 28, 2026
5657724
rm create and interact from Makefile
ala-mode Mar 28, 2026
41a5524
rm scripts for create wallet and interact
ala-mode Mar 28, 2026
fe29200
mv notes to README
ala-mode Mar 28, 2026
98f9b02
update StageX images, WIP ARGs etc
ala-mode Mar 28, 2026
e361630
mount cache paths with env vars
ala-mode Mar 28, 2026
fcbf14c
set workdir with ARG
ala-mode Mar 29, 2026
d112222
WIP release stage ARGs and ENVs
ala-mode Mar 29, 2026
1c3e5e0
complete ARGs and ENVs in release stage, rm cargo metadata, begin cac…
ala-mode Mar 29, 2026
8ff49f4
WIP transition to bind mounts
ala-mode Mar 29, 2026
d5ba6c3
set bind paths to project root, working hacks for params build script
ala-mode Mar 31, 2026
8850f20
reset cargo install path for binary
ala-mode Mar 31, 2026
a1a4030
add optional FEATURES to build, add GH issue #s, cleanup
ala-mode Mar 31, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.helix
build/
cobertura.xml
target
wallets/*
Expand Down
121 changes: 121 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# syntax=docker/dockerfile:1
# check=skip=UndefinedVar,UserExist

############################
# Global build args
############################
ARG RUST_VERSION=1.91.1
ARG UID=10901
ARG GID=${UID}
ARG USER=user
ARG HOME=/home/user

############################
# Dependencies
############################
# Build Deps
FROM stagex/pallet-rust@sha256:4062550919db682ebaeea07661551b5b89b3921e3f3a2b0bc665ddea7f6af1ca AS pallet-rust
FROM stagex/user-protobuf@sha256:b399bb058216a55130d83abcba4e5271d8630fff55abbb02ed40818b0d96ced1 AS protobuf
FROM stagex/user-abseil-cpp@sha256:926f69e9cd112dfe3450a0af56d1560dc0a62589e61047e8c92c3b7edf8dd71e AS abseil-cpp
FROM stagex/core-sqlite3@sha256:44807b914585c81dda2bb0a5617cab53395255fe6685ce9599628060229c8929 AS sqlite3
# Runtime Deps
FROM stagex/core-busybox@sha256:d608daa946e4799cf28b105aba461db00187657bd55ea7c2935ff11dac237e27 AS busybox

############################
# Builder
############################
FROM pallet-rust AS builder
COPY --from=protobuf . /
COPY --from=abseil-cpp . /
COPY --from=sqlite3 . /

SHELL ["/bin/sh", "-euo", "pipefail", "-c"]
WORKDIR /usr/src/app

# Set environment variables
ENV SOURCE_DATE_EPOCH=1
ENV CARGO_HOME=/usr/local/cargo

ENV RUST_BACKTRACE=1
ENV TARGET_ARCH="x86_64-unknown-linux-musl"
ENV RUSTFLAGS="-C codegen-units=1"
ENV RUSTFLAGS="${RUSTFLAGS} -C target-feature=+crt-static"
ENV RUSTFLAGS="${RUSTFLAGS} -C link-arg=-Wl,--build-id=none"

# Copy entire workspace
COPY . .

RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
cargo fetch --locked --target $TARGET_ARCH

RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
cargo metadata --locked --format-version=1 > /dev/null 2>&1

# TODO : --network=none was removed due to network requests in build script
# this needs to be re-added to ensure hermeticity
RUN --mount=type=cache,target=/usr/local/cargo/registry \
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use --network=none

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is being picked on in this issue.

--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/usr/src/app/target \
cargo build --release --frozen --target $TARGET_ARCH --bin zingo-cli && install -D -m 0755 /usr/src/app/target/${TARGET_ARCH}/release/zingo-cli /usr/local/bin/zingo-cli

############################
# Export stage
############################
FROM scratch AS export
COPY --from=builder /usr/local/bin/zingo-cli /zingo-cli

############################
# Runtime stage
############################
FROM busybox AS runtime

# Create a non-privileged user for running `zingo-cli`.
#
# We use a high UID/GID (10901) to avoid overlap with host system users.
# This reduces the risk of container user namespace conflicts with host accounts,
# which could potentially lead to privilege escalation if a container escape occurs.
#
# We do not use the `--system` flag for user creation since:
# 1. System user ranges (100-999) can collide with host system users
# (see: https://github.com/nginxinc/docker-nginx/issues/490)
# 2. There's no value added and warning messages can be raised at build time
# (see: https://github.com/dotnet/dotnet-docker/issues/4624)
#
# The high UID/GID values provide an additional security boundary in containers
# where user namespaces are shared with the host.
ARG UID
ENV UID=${UID}
ARG GID
ENV GID=${GID}
ARG USER
ENV USER=${USER}
ARG HOME
ENV HOME=${HOME}

COPY --chmod=550 <<-EOF /etc/passwd
root:x:0:0:root:/root:/bin/sh
user:x:${UID}:${GID}::${HOME}:/bin/sh
EOF

COPY --chmod=550 <<-EOF /etc/group
root:x:0:
user:x:${GID}:
EOF

WORKDIR /usr/local/bin

USER root
RUN mkdir -p /usr/local/bin/wallets && chown -R ${UID}:${GID} /usr/local/bin/ && chmod -R 770 /usr/local/bin/
COPY --chown=${UID}:${GID} --from=export /zingo-cli /usr/local/bin/zingo-cli
RUN chmod 550 /usr/local/bin/zingo-cli
COPY --chown=${UID}:${GID} ./utils/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod 550 /usr/local/bin/entrypoint.sh

USER $USER
# ./entrypoint.sh runs, then executes CMD (or custom command if provided).
# Prints zingo-cli version, address if a new wallet is created, and info on success.
ENTRYPOINT [ "./entrypoint.sh" ]
# Default command. Selected server uses zebra 4.1.0 and lwd v0.4.18-9-gb932e8e at time of commit.
CMD [ "./zingo-cli", "--server", "https://zzz.stripest.online:443", "settings" ]
34 changes: 34 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Simple wrapper for scripts with printed status messages.
#
# Running `make` or `make stagex` will leverage the steps below
# to check compatibility and build the binaries via StageX.

.PHONY: stagex compat build load create interact

stagex: compat build
@echo "[Stageˣ] build completed via make."

compat:
@echo "Beginning Compatibility Check step."
@./utils/compat.sh
@echo " [PASS] Compatibility Check passed."

build:
@echo "Entering Build step."
@./utils/build.sh
@echo "Build step complete."

load:
@echo "Attempting to load OCI image into local docker image store."
@./utils/load_image.sh
@echo "make load step complete."

create:
@echo "Attempting to make zingo-cli wallet, if there is none. The Docker container's runtime shares the host kernel's entropy source."
@./utils/create_wallet.sh
@echo "Wallet creation script complete."

interact:
@echo "Starting interactive session with zingo-cli."
@./utils/interact.sh
@echo "Interactive session complete."
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ cargo build --release --package zingo-cli

This will launch the interactive prompt. Type `help` to get a list of commands.

## Reproducible builds via StageX

A bootstrapped and reproducible build pipeline using StageX is included in this repo.
If you meet all the compatibility requirements, to create `zingo-cli`, you can run
`make` in the root directory. The resulting binary will be found in the
`/build/` directory.

## Notes:
* If you want to run your own server, please see [zingo lightwalletd](https://github.com/zingolabs/lightwalletd), and then run `./zingo-cli --server http://127.0.0.1:9067`
* The default log file is in `~/.zcash/zingo-wallet.debug.log`. A default wallet is stored in `~/.zcash/zingo-wallet.dat`
Expand Down
32 changes: 32 additions & 0 deletions utils/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/sh

set -e

DIR="$( cd "$( dirname "$0" )" && pwd )"
REPO_ROOT="$(git rev-parse --show-toplevel)"
PLATFORM="linux/amd64"
OCI_OUTPUT="$REPO_ROOT/build/oci"
DOCKERFILE="$REPO_ROOT/Dockerfile"

export DOCKER_BUILDKIT=1
export SOURCE_DATE_EPOCH=1

echo $DOCKERFILE
mkdir -p $OCI_OUTPUT

# Build runtime image for docker run
echo "Building runtime image..."
docker build -f "$DOCKERFILE" "$REPO_ROOT" \
--platform "$PLATFORM" \
--target runtime \
--output type=oci,rewrite-timestamp=true,force-compression=true,dest=$OCI_OUTPUT/zingo-cli.tar,name=zingo-cli \
"$@"

# Extract binary locally from export stage
# removed --quiet
echo "Extracting binary..."
docker build -f "$DOCKERFILE" "$REPO_ROOT" \
--platform "$PLATFORM" \
--target export \
--output type=local,dest="$REPO_ROOT/build" \
"$@"
80 changes: 80 additions & 0 deletions utils/compat.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env bash
set -e
readonly MIN_BASH_VERSION=5
readonly MIN_DOCKER_VERSION=26.0.0
readonly MIN_BUILDX_VERSION=0.13
### Exit with error message
die() {
echo "$@" >&2
exit 1
}
### Bail and instruct user on missing package to install for their platform
die_pkg() {
local -r package=${1?}
local -r version=${2?}
local install_cmd
case "$OSTYPE" in
linux*)
if command -v "apt" >/dev/null; then
install_cmd="apt install ${package}"
elif command -v "yum" >/dev/null; then
install_cmd="yum install ${package}"
elif command -v "pacman" >/dev/null; then
install_cmd="pacman -Ss ${package}"
elif command -v "emerge" >/dev/null; then
install_cmd="emerge ${package}"
elif command -v "nix-env" >/dev/null; then
install_cmd="nix-env -i ${package}"
fi
;;
bsd*) install_cmd="pkg install ${package}" ;;
darwin*) install_cmd="port install ${package}" ;;
*) die "Error: Your operating system is not supported" ;;
esac
echo "Error: ${package} ${version}+ does not appear to be installed." >&2
[ -n "$install_cmd" ] && echo "Try: \`${install_cmd}\`" >&2
exit 1
}
### Check if actual binary version is >= minimum version
check_version(){
local pkg="${1?}"
local have="${2?}"
local need="${3?}"
local i ver1 ver2 IFS='.'
[[ "$have" == "$need" ]] && return 0
read -r -a ver1 <<< "$have"
read -r -a ver2 <<< "$need"
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++));
do ver1[i]=0;
done
for ((i=0; i<${#ver1[@]}; i++)); do
[[ -z ${ver2[i]} ]] && ver2[i]=0
((10#${ver1[i]} > 10#${ver2[i]})) && return 0
((10#${ver1[i]} < 10#${ver2[i]})) && die_pkg "${pkg}" "${need}"
done
}
### Check if required binaries are installed at appropriate versions
check_tools(){
if [ -z "${BASH_VERSINFO[0]}" ] \
|| [ "${BASH_VERSINFO[0]}" -lt "${MIN_BASH_VERSION}" ]; then
die_pkg "bash" "${MIN_BASH_VERSION}"
fi
for cmd in "$@"; do
case $cmd in
buildx)
docker buildx version >/dev/null 2>&1 || die "Error: buildx not found"
version=$(docker buildx version 2>/dev/null | grep -o 'v[0-9.]*' | sed 's/v//')
check_version "buildx" "${version}" "${MIN_BUILDX_VERSION}"
;;
docker)
command -v docker >/dev/null || die "Error: docker not found"
version=$(docker version -f '{{ .Server.Version }}')
check_version "docker" "${version}" "${MIN_DOCKER_VERSION}"
;;
esac
done
}
check_tools docker buildx;
docker info -f '{{ .DriverStatus }}' \
| grep "io.containerd.snapshotter.v1" >/dev/null \
|| die "Error: Docker Engine is not using containerd for image storage"
12 changes: 12 additions & 0 deletions utils/create_wallet.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh

set -e

echo "Checking local docker image store to see if a zingo-cli:latest image is present."
# Checks for empty string, discarding error messages.
if [ -z "$(docker images -q zingo-cli:latest 2>/dev/null)" ]; then
echo "There is no zingo-cli:latest image listed by docker."
else
echo "Creating wallet if there is none, then printing wallet's orchard u address."
docker run zingo-cli:latest
fi
48 changes: 48 additions & 0 deletions utils/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh

# Entrypoint for running zingo-cli in Docker.
#
# The main script logic is at the bottom.
#
# ## Notes
#
# zingo-cli runs with several defaults.
# Importantly, these include a data-dir with wallet file,
# which are created if they don't already exist:
# a `wallets` dir in location where executable is run,
# containing the wallet (`zingo-wallet.dat`) file.
# other defaults inlcude setting the chain to mainnet,
# using a default lightwallet server, using clearnet for price fetching,
# and not executing commands prior to a complete chain sync.

set -eo pipefail

# Currently there is no support for running tests in-container, due to
# requiring additional binaries.
#
# Main Script Logic
#
# 1. Print environment variables and config for debugging.
# 2. Tests if zingo-cli runs.
# 3. Creates a wallet, if the container has not been initialized before.
# 4. Process command-line arguments and execute appropriate action.

echo "INFO: Using the following environment variables:"
printenv

echo "Testing zingo-cli to print version string:"
./zingo-cli --version

if [ ! -f ./initialized ]; then
# A wallet will be created in this container if there is none. The address of the new wallet will be printed after sync."
echo "Container not initialized, creating wallet, syncing, and printing address..."
# selected server = zebra 4.1.0 and lwd v0.4.18-9-gb932e8e at time of commit
./zingo-cli --server https://zzz.stripest.online:443 --waitsync addresses
touch ./initialized
fi

echo "Lightwalletd server's info info:"
./zingo-cli --server https://zzz.stripest.online:443 --nosync info

echo "now exec'ing $@ "
exec "$@"
12 changes: 12 additions & 0 deletions utils/interact.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh

set -e

# Build runtime image for docker run
echo "Checking if the OCI output from build is present."
if [ -z "$$(docker images -q zingo-cli:latest 2>/dev/null)" ]; then
echo "There is no `zingo-cli:latest` image listed by docker."
else
echo "Creating wallet if there is none, then opening zingo-cli interactively."
docker run -it zingo-cli:latest ./zingo-cli --server https://zzz.stripest.online:443
fi
18 changes: 18 additions & 0 deletions utils/load_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh

set -e

REPO_ROOT="$(git rev-parse --show-toplevel)"
OCI_OUTPUT="$REPO_ROOT/build/oci"
TARBALL="${OCI_OUTPUT}/zingo-cli.tar"

# Build runtime image for docker run
echo "Checking if the OCI output from build is present."
if [ -f "$TARBALL" ];
then
echo "OCI output file present, loading tar file into local docker image store."
docker load < $TARBALL
echo "...Done!"
else
echo "OCI output file not present."
fi