Technology Stack
Two complementary systems powering retail marketing activation — cross-channel orchestration, and real-time onsite ad delivery.
Architecture Documentation
Choose a platform to explore its architecture, data flows, and component diagrams.
Naboria RMA
A Go + Next.js omnichannel campaign platform with ML budget optimization, geo-lift measurement, A/B testing, Prophet-based forecasting, and multi-channel sync across Waze, Meta, WhatsApp, Google, and Naboria Ads.
This is the orchestration brain — where campaigns are defined, budget is allocated, and incrementality is measured.
View ArchitectureNaboria Ads
A Rust/Axum ad serving pipeline with Kafka event streaming into ClickHouse, XGBoost CTR prediction, first-price auction clearing, budget pacing, and real-time delivery over server-sent events.
This is the engine that serves sponsored product placements on retailer storefronts — and the onsite surface RMA optimizes into.
View ArchitectureOne Loop, Two Systems
RMA decides where money should go. Naboria Ads is one of the surfaces it spends into — and one of the sources of evidence that comes back.
The two platforms are deliberately separable. RMA can orchestrate offsite channels without Naboria Ads existing, and Naboria Ads can monetize a retailer storefront without RMA. What makes them a stack is the measurement contract between them.
Naboria Ads exposes a Meta-compatible insights API. That is not an accident of convenience: it means RMA consumes onsite performance data through the same interface shape it already uses for offsite channels, so a sponsored placement and a Meta ad set are directly comparable inside one optimizer rather than being reconciled after the fact in a spreadsheet.
The daily loop runs in one direction and the evidence runs back the other way. RMA allocates budget across channels overnight, pushes campaign state out through each channel's API in the morning, and then pulls performance back in — impressions, clicks, spend, and where retailer data is available, sell-through. Geo-lift analysis turns that into an incrementality estimate, which becomes an input to the next allocation rather than a report nobody reads.
Why These Components
The short version of the decisions that shaped each system.
Rust for ad serving
Ad selection sits in the request path of a retailer's storefront, so its latency budget is single-digit milliseconds and its tail latency matters more than its average. Rust and Axum give predictable performance without a garbage-collection pause landing in the middle of an auction.
Kafka into ClickHouse
Ad events are high-volume, append-only, and queried analytically — the shape ClickHouse is built for. Kafka decouples serving from analytics so a slow query or a schema migration downstream cannot degrade ad delivery.
XGBoost for CTR
Click prediction here runs on tabular features with modest data volume by ad-tech standards. Gradient-boosted trees remain hard to beat in that regime, train in minutes rather than hours, and stay inspectable when a buyer asks why their product was not shown.
Hill curves and SLSQP
Channel response to spend saturates rather than scaling linearly. Hill curves capture that shape with few parameters, which matters when a geography has limited history. SLSQP then solves allocation under real constraints — minimum spends, channel caps, a fixed total.
Difference-in-differences
Last-click attribution credits whichever channel a shopper touched most recently, which systematically flatters lower-funnel spend. Comparing activated regions against matched controls estimates what would have happened anyway, and permutation tests check the result is not an artifact of which regions were picked.
Go and Next.js for RMA
Campaign orchestration is IO-bound fan-out across many third-party APIs with different rate limits and failure modes — Go's concurrency model fits it directly. Next.js covers the operator-facing surface where the work is dense forms, tables, and charts.
What the Diagrams Don't Show
Every external channel is an integration with its own authentication model, rate limits, and eventual consistency. A budget change pushed at 6 AM is not reflected in reporting instantly, and channels disagree about what a conversion is. A meaningful part of RMA is normalization and reconciliation rather than modelling.
Measurement is also constrained by what retailers are willing to share. Closed-loop attribution is strongest where aggregated sell-through is available and weaker where it is not, which is why geo-lift is designed to produce a useful estimate from regional outcomes rather than depending on per-transaction data.
The architecture pages go into the specifics — component diagrams, data flows, and where each of these constraints is handled.