Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Bitweb API Server

REST + WebSocket backend for the Bitweb wallet. Bridges HTTP/WS clients to an ElectrumX node over TLS, providing address balances, UTXO sets, transaction history, and real-time push notifications via Socket.IO.


Architecture

Browser / Wallet
│ REST + Socket.IO
▼
Nginx (TLS termination, rate limiting)
│ HTTP proxy
▼
Gunicorn + gevent (app.py → server/)
│ ElectrumX JSON-RPC over TLS
▼
ElectrumX node (electrumx.example.com:20002)

The server maintains:

  • Connection pool (ElectrumPool) — N parallel persistent TLS connections to ElectrumX, used for all REST calls.
  • Subscriber (ElectrumSubscriber) — one dedicated persistent connection for server-push notifications (new blocks, scripthash changes).
  • In-memory caches — coinbase cache, TX cache, history cache, tip-height cache; all bounded and self-evicting.

API Endpoints

MethodPathDescription
GET/infoCurrent block height
GET/balance/<address>Confirmed + unconfirmed balance (satoshis)
GET/unspent/<address>UTXO list. Optional: ?amount=<min_sat>?confirmed=true
GET/history/<address>Last N txs with direction/amount. Optional: ?limit=<n> (max 50)
GET/tx/<txid>Verbose transaction (vout includes value_sat)
GET/rawtx/<txid>Raw transaction hex
GET/feeFixed fee rate (satoshis)
POST/broadcastBroadcast raw tx hex (form field raw or raw body)

Response envelope

{ "error": null, "id": "api-server", "result": { ... } }
{ "error": { "code": 400, "message": "..." }, "id": "api-server", "result": null }

WebSocket (Socket.IO)

Connect to wss://api.example.com/socket.io/.

Client → Server

socket.emit("subscribe",{address: "WxxxYour address here"})

Server → Client

EventPayload
subscribed{ address }
balance_changed{ balance, confirmed, unconfirmed, pending_out, utxos[], height }
block{ height }
error{ message }

Requirements

System

  • OS: Ubuntu 22.04 / 24.04 LTS (or any systemd-based Linux)
  • Python: 3.10 or newer
  • Nginx: 1.18+ (for TLS termination and rate limiting)
  • Certbot: for Let's Encrypt certificates (optional but recommended)

Python packages

Listed in requirements.txt:

flask
flask-cors
flask-socketio
gevent
gevent-websocket
bech32
base58
gunicorn
python-dotenv

Install

1. Create a dedicated user

sudo useradd -r -m -s /bin/bash bitweb

2. Clone / copy the project

sudo mkdir -p /opt/bitweb-api
sudo cp -r . /opt/bitweb-api/
sudo chown -R bitweb:bitweb /opt/bitweb-api

3. Create a virtual environment and install dependencies

cd /opt/bitweb-api
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt

4. Configure environment variables

Copy the example and edit:

sudo cp /opt/bitweb-api/env.example /opt/bitweb-api/.env
sudo nano /opt/bitweb-api/.env
sudo chown bitweb:bitweb /opt/bitweb-api/.env
sudo chmod 600 /opt/bitweb-api/.env

See Configuration for all available variables.

5. Create the systemd service

sudo nano /etc/systemd/system/bitweb-api.service
[Unit]Description=Bitweb API Server
After=network.target
[Service]User=bitweb
Group=bitweb
WorkingDirectory=/opt/bitweb-api
EnvironmentFile=/opt/bitweb-api/.env
Environment="PATH=/opt/bitweb-api/venv/bin"ExecStart=/opt/bitweb-api/venv/bin/gunicorn \
--bind 127.0.0.1:21223 \
--worker-class gevent \
--workers 1 \
--timeout 0 \
--keep-alive 75 \
app:app
Restart=on-failure
RestartSec=5s
[Install]WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bitweb-api
sudo systemctl status bitweb-api

6. Configure Nginx

See the included api2.bitwebcore.example for a production-ready Nginx config. Replace api.example.com with your domain.

sudo cp api2.bitwebcore.example /etc/nginx/conf.d/bitweb-api.conf
# edit domain
sudo nginx -t && sudo systemctl reload nginx

7. Obtain a TLS certificate (Let's Encrypt)

sudo certbot --nginx -d your.domain.com

Configuration

All settings are read from a .env file in the project root (via python-dotenv).

VariableDefaultDescription
SECRET_KEYchange-me-in-productionFlask session secret — change this
HOST0.0.0.0Bind address (Gunicorn listens here)
PORT21223Bind port
DEBUGfalseFlask debug mode — never enable in production
ELECTRUM_HOSTelectrumx.example.comElectrumX hostname
ELECTRUM_PORT20002ElectrumX TLS port
ELECTRUM_TIMEOUT15Per-call timeout in seconds
ELECTRUM_VERIFY_SSLtrueSet false only for self-signed certs
ELECTRUM_POOL_SIZE4Number of parallel ElectrumX connections
FIXED_FEE_SATOSHIS10000Reported fee rate from /fee

Example .env

SECRET_KEY=replace-with-a-long-random-stringHOST=0.0.0.0PORT=21223DEBUG=falseELECTRUM_HOST=electrumx.example.comELECTRUM_PORT=20002ELECTRUM_TIMEOUT=15ELECTRUM_VERIFY_SSL=trueELECTRUM_POOL_SIZE=4FIXED_FEE_SATOSHIS=10000

Security note:.env contains secrets. It is owned by the service user (bitweb) with mode 600 and must never be committed to version control. Add .env to .gitignore.


Running

Via systemd (production)

sudo systemctl start bitweb-api
sudo systemctl stop bitweb-api
sudo systemctl restart bitweb-api
sudo systemctl status bitweb-api

Logs

sudo journalctl -u bitweb-api -f # follow live
sudo journalctl -u bitweb-api -n 100 # last 100 lines
sudo journalctl -u bitweb-api --since today

Development (local)

python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp env.example .env # edit as needed
python3 app.py

Tests

python3 test_address.py

Tests cover base58check decoding, all version bytes (mainnet/testnet P2PKH and P2SH), bech32 P2WPKH/P2WSH with the web HRP, and script-hash derivation.


Project Structure

.
├── app.py Entry point (gevent monkey-patch + gunicorn target)
├── requirements.txt Python dependencies
├── env.example Configuration template
├── server/
│ ├── __init__.py Flask app factory, SocketIO init
│ ├── rest.py All REST routes + WebSocket event handlers
│ ├── electrum.py ElectrumX client / pool / subscriber
│ ├── address.py Address → scriptPubKey / scriptHash conversion
│ ├── segwit_addr.py Bech32 / Bech32m reference implementation
│ └── utils.py JSON response helpers
├── test_address.py Unit tests for address.py
└── api.example_nginx Production Nginx config example

License

MIT — see LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages