← Back to work

Arbolito

Full-stack · product built with a UI/UX designer
Privacy-preserving location: coordinates deterministically fuzzed (SHA-256, 300m radius); exact position only revealed once the exchange is accepted
Blue-dollar rate with a circuit breaker over Bluelytics (CLOSED/OPEN/HALF_OPEN), in-memory cache falling back to Postgres, and a staleness flag
Request state machine with transactional creation (request + conversation + message), chat locked until acceptance, and cron-based expiry

P2P · USD/ARS · Argentina

A “Tinder” for the blue-dollar rate: see offers near you at the current rate, send a request for the amount you want, and if they accept, a chat opens to coordinate the exchange.

USD / ARS 1,482.00/1,515.00 live rate — the anchor of every screen
Feed of nearby offers with the live CurrencyPill

What problem does Arbolito solve?

In Argentina the informal dollar —the “blue”— is exchanged every day in cash, face to face, through arbolitos and cuevas. It is a huge market that lives in WhatsApp groups, referrals and gut-feel trust: you rarely know who you are buying from, the real rate, or whether the meeting is safe.

Arbolito started from a simple question: what if that exchange had the clarity of a fintech app and the discovery mechanics of a dating app? See offers near you, with the current rate, the other party’s reputation, and a channel to coordinate — without exposing your identity or location until there is a deal.

How was Arbolito built?

We started with the identity. With my designer partner we defined a full design system before writing any product: the “cueva” aesthetic —deep navy, mint green, elevation by color rather than shadows, tabular-nums for every figure— and the signature element, the CurrencyPill that keeps the rate alive in the header of every screen.

On that foundation I built the product end-to-end: a Turborepo monorepo with a NestJS API and a React PWA, real-time over Socket.io, and a set of domain modules (offers, requests, chat, geo, KYC, reviews, notifications).

Why it stayed a prototype. In parallel we ran a legal review of the model. The conclusion was that operating an informal-exchange marketplace put us in a regulatory grey zone we could not —and did not want to— sustain as a two-person side project. We chose to stop before launching and keep it as a working prototype. It was the right call, even if it stings: the product is finished at the design and architecture level, and that is what this case shows.

What technical decisions does Arbolito have under the hood?

The decisions that make Arbolito are not in the feature list, they are in the details. These are the ones I care about most.

Offer detail with rate and Request button
Trust & safety

Privacy-preserving location

Exchanging cash means meeting a stranger. That is why an offer’s exact location is never exposed: it is fuzzed server-side, and the real position is only revealed once the other party accepts the exchange.

  • Coordinates deterministically fuzzed with SHA-256 over the offer id
  • Uniform displacement within a 300 m radius (√ distribution so it does not cluster in the center)
  • Precise position only visible to the owner or once a request is accepted
Ad-creation wizard, sale price step
The rate, always present

The live blue rate, with a safety net

The rate is the heart of the product, so it could not fail. I pull it from a public source (Bluelytics) behind a custom circuit breaker, with an in-memory cache and a Postgres fallback. Ads can fix a price or track the market with a “±%”, and recompute themselves whenever the blue moves.

  • Custom circuit breaker (CLOSED / OPEN / HALF_OPEN) over the external API
  • In-memory cache (TTL) → Postgres fallback → staleness flag to the client
  • MARKET_PERCENT prices recomputed by cron when the rate changes
Request-sending screen
The match

From request to chat, as a state machine

The core mechanic is an explicit ask: you send a request for an amount, and the other party accepts or rejects it. Each request is a state machine (pending → accepted / rejected / expired) and its creation is transactional: request, conversation and first message are born together or not at all.

  • Atomic creation of request + conversation + message in one transaction
  • The chat stays locked until acceptance — no cold-messaging
  • Automatic cron-based expiry and event-driven notifications on every change
Chat with request card and actions
Real-time

A chat that feels instant and never lies

The chat runs over Socket.io with JWT auth and a room per user. The UI is optimistic so it feels immediate, but every message waits for server confirmation: if it fails, it rolls back. A custom rate-limiter keeps anyone from flooding a conversation.

  • JWT-authenticated gateway with per-user rooms
  • ACK-based delivery that rolls back the optimistic update on failure
  • Custom rate-limiter (5 msg/s) and strict server-side validation
Privacy and visibility screen
User control

Verification, visibility and blocking

In a trust market, control is part of the product. There is identity verification (KYC) as an isolated module, fine-grained visibility controls —who sees your name, your photo, your ads— and user blocking applied at the query level, not as a cosmetic filter.

  • KYC as a decoupled module, ready to plug in an automated provider
  • Per-field visibility: everyone / verified only / nobody
  • Blocked users filtered in the database across all relevant queries

A tour through the product

Entry screen with the arbo brand
Entry — the “arbo” brand and demo mode
Conversation list with verified users
Messages — exchange requests and verified users
My ads with market-linked pricing
My ads — “Market −3%” pricing, active/paused
Location picker on a map
Approximate location — drag to adjust
Why verify your account screen
Verification — why validating your account pays off
Account settings screen
Settings — profile, security and legal documents

Closing

Arbolito never reached production, but it did become a real product: a coherent design system, a domain-driven architecture, and a handful of decisions —location privacy, rate resilience, reliable real-time— that solved concrete problems of the Argentine context.

For me it was the exercise of taking an idea end-to-end with a design partner, and of knowing when to stop. I leave it here as a case: the code and the screens speak for themselves.