Supabase ships new projects with anon and authenticated granted every privilege on every table by default, and Row Level Security off. That default sat live on Suki for weeks. An audit on 2026-07-14 found the public anon key, the one shipped in the browser bundle, could read real order rows (names, phone numbers, addresses) and held delete and truncate rights on all 31 tables.
04 Consumer PWA · Two-Sided Marketplace
Suki Neighbors.
The condo group chat, replaced
Every condo tower in Metro Manila runs on the same hidden economy: home cooks and small sellers move food through a Viber or Facebook group chat that everyone in the building is already in. I built Suki Neighbors to replace that group chat with an actual marketplace, scoped to one building at a time, and took it from spec to a live, installable app in about two weeks.
The group chat that outgrew itself
Most Philippine condo towers have a buy-and-sell group chat on Viber or Facebook, sometimes three of them per building. Neighbors post what they are cooking that week: adobo, leche flan, meal-prep containers, whatever a home cook or a small carinderia has ready. Buyers reply in the thread to claim a plate. It works, in the sense that money changes hands, but it was never built to be a market.
The word for the relationship at the center of this is suki: a regular customer a seller comes to know and favor, the backbone of how Filipinos have always bought from small vendors. A condo building recreates that relationship at scale, except the group chat has no memory. A listing posted Tuesday morning is buried under two hundred unrelated messages by Tuesday night. Buyers ask "meron pa ba?" (is it still available) into threads the seller has not seen in hours, get seen-zoned, and give up. Sellers cannot tell a serious buyer from someone who will ghost after claiming the last order.
None of the big platforms solve this. Grab and foodpanda take a commission a P55 plate of adobo cannot absorb, and neither one lets a rider past a condo lobby without a resident badging them in. The market is real, every tower has one, but it is a market of maybe forty active sellers and a few hundred regular buyers: too small for a national platform and too disorganized for a chat app to manage.
The decisions
I built Suki as an installable web app, not an App Store or Play Store listing. A hyperlocal product for one building does not clear the bar of an app-store download: nobody searches an app store for "my condo's food group," and a $99-a-year developer account plus review cycles buys nothing a resident cares about. A link dropped into the same group chat Suki is replacing, tapped once, "Add to Home Screen," gets someone from zero to browsing in under a minute. That distribution math only works as a PWA.
The tenancy model is the real product decision. Most marketplaces scope by GPS radius; I scoped by community instead, chosen once at signup, because the actual market boundary in a condo tower is the building, not a five-hundred-meter circle that pulls in three unrelated towers and none of the trust that makes a suki relationship work. Every community can be public, unlisted, or gated behind a join code, and the person already running the building's Viber group becomes the manager, with co-admin roles, a moderation queue, and a printable QR code that gets people in without typing anything.
Discovery and trust both had to work at the scale of dozens of sellers, not thousands. Ratings live on the seller, not the platform, one review per order, shown only once a seller has at least one. Verification is a manual admin review of a submitted proof, not a self-serve checkbox. Listings auto-expire off the feed twelve hours after a seller last touched them, which forces the same "is it still available" signal the group chat never gave buyers, but as a fact the app enforces instead of a question buried in a thread. Sellers get a limited pool of free boosts each month, not a pay-to-win auction, because paying to outrank your literal neighbor breaks the thing that makes a suki market work in the first place.
Suki never touches money. Every seller sets their own accepted methods, cash on delivery, GCash, or bank transfer, and the buyer pays the seller directly, the same way the group chat already worked, which also keeps Suki outside the marketplace withholding-tax rules that kick in the moment a platform starts holding or remitting funds. Delivery is the seller walking a plate to a unit themselves or a scheduled pickup, not a third-party rider account required to badge into a lobby; fulfillment runs on ASAP, a scheduled window, or pickup, with an automatic apology and stock restore if a seller ghosts an order for an hour.
How it came together
What it actually does
Buying
- Community-locked browse, no account needed until checkout
- Per-seller cart (orders are fulfilled and paid seller by seller)
- Guest checkout with instant order confirmation
- Saved address book, up to 6 addresses, synced across devices
- Live order tracking with status pushes and a proof-of-delivery photo
- Seller ratings, one per order, shown after a 24-hour window
Selling
- Seller dashboard with a bucketed order queue and batch status advance
- Tracked and untracked stock with atomic decrement, auto sold-out at zero
- Listing drafts with a real live preview before going public
- Free monthly boosts plus manager-granted boosts from a shared community pool
- Storefront page with cover photo, tagline, hours, and a printable QR badge
- Sales analytics: today, week, month, unpaid COD balance, cancellation rate
Ordering and fulfillment
- ASAP, scheduled (up to 7 days), and pickup fulfillment
- Pre-orders and pasabuy (bulk buying-favor requests) with their own cutoff clocks
- Per-seller delivery fees with a free-delivery progress bar in cart
- Reason-coded cancellation with automatic stock restore
- Oversell protection: concurrent checkouts on the last unit cannot both win
Discovery
- Shuffle-by-default feed so the same few sellers do not always lead
- Category filters, search, and a "notify me when this exists" watchlist
- Follow-a-store subscriptions with a push on every new listing
- One manager-assigned Featured Store slot per community
- Island / region / province / city browse hierarchy for the public directory
Community and trust
- Public, unlisted, or join-code-gated communities
- Manager and co-admin roles for the person already running the building's group
- Verified badge via manual admin review, auto-expires after one year
- Warn, timed suspend, and takedown moderation tools
- Request-to-join queue for private communities
Admin and ops
- Verification review queue for seller proof submissions
- Platform-wide stats dashboard: users, communities, GMV, order status mix
- Automated strike ladder for sellers who repeatedly ghost orders
- Public feedback board with admin-set status (working on it, shipped, not planned)
Notifications
- Web push for restocks, order status changes, and boost queue turns
- Mailgun transactional email with bounce and spam-complaint suppression
- Real-time seller bell on new orders via Supabase Realtime
The parts that were hard
01The anon key could read every buyer's name and phone number
Forced RLS on every table, revoked every grant from anon and authenticated, and set default privileges so future migrations cannot silently reopen a new table, the likely original cause. Re-granted only the two policy-gated reads Realtime needs, added REPLICA IDENTITY FULL where Realtime required it, then verified the fix by re-running the actual exploit against the anon key, not by re-reading the config.
02Stopping the same last plate from selling twice
Two buyers checking out the same down-to-one-unit listing at the same moment could both succeed. A decrement that floors at zero stops negative stock, but it does not stop two people from paying for one plate of adobo.
Moved stock reservation into a single row-locked Postgres function called before any seller-facing side effect runs, so a shortfall triggers an automatic partial rollback, a system-cancelled order, and a buyer apology push instead of a stranded oversold order. Verified with a real concurrency test: forced a listing to quantity 1, fired two simultaneous authenticated checkouts, got exactly one 200 and one 409, stock landed at exactly 0.
03Making a 12-hour freshness clock exact on a once-a-day cron
Vercel's free tier only runs cron jobs daily, but a stale listing had to disappear from the feed within 12 hours of the seller going quiet, or buyers would see "sold out three days ago" plates and stop trusting the freshness signal entirely.
Split enforcement from cleanup. Every feed read filters listings against the last-active timestamp inline, in the query itself, so a listing vanishes the instant it crosses 12 hours no matter when the cron last ran. The daily cron only materializes the already-true state and pings the seller to relist. Pre-orders and Official Store sellers got explicit exemptions layered onto the same read-time filter instead of a second parallel code path.
04Cancelling a ghosted order without ever cancelling on a false alarm
An ASAP order a seller never confirms has to eventually cancel and apologize to the buyer, but a naive timer risks killing an order the seller was actually still preparing.
Built the guarantee as two separate, ordered steps: a warning push fires at 45 minutes and stamps a timestamp, and the cancel job at 60 minutes refuses to run unless that timestamp exists and is at least 10 minutes old. Layered an escalating suspension ladder on top, one strike per seller per day, 1, then 3, then 7 days, with an explicit guard that stops the automation from ever converting a permanent admin ban into a lifted timed one.
Where it stands
Suki Neighbors is live at sukineighbors.com, currently on v0.59.1, installable as a PWA on iOS and Android. The codebase carries 65 applied migrations, 600-plus automated unit tests, and around 112 page routes, including a full local-SEO layer of guides, comparison pages, and a PH island-to-city directory.
The live site currently runs as a single public Demo Community: a real, fully working marketplace seeded with sample sellers and listings so anyone can try browsing, ordering, and the seller dashboard before a real building signs on. That is a deliberate sequencing choice, not a stall. The original condo and village communities are archived and reversible while pioneer building managers get recruited one tower at a time.
Every core mechanic, the 12-hour freshness clock, the payment handoff, the oversell protection, the strike ladder, has been exercised against the live database with forced timing and concurrency, not just in unit tests.
What I would tell you
- Scope the market to where trust actually lives. One building beats one city when the product depends on neighbors recognizing each other.
- A once-a-day cron can still deliver second-accurate behavior if the check runs at read time and the cron only cleans up after.
- Cloud-provider defaults are tuned for a fast demo, not for real user data. Audit privilege grants and RLS before the first real row goes in, not after.
- Write a race condition's fix as an explicit, testable guarantee, warn before cancel, never sell twice, instead of trusting it to fall out of the code naturally.
- A PWA kills app-store friction for anything hyperlocal. Nobody searches an app store for their own building.
- Taking payment off the platform removes a whole category of compliance and trust problems before they exist.
Suki is a full two-sided marketplace with real-time inventory, seller moderation, a peer-to-peer payment handoff, and its own SEO growth engine, built, secured, and iterated solo in under two weeks and counting. It is what I bring to a team building a marketplace, funnel, or ops platform: I will find the race condition and the leaky default before your users do.
∗ Work with me
Got a project? Let’s talk.
Suki Neighbors started with a real problem and an empty repo. If you have something similar sitting in a spreadsheet, buried in email threads, or waiting in your notes, tell me about it. I will help you work out the smallest useful version.
Free 20 minutes · New Jersey · Eastern Time · No pitch deck