pecu

A Verus wallet that lives in your terminal.

Keys, transparent sends, air-gapped signing, transaction decoding, the VerusID lifecycle and currency operations — from one binary, with no full node to run.

Command reference Source cargo build --release
pecu doctor
$pecudoctor┌─┐┌─┐┌─┐┬veruswallet·v0.1.0├─┘├┤doctor└─┘└─┘└─┘┌─LOCAL───────────────────────────────────────────────────────┐profiletestnetnodehttps://api.verustest.netcurrencyVRSCTESTspendingspendingallowedconfig~/.cache/pecu-demo/config.tomlkeys~/.cache/pecu-demo/keysstoredkeys1keyreplycap8MiBtimeout20s├─BUILD───────────────────────────────────────────────────────┤pecu0.1.0verus-sdkae279eafeaturesnetwork├─NODE────────────────────────────────────────────────────────┤chainVRSCTESTdaemon1.2.17-6tip1,201,905mined31sagosyncinsyncdefidisabledsinceblock1,187,000launchesandconversionsarerefusedmempool0transactionstip1,201,905mined31sagolatency525mslatency525ms└───────────────────────────────────────────────────────────────┘noconfigfileyetrunningonthebuilt-inprofiles
where the files are, what the binary is, whether the node answers

The point

There is no daemon.

No verusd to install, no chain to sync, no wallet process to keep alive. pecu asks a public RPC endpoint questions and hands it finished transaction bytes. That is the whole architecture.

Starts in milliseconds

pecu id show VRSCTEST@ answers in about the time curl to the same node would take. There is nothing to warm up.

Keys never leave the machine

Generated locally, encrypted at rest with Argon2id and ChaCha20-Poly1305, signed in process. The node is never asked to hold, unlock or use a key.

One URL to point somewhere else

--node, an environment variable or a config file. Your own daemon works exactly as well as a public one; it is the same interface.

Built to teach the SDK

Every command takes --explain, which prints the exact verus-sdk calls it just made.

Reading the chain

It tells you what things mean.

Most of the value is not in fetching data — any HTTP client does that. It is in saying what the data implies, at the moment you are about to act on it.

An identity, and whether it can be revoked at all

An identity that is its own recovery authority cannot be revoked: consensus refuses a revocation nobody could undo. Nothing in the raw RPC reply says so. The last line does.

pecu id show VRSCTEST@
$pecuidshowVRSCTEST@┌─IDENTITY──────────────────────────────────────────────────────────┐nameVRSCTEST@i-addressiJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yqstatusactivelastchangeblock1,186,4122026-08-1202:08UTC(10d23hago)outputa65d3ee483…07c9f1:0├─CONTROL───────────────────────────────────────────────────────────┤signatures1-of-1RXKs5Gz8kRqpA52M25AW5FzP3aCNq46yMhrevocationiJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq(itself)recoveryiJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq(itself)published1contentkey├─TIMELOCK──────────────────────────────────────────────────────────┤stateneverlocked└─────────────────────────────────────────────────────────────────────┘thisidentitycannotberevoked:itisitsownrecoveryauthority,andconsensusrefusesarevocationnobodycouldundooutputanauthoritypointingattheidentityitselfanswerstothesameprimarykeysabove;theycanhandittoanotherVerusID,butcannottakeitbackafterwards
authorities, timelock state, and whether the identity can be revoked at all

A transaction, output by output

A real VRSCTEST currency launch — seven outputs, one of which holds a VerusID and one of which holds reserves. Output #2 is why this command exists: an undecodable CryptoCondition that may hold currency, called out rather than printed as an empty 0.00000000.

pecu tx explain <txid>
$pecutxexplain2fecffbb22cc3117526af0a8b9aa262465e7c088558039676e6df7a261712cd1┌─TRANSACTION──────────────────────────────────────────────────────────┐txid2fecffbb22cc3117526af0a8b9aa262465e7c088558039676e6df7a261712cd1expiryheight1,178,481├─INPUTS───────────────────────────────────────────────────────────────┤#060a76a8b3a…7e2ac8:0#121743ccbec…7ba57c:1├─OUTPUTS──────────────────────────────────────────────────────────────┤7outputs356.97648000innativesatoshis#00.00000000theVerusIDpecudepth3@1-of-1,revocationi7kDJurgp…ryDB,recoveryi7kDJurgp…ryDB#10.00000000aCryptoConditionthisSDKdoesnotdecode(eval2)itcannotholdcurrency#20.00000000aCryptoConditionthisSDKdoesnotdecode(eval13)ITMAYHOLDCURRENCY;donottreatthisoutputasempty#30.00000000aCryptoConditionthisSDKdoesnotdecode(eval5)itcannot#40.00000000aCryptoConditionthisSDKdoesnotdecode(eval12)itcannot#5100.00000000reservesheldfori7kDJurgpZA63cjPTuyK49CeCKihB5ryDB:├─INPUTS───────────────────────────────────────────────────────────────theVerusIDpecudepth3@holdHOLDCURRENCY;donottreatthisoutp100.00000000100.00000000iJhCezBEx…f2yq#6256.97648000RComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hm└────────────────────────────────────────────────────────────────────────┘
a real VRSCTEST currency launch, output by output

A balance you can actually spend

Spendable, withheld, in-conditions and every token on the address, kept apart — because a balance that adds them together is a balance you cannot spend from.

pecu wallet balance
$pecuwalletbalance--addressRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hm┌─WALLET─────────────────────────────────────────────┐addressRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hmtip1,201,745├──────────────────────────────────────────────────────┤SPENDABLE4098.74023600VRSCTEST(49outputs)WITHHELD0.00000000VRSCTEST(1output)INCONDITIONS0.00000000VRSCTEST(3outputs)├─TOKENS─────────────────────────────────────────────┤1000.00000000pecubask1@i9dpvtcsH…9iD11000.00000000pecuref9@iKh6DBXjP…Yokg0.28145956triccrypto2@iQkcGykMB…7skbWITHHELD0.00000000VRSCTEST└───────────────────────────────────────────└──────────────────────────────────────────────────────┘withheld:thenodereportedtheseasnotspendable.Usuallycoinbasematurity100confirmationsbutnotalwaysinconditions:CryptoConditionoutputsthatarenotplainidentitypayments.Anytokentheycarryiscountedabove,initsowncurrency
spendable, withheld and token balances kept apart

Writing to the chain

Nothing irreversible happens quietly.

Every spending command builds a panel first, names what cannot be undone, and then asks. --dry-run stops after signing, so you can read the whole thing before a single byte is broadcast.

Send

Built and signed and stopped. Nothing broadcast, nothing written.

pecu send --dry-run
$pecusend--toRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hm--amount1--fromdemo--dry-run┌─REVIEW───────────────────────────────────────────────────────────────┐fromRDDQLEqnSA14MRuxE9BituXNXAu754Uu8X(demo)toRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hmamount1.00000000VRSCTESTfee0.00010000VRSCTESTchange128.99990000VRSCTESTtxid1be90cb4a96da69037f3bc9c73e1b63a0c5c70d9e58d0bb79200669908e211d4expiryheight1,201,764├─OUTPUTSASBUILT─────────────────────────────────────────────────────┤#01.00000000VRSCTESTRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hm#1128.99990000VRSCTESTRDDQLEqnSA14MRuxE9BituXNXAu754Uu8X└────────────────────────────────────────────────────────────────────────┘┌─SIGNEDTRANSACTION───────────────────────────────────────────────────┐0400008085202f8901999de722574cf36597262c56504a77e4a80ea68e44b3d4463ad7292d004197bb010000006a473044022066e449157b1d6af8621f705afbe47daeaa7d15360a732ce1c847a7badb35ea4002203481144265ca0b2b94b1898acf5c7a15a45e136f68b53c331873aa1e48a1b1de012102f006b56242f2f687378b3b50e47196ad1754fc8ccb18ef6e69e6cdf01150258effffffff0200e1f505000000001976a91426b0e358416b55160b5d0ec55ca18e4be15d593b88acf039e600030000001976a9142b29117b5525e4change7df53fcf9d5dc5fe8568eb903888ac00000000645612000000000000000000000000nothingwasbroadcast.`pecutxexplain<hex>`readsthisback,and`--json`givesitunwrapped
built and signed, and stopped before anything is broadcast

Send a token

--currency moves a token instead of the chain's own coins. The amount is labelled with what is actually moving; the fee stays in VRSCTEST, because that is what the miner is paid in. The token-carrying outputs read 0.00000000 VRSCTEST because a token rides in the output's script, not its value.

pecu send --currency
$pecusend--toRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hm--currencysdkcuralpha@--amount5--fromdemo--dry-run┌─REVIEW───────────────────────────────────────────────────────────────┐fromRDDQLEqnSA14MRuxE9BituXNXAu754Uu8X(demo)toRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hmamount5.00000000sdkcuralpha@currencyidi7UCaJkKRFXBCK4S1AMrkfKTnPwdLc7dV7fee0.00010200VRSCTESTchange129.99989800VRSCTESTtxid6460fc9608373761364350e9555b6cdaef1fe7fec4eed525eea682767c40accdexpiryheight1,201,765├─OUTPUTSASBUILT─────────────────────────────────────────────────────┤#00.00000000VRSCTESTRComfCn4wHHsGR8vWBAU7T1r3tHHyxN9Hmholds5.00000000i7UCaJkKR…7dV7#10.00000000VRSCTESTRDDQLEqnSA14MRuxE9BituXNXAu754Uu8Xholds20.00000000#2129.99989800VRSCTESTRDDQLEqnSA14MRuxE9BituXNXAu754Uu8X└────────────────────────────────────────────────────────────────────────┘┌─SIGNEDTRANSACTION───────────────────────────────────────────────────┐0400008085202f8902bd155d8209972e07ccf39228934cdb85a7d3c762f757a46a02a738ca0b49777c00000000694c67010101012102f006b56242f2f687378b3b50e47196ad1754fc8ccb18ef6e69e6cdf01150258e40634290a7e73f1f710b4106caeb7b26fca27a2a226595eea6a0d3d7088a381f4471d7a196f272751c3198127599782a8f70c8e5b3b4c4d893f38595526505ffbeffffffff999de722574cf36597262c56504a77e4a80ea68e44b3d4463ad7292d004197bb010000006a47304402201f4d66539fd799227b95e6ed54e352127c6f83e44997df628677fc23392ccf7002203b7f1ff04128d4d1448ff63fce64289dd00531e33ec055fef721754fa3335791012102f006b56242f2f687378b3b50e47196ad1754fc8ccb18ef6e69e6cdf01150258effffffff030000000000000000531a04030001011426b0e358416b55160b5d0ec55ca18e4be15d593bcc3504030901011426b0e358416b55160b5d0ec55ca18e4be15d593b1a012bd0c2dcf49d034269ad0cd786c01bdd4bc2f9d680edb4c900750000000000000000531a0403000101142b29117b5525e47df53fcf9d5dc5fe8568eb9038cc350403090101142b29117b5525e47df53fcf9d5dc5fe8568eb90381a012bd0c2dcf49d034269ad0cd786c01bdd4bc2f9d686b8d5a70075281adcfee#0#2129.999898000603000000106030000001976a9142b29117b5525e47df53fcf9d5dc5fe8568eb903888ac00000000655612000000000000000000000000nothingwasbroadcast.`pecutxexplain<hex>`readsthisback,and`--json`givesitunwrapped
a token moves; the fee stays in the chain's own coin

Register a VerusID

Two transactions: one commits to the name, a second claims it once the first confirms. The reservation is written to disk before the commitment is broadcast, so Ctrl-C, a timeout or a dead connection all leave a registration the same command picks up. That is not theoretical — an earlier run lost its node mid-wait and resumed from the file.

pecu id register
$pecuidregisterpecu-demo--fromdemo--yes--timeout40┌─REGISTER───────────────────────────────────────────────────┐namepecu-demo@payingfromRDDQLEqnSA14MRuxE9BituXNXAu754Uu8X(demo)fee100.00000000VRSCTESTburned,notrecoverablesignatures1-of-1RDDQLEqnSA14MRuxE9BituXNXAu754Uu8X└──────────────────────────────────────────────────────────────┘twotransactions:thiscommitstothename,andasecondoneclaimsitoncethefirstconfirmsasregisteredthisidentityCANNOTBEREVOKED:itisitsownrecoveryauthority,andconsensusrefusesarevocationnobodycouldundobothauthoritiespointattheidentityitself,sothekeysabovearetheonlythingprotectingitthereisnothingelsetofallbackonyoucanpointrecoveryatanotherVerusIDlater,whichmakesitrevocablebutonlywhileitisstillitsownauthority.Thathand-offisone-waystep1of2commitmentcf1200424d1b8cb793d7f8fb77a95855cb757f5d74ed27753913a828cf8cba22┌─SAVED──────────────────────────────────────────────────┐registration~/.cache/pecu-demo/pending/pecu-demo.json└──────────────────────────────────────────────────────────┘thisfileholdsthesalt.Withoutitthenamecannotbeclaimedandthecommitmentfeeislostwaitingforittoconfirm.Interruptingissafethefileabovesurvivesandthesamecommandpicksupwherethisleftoffwaitinginthemempool,30selapsedbroadcasttxidcb7732a7908eb61ee953172d48859c09f0149d8b10a4d5512dc117a7fcacabf4┌─REGISTRATIONSENT────────────────────────────┐namepecu-demo@i-addressiFVooxAVTEVMHRCGW28LUrVku44CYcHiR6paid100.00000000VRSCTEST└────────────────────────────────────────────────┘notonchainuntilthisismined`pecuidshow`willnotfindituntilthenhttps://testex.verus.io/tx/cb7732a7908eb61ee953172d48859c09fhttps://testex.verus.io/tx/cb7732a7908eb61ee953172d48859c09f0149d8b10a4d5512dc117a7fcacabf4
both phases of a VerusID registration, resumable in between

Air gap

Three commands, three machines.

The machine holding the key never opens a socket. The machines that talk to the network never see a key. Each step also speaks QR codes, so the offline machine needs no cable either.

# 1. where the node is. No key on this machine at all.
pecu plan send --address R… --to R… --amount 1 --out plan.hex

# 2. where the key is. This one opens no socket.
pecu sign @plan.hex --key cold --out signed.hex

# 3. back where the node is. Carries no key.
pecu broadcast @signed.hex

The gap carries the chain's own coins, and nothing else. pecu plan send takes --currency and --from-identity and refuses each by name, before it opens a socket: a token rides in an output's script and a VerusID's funds sit in pay-to-identity outputs, and the SDK builds an unsigned form of neither. pecu send moves both, and it signs on the machine that talks to the node.

  • Mainnet ships unable to spend Spending is off for it by default. Moving real coins takes a deliberate edit, not a forgotten --profile.
  • --json is output, not consent Machine-readable mode refuses to spend without --yes, because the confirmation prompt would go to the stream you are parsing.
  • Burned fees are named as burned A registration fee is not recoverable, and the panel says so in those words before you agree to it.
  • Refusals are diagnostics, not stack traces Each one has a code, an explanation of what the chain would have done, and the next thing to try.

Status

Early, and specific about it.

This is the SDK's example app before it is anything else. The honest version of where it stands:

Proven on chain

Identity registration and reads, transaction decoding, key management, balances and UTXO reads, transparent sends.

Built, waiting on a chain

--nft, seeded contributions at launch, and paying a token to a VerusID are each refused by a named diagnostic rather than attempted.

Not in scope

Shielded and z-address operations, marketplace offers.

DeFi is currently switched off on both VRSCTEST and VRSC. While the switch is in force the daemon refuses every currency launch and every conversion, whatever the transaction contains. pecu doctor reads that from the chain's notification oracle and tells you before you sign one — see Status for what it unblocks.

Start here

Rust 1.95, which rust-toolchain.toml pins. No other system dependency.

git clone https://github.com/chainvue/pecu-cli
cd pecu-cli
cargo build --release
./target/release/pecu doctor

The name

pecū n. Latin

cattle; livestock; a herd.

The root of pecūnia, the Latin word for money: wealth was a herd, before there were coins to count it in. The same root sits inside pecuniary and impecunious — and inside peculium, the Roman fund a dependent could hold and trade with while ownership stayed elsewhere.

Held is not the same as free to move. pecu keeps spendable, withheld and in-conditions apart for that reason: a beast already pledged against a debt still grazes in your field, but it isn't yours to drive to market.