When I first tried to build a web interface for Taproot Assets, I expected the usual crypto development challenges. What I didn't expect was to be completely blocked by something as basic as CORS headers. Two days later, after fighting with proxy configurations and authentication nightmares, I realized this wasn't just my problem - it was everyone's problem.
The Wall Every Web Developer Hits
Picture this: You're excited about Taproot Assets. You want to build a slick web interface. You fire up your React app, point it at tapd's REST API, and... nothing. The browser console screams at you:
Access to fetch at 'https://localhost:8089/v1/taproot-assets/assets'
from origin 'http://localhost:3000' has been blocked by CORS policy
You can't even get started. The browser literally won't let you talk to tapd.
This isn't a minor inconvenience - it's a fundamental blocker. Without CORS headers, web browsers refuse to make requests. Period. No amount of JavaScript wizardry will save you.
The Authentication Maze
Let's say you give up on the browser and try building a backend service. Now you face challenge #2: macaroon authentication.
Here's what the tapd docs don't emphasize enough - you need to:
Find the admin.macaroon file (good luck if using Docker)
Read it as binary
Encode it as hex
Add it to every request header
Deal with TLS certificates
Handle gRPC error codes
By the time you've figured this out, you've written 200 lines of boilerplate before even making your first real API call.
Enter the REST Gateway
I built the Taproot Assets REST Gateway because I was tired of watching developers hit these same walls. It's a simple proxy that sits between your web app and tapd, handling all the complexity.
What started as a weekend hack to solve my own problem has evolved into something more useful. The gateway now handles:
CORS headers - So browsers can actually make requests
Automatic authentication - No manual macaroon handling
Better error messages - Not just gRPC status codes
Request tracking - UUID for every request
Rate limiting - Basic protection against abuse
Show Me the Code
Here's how simple it becomes with the gateway:
Before (Won't Work in Browser)
// This will fail with CORS error
const macaroonHex = fs.readFileSync('admin.macaroon').toString('hex');
const response = await fetch('https://localhost:8089/v1/taproot-assets/assets', {
headers: {
'Grpc-Metadata-macaroon': macaroonHex
}
});
// CORS error - request blocked
After (Works Everywhere)
// Just works™
const response = await fetch('http://localhost:8080/v1/taproot-assets/assets');
const assets = await response.json();
console.log(assets);
No authentication headers. No CORS errors. No complexity.
Real Use Cases
This enables developers to build:
Web Wallet Interface
async function createWallet() {
// Create new address
const addr = await fetch(`${GATEWAY}/v1/taproot-assets/addrs`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
asset_id: selectedAsset,
amt: '1000'
})
}).then(r => r.json());
// Display QR code
showQRCode(addr.encoded);
}
Asset Explorer
async function exploreAssets() {
// List all assets
const { assets } = await fetch(`${GATEWAY}/v1/taproot-assets/assets`)
.then(r => r.json());
// Get balance for each
const balances = await fetch(`${GATEWAY}/v1/taproot-assets/assets/balance`)
.then(r => r.json());
// Display in UI
renderAssetDashboard(assets, balances);
}
Payment Widget
// Embeddable payment button
function TaprootPayButton({ assetId, amount, onSuccess }) {
const handlePay = async () => {
const invoice = await createInvoice(assetId, amount);
const payment = await processPayment(invoice);
onSuccess(payment);
};
return <button onClick={handlePay}>Pay with Taproot Assets</button>;
}
Architecture That Doesn't Suck
The gateway is built with boring, proven technology:
Your Web App
↓ (Regular HTTP + CORS headers)
REST Gateway (Rust + Actix-web)
↓ (Authenticated requests)
tapd REST API
Why Rust? Because when you're proxying financial transactions, "oops I segfaulted" isn't acceptable. The gateway implements 70+ endpoint proxies covering all major tapd operations.
Performance benchmarks show the gateway adds virtually no overhead:
1,586 requests/second for balance queries
0.07ms average latency overhead (P95: actually faster than direct calls!)
32MB memory footprint - lighter than most Electron apps
These aren't synthetic benchmarks - they're measured against a real tapd instance on Polar. The gateway can handle 42,520 requests/second for its own endpoints, proving the bottleneck is always tapd, never the gateway.
Current Reality Check
Let me be clear about what this is and isn't:
What it is:
A working solution to the CORS problem
Implements proxy for 70+ tapd endpoints
Comprehensive test suite: 26 modules covering addresses, assets, channels, universe sync, wallet operations, edge cases, and performance
Measured performance: <0.1ms overhead (basically free)
Tested on regtest with real Taproot Assets operations
Open source (MIT license)
Docker ready
What it isn't:
Production-ready for mainnet (needs security audit)
Feature-complete (no WebSocket support yet)
Officially supported by Lightning Labs
A replacement for tapd
The Roadmap
I'm applying for grants to turn this from a useful hack into production infrastructure:
Phase 1: Make it Bulletproof
Security audit (critical before mainnet)
Enhanced error handling
Retry logic for resilience
Circuit breakers for failover
Phase 2: Make it Real-time
WebSocket support
Event subscriptions
Live updates
Phase 3: Make it Scale
Further performance optimization
Redis caching for frequently accessed data
Horizontal scaling support
Production monitoring
Try It Today
Don't wait for perfection. If you're building on Taproot Assets, you can use this today:
# Clone it
git clone https://github.com/privkeyio/taproot-assets-rest-gateway
cd taproot-assets-rest-gateway
# Configure it
cp .env.example .env
# Edit .env with your tapd details
# Run it
docker-compose up -d
# Use it
curl http://localhost:8080/v1/taproot-assets/assets
Why This Matters
Lightning Labs is doing incredible work on the Taproot Assets protocol. But protocols need applications, and applications need developers. By removing the barriers that stop web developers from building, we can accelerate the entire ecosystem.
Every developer who gives up because of CORS errors is a missed opportunity. Every hour spent fighting authentication is an hour not spent on innovation.
Contributing
This is bigger than one person can build. I need:
Users: Try it, break it, complain about it
Contributors: Features, fixes, documentation
Feedback: What's missing? What's confusing?
Security researchers: Find bugs before mainnet users do
The Vision
Imagine a world where building a Taproot Assets app is as easy as building any web app. No special infrastructure. No authentication complexity. No browser limitations.
That's what the REST Gateway enables. Not by reinventing anything, but by building the missing bridge between web developers and Taproot Assets.
Getting Started: GitHub Repository
Contact:
Nostr:
npub1acr7ycax3t7ne8xzt0kfhh33cfd5z4h8z3ntk00erpd7zxlqzy3qrn2tqw
npub1h3fzzzeq60acjvnyvw34rpn5clkaueteffmkt3ln4ygekg9lcm0qhw96sj
Email:
kyle@privkey.io
william@privkey.io
GitHub: @privkeyio
Built by a developer who was tired of CORS errors, for developers who just want to build cool stuff.