Repository navigation
Releases: cachet-zec/cachet
Release list
Cachet 0.6.0
Highlights
Atomic swaps between two browsers. Two people trade two assets in one OrchardZSA transaction: both payments land together or not at all, and nobody holds anything in between. The maker parks the units it offers in a one-off swap slot of its own seed, so the offer shows that slot and never the maker's main holdings. The taker builds, proves and signs the whole swap; the maker's page checks it pays what was asked and only then signs its own spend. Protocol, limits and alternatives in docs/adr/004-atomic-swaps.md.
A public swap board. /swaps lists open offers and carries the three messages of a swap, so nothing has to be passed by hand. The registry holds messages, never keys: a take is read back only by the maker and a countersignature only by the taker, each with its own token. Before a take holds an offer, the registry checks it without any key (the proof verifies, the binding signature balances every asset, the taker's spends are signed), so blocking an offer costs a real proof over real notes. Offers are held to the chain's recent Orchard roots: listed while a taker's wallet can still build on them, then marked stale, and the maker's open page posts them again on the current root.
One page to trade. The board, the wallet and the make or take form now live together on /swaps. The seed you open on the mint page comes with you and is still forgotten on reload. "You want" is a search over the registry's names, with the sealed image of each asset. The first scan of the chain reads pages of 100 blocks and downloads the next one while your browser reads the current one, and says how far it has come.
Identity anyone can recompute. Every asset page derives the asset id, the 64-byte Asset Digest and the description hash from the issuer key and the description (ZIP 227), in your browser, and shares the asset by link or QR code. Test vectors for other wallets and explorers are in packages/registry-spec/vectors.
Reading the chain.
/tx/<txid>decodes any transaction's public parts: what it issues, seals or burns, and how many shielded actions it carries. Issue-note recipients are public on chain; the page leaves them out on purpose.- An issuer can seal an open supply without minting a unit (
/mint?seal=<asset id>). - The registry can be shown as a gallery of sealed images and ordered by recent activity (issuance, burn or seal; shielded transfers never count). Asset pages list the board's offers for that asset.
Also
- The protocol stack moves to the corrected Orchard circuit, on QEDIT's new network
rpc.test-zsa.org, live since 25 September. - The README, the API reference (now with the swap board's routes) and the working paper, v1.6, describe this release.
- Server hardening: per-client offer budget, take holds of two minutes, verifying key built at startup, browser source maps never served.
Upgrading a self-hosted instance
Migrations 12 (the swap board's table) and 13 (assets.last_height, filled from the indexed events) apply at start. Going back to 0.5.0 is described in docs/DEPLOY.md.
Full list of changes: CHANGELOG.md. Working paper: https://cachetzec.com/cachet-whitepaper.pdf
Cachet 0.5.0
Testnet software. Everything here runs on the public ZSA test network, which is operated by QEDIT, not by Cachet.
Highlights
A new identity. Deep green, copper, Bodoni Moda, and a new mark: the scalloped edge of a pressed seal around an open ring that reads as a C. The favicon, both social cards, the README banner and the working paper carry it. The landing's engraved watermark draws itself in a loop, the mark's C last.
The asset page, redone. A register entry: the sealed image as a plate, the supply and whether it can still grow, two checks done in your browser and said in words, then the identifiers and the public history. The name and image shown are the ones the page verified itself, never the registry's own fields. An issuer's link is no longer shown: anyone can mint, and the page should not carry an address it has not checked.
Assets that survive a reset. A test network can be reset, and a reset takes every asset with it. The registry's journal and bundles are keyed by asset id and stay. The page of a lost asset now shows what was kept and offers to mint it again. The mint page loads the sealed name, text and image as they are, locks them, and tells you whether your seed gives the same asset id before anything is signed. GET /api/v1/kept lists them.
A registry that scales. The list, an issuer's page and the landing count ask for what they show, a page or a figure, and Postgres answers the page from indexes: a stored name rank for "named first", trigrams for text search, key ranges for id prefixes. GET /api/v1/assets takes offset, q, issuer, supply and order, with totals in three headers; without parameters it still returns everything. Listings carry an ETag and answer 304 when nothing changed. A read never waits behind a node call or a catch-up sync, and a chain reset replaces the index in one transaction. An asset's social card is drawn once and kept a few minutes, two at a time at most.
Better privacy at the one public moment. An issuance writes its first recipient in clear on chain. Issued units now go to a dedicated diversified address of the minter's own wallet, which cannot be linked to the address a person shares to be paid. Browser engine and server both.
Less dependence on this instance.
- The mint page can hand you the signed transaction instead of relaying it, and does so on its own if the relay refuses.
scripts/restore.pygives a mirror back to any running instance through the public routes: no database access, no operator permission.scripts/verify-site.pychecks that a live site serves the mint worker and wasm engines committed to the repository, byte for byte.- A console can offer several registries to read from, fixed at build time.
- Every sealed bundle has an IPFS address derived from the hash the asset id already commits to;
scripts/mirror.py --ipfspins them to your own node.
Also
- An API reference at
/reference, built from the OpenAPI contract the server exports. - A support page with the donation address and what a donation does not buy.
- Heights and transaction ids link to a block explorer when the deployment names one.
- Every page has a canonical URL and its own title and card on a shared link.
- Motion tied to meaning, all of it still under reduced-motion settings. No library.
- Working paper v1.5, figures re-measured.
- An operator page redesigned, pinned to its own instance.
- The public instance's write paths were adjusted to keep it running smoothly for everyone.
Upgrading an instance
- Database migrations 9, 10 and 11 apply at startup: derived columns and indexes only, plus the
pg_trgmextension. Pull a backup first.docs/DEPLOY.mdsays how to go back. - The wasm engines changed: rebuild or use the committed ones, then
python scripts/verify-site.py --site https://your.site. - New build-time settings, all optional:
CACHET_REGISTRIES,CACHET_EXPLORER_BLOCK_URL,CACHET_EXPLORER_TX_URL.CACHET_CORS_ORIGINnow comes from.env.prodand accepts several exact origins.
Full list: CHANGELOG.md.
Cachet 0.4.0
The release that draws the line around what a name is. The registry now knows exactly two kinds: a name sealed into the asset id by the Cachet envelope, verified in the browser, or an issuer's free-text label shown for what it is, unverified. Recognition of a third-party descriptor convention, and the manifest route that served it, are gone. Around that: a mint studio that hides the seed by default and queues instead of failing, an asset page with a lightbox and its own social card, a registry with filters, a landing that leads with the operator's picks, a terms page, and an operator page that can purge content rather than merely hide it. Under the surface, every write path is throttled per client, the daily backup actually runs, and the working paper is at v1.3.
Live at cachetzec.com, against the public ZSA testnet.
Removed
- Recognition of a third-party descriptor convention, its manifest resolution route and the IPFS gateway setting that served it. Assets carrying that convention's descriptors are listed like any other asset with a free-text description: displayed, never as a verified name.
- Working paper v1.3 (3 September): the registry section and the references follow the change above; nothing else moved.
Added
- The generated seed is masked by default (fixed-width dots, so even word lengths stay private), with a show/hide toggle. Copy still copies the real phrase while masked: the safe path is the default path, and a screen recording of a mint no longer exposes anything.
- The sealed image opens full size in a lightbox from the asset page; click or Escape closes.
- Every asset page has its own social card: the sealed image beside the name, the supply and the supply state, in the site's lockup. The card reads through the public API, so moderation applies by construction: a hidden issuer yields a generic card, a hidden or purged bundle a card without picture.
- Registry filters that compose with the search: supply state (all, sealed, open) and "named assets only".
- The landing showcase leads with the operator's featured assets (
CACHET_FEATURED_ASSETS, baked at build time), then fills with the newest attested entries. The landing is editorial; the console still lists everything. - A terms and content policy page (
/terms), linked from the footer: what the instance is, what may not be uploaded, what the operator does about it, privacy in one paragraph, and how to report content. - The operator page lists the assets carrying content with their thumbnails and offers per-row actions: hide the bundle, hide the issuer, or purge the bundle. Purging deletes the bytes from this registry's disk (hiding only withholds them), for content an operator must not keep; the moderation entry stays so the same bytes are refused if uploaded again, and the chain commitment is untouched.
Changed
- Below-the-fold landing sections play their entrance when they become visible instead of at page load. The testnet stamp presses itself when the notice is actually being looked at, the four steps settle line by line, and reduced-motion users get the content with no animation at all.
- The social card drops the keys line, as the landing and banner already did.
- A finalized supply is stamped "sealed forever" in the asset card's corner, the same press as the landing's testnet stamp; the open state stays an inline chip.
- The mint studio treats a 429 from the relay as "queue", not failure: it waits for a slot (up to 8 times, 2.5 s apart) and keeps the proof.
- Every API error response carries
Cache-Control: no-store; a cached 410 could otherwise keep an unhidden bundle "hidden" in a browser. - Working paper v1.2 then v1.3: the abuse-surface paragraph describes the per-client throttles and their salted, process-bound key; the OpenAPI document declares the 429 responses of the relay and upload routes.
- The host setup drives the daily backup from a systemd timer: a minimal Debian ships no cron daemon, so
/etc/cron.dailyalone ran nothing on the public instance for two days. Documentation reconciled with the code throughout.
Security
- The Next.js image optimizer is disabled: the console never uses
next/image(every image is a plain<img>whose bytes are verified in the browser), so the/_next/imageendpoint and the sharp/libheif surface behind it, where two critical advisories lived in August 2026, both already fixed in the pinned 15.5.24, no longer exist on the server. - Dependabot no longer proposes the Next major: it is a deliberate migration done by hand, and the security fixes are backported to the current line.
- Write paths are throttled per client, not per instance. The global upload counter let one client starve every other browser mint at near zero cost; the relay held its lock for a full 10 s wait even after the node had rejected the block. Uploads are now budgeted per client, relays capped per client in flight, and a
submitblockverdict is a rejection immediately. The key is a salted hash of the address (salt drawn at boot, memory only, never logged), documented in PRIVACY.md P2. - Issuer-level moderation is applied on the description-resolution route too; it previously bypassed it.
- The admin token comparison hashes both sides to a fixed size first, so the configured token's length no longer shapes the timing.
.gitignorecovers every.env*file, not just the local variants.
Full changelog: v0.3.0...v0.4.0
Cachet 0.3.0
The release that closes the trust chain. The asset page now re-derives the asset id itself from the issuer key and the served description (ZIP 227), through a dedicated 247 KB verification engine with no proving circuit in it, so "trust the math, not the registry" stopped being half true. Around it: a continuity page that answers what survives this instance, a registry that surfaces attested names instead of burying them, one meaning per word for "sealed", and a security posture where every advisory is either fixed or documented with its migration already built.
Live at cachetzec.com, against the public ZSA testnet.
Added
- The asset id itself is verified, in the browser. The page used to re-hash the metadata bundle against the
sha256inside the on-chain description and call that "trust the math, not the registry". Nothing checked the description itself, so a registry serving a fabricated description with a matching fabricated bundle passed with a green badge. ZIP 227 makes identity derived rather than assigned, so the page now recomputes the asset id from the issuance validating key and the served description and compares it with the id it asked for: a registry can serve any description, but not one that derives the right id. The check covers every asset with a description, so ZMD-1 and free-text names are checkable too, not only sealed ones. cachet-verify-engine: a second, much smaller wasm module (247 KB against the mint engine's 4 MB) carrying the derivation and nothing else: no Halo2 circuit, no keys. Loaded once per session, only when there is a description to check, and tested against a real asset confirmed at testnet height 520.- A continuity page, /continuity, answering the question infrastructure gets asked and rarely answers: what happens if this instance disappears. What would be lost, what would not, and how to take over without asking anyone, including the honest remainder that a successor inherits neither the domain nor the signing key.
GET /api/v1/assets?resolved=truenarrows a listing to assets whose description is known. The landing showcase uses it: most of this testnet is script-minted under a shared demo key with no description, so an unfiltered showcase was five rows of hex.- The files a public site is expected to serve:
robots.txtwith the sitemap pointer,sitemap.xml(entry points only, since asset pages change with every block), and an Apple touch icon that Safari probed on every visit and turned into a console error. The site origin behind them is a build argument (CACHET_SITE_URL) like the API origin, so a fork's robots.txt stops pointing crawlers at this deployment.
Changed
- The registry lists attested names first: sealed, then ZMD-1, then unverified label, then unresolved. Reordering, not filtering: every asset stays listed, the count is unchanged, and one click returns to strict chain order.
- The verification badge states what was actually established (full verification, description only, or bundle only) instead of one fixed sentence. An asset id mismatch outranks a bundle mismatch, because it makes everything under it moot.
- One word no longer means two things. "Sealed" named committed metadata on the landing and finalized supply everywhere else, which is exactly how a reader concludes an unsealed asset has editable metadata (it does not: the description hash derives the asset id). The landing step is now "Bind"; both supply states are stated instead of one being the absence of a stamp ("sealed" / "open supply", the latter in the attention colour since it carries dilution risk for the holder); and reissuing announces itself before proving and on the receipt, instead of silently adding units to an existing asset.
- Names in the landing showcase carry their provenance visibly ("zmd-1", "unverified"): the anti-phishing display rule applied where only typeface used to carry it.
- Working paper v1.1: every engine measurement re-taken after three rebuilds had drifted them (4.0 MB module, 1.56 MB zstd on the wire, 74 percent; load restated as 24 ms warm / ~0.5 s cold), the provenance point made with a chain-wide aggregate instead of naming one project's batch, issuer moderation verbs added to the CLI listing, P1 through P8, and no heading is ever the last thing on a page.
- Documentation reconciled with the code as it ships: the three non-chain-derivable tables named consistently everywhere, the GC keep-set described as the resolved description journal (what the code does) rather than "on-chain assets" (what it does not), and DEPLOY's one-time setup no longer omits the third mandatory secret.
- CI actions moved off the Node 20 runtimes before their removal (checkout v7, setup-node v7, cache v6, upload-artifact v7, pnpm/action-setup v6), with automatic package-manager caching opted out in the one job that installs no package manager.
- The navbar and footer GitHub links land on the repository rather than the organization page.
Fixed
- The verification engine was served with
max-age=0, so every asset page revalidated 247 KB per visit. Same cache rule as the mint engines now: an hour, plus a stale-while-revalidate day. - The mint page printed eight copies of a wasm-bindgen deprecation warning, one per spawned thread, from the generated rayon worker bootstrap. Same call, object form, silence; re-applied by the engine build script so the next rebuild does not undo it.
- In attested order the ledger index counted down from the registry total, labelling the best-attested assets as the chain's newest. Rows are numbered by rank there; the countdown remains only in chain order, where it is true.
Security
- The four postcss advisories are resolved rather than accepted:
nextpins postcss 8.4.31 exactly, so the patched line was unreachable until a pnpm override reached it. Verified against the build and the end-to-end suite. - The critical halo2_gadgets soundness advisory (under-constrained base in variable-base scalar multiplication) is now described in SECURITY.md as what it is, with the exposure reasoning stated so a reader can disagree with it. The full migration to the fixed circuit is built and tested on
chore/bump-qedit-stack, blocked on consensus rather than on this repository: the fix changes the verifying key, and the deployed testnet still verifies with the old one. Tracked in #3, reported upstream in QED-it/zebra#164.
Full history in CHANGELOG.md.