3.1 Backend - Phoenix / Elixir / Erlang
| Concern | Choice | Rationale |
|---|---|---|
| Language / runtime | Elixir on the BEAM (Erlang VM) | Fault isolation, lightweight processes, massive concurrency; one tenant's failure cannot cascade |
| Web framework | Phoenix | Mature, productive, first-class real-time support |
| Internal web UI | Phoenix LiveView | Server-rendered, real-time UI for boards, dashboards, job folders, and settings with minimal JavaScript |
| Interactive canvases | React islands (React Flow) inside LiveView pages | The automation canvas and quote designer are too interaction-heavy for LiveView alone |
| Public API | GraphQL-primary (Absinthe) + thin REST facade | One Absinthe schema is the source of truth and serves mobile, web islands, and public partners. A thin REST facade (Phoenix controllers delegating to the same contexts) covers the top 10–15 partner operations - create lead, fetch job, attach document, webhook management - because the long tail of integrators (suppliers, Zapier-class tools, small agencies) speaks REST + webhooks. Public GraphQL ships with query-cost analysis, depth limits, and persisted queries; webhooks are REST-shaped regardless. |
| Background jobs & workflows | Oban Pro (Postgres-backed; commercial license - see §6) | Durable, multi-day workflow execution with retries, scheduling, and workflow/batch primitives - replaces Temporal, BullMQ, and Redis queues |
| Real-time | Phoenix Channels + PubSub | Live board updates, presence, texting UI - built into the framework |
| Domain eventing | Transactional Postgres event log (append-only, outbox-style) | Every state change inserts an immutable event row in the same transaction as the change - stronger consistency than a broker can give without outbox machinery. Consumers (automation engine via Oban, audit trail, projections) track cursors; PubSub broadcasts after commit; replay is a WHERE id > cursor query |
| Caching | ETS / Cachex | In-BEAM caching removes a Redis dependency |
Why the BEAM fits this product: the automation engine is the core of the product, and OTP supervision trees give per-tenant isolation with clean restarts; real-time collaboration is pervasive and Phoenix makes it a language feature; the event surface (Twilio, suppliers, measurements, payments, web tracking) is large and BEAM concurrency absorbs bursts gracefully; and PubSub, queues, caching, scheduling, and now eventing all live inside the runtime or the primary database, collapsing infrastructure to a minimum.
3.2 Data layer (v1)
| Concern | Choice | Rationale |
|---|---|---|
| System of record | PostgreSQL + TimescaleDB extension | JSONB for per-tenant custom fields; row-level security for multi-tenancy; transactions across state + events; backs Oban and pgvector. TimescaleDB is a Postgres extension, so the entire Ecto/Oban/pgvector toolchain is unchanged |
| Event log | Append-only events hypertable (monthly chunks) | Automatic time partitioning from day one; native compression on old chunks keeps growth manageable |
| Analytics & tracking data | RudderStack landing tables in Postgres/Timescale + continuous aggregates | Incrementally-maintained rollups power the reporting dashboards and per-lead website-activity panels without a separate OLAP system. Run on a hypertable-enabled instance that can start shared and later split from OLTP |
| Vector store | pgvector | Embeds contact communication history to ground AI selling playbooks |
| Geospatial | PostGIS | Parcel geometries, storm-swath polygons, radius/polygon segment queries, and territory routing for the prospect data engine - same database, same toolchain |
| Prospect warehouse | Separate schema/database on the same Postgres+Timescale+PostGIS cluster | Physically colocated for shared tooling, logically isolated from CRM OLTP; parcels and storm events are naturally geospatial/time-series |
| Object storage | S3 + CloudFront | Photos/documents dominate storage; lifecycle to Glacier for closed jobs |
| Search | OpenSearch (Apache 2.0; preferred over Typesense's GPL server - see §6) | Typo-tolerant global search |
Deferred infrastructure - reintroduction triggers
| Component | Status | Trigger to introduce |
|---|---|---|
| ClickHouse | Deferred | Tracking-event volume or dashboard query latency exceeds what Timescale continuous aggregates handle economically; columnar scans become decisively cheaper. Reporting queries live behind a repository interface so the swap is a data migration, not a rewrite |
| Kafka / Redpanda | Deferred | Event-log write volume contends with OLTP; retention growth outpaces partition+compression; or consumer fan-out (dozens of cursors polling) gets clumsy where consumer groups are effortless. Consumers already think "read events after cursor," so migration is mechanical. Note Redpanda is BSL (see §6); Apache Kafka is Apache 2.0 |
Rejected alternatives
3.3 AI / personality layer - locally hosted, purpose-tuned LLM Changed in v4
3.4 Website tracking
3.5 Mobile
3.6 Third-party services
| Function | Provider |
|---|---|
| Payments | Stripe (cards, ACH, text-to-pay) - or embedded payments via Payrix/Finix for a revenue share |
| Consumer financing | Wisetack API; Sunlight Financial secondary |
| E-signing | Dropbox Sign or Documenso embedded APIs |
| Telephony / SMS | Twilio (two-way texting, caller ID, A2P 10DLC) |
| Postmark (transactional) + Nylas (two-way inbox sync) | |
| PDF generation | Playwright/Chromium render farm |
| Accounting | Intuit API for QBO; lightweight web connector for QuickBooks Desktop |
| Measurements | Direct REST integrations behind an internal adapter interface |
3.7 Infrastructure & DevOps
3.8 Security & compliance
3.9 Data acquisition & activation services (prospect data engine) New in v3
| Function | Source / provider | Cost model |
|---|---|---|
| Parcel & ownership data | County assessor/recorder feeds directly, or an aggregator (Regrid, ATTOM, CoreLogic) for normalized multi-county coverage | Free-to-modest per county (labor-intensive) or aggregator subscription |
| Building permits | Municipal portals or permit aggregators (e.g., Shovels) | Free per portal or API subscription |
| Storm swaths | NOAA/NWS public hail and wind data | Free |
| Contact enrichment | Broker append APIs (Melissa, BatchData, Endato, PropertyRadar) behind an internal adapter interface - not realistically self-sourceable | Pay-per-record, on demand, segment-gated |
| Address hygiene | USPS CASS/NCOA processing | Per-batch service fee |
| DNC / suppression | National + state DNC scrub services | Subscription |
| Direct mail | Mail-house API (Lob, PostGrid) | Per-piece |
| Ad audiences | Meta / Google customer-match APIs (hashed uploads) | Ad spend |
| Offline attribution | Per-campaign tracking numbers (Twilio), UTM/QR codes | Per-number/usage |
Ongoing external costs are unavoidable for contact enrichment, address hygiene, DNC scrubbing, and channels - the economics shift from renting bulk lists annually to owning the public-records base layer and paying retail only for enrichment on demand. Realistic accuracy expectations: county data authoritative but weeks-to-months lagged; appended emails 60–80% deliverable and phones worse, industry-wide. Enrichment contracts should be negotiated for perpetual rights to appended records before signing (see §5).
AYO & Ryzo Platform - hypothetical product specification and wireframe set, v4.0 (July 2026). Feature inventory derived from public JobNimbus and SumoQuote materials; all screens, names, and figures are illustrative. Back to index