Core Concepts
NullifierRecord
The NullifierRecord PDA is how Althea guarantees that one real person can have at most one verified wallet, forever. It is derived deterministically from the user's nullifier — a one-way hash of their idHash.
Account layout
#[account]
pub struct NullifierRecord {
pub nullifier: [u8; 32], // Poseidon(idHash)
pub created_at: i64, // unix seconds
pub bump: u8,
}
impl NullifierRecord {
pub const SIZE: usize = 32 + 8 + 1;
}PDA seeds
seeds = [b"africazk-nullifier", nullifier_bytes]
program_id = AfricaZK1111111111111111111111111111111111Purpose
Without this account, a user could verify with their real NIN once, wipe their wallet, generate another wallet, and verify again — the same identity attested to multiple wallets. The NullifierRecord PDA is init-only on creation: attempting to derive it a second time fails because the account already exists. One nullifier = one wallet.
Why this doesn't leak identity
The nullifier is Poseidon(idHash) — a one-way hash of an already-hashed ID number. Given a NullifierRecord on-chain, an observer cannot recover the idHash, let alone the NIN. They can only check whether this specific nullifier has been registered.
Linkability across dApps
A dApp that knows a user's nullifier can correlate that user across protocols that also know it. The SDK never exposes the nullifier to your dApp by default — only Althea's Anchor program ever needs it. If you build something that requires nullifier-level deduplication beyond Althea, do so explicitly so users know.Rent
The account is rent-exempt and paid for by the user on creation. The ~41-byte account costs about 0.0009 SOL.