Cargo.lock, forbids unsafe code at the workspace level, and links no networking library. The TypeScript CLI has four runtime dependencies and npm audit reports no advisories at the 3.0.0 release.
Threat model
In scope
Out of scope
Key hierarchy
Two keys exist, both 32 random bytes generated bynpm run init with Node’s CSPRNG and stored in .trusset/keys.env at mode 0600. Everything else derives from them.
subject_id is the checksummed wallet address as ASCII bytes. config_hash is the SHA-256 of the canonical config projection described on Configuration, so editing the predicates re-keys every subject. circuits_version is 3, so a circuit-set bump re-keys every subject at once.
The nonce for a leaf depends on the leaf name, the circuit id and the parameters. An 18-year and a 21-year assertion over the same date of birth are therefore distinct leaves with distinct nonces and commitments.
Determinism
Undersecurity.deterministic: true, the default, two runs over the same records with the same keys, the same config and a pinned batch date produce byte-identical bundles. kycHash.hex, manifest.sig, secrets.enc and every proof file compare equal, and manifest.json differs only in createdAt. That is what lets an operator regenerate a bundle for an auditor, and it is what the repository’s canary test asserts.
The tool enforces it rather than assuming it. prover/src/entropy.rs is the only path to OS randomness in the prover, and it refuses to draw while TRUSSET_KYC_DETERMINISTIC=1 is set, returning E_DETERMINISM_VIOLATION. The CLI exports that variable for every deterministic run. A batch date taken from the wall clock is the other way a bundle becomes unreproducible, so utc_today is refused outside --dev-unpinned-epoch. A bundle built that way says so in its manifest for the rest of its life.
The cost is honest to state. Under deterministic mode the subject key is derived from the master key, so an adversary holding only the master key can reconstruct it without the bundle. The wrapping still keeps the nonce chain out of anything that leaks without secrets.enc, but it does not by itself defeat a master-key compromise. The wrapping benefit fully materialises only under --no-deterministic, where the subject key comes from OS entropy and exists nowhere except inside that subject’s wrapped blob. An operator who does not need reproducibility as an audit property can run that way and back up secrets.enc. A lost blob then means the subject must be re-run from the originating sources.
Determinism also means the proof bytes are a stable fingerprint of the witness and nonce. Two deterministic bundles for the same subject and batch date carry identical proofs, so a holder of both can tell whether a field changed without learning it.
Signing
The signing key produces deterministic RFC 8032 ed25519 signatures over the canonical manifest withcreatedAt set to the sentinel 1970-01-01T00:00:00Z, so two runs at different times produce identical manifest.sig files. The same key signs the three registries, so a counterparty who trusts the bundles trusts the registries without pinning a second key. The public key is printed by init and show and recorded in manifest.issuer.publicKey. manifest.issuer.keyId labels it, so a later rotation reads as a new key rather than a new issuer.
Master key rotation
Rotating the master key does not require re-proving:secrets.enc in place. The manifest, its signature, kycHash.hex and every proof are untouched, because secrets.enc sits outside the signing payload by design. Only secretsKdfVersion: 2 bundles support it. Under deterministic mode a re-keyed bundle can no longer be regenerated from the new master key alone, since its subject key was derived from the old one.
Domain separation
Every Rescue call is prefixed with one of ten 32-byte ASCII tags, padded with-, and pairwise distinctness is asserted at compile time for all 45 pairs. Adding a tag requires extending that matrix, and the workspace refuses to build if any two tags are equal.
The two allowlist tags differ from the two subject-tree tags, so a list entry can never be presented as a subject leaf. A padding leaf can never be presented as a member.
What lives on disk
Decrypting
secrets.enc needs the master key plus walletAddress, epochDays and configHash, all of which the manifest records. If a manifest or secrets.enc is lost but the records and keys remain, a deterministic run regenerates them byte for byte.
What the tool does not protect
Formal zero knowledge. Winterfell 0.13 adds no blinding columns and every circuit proves from a zero seed, so proof bytes are deterministic in the witness and the nonce. Hiding rests on the commitment nonce. The repository tracks this indocs/FUTURE_WORK.md as a proving-stack question.
Post-quantum signatures. ed25519 is pre-quantum. Root-anchored verification does not use it.
Key custody. Keys arrive through environment variables from a file at mode 0600. There is no HSM integration; the spec that the repository follows leaves it to a separate workstream.
preimage_knowledge semantics. That circuit establishes that the operator knows an opening of a commitment the operator generated, which is not a claim about the subject. Circuits states exactly what it does and does not establish, and commitment_only exists as the truthful alternative.
Two plaintext fields. country and investorType travel in the clear in the index CSV and manifest.passthrough. For those two, the proof binds the value and does not hide it.
Registry signatures read from a directory. The verifier checks the structure of registries under --registry and their signatures only inside an archive with --public-key.
In the repository
- docs/SECURITY.md, the full threat model and the disclosure table
- docs/AUDIT_PREP.md, the traceability matrix from each security requirement to code and test
- docs/RELEASE_GATE.md, the 3.0.0 gate results and the three findings of the launch security review
- circuits/common/src/seed.rs, circuits/common/src/domain.rs, prover/src/entropy.rs
