Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c869139
feat(bitcoin): optional Bitcoin node + BDK wallet sidecar (phase 1+2)
erubboli Sep 1, 2026
7cb38b2
feat(bitcoin): BTC wallet page, settings section, API routes, docs
erubboli Sep 1, 2026
905df0c
fix(bitcoin): issues found during live VM testing
erubboli Sep 1, 2026
2395214
fix(bitcoin): setup robustness for non-auto-start and arbitrary UIDs
erubboli Sep 1, 2026
12ca224
feat(setup): one seed initializes both wallets; fix form CSRF on Chro…
erubboli Sep 1, 2026
d7c57a4
fix(bitcoin): drop Immature balance card from the wallet UI
erubboli Sep 2, 2026
112a8bf
feat(bitcoin): link transactions and addresses to mempool.space
erubboli Sep 2, 2026
86ec642
feat(bitcoin): self-hosted explorer for regtest + explorer URL override
erubboli Sep 2, 2026
96aae2f
feat(addresses): bech32 typo recovery in ML and BTC send flows
erubboli Sep 2, 2026
d01413c
feat(bridge): EVM<->Mintlayer token bridge UI with MetaMask
erubboli Sep 2, 2026
0d4d54f
feat(bridge): live fee totals, receiver typo recovery, copy cleanup
erubboli Sep 2, 2026
0b8c998
feat(bridge): show the bridge only on Mintlayer mainnet
erubboli Sep 2, 2026
9b7f3f1
test(bridge): cover the bridge SDK and the ml-intent-tx route
erubboli Sep 2, 2026
cd98420
ci(docker): publish multi-arch images so installs need zero builds
erubboli Sep 3, 2026
a2e9c52
ci(docker): fix duplicate name key that broke workflow parsing
erubboli Sep 3, 2026
4801781
ci(docker): build Mintlayer daemon images for amd64 + arm64
erubboli Sep 3, 2026
7dd694a
fix(ci+installer): parseable daemon workflow; BTC prune option in ini…
erubboli Sep 3, 2026
03795fa
ci(docker): correct Dockerfile path; don't cancel the other arch
erubboli Sep 3, 2026
3d00fd5
ci(docker): manifest job was missing the /web-gui repo namespace
erubboli Sep 3, 2026
9b7eb57
ci(docker): retry manifest create to ride out GHCR propagation lag
erubboli Sep 3, 2026
9e2b52f
ci(docker): push per-arch daemon images into the /web-gui namespace
erubboli Sep 3, 2026
0edf1ee
ci(docker): derive sha tag from GITHUB_SHA; manifest job has no checkout
erubboli Sep 3, 2026
17e7061
ci(docker): native per-arch builds + manifest merge; attestations bro…
erubboli Sep 4, 2026
3e11a0f
fix(compose+init): bitcoind -chain main (not mainnet); explicit CMD f…
erubboli Sep 4, 2026
954e201
fix(bitcoin): robust one-seed init (retry, logging) and restore-first…
erubboli Sep 4, 2026
dadbe69
feat(https): LAN access via Caddy local CA (init.sh wizard + Settings…
erubboli Sep 4, 2026
2a2d0b8
fix(init): pin image tags to branch slug on feature deploys (:latest …
erubboli Sep 4, 2026
a6754a7
fix(auth): proxy-aware CSRF origin check behind the TLS gateway
erubboli Sep 4, 2026
a9a90f6
fix(init): pre-create uploads/ so the bind mount is not root-owned
erubboli Sep 4, 2026
ceb2cbb
fix(setup): guard against double submit; explain the daemon's one-wal…
erubboli Sep 4, 2026
e8ada0e
feat(bridge): one-click receiver pre-fill; disable page while indexer…
erubboli Sep 4, 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
2 changes: 2 additions & 0 deletions .ecc/memory/project/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
61 changes: 61 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
ML_USER_ID=1000
ML_GROUP_ID=1000

# ── Prebuilt images ──────────────────────────────────
# CI publishes multi-arch (amd64+arm64) images to ghcr.io and init.sh pulls
# them by default. Override refs here; `docker compose build` still compiles
# locally when you want to run from source.
# WEB_GUI_IMAGE=ghcr.io/mintlayer/web-gui/web-gui:latest
# BDK_WALLET_IMAGE=ghcr.io/mintlayer/web-gui/bdk-wallet:latest
# BTC_EXPLORER_IMAGE=ghcr.io/mintlayer/web-gui/btc-explorer:latest
# Mintlayer daemons are also rebuilt from v1.4.0 source for arm64:
# ML_NODE_DAEMON_IMAGE=ghcr.io/mintlayer/web-gui/node-daemon:latest
# ML_WALLET_RPC_DAEMON_IMAGE=ghcr.io/mintlayer/web-gui/wallet-rpc-daemon:latest
# ML_API_SCANNER_IMAGE=ghcr.io/mintlayer/web-gui/api-blockchain-scanner-daemon:latest
# ML_API_WEB_SERVER_IMAGE=ghcr.io/mintlayer/web-gui/api-web-server:latest

# ── Node RPC ─────────────────────────────────────────
# Credentials for the node-daemon RPC server.
NODE_RPC_USERNAME=node_user
Expand Down Expand Up @@ -38,6 +51,23 @@ WEB_GUI_BIND=127.0.0.1
# Used to display the correct URL in the summary and to derive the Passkeys RP ID.
WEB_GUI_HOST=localhost

# ── LAN HTTPS gateway (Caddy local CA, profile: web) ──
# Set WEB_GUI_TLS=true to start the caddy service: it terminates TLS on
# WEB_GUI_TLS_PORT with a certificate for WEB_GUI_HOST from its own local
# Certificate Authority (no public domain needed). web-gui stays on
# 127.0.0.1; devices open https://<WEB_GUI_HOST> and can download the CA
# root from Settings -> HTTPS. Hostname must be letters/digits/dots/hyphens.
WEB_GUI_TLS=false
WEB_GUI_TLS_PORT=443
# Interface the HTTPS port binds to (0.0.0.0 = whole LAN).
WEB_GUI_TLS_BIND=0.0.0.0
# CADDY_IMAGE=caddy:2-alpine
# Note: keep TRUST_PROXY unset (default). Behind caddy every client shares
# the caddy container IP, which merges login rate-limit buckets; enabling
# TRUST_PROXY there would trust an attacker-controllable header. The
# shipped Caddyfile overwrites X-Forwarded-For with the real client IP, so
# only enable TRUST_PROXY if you change that behavior knowingly.

# ── Passkeys (WebAuthn) ───────────────────────────────
# Set by init.sh from WEB_GUI_HOST. Override only if behind a reverse proxy
# that rewrites the hostname (e.g. your Node process sees a different URL
Expand Down Expand Up @@ -87,6 +117,37 @@ PINATA_JWT=
# Found in Pinata dashboard → Gateways. Looks like: yourname.mypinata.cloud
PINATA_GATEWAY_URL=

# ── Bitcoin node + BTC wallet (optional, profile: bitcoin) ──────────
# Light wallet keys live in the bdk-wallet sidecar; bitcoind provides chain
# data and broadcasts transactions. Enable with:
# docker compose --profile bitcoin up -d
BITCOIN_ENABLED=false

# Bitcoin network. Empty = follow NETWORK above (mainnet/testnet).
# Valid values: mainnet, testnet, regtest, signet.
BITCOIN_NETWORK=

# Credentials for the bitcoind RPC (used by the bdk-wallet sidecar).
BITCOIN_RPC_USERNAME=bitcoin_user
BITCOIN_RPC_PASSWORD=bitcoin_password_change_me

# Credentials the web GUI uses to reach the bdk-wallet sidecar's HTTP API.
BITCOIN_WALLET_HTTP_USERNAME=btcwallet_user
BITCOIN_WALLET_HTTP_PASSWORD=btcwallet_password_change_me

# Advanced: transaction index and pruning for bitcoind.
# txindex=1 (default) is required for full wallet history.
# prune>0 saves disk but is incompatible with the wallet history sync
# (mainnet chain data is ~700 GB with txindex=1).
BITCOIN_TXINDEX=1
BITCOIN_PRUNE=0

# Block explorer used for tx/address links in the Bitcoin page.
# Empty default: public networks link to mempool.space; regtest links to
# the self-hosted btc-rpc-explorer sidecar (http://localhost:3002).
# Set to any explorer base URL to override both.
BITCOIN_EXPLORER_URL=

# ── Watchtower (optional, profile: watchtower) ───────
# Auto-updates Mintlayer Docker images daily at 04:00.
# Start with: docker compose --profile watchtower up -d
Expand Down
174 changes: 174 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
name: Docker Images

# Builds the deployable images (web-gui, bdk-wallet, btc-explorer) for
# amd64 + arm64 so a VPS / Raspberry Pi can `docker compose pull` without
# building anything locally.
#
# Native builds on GitHub's free amd64 + arm64 runners (public repos);
# per-arch images pushed as <ref>-amd64 / -arm64, then merged into
# multi-arch manifests. This mirrors mintlayer-daemons.yml: buildx
# multi-platform pushes with provenance attestations produced indexes
# GHCR would not serve (manifest unknown on pull, even by digest).
#
# - push to main -> ghcr.io/mintlayer/web-gui/<image>:latest (+ :sha)
# - push to other branches-> ghcr.io/mintlayer/web-gui/<image>:br-<branch>
# - tag v* -> ghcr.io/mintlayer/web-gui/<image>:v<tag>
# - pull_request -> build only (no push), catches Dockerfile breakage

on:
push:
branches: ["**"]
tags: ["v*"]
pull_request:
branches: [main]

env:
REGISTRY: ghcr.io
OWNER: mintlayer
IMAGE_NS: ghcr.io/mintlayer/web-gui
IMAGES: "web-gui bdk-wallet btc-explorer"

permissions:
contents: read
packages: write

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runs-on: ubuntu-latest
- arch: arm64
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v7

# The bdk-wallet release build needs serious disk; prune the runner's
# big preinstalls.
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
/opt/hostedtoolcache/CodeQL /usr/local/share/boost || true
sudo docker image prune -af || true
df -h /

- uses: docker/setup-buildx-action@v3

- uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Compute tag base
id: tagbase
if: github.event_name != 'pull_request'
env:
GITHUB_DEFAULT_BRANCH: main
run: |
if [[ "$GITHUB_REF" == "refs/tags/v"* ]]; then
echo "base=v${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
elif [[ "$GITHUB_REF_NAME" == "$GITHUB_DEFAULT_BRANCH" ]]; then
echo "base=latest" >> "$GITHUB_OUTPUT"
else
base="br-${GITHUB_REF_NAME//\//-}"
echo "base=$base" >> "$GITHUB_OUTPUT"
fi
echo "sha=sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"

- name: Build and push per-arch images
if: github.event_name != 'pull_request'
env:
BASE: ${{ steps.tagbase.outputs.base }}
SHA_TAG: ${{ steps.tagbase.outputs.sha }}
ARCH: ${{ matrix.arch }}
run: |
set -eu
declare -A CONTEXTS=( [web-gui]=./app [bdk-wallet]=./bdk-wallet [btc-explorer]=./btc-explorer )
for image in $IMAGES; do
for variant in "$BASE" "$SHA_TAG"; do
docker buildx build \
--platform "linux/$ARCH" \
--provenance=false \
--push \
-t "$IMAGE_NS/$image:$variant-$ARCH" \
"${CONTEXTS[$image]}"
done
done

- name: Build only (pull_request)
if: github.event_name == 'pull_request'
run: |
set -eu
declare -A CONTEXTS=( [web-gui]=./app [bdk-wallet]=./bdk-wallet [btc-explorer]=./btc-explorer )
for image in $IMAGES; do
docker buildx build \
--platform "linux/${{ matrix.arch }}" \
--provenance=false \
--load \
"${CONTEXTS[$image]}"
done

manifest:
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
env:
DOCKER_CLI_EXPERIMENTAL: enabled
steps:
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Compute tag base
id: tagbase
env:
GITHUB_DEFAULT_BRANCH: main
run: |
if [[ "$GITHUB_REF" == "refs/tags/v"* ]]; then
echo "base=v${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
elif [[ "$GITHUB_REF_NAME" == "main" ]]; then
echo "base=latest" >> "$GITHUB_OUTPUT"
else
base="br-${GITHUB_REF_NAME//\//-}"
echo "base=$base" >> "$GITHUB_OUTPUT"
fi
echo "sha=sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"

- name: Create and push multi-arch manifests
env:
BASE: ${{ steps.tagbase.outputs.base }}
SHA_TAG: ${{ steps.tagbase.outputs.sha }}
ARCHES: "amd64 arm64"
IMAGE_NS: ${{ env.IMAGE_NS }}
run: |
set -eu
# GHCR is eventually consistent right after a push: manifest
# creates can hit "manifest unknown" for a minute or two. Retry.
sleep 30
for image in $IMAGES; do
for variant in "$BASE" "$SHA_TAG"; do
args=()
for arch in $ARCHES; do
args+=("--amend" "$IMAGE_NS/$image:$variant-$arch")
done
attempt=1
until docker manifest create "$IMAGE_NS/$image:$variant" "${args[@]}" >/dev/null; do
if [ "$attempt" -ge 6 ]; then
echo "::error::manifest create failed for $IMAGE_NS/$image:$variant after $attempt attempts"
exit 1
fi
echo "attempt $attempt failed - retrying in 30s"
sleep 30
attempt=$((attempt + 1))
done
docker manifest push "$IMAGE_NS/$image:$variant"
done
done
Loading
Loading