Add-ons & Sidecars

dvb-WarpPool is designed to be extended from the outside — a clean, stable contract that lets you (or a commercial vendor) build extra functionality without forking the core.

The sidecar model (and the AGPL boundary)

dvb-WarpPool is licensed under AGPL-3.0-or-later (see LICENSE). The single most important rule for an add-on:

An add-on must be a separate process (a "sidecar") that talks to the pool over its HTTP API / SSE — never an in-process Rust crate linked into the daemon.

Why this matters:

  • A crate compiled into the AGPL daemon becomes a derived work → it is covered by the AGPL and its source must be offered to users.
  • A separate process that only consumes the pool's public API/SSE is an independent work. You can license it however you like — including keeping a proprietary commercial add-on closed-source. (For commercial use of the core itself, see COMMERCIAL-LICENSE.md.)

This is not a theoretical pattern — two sidecars already ship with the project:

  • the Stratum V1↔V2 translator (dvb-warppool-translator), and
  • the Solar bridge (a small Python service the pool polls over HTTP; see Configuration Reference[solar], kind = "generic_http").

Dock points (the extension contract)

A sidecar has four ways to integrate:

  1. Read APIGET /api/overview, /api/workers, /api/blocks, /api/hashrate, /api/best-shares, /api/version. These are public (no auth), return JSON, and are the stable surface (see below).
  2. Live events (pull)GET /api/events is a Server-Sent-Events stream. Event types: block_found, new_job, shares_accepted, health_snapshot, profile_switched, update_available (plus heartbeat). Subscribe instead of polling when you need to react in real time (e.g. a billing or notification add-on reacting to block_found).
  3. Live events (push)outbound webhooks. Configure one or more [[webhooks]] and the pool POSTs a JSON payload to your URL when a matching event fires. This is the inverse of the SSE stream: instead of holding a connection open, your sidecar exposes an HTTP endpoint and the pool calls it. See the comparison below and Configuration Reference[[webhooks]].
  4. Inbound data — the generic_http provider pattern: your sidecar exposes a small JSON endpoint and the pool polls it. This is how the Solar bridge feeds live PV data into /api/energy. A clean way to push into the pool without touching its code.
  5. Authenticated actions — for anything beyond public reads, use an API token (Admin → API tokens). Prefer a read-only token for least privilege (next section).

Webhook (push) vs SSE (pull) — which to use

Both carry the same event types; pick by how your sidecar wants to receive them:

Webhook ([[webhooks]])SSE (GET /api/events)
DirectionPool → you (pool initiates the POST)You → pool (you hold the stream open)
Your side runsan HTTP server (endpoint)an HTTP client (long-lived GET)
Best forserverless / no-code / cloud functions; rare low-frequency eventsalways-on local sidecars; high-frequency / every event
If your side is downevent is dropped after retries (no buffer)you simply reconnect and resume
Auth modeloptional HMAC signature (secret_env)API token header (or none for public reads)
High-frequency eventsopt-in; mind the POST-storm warningfine — it's a single stream

Rule of thumb: a cloud function or no-code automation that can't keep a connection open → webhook. A persistent local process that wants every share update → SSE. The webhook payload is { "event", "ts", "data" }, signed with X-WarpPool-Signature when a secret is configured (verify it like a GitHub webhook). Loopback/LAN webhook targets are allowed (there is no SSRF block — the URL is operator-trusted config), so a local sidecar can receive POSTs at http://127.0.0.1:….

Authentication & least privilege

API tokens carry a scope:

  • admin (default) — full access, including mutating POST/PUT/DELETE.
  • readGET/HEAD/OPTIONS only. Any mutating request is rejected with 403 Forbidden.

Give an add-on a read token, not an admin one: even if the add-on (or its host) is compromised, it cannot reconfigure the pool. Create one with:

curl -X POST https://your-pool/api/admin/tokens \
  -H "Authorization: Bearer <admin-token-or-session>" \
  -H "content-type: application/json" \
  -d '{"name": "my-sidecar", "scope": "read"}'

The raw token is returned once — store it in the sidecar's secret store.

API stability

  • Stable surface: the public read endpoints listed above, the /api/events SSE stream, and /api/version. Build add-ons against these.
  • Versioning: the project follows SemVer. Breaking changes to the stable surface happen only on a MAJOR version bump. New fields may be added within a minor release — parse defensively (ignore unknown fields).
  • Check the running version at GET /api/version{ current, latest, update_available }. Pin or feature-detect against current if you depend on a recent field.
  • Admin / mutating endpoints (/api/admin/*) are operator tooling and may evolve faster; if you depend on them, pin to a specific release.

A minimal read-only sidecar (sketch)

import requests, sseclient   # pip install requests sseclient-py

POOL = "http://127.0.0.1:18334"

# Option A — poll a snapshot every 10s
ov = requests.get(f"{POOL}/api/overview").json()
print(ov["pool"]["best_share_difficulty"], ov["active_miners"])

# Option B — react to live events
for ev in sseclient.SSEClient(f"{POOL}/api/events"):
    if ev.event == "block_found":
        ...  # do your thing — notify, bill, log

No token is needed for the public reads above; add an Authorization: Bearer <read-token> header only if you later consume an authenticated endpoint.