Verification API¶
Sender verification via probe-based challenge or SPKI-based TOFU.
VerifyingHandler¶
VerifyingHandler ¶
VerifyingHandler(
wrapped: MessageHandler,
verifier: ProbeVerifier
| SPKIVerifier
| CombinedVerifier,
mode: VerificationMode = VerificationMode.OPTIONAL,
method: VerificationMethod = VerificationMethod.PROBE,
)
Bases: MessageHandler
Wraps a MessageHandler to verify sender addresses before delivery.
Zero-length messages (verification probes) are always forwarded to the wrapped handler without verification to avoid infinite loops.
Source code in src/titlani/verification/handler.py
ProbeVerifier¶
ProbeVerifier ¶
ProbeVerifier(
cache: SenderVerificationCache,
identity_cert: Path,
identity_key: Path,
port: int = DEFAULT_PORT,
timeout: float = 10.0,
)
Verify senders by probing their server with a zero-length request.
A successful probe (status 20) means the sender's server recognises the mailbox and returns its certificate fingerprint. Results are cached so each sender is probed at most once.
Source code in src/titlani/verification/verifier.py
SPKIVerifier¶
SPKIVerifier ¶
SPKIVerifier(
cache: SenderVerificationCache,
port: int = DEFAULT_PORT,
timeout: float = 10.0,
on_spki_change: str = "reject",
)
Verify senders by caching their server's SPKI hash (TOFU model).
On first contact with a server, connects via TLS, extracts the SPKI hash, and caches it. While the cache entry is valid (within TTL), subsequent verifications return immediately without a network call. When the entry expires, the server is re-checked and its current SPKI compared against the last known value.
Source code in src/titlani/verification/spki_verifier.py
SenderVerificationCache¶
SenderVerificationCache ¶
SQLite-backed cache for probe fingerprints and server SPKI hashes.
Tables:
- verified_senders: maps sender addresses to probe fingerprints
- server_spki: maps hostnames to SPKI hashes (for SPKI verification)
Source code in src/titlani/verification/cache.py
get_fingerprint ¶
Return cached fingerprint for address, or None if missing/expired.
Source code in src/titlani/verification/cache.py
add_verified ¶
Store (or update) a verified sender.
Source code in src/titlani/verification/cache.py
revoke ¶
Remove address from cache. Returns True if it existed.
Source code in src/titlani/verification/cache.py
list_verified ¶
Return all verified senders as (address, fingerprint, verified_at).
Source code in src/titlani/verification/cache.py
cleanup ¶
Remove expired entries from all tables. Returns total purged rows.
Source code in src/titlani/verification/cache.py
get_server_spki ¶
Return cached SPKI hash for hostname, or None if missing/expired.
Source code in src/titlani/verification/cache.py
get_last_server_spki ¶
Return last known SPKI hash for hostname, ignoring TTL.
Used during re-verification after cache expiry to detect key changes.
Source code in src/titlani/verification/cache.py
add_server_spki ¶
Store (or update) a server SPKI hash.
Source code in src/titlani/verification/cache.py
list_server_spki ¶
Return all server SPKI entries as (hostname, spki_hash, verified_at).
Source code in src/titlani/verification/cache.py
clear_server_spki ¶
Remove all server SPKI entries. Returns count of removed rows.
Source code in src/titlani/verification/cache.py
VerificationMode¶
VerificationMode ¶
Bases: StrEnum
VerificationMethod¶
VerificationMethod ¶
Bases: StrEnum
VerificationResult¶
VerificationResult
dataclass
¶
VerificationResult(
verified: bool,
fingerprint: str | None = None,
cached: bool = False,
reason: str | None = None,
checks: dict[str, VerificationResult] | None = None,
)