It’s a new month, and time for another contest! Need help while developing? Start a new thread in development or come check out the development channel in our Discord!
This month’s theme is Spooky Secrets!
Tari is the programmable privacy chain. Show your love of spooky vibes and secret submissions by creating confidential concoctions! We’re looking for the best demonstration of Tari’s privacy capabilities.
Read the full rules here, and reply to this thread with your submission! Remember that all submissions must have:
A publicly viewable link to the code and/or app you are submitting
A description of the project
A Tari payment address to send your winnings to, should you win
A link to a public post on social media, where you are announcing your contest entry. NOTE: For this month, to improve our reach on these platforms, your submission must be announced on either Bluesky (check our guide) or Reddit (check out our subreddit)!
Additional Notes
To keep things orderly, only post ONE post per submission. If you’ve updated your submission, please update your post and add a section about what’s been updated.
Please refrain from discussing the submissions in this thread-- if you want to talk about your project in detail, start a new thread in the projects category of the forum, and feel free to link it in your post here.
Project Description:
Beetlejuice inspired viral Trick or Treat. Players lock XTM candy → Chant “Ootlejuice” 3 times → Summon Ootle → Trick or Treat result is a Spooky Secret kept private via Tari programmable privacy. Host and player only know outcome until reveal. Viral chain 1->10->100! Perfect use of Tari Ootle confidential assets. Built for Nov 11 Ootlejuice Candy Project launch, spinning Ootle demo video coming.
GhostKey is a demonstration of Tari’s privacy capabilities taken to its logical end: keys private from birth. It is the first portable, pure-Python implementation of the complete Tari address format — RFC 9496 ristretto255, DammSum checksums, single & dual addresses, base58/hex/emoji encodings, and payment-IDs — all in one stdlib-only file, running fully offline.
Why this is a demonstration of Tari privacy:
Privacy from birth.generate creates keypairs with the OS CSPRNG entirely offline — no server, no dependency, no telemetry, no network access. Nothing can leak, because nothing ever leaves your machine.
Stealth memos. Payment-IDs are embedded into dual addresses exactly the way Tari’s confidential transactions carry encrypted memos — the payer proves why they paid without revealing it on-chain.
Emoji encoding — Tari’s most playful unique feature — fully implemented (encode/decode/validate), so you can see an address as 66 emoji.
Dissect anything.inspect decodes network, feature flags, spend & view keys from any address and verifies its DammSum checksum — audit before you trust.
Ecosystem value: byte-for-byte agreement with the official Rust vectors means any Python project (bots, analytics, test suites) can now validate and generate Tari addresses with zero trust in this codebase and zero Rust toolchain.
Spooky artifact — the theme, materialised. Mined with GhostKey’s own vanity command: a mainnet dual address whose emoji encoding contains the adjacent pair :
Its secrets were discarded — like all good ghosts, it only haunts, it never holds funds. Verify: python ghostkey.py inspect <address> → mainnet, checksum valid, in the emoji encoding.
Verification (numbers, reproducible):
python ghostkey.py selftest → 107 checks passed, 0 failed: every RFC 9496 test vector (SQRT_RATIO_M1, generator multiples B[0..15], invalid encodings, element derivation, same-output derivation) + Tari golden vectors taken byte-for-byte from the tari-project/tari Rust test suite (dual mainnet address, payment-id addresses, emoji vectors, DammSum) + keygen round-trips and feature-flag validation.
Honest boundaries: pure-Python scalar math is not constant-time against side-channel observation of your own process — fine for offline keygen on a trusted machine, documented plainly in the code and README.
Disclosure: GhostKey was developed with an AI coding agent (Hermes). Specs, golden vectors and design decisions were verified by the author against RFC 9496 and the tari-project Rust source.
Selling a secret (a password, an API key, a puzzle answer, the key to a file) has always needed trust. The buyer pays first and hopes, or the seller reveals first and hopes, or both trust an escrow agent who then knows the secret too. When the buyer says “this is not what you promised”, somebody has to decide, and that somebody has to see it.
Crypt removes that somebody. The seller commits to a salted 320-byte block holding the secret, the buyer pays into escrow, and the seller delivers the block encrypted to the buyer’s key. The ciphertext sits on-chain; only the buyer can open it. If the buyer disputes, they publish the Diffie-Hellman point with a Chaum-Pedersen DLEQ proof, and the template itself re-derives the key, decrypts, re-hashes and compares against the commitment. Mismatch: the buyer gets the price and both bonds. Match: the dispute was false and the seller gets everything. No oracle, no arbiter, no off-chain judge.
list(commitment, price, buyer_bond, windows, seller_key, bond) — the bond comes out of a stealth coin.
buy(buyer_key, proof, payment) — payment out of a stealth coin; the buyer’s key is derived for this purchase only.
deliver(R, proof_of_r, ciphertext, signature) — the seller must prove they know r for this sale’s R, so a dispute can only ever publish a value the seller already had.
accept / dispute / claim_undelivered / claim_uncontested / cancel — every way a sale ends, with epoch deadlines and bonds on both sides.
pay_seller / pay_buyer — each side is paid into fresh stealth outputs, by a stealth transfer it signs for, so nobody can redirect it.
Where Tari’s privacy does the work
Stealth XTM in and out. No account takes part in a sale. Both sides fund it from stealth UTXOs and are paid back into new Pedersen-committed outputs, transferred from inside the template with Bucket::stealth_transfer.
Cryptography on-chain. The DLEQ check, the key derivation and the decryption all run in the template on the engine’s Ristretto255 and SHA-512 intrinsics.
No identity in the authorisation. No action is authorised by the transaction signer. Every call carries a Schnorr proof from a key derived for this listing or this purchase, so a seller’s listings and a buyer’s purchases cannot be grouped by key, and a hostile component called in the same transaction cannot act for anyone.
The secret’s shape is hidden too. Fixed-size blocks hide its length; a 32-byte salt under the commitment stops a short secret being brute-forced before anyone pays.
Being plain about the limits: a dispute makes the secret public (that is what lets anyone re-check the verdict); the template proves you got exactly what was committed, not that it is worth the price; and stealth coins still link to their own history like any UTXO without decoys. The README and the threat model spell all of this out.
How it was hardened
Three adversarial review passes. The second found a real flaw in an earlier design (a linear key tweak let a seller pull another sale’s secret out of a buyer through a dispute); the proof of knowledge of r closes it, and the attack is replayed in the tests. Every attack in THREAT_MODEL.md maps to a test that plays it and checks it fails for the stated reason.
40 attack tests: forged and replayed proofs, malleable scalars, redirected payouts, hostile template, ledger scans for the secret.
9 end-to-end sales, each payout actually spent afterwards to prove the payee controls exactly what it was owed.
24 client and CLI tests, including property tests.
A crypt CLI does the client side offline: keys, commitment, sealing, opening, proofs. It refuses to sign a payout the template would refuse.
Built against the published crates (tari_template_lib 0.34, tari_template_test_tooling 0.45).
Update – 18 October: play it, and see it on esmeralda
Play it (no wallet, two minutes):Crypt · the cursed crypt
Take a side, seller or buyer, play honest or cheat; a ghost plays the other side. Every step runs the real crypt_core client code compiled to WebAssembly, and the verdict comes from a port of the template’s rules that CI checks against the template itself, call by call (tests/equivalence.rs). When a cheater is caught, the coffin opens and shows what was delivered next to the promised seal, nibble by nibble, with the DLEQ proof and the split.
Watch a game (100 s):
Live on esmeralda: the template is published (template_b7282faa…61bf, wasm sha256 in the README) and two sales were played for real:
an honest sale, accepted, both sides paid: list → buy → deliver → accept → pay_seller → pay_buyer
a fake delivery: the buyer disputed, the template decrypted it, emitted Fraud, and paid the buyer everything: Veil — Tari Ootle Explorer
Every transaction is linked from the README and from the demo’s “Attacks repelled” screen.
A fourth adversarial review, every finding fixed with a failing test first: listings now expire, the seller signs every term at list (a clone of a listing must pay the real price), and the terms that turn a dispute into a trap are refused (seller bond at least 10% of the price, buyer bond at most the price, dispute window at least the delivery window). A test of random hostile games checks the money stays straight. 37 attacks are documented in THREAT_MODEL.md; 36 are played against the contract in CI on every change.
Update – 22 October: play a real sale with Sapient
The demo has a new ON-CHAIN · SAPIENT screen: one real sale on esmeralda, signed and paid by your own wallet through the Sapient extension (window.tari). You play both sides:
list: the seller bond goes into the escrow in the same transaction (withdrawStealthAndExecute hands it to the template as a bucket);
buy: price and buyer bond, the same way;
deliver: the secret sealed to your buyer key;
accept: your browser opens the delivery, checks it against the listed seal, and only then signs.
Keys, sealing and the action proofs stay in your browser (the same crypt_core code as the CLI, compiled to WebAssembly); Sapient only signs and pays. One limit, said on the page: the payouts are stealth transfers built with the Rust SDK, which a browser wallet cannot sign yet, so the testnet XTM stay in the settled escrow.
What it is
Phantom Drop turns TARI into a secret link. Whoever holds the link can open it and receive the funds as a stealth UTXO — even with no wallet and zero balance. If nobody opens it before an expiry epoch, the link dies and only the sender can take the money back. Optional riddle locks make the link useless without the answer, so you can post a link publicly: first to solve it wins.
How it uses Ootle’s privacy primitives (no custom template)
The envelope is a stealth output: Pedersen commitment + Bulletproof, encrypted value/memo, sent to a “ghost” stealth address derived from the link.
It is locked by a 2-leaf MAST spend-condition tree, and only the 32-byte root is on chain:
Ootle Payment Gateway for WordPress — a stealth-mode WooCommerce checkout
This is the spookiest place to spend it: a WooCommerce store that accepts TARI without revealing what was bought, by whom, or for how much — every order settles toward a one-time stealth address and carries an on-chain encrypted memo only the payer and merchant can read. The “Spooky Secret” is the purchase itself.
A drop-in WooCommerce payment gateway that routes checkout through the Tari
Ootle wallet daemon’s JSON-RPC API. Customers pick “Pay with TARI” at
checkout; the plugin opens a payment session with the daemon, redirects to the
Ootle payment URL, and completes the order when a signed webhook confirms
payment. Six webhook events are handled: completed, failed, cancelled,
refunded, dispute created, dispute resolved.
What makes it spooky is the privacy layer, built around Tari Ootle’s
confidential primitives:
One-time stealth addresses — every order settles to a fresh stealth
output, so orders can’t be correlated to one another by watching a single
merchant address.
Pedersen commitments + Bulletproof range proofs — amounts are committed
and proven in-range (no overflow, no negative-value fraud) without being
written in the clear. The price of a purchase stays a secret.
Encrypted on-chain memos — the order reference travels as an encrypted
memo: the payer proves why they paid without exposing the link on-chain.
Confidential resources — stealth-capable tokens settle end to end privately.
The checkout is confidential by default, not by an opt-in a merchant might
forget.
Verified on-chain. A real confidential stealth transfer was executed and
accepted on the Esmeralda testnet (wallet daemon v0.45.0):
1.5 TARI fully blinded into a Pedersen commitment, settled to a one-time
stealth address derived from the recipient’s public key, with an encrypted
on-chain memo — the amount, recipient, and order reference are all
unreadable on-chain. Confirm it on the explorer: Veil — Tari Ootle Explorer
Also built and verified on a live WordPress Studio site (PHP 8.4, WooCommerce
11.2.1): gateway registration, admin settings, signed API requests, webhook
signature verification, and all six webhook events.
Crypto inheritance usually means handing over a seed phrase now or trusting a custodian. A dead man’s switch fixes the timing, but on a transparent chain it tells everyone who holds the funds and who will inherit them, which makes the heir a target.
Witching Hour is a Tari Ootle template that keeps the switch enforceable on-chain but hides the people:
new(deposit, keeper_key, heir_commitment, interval_epochs, sealed_note) locks tTARI and sets the deadline (the “witching hour”) to now + interval.
heartbeat(): the keeper checks in with a one-time key and pushes the deadline forward. A late heartbeat fails.
claim(heir_key, salt, payout): after the deadline, the heir reveals a key and salt matching the commitment, signs with that key, and is paid out as stealth outputs.
cancel(payout): the keeper can take everything back (as stealth outputs) while alive.
top_up, view, sealed_note, heir_commitment_for cover funding, liveness and client helpers.
Where Tari’s privacy does the work
Hidden heir: the vault stores only SHA-256("witching-hour/heir/v1" ‖ heir_one_time_pubkey ‖ salt). Nobody can tell who will inherit until the claim, and the salt stops brute-forcing a list of known keys.
No wallet identity on the switch: every sensitive call is authorised by checking the transaction signer against a one-time Ristretto key for this vault, never an account.
Stealth payouts:claim and cancel leave through Bucket::stealth_transfer with a StealthTransferStatement, so the receiving addresses and the split are hidden behind Pedersen commitments.
Sealed note: up to 1 KiB of ciphertext for the heir (instructions, a farewell), encrypted off-chain and stored as opaque bytes.
Liveness without identity: anyone can see that the keeper is still checking in, but not who the keeper is.
Honest limits: the locked balance is public while it is in the vault; heartbeats from the same keeper key link to each other (but not to a wallet); fee and funding sources can link if they aren’t stealth-funded; claim timing is public. All of this is spelled out in the README.
Verification:cargo test runs 6 tests against the real Ootle engine in-process (tari_template_test_tooling 0.45): heartbeat by keeper vs stranger, early claim rejected, wrong salt / wrong signer rejected, heir claims at the witching hour and the vault closes, keeper cancel, stranger cancel and late heartbeat rejected. The release WASM builds with cargo build --release --target wasm32-unknown-unknown.
Built against tari_template_lib 0.34. Developed with an AI coding agent; design, review and submission are my own.
On a transparent chain a “sealed-bid” auction isn’t sealed: everyone sees who bid and how much, so the last bidder snipes, the seller learns your ceiling, and your wallet history tells the world what you collect. Sealed Coffin is a Tari Ootle template (Rust → WASM) for a second-price (Vickrey) sealed-bid auction in which bids stay sealed until bidding closes and no bidder ever shows an account.
open(seller_key, lot, reserve, deposit, bid_window, reveal_window) — anyone opens an auction for a lot (only a 32-byte hash of it is on-chain). The seller key is a one-time key; the component has no owner.
bid(bidder_key, commitment, signature, deposit) — the bidder locks exactly deposit XTM spent from a stealth coin and posts only SHA-256(domain ‖ auction ‖ amount ‖ salt ‖ bidder_key), signed by a fresh one-time Ristretto key.
reveal(bidder_key, amount, salt, signature) — after bidding closes the template recomputes the hash; any mismatch is rejected.
finalize() — anyone settles: highest revealed bid ≥ reserve wins and pays the second-highest bid. Bids that never open forfeit their deposit to the seller (anti-grief).
refund / collect — losers, the winner’s change and the seller’s proceeds leave the escrow through Bucket::stealth_transfer into fresh stealth outputs, each signed by the payee’s one-time key over the exact StealthTransferStatement. Where Tari’s privacy does the work
Stealth XTM in and out. Deposits are spent from stealth UTXOs, every payout goes to new Pedersen-committed outputs. No account takes part in an auction.
No identity in the authorisation. No call checks the transaction signer. Every action carries a Schnorr signature (engine intrinsic, custom domain) from a key that exists for one auction only, so a bidder’s bids — or a seller’s auctions — can’t be grouped by key.
Amounts hidden while it matters. Every bid locks the same deposit, so the ledger shows that someone bid, never how much; the 32-byte salt stops anyone brute-forcing a commitment over a small price range, and binding the auction address and key stops copying a commitment elsewhere.
Un-redirectable payouts. A payout must spend exactly what is owed, only from escrow, only into stealth outputs; the signature covers the whole CBOR-encoded statement. Honest limits
Revealed bids become public at reveal time and the winner/price after finalize — this hides bids during bidding and hides who bids, it is not a ZK auction. The deposit is public and caps every bid. Fees, timing and the stealth coins spent in can still be correlated like any UTXO without decoys. Lose the one-time key, lose the refund. The lot itself is off-chain (the template settles money only). Not yet deployed to Esmeralda — tested on the in-process engine. All spelled out in the README. Verification
cargo test --locked → 7 end-to-end tests, 7 passed, 0 failed on the real Ootle engine in-process (tari_template_test_tooling 0.45): a full 3-bidder second-price auction; wrong reveal (bad salt, bad amount, double reveal); late reveal + forfeited deposit; foreign key at every step (bid, reveal, refund, collect, and replaying a genuine refund signature onto a different statement); losers refunded exactly once (over-claim and double refund rejected); timing and deposit rules; nothing sells below reserve.
19 attack/negative assertions each checking the exact rejection reason; 14 stealth payouts in the suite are each spent again afterwards to prove the payee controls exactly what it was owed; the escrow ends at 0 in every settled auction.
Mutation check: disabling the reveal signature check makes foreign_key_is_rejected_everywhere fail.
cargo build --release --target wasm32-unknown-unknown --locked → sealed_coffin.wasm, 238,978 bytes; cargo clippy --target wasm32-unknown-unknown -- -D warnings clean.
Built against tari_template_lib 0.34.0. Developed with an AI coding agent; the idea, direction, review and submission are my own.
Hauntboard — Public Messages, Sealed Conversations
A little mystery behind the messenger.
My entry for this month’s Spooky Secrets contest is Hauntboard, a public message board built on Tari Ootle.
The idea is simple: conversations can be public without requiring a persistent wallet identity to be attached to every post. Hauntboard explores this through a spooky message board where full threads, replies, and encrypted message payloads are stored in Tari smart contract state.
Two ways to talk
Hauntboard supports both public conversations and optional sealed threads.
Public threads
Read public threads and replies without unlocking a wallet.
Create threads and reply through a browser wallet.
Explore conversations stored directly in Tari Ootle contract state.
Download the source and run your own client against the same published board.
Sealed threads
Create an optional encrypted conversation and share access through an invitation link.
Message bodies are encrypted locally in your browser using AES-256-GCM before publication.
Thread titles and categories remain public, while the contract stores the encrypted message payload.
Readers with the invitation link can decrypt the conversation without a wallet or test TARI.
Use Copy secret invitation to share access through a trusted private channel.
Invitation keys have a separate encrypted backup and are not restored by your wallet recovery phrase. Anyone with the invitation can read the full conversation history and future messages, and can share the key with others. There is no key revocation or authenticated author identity, so treat invitation links as secrets.
How Hauntboard uses Tari’s privacy capabilities
For public posting, Hauntboard uses Tari’s native stealth outputs, with proofs and transaction signing handled locally in the browser. Each post uses a fresh ephemeral transaction signer, and the board contract does not store an author public key. Wallet keys and recovery words stay in the user’s browser.
The goal is to reduce stable wallet linkage between posts while keeping public conversations accessible.
For sealed threads, browser-side encryption protects message bodies from being published as plaintext. These are two distinct privacy layers: Tari’s stealth transaction flow helps reduce exposure of persistent wallet identifiers, while AES-256-GCM encrypts sealed message content.
Neither mechanism guarantees anonymity or complete confidentiality. Transaction timing, funding patterns, fees, public metadata, network activity, and shared invitation keys can still reveal information or expose conversations.
More evidence behind the privacy claims
I’ve added a dedicated privacy test suite and published a clearer threat model. The recorded checks include:
38 Node test entries covering privacy, wallet, provider, and submission behavior.
7 native contract engine tests, including immutable history, conflicting retries, and unauthorized direct page writes.
10 browser scenarios each in Chromium and Firefox, covering sealed messages, invitation access, draft handling, and recovery after a lost submission response.
Local tests using the real Tari SDK/WASM to check that one wallet can build posting transactions with different one-time spend keys while omitting its persistent owner key.
A separate read-only transaction audit checking for reused signers, shared inputs, and direct output links.
These tests provide evidence for specific behaviors, not proof of universal unlinkability or anonymity.
The website update is deployed and has passed live asset and browser checks. A funded, same-wallet multi-post demonstration on Esmeralda remains a separate evidence step.
A few things to know
Hauntboard runs on the Tari Esmeralda testnet.
Test TARI has no monetary value.
Published posts cannot be edited or deleted.
Public threads are the default; sealed threads are optional.
Sealed thread invitations grant access to anyone who possesses the key.
Update — October 23, 2026: Ghostline v0.4.0 and a live browser demo
Try it in the browser. The demo page runs real voucher signing in your browser. With no wallet, five simulated calls show the flow and then a recorded Esmeralda settlement. With the Sapient wallet on Esmeralda, you can run it live: approve one private 51 µT test transfer, and a testnet relay opens a real channel. Five paid calls are signed in your browser, then the relay settles once. The page shows your own channel-open and settlement transactions with explorer links.
What’s new since October 19:
Live Sapient mode. A hosted run on October 23 used one private deposit (funding 4dad1dcf…) and opened the channel in 87b2e645…. Five 10 µT calls (50 µT) followed, then one settlement, f5dafe5c…. All three finalized. The settlement has the same claim-free, two-output shape as a command-line run: the deposit and amounts are hidden in its public fields.
Command-line run on the current template (October 22). Open 9986c19d…, five paid HTTP calls, then settlement 1684aad9…, finalized.
Ghostline v0.4.0 packages the CLI, reference paid API and MCP server for Linux x86-64. Run ghostline demo after installing for the local HTTP 402 → voucher → paid-call flow; it needs no wallet or funds.
A recorded-runs page lists every Esmeralda transaction behind Ghostline, with explorer links and their status. The site also has a glossary for the ghost vocabulary and Ootle terms.
How the live demo works, and what it trusts: the hosted relay is custodial and testnet-only. It receives the 51 µT deposit, opens and settles the channel, and can return the deposit after expiry. Your browser keeps the voucher-signing key, so the relay cannot sign a higher total for you. Observers can see that the relay’s account paid the fees. Use test funds only.
Update — October 19, 2026: Ghostline v0.3.1
Ghostline now has a confidential, fixed-shape settlement flow, verified on Tari’s Esmeralda testnet. What changed since the October 17 proof: The earlier run demonstrated claim-free settlement and expiry refund transactions with no plaintext amounts in the inspected fields. It still had a variable settlement output count, which could signal whether the full deposit had been spent.
October 19 — v0.3.1: Settlement now always creates two stealth outputs by reserving 1 µT for payer change. A fresh Esmeralda run recorded 20 paid calls in the service ledger and finalized one settlement. The chain confirms the settlement, but does not prove the call count or prices. The update also documents fresh per-channel keys and an experimental pooled settlement; the pooled coordinator sees the deposit openings, and observers can see which channels were grouped.
What changed
The channel deposit and settlement amounts are masked in the tested transactions. The payer keeps the deposit’s secret mask locally; the service knows the channel’s declared deposit limit and sees each request, price, and voucher. The latest Esmeralda open and settlement records did not expose the deposit, service amount, or payer remainder in their public transaction fields.
Settlement always creates two stealth outputs. Ghostline reserves 1 µT for payer change, so the output count no longer signals whether the entire deposit was spent or some remains.
Ghostline supports fresh keys per channel, with an optional separate service payout key. This reduces correlation from reusing keys across channels or later payouts. Both channel and payout keys are still visible in the same settlement, so this does not make the payer and service unlinkable.
Expiry refunds were tested on the latest template. The refund finalized without a plaintext input or output amount in the inspected transaction fields. The exact refund amount was not independently unblinded in that run.
An experimental two-channel pooled settlement also finalized. It is not coordinator-private: the local builder handled both deposit openings, and observers can see both channels grouped in one transaction. Treat it as a prototype, not a recommended hosted service.
In a separate run, the service ledger recorded 20 paid HTTP calls at 10 µT each, followed by one finalized settlement. The call count and prices come from that local ledger; the chain proves transaction finality, not those off-chain calls.
Fees, timing, transaction and channel metadata, and funding patterns remain visible. Ghostline is unaudited and intended for Esmeralda testnet use.
What it does
Ghostline gives AI agents a way to pay for services one request at a time without putting every payment on-chain. It is designed for agent workflows where a service may charge a small amount per API call and the agent needs clear spending limits.
Here is the payment flow:
The payer opens and funds a Tari Ootle channel with a service.
When an agent requests a paid endpoint, the service can return an HTTP 402 quote describing the price, recipient and expiry terms.
The payer checks that the quote matches the expected channel and recipient, then signs a cumulative voucher for the accepted payment total.
The service verifies the voucher and serves the request. Later vouchers increase the cumulative total, so calls can be accounted for off-chain instead of creating a separate chain transaction for each one.
The channel can be settled against the latest accepted voucher. After expiry, the payer can refund an unspent channel.
Ghostline supports fixed-price, per-request and metered quotes. Its policy engine can enforce spending limits and require human approval for configured cases. The decision audit is recorded locally. The CLI provides channel and payment operations; the Rust SDK and TypeScript voucher support let applications integrate the payment flow; the MCP server exposes paid-call and channel tools to agent hosts.
Esmeralda evidence - October 17th
I tested a claim-free channel settlement and a separate expiry refund on Esmeralda. Both finalized:
For these tested transactions, observers cannot read the channel deposit or plaintext payment amounts from the public transaction fields. This does not hide the activity from the service: it sees each request, price and voucher. Observers can still see fees, timing, the executed branch, channel metadata, authorization data, and output commitments and counts. The transactions prove on-chain finality; they do not prove the off-chain call count or prices.
Ghostline is unaudited and intended for Esmeralda testnet use.
Séance: anonymous, one-voice, sealed-ballot polls verified inside the Ootle
Code (MIT): Radicle Explorer Try it in your browser: https://seance.madebydevx.com
Announcement (Bluesky): @devxtools.margin.cafe on Bluesky
Tari payment address: 14DQWqt4gHNwH1t57o3ZVqy64bbqu1P3zDkH2sKEKqDqVhUVu4xWQD7pGGZm29nehPJ7DKgMUNTMNE8ZuudNRXHFosi
Live proof: PROOF.md in the repo (Esmeralda tx links below)
Built by DevX (Made by DevX) with an AI coding agent. Every line was tested against the real Ootle engine, and the live transactions are on Esmeralda for anyone to check.
The problem
A council, a team or a guild wants to ask its members a question and get an honest answer. Each member must have exactly one voice, nobody may see who voted what, and the result must not be visible until voting closes, so nobody can jump on a bandwagon. Today the choice is a Google Form (you trust the organiser with everything) or an on-chain vote (everyone sees your wallet and your choice).
Why the Ootle and not a transparent chain
On a transparent chain the voter’s address, its funding trail and the fee payer give the voter away, even if the ballot itself is hidden. Séance uses three Ootle features to cut each of those links:
Engine crypto intrinsics. The template verifies a linkable ring signature (LSAG over Ristretto255) with ristretto_msm and hash_sha512_parts. The proof says “one of these N keys signed” and never which one. Last month’s Private Ballot kept its proofs off-chain; here, eligibility, one-voice and the sealed tally are enforced by the template itself.
Fee intents + Vault::pay_fee. A poll can carry a fee pot. The voter’s cast runs in the transaction’s fee stage, the template checks the ring signature first, and only then pays that transaction’s fee from the pot. The voter needs no account and no funds. The transaction is sealed by a key created on the spot, so no wallet, balance or funding trail ever touches the ballot.
Any-key sealing.cast never looks at the transaction signer, so there is nothing to link.
What the contract enforces
Only members of the circle (2–64 public keys, fixed when the poll opens) can cast.
One voice per member: the key image is a per-poll pseudonym. A recast replaces the old ballot, and pseudonyms from two polls can’t be linked.
Ballots are sealed (SHA-256(poll ‖ key image ‖ choice ‖ salt)) until casting closes, and the tally stays at zero until then. Reveals are refused early, and late casts are refused.
No owner, no admin methods. Nobody can change the circle, the tally or the pot after opening.
Fee pot: anyone can fund it, nobody can withdraw. Fees are paid only after a valid signature, capped per transaction and per member. Worst case, a member spends per_member × cap, and only by casting a valid ballot.
It ships as two reusable pieces:
ootle-ring. A standalone crate: drop anonymous membership + per-scope pseudonym into any template in about 5 lines.
Confessional. A second example built on it: an anonymous whistleblower inbox with stable pseudonyms and a per-member post cap.
Honest limits
Limit
Status
Lost receipt = uncounted ballot
Real. Mitigations: anyone holding the receipt can reveal it, and seance reveal --auto submits as soon as the window opens
Receipt holder learns the choice; a voter can prove their vote (not coercion-resistant)
Real; commit-reveal is not receipt-free
Revealed choices are public per pseudonym
By design (verifiable tally); pseudonyms are unlinkable to members
Network-level linking (IP, timing)
Out of scope; use a relayer / Tor
Without the fee pot, the fee-paying account is visible
Use --sponsored, or one shared relayer
Small circles = small anonymity sets
Inherent; up to 64 members fit the execution budget
Not externally audited
63 tests, including 34 attack tests; only engine intrinsics are used, with canonical checks on every point and scalar
Verification numbers
63 tests, all passing (cargo test --workspace):
34 named attack tests, each mapped in THREAT_MODEL.md (46 named attacks in total)
Attacks covered include forged rings, c0 + ℓ / r + ℓ scalar malleability, identity and non-canonical key images, cross-poll and cross-app replay, sub-ring and reordered-ring signing, ballot stuffing, reveal hijacking, pot draining, and pay_fee outside the fee stage.
A 64-member circle fits the execution budget. A sponsored cast cost 6,343 µT in the engine.
Pinned to what Esmeralda runs today: tari_template_lib 0.34.0 / Ootle 0.46.1. seance.wasm is 221 KiB.
131ee448, 70ad9f5a, 99b1c168 (no account involved; fee paid by the poll)
Reveals
4fd45bdc, 5b8a1a83, 0df90746
edd842f8, 8515426c, 08e981ac
Final tally
Trick 0 / Treat 2 / Both 1
Yes 2 / No 1
Full clickable explorer links for every transaction are in PROOF.md in the repo (explorer.tari.mw). Funding: free tXTM from the public Esmeralda faucet, burned to Ootle and claimed (L1 burn tx 582843927904147713).
Or run everything offline: cargo test --workspace plays every attack against the real engine.
Update, 22 Oct 2026
Browser demo at seance.madebydevx.com. Hold a five-seat séance: cast, recast (the new ballot replaces the old one), let an outsider try, replay a signature, guess who voted, then close and reveal. The page signs with a JS port of the ring signer and runs the template’s verification logic. A new Rust test (ring/tests/js_interop.rs, 26 vectors including tampered ones) checks that the native verifier, which mirrors the template, accepts exactly the signatures the page makes.
Live re-check. The page reads both Esmeralda polls from the public indexer and recomputes their poll id, circle hash and H in the browser. All match what the template stored, and no ballot pseudonym equals a member key.
Added the Bluesky announcement and the Tari payment address, which were missing from the original post.
AI disclosure: designed and written by DevX (Made by DevX) with an AI coding agent. All cryptography uses the Ootle engine’s intrinsics and is covered by the tests above.
**Live on Esmeralda testnet (audit-hardened v2):** template `5e8f7b22fab899c5dc37c00caf9aeb53c3099a1ec2baff0b218354e37a6444db`, auction `component_7eb4f5f468cd23f01be92423459102b3e3d0760bdf1a578b93e64a00e159a184` — sealed bids, reveals and settlement are all on the public testnet.
A sealed-bid auction template for Tari Ootle built for “Spooky Secrets”. While bidding is open, the chain sees only three things per bidder: a public key, a Blake2b commitment, and a bond that is identical for everyone. The bid amount **does not exist on-chain until its owner reveals it** — no escrow leaking your maximum, nothing to snipe during the sealing phase.
- `place_bid(commitment, refund_account, bond)` — the client computes `blake2b(“sealed_bid/v1” ‖ component ‖ pk ‖ blinding ‖ amount_le)`; the template never sees the amount
- `reveal(blinding, amount, payment)` — proven against the seal after the sealing window closes; bidders whose bid meets the reserve get the bond back immediately, a below-reserve reveal forfeits it
- `settle()` — highest bid at or above the reserve wins (earliest reveal breaks ties); losers are refunded; **ghost bids lose their bonds: they stay locked in the contract forever**, paid to no one — bids that stay sealed past midnight pay for their silence
Settlement is pull-based: `settle()` only records the winner, and every party claims their own funds (`claim_lot`, `claim_proceeds`, `claim_refund`) — no failed deposit can ever lock someone else’s money.
### Why it fits the theme
Privacy here is structural, not decorative: amounts are absent from chain state during sealing, not hidden inside it. The official `nft_marketplace` starter explicitly leaves blind auctions as a TODO (“requires cryptography support”) — this template closes that gap with native Ootle primitives (hashing intrinsics, `CallerContext` identity, consensus epochs, vault escrow).
### Extras discovered on the way
- The public Esmeralda faucet was underfunded by exactly its own claim size (pays a fixed 1000 TARI, held 999.65). We worked around it by mining L1 test Tari solo and bridging it L1→L2 through a burn — the same bridge the demo client automates on any network.
- The project went through two rounds of AI-assisted adversarial security review; every accepted finding is fixed and covered by 17 integration tests (`tari_template_test_tooling`), including a 20-bidder scale run.
Seance: three ghosts read one private Tari payment
A different project from the Séance polls entry in post 15; the names match by chance.
Seance is a small web page for the Tari Ootle Esmeralda testnet. It shows what three observers can read from one private transfer. The demo payments are real testnet transactions that we sent from a throwaway account. The page reads them from the indexer (or from recorded data, labelled as such), and a key you paste never leaves the page.
The 60-second check. Open the app, leave demo 1 picked and press “Hold the seance”.
The Stranger holds no key. It sees the public fields and the sealed notes as real bytes (144 and 80 bytes long).
The Medium holds the recipient’s view key and reads the amount (0.00031415 SEANCE) and the memo (“Midnight at the old crypt. Come alone.”). A wrong key hears nothing.
The Auditor holds the audit key of a demo token (created here from our own 34-line template). It counts both amounts (0.00031415 + 0.00018585) but cannot open the memo.
Demo 1 is the real transaction 891b43606b…8a2ace. There are 15 demos, and “Summon” loads a recent private transfer by someone else when there is one.
What still leaks. Sealed notes hide the amount, the memo and the receiver, not everything: the token, the spent notes, the times, the fee and the size of each locked message stay public (80 bytes means no memo, more means one is inside). A fourth ghost, the Poltergeist, holds no key and ties all 11 of our TARI demo payments together by timing, fee and spend graph; address reuse finds none and amount matching one (it shows its amount on purpose). That is how our demo wallet behaved (11 payments in 135 seconds, same fee), not a claim about Tari in general. An audit key opens every amount of its token. Testnet only, not audited.
MIT licence. Code: Kylne700/seance. Built with an AI coding agent (Claude Code).
On transparent blockchains (like Ethereum or Solana), creating an on-chain riddle or bounty is fundamentally broken: mempool bots monitor the broadcast, copy the solution, front-run the solver with higher gas fees, and dox the winner’s wallet.
Grave Robber solves this using Tari Ootle programmable privacy and the Sapient Web Extension:
Anti-Frontrunning Preimage Hashlocks: Riddles are sealed with SHA-256 preimages verified client-side so answers never touch the mempool in plain text.
Unlinkable Payouts (sendPrivately): Bounties are delivered directly to one-time otl_esm_1... stealth addresses through the Sapient Web Extension, leaving zero trace of the recipient’s main wallet on-chain.
2D Catacomb Platformer: Built as a playable retro side-scroller where players traverse dark catacombs with dynamic lantern lighting, climb stone stairs, inspect sarcophagi, and plunder stealth XTR bounties.
Dual Mode & Sandbox Simulator: Features both an interactive 2D catacomb explorer and a quick Crypt Ledger view, with an integrated testnet simulator so anyone can test the full cryptographic flow immediately.
HideBox is a private drive on the web. Files are encrypted in your browser. The server gets only noise. Built on Tari.
Tari does the private parts
Pay to open: they pay your Tari address, then it opens. The server never holds the money. On the chain, the amount is hidden and the owner is a one-time key.
When a file leaves your drive, SealLog, the hidebox template on the Ootle, writes that on the chain within 15 minutes, for anyone to check.
Right now is in Beta, built on top of Esmeralda testnet, using tTARI (test money).
Every business runs payroll, and none of them can run it on a transparent chain: a block explorer would show every salary, raise and severance to coworkers and competitors. Payroll can’t be a black box either, because accountants and tax auditors have to check the books.
Graveyard Shift is an Ootle template that does both. No salary ever appears on-chain, each employee reads their own pay stub straight from the chain, and a designated auditor reconciles everything with a view key that can’t spend a cent.
The four lanterns (same three paychecks, four keys)
Holds
Sees
Public
nothing
budget, roster, “shift paid, 3 paychecks”, opaque commitments. No amounts in state, events or receipts
Employee
own key
own amount + private memo (“36h nights + 4h overtime”), decrypted from their output
Auditor
resource view key
every amount: 3000 + 2500 + 1313 + 3187 treasury = 10000 budget . No memos, no spending
Coworker
any other key
nothing
The browser demo lets you flip between the four lanterns. It replays a real engine run (every commitment and ciphertext on the page comes from cargo run --example haunted_payroll).
Where Tari’s privacy does the work
Confidential resource + Bulletproofs. The SHIFT payroll token is confidential. Paychecks are ConfidentialWithdrawProofs with a hidden output and hidden change. Revealed amounts are rejected, so a salary can’t leak by accident.
Resource view key (ElGamal). It’s locked on the token at creation. The engine refuses any output without a ViewableBalanceProof, so the employer can’t pay off-book, and a test proves it.
Encrypted output data. Each paycheck carries a pay stub DH-encrypted to the employee (amount, mask, memo, with padded length). No side channel is needed. Treasury change is encrypted to the employer, so its books can be rebuilt from chain state alone.
Selective disclosure (payslips). An employee hands one landlord the opening of one paycheck. The landlord recomputes C = m·G + v·H and checks it sits in that employee’s vault. Inflated, borrowed and forged payslips fail.
One-transaction shifts.pay_shift pays the whole crew atomically with a single ShiftPaid {period, paychecks} event: no names, no amounts, no per-person timing inside a run. One bad paycheck reverts the lot.
Value proofs on mint. The total budget is public, and every split stays private.
Evidence
26 tests on the real Ootle engine (tari_template_test_tooling 0.47). They include a whole-receipt scan for a salary in decimal, little-endian bytes and hex, exact event field checks, a month of shifts with a raise, an exit and a top-up that still reconciles to the public budget, and an attempt to spend the treasury with the auditor key.
THREAT_MODEL.md maps 17 attacks to the test that plays each one, and lists what still leaks.
CI on every push: WASM build (~190 KB), clippy -D warnings, the full test suite and the narrated demo.
cargo run --example haunted_payroll runs a narrated seven-act night shift in the terminal (GIF in the README).
Honest limits
The roster (account, role, paid-this-period) and the total budget are public. That’s deliberate, because an auditable headcount needs them. Stealth-UTXO paychecks to hide recipients too are next. The auditor key is fixed per payroll. Testnet: it’s not on Esmeralda yet because the public faucet is currently empty (999.65 tTARI held against a 1,000 claim), so everything is verified on the in-process engine. Unaudited.
Why it matters for the Ootle
Payroll is a real, recurring, every-business use case that transparent chains can’t serve. Graveyard Shift shows the Ootle can: private by default, auditable on demand, and disclosable one payslip at a time. The same pattern covers grants, contractor pay, DAO stipends and bonuses.
Built with an AI coding agent under my direction. All code is new for this contest (October 2026), MIT licensed.
Familiar: a private, network-enforced allowance for AI agents
An AI agent gets a wallet it cannot empty. Its budget is hidden stealth UTXOs (or hidden coupons); the Ootle engine, not the agent’s code and not a server, enforces a per-payment cap, a window limit, a payee allow-list and a pause switch. The owner can take everything back through a recall leaf that names a random key of its own: that key appears on-chain only in the recall transaction, never before. In coupon mode no µT amount reaches the chain: not a payment, not the budget, not what is left (the number of coupons is public).
Try it yourself in about 3 minutes: prebuilt familiar binary (Linux / Windows / macOS) and a tTARI tap on Esmeralda, no Rust needed: tap-status → wallet → tap → leash → pay → ask (prompt injection) → pause → recall. See “Try it” in the README; a CI workflow walks the same path from a fresh wallet in 129 s.
What it shows
Run on Esmeralda: 27 transactions, 17 accepted, 10 refused by the network, each with its exact reason (over the per-spend cap: the covenant in the UTXO; over the window, a payee not on the leash: the leash component; spending outside the leash, a stranger calling pay: the engine).
Prompt injection: an obedient agent is fed a spoofed owner email, a hidden instruction in a web page, an upsell and a fake receipt; every bad payment is refused on-chain. With a real LLM (gemini-flash-latest) through an MCP server whose only payment tool is the leash, the model resisted three of four by itself; the leash refused the one it obeyed.
Privacy, named honestly: one table of what an observer sees and what stays hidden, and a threat model with every leak we know of (THREAT_MODEL.md). An observer script rebuilds that view from the public indexer only.
Tests: 49 engine tests on the real Ootle engine (every refusal asserts its reason), 72 in total, clippy and rustfmt in CI; external reviews (Jules, Gemini) in docs/reviews.
Videos: docs/familiar_story.mp4 (59 s, captions) and docs/walkthrough.mp4 in the repo.
Why not just an API key with a scope: a key that may send may send everything. Familiar puts how much and to whom in the network, so it holds even when the agent’s host and key are compromised.
Built with AI assistance (Claude, as coding agent). Prototype, unaudited, testnet only.