Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CoinCync

A privacy cryptocurrency with CPU-only proof of work.

What it is

CoinCync is a privacy-by-default blockchain. Every transaction hides its sender, its recipient, and its amount, using:

  • CLSAG ring signatures — the sender is one of N possible signers, and which one is computationally indistinguishable
  • Stealth addresses — every transaction output is a one-time address that only the recipient can recognize
  • Bulletproofs+ range proofs — amounts are encrypted, but the network can prove they fall in a valid range without learning the value
  • MimbleWimble cut-through (Phase 2, planned) — historical transaction graphs collapse, leaving only unspent commitments
  • Lelantus Spark anonymity sets (Phase 2, planned) — large-anonymity-set spend proofs giving up to 16,384-coin rings

Activation status: Orchard/Spark phase-2 modules are currently disabled on the live public testnet and are not active consensus rules yet.

Mining is RandomX only by Constitution Article V — the same CPU-biased, memory-hard PoW algorithm Monero has used in production since 2019. Any laptop can participate. No ASICs, no GPU farms, no permission, no KYC, no stake. See Consensus & PoW for the full rationale on why a single strong algorithm is stronger than a rotation.

What it isn’t

  • Not transparent. Unlike Zcash, there is no transparent escape hatch. Every transaction goes through the privacy machinery — there is no t-addr to launder funds out of. Mandatory privacy is enforced as a consensus rule, not as a recommendation.
  • Not premined. Every coin is mined by someone. There is no founder allocation, no ICO, no airdrop, no dev tax.
  • Not behind a CDN. The public infrastructure (block explorer, RPC API, landing page) is run as a federation of independent Caddy hosts under the operator’s direct control. No traffic passes through a third-party MITM. See Federation & DDoS for the full reasoning.
  • Not opaque to its users. View keys let any wallet holder selectively disclose to an exchange, an auditor, or a tax authority — without breaking privacy for anyone else.

What’s in these docs

SectionWhat you’ll find
Getting startedBuild the binary, run a testnet node, create a wallet
ProtocolThe cryptographic guarantees, the consensus model, the emission curve, the transaction wire format
API referenceJSON-RPC 2.0 and REST endpoint inventory with examples
OperationsRunning production nodes, deploying the explorer, federation, Tor hidden services
GovernanceThe constitution, the bill of rights, what changes can and can’t be made, the disclaimer

Where the live data is

Status

CoinCync is pre-launch on mainnet. The current testnet is live and stable. Mainnet genesis is scheduled for October 1, 2026 00:00:00 UTC. Until then, the explorer and API surfaces serve testnet data; mainnet selectors return a launch countdown.

For protocol decisions, design notes, and the why-it-works-this-way reasoning, start with Privacy model and read forward.

Build from source

CoinCync is written in Rust. Pre-built Linux and Windows binaries are available — see the download table at the top of Run a testnet node. Building from source is for users on other platforms, those who want to verify reproducibility, or contributors patching the code. The build is reproducible: same commit + same Rust version → byte-identical output.

Prerequisites

ToolVersionWhy
Rust toolchain1.75 or newerEdition 2021, rust-version pinned in Cargo.toml
cmake, clang, pkg-config, libssl-devany recentfor building librocksdb-sys (the storage layer)
gitanysource checkout
cargobundled with Rustbuild orchestration

Install on Debian / Ubuntu:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
sudo apt update
sudo apt install -y cmake clang libclang-dev build-essential pkg-config libssl-dev git curl

Install on macOS:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
brew install cmake llvm git

Windows: use WSL2 running Ubuntu, then follow the Linux instructions. Native Windows (MSVC) builds also work, with one known test-suite quirk described in Run the test suite below.

Clone and build

git clone https://github.com/CoinCync/Coincync-Testnet-.git
cd Coincync-Testnet-
cargo build --release --features "randomx testnet"

First build takes 5–15 minutes depending on the machine because RocksDB and several Rust crypto crates compile from source. Subsequent builds are incremental (seconds).

The five binaries land under target/release/:

BinaryPurpose
coincync-nodethe full node daemon (P2P + RPC + chain)
coincync-walletcommand-line wallet (create, list, send)
coincync-minerstandalone solo miner (talks to a node via RPC)
coincync-tui-minerterminal UI for monitoring the miner
coincync-tui-operatorterminal UI for monitoring node health

Feature flags

The build accepts compile-time features. The defaults are sensible for a testnet node; you only need to override them in unusual cases.

FeatureDefaultEffect
randomxonEnables the RandomX PoW backend. Required — running without it makes compute_pow_hash panic at runtime. There is no non-RandomX fallback; the panic is by design so you can’t accidentally ship a PoW-skipping node.
testnetoffSelects testnet network magic, ports, and genesis. Enable when you want a node that talks to the public testnet seed nodes.
mainnetoffSelects mainnet network magic, ports, and genesis. Enable after the October 1, 2026 mainnet launch.
metricsoffExports Prometheus metrics on a separate HTTP port. Recommended for production hosts.
test-utilitiesoffExposes a few Mempool::add_skip_crypto-style helpers used only by integration tests. Never enable in production builds.

Verify the build

./target/release/coincync-node --help
./target/release/coincync-node print-genesis-hash
# Should print the expected genesis hash; if it doesn't match the
# hardcoded constant in src/testnet.rs / src/mainnet.rs, the build
# is corrupt or the source is modified.

Run the test suite

cargo test --lib -p coincync
# Expect ~520+ tests passing, 0 failed, 0 ignored.

For the full suite including the integration tests in tests/*.rs:

cargo test -p coincync

Windows: the librocksdb_sys rlib race

If you’re on native Windows (MSVC), cargo test -p coincync will sometimes fail partway through with an error like:

error: crate `librocksdb_sys` required to be available in rlib format,
       but was not found in this form

cascading into a bunch of could not compile coincync (test "…") failures. Individual tests (cargo test --lib -p coincync, cargo test --test wallet_roundtrip -p coincync) work fine, and Linux / macOS are unaffected.

Root cause: parallel-link race on a static-archive read. cargo test -p coincync builds ~12 integration-test binaries, each of which statically links librocksdb_sys (~80 MB). With the default build parallelism (= CPU count), several of those final link steps fire simultaneously, and each rustc invocation opens the same liblibrocksdb_sys-<hash>.rlib as a static archive to pull symbols out of. On Windows, the file-sharing semantics for static-archive reads are strict enough that a second reader occasionally observes a partial / wrong-magic view of the archive and errors out.

This was previously blamed on OneDrive sync interference. That was wrong — relocating CARGO_TARGET_DIR to %LOCALAPPDATA% (completely outside any sync daemon) does not fix the race. The real cause is Windows + parallel static-archive reads.

The fix: serialize the build with --jobs 1. cargo test -p coincync --jobs 1 builds one target at a time. Dependency compilation is slower end-to-end (~5 min vs ~2 min), but test execution itself is still parallel under the Rust harness — --jobs affects the build, not the runner. The rlib race cannot happen because there is only ever one reader at a time.

Fix A — use scripts/windows-test.sh (recommended). The script sets --jobs 1 automatically on Windows and also relocates CARGO_TARGET_DIR out of OneDrive (IO-cost win, not correctness):

./scripts/windows-test.sh                         # full suite
./scripts/windows-test.sh --lib                    # library tests only
./scripts/windows-test.sh --test wallet_roundtrip  # one integration test

It’s a no-op on Linux / macOS — no target relocation and no --jobs 1 forced. If you ever need to opt out of serial build on Windows (you shouldn’t), set COINCYNC_SERIAL_BUILD=0 in the environment.

Fix B — run the raw cargo command yourself:

cargo test -p coincync --jobs 1

Works anywhere; slower than parallel but reliable.

Fix C — run integration tests one at a time. Cargo only links one test binary per invocation, so there’s no concurrent access to the same rlib:

cargo test --lib -p coincync                      # fastest path, always safe
cargo test -p coincync --test wallet_roundtrip
cargo test -p coincync --test transaction_lifecycle
# ... and so on

This is what scripts/windows-test.sh falls back to if you pass it explicit --test <name> arguments.

Why relocating off OneDrive is still a good idea

Even though the parallel-link race is not OneDrive-specific, keeping target/ out of OneDrive / Dropbox / iCloud is still worth doing for two separate reasons: (1) fewer IO round trips because the sync daemon doesn’t index multi-GB artifact trees, and (2) faster incremental rebuilds because no file-lock contention with the sync agent. scripts/windows-test.sh does this for you automatically. If you want it for every cargo invocation (not just tests), set the env var in your shell profile:

# PowerShell profile ($PROFILE):
[Environment]::SetEnvironmentVariable('CARGO_TARGET_DIR', "$env:LOCALAPPDATA\coincync-target", 'User')

# Git Bash profile (~/.bashrc):
export CARGO_TARGET_DIR="$LOCALAPPDATA/coincync-target"

Rust-analyzer, VS Code, and IntelliJ Rust all honor CARGO_TARGET_DIR.

Where to put the binaries on a real host

Production deployment ships the binaries to /usr/local/bin/ and runs them under systemd. See Deploying a node for the full runbook. For local development just leave them in target/release/.

Run a testnet node

You can either download a pre-built binary or build from source (Build from source).

Download a pre-built binary

# Linux example
curl -L -o coincync-node \
  https://coincync.network/release/v1.0.1-testnet/coincync-node-linux-x86_64
chmod +x coincync-node
./coincync-node --network testnet --data-dir ~/.coincync

Where to run a node, where NOT to mine: running a coincync-node (P2P relay, RPC, no PoW computation) on a cloud VPS is fine. Mining is different — DigitalOcean, Cloudflare, AWS, GCP, and most major hosting providers explicitly prohibit cryptocurrency mining in their TOS and will terminate accounts that compute PoW. Run coincync-miner / coincync-tui-miner on personal hardware (your laptop, desktop, or a server you physically own). RandomX is CPU-only by design specifically so home machines can secure the chain — that’s the architecture, not a workaround.

Quick start

./coincync-node --network testnet --data-dir ~/.coincync

That’s it. The node will:

  1. Open (or create) a RocksDB database under ~/.coincync/testnet/
  2. Initialize the genesis block if the database is empty (and verify its hash against the hardcoded constant in src/testnet.rs)
  3. Start the P2P listener on 0.0.0.0:28080
  4. Start the JSON-RPC server on 127.0.0.1:28081
  5. Connect to the public testnet seed nodes and begin syncing

Stop the node with Ctrl-C. Restart it later — chain state is on disk; it picks up where it left off.

What success looks like

The first ~30 seconds of log output should look something like this:

INFO  CoinCync 1.0 node starting
INFO  Network:  Testnet
INFO  Data dir: "~/.coincync"
INFO  Opening database at "~/.coincync/testnet"
INFO  Chain tip: height=0, hash=41f970df
INFO  Starting P2P listener on 0.0.0.0:28080
INFO  Starting RPC server on 127.0.0.1:28081
INFO  RPC server bound, methods: get_info, get_blockchain_info, ...
INFO  Connected to seed1.coincync.network:28080
INFO  Sync started — target height 12345
INFO  Node is running. Ctrl-C to stop.

If you see CRITICAL: Genesis hash mismatch! instead, your build is corrupt or you modified src/testnet.rs without recomputing the hash. Don’t ignore that error — it means the binary you built will fork off the live testnet on the very first block.

Talk to the running node

In a second terminal:

# How's the chain tip?
curl -sX POST http://127.0.0.1:28081 \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_info"}' | jq .

# Block at a specific height
curl -sX POST http://127.0.0.1:28081 \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_block_by_height","params":[100]}' | jq .

# Mempool stats
curl -sX POST http://127.0.0.1:28081 \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_mempool_info"}' | jq .

The full method inventory is in JSON-RPC reference.

Common CLI flags

FlagDefaultWhat it does
--network <testnet|mainnet|regtest>testnetWhich network to join
--data-dir <path>~/.coincyncWhere chain + wallet state lives
--p2p-bind <addr>0.0.0.0:28080 (testnet)P2P listen address. Set to 0.0.0.0:port to accept inbound
--rpc-bind <addr>127.0.0.1:28081 (testnet)JSON-RPC listen address. Always keep this on 127.0.0.1 in production unless you’ve put a Caddy reverse proxy in front (see Deploying a node)
--rest-bind <addr>noneOptional axum REST API. Defaults to 127.0.0.1:<rpc_port + 2> if --explorer is passed
--exploreroffMounts the embedded block explorer at GET / on the REST port. Local development only. Production uses /deploy/explorer/
--addnode <ip:port>(seed list)Force-add a peer at startup. Useful for isolated test networks
--no-peersoffDisable all peer discovery. Used for regtest and isolated tests
--log-level <trace|debug|info|warn|error>infoTracing-subscriber filter

Networks

NetworkP2P portRPC portGenesisStatus
testnet2808028081March 6, 2026live
mainnet1908019081October 1, 2026pre-launch
regtest(any)(any)local-onlyfor isolated dev

Try the local explorer

If you want to see the block explorer alongside your node, pass --explorer:

./target/release/coincync-node --network testnet --explorer

Then visit http://127.0.0.1:28083/ in your browser. Localhost-only by default — nobody on the LAN or the public internet can see it. This is for local development only; for a public block explorer use the standalone Caddy stack in /deploy/explorer/.

Operating a public seed (operators)

If you run infrastructure that should accept inbound peers: open TCP 28080 in your cloud firewall (not only the host), install coincync-node under systemd, and verify DNS/TCP health. Broader deployment notes are in Deploying a node. In the source tree, scripts live under deploy/ops/ (README): install-testnet-node.sh, verify-community-bootstrap.sh, plus scripts/verify-community-join-readiness.ps1 on Windows.

Next steps

  • Create a wallet — generate a seed phrase, derive an address, receive testnet CYNC from the faucet
  • Deploying a node — systemd unit, docker compose, firewall rules, monitoring
  • JSON-RPC reference — every method the node exposes, with sample requests

Create a wallet

The coincync-wallet binary creates encrypted on-disk wallets, derives addresses, signs transactions, and (when pointed at a running node) scans the chain for incoming payments.

Generate a new wallet

./target/release/coincync-wallet --network testnet create

You’ll be prompted for:

  1. A wallet password (used to AEAD-encrypt the seed on disk via Argon2id + XChaCha20-Poly1305). Pick something long.
  2. Confirmation of the password.

The command prints a 24-word BIP39 mnemonic phrase. Write it down on paper. Keep the paper somewhere you can find it but a stranger cannot. The mnemonic is the only way to recover the wallet if the encrypted file is lost. If both the file and the mnemonic are lost, the funds are unrecoverable — there is no customer support, no recovery service, no backdoor.

The wallet file lands at ~/.coincync/wallets/default.wallet by default. Override with --wallet-file <path>.

Restore a wallet from a seed phrase

./target/release/coincync-wallet --network testnet restore

Prompts for the mnemonic phrase, then a new password to encrypt the restored wallet on disk. Produces an identical wallet to the one the mnemonic was originally generated for — same addresses, same view keys, same scan position.

Show your address

./target/release/coincync-wallet --network testnet address

Prints the primary stealth address. This is what you give to a sender. Every transaction to this address generates a fresh one-time output that only you (with your view key) can detect; observers cannot link multiple payments to the same address — that’s the whole point of stealth addressing.

Check the balance

./target/release/coincync-wallet --network testnet balance

To know your balance, the wallet has to scan the chain for outputs addressed to you. The first scan from a fresh wallet on a synced testnet node takes a few minutes — every block since the wallet’s scanned_height is fetched, every output is tested against your view key, and matches are decrypted to recover the amount.

Subsequent scans are incremental — only blocks since the last scan are checked.

Get testnet CYNC from the faucet

# Visit https://faucet.coincync.network in a browser
# OR via the API:
curl -sX POST https://faucet.coincync.network/api/drip \
     -H 'content-type: application/json' \
     -d '{"address":"<your stealth address>"}'

The faucet sends a fixed amount (typically 10 CYNC) per address per cooldown window (24 hours). Wait a few seconds, then re-run balance and you should see the funds.

Send a payment

./target/release/coincync-wallet --network testnet send \
     --to <recipient stealth address> \
     --amount 1.5

The wallet:

  1. Selects your unspent outputs (UTXOs) that sum to ≥ amount + fee
  2. Picks a ring of decoys from the chain’s anonymity set
  3. Constructs a CLSAG ring signature
  4. Generates a Bulletproofs+ range proof for each output
  5. Computes the canonical signing_hash (single source of truth — see Transaction format)
  6. Submits the transaction to the local node’s mempool via send_raw_transaction
  7. Returns the transaction hash

The transaction propagates through the P2P network and is mined into a block within ~2 minutes (testnet inherits mainnet’s 120-second target block time). Once it’s in a block, the recipient’s wallet detects it on its next scan.

Show the seed phrase (with password)

./target/release/coincync-wallet --network testnet show-seed

Prompts for the wallet password, then prints the mnemonic. Use this for backup. Don’t run it on a screenshare or in a recorded terminal session.

Wallet security checklist

  • Mnemonic written down on paper, stored somewhere physically secure
  • Wallet file backed up to an offline medium (not a cloud sync)
  • Wallet password is long enough that even a compromised file is uncrackable in any reasonable time. The Argon2id KDF with default parameters resists offline attack at ~1 hash/second on commodity hardware
  • No screenshare, no terminal recording when running show-seed
  • Don’t store the wallet file on a machine where untrusted code runs (browsers, IM clients, etc.) — the file is encrypted but a keylogger captures the password as you type it

For the threat model behind these recommendations, see Operations → Security.

Next steps

  • Privacy model — what the cryptographic primitives actually protect against
  • JSON-RPC reference — call the node’s API directly without the wallet binary

Mine CYNC from your PC

CoinCync uses RandomX — a CPU-only proof-of-work designed so that anyone with a regular computer can secure the network. No GPUs, no ASICs, no warehouse. The chain is more decentralized when more home machines mine; that’s the whole point of the algorithm.

You will need

  • Any modern computer (Windows, Linux, or macOS)
  • A CoinCync wallet address starting with tCYNC... to receive rewards (create a wallet if you don’t have one)
  • A few minutes

You do not need a GPU, special hardware, or any kind of exchange account.

Quick start

Windows

Download two binaries from coincync.network:

Open PowerShell in the folder where you saved them and run:

# Start a node (one-time, leave running)
.\coincync-node-windows-x86_64.exe --network testnet

# In a second window, start mining (replace tCYNC… with your address)
.\coincync-tui-miner-windows-x86_64.exe --testnet --address tCYNC3Z6jt428m... --threads 4 --log

Linux

curl -L -O https://coincync.network/release/v1.0.1-testnet/coincync-node-linux-x86_64
curl -L -O https://coincync.network/release/v1.0.1-testnet/coincync-tui-miner-linux-x86_64
chmod +x coincync-node-linux-x86_64 coincync-tui-miner-linux-x86_64

./coincync-node-linux-x86_64 --network testnet &
./coincync-tui-miner-linux-x86_64 --testnet --address tCYNC3Z6jt428m... --threads 4 --log

macOS

Same as Linux. The macOS-native build is not yet published; build from source for Apple Silicon (see Build from source).

What success looks like

The miner prints scrolling tracing-style output:

2026-05-02T19:00:01Z  INFO  miner: ✓ BLOCK FOUND at height 753!
2026-05-02T19:00:03Z  INFO  chain h=753 tip_age=2s diff=302 peers=8 synced=true
2026-05-02T19:00:18Z  INFO  miner: ✓ BLOCK FOUND at height 754!

Each BLOCK FOUND line means your machine just produced a block. The mining reward (currently ~50 CYNC per block) goes to the address you passed in --address.

Where NOT to run a miner

Most cloud and dedicated-server providers prohibit mining, including DigitalOcean, AWS, GCP, Azure, Cloudflare, Hetzner, OVH, and Vultr. Trying to mine on those services will get your account suspended.

The right places to run a CoinCync miner are:

  • Your home computer
  • A spare machine you own (mini-PC, Pi 5, old laptop)
  • A box you’ve put in colocation (you own the hardware, the provider only rents space and bandwidth)

This is fine — RandomX is designed to run efficiently on the kind of CPU you already have. A modest desktop will mine just as well as a purpose-built rig.

How much will I earn?

Testnet CYNC has no monetary value. Treat mining rewards as a way to verify your wallet works, send transactions, and stress-test the network. Mainnet launch is targeted for October 2026; testnet mining is preparation, not income.

Stopping the miner

Press Ctrl+C in the miner window. The node keeps running until you stop it the same way. Restart either at any time — your chain state and wallet are saved on disk and pick up where they left off.

What if I’m the only miner?

If everyone running CoinCync stops mining, the chain stops producing blocks. The chain doesn’t break — it just sits at the current tip until someone resumes. This makes mining inherently a community activity. The more home miners, the more resilient the network. That’s exactly the architecture we want.

Privacy model

CoinCync inherits the privacy stack used by Monero (CLSAG ring signatures + stealth addresses + bulletproofs), with two upgrades for Phase 2 (MimbleWimble cut-through and Lelantus Spark anonymity sets), and one design difference: mandatory privacy enforced as a consensus rule, with no transparent escape hatch.

What an outside observer sees

For a CoinCync transaction in a public block:

FieldVisible to observer?Why
SenderhiddenThe CLSAG ring signature proves “one of these N ring members signed” without revealing which one
RecipienthiddenThe stealth address is a one-time public key only the recipient can recognize using their view key
AmounthiddenThe Pedersen commitment is encrypted; the Bulletproofs+ range proof attests it falls in [0, 2^64) without revealing the value
Transaction graphobscuredDecoy outputs in every ring make it computationally infeasible to track UTXOs through history
Block-level metadatavisibleBlock height, timestamp, miner pubkey (a one-time stealth address), tx count, total block size — these are by-design public so the chain can be verified by anyone

A network observer who logs every transaction sees: timestamps, byte sizes, ring sizes, and the cryptographic proofs. That’s all.

The four primitives

1. CLSAG ring signatures

CLSAG (Concise Linkable Spontaneous Anonymous Group signatures) is a ring-signature scheme where:

  • The signer chooses N − 1 decoy outputs from the chain’s anonymity set (the UTXO pool of all unspent outputs)
  • The signer’s real input is mixed in with the decoys
  • The resulting signature proves “I know the spending key for one of these N outputs” without revealing which
  • A key image (I = x · H_p(x · G), where x is the secret key) is published with the signature so the network can detect double-spends without learning which output was actually spent

CoinCync’s minimum ring size is 11 (constitutional minimum, MIN_RING_SIZE in src/constants.rs). The default is the same as the minimum — there’s no incentive to use a larger ring than the network requires, since wallets are uniform in their behavior and any deviation creates a fingerprint.

The CLSAG implementation lives in src/crypto/clsag.rs. The verifier in src/consensus/validation.rs::verify_ring_signature is called by both the consensus block-validator and the mempool admission path — same function, no drift between miner and verifier.

2. Stealth addresses

A stealth address pair (spend_pub, view_pub) lets a sender generate a fresh one-time output public key for every transaction:

shared_secret = ECDH(tx_secret, view_pub)
output_pub_key = H(shared_secret || output_index) · G + spend_pub

The sender publishes tx_pub_key = tx_secret · G along with the output. The recipient, scanning the chain with their view key, recomputes the shared secret and tests every output for membership.

Because each output has a unique key, an external observer cannot link multiple payments to the same recipient. The recipient sees them all (they all derive from the same (spend_pub, view_pub) pair), but nobody else can.

Implementation: src/crypto/stealth.rs. The wallet’s chain scanner is in src/wallet/scanner.rs.

3. Bulletproofs+ range proofs

Output amounts are stored as Pedersen commitments C = a·G + b·H where a is the amount and b is a blinding factor. The amount itself is never on the chain.

A range proof attests that the committed amount falls in [0, 2^64) without revealing it — this prevents a malicious sender from creating a transaction with a negative amount (which would let them mint money from nothing). CoinCync uses Bulletproofs+, which is ~96 bytes shorter and ~10% faster to verify than original Bulletproofs.

Implementation: src/crypto/bulletproofs.rs. Verification is called from src/consensus/validation.rs::verify_output_range_proofs, used by both the consensus path and mempool admission.

4. Balance proof

Pedersen commitments are additively homomorphic: C(a) + C(b) = C(a + b). The balance proof leverages this:

sum(input_pseudo_commitments) = sum(output_commitments) + fee_commitment

The verifier checks this equation without learning any individual amount. If the sender tried to create money from nothing, the equation wouldn’t hold and the transaction would be rejected.

Implementation: src/crypto/disclosure.rs::verify_balance_proof. Same dual-call pattern as ring sig and range proof.

View keys

A view key is a separate scalar that lets a third party scan the chain for outputs to a specific address without being able to spend them. The wallet derives one view key per epoch.

This enables selective disclosure:

  • Show your view key to your accountant — they see your incoming and outgoing payments and can compute capital gains, but they cannot move funds
  • Provide your view key to an exchange for KYC compliance — they verify your deposits without holding withdrawal authority
  • Prove a specific transaction is yours without revealing your spend key

The view key is read-only. The spend key (which CAN move funds) is never required to be shared.

Implementation: src/crypto/view_keys.rs. Wallet integration: src/wallet/key_epoch.rs.

Mandatory privacy

This is where CoinCync differs from Zcash. In Zcash you can hold value in a transparent address (t-addr) that behaves exactly like a Bitcoin address — sender, recipient, and amount all visible. The shielded pool is an option, not a requirement. In practice, most Zcash transactions are transparent because shielded operations were historically expensive.

CoinCync follows the Pirate Chain model: there is no transparent escape hatch. The consensus rule in src/consensus/privacy_policy.rs::enforce_privacy_policy rejects any transaction that:

  • Has a zero output commitment (a transparent amount)
  • Has a zero stealth address (a transparent recipient)
  • Has zero ring inputs (a transparent sender) — coinbase exempt

This is enforced at the consensus level, not the mempool level — even a maliciously-crafted block trying to include a transparent transaction is rejected.

The motivation: optional privacy is no privacy at all, because the small population of users who do opt in stands out from the larger population who don’t. Mandatory privacy means every user is part of the same anonymity set.

Phase 2 upgrades

Status: these upgrades are planned and code-gated, but currently disabled on the live public testnet until formal activation.

These are implemented in the codebase, header-committed in every block, but verification is gated until the respective fork-signal activation heights:

MimbleWimble cut-through

Once a transaction is buried under enough confirmations and the chain is sure no reorg will revisit it, MW cut-through collapses the historical transaction graph. Outputs that have been spent and re-spent disappear from the chain, leaving only the unspent commitments. This:

  • Compresses the chain (the chain shrinks rather than grows monotonically)
  • Eliminates traceable transaction history (you can’t analyze a graph that no longer exists)
  • Comes with a kernel-set commitment (mw_kernel_root in the block header) that proves the cut-through was valid

Activation: CIP-006 (BIP9-style fork signaling). Implementation: src/crypto/mw_cutthrough.rs + src/storage/kernels.rs.

Lelantus Spark anonymity sets

The current 11-member CLSAG ring gives a 1-in-11 anonymity set. Lelantus Spark replaces this with a one-out-of-many proof scheme that supports anonymity sets of up to 16,384 coins per spend. The crypto is more expensive but the anonymity gain is roughly 1000× per transaction.

Activation: CIP-005 (BIP9-style fork signaling). Implementation: src/crypto/lelantus_spark.rs + src/storage/spark.rs. The current implementation is a Schnorr-style one-out-of-many proof using the existing CLSAG primitives — not the logarithmic-size Groth-Kohlweiss proof from the original Spark paper, but with the same security properties (completeness, soundness, anonymity, linkability).

Hash domain separation

Every hash preimage in the protocol is domain-separated with a unique tag prefix. This is the kind of thing that doesn’t matter until it does, and when it does, it’s a consensus split.

PreimageDomain tag
Block header hashcoincync/header/v1
Transaction signing hashcoincync/tx-sign/v1
CLSAG Fiat-Shamir(per the CLSAG construction in src/crypto/clsag.rs)
Bulletproof transcriptCoinCync_RangeProof (Merlin transcript)
BP+ transcriptCoinCync_BPPlus_RangeProof

Two hashes computed with different tags can never collide, even if they hash the same underlying bytes. This eliminates a class of cross-protocol attacks where an adversary tries to make a CLSAG signature also be a valid block header hash, or similar.

What CoinCync’s privacy doesn’t protect against

Honesty about limitations:

  • Network-layer correlation. If you broadcast a transaction from your home IP, the network sees the IP that broadcast it. Use Tor (Caddy supports .onion, the node binary supports --proxy socks5://...) if your threat model requires it. See Tor hidden services.
  • Timing analysis. A user who broadcasts at predictable times reveals patterns. Wallets should batch and randomize broadcast timing where possible.
  • Public infrastructure correlation. A user who only ever queries the chain through explorer.coincync.network from one IP is correlated by the explorer’s logs. Use a self-hosted node, federation mirrors, or the .onion mirror.
  • Out-of-band metadata leakage. If you tell someone “I sent you 10 CYNC at 14:32 on Tuesday,” your privacy is bounded by what you’ve voluntarily disclosed, regardless of how good the on-chain crypto is.
  • Chainalysis-style scraping. Industrial-rate scraping of public explorers builds correlation datasets over time. The defense is rate limiting at the explorer (deploy/explorer/Caddyfile) and federation across multiple hosts. See Federation & DDoS.
  • Ring decoy-selection attacks. If a wallet’s decoy selection algorithm is biased, an attacker can statistically distinguish the real input from the decoys. CoinCync uses an age-weighted decoy selector (src/crypto/ring_selection.rs) that matches Monero’s current best practice.

Next reading

Consensus & proof of work

CoinCync’s consensus layer is proof of work — single algorithm, memory-hard, CPU-biased, ASIC-resistant. Article V of the Constitution commits to RandomX and only RandomX. No rotation, no fallback, no hybrid, no PoS transition path. Single knob, thoroughly studied, verifier and miner call the same function.

RandomX

RandomX is a CPU-friendly, ASIC-resistant, memory-hard proof-of-work algorithm originally designed for Monero and in production there since November 2019. It uses a 256 MB scratchpad and a virtual machine that interprets randomly-generated bytecode — both of which favor general-purpose CPUs over fixed-function hardware. An ASIC for RandomX would essentially be a CPU, which offers no economic advantage over commodity consumer hardware. This is the longest-running, most-battle-tested CPU-biased PoW design in production on any live chain.

CoinCync wraps the upstream randomx-rs crate. The implementation lives in src/consensus/pow.rs. The randomx Cargo feature is on by default — building without it makes compute_pow_hash return an error at runtime with "RandomX feature not enabled". This is intentional: there is no non-RandomX fallback, so an accidental misbuild produces a binary that refuses to mine, not one that silently uses a weaker hash.

PowAlgorithm in src/consensus/pow.rs is a single-variant enum. The enum exists (rather than being removed entirely) so that any code that matches on algorithm kind continues to compile cleanly, and so any future attempt to add a second variant immediately trips code review. Adding a second PoW algorithm is a constitutional violation (Article V) and would also require refreshing critical_files.lock, so the change cannot happen silently.

Why RandomX-only and not multi-algo rotation

This is worth explaining because it’s the opposite of the “diversify across algorithms” intuition a lot of people start with:

  1. A rotation is as ASIC-resistant as its weakest algorithm, not its strongest. Any rotation that includes a fast algorithm like Blake3 is trivially ASIC-mineable every N blocks. The weakest algorithm defines the upper bound on an adversary’s ability to concentrate hashrate.
  2. Multi-algo expands attack surface. Per-algorithm difficulty tracking, cross-algorithm reorg rules, algorithm-selection enforcement — every one adds lines to the verifier, and every line is a potential bug. Chains that tried multi-algo rotation (Verge, Bitcoin Gold) have a documented history of consensus-level exploits that cleaner single-algo designs did not have.
  3. RandomX alone has a five-year production record as of 2024 on Monero. Zero consensus exploits attributable to algorithm choice. Zero hashrate concentration events attributable to algorithm choice. That’s an outcome you cannot replicate by inventing a new rotation.

See the Constitution Article V rationale for the full argument. It is load-bearing and worth reading.

The PoW preimage

input  = anchor || nonce || tx_root
height = (block height — used for RandomX VM key derivation)
output = RandomX(input, height)  // 256-bit hash

A block satisfies the difficulty target if output ≤ target, where target is the per-block difficulty target stored in header.target.

The anchor is a deterministic chain-history value computed by compute_full_anchor(prev_hash, height, timestamp). It binds every PoW solution to its specific chain context — you cannot pre-compute solutions for a hypothetical future chain, and a solution computed for block h is invalid at block h+1 because the anchor differs.

The RandomX VM key derives from the block’s seed epoch. CoinCync rotates the VM seed every RANDOMX_KEY_EPOCH = 64 blocks, which is ~2 hours at 120-second blocks. This matches Monero’s epoch cadence and amortizes the ~1-second VM-key setup cost across ~60 blocks of hashing.

Implementation:

  • src/consensus/pow.rs::compute_pow_hash — what the miner calls
  • src/consensus/pow.rs::verify_pow — what the verifier calls inside validate_block
  • They are the same function. The miner does not have a “fast path” or a “loose mode.” If the miner produces a hash, the verifier produces the same bytes.

Difficulty adjustment

CoinCync uses LWMA (Linearly Weighted Moving Average) difficulty adjustment, the same algorithm Monero adopted in 2018. LWMA is responsive to short-term hashrate changes (better than Bitcoin’s 2-week retarget), resistant to time-warp attacks, and produces stable block intervals.

The target block time is fixed at 120 seconds (TARGET_BLOCK_TIME in src/constants.rs). 2-minute blocks align with the mountain emission curve and give LWMA enough samples to damp hashrate volatility without overcorrecting. The window over which LWMA averages is 60 blocks (~2 hours, one full VM-key epoch).

Emergency difficulty adjustment triggers if block production stalls beyond EMERGENCY_DIFFICULTY_BLOCKS * EMERGENCY_TIME_MULTIPLIER * TARGET_BLOCK_TIME — this is the anti-wedge rule that prevents a 95% hashrate drop from leaving the chain stuck producing one block per day until the next LWMA window completes.

Implementation: src/consensus/difficulty.rs::calculate_difficulty. Called on every block accepted into the chain.

Header structure

#![allow(unused)]
fn main() {
pub struct BlockHeader {
    pub network_magic:     [u8; 4],   // testnet/mainnet/regtest discriminator
    pub version:           u8,        // protocol version
    pub height:            u64,
    pub timestamp:         u64,       // unix seconds
    pub prev_hash:         Hash,      // parent block
    pub tx_root:           Hash,      // merkle root of transactions
    pub anchor:            Hash,      // pow input anchor
    pub algorithm:         u8,        // always 0 (RandomX) — Article V
    pub nonce:             u64,
    pub target:            Hash,      // difficulty target this block met
    pub miner_pubkey:      PublicKey, // stealth address that gets the reward
    pub supply_commitment: [u8; 32],  // running supply commitment (Article IV)
    pub checkpoint_vote:   Option<(u64, Hash)>,
    pub spark_set_root:    [u8; 32],  // Phase 2 — Lelantus Spark accumulator root
    pub mw_kernel_root:    [u8; 32],  // Phase 2 — MimbleWimble kernel set root
}
}

Every field is included in the header hash preimage. BlockHeader::hash() prefixes the preimage with the domain-separation tag coincync/header/v1 so the header hash can never collide with the transaction signing hash, the PoW input, or any other hash in the protocol. See Privacy model → Hash domain separation.

The algorithm field is always 0 (RandomX). It is kept in the header for format stability — if a future constitutional amendment ever justified adding a second algorithm, the header schema would not need to change. Validation rejects any non-zero value.

The Phase 2 roots (spark_set_root, mw_kernel_root) are hashed unconditionally — zero bytes pre-activation, real accumulator roots post-activation. This means the upgrade is a soft fork rather than a hard fork: pre-activation blocks already commit to the (zero) roots, so there’s no header schema change at activation time, just a new validation rule that starts checking the field is non-zero and well-formed.

Current live status: public testnet is still pre-activation, so both roots are expected to remain zero in valid blocks.

The supply_commitment field is what lets Article IV (Auditable Integrity) work. Each block commits to the running Pedersen-accumulator supply, so any node can verify the total in-circulation CYNC matches the emission schedule without seeing individual transaction amounts.

Validation rules

src/consensus/validation.rs::validate_block applies these checks in order:

  1. Network magic — first check, before any crypto. Rejects testnet blocks reaching a mainnet node and vice versa. The expected magic is selected at compile time via the testnet / mainnet Cargo feature.
  2. Header sanity — version, height monotonic, prev_hash matches parent, timestamp monotonic, timestamp not far in the future (with MAX_TIMESTAMP_DRIFT = 600 seconds, except for height 0 — see below).
  3. Algorithm slotheader.algorithm == 0. Any other value is an instant reject — Article V permits RandomX only.
  4. PoW — RandomX hash must meet header.target. Genesis (height 0) is exempt because the genesis block is a hardcoded constant, not mined.
  5. Difficulty targetheader.target matches what LWMA computes for this height.
  6. Mandatory privacyenforce_privacy_policy checks every non-coinbase tx has Pedersen-committed amounts, a stealth address, and at least one privacy-preserving input. Structural fast-fail before the expensive crypto verification at stage 8.
  7. Coinbase — first transaction must be a Coinbase type, must have zero inputs, must pay the correct mountain-curve reward to header.miner_pubkey.
  8. Per-transaction validation — every non-coinbase tx is run through validate_transaction, which calls into the same crypto verifiers the mempool uses (verify_ring_signature, verify_output_range_proofs, verify_balance_proof).
  9. No double-spend — every key image in the block must be unique within the block AND not already spent in chain history.
  10. Supply commitmentheader.supply_commitment matches the running Pedersen accumulator after applying this block’s coinbase + fee-burn deltas. This is what enforces Article I (Fixed Supply) — a block that claims to mint more than the emission schedule allows fails the accumulator check.
  11. Block size cap — total serialized size ≤ MAX_BLOCK_SIZE (2 MB).

Failing any check returns BlockValidation { valid: false, errors: [...] } and the block is rejected.

Genesis timestamp exemption

Mainnet genesis has a hardcoded timestamp of 1790812800 (October 1, 2026 00:00:00 UTC). If you run a mainnet node before that date, the genesis block’s timestamp is in the future by wall-clock time. Without an exemption, validate_header would reject the genesis block as “too far in future.”

The fix in validate_header:

#![allow(unused)]
fn main() {
if header.height > 0 && header.timestamp > current_time + MAX_TIMESTAMP_DRIFT {
    result.add_error("Block timestamp too far in future");
}
}

The header.height > 0 guard exempts genesis (which is hardcoded and trusted by hash-match in init_genesis, not by timestamp validation). Non-genesis blocks are still bounded by MAX_TIMESTAMP_DRIFT.

Finality

CoinCync uses probabilistic finality, consistent with Constitution Article V’s commitment to pure PoW (no PoS finality gadget). Rough exchange-grade confirmation depths with 120-second blocks:

  • 1 confirmation (~2 min): tip block
  • 6 confirmations (~12 min): standard “safe” depth for low-value txs
  • 12 confirmations (~24 min): standard “deep” depth for high-value txs
  • 180 confirmations (~6 h): considered finalized for exchange-grade workflows

There’s no Casper-style finality gadget. There’s no checkpoint enforcement except at the consensus level for hard-coded historic checkpoints (which exist primarily to defend against long-range attacks during initial sync).

Reorgs

The chain follows the heaviest chain rule: the chain with the most accumulated work wins. If the node sees a competing fork that has more total work than its current tip, it reorganizes:

  1. Roll back blocks on the old tip until the fork point
  2. Apply blocks from the new chain
  3. Move orphaned txs back to the mempool
  4. Fire ChainEventType::Reorg for the explorer’s convergence timeline

Implementation: src/chain.rs. The reorg path is the source of most chain-storage complexity.

What’s not in the consensus layer

Things that exist in other privacy coins but are deliberately out of scope for CoinCync:

  • No PoS, no hybrid PoS/PoW, no finality gadget. Article V permanently prohibits any transition to Proof of Stake. Stake-weighted consensus reintroduces the “already-wealthy get richer” problem the chain was built to reject.
  • No multi-algorithm rotation. Single-algorithm PoW (RandomX). See the “Why RandomX-only” section above.
  • No GPU-friendly fallback. RandomX is CPU-optimized by design. GPUs can run it but are uneconomic compared to CPUs at typical power costs.
  • No merged mining with other coins. Each block must commit to exactly one chain. Merged mining adds attack surface for marginal hashrate gain.
  • No checkpoint server. The consensus rules are self-sufficient; new nodes verify from genesis.
  • No instant finality. Probabilistic PoW finality model, same as Bitcoin and Monero.

Next reading

Emission curve

The amount of CYNC paid out per block as the coinbase reward, as a function of block height. This is the only way new coins enter circulation — there is no premine, no founder allocation, no ICO, no airdrop, no dev tax. These properties are locked by Article I (Fixed Supply) and Article II (No Pre-mine, No Developer Tax) of the Constitution.

Parameters

ParameterValueSource
Target block time120 seconds (2 minutes)TARGET_BLOCK_TIME in src/constants.rs
Blocks per year262,800BLOCKS_PER_YEAR = 365 * 24 * 60 * 60 / 120
Base unit1 CYNC = 10¹² atomic unitsCOIN in src/constants.rs
Hard supply cap250,000,000 CYNCTOTAL_SUPPLY_TARGET + MAX_SUPPLY
Tail emission1.0 CYNC / block (perpetual)TAIL_EMISSION = 1_000_000_000 atomic
Tail starts atYear 20 (~height 5,256,000)EMISSION_ANCHORS in src/constants.rs
Curve typeMountain curve (anchor-point interpolation)src/emission/curve.rs::base_reward

The mountain curve

Unlike Bitcoin’s step-function halving, CoinCync uses a mountain curve — five anchor points with linear interpolation between them. This produces a smooth, monotonically-decreasing reward from the launch phase all the way into the tail.

YearBlock rewardCumulative supply (approx)
0 (launch)143 CYNC / block0
1143 CYNC / block (flat)~37.6 M
586 CYNC / block~157 M
1028 CYNC / block~232 M
137 CYNC / block~246 M
15~3 CYNC / block~249.5 M (cap approached)
20+1 CYNC / block (tail)~250 M

After year 20 the reward floors at the tail emission of 1 CYNC / block and stays there forever. The 250M cap is reached asymptotically; no issuance beyond it is possible because the consensus code enforces the cap directly.

Reward
   ↑
143 ●●●● (years 0-1, flat launch)
   │    ●●●
   │       ●●●●
 86│           ● (year 5)
   │            ●●●
   │               ●●●
 28│                  ● (year 10)
   │                   ●●
  7│                     ● (year 13)
  1├──────────────────────────●●●●●●●●●●●●●●●●●→ (year 20+ tail)
   └─────────────────────────────────────────────→
   0                                            height

Why a mountain curve instead of halvings

Halvings create predictable supply shocks — every four years, Bitcoin’s block reward drops by 50% in a single block. That causes forced-seller events at hash-power equilibria and strong psychological effects on price discovery. A mountain curve distributes the same total issuance smoothly, removing the cliff edges and giving miners continuous rather than discrete incentive changes.

The anchor points (not the interpolation formula) are what the constitution fixes. They’re stored as a compile-time constant in src/constants.rs:

#![allow(unused)]
fn main() {
pub const EMISSION_ANCHORS: &[(u64, u64)] = &[
    (0,                          143 * COIN),
    (BLOCKS_PER_YEAR,            143 * COIN),
    (BLOCKS_PER_YEAR *  5,        86 * COIN),
    (BLOCKS_PER_YEAR * 10,        28 * COIN),
    (BLOCKS_PER_YEAR * 13,         7 * COIN),
    (BLOCKS_PER_YEAR * 20,  TAIL_EMISSION),
];
}

Changing any of these numbers is a hard fork and a constitutional amendment — not permitted under any circumstances per Article X.

Why a tail emission instead of zero

This is one of the load-bearing design decisions.

A blockchain’s security budget is the value of the block reward that miners earn. As Bitcoin’s reward halves every four years and approaches zero, security is supposed to come from transaction fees alone. But fee markets are notoriously volatile, and a chain whose security depends on a healthy fee market is a chain whose security is hostage to its short-term throughput.

A fixed tail emission removes that dependency. After block year 20, there is always a constant 1 CYNC / block flowing to miners regardless of fee market conditions. This:

  • Guarantees a baseline security budget forever
  • Makes the long-term inflation rate decreasing but non-zero (asymptotically approaches 0% as the supply grows, but never reaches it)
  • Removes the moral hazard of a “fee market crisis” decades from now where the network has to choose between underpaying miners and mining empty blocks

Monero pioneered this design. Pirate Chain followed suit. Bitcoin notably did not, and has been having the “what happens after the last halving” debate for over a decade.

Supply cap interaction with the tail

At 1 CYNC / block tail × 262,800 blocks/year = 262,800 CYNC / year of tail issuance. The mountain curve’s 19 years of pre-tail emission puts cumulative supply at roughly ~248–249 million CYNC by year 20, leaving about 1 M CYNC of headroom before the 250 M cap.

That headroom is consumed by tail emission over the decades after year 20 until the cap is reached. The exact year varies depending on the pre-tail integration, but the order of magnitude is “hundreds of years of tail emission”. Once the cap is hit, the reward is clamped to zero and miners are compensated by fees only — the same endgame Bitcoin faces, but pushed far enough into the future that the question is about generations from now, not decades from now.

Article I of the Constitution locks the cap absolutely. Nothing — no governance vote, no supermajority, no emergency override — can raise it. The src/constants.rs compile-time assertion is the mechanical backstop:

#![allow(unused)]
fn main() {
const _: () = assert!(TOTAL_SUPPLY_TARGET == 250_000_000,
    "UNCONSTITUTIONAL: Article I — Supply cap must be exactly 250,000,000 CYNC");
}

Coinbase outputs

The coinbase transaction in every block is a single output paying base_reward(height) + collected_fees - burned_fees to the miner’s stealth address. Even the coinbase uses the privacy machinery — the miner’s address is a one-time stealth output, not a reusable transparent address. The view key for the miner’s wallet recognizes the output; nobody else can.

The miner’s address is published in the block header for the explorer to display, but this is a one-time stealth output derived from the miner’s static pubkey plus a per-block transaction secret. It is not a re-identifiable wallet address — every miner can only show the world “I mined this block” if they choose to disclose, never “I am the same miner who mined block N − 5” unless they voluntarily link them off-chain.

Fee burn

CoinCync has an activity-scaled fee burn that activates after year 1, independent of the base reward. A percentage of transaction fees is permanently removed from circulation, accelerating the approach to the supply cap during high-activity periods and dampening it during low-activity periods. The burn rate schedule is in src/constants.rs::BURN_RATE_ANCHORS, capped at 50% (MAX_BURN_RATE = 5000 basis points). The miner receives MINER_SPLIT_PERCENT = 60 of the non-burned fees.

Fee burn does not create coins, it only destroys them — it cannot violate Article I. The cap is a ceiling, and burn can only push the total further below that ceiling.

Verifying the curve

The block explorer at explorer.coincync.network renders the emission curve on its supply panel. The same curve is queryable via JSON-RPC:

curl -sX POST https://api.coincync.network/rpc \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_supply_info"}' | jq .

Returns something like:

{
  "height":         12345,
  "current_reward": 143000000000000,
  "total_emitted":  1765035000000000000,
  "emission_phase": "Launch",
  "max_supply":     250000000000000000000
}

Atomic units: 1 CYNC = 10¹² atomic. 143000000000000 atomic = 143 CYNC.

What can change about emission

Nothing, by design. The emission curve is locked by Constitution Article I and Article X (Immutability). Changing the initial reward, the anchor points, the tail, the block time, or the supply cap would require:

  • A hard fork (the resulting chain is incompatible with the old chain)
  • A deliberate constitutional violation (which is prohibited by Article X and mechanically blocked by the compile-time assertions)
  • Every operator on the network to deliberately opt in to a chain that is, by definition, no longer CoinCync

There is no operator key, no admin key, and no DAO that can modify the emission. The numbers are baked into src/constants.rs and src/emission/curve.rs and into every node binary that anyone has ever built. Changing them requires every operator to deliberately upgrade to a new binary and reach social consensus first — and any binary that tries to ship with altered numbers fails the constitutional assert!s at compile time.

Implementation references

  • src/constants.rs::TOTAL_SUPPLY_TARGET, MAX_SUPPLY, EMISSION_ANCHORS, TAIL_EMISSION, TARGET_BLOCK_TIME
  • src/emission/curve.rs::base_reward — the mountain-curve linear-interpolation function
  • src/emission/curve.rs::emission_phase — the Launch/Growth/Maturity/Decline/Tail phase classifier
  • src/emission/supply.rs::SupplyState — tracks cumulative supply and the cap
  • src/mining/template.rs — coinbase construction

Next reading

Transaction format

The on-the-wire and on-disk encoding of a CoinCync transaction. Defined by src/transaction/types.rs. The hash that gets signed (the “signing hash”) is computed by a single canonical helper that both the wallet’s signer and the consensus verifier funnel through — there is no second copy of the preimage logic.

Struct

#![allow(unused)]
fn main() {
pub struct Transaction {
    pub version:     u8,
    pub tx_type:     TxType,    // Coinbase | Transfer | Churn
    pub inputs:      Vec<TxInput>,
    pub outputs:     Vec<TxOutput>,
    pub fee:         Amount,
    pub range_proof: Vec<u8>,
    pub extra:       Vec<u8>,
}

pub struct TxInput {
    pub key_image:                 KeyImage,                // CLSAG: I = x · H_p(x·G)
    pub ring_members:              Vec<RingMemberRef>,      // decoys + the real input
    pub signature:                 ClsagSignature,
    pub pseudo_output_commitment:  [u8; 32],                // for the balance equation
}

pub struct TxOutput {
    pub stealth_address:  PublicKey,
    pub tx_public_key:    PublicKey,
    pub commitment:       [u8; 32],     // Pedersen commitment to the amount
    pub encrypted_amount: Vec<u8>,      // ECDH-encrypted, recipient-only
    pub view_tag:         u8,           // 1-byte filter — wallet rejects 255/256 outputs without doing ECDH
    pub lock_height:      Option<u64>,  // optional time lock
    pub encrypted_memo:   Vec<u8>,      // optional ECDH-encrypted memo (≤ 256 bytes)
}
}

Borsh is the canonical serialization format. JSON encoding (used in the RPC layer) is a non-canonical view that’s never hashed.

The signing hash

The CLSAG ring signature is computed over a hash of every transaction field except the ring signatures themselves. This is the “signing hash” — the message that CLSAG binds to.

A bug in the signing-hash construction is a sign/verify mismatch: the wallet produces signatures the verifier rejects (best case) or the wallet produces signatures that validate against a different transaction (worst case — malleability vulnerability).

CoinCync prevents this category of bug by routing both the signer and the verifier through the same helper:

#![allow(unused)]
fn main() {
impl Transaction {
    pub fn signing_hash(&self) -> Hash {
        Self::compute_signing_hash(
            self.version, self.tx_type, self.fee,
            self.inputs.iter().map(SigningInputView::from_txinput),
            &self.outputs, &self.range_proof, &self.extra,
        )
    }

    /// Single source of truth for the CLSAG signing message.
    pub fn compute_signing_hash<'a, I>(
        version: u8, tx_type: TxType, fee: Amount,
        inputs: I, outputs: &[TxOutput],
        range_proof: &[u8], extra: &[u8],
    ) -> Hash
    where I: IntoIterator<Item = SigningInputView<'a>>
    { /* ... */ }
}
}

SigningInputView is a light view over the fields of a TxInput except the signature, so the builder can compute the signing hash before any CLSAG signature exists. The tests/wallet_roundtrip.rs::signing_hash_is_single_source_of_truth integration test pins the two paths together — any future drift fails CI.

Preimage layout

Every byte that goes into the signing hash, in order:

domain tag:  "coincync/tx-sign/v1"          (19 bytes, ASCII, no length prefix)
version:     u8                             (1 byte)
tx_type:     u8                             (1 byte)
fee:         u64 little-endian              (8 bytes)
n_inputs:    u32 little-endian              (4 bytes)
for each input:
    key_image:                32 bytes
    pseudo_output_commitment: 32 bytes
    n_ring_members:           u32 LE
    for each ring member:
        public_key:           32 bytes
        commitment:           32 bytes
n_outputs:   u32 little-endian              (4 bytes)
for each output:
    stealth_address:          32 bytes
    tx_public_key:            32 bytes
    commitment:               32 bytes
    encrypted_amount.len():   u32 LE
    encrypted_amount:         (var)
    view_tag:                 u8
    lock_height tag:          u8 (0 = None, 1 = Some)
    if Some(h):
        h:                    u64 LE
    encrypted_memo.len():     u32 LE
    encrypted_memo:           (var)
range_proof.len():            u32 LE
range_proof:                  (var)
extra.len():                  u32 LE
extra:                        (var)

Then BLAKE3 over the whole concatenated buffer.

Why every variable-length field is length-prefixed

Without the length prefixes, an attacker could craft two transactions that serialize to the same bytes by reshuffling input/output counts and content — a classic prefix-collision vulnerability. The u32 length prefix on every variable-length field (and the explicit n_inputs / n_outputs counts before each loop) makes that impossible.

Why lock_height uses an explicit tag

lock_height: Option<u64> could be encoded as just the u64 with 0 meaning “no lock,” but then Some(0) and None would be indistinguishable. The explicit u8 tag (0 for None, 1 for Some) keeps them distinct.

Why the domain tag

coincync/tx-sign/v1 is the domain-separation tag. Every hash preimage in the protocol uses a different tag, so two hashes computed with different tags can never collide regardless of the underlying bytes. This eliminates a class of cross-protocol attacks where an adversary tries to make a CLSAG message also be a valid block header hash, or similar. See Privacy model → Hash domain separation.

The transaction hash

Transaction::hash() is different from signing_hash(). The transaction hash includes the signatures (it’s borsh-of-the-whole-transaction blake3’d) and is what gets used as the txid for mempool lookup, block inclusion, and explorer references. The signing hash is purely the CLSAG message.

What’s in extra

The extra field is a free-form byte buffer included in the signing hash. It’s typically empty for normal transactions. Coinbase transactions store the genesis message in here for the genesis block ("CoinCync Mainnet Genesis - Privacy You Can Audit - October 2026").

The protocol does not assign meaning to extra for non-coinbase transactions. Wallets that want to embed protocol-level annotations (multi-tx batching IDs, payment IDs, etc.) can do so here, but they should be aware that:

  • The bytes are visible in the on-chain transaction (they’re length-prefixed in the signing hash but not encrypted)
  • Anything in extra is potentially a deanonymization side-channel — a wallet that tags transactions with patterns identifies its users to a chain analyst
  • The recommended posture is “leave it empty unless you have a specific protocol need”

Wire size

Approximate transaction size as a function of ring size and output count:

Ring size1 input → 2 outputs1 input → 4 outputs
11 (current minimum)~2.1 KB~3.5 KB
16 (CIP-002 future)~2.6 KB~4.0 KB

Most of the bulk is the range proof (~700 bytes for 2 outputs with Bulletproofs+, ~1400 bytes for 4 outputs).

See also

  • src/transaction/types.rs — the canonical struct and the signing-hash helper
  • src/transaction/builder.rs — the wallet-side TransactionBuilder that funnels through compute_signing_hash
  • src/consensus/validation.rs::validate_transaction — the verifier-side check
  • Privacy model — the cryptographic primitives that protect each field

JSON-RPC 2.0

CoinCync’s primary programmatic interface. Spoken by the coincync-node daemon’s jsonrpsee server (default 127.0.0.1:28081 on testnet, 127.0.0.1:19081 on mainnet) and by the public reverse proxies in front of the production fleet.

This page covers the protocol envelope, transports, and the read-only / write split. The full method inventory with parameters and return values is in Method reference.

Endpoints

EndpointWhat it speaksWhen to use
http://127.0.0.1:28081 (local node)Raw JSON-RPC 2.0Local development, scripts running on the same host
https://api.coincync.network/rpcJSON-RPC 2.0 (testnet, default)Wallets, SDKs, tooling — anywhere on the public internet
https://api.coincync.network/rpc/mainnetJSON-RPC 2.0 (mainnet, post-launch)Same, but explicitly targeting mainnet
https://explorer.coincync.network/api/testnetJSON-RPC 2.0 (testnet)The explorer’s inline JS uses this; you can too
https://explorer.coincync.network/api/mainnetJSON-RPC 2.0 (mainnet, post-launch)Same, mainnet

The local-node endpoint exposes the full RPC surface (including write methods like submit_block and send_raw_transaction). The public proxies enforce a read-only allowlist (see REST endpoints and src/rpc/rest.rs::RPC_ALLOWED_METHODS).

Protocol

Standard JSON-RPC 2.0 over HTTP POST:

curl -X POST http://127.0.0.1:28081 \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_info","params":[]}'

Successful response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "version":      "1.0.0",
    "network":      "testnet",
    "height":       12345,
    "top_hash":     "26ec6abd09fd83b0...",
    "tip_hash":     "26ec6abd09fd83b0...",
    "tip_age_secs": 17,
    "synced":       true,
    "peer_count":   8,
    "tx_pool_size": 3,
    "anonymity_set": 41782,
    "effective_ring_size": 11,
    "status":       "healthy",
    "health_score": 1.0
    /* ... */
  }
}

Error response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code":    -32601,
    "message": "Method not found"
  }
}

Error codes

CodeMeaning
-32600Invalid request (malformed envelope)
-32601Method not found
-32602Invalid params (bad type, out-of-range integer, etc.)
-32603Internal error
-32000Resource not found (e.g. block at unknown height)
-32001Block rejected by validator (during submit_block)
-32002Transaction rejected by mempool (during send_raw_transaction)

Read-only vs write methods

The public REST proxy in front of the API server enforces an allowlist that blocks every write method. Wallets cannot submit transactions through https://api.coincync.network/rpc — they must either run their own local node and use http://127.0.0.1:28081, or use a separate authenticated submit endpoint (currently not exposed in the P0 deployment).

This is a deliberate security tradeoff:

  • Reads are public, lots of clients, lots of inspection — the explorer, wallet sync, third-party blockchains analyzers, monitoring dashboards. The allowlist is exhaustive on the read side.
  • Writes are sensitive and per-user — submitting a transaction to a stranger’s node trusts that node to relay rather than censor. Most wallets should submit to a local node they control. The public submit surface is reserved for a future P1 wallet-RPC stack with proper authentication.

See REST endpoints → allowlist for the full list.

Rate limiting

The public proxies enforce per-source-IP rate limits. The exact thresholds are documented in deploy/api/Caddyfile and deploy/explorer/Caddyfile:

  • api.coincync.network: 30 requests/min/IP
  • explorer.coincync.network: 60 requests/min/IP

A client that hits the limit gets 429 Too Many Requests. Back off and retry — the limit is per-minute, not per-second, so spreading 30 calls evenly across 60 seconds always works. The primary purpose of these limits is not botnet defense — it’s making industrial-rate scraping by chain-forensics firms expensive. See Federation & DDoS.

Authentication

Public hosted read-only endpoints may be unauthenticated by deployment policy, but self-hosted nodes can require bearer auth by setting COINCYNC_RPC_API_KEY.

When bearer auth is enabled:

  • JSON-RPC requests must include Authorization: Bearer <token>.
  • WebSocket upgrade paths must also include the same bearer token.
  • Non-upgrade GET requests are rejected when auth is enabled.

Even with unauthenticated public read endpoints, the allowlist and rate limits remain the baseline control plane for abuse resistance.

Choosing a transport

You want to…Use this endpoint
Build a wallet that submits transactionsA local node at http://127.0.0.1:28081 (run your own)
Build a read-only block explorerhttps://api.coincync.network/rpc — public, rate-limited, free
Build a chain analyzer / indexerA local node — the public proxies will rate-limit you and the bandwidth bill on your end is significant anyway
Embed live chain data in a web pageThe explorer’s inline JS already does this from https://explorer.coincync.network/api/testnet
Run a faucetA local node next to the faucet binary, with the wallet RPC speaking to localhost

See also

  • REST endpoints — the higher-level REST surface that wraps the JSON-RPC for browser-friendly use
  • Method reference — every method, every parameter, every field

REST endpoints

The REST API is a higher-level wrapper around the JSON-RPC surface, served by src/rpc/rest.rs (an axum app). It’s the right transport for browser-side code that wants typed JSON without managing JSON-RPC envelopes.

Where it lives

URLWhat it is
https://api.coincync.network/v1/...Public REST, testnet (default)
https://api.coincync.network/v1/mainnet/...Public REST, mainnet (post-launch)
http://127.0.0.1:28083/api/v1/...Local REST when the node is started with --rest-bind

The REST surface is only started when the operator passes --rest-bind <addr> or --explorer to coincync-node. It’s opt-in. The bare coincync-node only runs the JSON-RPC jsonrpsee server.

Endpoint inventory

GET  /api/v1/health
GET  /api/v1/status
GET  /api/v1/supply

GET  /api/v1/blocks/recent
GET  /api/v1/block/hash/{hash}
GET  /api/v1/block/height/{height}
GET  /api/v1/block/{height}/transactions

GET  /api/v1/transaction/{hash}
POST /api/v1/transaction/submit         (allowlisted out of public proxy)

GET  /api/v1/mempool
GET  /api/v1/mempool/stats

GET  /api/v1/search?q=...
GET  /api/v1/network
GET  /api/v1/anonymity
GET  /api/v1/peers
GET  /api/v1/stats
GET  /api/v1/emission
GET  /api/v1/events
GET  /api/v1/asset/{id}                 (NotImplemented in 1.0)
GET  /api/v1/assets                     (NotImplemented in 1.0)

GET  /api/v1/ws                         (WebSocket upgrade for live events)

POST /rpc                               (raw JSON-RPC passthrough, allowlisted)

CORS

The public REST server allowlists three origins for browser fetch:

  • https://explorer.coincync.network
  • http://localhost:3000 (dev convenience)
  • http://127.0.0.1:3000 (dev convenience)

Plus any origins set via the COINCYNC_CORS_ORIGINS environment variable on the REST host.

This is deliberately restrictiveCorsLayer::permissive() would let every website on the internet make authenticated browser requests to the API on behalf of the visitor, which is a CSRF-like attack surface even on read-only endpoints (because of cookies, etag-tracking, etc.). The allowlist limits CORS-enabled access to origins under operator control.

Allowlist

The POST /rpc proxy enforces a read-only allowlist over the underlying jsonrpsee surface. The exhaustive list lives in src/rpc/rest.rs::RPC_ALLOWED_METHODS:

Allowed (read-only):

  • get_info, get_blockchain_info, get_network_info, get_sync_status
  • get_anonymity_set, get_chain_events, get_supply_info
  • get_privacy_stats, get_shielded_anchor, get_spark_anchor
  • get_block_by_height, get_block, get_block_range
  • get_peers, get_mempool_info
  • is_nullifier_spent, is_spark_serial_spent, get_decoys
  • get_transaction, get_asset_info (both currently NotImplemented stubs)
  • Forward-compat reservations: health, rpc.discover, get_block_count, etc.

Blocked (write or sensitive):

  • submit_block, send_raw_transaction — write methods, blocked
  • get_mining_live — exposes hashrate / hardware fingerprint, blocked
  • Any wallet-side methods (get_balance, unlock_wallet, etc.) — blocked

Verifying the allowlist is enforced is one of the regression tests: src/rpc/rest.rs::tests::test_rpc_allowlist_blocks_sensitive.

Sample requests

# Health check (always 200 if the node is up)
curl -s https://api.coincync.network/v1/health

# Chain status — height, tip, sync flag, peer count
curl -s https://api.coincync.network/v1/status | jq .

# Latest 10 blocks (paginated)
curl -s 'https://api.coincync.network/v1/blocks/recent?limit=10' | jq .

# A specific block by height
curl -s https://api.coincync.network/v1/block/height/12345 | jq .

# Search bar (block hash, txid, or height)
curl -s 'https://api.coincync.network/v1/search?q=12345' | jq .

# Anonymity set size — privacy coin's most important metric
curl -s https://api.coincync.network/v1/anonymity | jq .

# Recent chain events (reorgs, fork detections, rejects)
curl -s https://api.coincync.network/v1/events | jq .

# Connected peer table
curl -s https://api.coincync.network/v1/peers | jq .

# Live event WebSocket (if your host exposes /v1/ws)
const ws = new WebSocket('wss://api.coincync.network/v1/ws');
ws.onmessage = (e) => console.log('event:', JSON.parse(e.data));

/api/v1/ws availability depends on deployment and proxy configuration. For clients that need maximum compatibility across hosts, prefer polling REST endpoints and treat WebSocket support as optional.

Response shapes

Every endpoint returns JSON. Empty responses are {} not null. Errors are HTTP status codes plus a JSON body of the form {"error": "..."}.

The exact shape of each response is documented in src/rpc/types.rs (the typed structs that get serialized) and in Method reference (the human-readable field-by-field descriptions).

Why both REST and JSON-RPC?

REST is more browser-friendly:

  • One URL per concept — bookmarkable, cacheable, indexable
  • Standard HTTP status codes (404 for “block not found” instead of { "error": { "code": -32000 } })
  • GET requests instead of POST for read methods (CDN-cacheable in the rare case CDN ever gets used)
  • WebSocket support for live events without per-event polling

JSON-RPC is more backend-friendly:

  • One URL for everything — easy to set up RPC clients in any language
  • Symmetric request/response envelope
  • Method introspection (rpc.discover — not yet wired)

The two share the same backend state (chain, mempool, P2PNode), so a wallet can use whichever transport it prefers without consistency surprises.

See also

Method reference

Every JSON-RPC method registered by src/rpc/server.rs, with parameters, return shape, and the access level (public via REST allowlist vs local-only).

Conventions

  • Atomic units: all amounts are integer atomic units. 1 CYNC = 10^12 atomic units. So 49500000000000 atomic = 49.5 CYNC.
  • Hashes: 64-char lowercase hex strings (no 0x prefix).
  • Heights: u64 starting at 0 (genesis).
  • Timestamps: u64 unix-epoch seconds.
  • Public: callable via https://api.coincync.network/rpc and https://explorer.coincync.network/api/....
  • Local: callable only on the local node’s jsonrpsee endpoint (http://127.0.0.1:28081).

Node info

get_info (public)

Returns the node’s current status — the canonical “health” payload consumed by both TUIs and the embedded explorer.

Params: none.

Returns:

{
  "version":                 "1.0.0",
  "network":                 "testnet",
  "height":                  12345,
  "target_height":           12345,
  "top_hash":                "26ec6abd...",
  "tip_hash":                "26ec6abd...",
  "tip_timestamp":           1772900000,
  "tip_age_secs":            17,
  "clock_available":         true,
  "difficulty":              "9876543",
  "total_difficulty":        "1234567890",
  "synced":                  true,
  "is_synced":               true,
  "peer_count":              8,
  "tx_pool_size":            3,
  "mempool_size":            3,
  "anonymity_set":           41782,
  "available_outputs":       41782,
  "effective_ring_size":     11,
  "status":                  "healthy",
  "health_score":            1.0,
  "process_count":           1,
  "process_count_available": false,
  "has_zombies":             false,
  "rpc_auth_enabled":        true,
  "metadata_minimized":      true,
  "stratum_public_bind_requested": true,
  "stratum_public_bind_ack": true,
  "stratum_native_tls_enabled": true,
  "stratum_tls_proxy_ack": false,
  "stratum_transport_hardened": true
}

tip_age_secs is null if the system clock is unreadable (in which case clock_available is false). Consumers should treat null as “unknown” — never as 0. See src/rpc/server.rs for the rationale.

Hardening posture fields in get_info / get_blockchain_info:

  • rpc_auth_enabled: runtime RPC auth mode.
  • metadata_minimized: peer metadata redaction posture.
  • stratum_public_bind_requested: Stratum requested to bind publicly.
  • stratum_public_bind_ack: explicit public-bind acknowledgement is present.
  • stratum_native_tls_enabled: native Stratum TLS transport is enabled.
  • stratum_tls_proxy_ack: operator acknowledged trusted upstream TLS termination.
  • stratum_transport_hardened: computed safety flag (true when Stratum is not public, or public with password + ack + encrypted transport).

get_blockchain_info (public)

Same idea as get_info but with extra accounting fields (total_supply, total_difficulty).

get_network_info (public)

Peer breakdown. connections is the total live peer count. incoming, outgoing, white_peers, grey_peers are null in the P0 release because the thin network_stats() exposes only the total; the per-direction split lands in P1.

{
  "network": "testnet",
  "version": "1.0.0",
  "protocol_version": 1,
  "connections": 8,
  "incoming": null,
  "outgoing": null,
  "white_peers": null,
  "grey_peers": null
}

get_sync_status (public)

{
  "synced":        true,
  "height":        12345,
  "target_height": 12345,
  "progress":      1.0,
  "peers":         8
}

get_anonymity_set (public)

The most important privacy-coin metric: total unspent outputs (potential decoys) plus average outputs per block.

{
  "anonymity_set":     41782,
  "height":            12345,
  "outputs_per_block": 3
}

get_chain_events (public)

Recent reorgs, fork detections, rejects, and checkpoints. Server-capped to 500 entries; the default limit is 100.

Params: [limit: usize] (optional).

Returns: { events: [...], count: usize, current_height: u64, current_tip: hex }. Each event has event_type, height, hash, timestamp, and an event-specific details object.

Blocks

get_block_by_height (public)

Params: [height: u64].

Returns: the rich block payload (see “Rich block payload” below).

get_block (public)

Params: [hash: 64-char hex].

Returns: the rich block payload (same shape as get_block_by_height).

get_block_range (public)

Params: [start: u64, end: u64]. Server caps the range to 100 blocks per call.

Returns: { start, end, count, blocks: [...] } where each block uses the same rich payload.

Rich block payload

{
  "height":         12345,
  "hash":           "abc123...",
  "prev_hash":      "def456...",
  "tx_root":        "789...",
  "timestamp":      1772900000,
  "nonce":          42,
  "algorithm":      0,
  "algorithm_name": "RandomX",
  "difficulty":     "9876543",
  "target":         "ffffff...",
  "tx_count":       3,
  "size":           4096,
  "reward":         48500000000000,
  "transactions": [
    {
      "hash":    "...",
      "kind":    "coinbase",
      "inputs":  0,
      "outputs": 1,
      "fee":     0
    }
    /* ... */
  ],
  "bytes": "<hex of borsh-serialized block>"
}

The transactions array carries lightweight per-tx records (hash, kind, inputs, outputs, fee). For full transaction bodies you currently need to deserialize the bytes field — the txid → block index that would let get_transaction work as a standalone lookup is a P1 deferred item.

get_block_count (forward-compat reservation, not yet registered)

Reserved in the REST allowlist for future implementation. Currently returns MethodNotFound.

Transactions

get_transaction (public, NotImplemented stub)

Currently returns -32601 Method not found with a labelled message:

get_transaction is not yet wired in P0: a txid → (block, index) lookup index lands in P1 alongside the wallet RPC. See chain::Blockchain — there is no tx index today, so this method cannot be satisfied without a full chain scan.

This is intentional. The REST allowlist still includes the method so the explorer’s search bar gets a labelled error rather than a generic 403, but the implementation is honest about not being wired yet.

submit_block (local only)

Params: [block_hex: string]. Borsh-serialized block, hex-encoded.

Returns: { "accepted": true, "hash": "..." } or { "error": "..." }.

Used by the standalone coincync-miner to submit found blocks. Blocked from the public REST proxy.

send_raw_transaction (local only)

Params: [tx_hex: string]. Borsh-serialized transaction, hex-encoded.

Returns: { "accepted": true, "hash": "..." } or { "error": "..." }.

The mempool runs the same crypto verifiers consensus does — no fast path. Blocked from the public REST proxy.

Mempool

get_mempool_info (public)

{
  "size":       3,
  "bytes":      8421,
  "total_fees": 1500000,
  "max_size":   100000
}

Mining

get_mining_live (local only — fingerprint-leak risk)

Returns the running miner state. Deliberately blocked from the public REST proxy because it would let observers fingerprint the miner’s hashrate / hardware. Available only on the local jsonrpsee endpoint.

{
  "is_mining":        false,
  "hashrate":         0.0,
  "hashes_total":     0,
  "blocks_found":     0,
  "algorithm":        0,
  "algorithm_name":   "RandomX",
  "mining_height":    12346,
  "target_hex":       "",
  "best_hash_hex":    "",
  /* ... */
}

A node that isn’t mining (just running as a relay) honestly returns is_mining: false with zeroed fields.

Privacy stores

get_privacy_stats (public)

Aggregate Phase 2 store snapshot. Pre-activation, all roots are zero and all sizes are 0 — it’s the same payload, just a baseline. Public testnet is currently in this pre-activation mode.

{
  "shielded_root":            "00000000...",
  "shielded_tree_size":       0,
  "spark_root":               "00000000...",
  "spark_accumulator_size":   0,
  "mw_kernel_root":           "00000000...",
  "mw_kernels_kept":          0,
  "mw_pending_candidates":    0,
  "mw_bytes_saved":           0,
  "mw_compression":           0.0,
  "mandatory_confidential":   true,
  "mandatory_stealth":        true
}

get_shielded_anchor, get_spark_anchor (public)

Returns the current Merkle root that a light wallet should anchor its spend proofs against. Pre-activation, returns the zero anchor.

is_nullifier_spent, is_spark_serial_spent (public)

Params: [nullifier_hex: 64-char hex].

Returns: { "spent": true|false, "height": u64|null }.

Required for recipient-side “is this coin still spendable” checks in light wallets. Public because nullifier sets are public chain state by design.

get_decoys (public)

Selects decoy outputs for ring-signature construction. Params: [count: u32, max_height: u64]. Returns a list of (tx_hash, output_index, public_key, commitment) tuples sampled by the age-weighted decoy selector.

Asset queries (permanently NotImplemented)

get_asset_info (public, NotImplemented stub)

Returns -32601 Method not found with the labelled message:

get_asset_info is not implemented: CoinCync 1.0 has no confidential-asset layer (the asset stack was removed in the 2.0 → 1.0 trim). Single-asset CYNC only.

This is permanent, not deferred. CoinCync 1.0 stripped the confidential-asset machinery. Single-asset CYNC only. The endpoint is allowlisted in REST so the explorer’s search bar gets a labelled error rather than a 403 when someone types something that isn’t a block hash, txid, or height.

Chain Verification Methods (added v1.0.0-testnet)

These support scripts/coincync-verify-chain.sh — a 5-level chain validation tool.

get_expected_reward

Returns the expected block reward at a given height per the emission curve.

Params[height: u64]
Returns{ reward, height, in_cync }
AccessPublic

verify_keyimage_uniqueness

Scans the entire chain for duplicate key images (global double-spend check).

Params[] (no params)
Returns{ valid, duplicates, duplicate_images, total_checked }
AccessPublic

check_zero_commitments_in_range

Detects zero commitments and zero stealth addresses (burning bug — H-19).

Params[start_height: u64, end_height: u64]
Returns{ zero_count, locations }
AccessPublic

verify_signatures_in_range

Re-verifies every CLSAG ring signature in a block range.

Params[start_height: u64, end_height: u64]
Returns{ valid, checked, failures, findings }
AccessPublic

verify_range_proofs_in_range

Re-verifies every Bulletproofs+ range proof in a block range.

Params[start_height: u64, end_height: u64]
Returns{ valid, checked, failures, findings }
AccessPublic

verify_commitment_balance_in_range

Verifies Pedersen commitment balance for every transaction (no money printing).

Params[start_height: u64, end_height: u64]
Returns{ valid, checked, failures, findings }
AccessPublic

full_chain_audit

Runs all verification checks in one call. Merkle roots, rewards, structural validation, CLSAG, range proofs, and balance — everything.

Params[start_height: u64, end_height: u64]
Returns{ valid, blocks_checked, txs_checked, findings, details }
AccessPublic

See also

  • JSON-RPC 2.0 — the protocol envelope
  • REST endpoints — the higher-level wrapper
  • src/rpc/server.rs — the canonical method registrations (this page is generated from reading that file by hand; if it diverges, the source is correct)

Deploying a node

The runbook for putting a CoinCync node into production. Three deployment patterns: systemd (the canonical Linux way), docker compose (the easier-to-reproduce way), and manual (for development). Pick whichever matches your operational style.

Pre-deployment checklist

Before deploying any node to a public host, confirm:

  • Hardware: 2 vCPUs, 4 GB RAM, 80 GB SSD. The chain grows by ~50 MB/day on testnet — provision accordingly.
  • OS: Ubuntu 22.04 / Debian 12 / equivalent. CoinCync builds on macOS and Windows but the production deployment story is Linux.
  • Cloud firewall (DigitalOcean / AWS / Hetzner / etc.): allow only the ports you intend to expose. Default deny everything else.
    • P2P port (28080 testnet / 19080 mainnet) — must be public for the node to accept inbound peer connections
    • RPC port (28081 / 19081) — never public; bind to 127.0.0.1
    • REST port (28083 / 19083, optional) — never public
    • Metrics port (9091, optional) — never public; if you want metrics scraped from another host, use SSH tunneling or WireGuard
    • nginx (80 + 443) — only on hosts that run public web ingress; see Block explorer, Public RPC API, or the landing/docs deployment.
  • DNS pointing at the host (if you want a public hostname).
  • Time synchronization: chronyd or systemd-timesyncd running. PoW timestamp validation has a 10-minute drift window; a clock that’s off by more than that will reject incoming blocks.

Pattern A: systemd

The canonical production deployment for a single-node host.

1. Build the binaries

On your build host (could be the deployment target itself, or a dev machine):

git clone https://github.com/CoinCync/Coincync-Testnet-.git /opt/coincync
cd /opt/coincync
cargo build --release --features "randomx testnet"

For a mainnet host, swap testnet for mainnet. (Or build with --features "randomx testnet mainnet" and select at runtime via --network.)

2. Install the binaries

sudo install -m 0755 target/release/coincync-node /usr/local/bin/coincync-node
sudo install -m 0755 target/release/coincync-wallet /usr/local/bin/coincync-wallet
# coincync-miner only on hosts that will mine

3. Create the service user and data directory

sudo useradd -r -s /bin/false coincync
sudo mkdir -p /var/lib/coincync
sudo chown coincync:coincync /var/lib/coincync
sudo chmod 0700 /var/lib/coincync

4. Install the systemd unit

The unit file lives at /deploy/coincync-node.service:

sudo install -m 0644 deploy/coincync-node.service /etc/systemd/system/coincync-node.service
sudo systemctl daemon-reload
sudo systemctl enable --now coincync-node
sudo systemctl status coincync-node

The unit:

  • Runs as the coincync system user (no shell, no home directory)
  • Writes to /var/lib/coincync only — ProtectSystem=strict, ReadWritePaths=/var/lib/coincync
  • Drops capabilities: NoNewPrivileges, PrivateTmp, ProtectHome
  • Restarts on failure with a 10-second backoff
  • Logs to journald with the coincync-node syslog identifier

5. Verify

journalctl -u coincync-node -f
# Watch for: "Node is running. Ctrl-C to stop." (the systemd version doesn't actually want Ctrl-C, just confirms the startup completed)

curl -sX POST http://127.0.0.1:28081 \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_info"}' | jq .

If get_info returns the chain tip, the node is live.

Pattern B: docker compose

Easier to reproduce, easier to roll back, easier to run multiple variants on the same host. The docker compose file lives at /deploy/docker-compose.yml.

git clone https://github.com/CoinCync/Coincync-Testnet-.git /opt/coincync
cd /opt/coincync
docker compose pull
docker compose up -d
docker compose logs -f node

This compose file builds the node from source on first run and caches the image. Updates are git pull && docker compose up -d --build.

Profiles

The compose file ships several optional profiles for hosts that play specific roles:

ProfileAddsRun on
(default)just the relay nodeevery node
miningthe coincync-miner daemonhosts that mine (one is enough; running on every host is wasteful)
faucetthe testnet faucet HTTP serviceone host (the faucet host)
monitoringPrometheus + Grafanaone host (the monitoring host)
explorera separate explorer service via /deploy/explorer/the explorer host

Activate a profile with docker compose --profile mining up -d.

Pattern C: manual (development)

cd /path/to/Coincync-Testnet-
./target/release/coincync-node --network testnet --data-dir ~/.coincync

For development only. Doesn’t survive reboot, doesn’t restart on crash, doesn’t run as an unprivileged user. See getting-started/run-a-node.md for the developer workflow.

RPC binding rules

The single most important security rule:

The RPC port (28081 / 19081) MUST NOT be bound to a public interface in production.

Public deployments bind RPC to 127.0.0.1 only. The nginx configs in deploy/explorer/, deploy/api/, and deploy/landing/ are the only public-facing HTTP services. nginx reverse-proxies allowlisted requests to the localhost RPC.

If you bind RPC publicly, you’ve defeated the purpose of every ingress security control:

  • The REST allowlist that blocks write methods → bypassable by going straight to RPC
  • The rate limit → bypassable
  • TLS termination → bypassable
  • The Cloudflare-like compliance avoidance argument → moot, because everyone can hit your RPC directly

Verify after deployment:

ss -tlnp | grep -E '28081|19081|28083|19083|9091'

Every match should be 127.0.0.1:port, never 0.0.0.0:port. The only 0.0.0.0 listeners on a production node should be:

  • 28080 / 19080 — P2P port, deliberately public
  • 80 / 443 — nginx, on hosts that run public web ingress

Stratum banlist operations

If you run Stratum (coincync-miner pool mode), abuse tracking now persists across restarts.

  • Banlist file path:
    • COINCYNC_STRATUM_BANLIST_PATH=/var/lib/coincync/stratum_bans.json
    • default if unset: stratum_bans.json in the process working directory

Use the built-in maintenance tool:

# View entries
cargo run --bin stratum_ban_tool -- list --file /var/lib/coincync/stratum_bans.json

# Remove one IP
cargo run --bin stratum_ban_tool -- clear-ip --file /var/lib/coincync/stratum_bans.json --ip 203.0.113.10

# Remove expired bans
cargo run --bin stratum_ban_tool -- clear-expired --file /var/lib/coincync/stratum_bans.json

# Clear all entries
cargo run --bin stratum_ban_tool -- clear-all --file /var/lib/coincync/stratum_bans.json

Operational recommendations:

  • Keep banlist under the node data dir (/var/lib/coincync) with 0600/0640-style permissions.
  • Rotate/clear expired bans as part of routine host maintenance.
  • If you expose Stratum publicly, keep COINCYNC_STRATUM_PASSWORD set and ensure encrypted transport (prefer native TLS; upstream TLS termination is accepted with explicit acknowledgement).

Stratum hardening env reference

For public Stratum exposure, CoinCync now enforces explicit transport/security intent. Use this as the operator checklist:

  • COINCYNC_STRATUM_PUBLIC_BIND=1
    • Requests public bind (0.0.0.0:3333).
    • If unset, Stratum defaults to loopback-only.
  • COINCYNC_STRATUM_PUBLIC_BIND_ACK=1
    • Mandatory acknowledgement before any public bind is allowed.
  • COINCYNC_STRATUM_PASSWORD=<strong-secret>
    • Mandatory for public Stratum; workers must authorize.
  • COINCYNC_STRATUM_TLS_ENABLED=1
    • Enables native TLS transport inside Stratum.
    • Optional cert/key paths:
      • COINCYNC_STRATUM_TLS_CERT_PATH=/path/to/cert.pem
      • COINCYNC_STRATUM_TLS_KEY_PATH=/path/to/key.pem
    • If cert/key are not provided, Stratum auto-generates a self-signed pair under:
      • COINCYNC_STRATUM_TLS_DATA_DIR (default .coincync/stratum-tls)
  • COINCYNC_STRATUM_TLS_PROXY_ACK=1
    • Use this only when TLS is terminated by a trusted reverse proxy/L4 terminator.
    • Required for public bind when native TLS is not enabled.

Secure combinations:

  • Recommended (native TLS):
    • COINCYNC_STRATUM_PUBLIC_BIND=1
    • COINCYNC_STRATUM_PUBLIC_BIND_ACK=1
    • COINCYNC_STRATUM_PASSWORD=...
    • COINCYNC_STRATUM_TLS_ENABLED=1
  • Accepted (external TLS terminator):
    • COINCYNC_STRATUM_PUBLIC_BIND=1
    • COINCYNC_STRATUM_PUBLIC_BIND_ACK=1
    • COINCYNC_STRATUM_PASSWORD=...
    • COINCYNC_STRATUM_TLS_PROXY_ACK=1

Quick verification:

curl -sX POST http://127.0.0.1:28081 \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"get_info"}' | jq '.result | {
    stratum_public_bind_requested,
    stratum_public_bind_ack,
    stratum_native_tls_enabled,
    stratum_tls_proxy_ack,
    stratum_transport_hardened
  }'

stratum_transport_hardened should be true for production profiles.

Pool public bind hardening envs

Pool service exposure uses explicit acknowledgements similar to Stratum:

  • COINCYNC_POOL_PUBLIC_BIND_ACK=1
    • Required before the pool may bind publicly.
  • COINCYNC_POOL_TLS_PROXY_ACK=1
    • Required for public pool exposure when TLS is terminated by a trusted upstream proxy.

If these are not set, keep pool endpoints loopback-only.

Network roles

Most production nodes do exactly one of these. Specializing per host limits blast radius.

RoleWhat runs therePublic portsExample host
Seed nodecyncd28080 (P2P)seed1, seed2, seed3
Explorer hostcyncd + nginx + explorer assets80, 443, 28080LON (current public host)
API hostcyncd + nginx (REST/RPC proxy)80, 443, 28080TOR
Minercyncd + coincync-miner28080NYC3
Faucetcyncd + faucet HTTP service28080, 8080 (faucet UI)(operator-assigned host)
Landing/docsnginx (no cyncd needed)80, 443NYC3
MonitoringPrometheus + Grafana(no public DNS)(operator-assigned host)

Splitting roles across hosts is the same federation strategy explained in Federation & DDoS. One host going down only affects one role.

Updates

cd /opt/coincync
git fetch
git log HEAD..origin/main --oneline   # review what's about to come in
git pull --ff-only

# systemd:
cargo build --release --features "randomx testnet"
sudo install -m 0755 target/release/coincync-node /usr/local/bin/coincync-node
sudo systemctl restart coincync-node

# docker compose:
docker compose build
docker compose up -d

The chain database survives across upgrades — the on-disk format is stable. If a future upgrade ever changes the database format, the upgrade notes will say so explicitly and the node will refuse to start with a clear error rather than corrupting state.

Backups

Three things to back up:

  1. Wallet files (~/.coincync/wallets/*.wallet) — encrypted, but lose the password and they’re gone forever. Back up the encrypted file AND the password, separately.
  2. Wallet seed phrases — the 24-word BIP39 mnemonic. Paper, fireproof safe, ideally two copies in different locations.
  3. Node identity (~/.coincync/node_key) — the Noise XX static key that gives this node its persistent peer ID. Optional to back up; if you lose it, the node generates a new one and gets a new peer ID.

The chain database does NOT need backing up — it can always be re-synced from the network.

Next reading

Signed Bootstrap Manifest

Use a signed seed manifest when you want bootstrap peers to come from an authenticated operator-controlled list instead of unsigned DNS/hardcoded seeds.

Why this exists

  • DNS seeds are useful, but they are still a trust surface.
  • A signed manifest lets you pin bootstrap peers to what your operators explicitly approved.
  • Signature verification happens in-node before peers are accepted.

CoinCync verifies signatures over:

  • b"coincync/bootstrap-manifest/v1" + manifest_bytes

with an Ed25519 public key you provide at runtime.

Manifest format

JSON file:

{
  "peers": [
    "1.2.3.4:28333",
    "5.6.7.8:28333"
  ]
}

Tooling

CoinCync ships a first-party helper binary:

  • bootstrap_manifest_tool

1) Generate signing keypair

cargo run --bin bootstrap_manifest_tool -- \
  keygen \
  --secret-out ./bootstrap_signing.key \
  --public-out ./bootstrap_signing.pub
  • bootstrap_signing.key: raw 32-byte secret key (keep private)
  • bootstrap_signing.pub: hex public key (safe to distribute)

2) Create manifest

cargo run --bin bootstrap_manifest_tool -- \
  create \
  --out ./bootstrap_seeds.json \
  --peer seed1.coincync.network:28333 \
  --peer seed2.coincync.network:28333 \
  --peer seed3.coincync.network:28333

3) Sign manifest

cargo run --bin bootstrap_manifest_tool -- \
  sign \
  --manifest ./bootstrap_seeds.json \
  --secret-key ./bootstrap_signing.key \
  --sig-out ./bootstrap_seeds.json.sig
cargo run --bin bootstrap_manifest_tool -- \
  verify \
  --manifest ./bootstrap_seeds.json \
  --public-key ./bootstrap_signing.pub \
  --signature ./bootstrap_seeds.json.sig

Expected output:

Signature OK

Node runtime configuration

Set these environment variables for coincync-node:

  • COINCYNC_BOOTSTRAP_SIGNED_MANIFEST=/path/bootstrap_seeds.json
  • COINCYNC_BOOTSTRAP_SIGNING_PUBKEY=<32-byte-hex-public-key>
  • Optional: COINCYNC_BOOTSTRAP_SIGNED_MANIFEST_SIG=/path/bootstrap_seeds.json.sig
    • default if omitted: <manifest>.sig

Additional bootstrap hardening knobs:

  • COINCYNC_BOOTSTRAP_MANIFEST_ONLY=1
    • Use signed manifest peers only (skip DNS and hardcoded fallback)
  • COINCYNC_BOOTSTRAP_DISABLE_DNS=1
    • Skip DNS seeds even when not in manifest-only mode
  • COINCYNC_BOOTSTRAP_SEED_ALLOWLIST=ip:port,ip:port,...
    • Filter final candidate peers to explicit allowlist

Example (systemd environment file)

COINCYNC_BOOTSTRAP_SIGNED_MANIFEST=/etc/coincync/bootstrap_seeds.json
COINCYNC_BOOTSTRAP_SIGNING_PUBKEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
COINCYNC_BOOTSTRAP_SIGNED_MANIFEST_SIG=/etc/coincync/bootstrap_seeds.json.sig
COINCYNC_BOOTSTRAP_MANIFEST_ONLY=1

Use a preflight check so the service fails before startup when manifest or signature config is broken.

Script:

  • scripts/preflight_bootstrap_manifest.py

Example unit override:

[Service]
EnvironmentFile=/etc/coincync/bootstrap.env
ExecStartPre=/usr/bin/python3 /opt/coincync/scripts/preflight_bootstrap_manifest.py

Template environment file:

  • deploy/bootstrap.env.example

This verifies:

  • required env vars are present
  • manifest and signature files exist
  • Ed25519 signature is valid for the manifest (same domain-separated scheme as node bootstrap verification)

Operational guidance

  • Keep signing keys offline if possible.
  • Rotate signing keys with an overlap window (ship new pubkey + new manifest before removing old).
  • Treat manifest publication like release artifacts: change review, checksum, signature verification.
  • Log and alert on bootstrap candidate count dropping unexpectedly (empty manifest, signature mismatch, etc.).

Troubleshooting

Symptom: loaded 0 signed manifest peers

Common causes:

  • Manifest path is wrong (COINCYNC_BOOTSTRAP_SIGNED_MANIFEST).
  • Manifest JSON is malformed.
  • peers list is empty.
  • Entries are not valid ip:port socket addresses.

Check quickly:

ls -l /path/bootstrap_seeds.json
python -m json.tool /path/bootstrap_seeds.json

Symptom: Bootstrap manifest signature verification failed

Common causes:

  • Signature file does not match manifest bytes.
  • Wrong COINCYNC_BOOTSTRAP_SIGNING_PUBKEY.
  • Signed a different file than the one deployed.
  • Wrong signature file path (COINCYNC_BOOTSTRAP_SIGNED_MANIFEST_SIG).

Verify end-to-end:

cargo run --bin bootstrap_manifest_tool -- \
  verify \
  --manifest /path/bootstrap_seeds.json \
  --public-key /path/bootstrap_signing.pub \
  --signature /path/bootstrap_seeds.json.sig

Symptom: node starts but still uses DNS/hardcoded peers

You likely forgot strict mode.

Use:

COINCYNC_BOOTSTRAP_MANIFEST_ONLY=1

Optional extra hardening:

COINCYNC_BOOTSTRAP_DISABLE_DNS=1

Symptom: no peers after enabling manifest-only

Likely causes:

  • Manifest signature/key mismatch (manifest ignored).
  • Manifest peer list unreachable from this host.
  • Firewall blocks outbound P2P.

Checks:

ss -tlnp | rg 28080
journalctl -u coincync-node -n 200 --no-pager

Look for bootstrap log lines about manifest load count and signature verification.

Block explorer

How to run the public block explorer at https://explorer.coincync.network. Lives in /deploy/explorer/.

Architecture (one-line)

A single Caddy reverse proxy on port 443, fronting a localhost-bound coincync-node jsonrpsee server, with the embedded block explorer HTML served from GET / and POST /api/{testnet,mainnet} proxying to the cyncd RPC.

The explorer UI loads chain data only through that JSON-RPC surface (same-origin /api/testnet or /api/mainnet). On the Blocks page it walks the full canonical height range using batched get_block_range (100 heights per request, server-capped) so the table can list every block the upstream node stores, without thousands of per-height round trips.

Recent UX hardening keeps the CoinCync visual style while making chain views more operator-friendly:

  • blocks/mempool/network/health/iron pages show explicit source + freshness badges,
  • the blocks table supports newest reset + height jump + progressive backfill,
  • duplicate low-signal widgets were removed from high-traffic pages to prioritize chain state.
    public 443/tcp ── Caddy ── 127.0.0.1:28081 (cyncd testnet RPC)
                        │   └─ 127.0.0.1:19081 (cyncd mainnet RPC, post-launch)
                        ├─── /var/www/explorer/index.html (embedded HTML)
                        └─── /static/vendor/* (vendored CDN assets)

Files

FilePurpose
CaddyfileTLS termination, security headers, rate limit, RPC proxies
docker-compose.ymlCaddy 2.8-alpine, host networking, persistent ACME volumes
README.mdOperator runbook, DDoS-mitigation discussion
fetch-vendor.shDownloads chart.js / d3 / globe.gl / topojson / world-atlas / three-globe textures + Google Fonts. TOFU SHA-256 pinning.
patch-vendor.shRewrites src/explorer/index.html to use /static/vendor/ paths instead of external CDN URLs.
static/vendor/Vendored CDN assets after fetch-vendor.sh runs. Committed to git for reproducibility.
static/vendor/checksums.txtSHA-256 of every vendored file. Verified on every fetch-vendor.sh run.

First-time deploy

On the explorer host (currently LON):

# 1. Pre-flight: confirm cyncd is bound to localhost only
ss -tlnp | grep -E ':(28081|19081) '
# Expect: 127.0.0.1:28081 (testnet RPC). Mainnet RPC appears post-launch.
# DO NOT see 0.0.0.0 — that defeats the entire Caddy security model.

# 2. Pre-flight: confirm 80/443 are clear and DNS resolves
ss -tlnp | grep -E ':(80|443) '   # expect nothing
dig +short explorer.coincync.network
# Expect: the IP of the current explorer host (see operator inventory)

# 3. Open public ports in DigitalOcean firewall: 80/tcp and 443/tcp.
#    DO NOT also open 28081 (the whole point is that only Caddy is public).

# 4. Pull and deploy
cd /opt/coincync
git pull --ff-only
cd deploy/explorer

# 4a. (Recommended) vendor the CDN dependencies
./fetch-vendor.sh
# Reviews + commits checksums.txt
git add static/vendor && git commit -m "explorer: vendor CDN deps"

# Then patch index.html to use the vendored paths
./patch-vendor.sh
git add ../../src/explorer/index.html && git commit -m "explorer: use vendored assets"

# Re-run lib tests to confirm the CDN-enumeration test still passes
cargo test --lib -p coincync explorer

# 4b. Start Caddy
docker compose up -d
docker compose logs -f caddy   # watch the ACME handshake

First request to https://explorer.coincync.network/ triggers Let’s Encrypt cert provisioning (~5 seconds). Subsequent requests are instant.

Verification

curl -I https://explorer.coincync.network/
# expect: HTTP/2 200, content-type: text/html, cache-control: public, max-age=300, must-revalidate

curl -sX POST https://explorer.coincync.network/api/testnet \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_info"}' | jq .

# Mainnet pre-launch returns 502 (no upstream). Post-launch returns chain data.
curl -sX POST https://explorer.coincync.network/api/mainnet \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_info"}' -w '\n%{http_code}\n'

Updating the explorer HTML

The HTML is bind-mounted live from src/explorer/index.html. To update:

cd /opt/coincync
git pull --ff-only       # pulls any changes to src/explorer/index.html
# Caddy doesn't need to restart. The HTML cache is 5 minutes, so worldwide
# rollout completes in ≤ 5 minutes.

To force-refresh immediately, docker compose restart caddy.

Production keep/remove checklist

Use this as the default trim for the public explorer. The goal is a safer, lower-noise operator surface.

Keep (public-by-default)

  • home (summary, latest blocks, chain status)
  • blocks, block, tx, mempool (core explorer flow)
  • network, health (operational observability)
  • supply, burn (tokenomics transparency)
  • privacy, about, constitution, changelog (project/legal context)
  • docs, api, search, faucet (user support and read-only API docs)

Dev-only or remove from public nav

  • wallet (browser key generation risk, even with warnings)
  • apilive (encourages ad-hoc probing from browsers)
  • txbuilder (implies browser tx construction workflow)
  • multisig (advanced flow better documented in wallet/CLI docs)
  • balancelookup, richlist (privacy/targeting metadata amplification)
  • webhooks, livestream (non-essential attack surface and data churn)
  • status, miningtutorial, governance (better hosted in docs pages, not top nav)

Runtime control now in index.html

The explorer now defaults to production mode, which hides the dev-only pages above.

  • Public deployment behavior: pages are hidden and direct navigation falls back to home
  • Local dev override: run on localhost or 127.0.0.1 with ?dev_explorer=1
  • External dependency override: remote assets/fetches are disabled by default and only enabled on localhost with ?allow_external_deps=1

Example:

http://localhost:19082/?dev_explorer=1

Security model

The explorer host is a trust boundary. Every public-facing HTTP request lands on Caddy, which enforces:

  • TLS via Let’s Encrypt (auto-provisioned, auto-renewed)
  • HSTS preload (Strict-Transport-Security: max-age=31536000; includeSubDomains; preload)
  • CSP (default-src 'self' target posture; temporary external origins must stay explicitly documented and minimized)
  • Rate limit of 60 req/min per source IP (whose primary purpose is making industrial-rate scraping by chain-forensics firms expensive — see Federation & DDoS)
  • Body size cap of 64 KB per request (mirrors the rest.rs proxy’s cap)
  • Method allowlist enforced by the upstream rest.rs proxy when the explorer’s JS calls /api/v1/...
  • No third-party MITM (no Cloudflare, no CDN, no edge cache that sees decrypted traffic)

The cyncd node itself is NOT exposed publicly. Caddy proxies allowlisted requests to it on localhost. Any attacker who finds a vulnerability in jsonrpsee can still only hit it through Caddy’s allowlisted method set, with the rate limit applied.

What this stack deliberately does NOT do

  • Cloudflare in front — see Federation & DDoS. The whole reason to NOT use a CDN is structural to the privacy-coin threat model.
  • Per-user authentication — every endpoint is read-only and rate-limited. Authentication on a write-capable wallet endpoint lands in P1.
  • Transaction submissionsubmit_block and send_raw_transaction are blocked from the public REST proxy by RPC_ALLOWED_METHODS. Wallets that need to submit transactions submit to their own local node, not to the public explorer.
  • Tx-by-hash lookupget_transaction returns a labelled NotImplemented because the chain doesn’t yet have a txid → (block_height, tx_index) index. Lands in P1.
  • Asset queries — permanently NotImplemented. CoinCync 1.0 stripped the confidential-asset layer.

Adding a .onion mirror

See Tor hidden services. The same Caddy stack can additionally serve the explorer over an .onion for users whose threat model demands strong anonymity. ~20 minutes of setup, additive to the clearnet site.

See also

  • Federation & DDoS — why this is one host and not behind a CDN
  • Public RPC API — the parallel deployment for the API subdomain
  • Tor hidden services — adding .onion availability
  • The deploy/explorer/README.md source file — has the same content as this page plus operator-runbook details

Public RPC API host

How to run the public JSON-RPC + REST API at https://api.coincync.network. Lives in /deploy/api/.

Architecturally identical to /deploy/explorer/, with three differences calibrated for an API audience instead of a human-browser audience:

deploy/explorer/deploy/api/
Audiencehumans (browsers)machines (wallets, SDKs, bots)
Rate limit60/min/IP (lenient)30/min/IP (strict)
Body shapeHTML + static assets + JSONJSON-only
CORSrequired (CDN allowlist)none (server-to-server)
Cachingaggressive on static assetsno caching
HostExplorer hostTOR (api.coincync.network)

The strict per-IP limit is calibrated for machine clients. A wallet polling get_info once per block (every 120 seconds) uses 0.5 req/min — two orders of magnitude under the limit. Anything faster than ~30 req/min sustained is either a bug or a chain-analysis scraper, and slowing them down is a feature, not a bug.

URL surface

Public URLBackendPurpose
POST /rpc127.0.0.1:28083 (rest.rs proxy → testnet jsonrpsee)Default JSON-RPC 2.0, testnet
POST /rpc/mainnet127.0.0.1:19083 (post-launch)JSON-RPC 2.0, mainnet
GET /v1/statusRESTChain status, height, tip
GET /v1/blocks/recentRESTLatest blocks (paginated)
GET /v1/block/height/{h}RESTBlock by height
GET /v1/block/hash/{hash}RESTBlock by hash
GET /v1/anonymityRESTAnonymity-set size + ring stats
GET /v1/peersRESTConnected peer list
GET /v1/supplyRESTEmission curve + total supply
GET /v1/eventsRESTRecent reorgs / forks / rejects
GET /inline HTMLDeveloper landing — points at explorer + curl examples

Every method is read-only. The REST proxy in src/rpc/rest.rs::RPC_ALLOWED_METHODS enforces this server-side; submit_block, send_raw_transaction, get_mining_live, and any other write or fingerprint-sensitive method returns 403 Forbidden.

First-time deploy

On the API host (TOR — resolve api.coincync.network for the current address):

# 1. Pre-flight: cyncd RPC + REST bound to localhost only.
ss -tlnp | grep -E ':(28080|28081|28083) '
# Expect:
#   0.0.0.0:28080      ← P2P (this host is also seed1, so P2P is public)
#   127.0.0.1:28081    ← jsonrpsee, localhost only
#   127.0.0.1:28083    ← REST (rest.rs), localhost only

# 2. Open 80/443 in DigitalOcean firewall. Do NOT open 28081 / 28083 / 19081 / 19083.
# 3. Deploy
cd /opt/coincync
git pull --ff-only
cd deploy/api
docker compose up -d
docker compose logs -f caddy

Verification

# JSON-RPC works (testnet by default)
curl -sX POST https://api.coincync.network/rpc \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_info"}' | jq .

# REST works
curl -s https://api.coincync.network/v1/status | jq .

# Write methods are blocked (allowlist enforced server-side)
curl -sX POST https://api.coincync.network/rpc \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"submit_block","params":["00..."]}' \
     -w '\n%{http_code}\n'
# expect: 403 (or a JSON-RPC error)

# Mainnet pre-launch returns 502 (no upstream until October 2026)
curl -sX POST https://api.coincync.network/rpc/mainnet \
     -H 'content-type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"get_info"}' -w '\n%{http_code}\n'

Mainnet launch (Oct 1, 2026)

The Caddyfile already has the /rpc/mainnet upstream pool defined. To activate:

  1. Start a coincync-node --network mainnet --rpc-bind 127.0.0.1:19081 --rest-bind 127.0.0.1:19083 instance on this host.
  2. docker compose restart caddy.
  3. Verify with curl https://api.coincync.network/rpc/mainnet.

Federation

When traffic on the public API outgrows what one Toronto host can handle, the right answer is a second API host with api2.coincync.network pointing at it. Not a CDN. The full reasoning is in Federation & DDoS. The Caddyfile is parameterized so a second operator can drop in a new deployment with a different hostname.

See also

Federation & DDoS mitigation

The defensive posture for CoinCync’s public infrastructure. The TL;DR is distribute, don’t centralize. The longer reading explains why every “obvious” answer (Cloudflare, edge CDN, scrubbing service) is actively wrong for a privacy coin.

Threat model

Privacy coins face DDoS for very different reasons than e-commerce sites:

1. Market manipulation / short-selling

Someone shorting CYNC wants the public explorer down during a coordinated FUD campaign. “Blockchain is broken, the explorer is offline” headlines move price. Taking the explorer offline for 6 hours during a Twitter storm manufactures the appearance of technical failure at exactly the moment when speculators are looking for confirmation bias. The DDoS is advertising.

2. Compliance pressure

Governments hostile to privacy coins (US Treasury OFAC, China, Korea, India) have documented motivation to disrupt privacy-coin infrastructure. The Tornado Cash precedent in August 2022 is the clearest case: a privacy tool whose frontend was hosted behind Cloudflare got its Cloudflare account terminated within days of the US Treasury sanctions announcement. The attack wasn’t a botnet flood — it was a letter.

A regulator who wants a privacy coin off the public internet doesn’t attack your servers; they send Cloudflare a letter, and Cloudflare complies within hours. Using Cloudflare makes your operational survival a function of someone else’s legal calculus. This is why “just put it behind Cloudflare” is structurally wrong for a privacy coin no matter how technically convenient it is.

3. Traffic-analysis piggybacking

Adversaries flood the explorer specifically to manipulate the statistical signature of legit queries. Two variants:

  • Cover traffic: produce noise that looks like a wallet scanner so when a real wallet scanner queries, it doesn’t stand out.
  • Differential analysis: force the rate-limit logic to throttle specific IPs into predictable timing patterns, giving the attacker a clock to correlate against on-chain activity.

Cloudflare’s edge caching makes this worse by homogenizing response timing across geographic regions.

4. Chainalysis-style scraping

This is the actual sustained baseline traffic. Chain-forensics firms (Chainalysis, TRM Labs, Elliptic, CipherTrace) scrape public block explorers at industrial rates because they need the data for pattern analysis. It’s not technically a DDoS — it’s legitimate-looking HTTP traffic — but:

  • It eats your bandwidth budget
  • It slows the explorer for real users
  • It builds a dataset the scraper uses to deanonymize users
  • Rate-limiting them hits real users first, because forensics firms use rotating residential proxies and rotating IPs that Cloudflare can’t distinguish from real users

The rate limit you put in Caddy is primarily defending against this, not against botnet floods. That’s actually the dominant defensive use case on a privacy coin.

5. Targeted user attacks

If an attacker knows a specific user (whistleblower, journalist, dissident) checks their CoinCync balance via the explorer at a specific time, taking the explorer down during that window is an attack on that user specifically. Small surface-area attacks that look like minor downtime spikes.

6. Compliance harassment

Paperwork floods aimed at your hosting stack: rapid-fire takedown requests against your hosting provider (DigitalOcean), domain registrar, DNS provider, or any third party in the chain. DigitalOcean has complied with takedowns. Cloudflare has de-platformed customers. The defense is distribution — the more places your infrastructure lives, the more paperwork a regulator has to file, the more chances one of them says no.

Why Cloudflare (and most CDNs) are wrong

Five structural reasons:

  1. Cloudflare MITMs every request. It terminates TLS at its edge, decrypts every request, inspects it, then re-encrypts to your origin. Even in “Full (strict)” mode, Cloudflare is a designed-in MITM. For a privacy coin, that means a US corporation logs (visitor_ip, queried_address, timestamp) tuples for every explorer lookup. The cryptographic privacy you built into CLSAG, Bulletproofs, and stealth addresses is defeated at the network layer.

  2. Cloudflare blocks Tor users via captcha challenges that make many sites unreachable through Tor Browser. The users who most need privacy are the ones routing through Tor — and they’re the ones Cloudflare treats as suspicious.

  3. Cloudflare is a compliance chokepoint (the Tornado Cash precedent above).

  4. Cloudflare centralizes outage risk. A Cloudflare-wide outage (June 2024, July 2019) takes down everyone behind it simultaneously. A privacy coin whose explorer AND API both depend on Cloudflare goes fully dark on those days.

  5. The threat Cloudflare solves isn’t the threat a privacy coin actually faces. Cloudflare is excellent against drive-by botnet floods. The real threats are the six categories above, none of which Cloudflare meaningfully addresses.

The correct defensive posture

Five layers, in rough order of effort:

Layer 1 — aggressive own-edge rate limiting

The Caddyfiles in deploy/explorer/ and deploy/api/ already do this:

  • explorer.coincync.network: 60 req/min/IP
  • api.coincync.network: 30 req/min/IP

The primary purpose of these limits is not botnet defense — it’s making Chainalysis-style scraping expensive. Scrapers will still get data, but at a rate that degrades their deanonymization tooling.

Layer 2 — federation instead of centralization

Run multiple independent hosts. When explorer.coincync.network is at capacity, the right answer is not “add a CDN” — it’s “run a second host.”

Bitcoin’s explorer ecosystem is the model:

  • blockstream.info
  • mempool.space
  • blockchain.info

Three parallel independent deployments. A user who doesn’t trust one picks another. A DDoS that takes one down leaves the others up. A takedown against one doesn’t affect the others. There is no single point a regulator can lean on.

For CoinCync, the federation plan looks like:

explorer.coincync.network    →  RIC      (primary)
explorer2.coincync.network   →  <new>    (second host you control)
explorer3.coincync.network   →  <community-run>

A first-time visitor lands on explorer.coincync.network. If it’s slow or down, the page shows a “try a different mirror” link to the alternatives. Each mirror is independently operated; the operator just runs the same Caddy stack from deploy/explorer/ with a different CADDY_HOSTNAME env var.

The DDoS resistance comes from there being nowhere to aim, not from a single scrubber in front.

Mirror discovery

The mirror list is published as a small static JSON file at https://docs.coincync.network/mirrors.json (or a dedicated mirrors.coincync.network if you want it on its own host):

{
  "explorer": [
    { "url": "https://explorer.coincync.network",  "host": "RIC",      "operator": "official" },
    { "url": "https://explorer2.coincync.network", "host": "<region>", "operator": "<community>" }
  ],
  "api": [
    { "url": "https://api.coincync.network",       "host": "Toronto",  "operator": "official" }
  ]
}

The explorer’s inline JS fetches this on load and offers a “switch mirror” affordance in the UI. A failed RPC fetch on the primary auto-falls-back to the next mirror in the list.

This file is the only thing CoinCync centralizes — and it’s static, cacheable, replicable, and the failure mode is graceful (a stale mirrors list still works because the URLs in it are stable).

Layer 3 — accept occasional downtime

Privacy coin explorers don’t need five-9s uptime. If an attacker takes the explorer offline for an hour, users run their own node and verify directly. The node is the source of truth; the explorer is a convenience. Don’t spend operational complexity on availability you don’t need — spend it on the privacy guarantees you do need.

Layer 4 — privacy-respecting CDN if traffic genuinely demands it

If traffic ever grows past what federation can handle (tens of thousands of concurrent users), there are two CDNs whose privacy posture is meaningfully better than Cloudflare’s:

  • BunnyCDN — European, GDPR-friendly, explicit “we don’t mine customer data” policy. Cheap.
  • Fastly — more expensive; used by Wikipedia, GitHub, Stripe, Mozilla. Strong privacy posture, no data resale.

Both are strictly better than Cloudflare for a privacy coin. Still not as good as no CDN at all, but if you genuinely need edge presence for performance, start with one of these. Almost certainly you’ll never need to.

Layer 5 — Tor hidden service availability

Publish an .onion mirror alongside the clearnet site for users whose threat model demands strong anonymity. Additive, not replacement. ~20 minutes of setup. See Tor hidden services for the runbook.

TL;DR

ThreatWrong answerRight answer
Botnet DDoSCloudflareCaddy rate limit + federation
Chainalysis scrapingCloudflareCaddy rate limit (primary purpose)
Traffic-analysis piggybackingCloudflare (makes it worse)Federation + .onion availability
Regulator takedownCloudflare (single chokepoint)Federation across jurisdictions
Short-seller narrative DDoSCloudflareFederation + accept occasional downtime
Targeted user attackCloudflare.onion availability for that user

Never route privacy-coin public traffic through a third party whose business model involves inspecting the traffic. Your users’ threat model says “nobody should see my queries.” Cloudflare’s business model says “we see everything to add value.” Those are incompatible premises.

See also

Tor hidden services

Publishing CoinCync’s public infrastructure (block explorer, RPC API) as Tor hidden services is additive, never replacement. The clearnet sites stay live at explorer.coincync.network and api.coincync.network; an .onion mirror is published alongside them for users whose threat model demands end-to-end Tor.

This is the same model Pirate Chain, Monero, and Zcash follow. Their main websites are clearnet HTTPS; their .onion mirrors exist for the hardened threat model and are published in their docs and on their Twitter.

Why an .onion is meaningfully better than “use Tor Browser to visit our clearnet site”

Visiting https://explorer.coincync.network through Tor Browser still leaves a Tor exit node in the path — and many exit nodes are run by researchers, governments, or adversaries who log traffic. Even though the traffic is HTTPS-encrypted, the exit node sees:

  • Which .coincync.network host you’re talking to
  • The TLS SNI (which can leak the subdomain even on encrypted connections)
  • Timing patterns
  • The fact that someone using Tor is interested in CoinCync

An .onion is end-to-end within Tor. There is no exit node, only relays that see encrypted onion-layer traffic they cannot inspect. The destination is reached via a 6-hop circuit (3 client-side + 3 server-side, meeting in the middle) with no point of plaintext exposure.

For a privacy coin, this matters. The clearnet site is for users who don’t need that level of unlinkability. The .onion mirror is for users who do.

Setup on the explorer host

Roughly 20 minutes of work on the explorer host that already runs the Caddy stack.

1. Install Tor

sudo apt update
sudo apt install -y tor

Verify it started:

systemctl status tor

2. Configure a v3 hidden service

Append to /etc/tor/torrc:

# ── CoinCync explorer hidden service ─────────────────────
HiddenServiceDir /var/lib/tor/coincync_explorer/
HiddenServiceVersion 3

# Forward the .onion's port 80 to Caddy's HTTP bind on loopback.
# Tor clients speak HTTP (not HTTPS) over the onion because the Tor
# circuit already provides end-to-end encryption + auth — layering
# TLS on top is redundant and leaks cert metadata via the
# Server-Name-Indication field in the TLS handshake.
HiddenServicePort 80 127.0.0.1:80

Reload Tor:

sudo systemctl restart tor

Tor generates a v3 (56-character) onion hostname on first start. Read it:

sudo cat /var/lib/tor/coincync_explorer/hostname
# → <56-char-v3-onion>.onion

Save this hostname — you’ll need it for the next step and to publish.

3. Add a server block to the Caddyfile

The existing deploy/explorer/Caddyfile only listens on :443 (and gets :80 automatically for the ACME challenge). To also serve plaintext HTTP on 127.0.0.1:80 for Tor, append:

# ── .onion mirror ─────────────────────────────────────────
# Listens only on the loopback interface so only the local Tor
# daemon can reach it. The public 443 site is unaffected.
#
# `http://` is intentional — no TLS. Tor circuits are already
# end-to-end encrypted; layering TLS on top is redundant and
# leaks cert metadata via SNI in the TLS ClientHello.
#
# Caddy's auto-HTTPS is disabled for this site by binding to
# `http://` explicitly.
http://127.0.0.1:80 {
    handle /api/testnet {
        request_body { max_size 64KB }
        reverse_proxy 127.0.0.1:28081 {
            header_up Host {host}
            header_up Content-Type "application/json"
        }
    }
    handle /api/mainnet {
        request_body { max_size 64KB }
        reverse_proxy 127.0.0.1:19081 {
            header_up Host {host}
            header_up Content-Type "application/json"
        }
    }
    handle /metrics {
        reverse_proxy 127.0.0.1:9091
    }
    handle {
        root * /var/www/explorer
        encode zstd gzip
        try_files {path} /index.html
        file_server
    }
    log {
        output stdout
        format json
        level INFO
    }
}

Reload Caddy:

docker compose -f /opt/coincync/deploy/explorer/docker-compose.yml exec caddy \
  caddy reload --config /etc/caddy/Caddyfile

4. Verify

From the same host:

# Caddy is now listening on 127.0.0.1:80
ss -tlnp | grep ':80 '
# expect: 127.0.0.1:80

# Curl through the local Tor SOCKS proxy
curl --socks5-hostname 127.0.0.1:9050 \
     -I http://<your-56-char-v3-onion>.onion/
# expect: HTTP/1.1 200 OK

From a different machine running Tor Browser, visit http://<your-56-char-v3-onion>.onion/ directly. The explorer should load identically to the clearnet site.

5. Publish the onion address

Add it to:

  • https://docs.coincync.network/operations/tor.md (this page — the runbook itself doubles as the announcement)
  • mirrors.json on the docs host (see Federation & DDoS)
  • The project’s social media / Twitter / Mastodon
  • https://coincync.network landing page (in the “find us” section)

Once it’s published, users running Tor Browser can paste it directly. Tor Browser handles the routing; users don’t need to install Tor separately.

Same setup for the API host

The API stack at Toronto can publish its own .onion using exactly the same recipe — just point HiddenServiceDir at /var/lib/tor/coincync_api/, append a parallel http://127.0.0.1:80 block to deploy/api/Caddyfile that proxies /rpc and /v1/* to the local cyncd, and publish the resulting onion hostname.

The two .onion addresses are independent. They’re both on the Tor network but they have separate keys, separate hostnames, and separate failure modes.

Hidden service security checklist

  • Hostname recorded somewhere offline. If the HiddenServiceDir files are lost, the onion address is gone forever (you can’t recover it from the network — the address is the public key, derived from the private key in that directory).
  • HiddenServiceDir contents backed up. hostname, hs_ed25519_public_key, hs_ed25519_secret_key. The secret key is what proves you control the onion. If it leaks, anyone with the key can impersonate your onion, including running a man-in-the-middle that serves modified explorer HTML.
  • Permissions on HiddenServiceDir are 0700, owned by debian-tor. Tor refuses to start otherwise.
  • Caddy on 127.0.0.1:80 is bound only to loopback. Verify with ss -tlnp | grep ':80 '. If it’s 0.0.0.0:80, the explorer is also publicly reachable on plaintext HTTP, which defeats both the TLS-on-clearnet and the Tor-only-via-onion contracts.
  • No external CDN dependencies in the embedded HTML. The vendoring pass in deploy/explorer/fetch-vendor.sh + patch-vendor.sh is required for the .onion to actually work — a Tor user can’t reach cdn.jsdelivr.net from inside a .onion session unless their Tor Browser is configured to allow exit-node requests for those origins (most aren’t, by default, and that defeats the point anyway).

What an .onion does NOT protect

  • Application-layer fingerprinting. A wallet that uses a unique ring-size or transaction shape leaks its identity through the on-chain data even if the network path is anonymous.
  • Out-of-band correlation. If a user mentions their balance in chat, in email, in a leak, the cryptography didn’t fail — they did.
  • Tor itself. Tor has known limitations against well-funded adversaries who can observe entry and exit traffic. The hidden-service circuit is more robust than clearnet-Tor (no exit node), but it’s not magic.

See also

Constitution

The Supreme and Permanent Law of the CoinCync Protocol

Version: 1.0 Ratified: Block 0 (Genesis) Network: CoinCync Mainnet

The authoritative source is CONSTITUTION.md at the repository root. This page reproduces it in full and adds, below each article, a code-enforcement note pointing at the exact source line where that article is mechanically enforced (where possible). If this page ever drifts from the repository file, trust the repository file.

See also the companion Bill of Rights — the user-facing rights guarantee that complements these operator-facing articles.


Preamble

This document defines the immutable principles of the CoinCync protocol. These are not guidelines. They are not aspirations. They are hard commitments — promises written into the code that no developer, committee, majority vote, legal order, or act of governance can override.

Any protocol change that violates this Constitution is invalid, regardless of how many people support it, how much proof of work backs it, or who proposes it.

This Constitution was written once. It will not be rewritten. It exists not because we distrust the people building CoinCync today — but because we cannot know who will be building it tomorrow. These principles protect users from every possible future, including futures we cannot predict.

Ten articles follow. Each one is a wall. None of them have doors.


Article I — Fixed Supply

The total supply of CYNC shall never exceed 250,000,000 coins. This limit is absolute. It is not subject to amendment, emergency override, governance vote, or any other mechanism.

The emission schedule is defined in code and enforced by every node on the network. No entity — no developer, no foundation, no majority of miners — can create coins beyond this limit. The network itself will reject any block that does.

A tail emission exists to sustain mining security after the primary distribution ends. It is not additional supply. The 250,000,000 coin cap is absolute and includes all tail emission across the full lifetime of the chain. The tail emission rate shall not exceed a level that would cause total supply to approach the cap before the theoretical end of the emission schedule.

Anyone can verify the current supply at any time using the Pedersen commitment accumulator built into every node. If the mathematics do not confirm the supply, the chain is invalid. Trust the math, not the announcement.

Enforcement: Protocol-enforced. Every node independently validates supply on every block.

Code enforcement: Compile-time assertions in src/constants.rs:

#![allow(unused)]
fn main() {
const _: () = assert!(TOTAL_SUPPLY_TARGET == 250_000_000,
    "UNCONSTITUTIONAL: Article I — Supply cap must be exactly 250,000,000 CYNC");
const _: () = assert!(MAX_SUPPLY == 250_000_000u128 * COIN as u128,
    "UNCONSTITUTIONAL: Article I — MAX_SUPPLY atomic-unit value must match 250M cap");
}

Any attempt to ship a binary with a different cap fails the build. See Emission curve for the exact mountain curve that stays below the cap across the chain’s lifetime.


Article II — No Pre-mine, No Developer Tax

Zero coins were created before block 0. Zero coins are or will ever be diverted to developers, foundations, treasuries, or any other entity by protocol design. Every CYNC in existence was mined by someone who contributed proof of work to the network.

The CoinCync protocol shall never introduce:

  • A developer fund, foundation treasury, or protocol-level savings account
  • A percentage-based fee or tax redirected to any address or entity
  • A governance token, staking requirement, or bonding mechanism that grants economic advantage to any party
  • Any mechanism that creates coins outside the published emission schedule
  • Any retroactive claim on mined supply by founders or contributors

This is not a promise that might be reconsidered if circumstances change. There are no circumstances under which this article can be suspended, amended, or overridden.

Enforcement: Protocol-enforced. No dev tax mechanism exists in the codebase. Supply is publicly verifiable.

Code enforcement: src/constants.rs:

#![allow(unused)]
fn main() {
pub const DEV_TAX_PERCENT: u64 = 0;
const _: () = assert!(DEV_TAX_PERCENT == 0,
    "UNCONSTITUTIONAL: Article II — Dev tax must be zero. No fee extraction to developers.");
}

The genesis coinbase is deliberately unspendable — the spend key is the all-zero key, which no one holds.


Article III — Mandatory Privacy

All transactions on CoinCync are private. There is no transparent mode. There is no opt-in privacy tier. There is no reduced-privacy transaction class. Every transaction, without exception, uses the full privacy stack:

  • Ring signatures to conceal the sender among a set of cryptographic decoys
  • Stealth addresses to generate one-time destinations that cannot be linked to any public address
  • Pedersen commitments to hide transaction amounts from all observers
  • Bulletproofs to prove that amounts are valid without revealing them

Privacy is not a feature that can be toggled, deprecated, or made optional. It is the default and only state of every coin, every transaction, and every block on CoinCync.

No protocol change shall ever create a class of transactions with weaker privacy guarantees than those described above. Privacy may be strengthened through technical advancement. It may never be weakened.

Enforcement: Protocol-enforced. Ring signatures, stealth addresses, and Bulletproofs are required for all transactions.

Code enforcement: src/constants.rs + src/consensus/privacy_policy.rs:

#![allow(unused)]
fn main() {
const _: () = assert!(MIN_RING_SIZE >= 11,
    "UNCONSTITUTIONAL: Article III — Minimum ring size must be >= 11 for mandatory privacy");
}

enforce_privacy_policy in src/consensus/privacy_policy.rs rejects any block containing a transparent transaction, at the consensus-validation layer — before the block is relayed, added to the mempool, or applied to state. See Privacy model for the full implementation.


Article IV — Auditable Integrity

Despite mandatory privacy, anyone can mathematically verify — without trusting any third party — that:

  1. No coins were created outside the emission schedule
  2. No transaction created coins from nothing
  3. No double-spend has ever occurred on the chain
  4. The total supply at any block height matches the expected emission

This is achieved through the Pedersen commitment accumulator — a cryptographic structure embedded in every node that proves supply integrity without revealing individual transaction amounts. Every node validates this accumulator on every block. A block that fails this check is rejected by the entire network.

Privacy and auditability are not in conflict. CoinCync proves both simultaneously. This is the answer to every critic who says privacy coins cannot be trusted. The math is open. Anyone can check it.

Enforcement: Protocol-enforced. Pedersen accumulator validated on every block by every node.

Code enforcement: Bulletproofs+ range proofs ( bulletproofs / tari_bulletproofs_plus crates) enforce amounts ≥ 0 and ≤ 2^64, mathematically preventing hidden inflation. The Pedersen commitment math in src/crypto/ + src/consensus/validation.rs is what allows every node to verify total supply without seeing individual amounts.


Article V — Open Mining

CoinCync uses RandomX, and only RandomX, as its proof-of-work algorithm. RandomX is a CPU-biased, memory-hard, ASIC-resistant algorithm originally designed for Monero. It has been in production on the longest-running CPU-only privacy coin since November 2019 and remains, as of the ratification of this document, the gold standard for permissionless, hardware-accessible mining.

RandomX uses a 256 MB scratchpad and a virtual machine that executes randomly-generated bytecode — both of which favor general-purpose CPUs over fixed-function hardware. An ASIC for RandomX would essentially be a CPU, which offers no economic advantage over commodity consumer hardware. This is what makes mining permissionless in practice and not only in policy.

The following principles are permanent:

  • Single algorithm. RandomX is the only PoW algorithm. Multi-algorithm rotation schemes are forbidden — they are not security through diversity, they are security through the weakest algorithm in the rotation, and they have a documented history of enabling selfish-mining and algorithm-spam attacks on other chains that tried them. The right defense against ASICs is a single strong memory-hard algorithm, not a rotation.
  • No ASIC-friendly algorithms. Blake3, SHA-256, Equihash, Ethash, and any other algorithm with published ASIC implementations are permanently forbidden. Any deviation — even a single block mined under a different algorithm — produces a chain that is not CoinCync.
  • No Proof of Stake, no hybrid PoS/PoW, no finality gadget. The CoinCync protocol shall never transition to any consensus mechanism that requires holding coins to validate blocks. PoS rewards the already-wealthy and reintroduces the exact permissioned-finance problems this chain was built to reject.
  • No permission, license, or registration is required to mine CoinCync.
  • No KYC, identity verification, or whitelist exists for mining participation.
  • Anyone with consumer hardware can participate in block production.
  • No minimum stake, bond, or holding is required to mine.
  • Mining rewards are determined solely by proof of work contributed.

Enforcement: Protocol-enforced. RANDOMX_ONLY = true is a compile-time constitutional guard in src/constants.rs; any build that tries to flip it fails to compile with "UNCONSTITUTIONAL: Article V". The randomx Cargo feature is on by default; building without it makes compute_pow_hash return an error at runtime so a PoW-skipping binary cannot silently ship.

Code enforcement: PowAlgorithm in src/consensus/pow.rs is a single-variant enum. compute_pow_hash dispatches only to compute_randomx_hash. src/constants.rs asserts RANDOMX_ONLY = true at compile time. Shipping a binary that uses a different algorithm requires deliberately editing three consensus-critical files, which critical_files.lock + build.rs hash verification catches before the build completes.

Why RandomX and not multi-algorithm rotation:

The argument for multi-algorithm rotation is that diversifying across algorithms neutralizes an ASIC advantage in any single one. The argument fails on three grounds:

  1. It reduces security to the weakest algorithm. A rotation including Blake3 is only ASIC-resistant 2/3 of the time. Every third block is trivially ASIC-mineable, which gives well-resourced adversaries a cheap lever to concentrate hashrate and chain-reorg history.
  2. It expands attack surface. Per-algorithm difficulty tracking, cross-algorithm reorg rules, and algorithm-specific validation code all add lines to the verifier. Each line is a potential bug. Multi-algorithm privacy coins (Verge, Bitcoin Gold) have a documented history of consensus exploits enabled by exactly this complexity.
  3. The precedent is RandomX alone. Monero has held ASIC resistance under RandomX-only for five years with no consensus-level exploits and no concentration events attributable to algorithm choice. It is the most thoroughly battle-tested CPU-only design in existence.

RandomX alone is not a compromise. It is the correct choice.

See Consensus & PoW for the implementation details.


Article VI — Voluntary Disclosure Only

CoinCync provides view keys that allow users to selectively prove their transaction history when they choose to do so — for tax reporting, personal audits, proof of payment, or any other voluntary purpose.

This disclosure mechanism is governed by three absolute principles:

  • Voluntary — No person can be required by the protocol to reveal their transactions. Disclosure is always a choice made by the key holder, never a requirement imposed by the network.
  • Selective — Users can disclose specific transactions or a specific time range without revealing their complete history. The granularity of disclosure belongs to the user.
  • Revocable — View keys can be rotated. Past keys cannot see transactions generated after rotation. Forward secrecy is preserved by design.

The CoinCync protocol shall never implement:

  • Mandatory identity verification of any kind
  • Backdoors for any government, regulator, law enforcement agency, or private entity
  • Travel rule enforcement at the protocol level
  • Mandatory transaction memo fields that associate transactions with identities
  • Any mechanism that weakens privacy without the explicit, per-transaction, voluntary consent of the key holder

The right to financial privacy is not contingent on having nothing to hide. It belongs to everyone.

Enforcement: Protocol-enforced for privacy. View key mechanism is the only sanctioned disclosure path.

Code enforcement: View keys live in src/crypto/view_keys.rs. They are generated by the wallet holder, stored on the user’s device, and never transmitted to any party by protocol code. No RPC method, no P2P message, no consensus rule asks for or accepts a view key — disclosure is entirely an out-of-band, user-initiated action outside the network.


Article VII — Permissionless Participation

No entity controls who can participate in the CoinCync network. The following rights of participation are absolute and unconditional:

  • Run a node — anyone may operate a CoinCync node without license, permission, or fee
  • Mine blocks — anyone may participate in block production
  • Send and receive CYNC — anyone may transact on the network
  • Read the blockchain — the chain data is public and accessible to all
  • Build on the protocol — anyone may create software, services, or tools using CoinCync
  • Fork the code — the MIT license guarantees the right to take the technology in a new direction

CoinCync is MIT-licensed. The code is open. The network is open. Participation requires nothing but software and an internet connection.

No future version of the protocol may introduce participation requirements based on identity, nationality, financial status, political affiliation, or any other characteristic. The network serves everyone or it serves no one.

Enforcement: Protocol-enforced for network access. MIT license enforces code freedom.

Code enforcement: No identity checks exist anywhere in the connection handshake (src/network/) or block validation path (src/consensus/validation.rs). The node accepts any peer that speaks the P2P protocol and any block that satisfies the cryptographic consensus rules. Running a node requires no account, no registration, no API key, no geographic check.


Article VIII — Protocol Governance

CoinCync governance operates on two distinct and separate layers. Both are necessary. Neither can override the other’s domain.

Layer 1 — Protocol Governance

Changes to the CoinCync protocol are proposed through CoinCync Improvement Proposals (CIPs). Any person may submit a CIP regardless of identity, holdings, or standing.

At the protocol level, legitimacy is measured by node operator adoption — not by committee vote, coin weight, or individual authority. A change becomes canonical when a supermajority of active nodes choose to run it. The chain with the most accumulated proof of work is always the valid chain. No committee, council, or individual can force a node to upgrade.

No CIP may violate this Constitution. Proposals to raise the supply cap, remove mandatory privacy, introduce a developer tax, add surveillance capabilities, restrict mining access, or weaken any right in the Bill of Rights are unconstitutional on their face — invalid regardless of support, invalid regardless of who proposes them.

Layer 2 — Project Governance

The CoinCync project — its codebase, community, communications, and relationships — is governed by the Separation of Powers document. That document establishes three roles: the Protocol Maintainer, the Community Steward, and the Community itself.

These roles have authority over project decisions. They have no authority over the protocol itself. A unanimous resolution of all role holders cannot change a single consensus rule without node operator adoption.

The project layer coordinates human effort around building CoinCync. The protocol layer guarantees that no amount of human coordination can betray the users this project was built to serve. When these layers conflict, the Protocol Layer wins. Always.

CIP Process

  1. Any person may submit a CIP as a public document
  2. Minimum 30-day public discussion period before any vote
  3. Working implementation required before activation
  4. Node operator adoption required for protocol-layer changes
  5. Constitutional amendments: not permitted under any circumstances

Enforcement: Node adoption is the only vote that matters at the protocol level.

Code enforcement: BIP9-style fork signaling lives in src/consensus/fork_signal.rs. A CIP activates when ≥ SIGNAL_THRESHOLD (1814 of 2016 blocks, ≈ 90%) in a SIGNAL_WINDOW (2016 blocks) signal its bit. Pending CIPs are listed in constants.rs; the table below reflects the current signal-bit assignments.

CIPBitNameWhat it does
CIP-0010view-tagsFaster wallet scanning via 1-byte view tags
CIP-0021ring-size-16Increase minimum ring size from 11 → 16
CIP-0032fee-market-v2Improved fee estimation
CIP-0043halo2-shieldedHalo2 shielded pool (Zcash Orchard style)
CIP-0054lelantus-sparkLelantus Spark large-anonymity-set pool
CIP-0065mw-cutthroughMimbleWimble cut-through

Constitutional invariants are not CIPs and cannot be changed by signaling. A CIP that proposes raising the supply cap, removing mandatory privacy, weakening the ring-size floor, introducing a developer tax, or adding surveillance hooks is unconstitutional on its face — the constitutional guard asserts in constants.rs would fail to compile.


Article IX — No Surveillance Infrastructure

The CoinCync protocol shall never include any feature whose primary purpose is to enable financial surveillance, network monitoring, or the identification of participants without their consent.

The following are permanently prohibited from the CoinCync protocol:

  • Blacklists or address-level transaction blocking of any kind
  • Chain analysis hooks or metadata fields that leak identifying information by design
  • Mandatory transaction memo fields that associate transactions with identities
  • IP address logging in the reference implementation
  • Any reporting mechanism that transmits user data to third parties
  • Any feature that allows a third party to identify the sender or receiver of a transaction without the voluntary disclosure of a view key

Encrypted peer-to-peer communication and Dandelion++ transaction routing are built into the protocol specifically to prevent network-level surveillance — the identification of transaction originators through network traffic analysis. These protections shall not be weakened.

The distinction between lawful monitoring and surveillance infrastructure is not recognized at the protocol level. The protocol does not know who is asking. It protects everyone equally or it protects no one reliably.

Enforcement: Protocol-enforced. No surveillance mechanism exists in the protocol. Dandelion++ and encrypted P2P are mandatory.

Code enforcement: Multiple compile-time guards in src/constants.rs:

#![allow(unused)]
fn main() {
pub const ADDRESS_BLACKLIST_ENABLED: bool = false;
const _: () = assert!(!ADDRESS_BLACKLIST_ENABLED,
    "UNCONSTITUTIONAL: Bill of Rights IV & X — No address blacklisting or transaction censorship");

pub const TX_CENSORSHIP_ENABLED: bool = false;
const _: () = assert!(!TX_CENSORSHIP_ENABLED,
    "UNCONSTITUTIONAL: Bill of Rights X — Transaction censorship is permanently prohibited");
}

Dandelion++ stem-and-fluff relay is implemented in src/network/dandelion.rs. Encrypted P2P is provided by the Noise framework (snow crate) wired into the peer handshake in src/network/.


Article X — Immutability

This Constitution is permanent.

It cannot be amended. It cannot be repealed. It cannot be superseded by any governance process, community vote, developer decision, legal order, court ruling, regulatory requirement, or supermajority of any kind. There is no emergency provision. There is no sunset clause. There is no mechanism by which these articles can be weakened, suspended, or removed.

The articles of this Constitution are not starting points for negotiation. They are the final word on what CoinCync is and what it can never become.

Any protocol that violates these articles is not CoinCync — regardless of what its developers call it, regardless of what exchanges list it, regardless of how much hash power backs it, and regardless of how many users it has. A chain that removes mandatory privacy is a different chain. A chain that raises the supply cap is a different chain. A chain that introduces a developer tax is a different chain.

Users, miners, and node operators are encouraged to identify such chains clearly and to continue running the original protocol.

The only legitimate evolution of CoinCync is one that honors every article of this Constitution in full, strengthens the protections it describes, and leaves its foundations untouched.

Enforcement: Enforced by every node on the network and by the community’s right and responsibility to fork.

Code enforcement: Every article above that can be expressed as a compile-time or runtime check is backed by assertions in src/constants.rs tagged "UNCONSTITUTIONAL: Article N". Shipping a binary that violates any of them requires deliberately removing the assertion — a change that cannot happen silently because it’s in the repository’s most-watched file.


Closing Statement

These ten articles were written because code without principles is just software — and software without principles serves whoever controls it.

CoinCync was built to serve its users. Not its developers. Not its investors. Not any government or institution. Its users — every person who runs a node, mines a block, sends a transaction, or simply holds CYNC in a wallet on a device in their pocket.

Financial privacy is not a luxury. It is not a feature for criminals. It is the condition under which free people conduct their lives without accounting for themselves to institutions that did not earn that accounting. It is the difference between a tool that serves you and a tool that reports on you.

CoinCync exists because that difference matters. This Constitution exists to make sure it always will.

These are not just words. They are compiled into every node, verified by every miner, and enforced by mathematics. No person, committee, or future version of this project can undo what is written here.

CoinCync serves its users. No one else.


Ratified at Block 0. Permanent thereafter.

What you can do if a constitutional article is violated

Constitutional violations are, by definition, consensus-incompatible: any node enforcing the constitution rejects blocks that violate it. The chain that violates the constitution is not CoinCync — it’s a fork.

If a malicious release ever ships that violates a constitutional article, the recovery procedure is:

  1. Don’t upgrade. The old binary is still valid; it just won’t follow the malicious chain.
  2. Coordinate via out-of-band channels to identify the violation and the responsible release.
  3. Fork the project, rebuild from a known-good commit, and continue under a new release.

The constitution is your defense against your own future maintainers being wrong, captured, or compromised. Treat it accordingly.

See also

Bill of Rights

The Ten Guaranteed Rights of Every CoinCync User

Version: 1.0 Ratified: Block 0 (Genesis) Network: CoinCync Mainnet

The authoritative source is docs/BILL_OF_RIGHTS.md at the repository root. This page reproduces it in full for the published docs site. If this page ever drifts from the repository file, trust the repository file.

The Bill of Rights is the user-facing complement to the operator-facing Constitution. The Constitution tells node operators and developers what invariants they cannot break; the Bill of Rights tells every user of CoinCync what they are guaranteed, regardless of who is running the network.


Preamble

These rights belong to every person who uses CoinCync — regardless of who they are, where they live, how much CYNC they hold, or what they use it for.

They were not granted by developers. They cannot be revoked by developers. They exist because they are built into the protocol itself wherever mathematically possible, and because this document — like the Constitution it accompanies — is permanent.

No governance process, no community vote, no legal order, and no future development team can remove, weaken, suspend, or reinterpret these rights. Any protocol that fails to honor them is not CoinCync.

Some of these rights are enforced by mathematics — ring signatures, stealth addresses, and Pedersen commitments that make violation of the right computationally impossible. Others are enforced by community commitment and open source code that any person can audit and verify.

Both categories are listed here. Both are equally binding. The distinction between them is noted not to suggest one matters less, but to be honest about the nature of each guarantee — because honesty is foundational to everything CoinCync is built on.


Right I — The Right to Financial Privacy

Every person using CoinCync has the right to conduct financial transactions in private. The network shall provide, by default and without exception, the following protections for every transaction:

  • Ring signatures to conceal the sender among a set of decoys
  • Stealth addresses to generate one-time destinations that cannot be linked to a public address
  • Pedersen commitments to conceal transaction amounts from all parties except sender and receiver
  • Bulletproofs to prove amounts are valid without revealing them

Privacy is not opt-in. There is no transparent mode. There is no reduced-privacy transaction class. Every transaction on CoinCync is private by construction.

Enforcement: Protocol-enforced. Mathematical guarantee.

Right II — The Right to Verify Supply

Every person has the right to independently verify the total supply of CYNC at any block height without trusting any third party — not the developers, not the foundation, not any exchange.

The Pedersen commitment accumulator built into the CoinCync protocol allows any person to confirm that no coins were created outside the emission schedule, that no transaction created coins from nothing, and that the total supply matches the expected emission at the current block height.

The supply is not something you are asked to trust. It is something you can prove.

Enforcement: Protocol-enforced. Mathematical guarantee.

Right III — The Right to Run a Node

No license, permission, fee, identity verification, or approval of any kind shall ever be required to run a CoinCync node. The software shall remain open source and freely available.

No person shall be blocked from participating in the CoinCync network based on:

  • Identity or nationality
  • Geographic location or jurisdiction
  • Political affiliation or beliefs
  • Financial status or holdings
  • Any other characteristic

Participation in the CoinCync network requires nothing but software and an internet connection.

Enforcement: Community-enforced. Open source license and protocol design.

Right IV — The Right to Self-Custody

No version of the CoinCync protocol shall ever implement backdoors, key escrow, or any mechanism that allows a third party to access, freeze, seize, or redirect a user’s funds without that user’s explicit and voluntary consent.

Your keys are your coins. The protocol has no override. There is no account recovery through a central party. There is no emergency freeze mechanism. There is no kill switch.

This right exists because financial self-custody is inseparable from financial privacy. A coin whose funds can be frozen is not a private coin — it is a monitored one with extra steps.

Enforcement: Protocol-enforced. No freeze mechanism exists in the protocol.

Right V — The Right to Pseudonymous Participation

No part of the CoinCync network, community, or governance shall require identity verification to participate. Every person may interact with the network, participate in community discussions, submit governance proposals, and contribute to the codebase using a pseudonym.

This right exists because the demand for identity verification is the first step toward financial surveillance. A network that requires you to identify yourself before participating is not a private network.

Contributors who choose to participate publicly under their real names are equally welcome. The right is to choose — not to be required.

Enforcement: Community-enforced. Governance and contribution processes shall never require identity.

Right VI — The Right to Exit

Any person may export their wallet, move their funds, and leave the CoinCync network at any time and for any reason. No lockup, vesting schedule, exit fee, or protocol-level restriction shall ever be imposed on withdrawing funds.

This right also extends to the code. Because CoinCync is MIT-licensed, any person may fork the protocol, modify it, and run their own version. The right to exit includes the right to take the technology with you.

A user who cannot leave freely is not a user. They are a captive.

Enforcement: Protocol-enforced for funds. Open source license enforces code portability.

Right VII — The Right to Audit the Code

The full CoinCync codebase shall remain publicly auditable at all times. No obfuscated, closed-source, or proprietary components shall be introduced into the core protocol.

Every person has the right to:

  • Read the full source code of the node, wallet, and all supporting tools
  • Compile the software themselves from source
  • Verify that compiled binaries match published source code
  • Audit the cryptographic primitives independently
  • Publish their findings without restriction

Auditability is not a feature. It is what makes all other rights verifiable.

Enforcement: Community-enforced. MIT license and public repository.

Right VIII — The Right to Participate in Governance

Any CYNC holder may submit governance proposals, participate in protocol discussions, and vote on project-layer decisions regardless of their holdings size, identity, or standing in the community.

No minimum holding threshold shall be set so high as to effectively exclude ordinary users from governance participation. No process shall be designed to favor large holders, early contributors, or any specific class of participant over others.

Governance participation is open to everyone. The protocol belongs to its users — all of them.

Enforcement: Community-enforced. Governance procedures shall guarantee open access.

Right IX — The Right to Fee Transparency

Transaction fees shall be calculated deterministically and disclosed to users in full before any transaction is broadcast. No hidden fees, no protocol-level fee extraction to a central party, and no fee mechanisms that advantage any party over another shall ever be implemented.

A user has the right to know exactly what they are paying before they pay it. A network that obscures costs from its users is not serving its users.

Fee changes may be proposed through the CIP process but may never be implemented in a way that directs any portion of fees to developers, foundations, or any centralized entity.

Enforcement: Protocol-enforced. Deterministic fee calculation with no central extraction.

Right X — The Right Against Censorship

The CoinCync protocol shall never implement transaction blacklists, address blocking, output freezing, or any mechanism that allows the censorship of valid transactions based on origin, destination, amount, or any other characteristic.

A valid transaction — one that satisfies all cryptographic proofs and consensus rules — shall always be eligible for inclusion in a block. No miner, node, developer, or governance body shall have the ability to prevent a valid transaction from ever being mined.

This right exists because financial censorship and financial surveillance are two sides of the same coin. A network that can block transactions can control its users. CoinCync shall never control its users.

Enforcement: Protocol-enforced. No blacklist or censorship mechanism exists in the protocol.


Closing Seal

These ten rights are final. They are not subject to amendment, revision, or repeal by any person, body, or process — now or at any point in the future.

They may only be strengthened by technical improvement. They may never be weakened by any means.

Any version of the CoinCync software that removes or weakens any of these rights is not CoinCync. It is a different coin wearing the same name. Users, miners, and node operators are encouraged to identify it as such and reject it.

Ratified at Block 0. Permanent thereafter.


Document hash stored on-chain at Block 0 as proof of existence and immutability.

See also

Disclaimer

CoinCync is software, not a product. There is no company. There is no support contract. There is no insurance. There is no recovery service. The maintainers cannot help you if you lose your wallet, your password, or your seed phrase. This page is the honest version of what you’re getting and what you’re not.

What CoinCync is

  • Open-source software under the MIT license, free to use, modify, and redistribute
  • A privacy cryptocurrency with the cryptographic properties documented in Privacy model
  • A network that anyone can join by running a node and that no one controls
  • A constitution of invariants (Constitution) that the maintainers commit to never breaking

What CoinCync is NOT

  • Not a regulated financial product. CoinCync is not registered with the SEC, FinCEN, FCA, or any other financial regulator. It is not a security under the laws of any jurisdiction the maintainers are aware of, but the maintainers are not lawyers and this is not legal advice. Whether you can use CoinCync legally depends on where you live and what you do with it. Some jurisdictions have outright bans on privacy coins (China, Korea, India, Russia, others). Check your local law before using or holding CYNC.
  • Not insured. There is no FDIC, no SIPC, no Lloyd’s of London. If a bug in the code causes a loss, the maintainers have no obligation to compensate you and no money to do so even if they wanted to.
  • Not a managed service. There is no customer support. There is no help desk. There is no recovery service for lost passwords or seed phrases. If you lose your seed phrase AND your wallet password, your funds are unrecoverable. There is no exception, no special case, no backdoor.
  • Not a stable store of value. The price of CYNC against any other currency is determined by markets that may not exist yet, may be illiquid, may be manipulated, and may go to zero. The maintainers make no representation about future value.
  • Not audited (yet). The cryptographic primitives (CLSAG, Bulletproofs+, RandomX) are individually well-studied and used by other privacy coins in production. The CoinCync-specific composition has been internally reviewed but has not undergone an independent third-party audit as of this writing. A bug in the composition could in principle be a privacy or correctness flaw.
  • Not future-proof. Cryptography ages. Quantum computing advances. The CoinCync stack is built on elliptic-curve assumptions that may not hold in 30 years. Like every cryptocurrency in existence, the long-term security depends on the underlying primitives remaining unbroken.

Risks you accept by using CoinCync

By running the binary, holding CYNC, or transacting on the network, you accept these risks (this list is not exhaustive):

  1. Loss of funds through:
    • Lost seed phrase + lost password
    • Software bugs in the wallet, the node, or the cryptographic primitives
    • Hardware failure of a device storing the wallet file
    • Compromised dependencies (the Rust crates the project depends on)
    • User error (sending to the wrong address, losing the encrypted wallet file)
  2. Privacy compromise through:
    • Network-layer correlation (broadcasting from your home IP)
    • Out-of-band metadata leakage (telling someone what you sent)
    • Chainalysis-style scraping of public infrastructure
    • Future cryptographic advances that weaken the current primitives
    • Operational mistakes (running a non-vendored explorer, leaking your view key)
  3. Legal risk through:
    • Jurisdictions that ban privacy coins
    • Tax authorities that treat CYNC as taxable income or capital gains
    • Sanctions regimes that restrict transacting with certain addresses
    • Your own conduct (CoinCync is privacy-preserving, not consequence-preserving — what you do with it is on you)
  4. Network risk through:
    • 51% attacks on the proof-of-work
    • Long-range attacks during initial sync
    • Eclipse attacks on individual nodes
    • Sybil attacks on the peer discovery
    • Bugs in the consensus rules that cause forks
  5. Project risk through:
    • The maintainers losing interest, moving on, or being unable to continue
    • The project being co-opted, forked, or pressured by external parties
    • Compromise of the GitHub repository or the release signing keys

What you should do

  • Read the source. It’s MIT licensed and available at github.com/CoinCync/Coincync-Testnet-. Don’t run cryptocurrency software you haven’t reviewed.
  • Run your own node. Don’t trust public infrastructure for anything you can’t lose. Public explorers, public APIs, and public faucets are conveniences — they are not the source of truth.
  • Back up your seed phrase on paper. Two copies, two locations, at least one fireproof. The seed phrase is the only thing that can recover funds; the encrypted wallet file alone is not enough if you forget the password.
  • Use a password long enough that an offline attacker can’t brute-force it even if they get the encrypted wallet file. The Argon2id KDF makes guessing expensive but not impossible — pick a password you wouldn’t be embarrassed to write on paper.
  • Don’t transact at scale until you’ve confirmed everything works at small scale. The first transaction you send is not the time to find out the wallet is misconfigured.
  • Keep a copy of the binary you trust. Future maintainers may release updates that you don’t trust; the old binary is still valid software. Pin it.
  • Understand what you’re using before you depend on it. This is a hand-rolled privacy coin. It is not Bitcoin and it is not Monero. The risk profile is different. The audit history is shorter. The maintainer base is smaller.

Maintainer commitments

The maintainers commit to:

  • Never adding a backdoor, no matter who asks
  • Never adding a remote upgrade mechanism
  • Never adding an operator key that can override consensus
  • Never violating the constitution (and rejecting any pull request that does)
  • Publishing the source for every release
  • Documenting known issues honestly, not burying them

The maintainers explicitly do NOT commit to:

  • Continuing to maintain the project indefinitely. If the maintainers stop, the source is still there; anyone can fork it and continue.
  • Rapid response to bug reports. This is a part-time project run by volunteers.
  • Any specific release schedule.
  • Compensating users for any loss arising from the use of this software.

License

CoinCync is licensed under the MIT License. The full text is in LICENSE at the repository root. The relevant clause:

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

That’s the legal version. The plain-English version: you’re using this at your own risk, and if it breaks, you keep both pieces.

See also

  • Constitution — the rules the maintainers commit to never breaking
  • SECURITY.md at the repository root — the responsible-disclosure process for security issues

Glossary

Terms used throughout the CoinCync documentation. Defined to be approximately consistent with how Monero, Zcash, and Pirate Chain use them, with footnotes where CoinCync diverges.


Anchor. A deterministic chain-history value mixed into the proof-of-work preimage so PoW solutions can’t be precomputed against a hypothetical future chain. Computed by compute_full_anchor(prev_hash, height, timestamp).

Anonymity set. The total population of unspent outputs that could plausibly be the real input of a given ring signature. CoinCync’s anonymity set is the entire UTXO pool minus already-spent outputs. Larger = better privacy.

Atomic units. The smallest indivisible unit of CYNC. 1 CYNC = 10^12 atomic units. All on-chain amounts are stored as atomic units; user-facing displays convert to CYNC.

BIP9 signaling. A miner-driven soft-fork activation mechanism originally from Bitcoin. Miners signal readiness for a CoinCync Improvement Proposal (CIP) by setting a bit in their coinbase. Once SIGNAL_THRESHOLD (1814 of 2016 blocks ≈ 90%) signal in a SIGNAL_WINDOW (2016 blocks), the fork locks in and activates at the start of the next window.

Blinding factor. The random scalar b in a Pedersen commitment C = a·G + b·H. Without b, the commitment to amount a would be deterministic and an attacker could brute-force it. The blinding makes commitments hiding.

Block reward. The amount of CYNC paid out as the coinbase output of a new block. Computed from the emission curve — a mountain curve starting at 143 CYNC/block in year 0–1, decaying through anchor points at years 5 (86), 10 (28), and 13 (7), and flooring at the 1 CYNC/block tail from year 20 onward. Total supply is capped at 250,000,000 CYNC by Constitution Article I.

Bulletproofs+. A range-proof scheme that proves a committed amount falls in [0, 2^64) without revealing it. CoinCync uses Bulletproofs+ (the 2022 improvement on the original Bulletproofs), which is ~96 bytes shorter and ~10% faster to verify.

Caddy. The reverse-proxy / TLS-terminator used in front of every public CoinCync HTTP service (explorer, API, landing). Auto-provisions Let’s Encrypt certs, enforces rate limits, applies security headers. Lives in deploy/explorer/, deploy/api/, and deploy/landing/.

CIP. CoinCync Improvement Proposal. An optional protocol change activated via BIP9 signaling. Listed in src/consensus/fork_signal.rs::DEPLOYMENTS. Does not include constitutional invariants — those cannot be CIP’d.

CLSAG. Concise Linkable Spontaneous Anonymous Group signature. The ring signature scheme CoinCync uses for transaction inputs. Provides sender anonymity (you know “one of these N signed” but not which) plus linkability (the same input can’t be spent twice without producing the same key image).

Commitment. See Pedersen commitment.

Confidential transaction. A transaction where amounts are hidden behind Pedersen commitments and proven valid via range proofs. Every CoinCync transaction is confidential — there is no transparent variant.

Constitution. The set of invariants that will never be broken by the CoinCync maintainers. Documented in Constitution and the repository-root CONSTITUTION.md.

CYNC. The native asset of CoinCync. Singular and plural — “1 CYNC”, “100 CYNC”.

Decoy. A ring member that is NOT the real input. Selected from the chain’s anonymity set by the wallet’s decoy selector (src/crypto/ring_selection.rs) using an age-weighted distribution that matches Monero’s current best practice.

Domain separator. A unique tag prefix on a hash preimage that prevents two hashes computed with different tags from colliding. CoinCync uses domain separators on every protocol hash (coincync/header/v1, coincync/tx-sign/v1, etc.). See Privacy model → Hash domain separation.

Dust output. A UTXO with an amount so small that the fee to spend it exceeds the value. CoinCync’s mempool policy doesn’t formally define a dust threshold; in practice, amounts below ~1000 atomic units are uneconomic to spend.

Effective ring size. The actual ring size used at a given height, possibly clamped above the constitutional minimum if the anonymity set isn’t large enough yet. Computed by effective_ring_size(height, available_outputs) in src/constants.rs.

Federation. Running multiple independent hosts of the same service (explorer, API) under different hostnames (explorer.coincync.network, explorer2.coincync.network, …) so the system has no single point a regulator or DDoS can lean on. The CoinCync alternative to putting everything behind a CDN. See Federation & DDoS.

Genesis block. Block 0 — the hardcoded first block of the chain. The genesis hash is a constant in src/testnet.rs::TESTNET_GENESIS_HASH and src/mainnet.rs::MAINNET_GENESIS_HASH. init_genesis() verifies the computed hash matches the constant; a mismatch is a fatal error.

Hashrate. The rate at which a miner computes PoW hashes, measured in hashes per second (H/s). RandomX hashrates are typically in the kH/s to MH/s range on commodity CPUs.

HSM. Hardware security module. CoinCync does not natively integrate with HSMs in 1.0; wallet keys are stored as encrypted files. HSM support is a future research item.

IBD. Initial Block Download. The process of a fresh node downloading and verifying every block from genesis to the current chain tip. On a synced testnet that’s ~12k blocks; the IBD takes ~10 minutes on commodity hardware.

Key image. The cryptographic value I = x · H_p(x · G) derived from the spend key x of a CLSAG ring input. Published with the signature so the network can detect double-spends without learning which output was spent. Same key image twice = double-spend = block rejected.

LWMA. Linearly Weighted Moving Average. The difficulty-adjustment algorithm CoinCync uses (same as Monero). Responsive to short-term hashrate changes, resistant to time-warp attacks. Implementation: src/consensus/difficulty.rs::calculate_difficulty.

Mempool. The pool of pending (unconfirmed) transactions held in memory by every node. New blocks are built from mempool contents. The mempool runs the same crypto verifiers consensus does — there is no fast path that bypasses verification.

MimbleWimble cut-through (MW cut-through). Phase 2 chain-compression technique where historical transaction graphs collapse, leaving only unspent commitments. Compresses the chain and eliminates traceable transaction history. Activates via CIP-006.

Mnemonic phrase. A 24-word BIP39 phrase that encodes the wallet’s master seed. The only way to recover a wallet if the encrypted file is lost. Memorize, paper-back, or both. Never digital, never online.

Nullifier. See key image. (Different name in Zcash; the concept is the same.)

Pedersen commitment. An additively homomorphic commitment scheme. C = a·G + b·H commits to amount a with blinding b. Two commitments add: C(a) + C(b) = C(a+b). CoinCync uses Pedersen commitments for amounts so the network can verify the balance equation sum(inputs) = sum(outputs) + fee without learning any individual amount.

PoW. Proof of work. CoinCync uses RandomX, and only RandomX, by Constitution Article V. Single memory-hard CPU-biased algorithm, same as Monero. No rotation, no hybrid, no PoS transition path. See Consensus & PoW for the rationale on why single-algorithm is stronger than multi-algo rotation.

Privacy policy. The consensus rule that rejects transparent transactions at the consensus layer (not just the mempool). Implemented in src/consensus/privacy_policy.rs::enforce_privacy_policy. Even a maliciously-crafted block trying to include a transparent transaction is rejected by validate_block.

RandomX. A CPU-friendly, ASIC-resistant, memory-hard proof-of-work algorithm designed for Monero, used as-is by CoinCync as one of the three rotating PoW algorithms per Article V. Uses a 256 MB scratchpad and a virtual machine that interprets randomly-generated bytecode — both of which favor general-purpose CPUs over fixed-function hardware.

Range proof. A zero-knowledge proof that a committed amount falls in a given range without revealing the amount. CoinCync uses Bulletproofs+ for the [0, 2^64) range that’s standard for currency amounts.

Reorg. Chain reorganization. When a node sees a competing fork that has more total work than its current tip, it rolls back blocks on the old tip and applies blocks from the new chain. Implementation: src/chain.rs::add_block.

Ring signature. See CLSAG.

Seed phrase. See mnemonic phrase.

Selective disclosure. Sharing your view key with an exchange or auditor so they can verify your incoming and outgoing payments without being able to spend funds. The view key is read-only; the spend key remains private.

Signing hash. The cryptographic hash that the CLSAG ring signature is computed over. Constructed by Transaction::compute_signing_hash — the single source of truth for the preimage. Both the wallet’s signer and the consensus verifier funnel through this helper. See Transaction format → signing hash.

Spend key. The secret key that authorizes spending an output. Derived from the wallet’s master seed. Never share this. Loss of this key = loss of access to funds.

Stealth address. A receiver-side address scheme where every payment to the same (spend_pub, view_pub) pair generates a fresh, unlinkable one-time output public key. Only the recipient (with their view key) can recognize that an output is theirs.

Tail emission. A constant block reward that kicks in after the early issuance phase ends, providing a permanent baseline mining incentive. CoinCync’s tail is 1 CYNC/block starting at year 20 (height ≈ 5,256,000). Tail issuance continues until the 250 M supply cap is reached — see Emission curve → Supply cap interaction with the tail.

Target block time. The intended interval between blocks. 120 seconds (2 minutes) for CoinCync. The LWMA difficulty adjustment keeps actual intervals close to this target.

Testnet. A live but valueless network used for development, testing, and staging. CoinCync’s testnet has the same code as mainnet but a different network magic, a different genesis, and a faucet that gives free CYNC away.

Tor hidden service. A way of publishing an HTTP service inside the Tor network so visitors reach it without going through a Tor exit node. CoinCync supports .onion mirrors for the explorer and API alongside the clearnet sites. See Tor hidden services.

UTXO. Unspent transaction output. The fundamental unit of state in a CoinCync wallet — every UTXO is an output that the wallet’s view key recognizes and that hasn’t been spent. The wallet’s balance is the sum of unspent UTXOs.

View key. A scalar that lets a holder scan the chain for outputs to a specific address WITHOUT being able to spend them. Enables selective disclosure: show your view key to your accountant, prove a transaction is yours to a regulator, etc., all without giving up spending authority.

View tag. A 1-byte filter on every output that lets a wallet reject 255/256 outputs without doing a full ECDH computation — speeds up scanning by ~250×. Optional; activated via CIP-001.

Hostname Reference

Updated: May 2, 2026 — public testnet relaunched on the new consensus rules (805c07d). Explorer host moved to LON, NYC3 took the active miner role, ATL demoted to seed-only.

Public Endpoints

SubdomainRole
explorer.coincync.networkBlock explorer (front-end + RPC proxy)
api.coincync.networkPublic JSON-RPC + REST API proxy
coincync.network (apex)Landing page + active miner
docs.coincync.networkDocumentation site

Resolve any of the above with dig to see the host serving it.

Testnet Fleet (logical names)

Active on the new consensus chain (genesis 41f970df…):

RegionRole
NYC3Active miner + landing + docs
LONPublic explorer host
TORSeed1 + public API
RICMirror explorer + relay
NYC1Mempool + relay
FRAMempool + relay
ATLSeed2 + relay (former miner)
AMSSeed3 + relay
SYDRelay

Excluded from this redeploy (intentional):

RegionReason
SFODivergent local history including a --no-p2p-encryption commit. Skipped pending operator review of whether that flag is load-bearing. Stays on its previous binary until decision is made.

Mining: NYC3 runs coincync-miner.service with 2 vCPUs, FULL_MEM RandomX. The service requires COINCYNC_RPC_API_KEY in its environment block to authenticate to the local node’s RPC; the value is sourced from the matching coincync-node.service drop-in.

Port Map

PortProtocolPurpose
28080TCPP2P (all nodes)
28081TCPRPC (localhost on production; bearer auth required for all POST when bound non-loopback)
80TCPHTTP (nginx on explorer/api/landing hosts)
443TCPHTTPS (nginx on explorer/api/landing hosts)

Binary Locations

All nodes: /usr/local/bin/coincync-node, /usr/local/bin/coincync-wallet, /usr/local/bin/coincync-tui-miner. Hosts running the headless miner also have /usr/local/bin/coincync-miner (NYC3 currently).

Avoid: the old binary path /root/coincync-new/target/release/... is deprecated. A /root/auto-sync.sh cron used to respawn nodes from that path every 30 min; the cron has been disabled fleet-wide as of the 2026-05-02 redeploy. Do not re-enable without first updating the script to use /usr/local/bin/.

systemd Services

All nodes use standardized systemd services. See Deploying a node.

  • coincync-node.service — node daemon, auto-restart, RPC bearer key from drop-in
  • coincync-miner.service — headless miner (NYC3 only by default), depends on coincync-node

nginx Proxy (by role)

  • Explorer host (LON): / static explorer at /var/www/explorer/, /api/testnet and /api/mainnet proxied to local RPC, /health/<node> fleet health fan-out with shared bearer key
  • API host (TOR): /rpc, /rpc/mainnet, /v1/* public API routes
  • Landing host (NYC3): coincync.network, docs.coincync.network, mirrors.json
  • Mirror explorer (RIC): Same /var/www/explorer/ layout as LON; available as a failover endpoint via the operator inventory.

Redeploy procedure

Operator-only — requires SSH access to fleet hosts. Specific host addresses live in the private operator inventory, not in this public doc.

# Sync canonical source to a fleet host (operator laptop has SSH key, fleet
# does not need GitHub creds)
git push ssh://root@<host>/opt/coincync main

# On each fleet host (with --rest-bind binding RPC to 0.0.0.0):
SKIP_PULL=1 DATA_DIR=/data/seed1 bash /opt/coincync/deploy/ops/redeploy-fleet.sh

# Frontend HTML deploy is SEPARATE — only needed on the explorer host:
bash /opt/coincync/deploy/explorer/deploy-explorer.sh   # on LON

Known gotcha: the redeploy script rebuilds Rust binaries via cargo build which assumes ~/.cargo/bin/cargo exists for the repo owner. Hosts that lack rustup (FRA, AMS at the time of the 2026-05-02 redeploy) cannot run the script — instead, SCP the built binary from a working fleet host using SSH aliases configured in ~/.ssh/config:

scp lon:/usr/local/bin/coincync-node fra:/usr/local/bin/coincync-node.new
ssh fra 'mv /usr/local/bin/coincync-node.new /usr/local/bin/coincync-node && \
         chmod +x /usr/local/bin/coincync-node && \
         mv /data/seed1/testnet /data/seed1/testnet.wiped.$(date -u +%Y%m%dT%H%M%SZ) && \
         systemctl restart coincync-node'