惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
V
V2EX
MongoDB | Blog
MongoDB | Blog
量子位
J
Java Code Geeks
GbyAI
GbyAI
Attack and Defense Labs
Attack and Defense Labs
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 叶小钗
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Cloudbric
Cloudbric
Recorded Future
Recorded Future
月光博客
月光博客
Help Net Security
Help Net Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
N
Netflix TechBlog - Medium
M
MIT News - Artificial intelligence
N
News and Events Feed by Topic
阮一峰的网络日志
阮一峰的网络日志
The Register - Security
The Register - Security
Scott Helme
Scott Helme
Google DeepMind News
Google DeepMind News
W
WeLiveSecurity
G
Google Developers Blog
T
Troy Hunt's Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
I
InfoQ
S
SegmentFault 最新的问题
G
GRAHAM CLULEY
C
Check Point Blog
Project Zero
Project Zero
有赞技术团队
有赞技术团队
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
P
Privacy International News Feed
AI
AI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
F
Full Disclosure
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More
H
Hacker News: Front Page
S
Secure Thoughts
罗磊的独立博客
T
Threat Research - Cisco Blogs
aimingoo的专栏
aimingoo的专栏
博客园_首页
宝玉的分享
宝玉的分享
C
Cybersecurity and Infrastructure Security Agency CISA

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
What I learned shipping a 5-day auction marketplace in 30 days (Cloudflare Pages + Supabase)
Jason · 2026-04-29 · via DEV Community

What I learned shipping a 5-day auction marketplace in 30 days (Cloudflare Pages + Supabase)

I built an auction marketplace for online businesses called ExitBid — single-region MVP from idea to live in about 30 days. This is a notes-from-the-field write-up of the technical decisions that worked, the ones that didn't, and the patterns I'd use again. Stack is dead-simple: Cloudflare Pages for the static site, Supabase for everything backend, Resend for transactional email. Paid integrations: Creem.io as Merchant of Record, NowPayments for crypto, Vonage for SMS verification.

I'm not going to pretend this is novel. The point of writing it down is the combination of choices and the gotchas that surface when you actually ship.

The architecture in one paragraph

The whole site is static HTML+CSS+JS hosted on Cloudflare Pages, served from the edge globally. The "backend" is entirely Supabase — Postgres for state, Auth for sessions, Storage for assets, Realtime for live bids, RLS for authorization, Edge Functions for the few things that need server-side logic (email OTP send, payment webhooks, crypto invoice creation). No Node server. No container. No K8s. The whole infra runs at $25/mo total — Supabase Pro plan plus Cloudflare Pages free tier.

The bid system

The interesting part. Auctions are 5-day timed runs with a hard close — no soft-close overtime, no extensions in the last minutes (sellers can extend the whole auction up to 3 times for $50 each, but only well before close). 14 concurrent slots on the homepage bento grid, $500 minimum bid increment.

The shape that mattered most: keep the display of current_bid cheap. Every visitor to the site sees the live auction grid and wants up-to-date numbers. Originally I was computing this client-side from the bids table for each card on render. Doesn't scale. Switched to a denormalized current_bid column on the auctions row, kept in sync via an AFTER INSERT trigger on bids:

CREATE OR REPLACE FUNCTION bids_after_insert()
RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
  UPDATE auctions
  SET current_bid = GREATEST(COALESCE(current_bid, 0), NEW.amount),
      bid_count   = COALESCE(bid_count, 0) + 1,
      last_bid_at = GREATEST(COALESCE(last_bid_at, '-infinity'::timestamptz), NEW.created_at)
  WHERE id = NEW.auction_id;
  RETURN NEW;
END $$;

Enter fullscreen mode Exit fullscreen mode

GREATEST matters more than it looks. I had a bug where back-dated test bids would pull last_bid_at backwards in time. Wrapping it in GREATEST makes the trigger monotonic — the timestamp only ever moves forward, regardless of insert order.

RLS as a foundation, not an afterthought

The single biggest leverage in this stack is row-level security on Postgres. Every table has policies. The auth-context comes from the JWT Supabase issues. Critically, this means the frontend can talk directly to Postgres via PostgREST — no API layer in the middle. A bid insert is literally supabase.from('bids').insert({...}). The policy checks the auth.uid() against the bidder_id and validates the bidder is verified. No backend code involved.

Where RLS doesn't fit, I use SECURITY DEFINER RPCs. Verifying an OTP code, atomic counters, anything that needs to bypass narrow column grants — these become functions with explicit grants to authenticated. Example for bidder OTP:

CREATE OR REPLACE FUNCTION verify_bidder_otp(p_code TEXT)
RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER
SET search_path = public AS $$
DECLARE v_uid UUID := auth.uid(); v_row RECORD;
BEGIN
  IF v_uid IS NULL THEN RAISE EXCEPTION 'Not authenticated'; END IF;
  SELECT * INTO v_row FROM bidder_otp_codes
    WHERE user_id = v_uid AND used = false AND expires_at > now()
    ORDER BY created_at DESC LIMIT 1;
  IF NOT FOUND THEN RAISE EXCEPTION 'No active code'; END IF;
  -- ... attempts check, code comparison ...
  UPDATE profiles SET phone_verified = true WHERE id = v_uid;
  RETURN true;
END $$;
GRANT EXECUTE ON FUNCTION verify_bidder_otp(TEXT) TO authenticated;

Enter fullscreen mode Exit fullscreen mode

SECURITY DEFINER runs with the function-owner's permissions (postgres role), so it can write to profiles even though the caller's RLS policies wouldn't allow direct UPDATE. The SELECT/UPDATE are strictly scoped to WHERE id = auth.uid() so a malicious caller can only affect their own profile. This pattern handled 90% of "I need server-side logic" needs without ever opening a Node process.

Realtime: the trap is in the publication

Supabase Realtime broadcasts INSERT/UPDATE/DELETE events from any table in a publication. By default, every public table is in supabase_realtime. This is fine for a small site, but each table in the publication forces logical decoding to walk the WAL for that table on every commit. With 8+ tables in the publication, my disk IO spiked enough that Supabase emailed me a warning.

The fix was 3 lines:

ALTER PUBLICATION supabase_realtime DROP TABLE notifications;
ALTER PUBLICATION supabase_realtime DROP TABLE sponsored_ads;
ALTER PUBLICATION supabase_realtime DROP TABLE support_messages;

Enter fullscreen mode Exit fullscreen mode

Audit which tables your frontend actually subscribes to, and drop the rest. In my case the UI subscribes to bids, messages, and questions — three tables, not eight.

The verification cost trap

I shipped with phone+email "verification" originally as a self-attestation: user types a phone number, RPC stores it, profile gets phone_verified=true. No SMS sent. This was fine as a friction-gate for spam but obviously not real verification.

Adding real SMS turned out to be the most painful integration. Twilio, MessageBird, Vonage, TextBelt, Brevo, Plivo — every provider has a different signup flow, different minimum top-up, different sender-ID policies, different country coverage. Most card-rejected my Eastern European Visa. The one that finally worked: Vonage on a free €2 trial credit, then refilling from a different card through their "buy credits" page (different processor than signup).

Lesson for next time: don't roll SMS verification yourself. Use Supabase's built-in auth.signInWithOtp({ phone }) + Twilio Verify config, set up at the project level. The integration is trivial; the painful part is just acquiring an SMS provider account, and that's a one-time cost.

What I'd build differently

Things that worked and I'd do again:

  • Cloudflare Pages + Supabase + Resend trio. $25/mo total. Edge-served HTML with dynamic data over a single Postgres connection.
  • Trigger-based denormalized counters (current_bid, bid_count, last_bid_at) instead of computing on read.
  • SECURITY DEFINER RPCs for every "this needs server-side logic" moment. Beats spinning up a backend.
  • pg_cron for periodic jobs (auction expiry, OTP cleanup) instead of external cron runners.

Things I'd skip:

  • mDNS / Bonjour for local agent discovery — Windows multicast is unreliable and the warnings filled my logs for weeks before I disabled it.
  • Self-rolled cron through external runners — every external scheduler I tried (OpenClaw cron, GitHub Actions, Cloudflare Cron Triggers) had subtle failure modes. pg_cron inside Postgres is the simplest reliable option when your job is a SQL call.
  • Heavy bidder-fee tracking. I started with deposits ($100 refundable), then switched to phone+email-only verification. The deposit added 4 layers of code (escrow, refund flow, ledger reconciliation) and zero buyer behavior change vs simple verification.

The brand-name lesson

The non-technical thing that surprised me: search engines have hard time disambiguating new brands. "ExitBid" looks visually similar to EZBID (US industrial-equipment auction), abetter.bid, and a couple of unrelated Instagram handles. For the first six weeks, Google in the CIS region ranked an unrelated Instagram account higher than my actual site for the literal query "exitbid". I wrote about that in a separate post — short version: when you're picking a brand, search the visual variations of the name, not just the exact spelling.

Final stack tally

Hosting:          Cloudflare Pages (free tier)
Database / Auth:  Supabase Pro ($25/mo)
Email:            Resend (free tier, 100 emails/day)
Payments:         Creem.io (Merchant of Record), NowPayments (crypto)
SMS verification: Vonage (€2 trial, then pay-as-you-go)
DNS / CDN:        Cloudflare
Languages:        English + Russian (hreflang annotated sitemap)

Enter fullscreen mode Exit fullscreen mode

Live at exitbid.io if you want to see what it looks like. Auctions are running, bidding is free after one-time verification.

Alex Web, founder of ExitBid