DevelopersSecurity & compliance

Security & compliance

How Orchard protects data and money, and what that means for what you build.

No card data, ever (PCI SAQ A)

Card entry happens entirely on Stripe-hosted Checkout. Orchard — and your storefront — only ever handle references (order ids, Stripe session ids), never card numbers, CVV, or track data. This keeps the platform in the smallest PCI scope (SAQ A). Don’t build any flow that collects raw card details into your app; redirect to the hosted checkout instead (see checkout).

Tenant isolation

Every store’s data is isolated with defense in depth — the application scopes every query to your tenant and the database enforces it again underneath. The tenant is resolved server-side from your API key; there is no tenant parameter you can change to reach another store. See Architecture → Tenant isolation.

API keys

  • Treat sk_* keys as secrets. Server-side only — environment variables, your platform’s secret store, CI secrets. Never commit them; never ship them in a browser bundle.
  • pk_* keys are browser-safe but still scoped — they only carry read scopes plus checkout:write.
  • Scope minimally. Ask the operator for keys with only the scopes you need.
  • Rotate on exposure. If a key leaks, have the operator revoke and reissue it.
  • A bad key returns a generic 401 (the reason is never disclosed); a key missing a required scope returns 403. See Authentication.

Customer sessions

Customer accounts are passwordless (magic link). Sessions are short-lived JWTs signed with a per-tenant secret, with a rolling refresh — a session minted for one store is meaningless at another. Store the session token in an HttpOnly, Secure cookie where you can. See customer accounts.

Webhooks

Outbound webhooks are signed so you can confirm authenticity and reject replays — always verify the signature before acting, and dedupe on the delivery id.

Privacy by design

Some endpoints are deliberately non-disclosing: lead capture and magic-link request always return the same response whether or not the email already exists, so an attacker can’t enumerate customers. Don’t build UI that depends on a different response for “exists” vs “doesn’t.”

Responsible disclosure

Found a security issue? Please report it privately to amir@dallal.me rather than filing it publicly. Include steps to reproduce and the affected surface; do not access or modify data that isn’t yours while investigating.