Skip to content
mintlayerPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Mintlayer Web GUI

A Docker Compose stack that runs a full Mintlayer node, a headless wallet, and an Astro-based web interface for wallet management.

Architecture

Browser → Astro web GUI (port 4321)
              │  server-side proxy — credentials never reach the browser
              ▼
        wallet-rpc-daemon  :3034 (internal)
              │  JSON-RPC 2.0
              ▼
         node-daemon  :3030 (internal)
              │  P2P
              ▼
        Mintlayer network

Optional (--profile indexer):
  node-daemon → api-blockchain-scanner-daemon → postgres → api-web-server :3000
              ▲
        web-gui reads the REST API to power Token Management and Trading pages

Without the indexer: the Token Management and Trading pages are hidden. All other features (balance, send, receive, staking, address management) work without it.

Prerequisites

  • Docker with the Compose plugin (v2) or docker-compose (v1)

Browser support

The UI is built with Tailwind CSS 4 and modern CSS (cascade layers, color-mix()), which sets the minimum supported browsers:

Browser Minimum version
Chrome / Edge 111+
Firefox 128+
Safari (macOS) 16.4+
Safari (iOS) 16.4+

Older releases — in particular Safari 16.3 and earlier — may render, but are untested and unsupported.


Running

Recommended — interactive setup

./init.sh

The script walks you through every option (network, wallet, passwords, ports, indexer, Pinata JWT), writes .env, and starts the stack. That's all you need for a first run.

Prebuilt images. CI publishes multi-arch (amd64 + arm64) images to ghcr.io/mintlayer/web-gui (web-gui, bdk-wallet, btc-explorer), and init.sh pulls them automatically — nothing is compiled on the target. First-time pulls of a private package need docker login ghcr.io (or flip the package to public in the GitHub UI). To build from source instead, run docker compose build after checkout.


Using Make

A Makefile wraps the most common Docker Compose commands:

Target What it does
make up Start all services
make down Stop and remove all containers (all profiles + orphans)
make restart-gui Rebuild and restart only the web-gui container
make build Rebuild all images without starting
make logs Tail logs for all services
make dev Start web-gui in dev mode with HMR — full stack including the indexer profile (node + wallet use prod images)
make dev-build Rebuild the dev image (run after adding npm packages)
make wallet-cli Open an interactive wallet-cli session
make bitcoin Start the optional Bitcoin node + BTC wallet
make bitcoin-cli CMD='getblockchaininfo' Run bitcoin-cli inside the node

Manual setup

1. Copy and edit the environment file

cp .env.example .env

Open .env and set at minimum:

Variable What to change
NETWORK mainnet or testnet
NODE_RPC_PASSWORD anything strong
WALLET_RPC_PASSWORD anything strong

2. Create the data directory

mkdir -p mintlayer-data

Node chain data and wallet files are stored here. Inside containers it maps to /home/mintlayer/.

3. Start the stack

docker compose up -d

Services started:

Container What it does
node-daemon Syncs the Mintlayer blockchain (takes hours on first run)
wallet-rpc-daemon Headless wallet — starts with no wallet loaded
web-gui Web interface at http://localhost:4321

4. Create your wallet

Open http://localhost:4321/setup and use the Create new wallet form.

  • Use /home/mintlayer/my_wallet as the path — it appears as ./mintlayer-data/my_wallet on the host
  • Write down the mnemonic shown — it will not be displayed again

After creating your wallet at http://localhost:4321/setup, the dashboard at http://localhost:4321 will show your balance and sync status.

Accessing the GUI from another machine (LAN)? The session cookie is marked Secure, so browsers only accept it on localhost or HTTPS. Login from a plain-HTTP LAN hostname (e.g. http://192.168.1.50:4321) will silently fail. By default the GUI also binds to 127.0.0.1 only — set WEB_GUI_BIND=0.0.0.0 in .env to expose it, and put a TLS reverse proxy in front for real remote access.

Sync time: balance and transaction history only appear once the node has fully synced. On first run this takes several hours for mainnet. The dashboard shows the current block height so you can track progress.


Optional: indexer stack

The indexer adds a PostgreSQL database, a blockchain scanner, and a REST API for querying blocks, transactions, and addresses.

docker compose --profile indexer up -d

The REST API is available at http://localhost:3000 (configurable via API_WEB_SERVER_PORT in .env).

Use a v1.4.1 (or newer) indexer. Older indexers still power the Token Management and Trading pages, but some features detect the version and switch off gracefully:

  • Pending-transaction awareness: toasts for outgoing transactions verify against the indexer's mempool endpoint, so a stuck transaction shows as still pending instead of a false failure.
  • Supply statistics & top holders on the token manage page (v1.4.1 /statistics endpoints).
  • Daemon images are pinned in .env (ML_*_DAEMON_IMAGE); the CI workflow builds them from the Mintlayer version set in .github/workflows/mintlayer-daemons.yml.

Upgrading from an indexer older than v1.4.1 requires a full indexer resync (storage format change); the node and wallet daemons should be upgraded together.


Optional: Bitcoin node + BTC wallet

Adds a Bitcoin Core node and a built-in BTC wallet (balance, receive, send) to the web UI.

docker compose --profile bitcoin up -d      # or: make bitcoin

How it works:

  • bitcoind provides chain data and broadcasts transactions. It needs no host ports.
  • bdk-wallet is a light-wallet sidecar (BDK, BIP84) that holds the BTC keys and signs transactions locally. The web GUI talks to it over the internal Docker network.
  • The wallet seed is generated in the web UI and shown exactly once — back it up when prompted.
  • All BTC API routes require a logged-in session; the sidecar is not reachable from outside.

Requirements and warnings

  • Mainnet chain data is roughly 700 GB with the default txindex=1. The first sync can take days. Use BITCOIN_NETWORK=testnet (or regtest) to try it out cheaply.
  • The BTC wallet is a hot wallet — keep only spending amounts on it.
  • Pruning (BITCOIN_PRUNE) is incompatible with the wallet's history sync; leave it off.
  • Bitcoin Core is pinned to 25.x: the BDK rpc backend cannot parse the warnings format used by Core 26+. Override only if you know what you are doing (BITCOIND_IMAGE).

Useful commands

# Start everything
make up                        # or: docker compose up -d

# Stop everything
make down                      # or: docker compose --profile indexer down --remove-orphans

# Watch logs
docker compose logs -f
docker compose logs -f wallet-rpc-daemon

# Interactive wallet CLI (connects to the running daemon)
make wallet-cli                # or: docker compose --profile wallet_cli run --rm wallet-cli

# Restart after changing .env
docker compose restart

# Pull latest images
docker compose pull && docker compose up -d

Web GUI pages

Page URL Description
Dashboard / Balance, sync status, staking state
Balances /balances Detailed balance breakdown
Management /management Wallet management overview
→ Addresses /management/addresses List and generate receive addresses
→ Transactions /management/transactions Transaction history
→ UTXOs /management/utxos UTXO list
→ Wallet /management/wallet Wallet settings and info
Send /send Send ML to an address
Staking /staking Staking status and instructions
Token Management /token-management Issue and manage tokens — requires indexer
Trading /trading DEX trading — requires indexer
Bitcoin /bitcoin BTC balance, receive and send — requires bitcoin profile
Wallet setup /setup Create or open a wallet

Token Management and Trading are hidden when INDEXER_ENABLED=false in .env.


Development

The quickest loop is make dev: it boots the full stack (node + wallet prod images, indexer profile) with the web-gui source mounted for hot reload at http://localhost:4321.

On first use it generates .env.dev automatically: fixed development credentials (dev / dev etc., from env.dev.example), your host UID/GID, and a fresh random SESSION_SECRET. The dev stack binds the web UI to 127.0.0.1 and publishes no daemon ports, so those fixed credentials are local-only by design — they are never valid for a real deployment (use ./init.sh, which generates random secrets, for that). Delete .env.dev to re-roll the secret.

The daemon images (ghcr.io/mintlayer/web-gui/node-daemon, wallet-rpc-daemon, api-blockchain-scanner-daemon, api-web-server) are private. Pull them after docker login ghcr.io (a GitHub PAT with read:packages), or build them locally with ./build-core-images.sh and run make dev-local instead.

To run the Astro app directly on the host against a running daemon:

cd app
npm install

export WALLET_RPC_URL=http://localhost:3034
export WALLET_RPC_USERNAME=wallet_user
export WALLET_RPC_PASSWORD=your_password

npm run dev
# → http://localhost:4321

To expose the wallet RPC port to the host, uncomment the ports block for wallet-rpc-daemon in docker-compose.yml.


Optional: MCP server (AI assistants)

The image ships a Model Context Protocol server (app/scripts/mcp-server.mjs, stdio) that exposes your wallet to AI assistants such as Claude Desktop or Cursor.

Enable it in the web GUI: Management → Settings → MCP Server. Three tiers:

Tier Grants Requires
read balances, addresses, transactions, UTXOs, staking and order overviews Enabled
actions new address, start/stop staking, abandon transaction Allow wallet actions
spend send coins, sweep, token issuance/management, order trading Allow fund-moving operations

Granting the actions or spend tiers requires 2FA to be configured and a valid authenticator code. Permissions are re-read from the prefs database on every tool call, so changes apply immediately. Secret operations (seed phrase, private-key unlock) are never available through MCP. A per-transaction send cap can be set with the mcp.max_send_amount pref (decimal ML).

Client configuration (shown in the settings panel):

{
  "mcpServers": {
    "mintlayer-wallet": {
      "command": "docker",
      "args": ["compose", "-f", "/path/to/mintlayer-web-gui/docker-compose.yml",
               "run", "--rm", "-T", "--no-deps",
               "web-gui", "node", "scripts/mcp-server.mjs"]
    }
  }
}

Tests: cd app && npx vitest run scripts/mcp-server.test.mjs scripts/mcp-permissions.test.mjs.

Credential recovery

Change password (you know the current one)

Go to Settings → Change password (/management/settings) while logged in.

Reset password (locked out)

If you are locked out and cannot log in, generate a new PBKDF2 hash and write it directly to the prefs database:

# 1. Generate a hash for your new password
NEW_HASH=$(docker run --rm node:22-alpine node -e "
  const crypto = require('crypto');
  const salt = crypto.randomBytes(16).toString('hex');
  crypto.pbkdf2('YOUR_NEW_PASSWORD', salt, 100000, 64, 'sha512', (_, key) => {
    process.stdout.write('pbkdf2:sha512:100000:' + salt + ':' + key.toString('hex'));
  });
")

# 2. Write it to the database
docker run --rm \
  -v "$(pwd)/mintlayer-data/prefs:/prefs" \
  alpine sh -c "apk add -q sqlite && sqlite3 /prefs/mintlayer_prefs.sqlite \
  \"INSERT OR REPLACE INTO prefs VALUES ('auth.password_hash', '\\\"${NEW_HASH}\\\"');\""

Reset TOTP 2FA (lost authenticator)

Use the bundled update-totp script — it generates a new secret, shows a scannable QR code, and only saves after you confirm with a valid code:

# Inside a running container
docker compose exec web-gui node scripts/update-totp.mjs

# One-shot (stack does not need to be running)
docker run --rm -it \
  -v "$(pwd)/mintlayer-data/prefs:/app/prefs" \
  <web-gui-image> node scripts/update-totp.mjs

# On the host (if Node is available)
PREFS_DB_PATH=./mintlayer-data/prefs/mintlayer_prefs.sqlite \
  node app/scripts/update-totp.mjs

After saving, restart the web-gui container so the new secret takes effect:

docker compose restart web-gui

Security

  • Run ./init.sh or set strong passwords in .env before exposing this to any network.
  • Wallet RPC credentials are never sent to the browser — all calls are proxied server-side by the Astro app.
  • Only a fixed allowlist of RPC methods is callable through the /api/rpc endpoint.
  • The wallet RPC (3034) and node RPC (3030) ports are not exposed to the host by default.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages