NEB Coin Information
NEB
Proof of Training: an independent chain whose mining work trains a small neural network, one integer training step per attempt
Coin Specifications
-
NEBCoin Name
-
NEBTicker Symbol
-
Proof of Training — one integer training step of a 784-128-10 network on 16 samples per attempt; the model changes every block; BLAKE3, 80-byte headerAlgorithm
-
60 seconds (ASERT difficulty)Block Time
-
17,999,999.9415 NEBMax Supply
17,099,999.9955 NEB to miners, 899,999.946 NEB project share (5 %); computed from the consensus rules (subsidy and project_share in neb-chain state.rs), not a figure published by the project -
20 NEB per block, 95 % (19 NEB) to the miner (5 % project share by consensus; halving every 450,000 blocks ≈ 312.5 days)Block Reward
Pool Information
-
1%Pool Fee
-
PPLNSPayout Scheme
-
Last 5 roundsPPLNS Window
-
Every 60 minutes (on the full hour, UTC), batch transaction (network fee paid by the recipients)Payout Schedule
-
0.01 NEB (or your own payout limit)Minimum Payout
-
Credited at 1 confirmation (shown as unconfirmed), payable at 101 (coinbase maturity 100)Block Confirmations
Fee Structure
-
1%Pool Fee
Deducted from the 19 NEB subsidy share the pool credits per block
-
5%Project share
Consensus rule of the NEB chain: 5 % of every block subsidy is paid to the project output of the coinbase (1 of 20 NEB in the first era) before the pool sees the reward
-
≈ 18.81 NEB / blockMiner Receives
99 % of the subsidy share the pool credits per block (19 NEB in the first era; transaction fees stay with the pool), shared PPLNS
Where a block reward goes
Every NEB block creates 20 NEB. The network pays the 5 % project share first; the pool keeps a 1 % fee of the rest and shares everything else among its miners by PPLNS. Transaction fees are not included.
Miners (PPLNS)
18.81 NEB94.05 %
Pool fee (1 %)
0.19 NEB0.95 %
NEB project share (5 %)
1.00 NEB5.00 %
Stratum protocol for miner and pool developers
The complete technical description of the stratum protocol this pool speaks (NEB stratum v1, used by suprminer -a neb), with real messages and a verified test vector. Write your own miner or pool from it, or point your AI agent at one of these files:
- neb-stratum.md: the full specification as plain Markdown (best for AI agents)
- neb-stratum-example.json: a captured session (including the full
mining.set_model) and one computed share, to test an implementation - neb-stratum-example-v2.json: the same after the nebhash v2 fork at block 8800 (KEP1 job, dataset index 2)
Show the full specification
Version 1 · as implemented by the neb.suprnova.cc pool (a NOMP-based stratum server) and by suprminer -a neb.
This document is meant to be complete: a miner or pool author (or their AI agent) should be able to implement a working NEB stratum client or server from it alone, plus the official NEB specification for the proof of work itself. Everything here is public; the pool needs no keys or credentials from the miner.
- Human-readable version: https://neb.suprnova.cc/CoinInfos.html#stratum
- This text (Markdown): https://neb.suprnova.cc/neb-stratum.md
- Full captured session and a verified share (test vector, JSON): https://neb.suprnova.cc/neb-stratum-example.json (v1 network)
- The same after the nebhash v2 fork (KEP1 job, dataset index 2): https://neb.suprnova.cc/neb-stratum-example-v2.json
- Official NEB node, PoW reference and specification: https://github.com/SilasEigen/neb (
spec/node.md,spec/shares.md,pow/)
1. Overview
NEB is a Proof-of-Training chain. An attempt is not a plain hash: the attempt key BLAKE3(header) selects 16 training samples from a dataset, the miner computes a gradient of a small integer neural network (784 → 128 → 10, 4-bit weights) on them, and the result is hashed into a 256-bit ticket. A ticket that is at or below a target is a share or a block.
The stratum protocol is Bitcoin-style stratum v1 (JSON lines over TCP, mining.subscribe / mining.authorize / mining.notify / mining.submit, extranonce1 + extranonce2 in the coinbase, nTime rolling, difficulty via mining.set_difficulty) with three NEB-specific changes:
mining.set_model: the pool pushes the network's current model weights (base64; 50 816 bytes for the v1 network, 13 000 / 11 140 bytes for the nebhash v2 networks KEP1 / RET2 from block 8800) that the attempts of the following jobs are evaluated on. The model changes with every block.mining.notifycarries NEB fields: block height, the model'sweights_hash, the dataset index (slot), the body'stx_count,shares_root, and the upper nTime boundntime_max. The header is NEB's 80-byte header, and the Merkle tree and body commitment use domain-separated BLAKE3.- Difficulty means expected attempts per share:
share_target = floor((2^256 - 1) / difficulty), compared with the ticket as 256-bit little-endian integers.
There are no other extensions. The miner needs the public NEB dataset file (section 3) and a PoW implementation; the pool sends everything else.
2. Connection
| Host | neb.suprnova.cc |
| Transport | plain TCP, no TLS; one JSON object per line, terminated by \n (UTF-8) |
| Port 7801 | VarDiff, start 20 000, min 500 (CPU and small GPUs); custom difficulty allowed |
| Port 7802 | fixed difficulty 20 000 |
| Port 7803 | fixed difficulty 2 000 000 |
| Port 7804 | VarDiff for big GPUs, start 5·10^8, min 10^5 (recommended for GPUs); custom difficulty allowed |
| Port 7805 | SOLO: VarDiff, start 5·10^7, min 500; custom difficulty allowed. Same protocol; a block found here is credited to its finder alone (99 %), shares only measure hashrate |
| VarDiff | aims at one share per 30 s; retargets after at least 90 s and 3 accepted shares (at once after 12 fast shares), at most ×4 or ÷4 per step; lowers the difficulty when a session has no accepted share for 90 s |
| Username | <neb1 address> or <neb1 address>.<worker>; worker = 1–32 characters of A–Z a–z 0–9 _ - |
| Password | anything (x); optionally d=<number> for a fixed difficulty on ports 7801, 7804 and 7805, within the port's limits |
The address is a NEB testnet address: neb1 followed by bech32m data, 62 lowercase characters in total. Payouts go to that address; there is no registration.
3. What a miner needs locally
- The dataset file
neb-datasets.binof NEB 0.3.0 (release asset, 259 897 080 bytes, also as a 26 MB zip). Its BLAKE3 hash is pinned by the network:296a174edbf67339fa0af341f063154292bba224b3d2cacf5b18b31c70da80eb. It holds 4 datasets (format:spec/shares.mdsection 11.6,spec/nebhash-v2.mdsection 2): index 0 Kepler transit signals and index 1 retinal OCT scans (NMD1, the v1 network, used up to block 8799), index 2 Kepler signals for KEP1 and index 3 retinal scans at 64 × 64 for RET2 (NMD2, from block 8800 / 8801). The pool names the dataset index of every job. The old 2-dataset file of NEB 0.2.1 (b1008b97…) still works until block 8799 only. - A ticket function
ticket(model, dataset[index], key): for anNMD1dataset exactly as inspec/shares.mdsections 3–5 (BLAKE3, ChaCha8, sample selection from the key, integer forward/backward pass, gradient digest,ticket = BLAKE3(digest || key)); for anNMD2dataset nebhash v2 (spec/nebhash-v2.md, the small convolutional networks KEP1 and RET2, same sample selection, digest and ticket rule). The C reference in the NEB repository (pow/ref/shares.c,pow/ref/nebhash2.c) is normative; the official CPU and GPU miners and suprminer are bit-exact with it. Any mismatch makes all your shares invalid, so test against the vectors in section 9.
4. Message flow
client pool
| --- mining.subscribe ----------------------------------> |
| <-- result [subscriptions, extranonce1, 8] ------------- |
| <-- mining.set_difficulty [D] -------------------------- |
| --- mining.authorize [user, pass] ---------------------> |
| <-- mining.set_difficulty [D] (only if it changed) --- |
| <-- mining.set_model [weights_hash, index, base64] ----- |
| <-- mining.notify [job ...] ---------------------------- |
| <-- result true ---------------------------------------- | (authorize result comes AFTER the first job)
| |
| --- mining.submit [user, job_id, en2, ntime, nonce] ---> |
| <-- result true / error [code, message, null] -------- |
| ... |
| <-- mining.set_model (when the model changes) ---------- |
| <-- mining.notify (new block: clean_jobs = true) ------- |
| <-- mining.notify (same block, refresh: clean = false) - |
Rules a client can rely on:
- Responses carry the request
id. Notifications have"id": nulland may arrive at any time, before or after any response. Never assume the next line is the answer to your last request. - The first
set_difficulty,set_modelandnotifyof a session are sent before themining.authorizeresult. - A
mining.set_modelis sent before amining.notifywhenever that job'sweights_hashdiffers from the one of the previous job on this session. In practice this happens with every new block (the model is trained by every block, and the dataset alternates). The pool sends it at most once per change; cache it. - A
mining.set_difficultyis sent right before amining.notifyand applies from then on. A share that meets only the difficulty in force before the last change is still accepted, so work in flight is never lost. - The pool sends a
mining.notifyat least every 75 seconds (a new block, a refreshed job, or the current job re-sent). A client may treat 180 seconds without any data as a dead connection and reconnect. - Line sizes: pool → client lines are at most about 70 KB (
mining.set_modelis about 68 KB); client → pool lines must stay below 10 KB.
Reading the socket (important)
Read like cpuminer or XMRig: one network thread does blocking reads into a persistent buffer and hands every complete line (ending in \n) to the miner. A 68 KB set_model line arrives in many TCP segments; a client that polls with short timeouts and discards a partially received line will drop it and loop forever. Treat a read timeout as "no data yet" (on Windows it is WSAETIMEDOUT 10060 / TimedOut, on Unix EAGAIN / WouldBlock), never as a lost connection. Reconnect with a backoff (for example 1, 2, 4 … 60 s).
5. Methods
All hex strings are lowercase, without 0x.
5.1 mining.subscribe
{"id":1,"method":"mining.subscribe","params":["myminer/1.0"]}
{"id":1,"result":[[["mining.set_difficulty","neb0000000001e4"],["mining.notify","neb0000000001e4"]],"4e0000012317f237",8],"error":null}
result[1]= extranonce1, 8 bytes (16 hex), unique per session.result[2]= extranonce2 size = 8 bytes. The miner chooses extranonce2 (16 hex) freely, for example a per-GPU counter.- params are optional and ignored. Subscribing twice on one connection is an error.
mining.extranonce.subscribeis answered withfalse(the extranonce never changes within a session).
5.2 mining.authorize
{"id":2,"method":"mining.authorize","params":["neb1…your-address….rig01","x"]}
{"id":2,"result":true,"error":null}
false means the address is not a valid neb1 address or the worker name breaks the rules in section 2. One worker per connection: run one connection per worker name.
5.3 mining.set_difficulty (pool → miner)
{"id":null,"method":"mining.set_difficulty","params":[20000]}
D = expected number of attempts per share (a number, may be fractional). Share target:
share_target = floor( (2^256 - 1) / D )
In exact integer arithmetic. The pool computes floor((2^256 - 1) * 10^6 / round(D * 10^6)), which is the same for any D with at most 6 decimals. For D = 20000 the target as 32 little-endian bytes is:
21fdf675e09c11a5bdc117265305a3923a014d840d4faf946588635ddc460300
5.4 mining.set_model (pool → miner)
{"id":null,"method":"mining.set_model","params":["a0022b7e3683ae94695b79f06005484ec98ddd98372e9f540672a50365cc3606",1,"IB4hPQ/wACv+ETBN…(67 756 base64 characters)…"]}
| # | field | meaning |
|---|---|---|
| 0 | weights_hash | 32 bytes hex: BLAKE3("neb-model-weights" ‖ pack(W1) ‖ pack(W2)) (spec/shares.md section 7) |
| 1 | index | index of the dataset in the network file this model trains: height mod 2 (0 or 1) up to block 8799, then 2 (KEP1) at even and 3 (RET2) at odd heights (spec/shares.md 11.7) |
| 2 | weights | base64 of the packed weights. v1 (index 0/1): 50 816 bytes, pack(W1) (50 176 bytes, W1 is 128 × 784) followed by pack(W2) (640 bytes, W2 is 10 × 128). KEP1 (index 2): 13 000 bytes, RET2 (index 3): 11 140 bytes: all weights of the architecture's layers in layer order, each tensor row-major (spec/nebhash-v2.md sections 3 and 6). Always signed 4-bit values −8..7, element e in the low nibble of byte e/2 when e is even and in the high nibble when odd (spec/shares.md section 3.4) |
Check: decode, verify BLAKE3("neb-model-weights" ‖ bytes) == weights_hash, and key the model by weights_hash. Jobs name the model they need by its weights_hash (notify field 10).
5.5 mining.notify (pool → miner)
{"id":null,"method":"mining.notify","params":[
"10985",
"513d8d34b62ba1aa3ec4495006dfca9256560e07791c931d9483dccc2121e76c",
"01010000000000000000000000000000000000000000000000000000000000000000ffffffff1441150000",
"0200b33f71000000000104290170dc4dc035fdc27c5a1db5b7bc7c0e09aee08a28f37abb6256a01827b700e1f5050000000002270bbd6380c0c7022176e202083d5671ed6bb7bc0c27350bcbd134846ce87dd5",
[],
"00000001","c3ce4480","6af447ee",true,
5441,
"a0022b7e3683ae94695b79f06005484ec98ddd98372e9f540672a50365cc3606",1,
"00000001",
"81e74225b9436f0fcd0ed90b31817f2acebb2e2b3d28577b90a7e46ad736c5f9",
"6af455fe"]}
| # | field | type | meaning |
|---|---|---|---|
| 0 | job_id | string | opaque job id, echo it in mining.submit |
| 1 | parent | hex 32 B | block id of the parent, in header byte order (copy as is) |
| 2 | coinb1 | hex | coinbase transaction up to the extranonce |
| 3 | coinb2 | hex | coinbase transaction after the extranonce |
| 4 | merkle_branch | array of hex 32 B | siblings of the coinbase leaf, bottom-up (empty when the block has only the coinbase) |
| 5 | version | hex u32 | header version (big-endian hex of the value; write it little-endian) |
| 6 | nbits | hex u32 | compact block target (big-endian hex of the value; write it little-endian) |
| 7 | ntime | hex u32 | job start time = lowest allowed nTime (big-endian hex) |
| 8 | clean_jobs | bool | true = new parent block: drop all older jobs, their shares are stale |
| 9 | height | number | height of the block being mined |
| 10 | weights_hash | hex 32 B | the model (from mining.set_model) to evaluate attempts on |
| 11 | slot | number | dataset index of the job's model (same as set_model param 1): height mod 2 up to block 8799, then 2 / 3 |
| 12 | tx_count | hex u32 | number of transactions in the body including the coinbase (big-endian hex) |
| 13 | shares_root | hex 32 B | commitment to the block's list of training shares (copy as is) |
| 14 | ntime_max | hex u32 | highest allowed nTime for this job (big-endian hex) |
Jobs with clean_jobs = false refresh the block template on the same parent (the node refreshes every 30 s). Shares on the older jobs of the same parent stay valid, up to 16 jobs back.
5.6 mining.submit
{"id":4,"method":"mining.submit","params":["neb1…your-address….rig01","10985","0000000000000000","6af447ee","00002eb1"]}
{"id":4,"result":true,"error":null}
| # | field | format |
|---|---|---|
| 0 | user | the authorized username (informational) |
| 1 | job_id | from notify field 0 |
| 2 | extranonce2 | 16 hex = the 8 bytes you placed in the coinbase, in byte order |
| 3 | ntime | 8 hex = big-endian hex of the u32 nTime you placed in the header |
| 4 | nonce | 8 hex = big-endian hex of the u32 nonce you placed in the header |
Submit every attempt whose ticket meets the share target. A ticket that also meets the block target (nbits) is a block; submit it the same way and the pool forwards it to the node at once. At most 16 submits may be unanswered at a time; send further ones after replies arrive.
5.7 Other methods
| method | answer |
|---|---|
mining.suggest_difficulty [D] |
true; on ports 7801/7804/7805 the difficulty is set to D clamped to the port's limits |
mining.configure |
{} (no extensions) |
mining.extranonce.subscribe |
false with error [20, "Not supported.", null] |
| anything else | error [20, "Unknown method", null]; the connection stays open |
6. Building an attempt
Notation: ‖ = concatenation; H(tag, a, b, …) = BLAKE3(ASCII(tag) ‖ a ‖ b ‖ …) with no separator or length prefix (spec/node.md section 3); LE32(x) = 4 bytes little-endian; all 256-bit numbers (targets, tickets) are little-endian byte strings.
coinbase = coinb1 ‖ extranonce1 (8 B) ‖ extranonce2 (8 B) ‖ coinb2
leaf = H("leaf", BLAKE3(coinbase))
acc = leaf; for each sibling s in merkle_branch: acc = H("node", acc ‖ s)
body_root = H("body", LE32(tx_count) ‖ acc ‖ shares_root)
header (80) = LE32(version) ‖ parent (32) ‖ body_root (32) ‖ LE32(ntime) ‖ LE32(nbits) ‖ LE32(nonce)
key = BLAKE3(header) (this is also the block id)
ticket = NEB ticket of key on (model of weights_hash, dataset[index]) (v1: spec/shares.md 5.4, KEP1/RET2: spec/nebhash-v2.md 4)
share if ticket <= share_target (256-bit little-endian compare)
block if ticket <= target(nbits) target = mantissa << shift, nbits = shift << 24 | mantissa
- The coinbase leaf is always the leftmost leaf, so it is always the left input of
H("node", …). - Treat coinb1 and coinb2 as opaque bytes. For reference: the coinbase witness is
LE32(height) ‖ extranonce1 ‖ extranonce2; coinb1 ends with the witness length andLE32(height), and coinb2 holds the outputs (pool and project share). - Work space: the 32-bit nonce gives 2^32 attempts per header. For more, change extranonce2 (a new
body_root) or nTime. An attempt costs far more than a hash, so a GPU rarely exhausts a nonce range within one job. - nTime must satisfy
ntime ≤ ntime_value ≤ ntime_maxfrom the job, and must not be more than 90 s ahead of the pool's clock. Using the job'sntimeunchanged is always fine. - The ticket depends on the model and the dataset of the job; the weights size and the dataset's
NMD1/NMD2header tell which network to run. Afterclean_jobs = true(new block) the model changes, so all in-flight attempts on older jobs are worthless; stop them at once.
7. Errors
error is [code, message, null].
| code | message (examples) | meaning |
|---|---|---|
| 20 | incorrect size or hex of extranonce2 / ntime / nonce, ntime out of range, malformed submit params, too many shares in flight, Unknown method |
malformed or out-of-range request |
| 21 | job not found |
stale: the job belongs to an older block (or is more than 16 jobs old) |
| 22 | duplicate share |
the same extranonce2/ntime/nonce was already submitted, or the same header on this parent |
| 23 | low difficulty share of 1234.56 |
ticket above the share target (the number is the ticket's difficulty) |
| 24 | unauthorized worker |
submit before a successful authorize |
| 25 | not subscribed |
submit before subscribe |
A few stale or duplicate shares around block changes are normal. Code 23 on every share means the ticket computation, the header assembly or a byte order is wrong; check them against section 9.
8. Limits and protection
- At most 50 messages per second and 16 unanswered submits per connection; a client line longer than 10 KB, invalid JSON or more than 50 messages per second closes the connection.
- A client that stops reading (more than 4 MB of unsent data) is disconnected.
- If 50 % or more of the last 100 shares of a connection are invalid, its IP is banned for 10 minutes.
- A connection that has sent no share for 600 s is closed at the next job.
- Idle TCP connections are closed after 600 s.
9. Test vector
The session above (the pool on port 7802, difficulty 20000) with extranonce2 = 0000000000000000 and ntime = 6af447ee. The full messages, including the complete set_model line, are in https://neb.suprnova.cc/neb-stratum-example.json.
extranonce1 4e0000012317f237
coinbase 01010000000000000000000000000000000000000000000000000000000000000000ffffffff14411500004e0000012317f23700000000000000000200b33f71000000000104290170dc4dc035fdc27c5a1db5b7bc7c0e09aee08a28f37abb6256a01827b700e1f5050000000002270bbd6380c0c7022176e202083d5671ed6bb7bc0c27350bcbd134846ce87dd5
merkle root 480b7029aa6d97b2cc61edafb5ab59a8b1cd999b637e9b73e9fa5c1dfdccdba7 (= leaf, the branch is empty)
body_root 022746bed909155d8814ac149e210f23cdd7040f996dc242d092b215a2c92977
nonce 00000000
header 01000000513d8d34b62ba1aa3ec4495006dfca9256560e07791c931d9483dccc2121e76c022746bed909155d8814ac149e210f23cdd7040f996dc242d092b215a2c92977ee47f46a8044cec300000000
key b1ca9209fee3ecb5a4f6579af92dea2021eda429fdf676132192dba783387b81
ticket 44ec6b4e59d8c0b6d159588155687d709fbf6499d027b55d56276259932a1c8f (not a share)
nonce 00002eb1 (the first share, found at the 11 954th attempt)
header 01000000513d8d34b62ba1aa3ec4495006dfca9256560e07791c931d9483dccc2121e76c022746bed909155d8814ac149e210f23cdd7040f996dc242d092b215a2c92977ee47f46a8044cec3b12e0000
key 06343b25ea077e3e5b322fa11b632433969add41d74e0e4784b38ec5e166c7fc
ticket eb366f7a7fa33f35b8138e5347533af03e0697726dc1a19b46fe288df0700200 (difficulty 26846.14 ≥ 20000: a share)
submit ["<address>.<worker>","10985","0000000000000000","6af447ee","00002eb1"]
Check your implementation step by step: the coinbase, then body_root, then the header bytes (note how ntime 6af447ee and nbits c3ce4480 appear little-endian in the header), then the key, and finally the ticket with the model from the example file and dataset slot 1.
9.1 nebhash v2 test vector
https://neb.suprnova.cc/neb-stratum-example-v2.json is the same exercise after the fork: a session with a KEP1 job (height
8800, dataset index 2, the fork model = the initial state of the upgrade seed BLAKE3("neb-model-upgrade" || LE32(8800) || LE32(0)),
13 000-byte set_model), the coinbase, body_root, header, key and ticket of nonce 0 and of the first share at difficulty 200, and the
accepted submit. Check your KEP1 ticket against it and the official vectors vectors/nebhash2.txt of NEB 0.3.0.
10. Checklist for a new miner
- Load
neb-datasets.binand check its BLAKE3 hash. - Connect, subscribe, authorize; read lines with a persistent buffer (section 4).
- On
set_model: decode the base64, check the hash, store the model underweights_hash. - On
notify: build coinbase → body_root → header for your extranonce2; take the model byweights_hashand the dataset by its index (field 11); run v1 or nebhash v2 by the dataset's network; onclean_jobsabort old work. - Roll the nonce (then extranonce2); compute
key = BLAKE3(header)and the ticket; submit whenticket ≤ share_target. - Reproduce the test vector of section 9 before mining on the pool.
11. NEB 0.3.0 network upgrade (nebhash v2 from block 8800)
From block 8800 (about Friday 13 November 2026, 19:00 UTC; the height decides) both models of the chain are small
convolutional networks, KEP1 (Kepler signals, from block 8800) and RET2 (retinal scans at 64 × 64, normal or abnormal,
from block 8801), each starting from a new model. On this pool's protocol only the values change, not the messages:
set_model and notify name dataset index 2 or 3, the weights are 13 000 or 11 140 bytes, and the ticket is computed with
nebhash v2. One attempt costs much more than a v1 attempt (CPU about 2 ms for KEP1 and 7 ms for RET2 instead of 0.4 ms), so
hash rates drop by that factor and the difficulty follows; VarDiff adjusts each connection. A miner that only knows the v1
network or the old dataset file cannot mine after block 8799. The pool also hands every share that meets the chain's share
target to its node, so the training steps of this pool's miners become part of the chain (spec/pool.md section 7.6).
Official NEB Links
NEB is developed on GitHub at SilasEigen/neb; the release page has the node, the wallet, the reference miners, neb-datasets.bin and the signed SHA256SUMS.