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
Metadata
Release files for ciphera 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ciphera-0.1.2.tar.gz | 9.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ciphera-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 20.7 kB
Release files / ciphera-0.1.2.tar.gz
| Download URL | ciphera-0.1.2.tar.gz |
|---|---|
| Size | 9.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6b70e40af7484fd1fc8dba68ddfbd6e47fb04028bf19a125f7d3059d81577277
|
|
BLAKE2b-256 checksum How to use checksums |
b03bd64899b93c3b17040775fdc323c9d9420cdbb769a255b10e6525d18b945b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|
Release files / ciphera-0.1.2-py3-none-any.whl
| Download URL | ciphera-0.1.2-py3-none-any.whl |
|---|---|
| Size | 11.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
32281d212446787535902918b64dade9ec222fa4ca82a50332e534cea4206a0b
|
|
BLAKE2b-256 checksum How to use checksums |
8b777bc6744221f477d8d21dc104085ba9eff5364511712e4e89ad68f2b85387
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|