Oyster Pool docs
Everything you need to mine, query, or audit Oyster Pool. The miner JSON-RPC is what the binary speaks to the pool; the REST + WebSocket API is what the dashboard reads — and what you can build on.
What Oyster Pool is
Oyster Pool is a Pearl-blockchain mining pool. Pearl is a Proof-of-Useful-Work chain whose PoW is matrix multiplication verified by a Plonky2 SNARK. Oyster Pool's job is to give your miner work, validate the proofs it ships back, find blocks, and route the rewards.
- 3 % fee. PPLNS over the trailing 2 hours of difficulty-weighted shares.
- 1 PRL minimum payout, K = 20 confirmations for maturity.
- Server-side vardiff. Per-GPU telemetry. Open REST + WebSocket API.
- One static Linux binary (~7 MB), one Docker image. (Windows: run the Docker image via Docker Desktop.)
Connect
Outbound TCP, TLS-wrapped JSON-RPC. The DNS name resolves to the nearest edge via anycast. No inbound ports.
stratum+tls://stratum.oysterpool.com:4444Quickstart on Linux + NVIDIA — replace prl1q…YOUR_WALLET:
curl -fSL https://oysterpool.com/install.sh \
| bash -s -- prl1q...YOUR_WALLET rig-01Docker, Windows, systemd, multi-GPU pinning, and static-difficulty overrides live on the Get started page.
Miner protocol
Line-delimited JSON-RPC 2.0 over TLS TCP. A connection lasts as long as the miner stays online. Method names are case-sensitive.
{"jsonrpc":"2.0","id":1,"method":"hello","params":{
"client":{
"name":"oysterpool-miner","version":"0.1.0",
"wallet":"prl1q...","worker":"rig-01",
"gpus":[{"model":"H100 SXM","vram_gb":80,"sm":"sm_90"}]
}
}}{"jsonrpc":"2.0","id":1,"result":{
"worker_id":123,
"server_time":1780000000,
"supported_shapes":["hopper-r1024","blackwell-r128","ada-r512", ...],
"assigned_shape":"hopper-r1024",
"initial_difficulty":64
}}{"jsonrpc":"2.0","id":2,"method":"getMiningParams","params":{}}{"jsonrpc":"2.0","id":2,"result":{
"shape_id":"hopper-r1024",
"m":65536,"n":65536,"k":16384,"rank":1024,
"rows_pattern":"dense","cols_pattern":"dense",
"mma_type":"Int7xInt7ToInt32",
"hash_tile_h":128,"hash_tile_w":256,"rounded_common_dim":16384,
"difficulty":64
}}{"jsonrpc":"2.0","id":3,"method":"getMiningInfo","params":{}}{"jsonrpc":"2.0","id":3,"result":{
"incomplete_header_bytes":"AAAAAB...", // base64, 76 bytes
"target":"4849...0000" // decimal, share-target
}}{"jsonrpc":"2.0","id":4,"method":"submitPlainProof","params":{
"plain_proof":"<base64>",
"mining_job":{"incomplete_header_bytes":"<base64>","target":"<decimal>"}
}}{"jsonrpc":"2.0","id":4,"result":"submitted"}{"jsonrpc":"2.0","id":5,"method":"telemetry","params":{
"hashrate_5s":7.4e9,"hashrate_1m":7.2e9,
"accepted":421,"rejected":0,"uptime_s":13800,
"miner_version":"0.1.0","shape_id":"hopper-r1024",
"gpus":[{"idx":0,"model":"H100 SXM",
"temp_c":67,"power_w":612,"fan_pct":72,"util_pct":99,
"vram_used_mb":71400,"clock_mhz":1830,"throttle":null}]
}}{"jsonrpc":"2.0","id":5,"result":"ok"}// pushed when the pool retunes your difficulty (vardiff) or
// reassigns your shape after a chain advance:
{"jsonrpc":"2.0","method":"event","params":{
"type":"set_difficulty",
"difficulty":128
}}REST API
Everything the dashboard reads is in the public API. No auth, no rate cap below 60 req/min/IP, JSON only. Versioned under /api/v1/.
| Endpoint | Returns |
|---|---|
| GET /api/v1/pool/stats | Pool hashrate, network share, fee, last block. |
| GET /api/v1/pool/blocks?limit=50 | Recent blocks with reward + confirmations. |
| GET /api/v1/miner/{address} | Per-address hashrate (5m / 1h / 24h), unpaid, paid, last block. |
| GET /api/v1/miner/{address}/workers | Per-worker rows with reported + measured hashrate, GPU telemetry, miner version. |
| GET /api/v1/miner/{address}/payments | Payment history (queued / sent / confirmed / failed, with tx id). |
| GET /api/v1/miner/{address}/found-blocks | Blocks this address finished off (= got the network-hit share). |
| GET /api/v1/miner/{address}/advantage | Earnings vs each competitor — fee-delta + benchmark scaling. |
| GET /api/v1/blocks/{height}/credits | PPLNS credit allocation per address for that block. Verifiable. |
| GET /api/v1/benchmarks | Lab benchmarks + live fleet medians per (gpu_model, miner_name). |
| GET /api/v1/compare?gpu=...&kwh_usd=0.10 | Expected PRL/day on each pool for that GPU at that power price. |
OpenAPI JSON lives at /api/openapi.json.
WebSocket
Two streams: pool overview (one frame ~every 2 s) and per-address telemetry (one frame per worker push, ~every 10 s).
wscat -c wss://api.oysterpool.com/ws/pool
// → {"hashrate_1h_hs":3.42e9,"network_share_pct":12.4,
// "workers_online":1207,"blocks_24h":438,...}wscat -c wss://api.oysterpool.com/ws/miner/prl1q...
// → {"worker_id":42,"hashrate_1m":7.2e9,
// "gpus":[{"model":"H100 SXM","temp_c":67,"power_w":612,...}],
// "observed_at":"..."}Reconnect with exponential backoff. The dashboard ships with a small client at apps/web/src/lib/live.ts — copy it if you're building your own UI.
Economics
Each block's allocation is published at /api/v1/blocks/{height}/credits. Any miner can replay their share log and verify their cut.
Hardware
The miner auto-detects every supported card. The pool publishes one shape per GPU family — your miner is routed to the right one based on the GPU it declares in hello.
- NVIDIA Volta → Blackwell + Hopper.
- AMD MI300X / MI300A (preview, ROCm 6 backend).
- Pre-Volta (GTX 10-series and older) is not supported — no tensor cores.
- CPU mining is not supported — Pearl PoW needs MMA cores.
See the full grid + per-card recommended difficulty on the Get started page.
Operations
Is there a dev fee?▾
No. 3 % flat. We don't inject a hidden dev cut into the binary.
What happens during a chain reorg?▾
Oyster Pool watches every pending block for K = 20 confirmations. If a reorg invalidates a block, the matching pplns_credits rows are reversed from balance_sats in a single transaction. Already-mature credits are immutable, but we keep K high enough that this rarely triggers.
My measured hashrate doesn't match what my miner reports.▾
Both numbers are exposed on the dashboard. Measured is Σ difficulty × 2³² / window derived from accepted shares. Reported comes straight from the miner's 10-second telemetry frame. Some short-term divergence is normal; sustained divergence usually means rejected shares (network flakiness) or a stale fixed difficulty.
Vardiff isn't ratcheting fast enough on my rig.▾
Pin a static difficulty with --difficulty (or PEARL_DIFFICULTY=… in Docker). The dashboard's table on Get started lists per-card recommended values.
Where do my keys go?▾
Your wallet seed never leaves your wallet app. The pool only ever sees the bech32m P2TR address you provide. Payouts are sent to that address by the pool's hot wallet; no withdrawal flow, no balance you have to claim.
How do I report a bug?▾
Open an issue on GitHub or ping us on Discord. Real humans, fast replies.