Part 1 of 2 on how free2z accepts Zcash. Infrastructure and a real production purchase verified October 12, 2026. Part 2 introduces Zcash Payment Hooks, our open-source daemon for turning shielded payments into application events.
When you send ZEC to buy credits on free2z, software has to recognize your shielded payment and turn it into an account balance. We want that to feel natural: send from your wallet, see payment detected, then 2Z credited, and watch your balance update without a refresh.
We run a Zakura full node on a host called Picante to supply the chain data behind that experience. We also wrote a daemon that turns incoming payments into events our application can act on. Both are running in production.
We've been building with Zcash since free2z began in March 2022. Keeping its infrastructure healthy has not always been easy or convenient. But the ecosystem has changed enormously, and we are excited about the Zcash stack taking shape in Rust. Operating it—and contributing something useful to it—is work we are happy to take on.
From zcashd to a Zcash stack built in Rust
Our original receiving system used zcashd. It followed the blockchain and held the wallet in the same process. Our application queried its wallet RPC for incoming shielded notes and their memos, then credited the appropriate free2z accounts.
zcashd's retirement meant separating those responsibilities. Its final release reached its end-of-support halt on July 18, 2026, at block 3,417,100, ahead of the Ironwood network upgrade it did not support. The official deprecation notice describes the node and wallet migration paths.
Zebra made this transition possible. The Zcash Foundation built a new consensus implementation from the ground up in Rust, drawing on Zcash's core libraries and collaboration with ECC. The 2023 announcement of the move to Zebra was a major step toward the modern architecture we get to build on today.
Zebra is an important, actively maintained implementation. The Foundation's continuing work on independent verification and conformance benefits the whole network. Zakura builds on Zebra's code and engineering; that ancestry deserves explicit credit.
Our migration therefore involved more than changing a binary. We needed a consensus node to follow the chain, a receiving process to recognize our payments, and application code to decide what each payment buys. We briefly used zcash-devtool as a receiving stopgap. Our production receiver is Zcash Payment Hooks, connected to our own Zakura node.
Why Zakura fits us
Zakura is a consensus-compatible fork of Zebra, developed by Valar Group and Project Tachyon. It follows the same Zcash network. Our choice combines practical operator needs with enthusiasm for where that team is taking the software.
Pruning and snapshots make ownership practical. We can keep the chain state and recent blocks our services need without storing a complete archive. Snapshot bootstrapping gets an installation to useful state quickly, with a trust tradeoff we explain below.
The engineering direction interests us. Valar Group and Project Tachyon are working on synchronization, networking and the cryptography for a larger Zcash network. Their Zakura Common and wallet-library work connects node engineering with the experience of everyday wallet users. We want to test our integrations alongside that work and contribute concrete feedback from a running product.
Their engineering notes make that direction tangible: faster wallet scanning, better field arithmetic, proof-verification work and network stress tests. Some work is landed; some is research or development. We like that breadth, and we want our production integration to keep learning from it.
We want to own the operational details. We choose the release and configuration, inspect the chain view, and investigate receiving problems in services we control. We can test the exact RPC behavior our application needs rather than assume a replacement node will behave like zcashd.
Rust is part of that enthusiasm, and Zebra pioneered the node side. With Rust nodes, reusable Zcash cryptographic libraries and our own small Rust receiving daemon, this ecosystem gives us components we can assemble and improve. For free2z, that opens up a much more interesting future than one monolithic node-and-wallet process.
We want to stay close to Zakura's latest releases. That means making upgrades routine: reading what changed, testing our dependencies, and verifying the live deployment. Upstream performance benchmarks and ambitious future features are reasons to pay attention, not promises we make about Picante.
Picante: the chain, the receiver and the application
Our production payment path starts with local chain data:
Zcash mainnet │ ▼ PICANTE: Zakura full node consensus + chain state │ ┌────────────────┴────────────────┐ │ │ Full-block RPC Local lightwalletd │ compact-block interface ▼ for light wallet clients Zcash Payment Hooks viewing-key decryption + private payment journal │ detected / confirmed events │ ▼ free2z payment application memo → account → recorded credit + notification + durable event publication │ ▼ Browser over WebSocket payment status + updated balance
Each component has a specific job:
| Component | What we use it for |
|---|---|
| Zakura | Follow the Zcash chain and validate its consensus rules |
| lightwalletd | Serve a compact-block interface backed by our node |
| Zcash Payment Hooks | Recognize our incoming shielded payments and deliver payment events |
| free2z's application | Decide what the payment buys and record the account credit |
| Browser client | Show payment status, notifications and the authoritative balance |
The receiving daemon reads full blocks through Zakura's RPC. lightwalletd is a separate service alongside that path, not the backend our payment daemon synchronizes from. Our production receiver does not depend on a public lightwalletd provider.
Our receiver uses a Unified Full Viewing Key to recognize incoming shielded payments and decrypt their memos. It holds no spending key. Viewing authority is sensitive information: we keep the key and payment journal private.
Pruned is a storage choice
Picante runs Zakura in pruned mode. We keep chain state and a rolling window of block data rather than an archive of every historical block. That makes the storage cost manageable while the node follows and validates the chain.
We bootstrapped from a published pruned snapshot. This shortened setup, but it also introduced a trust assumption: we imported existing state instead of independently replaying the entire chain from genesis. Checking a download's checksum catches corruption or a mismatched file; it does not prove that the supplier's chain state is correct. After bootstrap, the node follows and validates subsequent blocks.
Our receiver needs full blocks from its configured starting height and saved scan position. If those blocks fall outside the node's retained history, it cannot reconstruct them from that node.
We monitor the receiver's progress alongside the node's tip. An outage that outlasts retention requires recovery from a backend that retains the missing history. The receiver stops rather than silently skipping payments. Pruning also limits which historical wallet restorations a node can support.
A full node and an archive are different commitments.
We operate a consensus node with bounded historical storage. We also accept the trust involved in our snapshot bootstrap. Both facts belong in an honest description of our infrastructure.
Keeping up is part of running the node
As checked on October 12, Picante runs Zakura 1.6.0, which is also the latest published release at that check. We pin its container image by content digest so the deployment names specific bytes.
A digest pin is useful, but it is not a maintainer signature. Zakura's download and verification guide provides signed checksums and build attestations for release assets and explicitly notes that its Docker images are unsigned. Artifact identity and artifact provenance are separate questions.
There is a more immediate operational deadline, too. Zakura releases have an end-of-support block height. For 1.6.0, Mainnet support extends through 3,536,467; the node refuses to continue after that height. The threshold is recorded in the tagged support implementation.
Installing that release tomorrow does not start a new support window. The chain keeps moving toward the same cutoff.
We monitor the running node's getdeprecationinfo response and the distance between its tip and that cutoff. Calendar estimates help us plan, but the enforced boundary is a height. We aim to upgrade with enough runway to investigate surprises, rather than wait for the built-in warning near retirement.
Our upgrade checks follow a few concrete questions:
- What changes? We read the release notes for consensus changes, security fixes, RPC changes and database format changes.
- Can we recover? We preserve appropriate state backups and check rollback compatibility before a migration. An older binary may be unable to open newer state.
- Did the intended software reach the host? We compare the declared deployment with the running artifacts and configuration.
- Does it work on the live chain? We check advancing height, connected peers, required RPCs, lightwalletd responses and the reported support height.
- Can we receive payments? We check the daemon's scan position, fresh health reports and delivery queues separately from node health.
The receiver's version, dependencies and journal need review and backup independently of Zakura. External price data and our crediting application also need their own health checks.
A container can be alive while its chain tip is frozen. A node can be healthy while a receiver has stopped scanning. A receiver can be caught up while its application hook is failing. Our monitoring has to distinguish those conditions.
What the buyer sees
2Zs, or Tuzis, are free2z's platform credits. Buying them with ZEC connects an on-chain payment to an account balance inside free2z.
We use a shared receiving address. The shielded memo tells our application which account should receive the credits:
{"act":"buy_2z","id":"your-username"}
That instruction can buy credits for yourself or someone else. You can send from a wallet or automate a purchase without visiting our UI. The username names the recipient account; it does not authenticate the sender. Optional promotion metadata can accompany the instruction without replacing these two fields.
Our wallet link and QR use a ZIP-321 payment URI containing the address, amount and memo together. The memo travels in Zcash's shielded memo field.
No memo, no automatic account credit.
Check the wallet's actual memo field before sending and use a shielded payment that preserves it. Transparent transfers cannot carry this instruction, and ordinary exchange withdrawals are unsuitable. If purchasing is unavailable in the UI, resolve that first rather than bypassing the warning by sending directly.
The number displayed before sending is a conservative estimate, not a fixed quote or guaranteed minimum. After at least three confirmations, our backend uses the ZEC actually received and fresh server-side price data to calculate the credit. free2z currently charges no Zcash purchase fee or settlement spread; wallet and network fees are separate.
Detection starts with a mined block, not a mempool signal. Block inclusion, scanning, confirmation and application processing all contribute to the wait. The first message means we recognized the payment; the second means the account credit was recorded.
A real payment, all the way to the browser
On October 12, a production purchase estimated at roughly 1,000 2Z arrived as 0.00867775 ZEC. Our receiving daemon detected it through Picante's local Zakura node. The browser showed the payment-detected message, then the completed-purchase message and the updated balance without a refresh.
The backend credited 1,112.081 2Z at its processing-time rate. It settled at five confirmations, satisfying our policy of at least three. This was a real payment through the production system, not a manually entered credit. Its timing is one observation, not a promise about every transaction.
We checked the records as well as the screen: one payment receipt, one credit, and a recorded purchase notification. Migration checks verified that historical credit receipts were preserved unchanged.
That distinction is essential. Hook delivery can repeat after a crash; our financial application uses durable payment identities and transactional records so retries cannot issue the same purchase twice. Live publication also has durable retry records. The browser's manually dismissed status messages accompany persistent notifications, and the balance comes from the account's authoritative state.
We can follow the path from a block we serve to the credit a person sees. That is what makes this infrastructure worth operating.
Next: payment → event → your code
Running Zakura solved the chain-data side. Applications also need a small receiving layer: recognize a shielded payment, preserve its identity and memo, and trigger code when it is observed or confirmed.
We built that layer as Zcash Payment Hooks: a receive-only Rust library and Unix daemon, zcash-paymentd, available under MIT and Apache-2.0. It uses Zcash's cryptographic libraries, keeps a private SQLite journal, and passes versioned JSON events to configured executables. Successful exit acknowledges an event; failures retry. Our free2z crediting rules live in our application, so other projects can attach their own automations. The core is early operator software, not an audited wallet, and its at-least-once delivery contract requires handlers to make their effects idempotent.
Part 2 will open up that interface: how to subscribe to payments, handle memos and confirmations, recover from retries and reorganizations, and connect your own code. Our reason for publishing it is practical: if you run Zcash infrastructure, accepting a private payment should give you a usable event to build on.
We want private payments to become an ordinary part of the software people use every day. Running the node and sharing this receiving layer are our contribution to that future.

