Systems thinking, shown — not claimed.
Two production platforms, one architect. Daedalus watches and self-heals Salesforce Marketing Cloud; Agora runs the cross-functional work behind campaigns.
This is the architecture underneath — and the reasoning behind every call. What runs in production is marked built. What is deliberate but not yet built is marked designed. No hand-waving in between.
The whole surface, honestly tagged.
Eight capabilities behind the two platforms. What is running in production is Built; what is designed but not yet shipped is Designed — and labeled as such.
Multi-tenant + secret management
Row-scoped tenancy, AES-256 credentials, tokens kept server-side.
Resilience & recovery
Durable queue, retried jobs, reruns that skip finished work.
Token & billing tracking
Per-tenant monthly AI budgets, overage policy, usage metering.
Human-in-the-loop AI
Advisory → Assisted → Autonomous. Staging-first, never prod.
Multi-stakeholder orchestration
Owners, SLAs, change-requests, a field-level audit trail.
Agentic orchestration + MCP
A multi-step recovery pipeline; an MCP tool server for agents.
Blue/green deployment
Zero-downtime cutover with instant rollback.
Horizontal scale-out
Runs in one process today; designed to broadcast across many.
The decisions — and the roads not taken.
Anyone can list features. Here is the reasoning behind five core calls — and the alternative I turned down each time.
Isolate tenants by row. Encrypt every credential. Never let the browser hold a token.
Fifty client business units behind one login. A leaked credential isn't an incident, it's fifty brands' subscriber data.
One agency manages fifty client business units from one place. Tenancy is enforced by scoping every query to a business-unit id — not by standing up a database per client. SFMC client secrets are encrypted at rest (AES-256-CBC, a fresh IV per credential) and decrypted only at the moment of an API call. The browser never sees a token: every request hits a server route that attaches the user's auth token and proxies onward.
Database-per-tenant — operationally brutal at fifty-plus BUs, and every migration multiplies. Browser-held tokens — one XSS and the keys to every client leak.
business-unit row-scoping · crypto.ts (AES-256) · server-side proxy routes
Put a durable queue in the middle. Never rerun work that already succeeded.
A dropped event is a send that never goes out. At 35 million messages a day, “we'll catch it in the morning” isn't a recovery plan, it's a postmortem.
A burst of SFMC events must not be able to overwhelm processing. The ingest service does almost nothing per request — validate, enqueue, return fast. The app drains the queue at its own pace, and every job is persisted in Postgres, so a restart loses nothing. When a failure triggers a rerun, the system rebuilds a copy containing only the failed steps and the ones that never started: a seven-step automation that died at step five reruns five-through-seven, not one-through-seven.
Synchronous processing — couples ingest latency to downstream load and drops events under burst. A separate queue service (Redis, SQS) — more infrastructure to run when Postgres already gives durability.
Daedalus ingest → pg-boss queue → worker pool · trimmed-rerun builder
Treat AI tokens as a metered, budgeted resource — per tenant, per month.
AI spend answers to a marketing P&L like every other line item. A model that quietly costs more than the campaign earns is a model nobody gets to keep.
Unbounded LLM spend is how an AI feature quietly becomes an AI liability. Every model call writes its input and output token counts to a usage row, summed against a per-business-unit monthly limit. Hit the cap and only AI pauses — monitoring, reruns, and events keep running. Overage is a policy choice per account: block, meter, or meter-with-a-cap. A busy tenant can never burn another's allowance.
Unmetered AI — no cost ceiling, no per-tenant fairness, a surprise on the invoice. A blanket org-wide limit — one heavy tenant starves everyone else.
AiUsage / KyntraUsage tables · daily usage-cycle sweep · metered-overage handling
Make AI autonomy a setting, not an assumption. Classify with rules before calling the model.
Most automation failures are a timeout or a locked table. Paying a language model to rediscover that every night is how AI becomes the thing finance asks about.
Most failures don't need an LLM. A fast rule-based classifier buckets errors — timeout, query conflict, data volume, syntax, permission, missing object — and only escalates to the model when a fix genuinely needs reasoning. Cheaper, faster, more predictable. When the model does run, it works on staging copies and never edits a production automation during diagnosis. Recovery has three gears the operator picks per rule: Advisory (diagnose only), Assisted (apply to staging, a human promotes), Autonomous (auto-promote on a clean rerun).
LLM-first for every error — slow, costly, and non-deterministic for problems a regex solves. All-or-nothing autonomy — teams won't adopt AI they can't dial down.
aiRemediation.ts classifier → Claude → staging apply → promote gate
Separate the workflow definition from the running instance. Record who changed what.
Somebody changed the audience after Legal approved the creative. Either the system can tell you who and when, or the next conversation is with your compliance team.
Cross-functional campaign work has many owners and constant change. A Blueprint is the reusable definition; an Initiative is a live instance of it, where every step becomes a section with an owner, an SLA, and a state. Edits flow through change-requests with field-level diffs and an append-only audit log. The same branching logic runs in the builder preview, the fill view, and the launch. One engine, not four. Agora is where that model gets explained properly.
One document per campaign — no reuse, process never compounds. Free-form edits without diffs — no source of truth, and approvals drift silently.
Module → Blueprint → Initiative · ChangeRequest diffs · ChangeLog audit · flow.ts engine
Designed, not yet shipped.
The scale and resilience work I've architected but deliberately haven't built — shown as design, with the honest reasoning for why each one is still on paper.
Scale and resilience, deferred on purpose
Blue/green deploys
Two identical environments behind a router. Deploy to the idle one, make only additive database changes so nothing breaks the side that's still live, test it, then flip traffic over. The old environment stays warm, so rolling back is a router change rather than a redeploy. It's designed but deliberately not running: a second always-on environment roughly doubles the hosting bill, so for a solo-funded lab it stays on paper until the uptime need justifies the cost.
Database failover
The durable queue already makes the app servers disposable: if one dies, another picks the work up. That leaves the database as the one piece worth protecting. Keep a second copy running in lockstep with the live one, have it take over automatically when the first stops answering, and a database outage becomes a brief blip instead of an incident.
Horizontal scale-out
The live dashboard is fed from inside a single running copy of the app today. Simple, and it works fine on one server. To scale out, every event would broadcast to every running copy over a shared channel, so the live view keeps up no matter how many are running. Designed and understood; deliberately left off while one is plenty.
Bringing AI into an org is a governance problem, not a model problem.
Three principles the build is organized around — and the core of how I would run AI enablement for a team.
Governance is a dial, not a switch.
Advisory, Assisted, Autonomous. Teams adopt AI at the trust level they're ready for and ratchet up as confidence grows — instead of an all-or-nothing leap nobody will sign off on.
Make AI accountable to a budget.
Every model call is metered against a per-tenant monthly allowance. AI spend is visible, attributable, and capped before it's a surprise — the difference between an AI feature and an AI liability.
Use AI where it earns its place.
Rules handle the deterministic eighty percent; the model is reserved for the twenty that genuinely needs reasoning. Knowing which is which — and proving it by classifying before escalating — is most of the job.