POPIA · AES-256 · Subdomain Isolation · Audit Trail

Security and POPIA compliance built in, not bolted on.

South African personal information law requires more than a checkbox. Here's exactly how Zyden protects your residents' data — and yours.

Encryption

PII encrypted before it touches the database.

Every personal identifier is encrypted at the application layer — before the INSERT. The database never sees plaintext names, phones, or ID numbers.

AES-256 Field-level Encryption

Column-level, not database-level

Full names, phone numbers, and other personal identifiers are encrypted with AES-256-CBC (256-bit key, random IV prepended to every ciphertext) at the application layer. Each field is independently encrypted.

First name, last name, full name
Phone number
Profile fields set by the estate

SA ID as Blind Index

HMAC-SHA256 · Never decrypted for lookup

South African ID numbers are special category data under POPIA — quasi-biometric. Zyden never stores them in plaintext. A keyed HMAC-SHA256 blind index is stored instead. This allows deduplication (same person, multiple schemes) without ever decrypting the SA ID for the lookup.

# What the database sees:

id_hash: a3f8c2d1e9b7... (HMAC-SHA256)

encrypted_id: AES256CBC(IV || ciphertext)

# What is never stored:

id_number: 9001015009087 ✕

Passwords hashed with bcrypt — never stored as plaintext or reversible hash

Password reset tokens are SHA-256 hashed before storage. The raw token exists only in the email sent to the account holder — never in the database.

Access Controls

Minimum access, verified on every request.

Access is not based on session state alone. Every admin request re-verifies the caller's role against the database.

Role Enforcement

Four roles — admin, trustee, owner, resident — stored in an access_map table. Every admin route executes a DB query to confirm the caller holds admin or trustee role for the active complex before processing.

  • Admin routes: DB-verified every request
  • Role checked against access_map, not session cache
  • Privilege escalation actively blocked

Subdomain Isolation

Each estate operates on its own subdomain. Session context is bound to the subdomain. A resident from one estate cannot authenticate on another — even if they have an account on both.

  • Session scoped to complex_id
  • Complex mismatch triggers a 403 error
  • No cross-estate data leakage possible

Super Admin Gate

Global admin access (for managing agents overseeing multiple schemes) requires explicit IS_SUPER_ADMIN flag in the database. It cannot be acquired by role or session manipulation — we actively tested and fixed this.

  • NULL complex_id no longer grants super admin
  • Super admin verified against identities.is_super_admin only
  • DelegateController patched to match
Audit Trail

Every sensitive action logged.

Not just what happened — who did it, when, and from where. The audit log is append-only and not editable through the admin console.

Security Audit Log — example entries
Initiated Password Reset resident@yourestate.co.za 2026-06-23 09:14:22
Move-in Approved — Unit 14 admin@yourestate.co.za 2026-06-23 08:52:11
Document Accepted: Complex Rules v2.1 resident@yourestate.co.za 2026-06-23 08:51:45
Owner Invited — Unit 14 admin@yourestate.co.za 2026-06-22 16:30:07
Birthed Estate: pinehurst superadmin@zyden.co.za 2026-06-20 11:02:55

What's logged

  • Estate provisioning and configuration changes
  • Move-in and move-out approvals
  • Admin delegation and role changes
  • Document acceptance (with IP address)
  • Password resets and auth events
  • Vault item downloads

Document signatures

When a resident signs the complex rules during onboarding, Zyden logs: the vault item ID, the acting identity, the signature name they typed, their IP address, and the timestamp. This record is retained even if the vault item is later replaced.

This creates a defensible POPIA-compliant record of consent at the time of onboarding.

Authentication Security

Hardened authentication endpoints.

CSRF Protection

Every state-changing POST request requires a CSRF token. Token mismatch triggers a 403 — no silent bypass.

Rate Limiting

The forgot-password endpoint is rate-limited to 5 attempts per IP per 15 minutes. Rate-limited requests receive the same success message — no oracle for attackers.

Secure Reset Tokens

Password reset tokens are SHA-256 hashed before database storage. The raw token exists only in the email — not in the database.

Session Isolation

Sessions are scoped to the active complex. A session token from one subdomain cannot be reused on another.

Path Traversal Prevention

Vault document downloads validate file paths with realpath() before serving. Paths outside the upload directory are blocked.

Minimum Password Length

All account creation and password setup flows enforce a 12-character minimum server-side — not just client-side.

POPIA Posture

What we do. What we don't. What's coming.

What's built
  • SA ID stored as blind index only — never plaintext
  • AES-256 field encryption on all PII columns
  • Signed acceptance + IP and timestamp log on complex rules
  • Role-based access — minimum necessary permissions
  • Subdomain data isolation — no cross-estate data access
  • Immutable security audit log
  • No PII in HTML attributes, onclick, or URL query strings
  • CSRF protection on all state-changing requests
  • Rate limiting on unauthenticated endpoints
Phase 2 — In Development
  • POPIA-compliant privacy notice at onboarding
  • Explicit consent capture with timestamp
  • Right-to-erasure pathway for identities
  • Data retention automation (purge after N years)
  • AES-256-GCM authenticated encryption (replaces CBC)
Not Zyden's Scope
  • Levy records and financial data — stays in your accounting system
  • Bank account details — never captured by Zyden
  • CCTV or biometric data — not collected
  • Third-party sharing — no resident data is shared with or sold to third parties

Security questions?

We're happy to walk through the architecture with your IT or compliance team. No sales deck — just an honest technical conversation.