Index is a property-search demonstrator for Siem Reap, Cambodia: 187 curated rentals, an open-street map with a walking-distance datum, and a scoring engine that shows its working. Behind it sits a deterministic ingestion pipeline that reads agency websites, extracts clean records without a single AI API call, and stages everything for one human approval before it ever reaches the catalogue.

Siem Reap’s rental market lives in Facebook groups, agency websites and street signs. A relocator asking “where should I live?” gets opinions, not evidence. The agencies have no tooling to show neighbourhood knowledge; they show listings.
Index’s premise: an area-intelligence product that maintains itself. The site keeps a curated seed catalogue honest, and an ingestion pipeline continuously reads agency sites, pulls structured records out of them with deterministic rules (no LLM, no API key, no per-listing cost), de-duplicates across four key tiers, and routes anything uncertain to a single review queue. The public site only ever shows what passed the gates.
The result is a fast, warm, editorial product that reads like it has a full research team behind it, built and sponsored by prtotype.com as a functional demonstrator of exactly that idea.
Warm paper, ink hairlines, mono labels and a scoring engine that treats honesty as a schema decision: a tour of what a visitor actually sees.

The full directory with faceted filters (area, beds, type, rent ceiling), server-side pagination and a match-score badge on every card. Prices show USD with a £ reference, because the audience is relocating expats.

Each property carries a tri-state amenity ledger: confirmed (evidence recorded), unconfirmed (never checked), absent (explicit negative evidence). “Not listed” never quietly becomes “doesn’t have it”. Unknown floor area records as zero plus a flag, never a guess.

Every listing plotted on OpenStreetMap, colour-coded by match score, with 1/2/3 km rings from Pub Street and a paper “spotlight” mask that keeps the view inside the 3.2 km walking radius. Pins sync both ways with the side list as you hover.

Four steps capture budget, beds, must-haves and deal-breakers, then re-score the whole city against your brief on the server. The weights are data, not secrets: each scoring axis carries named points, so a “why this score?” is answerable in one sentence.

A typed-content guide to Siem Reap’s seven zones: character, rent ranges and practical notes, with a sticky table of contents. The context a listings grid can never give you.

A rule-based Telegram-style chat answers /top, /areas, /stats and plain questions like “2 bed under $500 in Wat Bo” from the same merged pool the site serves, with a per-IP rate limiter and no LLM dependency.
The polish a renter never notices but always benefits from: correctness, speed and resilience baked into the architecture.
The seed dataset never ships to the browser. Brief scoring runs behind a server action, and a grep of the built client chunks proves the data is absent. Pages stay at ~112 kB first load.
Listing names come from third-party sites, so they are treated as hostile: JSON-LD is serialised with < escaping, and map popups are built with DOM APIs rather than string HTML. Injection has nowhere to land.
CSP (tuned until the map tiles actually loaded), frame denial, nosniff, referrer policy and a per-IP rate limiter on the bot API. The admin surface is blocked at the CDN edge entirely.
A paper-coloured radius mask hides everything beyond the 3.2 km walking datum, so the map reads as a tight spotlight on the walkable core instead of a sea of irrelevant pins. Geometry is unit-tested, not eyeballed.
Tri-state amenities, unknown-size flags and a status vocabulary automation may never touch are type-level decisions, not copywriting. Every feature inherited the honesty model for free.
Nothing is marked done without a passing test or a live probe: 154 offline tests, fixture-based, zero network in the suite. Four parallel fix agents once landed a full security wave in a day, re-verified 10/10 by an adversarial reviewer.
A seven-stage intake pipeline turns messy agency sites into clean, de-duplicated, human-approved records. Every stage is isolated, and the one irreversible action, publishing, always needs a person.
// discover → fetch → extract → normalise → validate → dedup → review
for (const adapter of ['realestate-kh', 'dabest', 'ips-cambodia', 'fazwaz']) {
const urls = await adapter.discover(); // SSRF allowlist + throttle
const parsed = await fetchAndParse(urls); // fixtures out, no writes
const clean = parsed
.map(extractDeterministic) // rules + synonyms, zero LLM
.map(normalise).filter(validate); // hard gates or fail loudly
const { unique, duplicates } = deduplicate(clean); // union-find, 4 key tiers
writePreview(unique, duplicates); // ← JSON only. never live
}
// a human approves records in the admin queue. that is the whole trick.A deliberately lean, mostly-static architecture: sophisticated where it counts, boring everywhere else.
Index is live: a real, self-maintaining area-intelligence demonstrator built and sponsored by prtotype.com to show what an evidence-gated rental product looks like in production.
Index went from spec to a live, self-maintaining product: audited, hardened and shipped with the update engine built in. If you have a data-heavy idea that needs the same treatment, let's talk it through.