CommunityPay generates institutional evidence packs — structured documents that prove what an HOA's financial and governance posture looked like at a specific point in time. This page explains what they contain, how they're verified, and why the verification model matters.
What Is an Evidence Pack?
An evidence pack is a frozen snapshot of an HOA's financial, governance, and compliance data, generated at a specific timestamp and bound to a cryptographic hash.
CommunityPay supports five pack types:
| Type | Name | Scope |
|---|---|---|
| HDEP | HOA Disclosure & Evidence Packet | Per unit |
| GCA | Governance Controls Attestation | Per HOA/period |
| FADR | Funds Authorization & Disbursement Record | Per HOA/period |
| RC | Resale Certificate | Per unit |
| RSR | Reserve Study Report | Per HOA/period |
Each pack type answers a different question. An HDEP answers "what is the complete financial and governance posture of this HOA, as it relates to this specific unit?" A GCA answers "what controls were active and what did they find during this period?"
Anatomy of a Pack
Every evidence pack contains:
Identification
- Public ID: A UUID for external API references. Immutable after creation.
- Reference Number: A human-readable identifier auto-generated at creation.
- Version Number: Increments with each regeneration for the same HOA/type/scope.
Data Snapshot
- evidence_snapshot: A JSON document containing all relevant financial and governance data, frozen at generation time. This is the canonical source of truth — not the PDF.
- as_of_at: The timestamp of the underlying data. This is when the data was captured, not when the pack was generated.
- generated_at: When the pack was actually generated. These two timestamps are distinct because you might regenerate a pack from the same data snapshot.
Proof References
The evidence snapshot includes references to the governance artifacts that produced it:
- policy_snapshot_ids: Which policy versions were active when the pack was generated
- evaluation_trace_ids: Which rule evaluations were performed
- enforcement_decision_ids: Which enforcement decisions were rendered
This creates a traceable chain from the pack back to the specific policy versions, rule evaluations, and decisions that produced the underlying data.
Verification
- content_hash: SHA-256 hash of the canonical JSON evidence snapshot.
- previous_packet_hash: Content hash of the previous version, creating a chain.
Lifecycle
- Status: draft, generated, delivered, superseded, void
- Sharing: Optional time-limited external sharing via secure token
Content Hashing
The content hash is the core verification mechanism. Here is how it works:
Canonical JSON
Before hashing, the evidence snapshot is serialized into canonical JSON:
- Keys are sorted alphabetically
- Separators are minimal (no extra whitespace):
(",", ":") - Encoding is UTF-8
This produces a deterministic byte sequence. The same data will always produce the same bytes, regardless of when or where the serialization happens.
SHA-256
The canonical bytes are hashed with SHA-256, producing a 64-character hexadecimal string. This hash is stored in the content_hash field.
What This Proves
If you have the evidence snapshot and the content hash, you can independently verify the pack:
- Serialize the evidence_snapshot to canonical JSON (sorted keys, minimal separators, UTF-8)
- Compute SHA-256 of the resulting bytes
- Compare with the stored content_hash
If they match, the evidence snapshot has not been modified since generation. If they don't match, the data has been tampered with.
Important: The hash is computed from the JSON evidence snapshot, not from the PDF. PDFs are rendered from the snapshot, but the snapshot is the authoritative data. Two PDF renderings of the same snapshot might differ in formatting, but the content hash will be identical.
Chain Continuity
Each pack version links to its predecessor via the previous_packet_hash field.
How Chaining Works
When a new version of a pack is generated for the same HOA/type/scope:
- The system looks up the current pack (the one being superseded)
- It copies the superseded pack's
content_hashinto the new pack'sprevious_packet_hash - The new pack's own
content_hashis computed from its new evidence snapshot
This creates a hash chain:
Pack v1:
content_hash = sha256(snapshot_v1)
previous_packet_hash = (empty, first version)
Pack v2:
content_hash = sha256(snapshot_v2)
previous_packet_hash = sha256(snapshot_v1) ← links to v1
Pack v3:
content_hash = sha256(snapshot_v3)
previous_packet_hash = sha256(snapshot_v2) ← links to v2
What Chain Continuity Proves
If you have consecutive packs in the chain, you can verify:
- This version hasn't been tampered with (content_hash matches snapshot)
- The previous version existed and had specific content (previous_packet_hash matches the predecessor's content_hash)
- Missing links are detectable when comparing expected continuity between consecutive versions
The chain is tamper-evident: if a party presents consecutive versions, links can be verified. An auditor or escrow company receiving Pack v3 can verify the entire history back to v1, provided earlier versions are available. No access to CommunityPay is required for this verification.
Immutability Enforcement
Evidence packs use a strict mutable/immutable field model.
Immutable Fields (Cannot Be Modified After Creation)
content_hashprevious_packet_hashevidence_snapshotpublic_idreference_numberpacket_typeversionscopeas_of_atcreated_byhoacreated_at
Mutable Fields (Can Be Updated)
status(e.g., draft to generated to delivered)pdf_file(PDF can be re-rendered from immutable snapshot)is_shareable,share_token,share_expires_atdelivered_at,voided_at,void_reasonnotes
How Immutability Is Enforced
When saving a pack, the system checks which fields are being updated. If any update targets an immutable field, a ValueError is raised and the save is rejected:
immutable_updates = set(update_fields) - MUTABLE_FIELDS
if immutable_updates:
raise ValueError(f"Cannot update immutable fields: {immutable_updates}")
This runs at the application layer, not just the database layer. You cannot bypass it through the Django admin, management commands, or bulk updates.
Lifecycle Tracking
Every significant event in a pack's lifecycle is recorded in an append-only event log.
Event Types
| Event | When It's Recorded |
|---|---|
| created | Pack record first saved |
| generated | Evidence assembled and hash computed |
| delivered | Pack sent to recipient |
| shared | External share link activated |
| accessed | Someone uses the share link |
| downloaded | Recipient downloads the PDF |
| voided | Pack marked as void |
| superseded | Newer version generated |
| share_revoked | Sharing disabled |
What Events Capture
Each event records:
- Actor: Which user performed the action
- Actor name: Denormalized so the record persists even if the user is later deleted
- Detail: Human-readable description
- IP address: Network origin of the action
- Timestamp: When it happened
Events are append-only. They cannot be updated or deleted. This creates a complete audit trail of every pack from creation through delivery.
Practical Verification
Independent verification does not require credentials, API calls, or trust in CommunityPay. Any party with the evidence data can verify integrity.
For an external party (auditor, escrow company, prospective buyer) to verify a pack:
- Obtain the pack's evidence_snapshot and content_hash (included in the pack itself)
- Compute:
sha256(canonical_json(evidence_snapshot)) - Compare: If it matches
content_hash, the data is intact - Optionally verify the chain: Check
previous_packet_hashagainst the predecessor'scontent_hash
Verification Recipe
import json, hashlib
# 1. Load the evidence_snapshot from the pack
snapshot = pack["evidence_snapshot"]
# 2. Serialize to canonical JSON
canonical = json.dumps(snapshot, sort_keys=True, separators=(",", ":")).encode("utf-8")
# 3. Compute SHA-256
computed_hash = hashlib.sha256(canonical).hexdigest()
# 4. Compare to stored content_hash
assert computed_hash == pack["content_hash"], "Hash mismatch — data has been modified"
This is what "tamper-evident" means in practice: not that tampering is impossible, but that tampering is detectable by anyone with the data.
CARI Integration
CARI reports — including Lender, Insurer, Title, and Buyer variants — are institutional packets built on the same evidence infrastructure described here. Each CARI report carries a SHA-256 content hash, version chain linking via previous_packet_hash, and an append-only event log, identical to HDEP, GCA, FADR, and VECR artifacts. CARI reports extend the evidence pack ecosystem to external institutional consumers, enabling lenders, insurers, and title companies to independently verify the integrity of the data they receive.
For published methodology and component weights, see CARI Methodology and Scoring Framework.
How CommunityPay Enforces This
- SHA-256 content hash computed from canonical JSON (not PDF bytes)
- Previous-packet hash creates tamper-evident chain between versions
- Immutability enforced: evidence_snapshot, content_hash, and proof references cannot be modified after creation
- Packet lifecycle tracked via append-only event log
- Proof references link packets to the policy snapshots, evaluation traces, and enforcement decisions that produced them