BDR Gate Systems

Security

Seven layers, each written on the assumption that the layer above it has already failed. What follows is what the running system does, not a roadmap.

Fail closed, everywhere

The project-wide default permission class denies. A view that forgets to declare its policy returns 403, never 200. That single line matters more than any other in the codebase: the opposite default is how an API ends up serving participant records to anyone who has the URL.

The same principle runs through configuration. Every secret is read through a loader that raises when it is absent, so the process refuses to start rather than booting with half its security configuration missing. There are no permissive defaults to fall back to.

The layers

LayerWhat it does
Edge TLS 1.3 only, WAF, per-principal rate limits, mutual TLS on partner endpoints with pinned certificates.
Identity OIDC with PKCE, short-lived tokens, refresh rotation with reuse detection, and MFA required for every write-capable role. Tokens are validated at the gateway and again in the application: a single validation point is a single bypass point.
Authorization Deny by default. Views declare a capability, never a role. One matrix is read by the API, the console and the test suite, so published policy and running system cannot drift apart.
Data layer Row-level security as defence in depth, separate database roles for the application, migrations and reporting, and UPDATE and DELETE revoked on the audit tables.
At rest AES-256-GCM field encryption under KMS-held keys in ap-south-1, with each ciphertext bound to its own column so a value moved between fields fails to decrypt rather than quietly revealing the wrong record. Contact details are masked by default, and unmasking is itself an audited event.
Integrity A SHA-256 hash chain over every audit record, each signed by a key the application does not hold, with a daily anchor and self-service verification.
Supply chain Dependency and static analysis in CI, blocking on high severity. Secrets come from a secret manager, never from source.

Why signatures, and not only hashes

A hash chain detects an edit. It does not detect an edit followed by a careful re-hash, which anyone with database access can perform. Signing each record with a key held outside the application closes that gap: rehashing needs only a database, re-signing needs the key.

The test suite performs exactly that attack — edit a row, recompute its hash, repair the following link — and requires the verifier to catch it on the signature. A guarantee nobody attacks is a guarantee nobody has.

Destructive actions need two people

Liquidating collateral, releasing a quarantine, closing a reconciliation break and overriding a compliance block each require a second, distinct approver. The approval carries the action and approving performs it, so an approver cannot sign one thing while a different thing is done, and an approved action cannot silently never happen. Self-approval is refused by the service and again by a database constraint.

Privacy

Personal data is encrypted at rest, masked on read, and redacted at the logging formatter rather than at each call site. Call-site discipline fails eventually — one forgotten log line in one exception handler is enough — and a log sink that has never held cleartext personal data needs no retroactive cleanup.

Responsible disclosure. If you believe you have found a vulnerability in anything we operate, please report it to us privately before publishing. We will acknowledge it, investigate, and tell you what we found.