Scarcity is a zero-cost, serverless cryptocurrency that achieves double-spend prevention without blockchains, mining, or global ledgers. It combines gossip-based nullifier sets with threshold timestamping to create a truly decentralized value transfer protocol.
Traditional cryptocurrencies require one of three sacrifices:
- Centralization (trusted servers)
- Cost (gas fees, mining rewards, staking)
- Publicity (transparent ledgers, wallet addresses)
Scarcity refuses all three. It's designed from first principles with these constraints:
-
Privacy-Preserving: With Freebird, sender/receiver unlinkable
-
Serverless: With HyperToken, P2P without catastrophic infrastructure
-
Provable: With Witness, cryptographically verifiable
-
Zero-Cost & Zero-Waste: No gas fees, no mining, no staking — and ~12 million times less energy than Bitcoin (details)
-
Anonymous: No wallet addresses, no on-chain history
-
Freebird: Anonymous authorization infrastructure
-
HyperToken: Distributed state synchronization
-
Witness: Threshold signature timestamping
Are you getting it?
Scarcity uses ~12 million times less energy per transaction than Bitcoin.
Traditional cryptocurrencies require massive energy expenditure for mining (Bitcoin: ~150 TWh annually) or validator infrastructure (Ethereum: ~0.01 TWh annually). Scarcity eliminates this waste entirely through gossip-based double-spend prevention and threshold timestamping.
Energy per transaction:
- Bitcoin: ~1,200 kWh (could power a US home for 40+ days)
- Ethereum: ~0.02 kWh (comparable to a Google search)
- Scarcity: ~0.0001 kWh (comparable to sending a message)
No mining. No proof-of-work. No global ledger synchronization. Just cryptography and gossip.
📊 Read the full environmental impact analysis →
Every cryptocurrency must prevent the same token from being spent twice. The traditional solution is a global ledger (blockchain) that everyone agrees on. But this requires:
- Mining/staking (cost)
- Public transaction history (no privacy)
- Consensus mechanisms (complexity)
Instead of a global ledger, Scarcity uses:
- Nullifier Sets - Unique "spent" markers gossipped P2P
- Witness Timestamping - Ground truth for disputes
- Probabilistic Acceptance - You don't need 100% certainty instantly
Scarcity implements a novel economic primitive The Church calls Lazy Demurrage—a system where currency behaves less like immutable gold and more like metabolic energy.
By enforcing a Rolling Validity Window (default ~1.5 years), Scarcity creates a high-velocity economy that automatically prunes dead capital. This mechanism draws on three historical and biological precedents:
Traditional demurrage charges a complex negative interest rate (e.g., -1% per month). Scarcity implements a computationally efficient Validity Cliff.
A token retains 100% of its value for its entire validity window. However, if it is not transferred (refreshed) before the window expires, its value drops instantly to 0. This acts as a decentralized form of Escheatment. Instead of a central bank seizing dormant accounts, the network itself reclaims the storage resources, and the token becomes unspendable static.
Unlike Bitcoin, which treats coins as immutable rocks that can sit in a desert for a thousand years, Scarcity treats tokens like ATP in a biological cell. Money is potential energy that must be used or regenerated to persist.
This model effectively eliminates the "Lost Coin" problem. If keys are lost, the network does not carry the burden of that unspendable UTXO forever. The economy metabolizes its own history, ensuring the ledger size remains bounded
This architecture effectively digitizes the Wörgl Experiment (1932) and Silvio Gesell's concept of Freigeld ("Free Money"). Just as the citizens of Wörgl had to affix a stamp to their banknotes monthly to keep them valid, Scarcity users must cryptographically "refresh" their funds by moving them to a new secret. This structural disincentive to hoarding forces circulation and economic activity.
┌─────────────────────────────────────────────────┐
│ ScarbuckToken: Value transfer primitive │
│ • Freebird: Anonymous ownership proofs │
│ • Nullifiers: Unique spend identifiers │
│ • Transfer packages with commitments │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ NullifierGossip: Fast propagation │
│ • P2P broadcast of spent nullifiers │
│ • Local nullifier sets per peer │
│ • Epidemic-style forwarding │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ TransferValidator: Acceptance logic │
│ • Gossip check (fast, probabilistic) │
│ • Witness check (slow, deterministic) │
│ • Confidence scoring (tunable risk) │
└─────────────────────────────────────────────────┘
const token = ScarbuckToken.mint(100, freebird, witness, gossip);Creates a new token with:
- Random token ID
- Secret ownership key
- Amount (arbitrary units)
const pkg = await token.transfer(recipientPublicKey);- Generate nullifier:
H(secret || tokenId || timestamp) - Blind recipient: Use Freebird VOPRF to hide recipient identity
- Create ownership proof: Prove you can spend without revealing secret
- Timestamp: Get Witness attestation for ordering
- Broadcast: Gossip nullifier to all peers
const newToken = await ScarbuckToken.receive(
pkg,
recipientSecret,
freebird,
witness,
gossip
);- Verify proof: Check Witness attestation is valid
- Validate ownership: Check sender's authorization
- Create new token: Generate new secret for recipient
const result = await validator.validateTransfer(pkg);Fast Path (instant):
- Check gossip network for nullifier
- Reject if seen (double-spend)
Witness Path (deterministic):
- Query Witness federation
- Reject if proven double-spend
Wait Period (tunable):
- Allow gossip propagation (default: 5 seconds)
- Check again after waiting
Confidence Scoring:
confidence = peerScore + witnessScore + timeScore
= (peers/10) + (depth/3) + (wait/10s)
= up to 0.5 + up to 0.3 + up to 0.2
= max 1.0 (perfect certainty)
Freebird Integration:
- Recipient commitments use VOPRF blinding
- Issuer cannot correlate issuance with redemption
- Verifier cannot link transfers to identities
No Addresses:
- No wallet addresses on-chain
- No transaction graph to analyze
- No cluster analysis possible
Bearer Tokens:
- Possession = ownership
- No identity required to hold
- No KYC/AML by design
Gossip Privacy:
- Nullifiers are random bytes
- No correlation to sender/receiver
- Network-level privacy via Tor/mixnets
Protected Against:
- Double-spending (via nullifier sets + Witness)
- Forgery (via Freebird's unforgeable VOPRF tokens)
- Replay attacks (nullifiers are single-use)
- Network partitions (gossip + eventual consistency)
- Sybil attacks on token issuance (via Freebird's pluggable Sybil resistance: invitation system, PoW, rate limiting, WebAuthn)
Not Protected Against:
- Token theft (secure your secrets! Use TLS for token transmission)
- Network-level correlation attacks (timing analysis by network observers - use Tor/mixnets)
- Quantum adversaries (ECDLP-based cryptography)
- Legal seizure (cash-like bearer instrument)
Gossip Network:
- Assumes at least some honest peers
- More peers = higher confidence
- Can operate with zero peers (degrade to Witness-only)
Witness Federation:
- Threshold assumption (< T witnesses collude)
- Recommended: 3-of-5 or 5-of-7
- Cross-network anchoring for hardening
Freebird:
- Issuer and verifier must be separate (timing attack mitigation)
- Discrete log problem hardness (P-256)
- TLS required for token transmission (prevents bearer token theft)
- Sybil resistance configured at deployment (invitation/PoW/rate-limit/WebAuthn/combined)
- Rate limiting without user tracking (via anonymous token consumption + nullifier sets)
Scarcity implements defense-in-depth against nullifier flooding attacks through three layers:
- Layer 1 (Network): Peer reputation scoring and rate limiting (leaky bucket)
- Layer 2 (Validation): Proof-of-work challenges and strict timestamp windows
- Layer 3 (Economic): Freebird Sybil resistance and ownership proof verification
For detailed information on the vulnerability, attack vectors, and mitigation strategies, see:
📖 SECURITY.md - Complete security documentation with configuration examples and attack cost analysis.
For Docker Setup (Recommended):
- Docker Engine 20.10+ or Docker Desktop
- Docker Compose V2
- 2GB+ RAM available
- 1GB+ disk space
For Local Development:
- Node.js 20+ (LTS recommended)
- npm 9+ (comes with Node.js)
- Git
- 2GB+ RAM available
- 500MB+ disk space
For Building from Source:
- All Local Development requirements
- Python 3.x (for native module compilation)
- C++ compiler toolchain:
- Linux:
build-essentialpackage (gcc, g++, make) - macOS: Xcode Command Line Tools (
xcode-select --install) - Windows: Visual Studio Build Tools or Windows Build Tools
- Linux:
- ✅ Linux (Ubuntu 20.04+, Debian 11+, Fedora 35+, Arch)
- ✅ macOS (11+ Big Sur or newer)
- ✅ Windows (10/11 with WSL2 recommended for Docker)
- Ports Used:
3000: HyperToken Relay WebSocket (Docker) / Web Wallet UI (Local)3001: Nullscape Explorer8080: Witness Gateway HTTP (Docker)8081: Freebird Issuer (Docker)8082: Freebird Verifier (Docker)
Note: When running both Docker services and local Web Wallet, port 3000 will be used by HyperToken Relay. Run the Web Wallet on a different port: PORT=3333 npm run web
- Tor (for anonymous networking):
torpackage installed and running on port 9050 - SQLite3 CLI tools (for manual database inspection)
Follow these steps to get Scarcity running on your machine:
- Install Docker Desktop or Docker Engine + Docker Compose
- Clone the repository:
git clone https://github.com/flammafex/scarcity.git cd scarcity - Run the entire stack:
docker compose up --build --abort-on-container-exit
- Verify success - You should see test output ending with "All tests passed! ✓"
- Try the CLI:
docker compose run --rm scarcity-tests ./dist/src/cli/index.js wallet list
That's it! Docker handles all dependencies, networking, and configuration automatically.
- Install Node.js 20+ from nodejs.org
- Verify installation:
node --version # Should show v20.x.x or higher npm --version # Should show 9.x.x or higher
- Install build tools (for native dependencies):
- Linux:
sudo apt install build-essential(Ubuntu/Debian) or equivalent - macOS:
xcode-select --install - Windows: Install Visual Studio Build Tools
- Linux:
- Clone the repository:
git clone https://github.com/flammafex/scarcity.git cd scarcity - Install dependencies:
npm install
⚠️ Ifbetter-sqlite3fails to build, ensure you have C++ build tools installed
- Build the project:
npm run build
- Set up external services (required for tests):
# Start all infrastructure with Docker (required) docker compose up -d freebird-issuer freebird-verifier witness-gateway hypertoken-relay - Run tests:
npm test- Tests REQUIRE external services (Docker recommended)
- You should see "All tests passed! ✓"
- Or set up each service manually (advanced - see QUICKSTART.md)
- Try the CLI:
./dist/src/cli/index.js wallet create alice ./dist/src/cli/index.js wallet list
- Optional: Install CLI globally:
npm install -g . scar wallet list
- Start the Web Wallet:
npm run web # Opens on http://localhost:3000 - Start the Nullscape Explorer:
npm run explorer # Opens on http://localhost:3001
If you encounter issues, see TROUBLESHOOTING.md for common problems and solutions.
Run the entire Scarcity ecosystem (Freebird, Witness, HyperToken) and integration tests with a single command. No manual setup required.
This command builds Scarcity, pulls the latest dependencies (Freebird/Witness), starts the p2p network, and runs the integration suite.
docker compose up --build --abort-on-container-exitTo run the infrastructure in the background and execute CLI commands manually:
# Start infrastructure (Freebird, Witness, Relay) in background
docker compose up -d freebird-issuer freebird-verifier witness-gateway hypertoken-relay
# Run the CLI against the local docker network
docker compose run --rm scarcity-tests ./dist/src/cli/index.js wallet listFor detailed deployment scenarios, see QUICKSTART.md.
// Single issuer (backward compatible)
const freebird = new FreebirdAdapter({
issuerEndpoints: ['https://issuer.example.com'],
verifierUrl: 'https://verifier.example.com'
});
// Multi-issuer for redundancy (if one fails, tries next)
const freebird = new FreebirdAdapter({
issuerEndpoints: [
'https://issuer1.example.com',
'https://issuer2.example.com',
'https://issuer3.example.com'
],
verifierUrl: 'https://verifier.example.com'
});
// Blind recipient public key using P-256 VOPRF
const commitment = await freebird.blind(recipientKey);
// Issue anonymous token with DLEQ proof verification
// With multiple issuers: tries each sequentially until one succeeds
const token = await freebird.issueToken(commitment);
// Create unforgeable Schnorr ownership proof (bound to nullifier)
const proof = await freebird.createOwnershipProof(secret, nullifier);P-256 VOPRF (Verifiable Oblivious Pseudorandom Function):
- Production-ready cryptographic blinding with DLEQ proofs
- Anonymous token issuance without revealing identity
- Verifiable: DLEQ proof ensures issuer used correct secret key
- Oblivious: Issuer cannot link token issuance to redemption
- Based on RFC 9497 and hash-to-curve (RFC 9380)
Issuer Redundancy:
- Multiple Endpoints: Configure multiple issuers for high availability
- Sequential Fallback: If one issuer fails, automatically tries the next
- Independent Verification: Each issuer's DLEQ proof is verified locally
- Trust Policy: For multi-issuer trust requirements, configure TrustPolicy on the Freebird verifier side
Sybil Resistance Mechanisms:
- Invitation System: Cryptographically signed invites with ban-trees and reputation tracking
- Proof of Work: Configurable computational cost requirements
- Rate Limiting: Anonymous token consumption tracking via nullifier sets (no user tracking)
- WebAuthn/FIDO2: Hardware-backed identity verification (optional)
- Combined Defense: Multiple mechanisms can be layered for stronger protection
- Configured via deployment environment (invitation/PoW/rate-limit/WebAuthn/combined/none)
const witness = new WitnessAdapter({
gatewayUrl: 'https://witness.example.com',
networkId: 'scarcity-mainnet'
});
// Timestamp transfer package
const attestation = await witness.timestamp(packageHash);
// Verify attestation (supports both Ed25519 and BLS12-381 signatures)
const valid = await witness.verify(attestation);BLS Signature Aggregation:
- Supports BLS12-381 aggregated signatures (50% bandwidth savings)
- 3 witnesses: 96 bytes (BLS) vs 192 bytes (Ed25519)
- Local verification with
@noble/curveslibrary - Automatic fallback to gateway verification
- Compatible with Witness federation modes
const hypertoken = new HyperTokenAdapter({
relayUrl: 'ws://relay.example.com:8080'
});
await hypertoken.connect();
// Create peer connections for gossip
// Connections automatically upgrade from WebSocket to WebRTC for lower latency
const peers = [
hypertoken.createPeer(),
hypertoken.createPeer(),
hypertoken.createPeer()
];
gossip.addPeer(peers[0]);Hybrid Architecture (WebSocket + WebRTC):
- Starts with WebSocket relay for initial connection and signaling
- Automatically upgrades to WebRTC DataChannel for direct P2P (lower latency)
- Falls back to WebSocket gracefully if WebRTC fails (NAT traversal issues)
- TURN relay support for restrictive network environments
- Transparent to the gossip protocol - same API for both transports
Privacy-Enhanced Connectivity:
Scarcity supports Tor hidden services (.onion addresses) for maximum privacy and censorship resistance. All HTTP/HTTPS integrations (Freebird, Witness) automatically route through Tor's SOCKS5 proxy when connecting to .onion addresses.
import { FreebirdAdapter, WitnessAdapter, configureTor } from 'scarcity';
// Option 1: Global Tor configuration
configureTor({
proxyHost: 'localhost',
proxyPort: 9050, // Default Tor SOCKS port
forceProxy: false // Only use Tor for .onion addresses
});
// Option 2: Per-adapter configuration
const freebird = new FreebirdAdapter({
issuerUrl: 'http://yourissuer123456.onion',
verifierUrl: 'http://yourverifier789.onion',
tor: {
proxyHost: 'localhost',
proxyPort: 9050
}
});
const witness = new WitnessAdapter({
gatewayUrl: 'http://yourwitness456.onion',
tor: {
proxyHost: 'localhost',
proxyPort: 9050
}
});Privacy Stack:
- IP Privacy: Tor hides your IP address via 3-hop onion routing
- Transaction Privacy: Freebird VOPRF makes sender/receiver unlinkable
- Network Privacy: HyperToken P2P eliminates central servers
- Temporal Privacy: Witness timestamps provide ordering without revealing identity
- Censorship Resistance: .onion addresses cannot be blocked or taken down
Setting up Tor:
# Install Tor
sudo apt install tor # Ubuntu/Debian
brew install tor # macOS
# Start Tor service
sudo service tor start # Ubuntu/Debian
brew services start tor # macOS
# Check status
curl --socks5 localhost:9050 https://check.torproject.org/Running your own .onion services:
- Configure hidden service in
/etc/tor/torrc:HiddenServiceDir /var/lib/tor/scarcity-issuer/ HiddenServicePort 8081 127.0.0.1:8081 - Restart Tor:
sudo service tor restart - Get your .onion address:
cat /var/lib/tor/scarcity-issuer/hostname - Use the
.onionaddress in your Scarcity configuration
Graceful Degradation:
- Automatically falls back to clearnet if Tor is unavailable
- Warns when .onion address detected but Tor not configured
- No crashes or failures - always functional
import {
ScarbuckToken,
NullifierGossip,
TransferValidator,
FreebirdAdapter,
WitnessAdapter,
HyperTokenAdapter
} from 'scarcity';
// Initialize infrastructure
const freebird = new FreebirdAdapter({
issuerEndpoints: ['https://issuer.example.com'],
verifierUrl: 'https://verifier.example.com'
});
const witness = new WitnessAdapter({
gatewayUrl: 'https://witness.example.com'
});
const hypertoken = new HyperTokenAdapter({
relayUrl: 'ws://relay.example.com:8080'
});
await hypertoken.connect();
// Create gossip network
const gossip = new NullifierGossip({ witness });
// Add peers
const peers = [
hypertoken.createPeer(),
hypertoken.createPeer(),
hypertoken.createPeer()
];
peers.forEach(peer => gossip.addPeer(peer));
// Create validator
const validator = new TransferValidator({
gossip,
witness,
waitTime: 5000, // 5 second wait
minConfidence: 0.7 // 70% confidence required
});
// Mint a token
const token = ScarbuckToken.mint(100, freebird, witness, gossip);
// Transfer to recipient
const recipientKey = { bytes: new Uint8Array(32) }; // recipient's public key
const transferPkg = await token.transfer(recipientKey);
// Recipient validates
const result = await validator.validateTransfer(transferPkg);
if (result.valid) {
console.log(`Transfer accepted with ${(result.confidence * 100).toFixed(0)}% confidence`);
// Receive the token
const recipientSecret = new Uint8Array(32); // recipient's secret
const receivedToken = await ScarbuckToken.receive(
transferPkg,
recipientSecret,
freebird,
witness,
gossip
);
console.log('Token received!', receivedToken.getMetadata());
} else {
console.error(`Transfer rejected: ${result.reason}`);
}// Fast validation (instant, lower confidence)
const fastResult = await validator.fastValidate(transferPkg);
// Deep validation (30s wait, maximum confidence)
const deepResult = await validator.deepValidate(transferPkg, 30_000);
// Custom confidence threshold
validator.setMinConfidence(0.9); // Require 90% confidenceSplit a single token into multiple smaller tokens with specified amounts.
import { ScarbuckToken } from 'scarcity';
// Mint a token
const token = ScarbuckToken.mint(100, freebird, witness, gossip);
// Split into 3 parts
const splitPkg = await token.split(
[30, 40, 30], // amounts (must sum to 100)
[recipient1Key, recipient2Key, recipient3Key] // one key per split
);
// Recipients receive their splits
const token1 = await ScarbuckToken.receiveSplit(
splitPkg,
recipient1Secret,
0, // split index
freebird,
witness,
gossip
);Properties:
- Amounts must sum to original token amount
- Each split gets a new token ID
- Original token is spent atomically
- All splits are created in a single timestamped transaction
Combine multiple tokens into a single larger token.
import { ScarbuckToken } from 'scarcity';
// Create multiple tokens
const token1 = ScarbuckToken.mint(30, freebird, witness, gossip);
const token2 = ScarbuckToken.mint(40, freebird, witness, gossip);
const token3 = ScarbuckToken.mint(30, freebird, witness, gossip);
// Merge into single token
const mergePkg = await ScarbuckToken.merge(
[token1, token2, token3],
recipientKey
);
// Recipient receives merged token
const mergedToken = await ScarbuckToken.receiveMerge(
mergePkg,
recipientSecret,
freebird,
witness,
gossip
);
console.log(mergedToken.getMetadata().amount); // 100Properties:
- All source tokens must be unspent
- All source tokens are spent atomically
- New token ID is generated for merged result
- Maintains double-spend prevention across all inputs
Transfer a token to multiple recipients atomically in a single transaction.
import { ScarbuckToken } from 'scarcity';
const token = ScarbuckToken.mint(100, freebird, witness, gossip);
// Transfer to 3 recipients atomically
const multiPartyPkg = await token.transferMultiParty([
{ publicKey: alice, amount: 30 },
{ publicKey: bob, amount: 40 },
{ publicKey: carol, amount: 30 }
]);
// Each recipient receives their portion
const aliceToken = await ScarbuckToken.receiveMultiParty(
multiPartyPkg,
aliceSecret,
0, // recipient index
freebird,
witness,
gossip
);Use Cases:
- Payroll disbursements
- Revenue sharing
- Multi-party settlements
- Group payments
Properties:
- All-or-nothing atomicity
- Single nullifier for source token
- Each recipient gets unique token ID
- Amounts must sum to source token amount
Hash Time-Locked Contracts enable conditional payments based on cryptographic secrets or time constraints.
import { ScarbuckToken, Crypto, type HTLCCondition } from 'scarcity';
// Create a secret preimage
const preimage = Crypto.randomBytes(32);
const hashlock = Crypto.hashString(Crypto.toHex(preimage));
// Create hash-locked transfer
const condition: HTLCCondition = {
type: 'hash',
hashlock
};
const htlcPkg = await token.transferHTLC(
recipientKey,
condition
);
// Recipient unlocks with preimage
const receivedToken = await ScarbuckToken.receiveHTLC(
htlcPkg,
recipientSecret,
preimage, // must match hashlock
freebird,
witness,
gossip
);Use Cases:
- Atomic swaps
- Cross-chain exchanges
- Trustless escrow
- Payment channels
// Timelock expires in 1 hour
const timelock = Date.now() + 3600_000;
const condition: HTLCCondition = {
type: 'time',
timelock
};
const htlcPkg = await token.transferHTLC(
recipientKey,
condition,
refundKey // required for time-locked HTLCs
);
// Recipient can claim before expiry
if (Date.now() < timelock) {
const receivedToken = await ScarbuckToken.receiveHTLC(
htlcPkg,
recipientSecret,
undefined,
freebird,
witness,
gossip
);
}
// Sender can refund after expiry
if (Date.now() >= timelock) {
const refundedToken = await ScarbuckToken.refundHTLC(
htlcPkg,
refundSecret,
freebird,
witness,
gossip
);
}Use Cases:
- Time-delayed payments
- Subscription renewals
- Conditional bounties
- Deadline-based escrow
Transfer tokens between different Witness federations while maintaining security guarantees.
import { FederationBridge, WitnessAdapter, NullifierGossip } from 'scarcity';
// Setup two federations
const sourceFederation = {
witness: new WitnessAdapter({
gatewayUrl: 'http://federation-a.example.com',
networkId: 'federation-a'
}),
gossip: new NullifierGossip({ witness })
};
const targetFederation = {
witness: new WitnessAdapter({
gatewayUrl: 'http://federation-b.example.com',
networkId: 'federation-b'
}),
gossip: new NullifierGossip({ witness })
};
// Create bridge
const bridge = new FederationBridge({
sourceFederation: 'federation-a',
targetFederation: 'federation-b',
sourceWitness: sourceFederation.witness,
targetWitness: targetFederation.witness,
sourceGossip: sourceFederation.gossip,
targetGossip: targetFederation.gossip,
freebird
});
// Bridge token from source to target
const token = ScarbuckToken.mint(100, freebird, sourceFederation.witness, sourceFederation.gossip);
const bridgePkg = await bridge.bridgeToken(token, recipientKey);
// Recipient receives in target federation
const receivedToken = await bridge.receiveBridged(
bridgePkg,
recipientSecret
);
// Verify bridge succeeded
const isValid = await bridge.verifyBridge(bridgePkg);How It Works:
- Lock Phase: Token is locked in source federation via nullifier
- Proof Phase: Source federation timestamps the lock
- Mint Phase: Target federation validates source proof and mints equivalent token
- Verification: Both federations maintain proofs for dispute resolution
Properties:
- Two-phase commit ensures atomicity
- Source token cannot be double-spent
- Target token requires proof from source
- Maintains traceability via token IDs
Use Cases:
- Cross-network value transfer
- Federation migration
- Multi-jurisdiction compliance
- Network interoperability
Scarcity includes a command-line interface (scar) for Phase 3 advanced token operations.
npm install -g scarcity
# or run directly
npx scarcityThe CLI stores wallet data and configuration in ~/.scarcity/:
# View current configuration
scar config list
# Set Witness gateway
scar config set witness.gatewayUrl http://localhost:5001
scar config set witness.networkId my-network
# Set Freebird services
scar config set freebird.issuerEndpoints http://localhost:8081
scar config set freebird.verifierUrl http://localhost:8082
# Or configure multiple issuers for redundancy (failover)
scar config set freebird.issuerEndpoints http://localhost:8081,http://localhost:8082,http://localhost:8083
# Set HyperToken relay
scar config set hypertoken.relayUrl ws://localhost:3000# Create a new wallet
scar wallet create alice
# List all wallets
scar wallet list
# Show wallet public key
scar wallet show alice
# Export wallet (private keys - keep secure!)
scar wallet export alice
# Import wallet
scar wallet import bob <secret-key># List all tokens in a wallet
scar token list alice
# Show token details
scar token show <token-id>
# Mint a new token (for testing)
scar token mint alice 100Split a single token into multiple smaller tokens:
# Split token into 3 parts: 30, 40, 30
scar split <token-id> \
--amounts 30,40,30 \
--recipients <pubkey1>,<pubkey2>,<pubkey3>
# Example with wallet public keys
scar split token-abc123 \
--amounts 30,40,30 \
--recipients $(scar wallet show alice -q),$(scar wallet show bob -q),$(scar wallet show carol -q)Options:
--amounts, -a: Comma-separated amounts (must sum to token amount)--recipients, -r: Comma-separated recipient public keys
Combine multiple tokens into a single larger token:
# Merge 3 tokens into one
scar merge <token-id-1>,<token-id-2>,<token-id-3> \
--recipient <recipient-pubkey> \
--wallet alice
# Merge all Alice's tokens for Bob
scar merge $(scar token list alice --ids-only) \
--recipient $(scar wallet show bob -q) \
--wallet aliceOptions:
--recipient, -r: Recipient's public key--wallet, -w: Wallet containing the source tokens
Send token portions to multiple recipients in one atomic transaction:
# Split payment to 3 recipients
scar multiparty <token-id> \
alice:30 bob:40 carol:30
# Using explicit public keys
scar multiparty token-abc123 \
--split <pubkey1>:30,<pubkey2>:40,<pubkey3>:20Format:
wallet:amount- Uses wallet's public key--splitwithpubkey:amount,pubkey:amount,...
Lock a payment that can only be claimed with the correct preimage:
# Create HTLC with hash lock
scar htlc create <token-id> <recipient-pubkey> \
--hash-lock <hash> \
-H <hash>
# Generate preimage and create HTLC
PREIMAGE=$(openssl rand -hex 32)
HASH=$(echo -n "$PREIMAGE" | sha256sum | cut -d' ' -f1)
scar htlc create token-abc123 $(scar wallet show bob -q) \
--hash-lock $HASHLock a payment that can be claimed before expiry or refunded after:
# Lock for 1 hour (expires at timestamp)
EXPIRY=$(($(date +%s) * 1000 + 3600000))
scar htlc create <token-id> <recipient-pubkey> \
--time-lock $EXPIRY \
--refund-key <refund-pubkey> \
-T $EXPIRYRecipient claims the locked payment:
# Claim hash-locked HTLC with preimage
scar htlc claim <package-json> \
--wallet bob \
--preimage $PREIMAGE
# Claim time-locked HTLC before expiry
scar htlc claim htlc-package.json \
--wallet bobOriginal sender refunds after time lock expires:
# Refund expired time-locked HTLC
scar htlc refund <package-json> \
--wallet aliceHTLC Use Cases:
- Atomic swaps between parties
- Conditional payments (unlock with secret)
- Time-delayed releases
- Cross-chain trading
Transfer tokens between different Witness federations:
Lock token in source federation and mint in target:
# Bridge to target federation
scar bridge transfer <token-id> <recipient-pubkey> \
--target-gateway http://localhost:5002 \
--target-network target-network \
-g http://localhost:5002 \
-n target-network
# Save bridge package for recipient
scar bridge transfer token-abc123 $(scar wallet show bob -q) \
--target-gateway http://federation-b.example.com \
--target-network fed-b \
> bridge-package.jsonRecipient claims in target federation:
# Switch to target federation
scar config set witness.gatewayUrl http://localhost:5002
scar config set witness.networkId target-network
# Claim the bridged token
scar bridge claim bridge-package.json \
--wallet bobBridge Properties:
- Two-phase commit (lock source, mint target)
- Maintains traceability via token IDs
- Requires both federations to be online
- Atomic transfer (all or nothing)
Transfer packages can be saved and shared:
# Save package to file
scar split token-123 --amounts 50,50 --recipients ... > split.json
# Load package
scar token receive split.json --wallet bob
# Show package details
scar package show split.jsonComplete workflow:
# 1. Setup
scar config set witness.gatewayUrl http://localhost:5001
scar wallet create alice
scar wallet create bob
# 2. Mint token
scar token mint alice 100
# 3. Split for multiple recipients
TOKEN=$(scar token list alice --ids-only | head -1)
scar split $TOKEN \
--amounts 40,60 \
--recipients $(scar wallet show bob -q),$(scar wallet show alice -q)
# 4. Bob creates HTLC for atomic swap
BOB_TOKEN=$(scar token list bob --ids-only | head -1)
PREIMAGE=$(openssl rand -hex 32)
HASH=$(echo -n "$PREIMAGE" | sha256sum | cut -d' ' -f1)
scar htlc create $BOB_TOKEN $(scar wallet show alice -q) \
--hash-lock $HASH \
> htlc-package.json
# 5. Alice claims with preimage
scar htlc claim htlc-package.json \
--wallet alice \
--preimage $PREIMAGEScarcity includes a modern web interface for managing wallets and tokens through your browser.
# Start the web wallet server
npm run web
# Open in browser
open http://localhost:3000The web wallet provides:
- Visual wallet management: Create, import, and manage wallets with a clean UI
- Token operations: Mint, transfer, receive, split, and merge tokens
- Real-time updates: Live balance and transaction history
- Easy transfers: Copy/paste transfer packages between wallets
- No framework dependencies: Pure HTML/CSS/JavaScript
Wallet Management:
- Create new wallets with generated keys
- Import wallets from secret keys
- Export secret keys for backup
- Set default wallet
- View public keys and balances
Token Operations:
- Mint new tokens
- Send tokens to recipients
- Receive tokens from transfer packages
- Split tokens into multiple parts
- Merge multiple tokens into one
- View transaction history
- Initialize Network: Click "Initialize Network" to connect to Scarcity infrastructure
- Create Wallet: Create or import a wallet to get started
- Mint Tokens: Mint initial tokens for testing or use
- Transfer: Send tokens using recipient public keys
- Receive: Paste transfer package JSON to receive tokens
- Operations: Use split/merge for flexible token management
The web wallet provides a REST API at http://localhost:3000/api:
# Health check
GET /api/health
# Initialize network
POST /api/init
# Wallet operations
GET /api/wallets
POST /api/wallets
GET /api/wallets/:name/balance
# Token operations
POST /api/tokens/mint
POST /api/tokens/transfer
POST /api/tokens/receive
POST /api/tokens/split
POST /api/tokens/mergeThe web wallet uses the same configuration as the CLI:
- Config:
~/.scarcity/config.json - Wallets:
~/.scarcity/wallets.json - Tokens:
~/.scarcity/tokens.json
# Run on custom port
PORT=8080 npm run web# Build and run
npm run web:dev
# File structure
src/web/
├── server.ts # Express API server
├── public/
│ ├── index.html # Main interface
│ ├── styles.css # UI styling
│ └── app.js # Frontend logic
└── README.md # Detailed docsSee src/web/README.md for complete documentation.
Real-time nullifier feed and network transparency tool for monitoring the Scarcity network.
# Start the explorer
npm run explorer
# Open in browser
open http://localhost:3001The Nullscape Explorer provides:
- Real-time nullifier feed: Live WebSocket updates as nullifiers propagate
- Persistent storage: SQLite database for historical records
- Search & query: Find nullifiers by hex, token ID, or federation
- Network statistics: Activity metrics, peer counts, witness depth
- Activity charts: Visual timeline of network activity
- Dark theme UI: Modern monitoring interface
Real-Time Monitoring:
- WebSocket feed of new nullifiers
- Live network statistics
- Instant search and filtering
Historical Analysis:
- Persistent SQLite storage
- 24-hour activity charts
- Per-federation statistics
- Nullifier count trends
Network Transparency:
- Double-spend detection visibility
- Peer count tracking
- Witness depth monitoring
- Federation activity comparison
- Start Collecting: Click "Start Collecting" to monitor the network
- View Feed: Watch nullifiers appear in real-time
- Inspect Details: Click any nullifier for full proof details
- Search: Find specific nullifiers by hex
- Analyze: Check activity charts and federation stats
The explorer provides a REST API at http://localhost:3001/api:
# Get network statistics
GET /api/stats
# Recent nullifiers (paginated)
GET /api/nullifiers?limit=50&offset=0
# Search by hex
GET /api/nullifiers/search?q=<hex>
# Hourly activity chart data
GET /api/activity/hourly
# Federation statistics
GET /api/federations/statsThe explorer uses the same infrastructure config as the CLI:
- Config:
~/.scarcity/config.json - Database:
~/.scarcity/explorer.db
# Run on custom port
PORT=8080 npm run explorerWhat's Public in Nullscape:
- Nullifiers (spent token markers)
- Witness proofs and timestamps
- Peer counts and network metrics
What Remains Private:
- Sender/recipient identities (Freebird anonymity preserved)
- Token amounts (not stored in nullifiers)
- Transaction linkability (nullifiers are unlinkable)
Nullscape provides transparency without compromising core privacy guarantees.
See src/explorer/README.md for complete documentation.
- Fast path: ~10-50ms (gossip check only)
- Standard: 5 seconds (default wait time)
- Deep validation: 30+ seconds (high-value transfers)
- Gossip broadcast: O(peers) per transfer
- Witness timestamping: ~100-1000 TPS per federation
- Validation: Parallel, scales with peer count
- Per-peer nullifier set: ~10KB per 1000 transfers
- Witness attestations: ~500 bytes per transfer
- Token secrets: 32 bytes per token
| Feature | Scarcity | Bitcoin | Zcash | Lightning | Monero |
|---|---|---|---|---|---|
| No blockchain | ✅ | ❌ | ❌ | ❌ | |
| No gas fees | ✅ | ❌ | ❌ | ❌ | |
| No addresses | ✅ | ❌ | ❌ | ||
| Sender/receiver unlinkable | ✅ | ❌ | ✅ | ❌ | ✅ |
| No global state | ✅ | ❌ | ❌ | ❌ | |
| Serverless | ✅ | ❌ | ❌ | ❌ | ❌ |
| Quantum-resistant | ❌ | ❌ | ❌ | ❌ | ❌ |
npm install
npm run build
npm testPrerequisites: The integration tests require the following services:
-
HyperToken Relay Server
cd /path/to/hypertoken node start-relay.jsDefault:
ws://localhost:8080 -
Witness Gateway + Nodes
# See https://github.com/flammafex/witnessDefault:
http://localhost:8080 -
Freebird Issuer + Verifier
# See https://github.com/flammafex/freebirdDefault issuer:
http://localhost:8081Default verifier:http://localhost:8082
Run Tests:
# Full integration test suite
npm test
# Individual test suites
npm run test:basic # Basic token transfer
npm run test:double-spend # Double-spend detection
npm run test:degradation # Graceful degradation tests
npm run test:phase3 # Phase 3 advanced featuresExpected Results (with Docker services running):
✅ Basic Token Transfer: pass
✅ Double-Spend Detection: pass
✅ Graceful Degradation: pass
✅ Phase 3 Features: pass
✅ Phase 3 CLI: pass
✅ Spam Mitigation: pass
✅ Security Hardening: pass
✅ Crypto Correctness: pass
All 8 test suites passing
Note: Tests REQUIRE Docker services. Fallback modes were removed for security.
Gossip Network:
- Run on Tor for network-level privacy
- Connect to multiple relay servers
- Bootstrap from trusted peers
Witness Federation:
- Deploy 3-5 independent witnesses
- Cross-network anchoring for hardening
- Regular key rotation
Freebird:
- Separate issuer/verifier infrastructure
- Configure Sybil resistance (invitation system/PoW/rate-limit/WebAuthn/combined)
- Hardware-backed keys (HSM)
- TLS required for all token transmission
- Monitor nullifier sets for replay attack attempts
- High-frequency trading: Validation latency (seconds)
- Large-scale retail: Gossip overhead at scale
- Regulated environments: No KYC/AML compliance
- Quantum future: ECDLP-based cryptography
- Eventual consistency: Not instant finality
- Gossip overhead: O(peers) bandwidth per transfer
- Trust assumptions: Witness federation threshold
- No atomic swaps: Cross-chain composability limited
Phase 1: Core Protocol ✅ COMPLETE
- Token minting and transfer
- Nullifier gossip network
- Probabilistic validation
- Real HyperToken P2P networking (WebSocket relay)
- Real Freebird HTTP client integration
- Real Witness threshold timestamping
- Comprehensive integration test suite (100% pass)
Phase 2: Hardening 🔨 ✅ COMPLETE (4/4 complete, 100%)
- BLS signature aggregation (Witness) ✅ COMPLETE
- WebRTC peer connections (HyperToken) ✅ COMPLETE
- VOPRF production integration (Freebird) ✅ COMPLETE
- Tor onion service support ✅ COMPLETE
Phase 3: Advanced Features ✅ COMPLETE (4/4 complete, 100%)
- Token splitting/merging ✅ COMPLETE
- Multi-party transfers ✅ COMPLETE
- Conditional payments (HTLCs) ✅ COMPLETE
- Cross-federation bridging ✅ COMPLETE
Phase 4: Tooling 🔨 IN PROGRESS (3/4 complete, 75%)
- Web wallet interface ✅ COMPLETE
- Mobile SDK (React Native) 📱 ROADMAP
- CLI tools for Phase 3 operations ✅ COMPLETE (split, merge, multiparty, HTLC, bridge)
- Nullscape Explorer (nullifier viewer) ✅ COMPLETE
nullifier = SHA-256(secret || tokenId || timestamp)
Properties:
- Unique per token + time
- Cannot be correlated to sender/receiver
- Single-use (timestamp prevents replay)
- Unpredictable (requires secret)
On receive(nullifier, proof):
1. Verify Witness attestation
2. Check if already seen
3. Add to local set
4. Forward to all peers (epidemic broadcast)
Properties:
- Eventually consistent
- Probabilistic guarantee (grows with time)
- Partition-tolerant
- Self-healing (re-gossip on reconnect)
peerScore = min(peers / 10, 0.5) // Up to 50%
witnessScore = min(depth / 3, 0.3) // Up to 30%
timeScore = min(waitMs / 10_000, 0.2) // Up to 20%
confidence = peerScore + witnessScore + timeScore // Max: 1.0Tuning:
- Small transfers: 0.5 confidence (instant)
- Medium transfers: 0.7 confidence (5s wait)
- Large transfers: 0.9+ confidence (30s+ wait)
Scarcity implements defense-in-depth security across multiple layers:
- Peer Reputation Scoring: Automatic disconnection of malicious peers
- Leaky Bucket Rate Limiting: 10 msg/sec per peer prevents flooding
- Proof-of-Work Puzzles: Optional computational cost for spam resistance
- Timestamp Validation: Early rejection of invalid nullifiers
- Multi-Gateway Quorum: Query 2-of-3 gateways to prevent censorship
- Outbound Peer Preference: Weight trusted outbound peers 3x higher
- IP Subnet Diversity: Detect and warn about Sybil attacks from same network
- Issuer Redundancy: Multiple issuers with sequential failover for high availability
High-Security Setup:
// Multi-gateway Witness with quorum
const witness = new WitnessAdapter({
gatewayUrls: [
'https://witness1.example.com',
'https://witness2.example.com',
'https://witness3.example.com'
],
quorumThreshold: 2, // 2-of-3 agreement required
powDifficulty: 16 // ~50-200ms computational cost
});
// Gossip with peer diversity
const gossip = new NullifierGossip({
witness,
peerScoreThreshold: -50,
maxTimestampFuture: 5,
maxNullifierAge: 86400000 // 24 hours
});
// Add diverse outbound peers
const outboundPeer = {
id: 'trusted-peer-1',
direction: 'outbound', // Connection YOU initiated
remoteAddress: '203.0.113.1',
send: async (data) => { /* ... */ },
isConnected: () => true
};
gossip.addPeer(outboundPeer);
// Validator with high confidence
const validator = new TransferValidator({
gossip,
witness,
waitTime: 5000,
minConfidence: 0.8 // 80% confidence required
});Monitor Security:
// Check subnet diversity
const subnetStats = gossip.getSubnetStats();
console.log('Subnet diversity:', subnetStats.size);
// Check peer direction mix
const peers = gossip.peers;
const outbound = peers.filter(p => p.direction === 'outbound').length;
console.log(`Outbound: ${outbound}, Total: ${peers.length}`);
// Check peer reputation
const scores = gossip.getAllPeerScores();
for (const [peerId, score] of scores) {
if (score.score < -20) {
console.warn(`⚠️ Low-reputation peer: ${peerId}`);
}
}For complete security details, threat models, and attack cost analysis, see SECURITY.md.
Q: Is this a blockchain? A: No. There's no global ledger, no mining, no consensus. Just gossip + timestamps.
Q: How do you prevent double-spending? A: Nullifiers are broadcast P2P. If seen twice, reject. Witness provides ground truth.
Q: What if the network partitions? A: Gossip heals on reconnect. Witness provides ordering for disputes.
Q: Is this Chaumian e-cash? A: Similar, but serverless. Freebird provides the blinding, Witness replaces the bank.
Q: Can I mine/stake for rewards? A: No. Scarcity is zero-cost by design. No incentives, no fees.
Q: How are tokens created? A: Application-specific. Could be fiat-backed, work-based, time-based, etc.
Q: Is this production-ready? A: Phases 1-3 are complete with all core features implemented and tested. Phase 2 hardening (BLS aggregation, WebRTC, production VOPRF, Tor support) is complete. Phase 3 advanced features (split/merge, HTLCs, bridging) are complete with CLI tools. This is still a research prototype - production deployment requires security audits, stress testing, and operational hardening.
Q: What are HTLCs and why would I use them? A: Hash Time-Locked Contracts enable conditional payments. Use hash locks for atomic swaps (both parties trade secrets), or time locks for refundable payments (claim before deadline or get refunded). Perfect for trustless escrow and cross-chain exchanges.
Q: Can I transfer tokens between different federations?
A: Yes! Use the bridge feature to transfer tokens between different Witness federations. The bridge uses a two-phase commit: lock token in source federation, then mint equivalent in target. Both CLI (scar bridge) and programmatic API are available.
Q: How do I split or merge tokens?
A: Use scar split to divide one token into multiple smaller tokens, or scar merge to combine multiple tokens into one. This is useful for making change, consolidating funds, or distributing payments. Both operations are atomic and maintain double-spend protection.
Q: Do I need to run my own infrastructure? A: Not necessarily. You can connect to existing Witness federations, Freebird services, and HyperToken relays. For maximum privacy and sovereignty, run your own. The CLI makes it easy to switch between federations via config.
Apache License 2.0
- Freebird: Anonymous authorization infrastructure
- HyperToken: Distributed state synchronization
- Witness: Threshold signature timestamping
Scarcity: You're fucking with the best.




