What "zero-trust" actually means
The phrase gets thrown around a lot. In our case it means one specific thing: no service inside the platform implicitly trusts any other service. Every inter-service call presents a client certificate. Every secret is fetched from Vault, never baked into a container. Every database has a client-auth listener. If a single service is compromised, the blast radius is that service, not the platform.
This is the same posture a tier-1 bank's payment infrastructure has to take. It's not what most early-stage fintechs do, because doing it right is expensive. We did it from day one because the alternative — building it later — usually means a rewrite.
Here's how it's wired.
The six pillars
1. Zero-trust security
Every Spring Boot service has its own per-service mTLS certificate issued by an
internal Vault PKI. The certs auto-rotate. Postgres, Redis, Kafka, Consul, and
Keycloak all run client-auth-enforced TLS 1.3 listeners — no cleartext on any
internal hop. If you tcpdump between two Danipa services in sandbox, you see
encrypted TLS frames, end of story.
2. Secrets management
HashiCorp Vault is the single source of secrets. Vault KV paths are scoped per environment (sandbox / production) and per service. Spring Cloud Config Server composites Vault with Git: secrets from Vault, non-secrets (feature flags, service URLs) from the config repo.
No secret lives in a Docker image, a Kubernetes manifest, a CI variable, or anyone's laptop. The rotation workflow is audited; the audit trail is queryable.
3. Data privacy & compliance
Card processing is PCI DSS-handled via Stripe — we never touch a PAN. For everything else, PII masking is enforced at the architecture layer. An ArchUnit test fails the build if a logger statement could leak a phone number, email, or card token. The guard is more reliable than a code-review checklist because it runs every commit.
Multi-tenant tables use Postgres row-level security. The TenantConnectionInterceptor sets the tenant id on every JDBC connection; queries that forget to filter by tenant return zero rows, not someone else's data.
4. Multi-jurisdiction readiness
Ghana statutory compliance — KYC tiers, AML, Ghana Card verification — is
shipped. CAD and USD banking corridors are wired via Stripe Connect with
per-country routing. The provider model is extensible: adding a new country's
payment rail means writing a PaymentGateway implementation, not re-architecting
the core.
5. Testing & observability
80%+ test coverage across every service. Not a marketing number — a JaCoCo gate that fails CI if coverage drops below the floor. Unit, integration, and E2E layers; ArchUnit at the build for architecture invariants (no wildcard imports, no PII in logs, no direct DB access outside repositories).
Structured logs carry a request-scoped trace id, tenant id, and user id across every service hop. A merchant ticket with a request id resolves in one click.
6. Sandbox-first development
Hetzner runs the sandbox. Every feature gets validated against real provider integrations — MTN MoMo against MTN's sandbox, Stripe against test mode, webhooks against the merchant's actual endpoint — before it can ship.
Production cuts over to Azure or AWS post-funding for multi-region scale. The sandbox isn't going away after that; it stays as the staging step before any production deploy. Every feature has to clear the sandbox bar twice — once to ship into sandbox, once to graduate.
Why I'm telling you this
Two reasons.
For developers: if you're considering building on Danipa's API, this is what your data is sitting on top of. The full architecture page lives at /architecture, with the same pillars in more detail.
For investors: the cost of building this discipline in later — after a hundred merchants are live, after a regulator asks a question, after a breach — is a complete platform rewrite. We did it before the marketing site existed. You're not buying a roadmap toward production-grade infrastructure; you're buying the infrastructure with a sandbox-stage label on it. The label comes off when the funding lands; the architecture does not change.
If you'd like the full read, the architecture page and the milestone docs in the platform repo are both open. If you'd like to talk, contact us.