Zero-Knowledge KYC for Algorand — verify KYC status on-chain without exposing personal data
Project description
Zero-Knowledge KYC for Algorand — Python SDK
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.js —
nodemust 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
License
MIT © Aditya Pandey
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b70e40af7484fd1fc8dba68ddfbd6e47fb04028bf19a125f7d3059d81577277
|
|
| MD5 |
35aba0ca0d91521dd345469edc747498
|
|
| BLAKE2b-256 |
b03bd64899b93c3b17040775fdc323c9d9420cdbb769a255b10e6525d18b945b
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32281d212446787535902918b64dade9ec222fa4ca82a50332e534cea4206a0b
|
|
| MD5 |
054e92d248e17e9fc210c8a9b87bdffe
|
|
| BLAKE2b-256 |
8b777bc6744221f477d8d21dc104085ba9eff5364511712e4e89ad68f2b85387
|