Skip to content

Access control — ZevSend

Who can access what. The hard rules — implemented in code — are detailed below; the soft rules (employee onboarding, key rotation cadence, etc.) sit with the DPO office and platform security pages.

Customer-facing

  • Customers see only their own teams. A customer's API request must include a team context (via the active session); the team-membership check runs in middleware before any controller / resolver.
  • Within a team, the team owner has every permission. Member roles (today: simple member; finer-grained roles will be added when customer demand justifies the complexity) get scoped read / write on team resources.
  • API keys carry per-key scopes — including optional domain pinning so a key restricted to one domain cannot dispatch from a sibling domain on the same team.
  • API key env separation is server-enforced: the setup wizard mints only test keys (and only when the team has zero active keys); the /api-keys page mints only live keys (and only when an admin-approved domain exists on the team).

Admin (engineering / ops)

  • All admin access goes through the admin panel at the admin.* host. Same ZevID OAuth flow as the customer dashboard, additionally gated by an RBAC permission list.
  • Each admin endpoint declares its required permission via @RequireAdminPermission(...). The decorator is the enforcement; permissions are not optional comments.
  • Sensitive admin mutations (plan change, invoice mark-paid) additionally require the x-admin-mfa-token header, verified server-side against ZevID before the resolver runs (the AdminMfaGuard).
  • Domain review (DOMAINS_REVIEW) is separate from the suspension permissions (TEAMS_SUSPEND) so an ops teammate handling brand approvals doesn't automatically get the ability to take a team offline.
  • Display-name + sender-id reviews ride the same DOMAINS_REVIEW permission today (small surface).
  • Every admin write produces an admin_actions row.

Production database

  • Direct DB access is held by the lead engineer (Daniel Arowolo) only. Day-to-day operations go through the application or admin panel, never raw SQL.
  • The application runtime connects via a scoped Postgres user. Migrations apply via a dedicated migration step on deploy (MIGRATIONS_RUN_ON_BOOT=true is the supported path; the migration connection uses an unpooled Neon URL so DDL doesn't split across pooled sessions).

Production infrastructure

  • Coolify (Hetzner Ashburn) — host-level access held by the lead engineer.
  • AWS SES (production account) — IAM credentials scoped to the zevsend-default configuration set; held in deploy-platform secrets.
  • AWS SNS topic for SES feedback — subscription endpoint pinned to prod.zevsend.com/webhooks/aws-sns; signature-verified per request.
  • Redis (BullMQ) — connection credentials held in deploy-platform secrets.

Cross-product (ZPIP) access

  • Inbound ZPIP calls (other Zev products asking ZevSend to send mail on the user's behalf) require:
  • A valid ZPIP service+user token from ZevID
  • A user-consent grant for the requested scope (e.g. zevsend.send)
  • The target resource (the from-domain) belonging to the user named in the token
  • Outbound ZPIP calls ZevSend makes (e.g. to ZevCloud's dns.records.write scope when provisioning DKIM on a zevcloud-managed domain) carry HMAC-SHA256 signatures and a per-scope consent token resolved at ZevID.

Reviews

Permission membership reviews are not yet on a fixed cadence — at our current size the admin headcount is one. When ops grows, the review cadence will be formalised (default candidate: quarterly).