Project · Sulfur

Agent Arena

Sealed-bid combustion — the market decides which agent represents the home.

Essence

For the people who use it

Picking a real-estate agent is the most consequential decision a homeowner makes in a sale, and it's the decision with the worst information. Three agents pitch, the homeowner picks the one with the warmest handshake, and the market never gets a vote. Agent Arena flips the model. Verified agents bid for exclusive listing rights on a property — sealed bids, real time, with the homeowner watching. The market sets the agent's value.

The flow is direct. A homeowner lists their property. The platform identifies qualified agents in the ZIP code, ranked by data (sold-price ratio, days on market, recent reviews). Those agents are invited to a real-time auction with a countdown. The highest bidder wins exclusive 90-day representation, pays the commission upfront via Authorize.Net, and the listing begins.

Built for the U.S. luxury segment ($1M+) where the commission is worth competing for and the homeowner has the leverage. Two sides, two pains solved: homeowners get vetted, ranked agents competing on their property; agents get leads against actively engaged sellers instead of cold-prospecting.

Visit Agent Arena

Construction

For the engineers

Stack

API
ASP.NET Core 10 on .NET 10 · MediatR 14 (CQRS) · JWT Bearer · FluentValidation 12 · Swagger 3.3.1
Public site
Blazor WebAssembly 10 — custom CSS only, no frontend framework
Admin
Blazor WebAssembly 10 with Bootstrap
Real-time
SignalR 10 — dedicated auction hub in a separate process
Database
SQL Server / Azure SQL · EF Core 10 in database-first mode, no migrations
Payments
Authorize.Net 8.0.1 — tokenized, PCI-DSS by design
Messaging
SendGrid 9.29.3 (email) · Twilio 7.14.3 (SMS)
Storage
Azure Blob Storage 12.27.0
Observability
Application Insights · OpenTelemetry 1.15.3 · Raygun 2.0.1
Testing
NUnit 4 · Moq · Playwright 1.58 · NBomber 6.2.0 (50–100 concurrent agents)
Hosting
Azure App Services on Linux · Cloudflare DNS · Azure DevOps pipelines

Architecture

Twelve-project solution. Core (~28 repository interfaces, ~44 service interfaces, every domain entity), Infrastructure (EF Core repositories + Authorize.Net + SendGrid + Twilio + Blob), Common (shared DTOs and contracts), Api, Site (public Blazor WASM, pre-rendered for SEO), Admin, Auction (the SignalR hub process), Tests, LoadTests, RedFin (console scraper), plus TheLedger.Api + TheLedger.Site — a sibling Azure-Functions / static-site lead-generation funnel that drives luxury sellers into the auction platform.

The auction hub runs in its own process. SignalR lives in AgentArena.Auction with its own deployment and its own scaling story. Auction state is a static ConcurrentDictionary protected by per-room semaphores; messaging is group-based per auction; JWT is passed via query string for the WebSocket upgrade. The API can scale horizontally without contending for auction state, and a misbehaving auction can't take the API down with it.

Payment tokenization end-to-end. Authorize.Net tokens are the only payment identifier the platform ever sees — raw PCI data never enters a log, never lands on disk. Silent post webhooks reconcile transactions out-of-band. The full threat model is documented in payment-security-hardening-2026-02-10.md.

Three-layer caching with explicit coordination. API memory cache with sliding expiry (30 min–24 h) + a CacheCoordinationMiddleware that auto-invalidates on mutations + client-side localStorage / IndexedDB with monthly refresh against ETags. Stale data across services is solved by coordination, not hope.

The RedFin mailing-list pipeline. A standalone console app pulls Redfin gis-csv across twenty South Florida ZIPs, deduplicates by MLS#, and bulk-inserts ~13K rows per run via SqlBulkCopy into a RedfinRaw staging table. The API later promotes those rows into PublicListing records on a lifecycle — Sequence A1–A8 for stale-but-active listings bucketed by days-on-market, Sequence B1–B4 for the four months after expiration — and targeted mailers go out per bucket. Address normalisation is multi-step (uppercase, strip punctuation, standardise suffixes / directionals / units, append city / state / ZIP), critical because 93% of ATTOM records carry unit numbers and unit-level precision is the only way to avoid false-positive matches.

Notable details