Skip to main content
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.js — node 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())

GitHub · PyPI · npm · Issues


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)

Source distribution for ciphera 0.1.2
File Size Uploaded
ciphera-0.1.2.tar.gz 9.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ciphera 0.1.2
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page