Skip to content
DevelopersSecurity

The security model

Defense in depth, written down, so you can check our work.

3 min readUpdated View as Markdown

On this page

1. Isolation

  • One database per service, each with its own credentials. A service can only read its own tables.
  • Tenant isolation in every query. Every row carries its organization id, and the organization always comes from a verified identity (the signed papi identity or the verified session), never from a header, path or body the caller controls. Every service runs a contract test: another organization's records answer 404, and writes to them are refused.
  • The customer PHI vault is on its own database server, on no gateway, reachable only from the customers service's tasks over the private network. It re-checks the person's membership itself, so a compromised caller still cannot read an organization the person is not in.
  • Private networking. Databases have no public address. Services talk through tapi, a private load balancer.

2. Encryption

  • In transit: TLS everywhere, HSTS on our hosts, no plain HTTP.
  • At rest: every database and bucket is encrypted. Sensitive services (orders, the vault, payments, papi) use their own keys.
  • Field-level: personal fields (names, emails, phones, addresses, notes), processor and carrier credentials, TOTP seeds and stored API secrets are encrypted per field under the owning service's key. Lookups use keyed one-way blind indexes, so an email can be searched without being stored in clear.

3. Identity

  • OpenID Connect with PKCE; confidential clients only for web apps, so no token reaches browser JavaScript.
  • 10-minute access tokens; rotating 30-day refresh tokens with reuse detection.
  • Session cookies scoped to a single host, Secure, HttpOnly, SameSite=Lax.
  • Two-factor available to everyone, required for staff; sensitive actions check the session's amr, not just the account's setting.
  • Password policy per NIST SP 800-63B: length and breached-password checks, no forced rotation. Rate limits per IP and per email; constant-time responses on sign-in and reset.

4. Service-to-service trust

  • papi signs a 60-second Ed25519 identity for each request (organization, key id, key class, scope, route). Services verify it with papi's public key and never see a tenant key.
  • Services call each other and deliver events with HMAC-signed requests using pairwise keys, with nonces to block replays.
  • Two checks: the gateway authorizes, and the service authorizes again.

5. Payments and PHI

  • No card data: cards become one-time tokens in the shopper's browser (PCI DSS SAQ A). We store processor ids, brand, last four and expiry only.
  • Processors never see products: every request to a processor passes one redaction function; lines become Item 1, descriptions become the order number.
  • Analytics stores no personal data: yearly-rotating pseudonyms with no reverse map, coarsened values, totals only, small counts suppressed.
  • Features that would send PHI to a vendor without a BAA are built but off for HIPAA organizations.

6. Logging without leaking

  • Every service keeps an append-only, hash-chained audit log, verified for breaks.
  • Traces record route templates, statuses, actors, organizations and touched record ids and field names. Never values.
  • Request logs at the public edge record the route template only: no path values, no query strings, no IP addresses, no keys.
  • Secrets never appear in logs, spans or error bodies.

7. The edge

  • A web application firewall in front of the public API: IP reputation lists, known-bad inputs, core rules, per-IP and per-key rate ceilings.
  • Security headers on every response: X-Frame-Options: DENY, X-Content-Type-Options: nosniff, strict Referrer-Policy, Cross-Origin-Opener-Policy.
  • Outgoing webhooks refuse private, loopback and link-local destinations (SSRF) and never follow redirects.
  • Uploaded files are identified by their bytes; anything executable or disguised is refused, and SVG scripts are stripped.
  • Rich text is cleaned by an allowlist on the way in: nothing that runs is ever stored.

8. Engineering practice

  • Every service's tests run on Postgres, against throwaway databases, never against live data.
  • Infrastructure is declared in Terraform; changes are reviewed and applied deliberately.
  • Changes that can fail requests ship with a rollback plan.

Report a vulnerability

Email support@dashfordevs.com with the subject "Security", the details and how to reproduce. Please give us reasonable time to fix before disclosing, and do not access data that is not yours. We answer every report.

Keep reading

Something wrong or missing on this page? Email support@dashfordevs.com.