3. Technology stack (v1)

v1 architecture decisions (this revision): single PostgreSQL with the TimescaleDB extension as system of record, event log, analytics store, and vector store · transactional Postgres event log instead of Kafka (RabbitMQ evaluated and rejected; Kafka/Redpanda deferred with triggers) · RudderStack for tracking ingestion, landing in Postgres (ClickHouse deferred with triggers) · GraphQL-primary public API with a thin REST facade · Cassandra and SurrealDB evaluated and rejected as system of record.

3.1 Backend - Phoenix / Elixir / Erlang

ConcernChoiceRationale
Language / runtimeElixir on the BEAM (Erlang VM)Fault isolation, lightweight processes, massive concurrency; one tenant's failure cannot cascade
Web frameworkPhoenixMature, productive, first-class real-time support
Internal web UIPhoenix LiveViewServer-rendered, real-time UI for boards, dashboards, job folders, and settings with minimal JavaScript
Interactive canvasesReact islands (React Flow) inside LiveView pagesThe automation canvas and quote designer are too interaction-heavy for LiveView alone
Public APIGraphQL-primary (Absinthe) + thin REST facadeOne 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 & workflowsOban 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-timePhoenix Channels + PubSubLive board updates, presence, texting UI - built into the framework
Domain eventingTransactional 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
CachingETS / CachexIn-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)

ConcernChoiceRationale
System of recordPostgreSQL + TimescaleDB extensionJSONB 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 logAppend-only events hypertable (monthly chunks)Automatic time partitioning from day one; native compression on old chunks keeps growth manageable
Analytics & tracking dataRudderStack landing tables in Postgres/Timescale + continuous aggregatesIncrementally-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 storepgvectorEmbeds contact communication history to ground AI selling playbooks
GeospatialPostGISParcel geometries, storm-swath polygons, radius/polygon segment queries, and territory routing for the prospect data engine - same database, same toolchain
Prospect warehouseSeparate schema/database on the same Postgres+Timescale+PostGIS clusterPhysically colocated for shared tooling, logically isolated from CRM OLTP; parcels and storm events are naturally geospatial/time-series
Object storageS3 + CloudFrontPhotos/documents dominate storage; lifecycle to Glacier for closed jobs
SearchOpenSearch (Apache 2.0; preferred over Typesense's GPL server - see §6)Typo-tolerant global search

Deferred infrastructure - reintroduction triggers

ComponentStatusTrigger to introduce
ClickHouseDeferredTracking-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 / RedpandaDeferredEvent-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

FunctionProvider
PaymentsStripe (cards, ACH, text-to-pay) - or embedded payments via Payrix/Finix for a revenue share
Consumer financingWisetack API; Sunlight Financial secondary
E-signingDropbox Sign or Documenso embedded APIs
Telephony / SMSTwilio (two-way texting, caller ID, A2P 10DLC)
EmailPostmark (transactional) + Nylas (two-way inbox sync)
PDF generationPlaywright/Chromium render farm
AccountingIntuit API for QBO; lightweight web connector for QuickBooks Desktop
MeasurementsDirect 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

FunctionSource / providerCost model
Parcel & ownership dataCounty assessor/recorder feeds directly, or an aggregator (Regrid, ATTOM, CoreLogic) for normalized multi-county coverageFree-to-modest per county (labor-intensive) or aggregator subscription
Building permitsMunicipal portals or permit aggregators (e.g., Shovels)Free per portal or API subscription
Storm swathsNOAA/NWS public hail and wind dataFree
Contact enrichmentBroker append APIs (Melissa, BatchData, Endato, PropertyRadar) behind an internal adapter interface - not realistically self-sourceablePay-per-record, on demand, segment-gated
Address hygieneUSPS CASS/NCOA processingPer-batch service fee
DNC / suppressionNational + state DNC scrub servicesSubscription
Direct mailMail-house API (Lob, PostGrid)Per-piece
Ad audiencesMeta / Google customer-match APIs (hashed uploads)Ad spend
Offline attributionPer-campaign tracking numbers (Twilio), UTM/QR codesPer-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).

← FeaturesArchitecture →

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