Skip to main content
Money moves through Guild. Partner consent flows through Guild. Personal identifiers flow through Guild. Security isn’t a feature here; it’s the substrate. This page is the high-altitude security story. Each section links to a deeper page.

The threat model

Each threat has at least one defence. Most have two.

The five guarantees

The promises Guild makes about security, in order of importance.

1. No webhook can credit a wallet on its own

Detail: Webhook verification. Every OPay webhook passes three checks before any wallet motion: HMAC-SHA512 signature verification, idempotency check on the event ID, and re-verification against OPay’s authoritative endpoint. Even if the webhook secret leaks, a forged webhook cannot fake a successful transaction — because we ask OPay to confirm.

2. PII never sits plaintext on disk

Detail: KYC tiers & data residency. BVN, NIN, last name, date of birth, gender, full address — all envelope-encrypted with XChaCha20-Poly1305 before being written. The master key lives in the host’s environment, not in the database. The AAD (additional authenticated data) is bound to the user’s ULID, so a swapped ciphertext blob fails to decrypt. A database leak alone yields ciphertext with no key.

3. Audit history is structurally immutable

Detail: Append-only audit ledger. channel_audit and payments_audit tables have BEFORE UPDATE OR DELETE triggers that reject mutation. Once a row is inserted, it’s permanent. Not editable by application code, not editable by an engineer in a psql session, not editable by anyone short of dropping the trigger (an action that is itself logged and reviewed).

4. Every internal call is cryptographically signed

Detail: Inter-service communication. @guild/svc-client signs every service-to-service request with HMAC-SHA256 over (method, path, body, timestamp, nonce, actor). A 300-second skew window plus a nonce cache (Redis, 10-minute TTL) prevents replay. crypto.timingSafeEqual for every comparison. Detail: Partner consent. Every snapshot read by a partner requires an active consent record on Guild’s side. The consent specifies the scopes the partner can see and an expiry date. The user can revoke at any time from their dashboard; the next snapshot fetch with that consent ID will fail with consent_revoked.

Defence-in-depth principles

The architecture follows three rules.

Assume every secret can leak

We don’t design the system to depend on a single secret staying secret. The webhook reconcile guard makes OPay’s authoritative endpoint the source of truth even if our webhook secret leaks. The envelope encryption makes the master key matter even if the database leaks. The session ULID is opaque, so even if a cookie leaks, it conveys no information.

Defence in depth, not defence by exception

We don’t allow “trusted” callers to bypass any check. The signed-request middleware applies to all inter-service traffic uniformly. The reconcile guard runs on every OPay webhook, not just the ones we’re suspicious of. Uniformity beats exception-list maintenance.

Make security checks part of the type system

Where possible, security checks are encoded in types, not just runtime checks. A wallet credit operation cannot be invoked without a verified webhook reference. A KYC-encrypted field cannot be read without the decryption helper. The compiler stops us from making certain classes of mistake at edit time.

Compliance posture

NDPR alignment

Nigeria Data Protection Regulation is the relevant compliance baseline. Our posture:
  • Consent. Explicit, granular, revocable. Logged in the audit ledger.
  • Data minimisation. We collect only what’s needed for the platform’s stated purposes (matching, payments, KYC, standing).
  • Right to erasure. A user who deletes their account triggers anonymisation of personal identifiers. The audit rows persist (required for transaction reconstruction and partner contracts) with an anonymised user ID.
  • Right to portability. Data export endpoint in the user dashboard. The user gets a signed JSON archive of their data.
  • Lawful basis for processing. Consent for marketing and analytics; legitimate interest for transactional processing; legal obligation for AML/KYC.

Data residency

Hosting is on virtual private servers. Backup snapshots replicate cross-region within the provider. We do not export user data to third parties outside the consent boundary.

BVN / NIN handling

Verification through licensed providers. The BVN and NIN themselves are envelope-encrypted; the application code reads them only when verifying or when explicitly displaying to the user. Application logs redact them. They never leave our control for any purpose other than the verified-once handshake.

Penetration testing and reviews

A short list of operational practices:
  • Quarterly internal security review of the codebase, signature implementations, and secret handling.
  • Annual third-party penetration test of the public surface (Partner API, BFF endpoints, webhook receivers).
  • Pre-launch bug bounty before significant feature releases that change the auth surface.
  • Continuous dependency scanning (pnpm audit, GitHub Dependabot) with automatic PR creation for security patches.

What we don’t claim

Honesty about scope:
  • We are not a SOC 2-certified system at v1. Working toward it as the platform matures. We follow SOC 2 principles in operation; certification is a process.
  • We do not encrypt every column. Non-sensitive fields (timestamps, status enums, references) sit plaintext. Only the columns containing PII or financial-account identifiers are encrypted.
  • We do not run multi-region failover today. Backup replication is cross-region; live failover is a known operational task at higher scale.

Where to go next

Webhook verification

The full signature spec for OPay, WhatsApp, voice, and Partner webhooks.

Audit ledger

The append-only contract and what it means in practice.

KYC tiers

Tier 1, 2, 3 — what’s verified at each step, what’s encrypted, how data flows.

NDPR posture

The compliance framing in detail.