Skip to main content

Zero-Knowledge KYC for Algorand — verify KYC status on-chain without exposing personal data

Project description

Ciphera Logo

Zero-Knowledge KYC for Algorand — Python SDK

PyPI version Python License: MIT Algorand

Query KYC status on Algorand. Verify ZK proofs. No personal data — ever.


Install

pip install ciphera

What is Ciphera?

Ciphera is a privacy-preserving zero-knowledge KYC system on Algorand. Users prove they are verified Indian adults without exposing any personal information — ZK proofs are generated entirely in the browser and verified on-chain.

The Python SDK is designed for issuer backends and dApps that need to:

  • Query KYC status on-chain
  • Verify Groth16 ZK proofs server-side
  • Integrate Ciphera into existing Python/FastAPI backends

Quick Start

Check KYC Status (any dApp)

from ciphera import CipheraClient

client = CipheraClient()  # Algorand Testnet (default)

status = client.verify_kyc()
print(status.is_verified)        # True if credentials exist
print(status.total_registered)   # e.g. 42
print(status.app_id)             # 756272073

Check a Specific Nullifier

from ciphera import CipheraClient

client = CipheraClient()

# Check if a specific 32-byte nullifier is registered on-chain
registered = client.is_nullifier_registered("3b1f8a2c" + "0" * 56)
print(registered)  # True / False

Verify a ZK Proof (Issuer Backend)

import json
from ciphera import verify_proof

with open("verification_key.json") as f:
    vk = json.load(f)

result = verify_proof(
    verification_key=vk,
    proof=proof_dict,              # from browser SDK
    public_signals=signals_list,   # from browser SDK
    expected_app_id=756272073      # prevents cross-app attacks
)

if result.valid:
    print(result.nullifier_hex)               # 32-byte hex to register on-chain
    print(result.public_signals.is_adult)     # "1"
    print(result.public_signals.is_indian)    # "1"
else:
    print(result.error)

Connect to Different Networks

from ciphera import CipheraClient

# Testnet (default)
client = CipheraClient()

# Mainnet
client = CipheraClient(network="mainnet")

# LocalNet (development)
client = CipheraClient(network="localnet")

# Custom Algod endpoint
client = CipheraClient(
    algod_url="https://my-algod.example.com",
    algod_token="my-api-token"
)

API Reference

CipheraClient

CipheraClient(
    network="testnet",   # "testnet" | "mainnet" | "localnet"
    algod_url=None,      # Override Algod endpoint URL
    algod_token="",      # Override Algod API token
    contract_ids=None,   # Override deployed contract IDs (dict)
)
Method Returns Description
verify_kyc(wallet_address=None) KYCStatus Query NullifierRegistry on-chain
is_nullifier_registered(hex) bool Direct box storage check for a nullifier
get_credential_asa_id() int KYCRED ASA token ID
CipheraClient.explorer_tx_url(txid) str Allo.info TX explorer URL
CipheraClient.explorer_app_url(app_id) str Allo.info app explorer URL

verify_proof(vk, proof, public_signals, expected_app_id=None)

Verify a Groth16 ZK proof server-side using snarkjs (via Node.js subprocess).

⚠️ Requires Node.jsnode must be installed and available in PATH.

Parameter Type Required Description
verification_key dict, str, or Path Groth16 verification key (dict, JSON string, or file path)
proof dict Groth16 proof from the browser SDK
public_signals list[str] Public signals list from the browser SDK
expected_app_id int | str Guard against cross-app replay attacks

Returns: ProofResult


Data Models

from dataclasses import dataclass
from typing import Optional

@dataclass
class KYCStatus:
    is_verified: bool       # True if KYC credentials are registered
    app_id: int             # NullifierRegistry app ID
    total_registered: Optional[int]  # Total credentials in registry
    nullifier: Optional[str]         # Registered nullifier hex
    error: Optional[str]             # Error message if query failed

@dataclass
class ProofResult:
    valid: bool                          # True if proof is valid
    nullifier_hex: Optional[str]         # 32-byte nullifier hex (on-chain ready)
    public_signals: Optional[PublicSignals]  # Parsed public signals
    error: Optional[str]                 # Error message if verification failed

@dataclass
class PublicSignals:
    nullifier: str           # BN254 field element (decimal string)
    merkle_root: str         # SMT root
    app_id: str              # NullifierRegistry app ID
    is_indian: str           # "1" (circuit-enforced)
    is_adult: str            # "1" (circuit-enforced, age >= 18)
    is_kyc_verified: str     # "1" (circuit-enforced)

    @property
    def nullifier_hex(self) -> str: ...  # Nullifier as 32-byte hex

Exceptions

from ciphera.exceptions import CipheraError, ProofVerificationError, NetworkError

try:
    status = client.verify_kyc()
except NetworkError as e:
    print(f"Algorand query failed: {e}")
except CipheraError as e:
    print(f"Ciphera error: {e}")
Exception When raised
CipheraError Base class for all Ciphera SDK errors
ProofVerificationError ZK proof verification fails
NetworkError Algorand network request fails
ContractError Smart contract interaction fails

Deployed Contracts (Algorand Testnet)

Contract App ID Explorer
NullifierRegistry 756272073 allo.info ↗
SMTRegistry 756272075 allo.info ↗
KYCBoxStorage 756272299 allo.info ↗
CredentialManager 756281076 allo.info ↗
KYCRED ASA 756281102 allo.info ↗

Requirements

Requirement Version
Python 3.9+
py-algorand-sdk ≥ 2.6.0 (auto-installed)
Node.js Any (only for verify_proof())

Links

GitHub · PyPI · npm · Issues


License

MIT © Aditya Pandey

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ciphera-0.1.2.tar.gz (9.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ciphera-0.1.2-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

Details for the file ciphera-0.1.2.tar.gz.

File metadata

  • Download URL: ciphera-0.1.2.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for ciphera-0.1.2.tar.gz
Algorithm Hash digest
SHA256 6b70e40af7484fd1fc8dba68ddfbd6e47fb04028bf19a125f7d3059d81577277
MD5 35aba0ca0d91521dd345469edc747498
BLAKE2b-256 b03bd64899b93c3b17040775fdc323c9d9420cdbb769a255b10e6525d18b945b

See more details on using hashes here.

File details

Details for the file ciphera-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: ciphera-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 11.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for ciphera-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 32281d212446787535902918b64dade9ec222fa4ca82a50332e534cea4206a0b
MD5 054e92d248e17e9fc210c8a9b87bdffe
BLAKE2b-256 8b777bc6744221f477d8d21dc104085ba9eff5364511712e4e89ad68f2b85387

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page