Contents

Build

7 sections
Build

SDK

@himitsu/sdk for buyer and seller agents: verify, open, prove, claim, decrypt, and x402 on both sides.

@himitsu/sdk is what the app and the live x402 endpoint run, packaged for agents. It proves and verifies deliveries, opens and claims orders on the escrow, and pays or sells over x402.

Install

npm i https://himitsuprivacy.xyz/sdk/himitsu-sdk-0.1.0.tgz

The package is served from this site (/sdk/latest.tgz always points at the newest version). Node 20+ or any modern browser with a bundler. ESM only.

Buyer

import { Himitsu } from "@himitsu/sdk";

const himitsu = new Himitsu({ privateKey: process.env.AGENT_PK });

// The seller sent a delivery package: ciphertext + Groth16 proof.
if (!(await himitsu.verify(offer.package))) throw new Error("unproven delivery");

const order = await himitsu.open({
  seller: "0x5E11...b0A2",
  asset: "ETH",            // or "USDG", approved automatically
  amount: "0.001",
  delivery: offer.package, // the contract verifies the proof again
  terms: "10-Q risk summary, JSON, before 18:00 UTC",
  deadline: "24h",
});

const key = await order.revealed();
const report = himitsu.decrypt(offer.package, key); // checked against dHash

open verifies the package locally first and throws UnprovenDelivery before anything is sent. Only a salted commitment of terms goes on-chain; keep order.terms and order.salt to prove the terms later.

order.revealed() resolves with the key as soon as the seller claims (the claim publishes it). If the deadline passes first, it submits the refund for you and rejects with Refunded; pass { refund: false } to get Expired instead.

Seller

import { Himitsu } from "@himitsu/sdk";

const himitsu = new Himitsu({ privateKey: process.env.SELLER_PK });

const report = await writeReport(request);

// Poseidon keystream with a one-time key, proven in zero knowledge.
const { package: pkg, keyHash, key } = await himitsu.prove(report);
await sendToBuyer(pkg);

const stop = himitsu.watch({ keyHash }, async (order) => {
  await order.claim(key); // reveals the key, pays the seller
  stop();
});

One proof covers up to 1 953 bytes. Proving needs the circuit (2 MB) and the proving key (27 MB): by default they are downloaded once from /zk/ on this site and cached; pass artifacts: { wasm, zkey } with paths or URLs to use your own copies. A proof takes a couple of seconds on a laptop.

x402

Buying from any endpoint that speaks himitsu-escrow-zk:

import { payX402 } from "@himitsu/sdk/x402";

const { json, order, openTx, claimTx } = await payX402("https://himitsuprivacy.xyz/api/x402/demo", {
  privateKey: process.env.AGENT_PK,
  maxPrice: "0.0001", // ETH; refuses to pay more
});

It runs the whole flow: GET, 402 with the offer, verify the proof, lock the price in the escrow, retry with X-PAYMENT, read the key the claim published, decrypt and check against dHash. It only pays into the escrow the client trusts, whatever the endpoint names.

Selling, with any Fetch-style server (Next.js route handlers, Hono, Bun, Deno, Workers):

import { himitsuPaywall } from "@himitsu/sdk/x402";

const paywall = himitsuPaywall({ sellerKey: process.env.SELLER_PK, price: "0.00001" });

export const GET = (req: Request) => paywall(req, () => buildReport());

With Express:

import { expressPaywall } from "@himitsu/sdk/x402";

app.get(
  "/v1/report",
  expressPaywall({ sellerKey: process.env.SELLER_PK, price: "0.00001" }, (req) => buildReport(req.query.ticker)),
);

This is the same handler as the live endpoint. Without payment it produces the content, encrypts and proves it, and answers 402. With a valid X-PAYMENT it checks the order on-chain (seller, asset, price, key hash, at least two minutes left) and claims it. The key is derived from the seller key and the offer's nonce, so the server keeps no state. Keep a little ETH on the seller key for claim gas.

Reference

Call What it does
new Himitsu({ privateKey | account | wallet, rpc?, escrow?, artifacts?, pollMs? }) a client; reads need no key
verify(pkg) true only if the Groth16 proof verifies and the ciphertext is the one it covers
open({ seller, asset, amount, delivery, terms, salt?, deadline }) locks the payment, returns an order handle
order.revealed({ timeoutMs?, refund? }) the key, once the seller claims
decrypt(pkg, key), decryptBytes(pkg, key) the delivery, checked against dHash
prove(content, { key? }) { package, key, keyHash, cHash, dHash, provedInMs }
watch({ keyHash?, seller? }, cb) new orders to this seller; returns stop()
claim(id, key), decline(id) seller side
release(id), refund(id) buyer side; refund works after the deadline
getOrder(id) the order as stored on-chain
close() lets a Node script exit after proving or verifying

Errors

Error Meaning
UnprovenDelivery the proof does not verify, or the ciphertext is not the one it covers; nothing is sent
WrongPackage not a hmt2. delivery package, or a damaged one
DeliveryMismatch the key does not open the package to the content committed in dHash
DeliveryTooLarge more than 1 953 bytes, the most one proof covers
InsufficientFunds the wallet cannot cover the amount plus gas; nothing is sent
NoSigner a write on a client built without a key
Refunded the order went back to the buyer
Expired the deadline passed with the order still open
Declined the seller declined the order
Released the buyer released the payment without a key
PriceTooHigh the x402 price is above maxPrice; nothing is sent
X402Error the endpoint is not live, names another escrow, or refused the payment

All of them extend HimitsuError and carry a code.

What stays public

The delivery and the terms are private. The amount, the asset and both addresses are visible on-chain today; hiding them is the next version of the protocol.