Skip to main content
This page is for a developer setting the KYC-STARKs tool (trusset-kyc-zk-proofs) up for the first time. After reading it you will have built the tool from source and produced a bundle for a sample subject. You will have verified it twice, once with your operator key and once with its root. Nothing here is skipped. Every command below was run in this order against a clean clone, and the output shown is what it printed. One substitution: where the tool prints an em dash, these excerpts show a hyphen.

Toolchain

You need three things on the machine: There are no prebuilt binaries. The first build compiles about 130 crates from the pinned Cargo.lock. On the machine used for this page it took just under four minutes; after that, builds are incremental and take seconds. Tested on macOS under Rosetta 2. Linux and Windows are untested. The key-loading step below assumes a POSIX shell.
1

Clone and build

npm install runs scripts/build.sh as a postinstall step: a release build of the Rust workspace, the WASM verifier when the wasm32 target is present, and the TypeScript CLI. The five native binaries land in target/release/: trusset-prover, trusset-verifier-cli, trusset-registry, trusset-archive, and trusset-list-commit.Every npm run command from here on re-runs that build script first, so each one prints a few [build] lines before its own output. When nothing changed, that takes about a second.
2

Initialise the working tree

init asks who is legally responsible for the assertions the tool will produce. Interactively it prompts for four values. To script it, pass them as flags:
In one pass it does six things. It generates both keys and writes them to .trusset/keys.env at mode 0600, adding that path to .gitignore. It writes a config.json with the batch date pinned to today and starting claim descriptors. It records version 1 of the sample allowlist in the signed registry. It checks the environment, and it prints your operator public key:
Your key will differ. An empty input/ is reported but does not block readiness.
init pins the batch date into config.json as "epochDaysSource": "fixed:<today>". Leave it pinned. Every per-subject key, nonce and leaf hash derives from that number, so a bundle proved against a moving clock cannot be reproduced tomorrow. Reproducing a bundle is exactly what an auditor asks for. The tool refuses to run unpinned unless you pass --dev-unpinned-epoch.
Running init again on an existing tree leaves config.json and the key file untouched and says so. --force replaces both, which makes every existing bundle unreproducible.
3

Load the keys

The prover reads both keys from the environment. Do this in every new shell:
Back the file up where you keep other operator-private material and never commit it. Lose the master key and no existing secrets.enc can be decrypted and no bundle can be regenerated. Lose the signing key and no future bundle can be signed under the same identity.
4

Add records

Create input/records.json with two subjects:
Both country codes are in the shipped sample allowlist of 17 ISO 3166-1 alpha-3 codes, and both tiers satisfy the generated config’s minTier: PROFESSIONAL. A RETAIL record would fail that predicate and be reported, not silently downgraded. Input records has every column.
5

Dry run

A dry run validates the whole batch through every stage, including the prover’s own predicate checks, reports every problem at once, and writes nothing. It needs no keys.
The -- is required: npm drops arguments that come before it. npm run hash is the same command as npm start.
6

Prove

Each subject took about seven seconds on the machine used here, almost all of it in the three set_membership proofs. Performance has measured numbers.The wallet is printed in its EIP-55 checksummed form, and that is the bundle directory name. Use that form in the commands below.
Four of the nine proof files are zero bytes long: firstName, lastName, documentType and documentNumber are commitment_only leaves, which carry a hiding commitment and no STARK. Bundle format describes every file.
The generated config.json sets security.clearInputAfterProcessing to true, so a real run deletes the files under input/ when it finishes. A dry run does not. Keep your own copy of the records if you need to re-run the batch.
7

Verify against your key

show prints the resolved configuration and, with the keys loaded, your operator public key. It is the key init printed.
Verify Alice’s bundle against it:
Exit code 0. Each leaf prints its claim descriptor, its validity window, and for set_membership leaves which list version the proof was made against.
8

Verify against the root

A counterparty who can read the kycHash from the chain does not need your key at all. The root authenticates the bundle. Here the root is read from the bundle itself, which is what a chain read would return:
Passing neither anchor is refused with exit code 1, because a bundle verified against nothing proves only its own internal consistency. Passing a wrong root fails with exit code 2. Verification explains what each anchor proves.

What to do next

  • Record where you published each root with npm run anchor, so a question about a past block can be answered without chain access. Then build an archive with npm run archive, one file that verifies on a bare machine. Both are on Registries and archives.
  • Review config.json. The claim descriptors init wrote are starting points that name a plausible instrument and provision. They are hashed into every leaf, so they are evidence, not comments. Configuration documents every field.
  • Replace the sample allowlist. The 17 codes in config/list_commitments/country_allowlist.source.txt are a sample, and recording a new version is one command.

In the repository