Developer Documentation · Testnet

Developer Docs

TruthLinked: RPC reference, governance, cells, names, and staking.

● Testnet Live Version 0.1.0 ML-DSA-65 · ML-KEM-768 TNCR VM
RPC Endpointhttps://rpc.truthlinked.org
Explorerhttps://explorer.truthlinked.org
Faucethttps://airdrop.truthlinked.org
NetworkTestnet (genesis hash all-zeros)
TokenTRTH, 9 decimals. 1 TRTH = 1,000,000,000 dilith.
SignaturesML-DSA-65 (FIPS 204 Dilithium), post-quantum
Testnet notice: Chain state may be reset. TRTH tokens have no monetary value. Use the faucet at airdrop.truthlinked.org. The RPC at https://rpc.truthlinked.org is rate-limited and for development use only.

01 Overview

TruthLinked is a post-quantum Layer 1 blockchain. All transaction signatures use ML-DSA-65 (FIPS 204 Dilithium). Peer-to-peer transport uses ML-KEM-768 (FIPS 203 Kyber). Smart contracts are called Cells and execute in the TNCR VM — a purpose-built register-based bytecode interpreter written in no_std Rust, embedded directly in the node. The native token is TRTH.

ComponentDetail
ConsensusStreaming BFT. 200ms batch intervals, attestation pipeline, epoch-based committee rotation.
SignaturesML-DSA-65 (FIPS 204). 1952-byte public keys, 3309-byte signatures.
TransportML-KEM-768 + AES-256-GCM. Post-quantum encrypted P2P sessions.
VMTNCR — TruthLinked Native Cell Runtime. Register-based, no_std Rust, u256 arithmetic, gas-metered.
StorageRocksDB-backed persistence. All storage keys and values are 32 bytes.
Account IDSHA-256 of "truthlinked-account-id-v1" || pubkey. 32 bytes (64 hex chars).

02 Quickstart

1. Get omega.py

# Download the CLI (no dependencies required)
curl -sSL https://get.truthlinked.org/install.sh | sh

2. Generate a keypair

python3 ~/.omega/omega.py keygen

3. Claim testnet tokens

python3 ~/.omega/omega.py faucet --keys mykeys.json
# 12-hour cooldown. Claims 50 TRTH.

4. Check balance

curl https://rpc.truthlinked.org/account/<your_account_id>

5. Send a transfer

python3 ~/.omega/omega.py send \
  --keys mykeys.json \
  --to <recipient_account_id> \
  --amount 1.5

03 Network Information

ParameterValue
RPC URLhttps://rpc.truthlinked.org
TokenTRTH — 9 decimals (1 TRTH = 1,000,000,000 dilith)
Block time~200ms target
Finality lag2 blocks
Max gas / tx10,000,000
Max gas / batch100,000,000
Max calldata256 KB
Max cell bytecode1 MB
Max call depth8
Nonce lookahead64
Min tx fee100 dilith

04 Chain & Node Endpoints

GET/health
Node health. Returns "ok" or "degraded" with reason ("syncing" | "no_peers").
GET/chain_info
Chain ID, height, finalized height, peer count, sync status, foundation mint authority.
GET/gas
Full gas schedule: all transaction type fees, limits, and CU pricing.
GET/metrics
Prometheus-format metrics.

05 Accounts & Balances

GET/account/{account_id}
Full account info: balance (dilith), nonce, compute escrow, is_cell flag. account_id is 64 hex chars.
POST/balance
Balance by account ID. Body: {"account_id": "hex"}. Returns balance, ve_balance, compute_escrow_trth.
POST/balance_by_pubkey
Balance by full Dilithium public key (1952 bytes = 3904 hex chars).
POST/transaction_history
Paginated tx history. Body: {"account_id": "hex", "limit": 50, "offset": 0}.
POST/token_balance
Token balance for a cell+account pair. Body: {"cell_id": "hex", "account_id": "hex"}.

06 Blocks & Transactions

GET/block/{height}
Block by height. Add ?full=true for full transaction details.
GET/block/latest
Latest block. Supports ?full=true.
GET/tx/{hash}
Transaction by hash (64 hex). Supports prefix lookup (min 8 hex chars). Add ?full=true for intent details.
GET/mempool
Pending mempool transactions. Query params: limit (max 500), cursor, tx_type.

07 Submit & Simulate

Transactions are postcard-serialised and submitted as raw bytes. omega.py handles this automatically.

POST/submit_raw
Submit a signed transaction. Body: raw postcard bytes. Content-Type: application/octet-stream.
POST/simulate_raw
Dry-run without submitting. Returns gas used, CU consumed, fees. Same body format.
Node must be synced: Both endpoints return an error if the node is syncing. Check /health first.

08 Validators

GET/validators
All validators: stake, jailed status, active flag, commission.
POST/validator_info
Detailed validator info including unbonding entries. Body: {"pubkey": "hex"}.

09 Cells & Tokens

Cells are TruthLinked smart contracts: TNCR bytecode accounts with storage, balance, and an owner. Token cells are Cells with a TokenConfig attached. Cell bytecode starts with magic bytes 54 4E 43 52 (ASCII "TNCR").

GET/cell/{cell_id}
Cell info: found status, is_token flag, immutability, owner.
GET/treasury_proposal/{proposal_id}
Treasury spend proposal: bucket, recipient, amount, votes, executed status.
GET/nft/{nft_id}
NFT info: owner, metadata URI, collection, royalty, approved address.

10 Names

Names are .tl suffixed identifiers (e.g. alice.tl). Max 12 lowercase ASCII letters, dots allowed. Registration requires a validator vote. Names expire after 12,960,000 blocks (~30 days at 200ms blocks).

GET/resolve/{name}
Resolve a .tl name to an account ID. Returns resolved_address, expiry.
GET/name_registry
All active .tl registrations with addresses and expiry timestamps.
GET/search?q={query}
Universal search: block height, tx hash, account ID, or .tl name.

11 Transaction Format

// Rust definition (simplified)
struct Transaction {
    sender:            [u8; 32],      // account ID
    intent:            TransactionIntent,
    nonce:             u64,
    expiration_height: u64,
    timestamp:         u64,           // unix seconds
    signature:         Vec<u8>,       // ML-DSA-65, 3309 bytes
    pubkey_bytes:      Vec<u8>,       // Dilithium pubkey, 1952 bytes
}

Signing message: postcard::to_allocvec(&intent) with context b"truthlinked-transaction-v1".

  • Nonce must equal account.nonce at execution time. Lookahead of 64.
  • Set expiration_height to current height + buffer (e.g. +100). Expired transactions are rejected.

12 Intent Types

IntentDescriptionGas
TransferSend TRTH to an account ID1,000
TransferToNameSend TRTH to a .tl name1,000
BatchTransferSend TRTH to up to 64 recipients1,000 + n
ClaimClaim airdrop tokens2,000
RotateKeyReplace Dilithium keypair1,000
StakeStake TRTH to a validator5,000
UnstakeBegin unbonding stake5,000
WithdrawStakeWithdraw fully unbonded stake5,000
UnjailUnjail a jailed validator10,000
DepositComputeDeposit TRTH into compute escrow1,000
WithdrawComputeWithdraw from compute escrow1,000
DeployCellDeploy a new TNCR Cell1,000,000
CallCellCall a Cell functionvariable
CallCellChainChained Cell calls in one transactionvariable
UpgradeCellUpgrade Cell bytecode (owner only)500,000
DeployTokenDeploy a fungible token Cell500,000
TokenTransferTransfer token balance5,000
TokenMintMint tokens (mint authority only)10,000
TokenBurnBurn tokens5,000
MintNFTMint an NFT50,000
TransferNFTTransfer NFT ownership10,000
BurnNFTBurn an NFT5,000

13 Governance

TruthLinked governance is fully on-chain. All protocol parameters, treasury spending, name registrations, and staking operations go through system cells — native Rust handlers with no sandbox overhead. Proposals require a 2/3 supermajority of voted stake and 1/3 minimum participation of total active stake.

Parameter Proposals

Any validator can propose a change to a governance parameter. The proposal goes through a voting period, then a timelock, then anyone can execute it.

Call the governance cell (governance_system_cell_id) with one of these selectors:

SelectorCalldata layoutWho
propose_param[4 sel][32 param_key][32 new_value][8 voting_period_blocks][8 timelock_blocks][1952 pubkey]Any validator with stake
vote_param[4 sel][32 param_key][8 approve_u64][1952 pubkey] — approve=1, reject=0Any validator with stake
execute_param[4 sel][32 param_key]Anyone, after voting ends + timelock
get_param[4 sel][32 param_key] — returns 32-byte valueAnyone

The param_key is SHA-256("truthlinked.param.v1" || param_name_bytes). Only whitelisted param names are accepted — see the full list below.

Execution conditions: voting must be closed, timelock elapsed, participation ≥ 1/3 total stake, approval ≥ 2/3 of votes cast, and votes_for > votes_against.

Treasury

The treasury holds TRTH across four buckets. Spend proposals require the same 2/3 supermajority. Voting period is fixed at 432,000 blocks (~1 day).

Bucket IDPurpose
1Community
2Treasury (protocol)
3Validators
4Strategic

Call the treasury cell (treasury_system_cell_id):

SelectorCalldata layout
propose_spend[4 sel][32 proposal_id][8 bucket][32 recipient][16 amount_u128][8 timelock_blocks]
vote_spend[4 sel][32 proposal_id][1 approve] — caller must be a validator
execute_spend[4 sel][32 proposal_id] — after voting ends + timelock

Name Registry

Names are .tl identifiers. Registration requires a validator vote. Rules: lowercase ASCII only, max 12 letters, dots allowed (no leading/trailing/double dots), must end in .tl.

Call the name registry cell (name_registry_system_cell_id):

SelectorCalldata layout
propose_name[4 sel][1 name_len][name_bytes][32 target][32 owner]
vote_name[4 sel][1 name_len][name_bytes][1 approve] — caller must be a validator
renew_name[4 sel][1 name_len][name_bytes] — resets expiry
transfer_name[4 sel][1 name_len][name_bytes][32 new_owner] — caller must be current owner
ParameterValue
Registration fee10 TRTH (10,000,000,000 dilith)
Renewal fee1 TRTH (1,000,000,000 dilith)
Expiration12,960,000 blocks (~30 days)
Voting period432,000 blocks (~1 day)
Approval threshold67% of voted stake

Staking Cell

Staking operations are routed through the staking system cell (staking_system_cell_id). The caller must own the validator pubkey (account ID derived from pubkey must equal caller), or be a registered delegate.

SelectorCalldata layoutGas
stake[4 sel][1952 pubkey][8 amount_u64]5,000
unstake[4 sel][1952 pubkey][8 amount_u64]5,000
withdraw[4 sel][1952 pubkey]5,000
unjail[4 sel][1952 pubkey]10,000
delegate_add[4 sel][32 delegate_account_id]1,000
delegate_remove[4 sel][32 delegate_account_id]1,000
stake_for[4 sel][32 owner_account_id][1952 pubkey][8 amount_u64] — caller must be delegate5,000
unstake_for[4 sel][32 owner_account_id][1952 pubkey][8 amount_u64]5,000
withdraw_for[4 sel][32 owner_account_id][1952 pubkey]5,000
unjail_for[4 sel][32 owner_account_id][1952 pubkey]10,000

Selector hash: FNV-1a 32-bit of the selector name string, little-endian 4 bytes.

All Governance Parameters

These are the exact parameter names accepted by propose_param. Genesis values are the defaults enforced at chain start.

Parameter nameGenesis valueType
gas.transfer1,000u64
gas.claim2,000u64
gas.rotate_key1,000u64
gas.register_validator10,000u64
gas.stake5,000u64
gas.unstake5,000u64
gas.withdraw5,000u64
gas.unjail10,000u64
gas.deploy_cell1,000,000u64
gas.deploy_token500,000u64
gas.upgrade_cell500,000u64
gas.token_transfer5,000u64
gas.token_mint10,000u64
gas.token_burn5,000u64
gas.price1u64
consensus.batch_interval_ms200u64
consensus.max_batch_size1,000u64
consensus.committee_size21u64
consensus.finalization_lag2u64
staking.slash_percentage10u64
staking.downtime_slash_percentage1u64
staking.censorship_slash_percentage5u64
limits.max_gas_per_tx10,000,000u64
limits.max_gas_per_batch100,000,000u64
limits.max_calldata_size262,144u64
limits.max_call_depth8u64
limits.max_return_data_size262,144u64
limits.max_cell_bytecode_size1,048,576u64
limits.nonce_lookahead64u64
storage.rent_lifetime_fee1 TRTHu128
storage.rent_grace_period_blocks1u64
fees.min_tx_fee100 dilithu64
name.registration_fee10 TRTHu128
name.renewal_fee1 TRTHu128
name.expiration_blocks12,960,000u64
name.voting_period432,000u64
name.approval_threshold67u64
airdrop.cooldown_secs43,200 (12h)u64
oracle.commit_quorum_percent67u64
oracle.reveal_quorum_percent67u64
oracle.request_timeout_blocks100u64
oracle.http_timeout_ms5,000u64
oracle.malicious_slash_bps100u64
agent.private_balance.max_fee1,000,000,000,000 dilithu128
agent.private_balance.fee_authoritysystem authority[u8;32]
network.ingress.max_connections1,000u64
network.ingress.max_message_bytes10,485,760u64
network.discovery.max_peers256u64
Encoding: u64 values are stored as 8-byte little-endian in a 32-byte slot (zero-padded). u128 values use 16-byte little-endian. [u8;32] values are stored directly.

14 omega.py CLI

omega.py is a single-file Python CLI. Requires Python 3.8+ and no pip installs. ML-DSA-65 key generation is handled by a bundled Rust binary.

# Install
curl -sSL https://get.truthlinked.org/install.sh | sh

# Generate keypair
python3 ~/.omega/omega.py keygen

# Claim faucet (12h cooldown)
python3 ~/.omega/omega.py faucet --keys mykeys.json

# Send TRTH
python3 ~/.omega/omega.py send --keys mykeys.json --to <account_id> --amount 10.5

# Send to .tl name
python3 ~/.omega/omega.py send --keys mykeys.json --to alice.tl --amount 5.0

# Check balance
python3 ~/.omega/omega.py balance --account <hex>

# Custom RPC
python3 ~/.omega/omega.py --rpc http://localhost:9944 balance --keys mykeys.json

15 Error Codes

CodeMeaning
1001account_id must be hex
1002account_id must be 32 bytes (64 hex chars)
1005pubkey must be hex
1006pubkey must be 1952 bytes (3904 hex chars)
1007cell_id must be hex
1008cell_id must be 32 bytes (64 hex chars)
1201resolve only supports .tl names
1202name expired
1203name not found
4001Storage not initialized
4002Node is syncing — try again later
4003Failed to deserialize transaction
4101Direct MCP tool calls not permitted
negativeExecution error — message contains details