zkv z:kv
Alpha · for testing, not production

A key-value database on Zcash.

z:kv stores key-value pairs as signed shielded memos on Zcash. Anyone with the zkv1… address can read; only authorized keys can write. Great for feature flags, price oracles, and other small public data.

github.com/zecrocks/zkv · MIT / Apache-2.0

~/projects/zcash-oracle
$ zkv init
Creating mainnet zkv database "default"
 
Recovery phrase. Write these 24 words down NOW.
 
  wisdom fabric essence bottom luxury tower
  arrest quick ozone raccoon defy spy …
 
✓ Created database "default" (mainnet, birthday 3338983)
 
$ zkv set zec_usd_price 1008.33
✓ broadcast tx 96feedf9…584166214
 
$ zkv get zec_usd_price
zec_usd_price = 1008.33

Download

Desktop browser and CLI binaries, published on GitHub Releases.

Builds aren't code-signed yet, so macOS Gatekeeper and Windows SmartScreen will warn on first launch. The install guide covers building from source.

Screenshots

The z:kv desktop browser. Click any image to enlarge.

CLI Example

Download the zkv CLI from the GitHub releases page.

watch a database
$ zkv watch zkvtest1sv30yl48750pcp6vvp69gnyuwu3szkh7rd4c2fp697umvqctwushne0upxfz8mx6mutx5gcavuuwxgw72juzv2va3v0rxumgx36gjs0f66almkcwry2gga84w0z0tjdfp43vykckt2qu0nkz5c2c7efedkjddstdrsfzvrlgwtmnnw05v0rk5hng3dq3y3s22y6d5ct00u6g4vrranefw88jpjl9d7a2zfu3nwrxd2zsk4sff4dnx40s92nfx8e7fgr8uctwhgsjs3um4xg5a49ysx6cs3x9ar5vuum0kvdm07q68jnsag0xnaa43kmv7vkm9ek678dvppf2rh8ysafgtr432skfx2ytzwp2txf
✓ Watching database "watch-st1tvm05" (now current)
  Syncing from birthday 4055623…  Done.
  Run `zkv get` to fetch state.
 
$ zkv get
# Database: watch-st1tvm05 (watch, testnet, sapling)
announce/0 = Governance polls are open, check the forum to participate.
feat/enable_flexa = 1
feat/enable_maya = 0
feat/enable_near = 1
feat/enable_orchard = 1
ios/latest_version = 3.5.3
ios/minimum_version = 3.5.0
servers/st/0 = eu.zec.stardust.rest:443
servers/st/1 = us.zec.stardust.rest:443
servers/zr/0 = zec.rocks:443
servers/zr/1 = eu.zec.rocks:443
servers/zr/2 = na.zec.rocks:443
servers/zr/3 = sa.zec.rocks:443
servers/zr/4 = ap.zec.rocks:443
servers/zr/tor/0 = vzzwzsmru5ybxkfqxefojbmkh5gefzeixvquyonleujiemhr3dypzoid.onion:443
servers/zr/tor/1 = kxn4wla7i4rczcpn7ljbtmtq4gd4xhtx6f6hxpze7qbfmu66atwsneqd.onion:443
 
$ zkv set prices/zec_usd 1088.31
SET prices/zec_usd  broadcasting…
✓ broadcast tx 9f7089c6…56d4df294
9f7089c6f9c5222eee213724c9d516a8828f69a3e24fcec4e4025cf56d4df294

Reference

A zkv database is a key-value store whose entries are signed Zcash shielded memos. The chain is the source of truth; everything on disk is a rebuildable cache.

Design

zkv stores a key-value log entirely on the Zcash blockchain. There is no separate database server; readers reconstruct state by scanning the chain with a viewing key and replaying the signed memos.

Chain as the source of truth

A "database" is a Unified Full Viewing Key plus a birthday height, living in a single shielded pool (Orchard by default, or Sapling). A write is a zero-value shielded output to the database's own address carrying a Memo::Text that begins with the wire magic ZKV0. Same-block ordering is (mined_height, txid, output_index); last write wins.

ZKV0 SET zec_usd_price 1008.33
0285d48bd429fd9382bb54145ce66107c8faee6fb38c4e55fccb8d44f9c0bd542d…

Snapshot + tail replay

Scanning every memo on every read would be O(total writes). Instead a local zkv_state.sqlite snapshot holds the projection of memos buried past a reorg-safe depth (100 blocks); only the recent live tail is re-verified in memory per read. The snapshot is a cache, safe to delete; it rebuilds on the next read.

Recoverable signatures

Each write carries a 65-byte recoverable ECDSA signature, so the reader recovers the signer's pubkey from the signature itself; the memo carries no pubkey field. That's what lets a database have many authorized writers without growing the wire format.

Receiver-binding & replay protection

Signatures commit to the database's receiver (derived from the viewing key) and a network tag, not the address text, so a memo signed for one database, or one network, can never verify against another. Each key and management target carries a tombstone-surviving high-water counter folded into the signing domain, so a verbatim re-broadcast is dropped as StaleVersion and can't revert a value or resurrect a deleted key.

Owners and writers

The root (UFVK-derived) key becomes owner #1 when INIT confirms, and is the permanent creator. Owners may write anything and manage the registry; writers are limited to a scope of CREATE / UPDATE / DESTROY. Authorization is enforced identically by every reader (it's just replay), so it holds with no central server. An owner can FINALIZE the database, a one-way latch that seals it permanently.

Privacy caveat: the address is a viewing key. Everyone you share it with can read all values, forever, with no forward secrecy. Don't put secrets in it; it's a public, auditable KV log.

Identifiers

Every identifier is a self-describing, checksummed Bech32m token; a mistyped character fails the checksum rather than denoting a different thing.

zkv1…: the database identity
The Unified Full Viewing Key (transparent + one shielded pool) plus a private-use zkv-meta item holding the 4-byte birthday, under a zkv HRP. zkvtest1… / zkvregtest1… on other networks; the network is recoverable from the prefix alone.
uview…: the same key, relabeled
A zkv address is just a viewing key under a different HRP, so it relabels to a standard uview… that pastes into any wallet (Zashi / Ywallet / zcashd) to view the raw memos.
zkvid1…: a signer pubkey
The canonical Bech32m encoding of a secp256k1 public key. This is what the owner/writer registry keys on, what management memos carry, and what signatures commit to.

Opcodes

Every write is a ZKV0 memo. The signature line is [seq]<130 hex chars, a 65-byte recoverable ECDSA signature>; the optional [seq] prefix carries replay-protection state. Unauthorized and stale writes are silently dropped on replay.

SETData

Set a key to a value (compact single-line form).

ZKV0 SET <key> <value>
[seq]<sig>

Owner, or a writer scoped CREATE / UPDATE.

SETLData

Length-framed set for empty, multi-line, or binary values.

ZKV0 SETL <key> <byte_len>
<value, exactly byte_len bytes>
[seq]<sig>

Owner, or a writer scoped CREATE / UPDATE.

DELData

Tombstone a key. The version counter survives the delete.

ZKV0 DEL <key>
[seq]<sig>

Owner, or a writer scoped DESTROY.

INITLifecycle

Bootstrap the database. Root-signed, first-valid-wins.

ZKV0 INIT <zkv_addr>
<sig>

The root (UFVK-derived) key only; becomes owner #1 / creator.

OWNERSETManagement

Grant (or re-affirm) owner authority.

ZKV0 OWNERSET <pubkey>
[seq]<sig>

Owner-only.

OWNERDELManagement

Revoke owner authority. The last owner can't be removed.

ZKV0 OWNERDEL <pubkey>
[seq]<sig>

Owner-only.

WRITERSETManagement

Grant (or overwrite) a scoped writer. Scope ⊆ CREATE,UPDATE,DESTROY.

ZKV0 WRITERSET <pubkey> <scope>
[seq]<sig>

Owner-only.

WRITERDELManagement

Revoke a writer entirely (all-or-nothing).

ZKV0 WRITERDEL <pubkey>
[seq]<sig>

Owner-only.

VERSIONVersion

Announce the required client/protocol epoch (forward-compat gate).

ZKV0 VERSION <n> <flags>
<sig>

Owner-only.

FINALIZELifecycle

Permanently seal the database. A one-way latch.

ZKV0 FINALIZE
<sig>

Owner-only.

FAQ

Can anyone read my data?

Yes, if they have your database's zkv1… address. The zkv address is a viewing key, so anyone you share it with can read every value, now and forever. There's no forward secrecy, so don't store secrets.

Who can write?

Any authorized signer, broadcasting to the database's address. Owners may write any key; writers act within their CREATE / UPDATE / DESTROY scope. The creator (the root key that signs INIT) is the first authorized signer and becomes owner #1.

Does each write cost money?

Yes. Every SET / DEL / INIT / management memo is one Zcash transaction with a ZIP-317 fee, real ZEC on mainnet. Fund the database's funding address first.

Is there replay protection?

Yes. Every write to a key carries a version number, and readers only honor the next expected version, so rebroadcasting an old signed memo can't roll a value back or bring a deleted key back; it's recognized as stale and dropped. Your own back-to-back writes to the same key are fine, since the client counts its in-flight writes.

What's the difference between SET and SETL?

SET is the compact one-line form; SETL frames the value by byte length so it can be empty, multi-line, or binary. They're semantically identical, but a signature is bound to one opcode string, so they're not interchangeable. The client auto-promotes SET → SETL when needed.

Can I make a database read-only forever?

Yes. Broadcast FINALIZE. Once it confirms, the database is permanently sealed: every later write (including another FINALIZE) is dropped. The latch is one-way and can't be undone, so freeze only a finished dataset.