DEV Community

Cover image for Dev Log: 2026-08-05 — reverse proxies, email journeys, and a tenant that wasn't there
Nasrul Hazim
Nasrul Hazim

Posted on

Dev Log: 2026-08-05 — reverse proxies, email journeys, and a tenant that wasn't there

TL;DR

  • On a deployment platform: added a real nginx reverse-proxy tier with health gating, provider capability enforcement, a standard blueprint library, and release rollback.
  • On a CRM app: shipped email automations (journeys) — trigger-based sequences with enrollment and send tracking — plus organisation member management.
  • Squashed a sharp multi-tenancy bug: self-registered users were created without a tenant, so they couldn't create anything. Two repos, same class of fix.

Two products moved meaningfully today (plus a dependency bump elsewhere). Honest rundown below.

Deployment platform: routing and safety rails

The headline is a tier-3 reverse proxy — a real nginx layer in front of workloads, with routing gated on workload health so traffic only reaches something that's actually up. Around it: infra providers now enforce capabilities (a provider advertises what it can do via a checklist, and the platform refuses operations it can't back), a standard blueprint library with MySQL/MariaDB provisioners, and release rollback so a bad deploy has a way home.

I also fixed two gotchas from spawning docker/kubectl out of PHP — the 30s web-request timeout and the empty-environment trap. That earned its own focused post today, so I won't repeat it here.

CRM: email journeys

The big feature is email automations — call them journeys or sequences. A contact hits a trigger (a tag added, a form submitted), gets enrolled, and walks through timed email steps. The design leans on the usual suspects:

Piece Role
Enums (SequenceTrigger, SequenceStepType, SequenceSendStatus) Typed state with label()/color()
Listeners on domain events Enroll a contact when a tag is added
Send-status sync from mail events Keep each step's delivery state honest

Modelling the send as its own tracked entity — not just "fired and forgot" — is what makes the sequence auditable later.

The bug worth naming: a missing tenant

Same shape landed in two apps today. Self-registered users were being created without a tenant assignment, so downstream every "create a record" call failed — the record had nowhere to live. The fix is to make tenant assignment part of user creation, not an afterthought.

// in the create-user action, not left to chance later
$user->tenants()->attach($tenant->id, ['role' => 'owner']);
Enter fullscreen mode Exit fullscreen mode

Lesson I keep relearning: in a multi-tenant app, "which tenant?" is not a later question. If a user can exist without one, some code path will find that gap.

What's next

Wire the reverse-proxy tier to real health checks end-to-end, and put the email-journey enrollment under a proper Pest suite before it touches live contacts.

Top comments (0)