Developer Docs
TruthLinked: RPC reference, governance, cells, names, and staking.
| RPC Endpoint | https://rpc.truthlinked.org |
| Explorer | https://explorer.truthlinked.org |
| Faucet | https://airdrop.truthlinked.org |
| Network | Testnet (genesis hash all-zeros) |
| Token | TRTH, 9 decimals. 1 TRTH = 1,000,000,000 dilith. |
| Signatures | ML-DSA-65 (FIPS 204 Dilithium), post-quantum |
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.
| Component | Detail |
|---|---|
| Consensus | Streaming BFT. 200ms batch intervals, attestation pipeline, epoch-based committee rotation. |
| Signatures | ML-DSA-65 (FIPS 204). 1952-byte public keys, 3309-byte signatures. |
| Transport | ML-KEM-768 + AES-256-GCM. Post-quantum encrypted P2P sessions. |
| VM | TNCR — TruthLinked Native Cell Runtime. Register-based, no_std Rust, u256 arithmetic, gas-metered. |
| Storage | RocksDB-backed persistence. All storage keys and values are 32 bytes. |
| Account ID | SHA-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
| Parameter | Value |
|---|---|
| RPC URL | https://rpc.truthlinked.org |
| Token | TRTH — 9 decimals (1 TRTH = 1,000,000,000 dilith) |
| Block time | ~200ms target |
| Finality lag | 2 blocks |
| Max gas / tx | 10,000,000 |
| Max gas / batch | 100,000,000 |
| Max calldata | 256 KB |
| Max cell bytecode | 1 MB |
| Max call depth | 8 |
| Nonce lookahead | 64 |
| Min tx fee | 100 dilith |
04 Chain & Node Endpoints
"ok" or "degraded" with reason ("syncing" | "no_peers").05 Accounts & Balances
account_id is 64 hex chars.{"account_id": "hex"}. Returns balance, ve_balance, compute_escrow_trth.{"account_id": "hex", "limit": 50, "offset": 0}.{"cell_id": "hex", "account_id": "hex"}.06 Blocks & Transactions
?full=true for full transaction details.?full=true.?full=true for intent details.limit (max 500), cursor, tx_type.07 Submit & Simulate
Transactions are postcard-serialised and submitted as raw bytes. omega.py handles this automatically.
postcard bytes. Content-Type: application/octet-stream./health first.08 Validators
{"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").
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).
.tl name to an account ID. Returns resolved_address, expiry..tl registrations with addresses and expiry timestamps..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.nonceat execution time. Lookahead of 64. - Set
expiration_heightto current height + buffer (e.g. +100). Expired transactions are rejected.
12 Intent Types
| Intent | Description | Gas |
|---|---|---|
| Transfer | Send TRTH to an account ID | 1,000 |
| TransferToName | Send TRTH to a .tl name | 1,000 |
| BatchTransfer | Send TRTH to up to 64 recipients | 1,000 + n |
| Claim | Claim airdrop tokens | 2,000 |
| RotateKey | Replace Dilithium keypair | 1,000 |
| Stake | Stake TRTH to a validator | 5,000 |
| Unstake | Begin unbonding stake | 5,000 |
| WithdrawStake | Withdraw fully unbonded stake | 5,000 |
| Unjail | Unjail a jailed validator | 10,000 |
| DepositCompute | Deposit TRTH into compute escrow | 1,000 |
| WithdrawCompute | Withdraw from compute escrow | 1,000 |
| DeployCell | Deploy a new TNCR Cell | 1,000,000 |
| CallCell | Call a Cell function | variable |
| CallCellChain | Chained Cell calls in one transaction | variable |
| UpgradeCell | Upgrade Cell bytecode (owner only) | 500,000 |
| DeployToken | Deploy a fungible token Cell | 500,000 |
| TokenTransfer | Transfer token balance | 5,000 |
| TokenMint | Mint tokens (mint authority only) | 10,000 |
| TokenBurn | Burn tokens | 5,000 |
| MintNFT | Mint an NFT | 50,000 |
| TransferNFT | Transfer NFT ownership | 10,000 |
| BurnNFT | Burn an NFT | 5,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:
| Selector | Calldata layout | Who |
|---|---|---|
| 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=0 | Any 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 value | Anyone |
The param_key is SHA-256("truthlinked.param.v1" || param_name_bytes). Only whitelisted param names are accepted — see the full list below.
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 ID | Purpose |
|---|---|
| 1 | Community |
| 2 | Treasury (protocol) |
| 3 | Validators |
| 4 | Strategic |
Call the treasury cell (treasury_system_cell_id):
| Selector | Calldata 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):
| Selector | Calldata 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 |
| Parameter | Value |
|---|---|
| Registration fee | 10 TRTH (10,000,000,000 dilith) |
| Renewal fee | 1 TRTH (1,000,000,000 dilith) |
| Expiration | 12,960,000 blocks (~30 days) |
| Voting period | 432,000 blocks (~1 day) |
| Approval threshold | 67% 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.
| Selector | Calldata layout | Gas |
|---|---|---|
| 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 delegate | 5,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 name | Genesis value | Type |
|---|---|---|
| gas.transfer | 1,000 | u64 |
| gas.claim | 2,000 | u64 |
| gas.rotate_key | 1,000 | u64 |
| gas.register_validator | 10,000 | u64 |
| gas.stake | 5,000 | u64 |
| gas.unstake | 5,000 | u64 |
| gas.withdraw | 5,000 | u64 |
| gas.unjail | 10,000 | u64 |
| gas.deploy_cell | 1,000,000 | u64 |
| gas.deploy_token | 500,000 | u64 |
| gas.upgrade_cell | 500,000 | u64 |
| gas.token_transfer | 5,000 | u64 |
| gas.token_mint | 10,000 | u64 |
| gas.token_burn | 5,000 | u64 |
| gas.price | 1 | u64 |
| consensus.batch_interval_ms | 200 | u64 |
| consensus.max_batch_size | 1,000 | u64 |
| consensus.committee_size | 21 | u64 |
| consensus.finalization_lag | 2 | u64 |
| staking.slash_percentage | 10 | u64 |
| staking.downtime_slash_percentage | 1 | u64 |
| staking.censorship_slash_percentage | 5 | u64 |
| limits.max_gas_per_tx | 10,000,000 | u64 |
| limits.max_gas_per_batch | 100,000,000 | u64 |
| limits.max_calldata_size | 262,144 | u64 |
| limits.max_call_depth | 8 | u64 |
| limits.max_return_data_size | 262,144 | u64 |
| limits.max_cell_bytecode_size | 1,048,576 | u64 |
| limits.nonce_lookahead | 64 | u64 |
| storage.rent_lifetime_fee | 1 TRTH | u128 |
| storage.rent_grace_period_blocks | 1 | u64 |
| fees.min_tx_fee | 100 dilith | u64 |
| name.registration_fee | 10 TRTH | u128 |
| name.renewal_fee | 1 TRTH | u128 |
| name.expiration_blocks | 12,960,000 | u64 |
| name.voting_period | 432,000 | u64 |
| name.approval_threshold | 67 | u64 |
| airdrop.cooldown_secs | 43,200 (12h) | u64 |
| oracle.commit_quorum_percent | 67 | u64 |
| oracle.reveal_quorum_percent | 67 | u64 |
| oracle.request_timeout_blocks | 100 | u64 |
| oracle.http_timeout_ms | 5,000 | u64 |
| oracle.malicious_slash_bps | 100 | u64 |
| agent.private_balance.max_fee | 1,000,000,000,000 dilith | u128 |
| agent.private_balance.fee_authority | system authority | [u8;32] |
| network.ingress.max_connections | 1,000 | u64 |
| network.ingress.max_message_bytes | 10,485,760 | u64 |
| network.discovery.max_peers | 256 | u64 |
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
| Code | Meaning |
|---|---|
| 1001 | account_id must be hex |
| 1002 | account_id must be 32 bytes (64 hex chars) |
| 1005 | pubkey must be hex |
| 1006 | pubkey must be 1952 bytes (3904 hex chars) |
| 1007 | cell_id must be hex |
| 1008 | cell_id must be 32 bytes (64 hex chars) |
| 1201 | resolve only supports .tl names |
| 1202 | name expired |
| 1203 | name not found |
| 4001 | Storage not initialized |
| 4002 | Node is syncing — try again later |
| 4003 | Failed to deserialize transaction |
| 4101 | Direct MCP tool calls not permitted |
| negative | Execution error — message contains details |