A P2P-first Bitcoin transaction broadcast client for Teranode with Arc-compatible API
CI / CD Β Β
|
|
Β Β Β Β Quality Β Β
|
|
Security Β Β
|
|
Β Β Β Β Community Β Β
|
|
π¦Β Installation
|
πΒ QuickΒ Start
|
πΒ Documentation
|
π³Β DockerΒ Guide
|
βΈοΈΒ DeploymentΒ Guide
|
ποΈΒ Architecture
|
π§ͺΒ ExamplesΒ &Β Tests
|
β‘Β Benchmarks
|
π οΈΒ CodeΒ Standards
|
π€Β AIΒ Usage
|
πΒ Resources
|
π€Β Contributing
|
π₯Β Maintainers
|
πΒ License
|
arcade requires a supported release of Go.
go get -u github.com/bsv-blockchain/arcade- Go 1.25+ (see
go.modfor exact version) - SQLite (included with most systems)
- Teranode broadcast URL (e.g.,
https://arc.taal.com)
git clone https://github.com/bsv-blockchain/arcade.git
cd arcade
go build -o arcade ./cmd/arcadeCreate config.yaml with your Teranode broadcast URL:
# Minimal working configuration
network: main
storage_path: ~/.arcade
server:
address: ":3011"
teranode:
broadcast_urls: # REQUIRED - at least one URL needed
- "https://arc.taal.com"
timeout: 30sSee config.example.yaml for all available options.
arcade -config config.yamlYou should see output indicating the server is running on port 3011.
Arcade is a lightweight transaction broadcast service that:
- Listens to Bitcoin network events via libp2p gossip
- Provides Arc-compatible HTTP API for easy migration
- Supports pluggable storage (SQLite) and event backends (in-memory)
- Tracks transaction status through the complete lifecycle
- Delivers notifications via webhooks and Server-Sent Events (SSE)
What is Teranode? Teranode is the next-generation BSV node implementation designed for enterprise-scale transaction processing. Arcade acts as a bridge between your application and the Teranode network, handling transaction submission and status tracking.
- Arc-Compatible API - Drop-in replacement for Arc clients
- P2P Network Listening - Direct gossip subscription for real-time updates
- Chain Tracking - Blockchain header management with merkle proof validation
- Flexible Storage - SQLite for persistent storage
- Event Streaming - In-memory pub/sub for event distribution
- Webhook Delivery - Async notifications with retry logic
- SSE Streaming - Real-time status updates with automatic catchup on reconnect
- Transaction Validation - Local validation before network submission
- Status Tracking - Complete audit trail of transaction lifecycle
- Extensible - All packages public, easy to customize
- API Reference β Dive into the godocs at pkg.go.dev/github.com/bsv-blockchain/arcade
curl -X POST http://localhost:3011/tx \
-H "Content-Type: text/plain" \
-H "X-CallbackUrl: https://myapp.com/webhook" \
--data "<hex-encoded-transaction>"curl http://localhost:3011/tx/{txid}curl http://localhost:3011/healthInteractive API documentation (Scalar UI) available at http://localhost:3011/docs
Client β Arcade
β
RECEIVED (validated locally)
β
SENT_TO_NETWORK (submitted to Teranode via HTTP)
β
ACCEPTED_BY_NETWORK (acknowledged by Teranode)
β
SEEN_ON_NETWORK (heard in subtree gossip - in mempool)
β
MINED (heard in block gossip - included in block)
Or rejected:
REJECTED (from rejected-tx gossip)
DOUBLE_SPEND_ATTEMPTED (from rejected-tx gossip with specific reason)
- Simpler Deployment - Single binary with SQLite, no PostgreSQL/NATS required
- P2P-First - Direct gossip listening instead of node callbacks
- Teranode-Only - Designed for Teranode, no legacy node support
- Extensible - All packages public for customization
Configuration Reference
| Field | Description | Default |
|---|---|---|
teranode.broadcast_urls |
Teranode broadcast service URLs (array) | Required |
| Field | Description | Default |
|---|---|---|
mode |
Operating mode: embedded, remote |
embedded |
url |
Arcade server URL (required for remote mode) | - |
network |
Bitcoin network: main, test, stn |
main |
storage_path |
Data directory for persistent files | ~/.arcade |
log_level |
Log level: debug, info, warn, error |
info |
server.address |
HTTP server listen address | :3011 |
server.read_timeout |
HTTP read timeout | 30s |
server.write_timeout |
HTTP write timeout | 30s |
server.shutdown_timeout |
Graceful shutdown timeout | 10s |
database.type |
Storage backend: sqlite |
sqlite |
database.sqlite_path |
SQLite database file path | ~/.arcade/arcade.db |
events.type |
Event backend: memory |
memory |
events.buffer_size |
Event channel buffer size | 1000 |
teranode.datahub_urls |
DataHub URLs for fetching block data (array) | - |
teranode.auth_token |
Authentication token for Teranode API | - |
teranode.timeout |
HTTP request timeout | 30s |
validator.max_tx_size |
Maximum transaction size (bytes) | 4294967296 |
validator.max_script_size |
Maximum script size (bytes) | 500000 |
validator.max_sig_ops |
Maximum signature operations | 4294967295 |
validator.min_fee_per_kb |
Minimum fee per KB (satoshis) | 100 |
webhook.max_retries |
Max webhook retry attempts | 10 |
auth.enabled |
Enable Bearer token authentication | false |
auth.token |
Bearer token (required if auth enabled) | - |
Planned but not yet implemented:
database.type: postgres- PostgreSQL storage backendevents.type: redis- Redis event backend for distributed deployments
HTTP Headers
Arcade supports Arc-compatible headers:
X-CallbackUrl- Webhook URL for async status updatesX-CallbackToken- Token for webhook auth and SSE stream filteringX-FullStatusUpdates- Include all intermediate statuses (default: final only)X-SkipFeeValidation- Skip fee validationX-SkipScriptValidation- Skip script validation
Webhook Notifications
When you provide X-CallbackUrl, Arcade will POST status updates:
{
"timestamp": "2024-03-26T16:02:29.655390092Z",
"txid": "...",
"txStatus": "SEEN_ON_NETWORK",
"blockHash": "...",
"blockHeight": 123456,
"merklePath": "..."
}Features:
- Automatic retries with linear backoff (1min, 2min, 3min, etc.)
- Configurable max retries via
webhook.max_retries - Delivery tracking
SSE Streaming
Arcade provides real-time transaction status updates via SSE, offering an alternative to webhook callbacks.
-
Submit Transaction with Callback Token:
curl -X POST http://localhost:3011/tx \ -H "Content-Type: text/plain" \ -H "X-CallbackToken: my-token-123" \ --data "<hex-encoded-transaction>"
-
Connect SSE Client:
const eventSource = new EventSource('http://localhost:3011/events?callbackToken=my-token-123'); eventSource.addEventListener('status', (e) => { const update = JSON.parse(e.data); console.log(`${update.txid}: ${update.status}`); });
-
Receive Real-Time Updates:
id: 1699632123456789000 event: status data: {"txid":"abc123...","status":"SENT_TO_NETWORK","timestamp":"2024-11-10T12:00:00Z"}
When the SSE connection drops, the browser's EventSource automatically reconnects and sends the Last-Event-ID header. Arcade replays all missed events since that timestamp.
No client code needed - this is handled automatically by the EventSource API.
Event IDs are nanosecond timestamps (time.UnixNano()), ensuring:
- Chronological ordering
- Virtually collision-free IDs
- Easy catchup queries by time
- Webhooks: Server-to-server notifications with retry logic
- SSE: Real-time browser updates with automatic reconnection
- Both: Use the same
X-CallbackTokenfor webhooks AND SSE streaming
Each SSE connection only receives events for transactions submitted with the matching callback token. This allows:
- Multiple users/sessions with isolated event streams
- Scoped access without complex authentication
- Simple token-based routing
See examples/sse_client.html and examples/sse_client.go for complete examples.
Remote Client
For applications that need Arcade functionality without running a full server, use the REST client:
import "github.com/bsv-blockchain/arcade/client"
c := client.New("http://arcade-server:3011")
defer c.Close() // Always close when done to clean up SSE connections
// Submit a single transaction
status, err := c.SubmitTransaction(ctx, rawTxBytes, nil)
// Submit multiple transactions
statuses, err := c.SubmitTransactions(ctx, [][]byte{rawTx1, rawTx2}, nil)
// Get transaction status
status, err := c.GetStatus(ctx, "txid...")
// Get policy configuration
policy, err := c.GetPolicy(ctx)
// Subscribe to status updates for a callback token
statusChan, err := c.Subscribe(ctx, "my-callback-token")
for status := range statusChan {
fmt.Printf("Status: %s %s\n", status.TxID, status.Status)
}Extending Arcade
All packages are public and designed for extension:
import "github.com/bsv-blockchain/arcade/store"
type MyStore struct {}
func (s *MyStore) GetOrInsertStatus(ctx context.Context, status *models.TransactionStatus) (*models.TransactionStatus, bool, error) {
// Your implementation - returns (existing status, was inserted, error)
// If transaction already exists, return existing status with inserted=false
}
// Implement other store.Store methods...import "github.com/bsv-blockchain/arcade/events"
type MyHandler struct {
publisher events.Publisher
}
func (h *MyHandler) Start(ctx context.Context) error {
ch, _ := h.publisher.Subscribe(ctx)
for update := range ch {
// Handle status update
}
}Troubleshooting
Server fails to start with "no teranodes configured"
- Ensure
teranode.broadcast_urlsis set in your config file with at least one valid URL
Transactions stuck in SENT_TO_NETWORK
- Check your Teranode broadcast URL is reachable
- Verify network connectivity and firewall rules
SQLite errors
- Ensure
storage_pathdirectory exists and is writable - Check disk space availability
- Arcade (
arcade.go) - Core P2P listener that subscribes to block, subtree, and rejected-tx gossip topics - Store (
store/) - SQLite storage for transaction statuses and webhook tracking - TxTracker (
store/tracker.go) - In-memory index of transactions being monitored - Event Publisher (
events/) - In-memory pub/sub for distributing status updates - Webhook Handler (
handlers/webhook.go) - Delivers status updates with retry logic - HTTP Routes (
routes/fiber/) - Arc-compatible REST API including SSE streaming
Arcade uses go-chaintracks for blockchain header tracking and merkle proof validation. Headers are loaded from embedded checkpoint files on startup and updated via P2P block announcements.
For detailed architecture documentation, see docs/ARCHITECTURE.md.
All unit tests and examples run via GitHub Actions and use Go version 1.25.x. View the configuration file.
Run all tests (fast):
magex testRun all tests with race detector (slower):
magex test:raceSee examples/ for SSE client examples and the combined server example.
Run the Go benchmarks:
magex benchNote: Comprehensive benchmarks for P2P operations (peer discovery, message throughput, connection establishment) are planned for future releases. The current focus is on correctness and stability of the networking implementation.
Read more about this Go project's code standards.
Read the AI Usage & Assistant Guidelines for details on how AI is used in this project and how to interact with AI assistants.
View the contributing guidelines and please follow the code of conduct.
All kinds of contributions are welcome π! The most basic way to show your support is to star π the project, or to raise issues π¬.
![]() |
![]() |
![]() |
|---|---|---|
| Siggi | Dylan | MrZ |


