A lightweight REST proxy that makes Lightning Labs' Taproot Assets daemon accessible to web applications by adding CORS support and simplifying authentication.
This is community-developed software that interfaces with Lightning Labs' Taproot Assets daemon. It is not affiliated with or endorsed by Lightning Labs.
| Component | Minimum version |
|---|---|
tapd |
0.8.0 |
lnd |
0.19.0 (required by tapd 0.8) |
tapd 0.8.0 or newer is required. The gateway sends asset_specifier on
/v1/taproot-assets/burn and exposes the wallet backup, mailbox remove, and
RFQ portfolio pilot endpoints, none of which exist on tapd 0.7.x. Because
tapd rejects unknown request fields, burning assets against 0.7.x fails with
unknown field "asset_specifier". Run gateway v0.2.x if you need tapd 0.7.x.
Lightning Labs' tapd REST API (port 8089) doesn't support CORS, making it impossible to use directly from web browsers. Additionally, managing macaroons and TLS certificates adds complexity for developers who just want to integrate Taproot Assets into their applications.
This gateway acts as a proxy between your web application and tapd, handling:
- CORS headers - Enables browser-based applications
- Macaroon authentication - No manual base64 encoding or header management
- TLS complexity - Configurable verification for development
- Better error messages - Meaningful errors instead of raw gRPC codes
- Request tracking - UUID for each request aids debugging
If you're building a web app that needs Taproot Assets, you have three options:
- Use tapd's gRPC API - Requires gRPC-web, complex for browsers
- Use tapd's REST API directly - No CORS support, won't work from browsers
- Use this gateway - Works immediately from any web app
- ✅ Complete REST API coverage for tapd
- ✅ CORS support for web browsers
- ✅ Automatic macaroon authentication
- ✅ Request ID tracking
- ✅ Basic rate limiting
- ✅ Docker support
- ✅ Health check endpoints
- 🚧 WebSocket support for real-time events (in progress)
- ❌ Response caching
- ❌ Metrics/monitoring endpoints
- ❌ Load balancing for multiple tapd instances
- ❌ Advanced rate limiting (per endpoint/user)
# Clone and configure
git clone https://github.com/yourusername/taproot-assets-rest-gateway.git
cd taproot-assets-rest-gateway
cp .env.example .env
# Edit .env with your tapd details
# Run with Docker
docker-compose up -d
# Or run directly
cargo run --release# Required
TAPROOT_ASSETS_HOST=127.0.0.1:8289
TAPD_MACAROON_PATH=/path/to/tapd/admin.macaroon
LND_MACAROON_PATH=/path/to/lnd/admin.macaroon
# Security (use true in production)
TLS_VERIFY=false
# CORS - Add your app's URL
CORS_ORIGINS=http://localhost:3000,http://localhost:5173
# Optional
SERVER_ADDRESS=127.0.0.1:8080
REQUEST_TIMEOUT_SECS=30
RATE_LIMIT_PER_MINUTE=100Web App → REST Gateway → tapd gRPC/REST
↓
[CORS Headers]
[Macaroon Auth]
[Rate Limiting]
[Error Handling]
The gateway forwards requests to tapd's REST API (port 8089) while adding the necessary headers and authentication that web browsers require.
For complete API reference, see the official Taproot Assets API documentation.
For a hands-on tutorial covering setup, minting, sending, receiving, and building a React app with Taproot Assets, check out:
👉 Taproot Assets Web Development Workshop
The workshop includes:
- Part 0: Gateway setup with Polar
- Part 1: Understanding CORS and authentication barriers
- Part 2: Complete asset lifecycle (mint, send, receive)
- Part 3: Production React app with TypeScript
curl -X POST http://localhost:8080/v1/taproot-assets/assets \
-H "Content-Type: application/json" \
-d '{
"asset": {
"asset_type": "NORMAL",
"name": "MyToken",
"amount": "1000"
},
"short_response": true
}'// No authentication headers needed - the gateway handles it
const GATEWAY_URL = 'http://localhost:8080';
// List your assets
async function listAssets() {
const response = await fetch(`${GATEWAY_URL}/v1/taproot-assets/assets`);
const data = await response.json();
return data.assets;
}
// Create a new address
async function createAddress(assetId, amount) {
const response = await fetch(`${GATEWAY_URL}/v1/taproot-assets/addrs`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
asset_id: assetId,
amt: amount.toString()
})
});
return response.json();
}
// Send assets
async function sendAssets(toAddress, feeRate = 5) {
const response = await fetch(`${GATEWAY_URL}/v1/taproot-assets/send`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tap_addrs: [toAddress],
fee_rate: feeRate
})
});
return response.json();
}import requests
GATEWAY_URL = 'http://localhost:8080'
# No need to handle macaroons or TLS certificates
def list_assets():
response = requests.get(f'{GATEWAY_URL}/v1/taproot-assets/assets')
return response.json()['assets']
def mint_asset(name, amount):
response = requests.post(
f'{GATEWAY_URL}/v1/taproot-assets/assets',
json={
'asset': {
'asset_type': 'NORMAL',
'name': name,
'amount': str(amount)
},
'short_response': True
}
)
return response.json()
def get_balance():
response = requests.get(f'{GATEWAY_URL}/v1/taproot-assets/assets/balance')
return response.json()package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
const gatewayURL = "http://localhost:8080"
type Asset struct {
AssetID string `json:"asset_id"`
Amount string `json:"amount"`
}
func listAssets() ([]Asset, error) {
resp, err := http.Get(gatewayURL + "/v1/taproot-assets/assets")
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result struct {
Assets []Asset `json:"assets"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return result.Assets, nil
}import { useState, useEffect } from 'react';
const GATEWAY_URL = process.env.REACT_APP_GATEWAY_URL || 'http://localhost:8080';
export function useTaprootAssets() {
const [assets, setAssets] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchAssets();
}, []);
const fetchAssets = async () => {
try {
const response = await fetch(`${GATEWAY_URL}/v1/taproot-assets/assets`);
const data = await response.json();
setAssets(data.assets || []);
} catch (error) {
console.error('Failed to fetch assets:', error);
} finally {
setLoading(false);
}
};
const sendAssets = async (address: string, amount: string) => {
const response = await fetch(`${GATEWAY_URL}/v1/taproot-assets/send`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tap_addrs: [address],
fee_rate: 5
})
});
if (!response.ok) {
throw new Error(`Send failed: ${response.statusText}`);
}
return response.json();
};
return { assets, loading, sendAssets, refetch: fetchAssets };
}- Install Polar for local Lightning development
- Create a network with at least one LND node
- Enable Taproot Assets on the node
- Find your macaroons:
# Use the helper script ./scripts/find-macaroons.sh - Configure
.envwith the paths - Run tests:
cargo test
- Bitcoin Core with RPC enabled (for integration tests)
- Set BITCOIN_RPC_USER and BITCOIN_RPC_PASS
- LND & tapd running
- Or use Polar for easier setup
- Only exposes endpoints available in tapd's REST API
- Some advanced gRPC-only features not accessible
- Rate limiting is basic (per-IP only)
- No built-in caching or response optimization
- Requires local access to macaroon files
We welcome contributions! See CONTRIBUTING.md for details. This project needs:
- Production hardening
- Better error messages
- WebSocket support for events
- Caching layer
- More comprehensive tests
MIT
| Feature | Direct gRPC | Direct REST | This Gateway |
|---|---|---|---|
| Browser Support | ❌ Complex setup | ❌ No CORS | ✅ Native |
| Authentication | Manual | Manual | ✅ Automatic |
| Error Messages | gRPC codes | gRPC codes | ✅ Friendly |
| Setup Complexity | High | Medium | ✅ Low |
- Never expose this gateway to the public internet without proper authentication
- The gateway has full access to your tapd node
- Use
TLS_VERIFY=truein any production setting - Secure your macaroon files with appropriate permissions
- Consider running behind a reverse proxy with additional security
This is experimental software for developers who want to quickly prototype Taproot Assets integrations. For production use, consider:
- Adding authentication to the gateway itself
- Implementing proper monitoring and alerting
- Running multiple instances behind a load balancer
- Regular security audits
- Macaroon rotation strategies
Built by developers who just wanted to use Taproot Assets from a web app without the complexity.