Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/env/90-project.env
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ GO_COVERAGE_EXCLUDE_PATHS=.github/,.mage-cache/,.vscode/,bin/,example/,examples/
# ================================================================================================

# Nancy CVE Exclusions
NANCY_EXCLUDES=CVE-2024-38513,CVE-2023-45142,CVE-2025-64702,CVE-2021-43668,CVE-2023-26248,CVE-2026-24051,CVE-2026-26014
NANCY_EXCLUDES=CVE-2024-38513,CVE-2023-45142,CVE-2025-64702,CVE-2021-43668,CVE-2023-26248,CVE-2026-24051,CVE-2026-26014,CVE-2026-26958

# Govulncheck/Magex CVE Exclusions
MAGE_X_CVE_EXCLUDES=CVE-2024-38513,CVE-2023-45142,CVE-2025-64702,CVE-2021-43668,CVE-2023-26248,CVE-2026-24051,CVE-2026-26014
MAGE_X_CVE_EXCLUDES=CVE-2024-38513,CVE-2023-45142,CVE-2025-64702,CVE-2021-43668,CVE-2023-26248,CVE-2026-24051,CVE-2026-26014,CVE-2026-26958

# CVE-2026-26014 for pion/dtls (EXCLUDED: Invalid/non-existent CVE)
#
Expand All @@ -58,6 +58,13 @@ MAGE_X_CVE_EXCLUDES=CVE-2024-38513,CVE-2023-45142,CVE-2025-64702,CVE-2021-43668,
# Rationale: No actual vulnerability exists. Nancy likely reporting stale/incorrect data.
# Resolution: Excluded as false positive. Already using latest pion/dtls versions.

# CVE-2026-26958 for filippo.io/edwards25519@v1.1.0 (CWE-665 Improper Initialization)
# Transitive dependency via github.com/bsv-blockchain/teranode@v0.13.2.
# Advisory notes that uses "only through github.com/go-sql-driver/mysql are not affected",
# which is our path (teranode uses it for MySQL auth). Cannot upgrade via go mod tidy since
# no package in this module directly imports edwards25519; pin would be removed. Will resolve
# when teranode upgrades to filippo.io/edwards25519 v1.2.0.

# CVE-2026-24051 for go.opentelemetry.io/otel/sdk@v1.39.0 (macOS PATH hijacking - low risk)
# CVE-2025-64702 for quic-go@v0.55.0
# GO-2024-3218: Content Censorship in IPFS via Kademlia DHT abuse in github.com/libp2p/go-libp2p-kad-dht
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ peer_cache.json

*.log

arcade
/arcade
config.yaml
44 changes: 32 additions & 12 deletions DEPLOY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ Arcade is configured via environment variables or a `config.yaml` file. Environm
| `ARCADE_STORAGE_PATH` | Root directory for persistent data | `/data` |
| `ARCADE_DATABASE_SQLITE_PATH` | Path to SQLite database file | `/data/arcade.db` |
| `ARCADE_CHAINTRACKS_STORAGE_PATH` | Path for chain header storage | `/data/chaintracks` |
| `ARCADE_CHAINTRACKS_BOOTSTRAP_URL` | URL to headers.bin for initial header sync | _(none)_ |
| `ARCADE_TERANODE_BROADCAST_URLS` | Comma-separated Teranode propagation URLs | _(none)_ |
| `ARCADE_TERANODE_DATAHUB_URLS` | Comma-separated Teranode DataHub URLs (fallback) | _(none)_ |
| `ARCADE_TERANODE_AUTH_TOKEN` | **Bearer token for Teranode authentication (required)** | _(none)_ |
| `ARCADE_SERVER_ADDRESS` | Listen address | `:3011` |
| `ARCADE_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error` | `info` |
| `ARCADE_AUTH_ENABLED` | Enable authentication | `false` |
Expand All @@ -33,34 +36,51 @@ The container exposes `GET /health` on port 3011. Use this for readiness/livenes

## Docker Compose

1. **Create a config file:**
The `docker-compose.yaml` includes production mainnet configuration by default.

### Authentication Required

⚠️ **You must provide a Teranode authentication token** to submit transactions:

```bash
cp config.example.yaml config.yaml
export ARCADE_TERANODE_AUTH_TOKEN="your-token-here"
```

Edit `config.yaml` and set your `teranode.broadcast_urls`.

2. **Start Arcade:**
### Quick Start

```bash
# Start Arcade (uses mainnet by default)
docker compose up -d
```

3. **Verify it's running:**
# View logs
docker compose logs -f arcade

```bash
# Check health
curl http://localhost:3011/health
docker compose logs -f arcade

# Stop
docker compose down
```

4. **Stop:**
Data is persisted in the `arcade-data` Docker volume and survives restarts.

### Using Different Networks

For testnet:
```bash
docker compose down
ARCADE_NETWORK=test \
ARCADE_TERANODE_BROADCAST_URLS="https://teranode-eks-testnet-eu-1-propagation.bsvb.tech,https://teranode-eks-testnet-us-1-propagation.bsvb.tech" \
ARCADE_TERANODE_DATAHUB_URLS="https://teranode-eks-testnet-eu-1.bsvb.tech/api/v1" \
docker compose up -d
```

Data is persisted in the `arcade-data` Docker volume and survives restarts.
For teratestnet:
```bash
ARCADE_NETWORK=teratestnet \
ARCADE_TERANODE_BROADCAST_URLS="https://teranode-eks-ttn-eu-1-propagation.bsvb.tech,https://teranode-eks-ttn-us-1-propagation.bsvb.tech" \
ARCADE_TERANODE_DATAHUB_URLS="https://teranode-eks-ttn-eu-1.bsvb.tech/api/v1" \
docker compose up -d
```

### Environment-only configuration (no config file)

Expand Down
10 changes: 3 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1

# Build stage
FROM golang:1.25.4-alpine@sha256:96f36e77302b6982abdd9849dff329feef03b0f2520c24dc2352fc4b33ed776d AS builder
FROM golang:1.25.4-alpine AS builder

# Install build dependencies
# build-base: includes gcc, g++, make and other build tools for CGO
Expand Down Expand Up @@ -29,18 +29,14 @@ ARG BUILD_DATE=unknown
# -ldflags explanation:
# -s: omit symbol table
# -w: omit DWARF debug info
# -X: set version variables
RUN CGO_ENABLED=1 GOOS=linux go build \
-ldflags="-s -w \
-X main.version=${VERSION} \
-X main.commit=${COMMIT} \
-X main.buildDate=${BUILD_DATE}" \
-ldflags="-s -w" \
-trimpath \
-o arcade \
./cmd/arcade

# Runtime stage - using Alpine for small size and C library compatibility (SQLite)
FROM alpine:3.21@sha256:22e0ec13c0db6b3e1ba3280e831fc50ba7bffe58e81f31670a64b1afede247bc
FROM alpine:3.21

# Install runtime dependencies
# ca-certificates: for HTTPS connections to Teranode
Expand Down
29 changes: 26 additions & 3 deletions cmd/arcade/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"os"
"os/signal"
"syscall"
"time"

chaintracksRoutes "github.com/bsv-blockchain/go-chaintracks/routes/fiber"
"github.com/gofiber/fiber/v2"
Expand Down Expand Up @@ -115,7 +116,7 @@ func run(ctx context.Context, cfg *config.Config, log *slog.Logger) error {
authToken = cfg.Auth.Token
log.Info("API authentication enabled")
}
app := setupServer(arcadeRoutes, chaintracksRts, dashboard, authToken)
app := setupServer(arcadeRoutes, chaintracksRts, dashboard, authToken, cfg.Chaintracks.StoragePath)

errCh := make(chan error, 1)
go func() {
Expand Down Expand Up @@ -156,7 +157,7 @@ func waitForShutdown(ctx context.Context, cfg *config.Config, log *slog.Logger,
return nil
}

func setupServer(arcadeRoutes *fiberRoutes.Routes, chaintracksRts *chaintracksRoutes.Routes, dashboard *Dashboard, authToken string) *fiber.App {
func setupServer(arcadeRoutes *fiberRoutes.Routes, chaintracksRts *chaintracksRoutes.Routes, dashboard *Dashboard, authToken string, chaintracksStoragePath string) *fiber.App {
app := fiber.New(fiber.Config{
DisableStartupMessage: true,
})
Expand All @@ -183,6 +184,15 @@ func setupServer(arcadeRoutes *fiberRoutes.Routes, chaintracksRts *chaintracksRo
chaintracksGroup := app.Group("/chaintracks")
chaintracksRts.Register(chaintracksGroup.Group("/v2"))
chaintracksRts.RegisterLegacy(chaintracksGroup.Group("/v1"))

// CDN static file serving for bulk header downloads
// Serves files like mainNetBlockHeaders.json and mainNet_X.headers
chaintracksGroup.Static("/", chaintracksStoragePath, fiber.Static{
Compress: true,
ByteRange: true, // Support Range requests for partial downloads
Browse: false,
CacheDuration: 1 * time.Hour,
})
}

// Health check (standalone arcade server only)
Expand All @@ -194,7 +204,20 @@ func setupServer(arcadeRoutes *fiberRoutes.Routes, chaintracksRts *chaintracksRo

// API docs (Scalar UI)
app.Get("/docs/openapi.json", func(c *fiber.Ctx) error {
return c.Type("json").SendString(docs.SwaggerInfo.ReadDoc())
arcadeSpec := docs.SwaggerInfo.ReadDoc()

// Merge with chaintracks spec if chaintracks routes are enabled
if chaintracksRts != nil {
mergedSpec, err := mergeOpenAPISpecs(arcadeSpec, "/chaintracks")
if err != nil {
// Log error but fallback to arcade-only spec
slog.Error("Failed to merge OpenAPI specs", slog.String("error", err.Error()))
return c.Type("json").SendString(arcadeSpec)
}
return c.Type("json").SendString(mergedSpec)
}

return c.Type("json").SendString(arcadeSpec)
})
app.Get("/docs", func(c *fiber.Ctx) error {
return c.Type("html").SendString(scalarHTML)
Expand Down
Loading