Skip to main content
@nullis/cli is the operator-facing tool. Before a policy touches the chain, the CLI checks it is well-formed and canonical; after a decision, it renders the Privacy Receipt in a readable form — for successes and blocks alike.

The policy manifest

A policy is a readable JSON manifest, hashed to policy_hash and registered on-chain with an active version.
{
  "policy_id": 1001,
  "version": 1,
  "action_type": "transfer",
  "asset": "USDC",
  "max_amount": 1000,
  "approved_root": "0x…",
  "app_domain": "remittance-ng-uk",
  "expiry": 1785000000
}

Field reference

FieldTypeMeaning
policy_idintegerUnique id, referenced by verify_and_execute
versionintegerBumped on every root rotation; stale versions are rejected
action_typetransfer | access_grantWhat the contract executes on success
assetstringThe asset the policy authorizes
max_amountintegerUpper bound the contract enforces (amount ≤ max_amount)
approved_rootfieldMerkle root of approved credential commitments
app_domainstringDomain separator for cross-app unlinkable nullifiers
expiryunix tsAfter this, all proofs are rejected
policy_hash is computed over ordered fields, never over raw JSON — field order and whitespace would make it non-canonical. The CLI enforces the canonical structure so what you publish is exactly what the circuit and contract expect. See Cryptographic design.

Validate a manifest

nullis policy validate policy.json
✓ schema — all required fields present, types valid
✓ canonical — fields ordered; no extra keys
✓ policy_hash = Poseidon(policy_id, version, action_type, asset,
                         max_amount, approved_root, app_domain_hash, expiry)
  → 0x1f6b…c04a
Policy is publishable.
If a field is missing, mistyped, or the manifest has extra keys, validation fails before anything is committed on-chain — you never publish a policy the circuit and contract would disagree with.

Render a Privacy Receipt

The receipt renders the same way whether the decision was a success or a block — that duality is the point.
Privacy Receipt ──────────────────────────
  result     VERIFIED
  executed   true
  policy     1001 · v1
  action     transfer 100 USDC → recipient
  nullifier  0x1f6485…9dfb
  identity disclosed   false
  cross-app identifier false

Canonical vocabulary

The CLI — and everything in Nullis — uses one fixed set of terms, no synonyms:
TermMeaning
policy / policy manifestReadable JSON config, hashed to policy_hash, registered on-chain
approved rootPer-policy, per-version Merkle root of approved credential commitments
commitmentPoseidon(credential_secret)
nullifierDomain-separated, cross-app unlinkable one-time marker
context_hashBinds a proof to one exact action
action_id / intent_nonceOne application-created authorization intent, consumed on-chain
app_domain / app_domain_hashDomain separator that makes one credential unlinkable across apps
Privacy ReceiptThe inspectable artifact emitted after every decision

Publish flow

How policies are registered and versioned on-chain.