DOLLA$
Building in public

The Road To Stacking Dollas$

full-stack · full pockets · full mission

Every shippable milestone on the path from thesis to a live, on-chain, $1-a-month creator economy. No vaporware — every entry links to a real receipt when there is one. Every follower is a dollar. Every dollar reaches the creator. 51% goes back to the world.

May Milestone Update

First week of May · 4 ships in 7 days

The first week of post-launch operations. The public surface expanded from a product into a program — Hall Pass opened a real front door for affiliate creators, the cap-reached waitlist made our growth discipline visible, and the share-preview infrastructure that broke silently is back so every DOLLA link unfurls as itself again.

  • May 3 · Cap-reached waitlist banner — split text/CTA pattern with server-rendered initial state, zero pop-in; signup_waitlist table + idempotent join API
  • May 2 · Hall Pass program — admin-granted comp tier access with daily expiry cron, full grant + revoke + history surface, gracious revert to free at expiry
  • May 2 · Hall Pass public waitlist — /hallpass front door with 9-field application, admin triage queue, one-click convert into actual grants
  • May 1 · Share-preview restored — root-caused two interlocking bugs (Next metadata replace + stale favicon.ico); brand hero unfurls again on every share
Cadence: ~1 milestone every 1.75 days·Posture: post-launch, cap-gated growth

Build velocity

13days
Repo to live MVP
Apr 14 → Apr 27
203commits
Production renditions
14 / active day avg
3rails
Payment flows live
Follows · Tiers · Donations
19migrations
Schema iterations
All applied to prod
43K+LOC
TypeScript shipped
Web + mobile + packages
2platforms
Web + iOS scaffolded
Same backend, both clients

Mission economics

0%
Creator fees, forever
100% reaches the creator
51%
Premium revenue pledged
Donated on-chain
~9%
Gas burn vs DOLLA cut
Sustainable at every scale

Under 14 days · Over 200 renditions · A full operating MVP of DOLLA

Latest milestone

2026-05-03

Cap-reached waitlist banner — split scroll/sticky pattern + zero pop-in

SHIPPED

When the real-user cap is enforced and the platform is at capacity, every visitor lands on a subtle, encouraging banner instead of a wall. Shipped a v1 single sticky strip in the morning, then iterated through ten polish passes during the day to land on a split text/CTA pattern with server-rendered initial state — the descriptive copy scrolls away with the page, the 'Join the waitlist →' button stays glued just below the relevant header, and there's zero pop-in flash on first paint.

  • Migration 00043 creates signup_waitlist with email + optional instagram_handle + tiktok_handle + source page + IP / user-agent for triage signals + a notified_at column for when we eventually invite people off the list. Unique index on lower(email) makes the insert idempotent — re-submissions return ok with alreadyOnList=true so the UX never throws on a duplicate. RLS locked all the way down (service role only)
  • POST /api/signup-waitlist/join — public, no auth. Email-only is the minimum field; IG and TT handles strip a leading '@' and trim before storage so users can paste either '@evan' or 'evan' without thinking about it
  • Final layout — SPLIT pattern on both /home and /discover: a descriptive text strip ('We're full for now. Our beta creators are stress-testing every feature so DOLLA launches to the world the best version of itself') sits in normal document flow at the top of the page and scrolls away with the hero / posts; a CTA strip with the 'Join the waitlist →' button is position:sticky and stays glued just below the relevant header (top:48 below TopNav on home, top:88 below FeedTabs on feed)
  • Server-rendered initial state — the /home page (now async) calls getUserCapStatus() during render and passes initialAtCapacity as a prop to the client banner. The banner uses that as its initial useState value and skips the client probe entirely, killing the ~150-300ms fetch-on-mount pop-in flash. Page revalidates every 30s so admin cap toggles propagate quickly without forcing the SEO-critical home page to be fully dynamic on every request
  • Per-placement sticky math on feed: CTA strip carries the same transform: translateY(var(--topnav-translate, 0px)) FeedTabs uses, so when the TopNav hides on scroll-down on /discover the CTA strip slides up with FeedTabs in lockstep — no transient gap above the banner during the slide. z:25 keeps the banner under FeedTabs' z:30 so any 1–4px overlap is hidden by FeedTabs rendering on top
  • Mobile button parity — both placements use flex-col on mobile / sm:flex-row on desktop in the CTA wrapper, so the 'Join the waitlist →' button stretches to full width on phones (more tappable) and sits naturally right-aligned on desktop. Click expands the button area inline into email + optional IG/TT handles + Submit, no modal
  • Held the line on scope per the new Karpathy CLAUDE.md guidelines installed earlier this week: no admin triage queue (Supabase Studio handles v1 review), no email notification wiring, no admin dashboard page — those land only if usage data shows demand. Final implementation is ~330 LOC total across one migration + lib helper + one POST endpoint + the banner component + two page wires

Everything before it

2026-05-02

Hall Pass waitlist — public front door for the affiliate-creator program

SHIPPED

A real, beautiful, brand-aligned waitlist page at /hallpass with a 9-field application form, plus the admin triage queue that turns approved applications into actual Hall Pass grants in one click. The program now has a public mouth, not just a backstage door.

  • Migration 00042 creates hall_pass_applications with full applicant payload — name, email, primary platform + per-platform handles in JSONB, audience size band, niche (mapped to the 16 diversity verticals from CLAUDE.md), requested tier + months, two long-form fields (why DOLLA + how-they-bridge-their-audience), referral source, status enum (pending → contacted → approved/rejected → converted), reviewer email + notes + reviewed_at, granted_pass_id link to the resulting Hall Pass row, and IP + user-agent for triage signals
  • Partial unique index on (lower(email)) where status='pending' enforces 'one pending application per email' at the database level — translates to a friendly client-side message instead of a duplicate-row insert error, and reopens automatically once the prior application is resolved
  • RLS locked all the way down — no client policies — so reads happen exclusively through the service role from /api/admin/hall-pass-applications. The public submit endpoint is the only write path that hits this table from outside the admin
  • Public page at /hallpass: brand-DNA hero with purple-themed orbs, four 'what you get' perk cards across complementary tier / growth coaching / ad-creative guidance / featured discovery, an explicit 'what we ask in return' covenant block that lifts the four disciplines from the Creator Playbook, and the form itself in a purple-gradient card
  • HallPassApplyForm: client island with section-grouped fields, per-platform handles as a 5-input cluster, native HTML5 validation backed by server-side validation, an explicit values-aligned agreement checkbox (gates submit), and a celebratory inline success state on 201 — no full-page redirect since the whole flow is one page
  • POST /api/hall-pass/apply: public, no auth. Captures submitted_ip + user_agent for triage signals, auto-links to a DOLLA account if one already exists for the email, and surfaces the database's duplicate-pending block as a human message instead of a 500
  • Admin queue at /admin-dashboard/hall-pass-applications: status-tabbed list (pending / contacted / approved / converted / rejected / all), per-application card showing identity + platform + audience + niche + ask, full-application <details> drawer for the long-form fields + review notes + linked granted-pass id, and an inline ApplicationActions client island with notes textarea + four buttons (mark contacted, approve, reject, convert)
  • Convert flow: PATCH for status changes, POST to convert. Convert calls grantHallPass under the hood — same code path as the per-user admin card — so the resulting pass is identical in every way to a directly-granted one. The application row's granted_pass_id then deep-links to the user's drill-in page so the audit trail closes the loop
  • Convert is gated client-side AND server-side: if the applicant has no linked DOLLA account, the button is disabled with a tooltip explaining 'ask them to sign up first' (since there's no users.id to bump). user_id is re-resolved at conversion time in case they signed up between submission and approval
  • Admin layout sidenav now carries 'Hall Pass apps' with a checkmark glyph alongside Marketing, so the queue is one click away from any admin surface — and the URLs sitemap lists all six new routes so a new collaborator finds them immediately
2026-05-02

Hall Pass program — admin-granted comp tier access for affiliate creators

SHIPPED

A formal, time-bounded, fully-audited comp tier program built into the admin surface. We gift selected affiliate creators top-tier access for a fixed window (typically 3 months) while we coach them on bridging their existing audience into a DOLLA community — social-media playbook, ad guidance, post cadence — and the platform handles the lifecycle automatically. The user sees a dignified 'thank-you' banner on their tier-settings page; the admin sees a full grant + revoke + history surface; the daily cron handles the gracious revert to free at expiry without a single human touch.

  • Migration 00041 creates the hall_passes table with tier (verified | sovereign), duration_months (1–24), required reason, granted_at + expires_at, granted_by_email, optional revoked_at + revoked_reason, an expiry_processed flag for idempotent cron processing, and prior_premium_tier + prior_creator_tier snapshots so expiry restores exactly the tier the user had BEFORE the gift instead of collapsing every comp to free
  • RLS policy hall_passes_user_select_own gates reads to the row's own user — the banner on /settings/tiers can render via the browser Supabase client without exposing other users' grants, and writes go through the service role from admin routes only
  • Layered on top of the existing admin_tier_override mechanism: granting a pass bumps users.premium_tier and/or creator_tier to the gifted tier, flips admin_tier_override = true with a 'Hall Pass · {reason}' note so verify-tier's self-heal won't claw it back, and snapshots the prior values onto the pass row for clean restoration on expiry or revocation
  • Admin grant + revoke API at /api/admin/users/[id]/hall-pass (POST grant + GET history) and /api/admin/users/[id]/hall-pass/[passId] (DELETE revoke). Grant blocks if an active pass already exists (the partial unique index hall_passes_one_active_per_user_idx enforces this at the database level too — defence in depth)
  • Admin UI: a new Hall Pass card on /admin-dashboard/users/[id] — purple-accented to distinguish it from the cyan tier-override card, shows the active pass with days-remaining + reason + revoke button, exposes a grant form with tier selector, duration dropdown (1 / 2 / 3 / 6 / 12 months), checkboxes for premium-side vs creator-side application (default both), and a free-text reason input that defaults to 'Affiliate creator — onboarding & growth coaching' but is fully editable
  • User UI: a HallPassBanner component renders at the top of /settings/tiers when an active pass exists for the signed-in user. Format reads '[Tier] [N]-Month Hall Pass · Expiring in [days] days' with a ticket-icon glyph in a tier-tinted card (Verified = green, Sovereign = purple), warm one-liner about the gift, and a small monospace expiry date
  • Daily expiry cron at /api/cron/expire-hall-passes (scheduled 04:30 UTC, 30 minutes after the stories cron so the two never collide) walks every pass past expires_at, restores the user's snapshotted prior_premium_tier and prior_creator_tier, clears admin_tier_override + its by/at/note fields, marks expiry_processed = true so reruns are idempotent, and returns a per-pass error array so any one failure can't block the rest of the sweep
  • Failure handling: revoking and expiring share the same restoration path — both put the user back under payment-governed verify-tier so their tier ends up wherever their actual paying state warrants (free if no sub, verified if they were subscribing on the side, etc.) without any hand-tuning
  • Pass history is preserved indefinitely on the row even after expiry or revocation — the admin card surfaces the last 20 grants per user with status (active / revoked / expired) so the affiliate program's track record is auditable per creator
  • Same-day hardening on the admin user drill-in: HallPassCard's type imports were inlined locally so a 'use client' component can never accidentally pull lib/hall-pass.ts (which references getSupabaseAdmin) into the browser bundle, and the page-level Promise.all gained .catch() guards so a brand-new feature can never break the entire admin user-detail render
  • Surfaced and fixed an unrelated latent bug while debugging a 404 on the user-detail page: the loadUser SELECT referenced four follow_limit_override* columns whose migration (00024) had silently never been applied to prod — applied it, plus patched loadUser to console.error any future SELECT failures so the next variant of this bug shows up in Vercel logs instead of as a silent notFound()
2026-05-01

Share-preview infrastructure restored — every social share renders the brand hero again

SHIPPED

Hours-long forensic on why iMessage and other share crawlers were rendering DOLLA links with the bare $ favicon glyph instead of the full hero card. Two interlocking bugs (one in metadata, one in the icon set) had both regressed silently; both fixed and verified live in production HTML.

  • Root cause #1 — Next.js metadata merge replaces, not merges. The site's /home, /coinbase-application, and many other SEO-oriented routes set their own openGraph block to override the layout's per-page title and url; per Next.js's merge rules, that REPLACES the parent's openGraph entirely and silently drops the file-convention auto-injection from app/opengraph-image.tsx. Result: production HTML shipped with no og:image and no twitter:image meta tags at all, so iMessage/Facebook/LinkedIn/Slack all fell back to the favicon
  • Fix: explicitly list /opengraph-image (and the twitter equivalent) in openGraph.images and twitter.images on the three highest-traffic routes — root layout, /home, /coinbase-application — so metadataBase resolves the relative path to https://justadolla.com/opengraph-image for every share crawler. Verified live: og:image, og:image:width, og:image:height, og:image:alt, twitter:image all now present in production HTML on both routes
  • Root cause #2 — favicon.ico was 4 days older than the rest of the icon set. apps/web/app/favicon.ico last changed 2026-04-25 while icon.svg + apple-icon.tsx + manifest.ts all moved to the current cyan-$ design on 2026-04-29. Mobile Chrome's URL-bar favicon comes from favicon.ico, so the address bar showed the old icon while bookmark tiles and the predictive dropdown (which read from icon.svg / apple-icon / manifest icons) showed the new one
  • Fix: scripts/regenerate-favicon.mjs renders the canonical icon.svg at 16/32/48 px via sharp and packs them into a multi-size ICO with PNG-encoded payloads (modern browsers + mobile Chrome accept this format). Idempotent — re-run any time icon.svg changes via `node scripts/regenerate-favicon.mjs`. Added sharp as a workspace devDependency since pnpm doesn't hoist it from its transitive position under Next.js
  • 21 other routes (calculators, blog, /breakdown, /minted, /alternatives, /for, /cities, /causes, /topics, /about, /creator-playbook, /business, /insights, /wallet-funding, the campaign route) still set openGraph without images and inherit the same trap — queued for a follow-up sweep behind a small lib/seo helper that returns the standard image objects so each page edit is a one-liner
2026-04-30

Coinbase production-readiness brief — public surface for partner-team review

SHIPPED

Shipped a dedicated, brand-aligned reassessment brief for the Coinbase Onramp partner team in support of DOLLA's path to standard production access. One link, ten sections, every architectural and compliance argument we'd otherwise be sending as attachments — all rendered with the rest of the site's design language so the case lands the same way the product does.

  • Single-page brief at /coinbase-application: executive summary + ask, product overview, the precise Onramp user-journey we run (six steps from intent to settlement), nine-point policy compliance walkthrough, the non-custodial architecture explained at the contract level, content policy + trust & safety enforcement, corporate structure under Kingdom Portfolios LLC and Kingdom Seed Foundation, eight live risk controls, three concrete asks for the meeting, and an appendix with every Base mainnet contract address we touch
  • Sticky desktop table of contents with 10 anchors so a reviewer can navigate the document the same way they'd navigate a spec — and so the founder can drive the conversation live without scrolling
  • Three asks framed for the meeting: (1) reopen the application with stated reasoning if a re-decline happens, (2) written confirmation of the approved use-case scope at production, (3) a named technical contact across Onramp / OnchainKit / CDP so future ecosystem questions land in the right inbox
  • noindex via metadata — link-shareable for the partner concierge but not surfaced in search; OG card metadata set so the link unfurls cleanly in iMessage, Slack, and email previews
  • Source-of-truth markup also lives at docs/coinbase-onramp-reassessment.html as a printable single-file leave-behind with a print stylesheet that drops the dark theme for clean PDF export
  • Brand consistency: same ambient orbs, font-mono uppercase tracking-widest pills, font-serif italic accent, and dark glass aesthetic as /breakdown — the case for production reads as the same product the case is about
2026-04-30

Stories — IG/TikTok parity pass, on-device verification + the full creator interaction model

SHIPPED

Overnight rebuild of the story surfaces from a working but rough prototype into something that reads, gestures, and renders the way Instagram and TikTok have trained users to expect. Every piece — tray, creator route, viewer, gestures, audio, accessibility — touched and improved against direct on-device feedback from the founder testing each ship in real time.

  • Migrations 00035-00038 applied to prod via the patched db-push script (the script's regex broke when the Supabase Custom Domain swap put 'auth' where the project ref used to be — now resolves the ref from the pooler URL's username so it survives any client-domain rewrite). Stories table, live_streams table, 80 seed stories across diversity verticals + a demo replay, and the public Storage bucket are all live
  • Camera promoted from a fixed-overlay modal to a real route at /story/new with a slide-in-from-left animation — iOS Safari's edge-back swipe and the browser back button both dismiss it the same way they dismiss any other page; the URL is shareable and refreshable
  • Live-camera capture parked after iOS WebKit autoplay rules made the inline viewfinder unreliable across devices (black-screen-after-permission-grant on iOS Chrome despite multi-event play retry, defensive track.enabled, and a tap-to-start fallback). Replaced with the iOS native photo/video picker — same path Instagram uses on mobile web — which works identically on every iOS browser. Camera component preserved in-tree for the eventual native-shell or service-worker workaround
  • Viewer fully ported to a document.body portal so the tray's backdrop-blur ancestor (which per CSS spec creates a containing block for fixed-positioned descendants) can never trap the player at sub-viewport scope. Z-index maxed at 2147483647, body scroll locked while open, entry animation a 200ms fade + scale-up
  • Gesture handler unified onto pointer events with proper hold-vs-tap-vs-swipe disambiguation: 200ms decides hold-to-pause vs tap-to-advance, axis-locked on first move >10px decides horizontal swipe (jumps to next/prev creator group, IG-style) vs vertical swipe (close at >80px). Tap left third = previous, right two-thirds = next
  • Audio: video stories start muted (autoplay-friendly across iOS WebKit and Chromium), with a top-right mute toggle button. Preference persists via sessionStorage so the viewer doesn't have to re-unmute every story. TikTok-style 'Tap to unmute' coachmark fades in for 3s on the first muted clip in each session
  • Visual feedback: 200ms dim + chrome opacity drop while held (pause is now legible without hiding the media), centered spinner during video buffering driven by waiting/canplay events, captions render multi-line with text-shadow for legibility on bright media
  • Creator strip on the player: avatar + name wrapped in next/link to /[username] when the API supplies a username — tap jumps to the creator profile and auto-closes the player. The /api/stories endpoint now returns username + displayName + avatarUrl per group inline, eliminating the 80-parallel-fetch N+1 pattern the tray was doing
  • Triple-dot menu: top-right ellipsis opens a small popover with Copy link (writes /story/<id> to clipboard) and Report story (POSTs the new /api/reports route which lands rows in the existing reports table for admin triage; rate-limited like comments, dedupes on (reporter, target, status='pending'))
  • Reply input bar at the bottom — IG's 'Reply to <name>…' pattern, focusing the input pauses the current story so the user isn't fighting a 5s timer. Three quick-reaction emojis sit alongside as one-tap shortcuts. Send currently fires a heart reaction + toast pending a real DM backend; UI is in place so the moment we wire DMs the path is one route call away
  • Per-viewer seen state persisted server-side via the story_views table (was previously local-session only), surfaced through /api/stories so a watched ring stays grey across visits / devices. Tray sort order matches IG: caller's own first → unwatched groups → watched groups
  • Pause-on-visibility-change so a backgrounded tab doesn't burn through stories the user can't see. Pre-loads the next story's image AND the next group's first image so cuts feel instant on cellular. Safe-area insets respected on top progress bars + bottom reply input + reaction bar
  • /api/cron/expire-stories tightened to a once-daily 4am UTC schedule (Vercel Hobby plan limit) — story media stays alive 24h + 24h grace before hard-delete, well inside the cron window so the worst-case stale-row never exceeds the grace period
2026-04-29

Stories + creator live streaming — the two video surfaces every social product needs, shipped end-to-end

SHIPPED

Ephemeral 24-hour stories with a 3-minute video cap and full IG/Snap interaction model, plus creator broadcasts with HLS playback, real-time chat, and concurrent viewer presence. Both surfaces use the same tier-gated architecture as posts (free / $1-mo / $1-wk), populate isLive across every feed query, and ship behind a provider-agnostic streaming adapter so Cloudflare Stream Live can plug in tomorrow without code changes.

  • Stories backend (migration 00035): stories + story_views + story_reactions tables with RLS, partial indexes for active-by-creator, and CASCADE cleanup. 3-min video cap enforced at API + DB. /api/stories POST creates with 24h TTL, GET returns groups visible to the caller (free + creators they follow at the right tier), [id]/view UPSERTs first-frame views, [id]/react toggles emoji from the fixed vocabulary (heart / fire / pray / clap / wow / laugh)
  • Stories UI: StoryRing (conic-gradient ring that greys when seen) + StoryPlayer (full-screen, tap-zone advance, hold-pause, swipe-down close, keyboard arrows, reaction bar) + StoryCreator (probes video duration client-side, uploads to Supabase Storage, then POSTs) + StoriesTray (caller-first +bubble, then active groups). Wired into /following and /discover above the feed
  • Stories cleanup: /api/cron/expire-stories runs every 6 hours, hard-deletes stories past a 24h grace window plus their underlying Supabase Storage objects. CASCADE handles views + reactions. Wired into vercel.json crons with bearer auth
  • Live streaming backend (migration 00036): live_streams + stream_chat_messages + stream_viewers tables. Provider-agnostic — DOLLA owns the state (status, peak, replay), the provider only pipes video. Currently-live partial index keeps the LIVE indicator query cheap. /api/streams POST provisions a stream + persists ingest secrets, GET lists live streams visible to caller, [id] PATCH ends the stream and pulls the replay URL, [id]/chat does Realtime delivery + durable persistence, [id]/viewers heartbeats for last-seen totals
  • Streaming provider adapter: lib/streaming/provider.ts exposes a 3-method interface (createStream, endStream, getReplay) with two implementations — cloudflare.ts for Cloudflare Stream Live ($1/1k min delivered, recordings included) and mock.ts that returns Apple's HLS test stream so dev/preview never depends on cloud creds. Falls back to mock automatically when CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_STREAM_API_TOKEN are missing — production wires real keys in Vercel envs and the adapter takes over with zero code change
  • Live streaming UI: GoLiveButton replaces the dead 'Live' indicator in the creator dashboard header — opens a preflight modal that respects creator_tier (monthly/weekly options locked for free creators), provisions the stream, and routes to the broadcaster page. BroadcasterPanel shows RTMP URL + stream key with copy-to-clipboard and OBS instructions. /dashboard/live/[id] is the broadcaster view (panel + chat side-by-side); /live/stream/[id] is the public viewer with HLS playback; /live/[username] is the share-friendly URL that resolves to the active stream or falls back to the most recent replay
  • HLS playback: Safari/iOS plays natively via <video>; everywhere else dynamic-imports hls.js — Safari users don't pay the bundle cost. Live latency tuned with liveSyncDurationCount=3, maxBufferLength=30. LiveChat hydrates last 100 messages then subscribes to a Supabase Realtime broadcast channel keyed by stream_id (instant delivery) AND posts to the durable API (so latecomers can replay the conversation). LiveViewerPresence uses presence channels for free real-time concurrent count plus 30s heartbeats to /viewers for total-watch-time totals
  • isLive wiring: getLiveCreatorIds() helper in lib/queries.ts joins live_streams.status='live' once per request and populates isLive across all 5 callsites — getCreators, search, getCreatorByUsername, the feed assembler, and trending. Following page now links live creators to /live/[username] instead of profile so a click on a red-ringed avatar opens the stream
  • Demo seed (migration 00037): one captioned free-tier story per seed creator with content-aware Unsplash images by niche (fitness, garden, faith, art, cooking, code, music, charity, environment), plus an ended stream with a replay URL on the top seed creator. Demo mode and screenshot reels look populated from minute one
  • Smoke coverage: e2e/stories-and-live.spec.ts confirms the public surfaces don't 500, /api/stories + /api/streams shapes are correct, unauthenticated POSTs return 401, /live routes survive unknown ids without crashing. Typecheck clean across the entire web app
  • Tomorrow's wire-up: apply migrations 00035-00037, create the public 'stories' Supabase Storage bucket, add Cloudflare Stream Live keys to Vercel envs (STREAMING_PROVIDER=cloudflare + CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_STREAM_API_TOKEN), then OBS → RTMP → /live/<username> for end-to-end verification
2026-04-29

The Creator Playbook — culture document that names what creator success looks like and why

SHIPPED

A public manifesto + practical guide that plants the disciplines, mechanics, and cultural pillars new creators need on day one. Reframes the creator-growth question from 'what algorithm should we build?' to 'what culture do we plant?' — because the disciplines that make creators succeed are upstream of any algorithm.

  • Six numbered sections at /creator-playbook: The Five Disciplines (Show up / Show your work / Show up FOR people / Show what you're for / Show the path), The Mechanics That Reward This (all 5 achievement tracks + Founding Creator badge), The Way We Operate (4 cultural pillars), The Quarterly Rhythm (Week 1 / Month 1 / Quarter 1 floor expectations), Sprints + Competitions (Minting Season / Streak Rewards / Charitable Match / Founding Creator Cohort), and What Doesn't Work Here (explicit anti-patterns)
  • Each discipline is tied to a specific Achievement Track (Influence, Impact, Retention, Philanthropy, Mint) so creators can see exactly which behavior unlocks which platform-visible reward
  • Founding Creator badge publicly named — the first ~25 hand-recruited creators across diversity verticals get a permanent profile mark and a private founders' channel. Creates urgency for the cohort phase without false scarcity
  • Cross-platform funnel guidance baked in: 'DOLLA isn't where they discover you; it's where they commit to you.' Every creator's TikTok / IG / YouTube becomes a top-of-funnel for $1/mo follows
  • Anti-patterns section publicly states what doesn't work: engagement-bait, reposting, transactional upgrade pressure, hiding the mission, treating $1 as nothing. Builds trust by being honest AND filters culture-mismatched creators before signup
  • Funnel attribution: pixels fire on view + section-depth via Clarity tags + CTA clicks. Conversion path /home → /creator-playbook → /signup?creator=true is end-to-end measured
2026-04-29

/wallet-funding — three-path Coinbase Smart Wallet onboarding for any visitor at any starting point

SHIPPED

Wallet friction is the #1 conversion killer for crypto-native creator platforms. New page meets visitors wherever they are: brand new to crypto, has Coinbase but USDC isn't on Base, or already fully set up. Each path is honest about time required and routes to the right next step.

  • Path A (~15 min): brand new to crypto. Open keys.coinbase.com, create Smart Wallet with passkey (no seed phrase), fund via Coinbase Onramp (Apple Pay / debit / bank), return to DOLLA. No exchange account required
  • Path B (~5–10 min): already on Coinbase exchange. Create Smart Wallet, send USDC over Base network (NOT Ethereum — saves $5–$30 per transfer in gas)
  • Path C (~30 sec): already on Smart Wallet with USDC on Base. Single CTA to /signup where wallet sign-in lives
  • Funnel attribution: pixel + GA4 + TikTok + Pinterest + Clarity events fire on initial view AND on segment selection (with chosen segment as content_id). Tells us which audience type is highest-intent and where to invest more polish
  • Linked from /home and /signup at the moments first-time visitors hit wallet-related friction. /home now offers two helper paths under the primary CTAs — 'New to crypto?' (consumer route) and 'Thinking of creating?' (creator route, links to the Playbook)
  • SEO-targeted at long-tail crypto-onboarding queries — 'fund Coinbase Smart Wallet on Base', 'USDC on Base Onramp', 'Coinbase Smart Wallet setup'
2026-04-29

Server-side error_log table live — production failures now persist instead of disappearing into console.error

SHIPPED

Migration 00031 applied to prod. Background-job failures (cron charge errors, paymaster issues, route-handler exceptions) now land in a queryable table with stack, surface, and structured context — replacing the previous 'silent log to console + warning' fallback.

  • First-party error tracking without taking on Sentry's pricing or SDK weight at this stage. Server handlers call logServerError(error, context) which writes one row per uncaught error
  • Captures: occurred_at, message, stack, error_name, surface (route path / 'cron' / 'background'), structured JSONB context, fingerprint (for trend analysis), resolved flag
  • Indexed for the queries the admin dashboard will need: occurred_at DESC, fingerprint, surface
  • Replaceable: when Sentry / Highlight / Datadog get funded, swap the implementation in lib/error-log.ts and keep this table as a redundant write OR drop it. The logServerError() signature is the durable contract
2026-04-29

Measurement + retargeting infrastructure — five ad platforms, one analytics provider, two server-side conversion APIs, all live

SHIPPED

DOLLA now has the full distribution-measurement stack a serious creator economy needs: every visitor builds remarketing pools on five ad platforms in parallel, every session is replayable for product debugging, and two server-side conversion APIs are ready for when paid acquisition begins.

  • Five client-side retargeting pixels live and collecting broad audiences in parallel: Meta Pixel, Google Ads tag, TikTok Pixel, Pinterest Tag, plus Google Analytics 4 for traffic attribution. Each fires on first paint AND on every SPA route change so single-page navigation is counted accurately
  • Microsoft Clarity wired for free unlimited session recordings + click heatmaps + scroll-depth + rage-click detection — turns 'why is this page bouncing' from a guess into a watchable replay
  • Two server-side Conversions APIs provisioned with access tokens stored in Vercel: TikTok Events API and Pinterest Conversions API. These bypass iOS 14.5+ tracking restrictions and ad blockers when conversion optimization gets switched on later
  • Strict no-Tag-Manager policy: every pixel installed inline directly in <head> for fastest first-paint, no GTM round-trip latency, no risk of duplicate firing from container misconfiguration
  • Typed wrapper modules (lib/pixel.ts, lib/google.ts, lib/tiktok.ts, lib/pinterest.ts, lib/clarity.ts) make per-platform conversion event wiring a one-line call site change when the time comes
  • Broad-retargeting-only strategy by design: no specific Conversion Actions or audiences defined yet. The infrastructure passively builds 'all visitors' pools across five platforms while the product matures — paid campaigns can attach to warm audiences on day one with zero cold-start period
2026-04-29

Engagement persistence restored — likes, reshares, and saves are landing in the database again

SHIPPED

A schema mismatch on the analytics table had been silently throwing on every interaction since 2026-04-15, cancelling the like/save/reshare writes that ran after it. Found via direct prod-DB audit, fixed via migration plus route hardening.

  • Diagnosed by querying the prod tables directly — likes / reshares / saves had ZERO new inserts in the prior 14 days while comments kept landing through their separate route, isolating the bug to the shared interactions endpoint
  • Root cause: feed_interactions table held the original boolean-rollup schema (did_like, did_reshare, watch_time_seconds) but the route writes the event-stream schema (interaction_type, feed_type, position, duration_seconds). Every insert errored on missing columns and threw, killing the engagement upserts that should have run next
  • Migration 00034 drops + recreates feed_interactions with the event-stream schema, plus drops + recreates the dependent post_engagement_stats materialized view (now keyed on duration_seconds for the avg-watch-time calc)
  • Route hardened: the feed_interactions insert no longer throws on failure — analytics is best-effort, engagement is critical path. Future schema drift on the analytics table can never silently wipe out user-facing writes again
  • Comments unaffected throughout — they go through /api/comments, which never touched feed_interactions
2026-04-29

Supabase Custom Domain cutover — auth.justadolla.com replaces the project-ref hostname

SHIPPED

The Google OAuth consent screen, magic-link emails, and storage URLs now read 'auth.justadolla.com' instead of the random Supabase project ref. Brand consistency across every authenticated touchpoint.

  • NEXT_PUBLIC_SUPABASE_URL clients (web + mobile) point at auth.justadolla.com — same Supabase project, branded host
  • next.config.ts images allowlist updated so storage-served avatars + post media on the new hostname flow through the optimizer
  • Mobile globals: 16px font-size on inputs/textareas at ≤768px suppresses iOS Safari's focus auto-zoom; touch-action: manipulation on interactive elements kills the double-tap-zoom drift
  • App icon reverted to the master DOLLA$ logo (Apple touch icon untouched)
  • RUNBOOK updated to note the Custom Domain auth host alongside the direct DB host so future deploys don't accidentally fall back to the project-ref URL
🥩 Steak-dinner moment
2026-04-27

Auth-aware root — DOLLA stops being a landing page, starts behaving like a product

★ MILESTONE

Signed-in users now land on /discover (the feed). Signed-out visitors get the marketing pitch at /home. The single root URL finally tells two different stories — the way every real social product works.

  • / became a server-rendered splitter that reads Supabase session cookies and redirects in <50ms — no flash, no client-side flicker, no double paint
  • Signed-in cohort: every return visit drops them straight into the feed. The platform feels owned, not visited. This is the IG/Twitter/TikTok pattern that retention is built on
  • Signed-out cohort: still gets the full marketing pitch, conversion CTAs, OG share image, Pixel events — moved verbatim to /home so nothing was lost
  • TopNav logo points to /home for everyone, so signed-in users can always click back to the pitch (useful for sharing the story to friends mid-session)
  • Structural shift, not cosmetic: this is the moment the product graduates from 'demo with a homepage' to 'real social app where the URL bar means something'. Every growth metric — DAU/MAU, session length, return rate — gets its honest baseline starting today
2026-04-27

Subscription owner sharded — 10 CDP wallets, 10x nonce parallelism

SHIPPED

Single Smart Account → 10 deterministic shards. Capacity ceiling jumps from ~100k paying subs to ~1M.

  • Provisioned 10 CDP Smart Wallets (dolla-subscription-owner-shard-00 through -09) on Base mainnet
  • Each new subscriber is hashed (FNV-1a on user_id) → assigned to a deterministic shard. User signs the spend permission against THAT shard's address
  • wallet_name persisted on subscriptions + snapshotted on the charge job so the worker calls charge() against the matching wallet later
  • Verified the project-wide Coinbase Paymaster auto-sponsors all 10 — no manual allowlist needed
  • Existing legacy subs (wallet_name NULL) keep working unchanged via resolveWalletNameForCharge fallback
  • Migration 00019: subscriptions.wallet_name + charge-job snapshot. Idempotent provisioning script: provision-subscription-owner-shards.mjs --count=N
2026-04-27

Cron queued — Postgres SKIP LOCKED + parallel workers

SHIPPED

Serial walker → fan-out queue. Throughput jumps from ~10 charges/sec to ~250/sec. Cron timing out is no longer existential.

  • Migration 00018: subscription_charge_jobs table with UNIQUE(subscription_id, period_end) for idempotent re-enqueue
  • claim_charge_jobs(batch_size) RPC using FOR UPDATE SKIP LOCKED — multiple workers each claim a disjoint batch atomically
  • /api/cron/charge-subscriptions (daily) ENQUEUES jobs only, then fires 5 parallel HTTP workers
  • /api/cron/charge-worker (new) — claims a batch via SKIP LOCKED, processes with Promise.allSettled, exits cleanly
  • lib/charge-job.ts: shared processChargeJob handler. Failed charges back off exponentially (1h → 6h → 24h) up to 3 attempts. Past grace deadline → mark sub expired (preserves old behavior)
  • Trivially swappable to Inngest / Vercel Queues later — no schema changes needed
2026-04-26

Coinbase Smart Wallet sign-in (SIWE) replaces Apple OAuth

SHIPPED

One-tap signup that creates the account AND links the wallet. No password, no email.

  • Apple removed everywhere — provider was returning 400 on signup and we don't depend on it for any flow
  • New /api/auth/wallet-signin/nonce + /api/auth/wallet-signin routes — server-issued nonce, viem.verifyMessage handles ERC-1271 + EIP-6492 (counterfactual Smart Wallets work)
  • Server mints a one-time Supabase magiclink token via admin.generateLink — NO email is ever sent
  • Client exchanges via supabase.auth.verifyOtp(token_hash, type:"email") for a real session
  • Wallet address is auto-linked to the user's row on first sign-in — ready to follow / tier-sub / donate immediately
  • EOAs rejected at the door (DOLLA needs Smart Wallet for recurring billing)
2026-04-26

Video + GIF posts, autoplay in feed

SHIPPED

Photos, GIFs, and videos all upload from one button. Videos autoplay muted with IG-style scroll pause.

  • New post page accepts image / GIF / video via single picker, per-kind size caps (8MB / 16MB / 100MB)
  • Feed video card: muted + loop + playsInline + IntersectionObserver (plays at ≥50% visibility, pauses when scrolled away — same window IG/TikTok use)
  • Bottom-right speaker toggle for opt-in audio
  • Post detail page renders autoplaying <video controls> when first media is video, image otherwise
  • GIFs animate natively via the existing <img> path — no extra code needed
2026-04-26

Mobile (Expo) — auth + live feeds + brand kit

SHIPPED

iOS app boots, signs in via Supabase, and pulls real posts from justadolla.com.

  • lib/supabase + lib/api: Supabase client with AsyncStorage token persistence; fetch wrapper forwards Bearer tokens so the same getAuthUser() server-side resolver works for mobile
  • (auth)/login + (auth)/signup wired to Supabase signIn / signUp
  • (tabs)/discover, trending, following all fetch from /api/feed/* — replaced mock data adapter
  • _layout: AuthProvider + auth-gated routing (signed-out → /login)
  • Brand kit v2.0 colors applied (Sovereign Purple, Brand Gold, Verified Green) matching web globals.css
  • Wallet integration deferred with a written 3-option decision doc (Privy embedded / Coinbase deep-link / WebView wrapper) — recommendation: Privy
2026-04-26

Achievements engine + Mints + Calling Cards — live

SHIPPED

5 progression tracks fire on every payment. Mint moments surface a notification to the creator who brought the new user in.

  • lib/achievements: runAchievementCheckBackground() + sendMintNotification() — direct in-process call, no HTTP roundtrip
  • Wired into follow/confirm, donate/confirm, donate/record-onetime, cron/charge-subscriptions
  • Income / Philanthropy / Influence / Impact / Mint tracks all evaluated against live stats from payments + donations + follows + mints + post engagement
  • AchievementsBoard: collapsible IG-style pill on profile ("⭐ N/5 cards · View stats ▼") — expands to the full 5-track grid + lifetime stats
  • "Minted by @creator" gold pill on profiles where applicable — the user who brought them in is permanently tagged
2026-04-26

Mock-data cleanup with ?demo=1 escape hatch

SHIPPED

709 seeded placeholder accounts hidden from production. Pitch-deck view still one query param away.

  • Migration 00015: users.is_seed + posts.is_seed columns
  • 709 seed users + 1020 seed posts flagged via UUID prefix, corp-wallet placeholder address, seed_creator_metrics override row, or generated name+digit pattern
  • All feed / search / creators queries default to is_seed=false, accept includeSeed flag
  • API routes read ?demo=1 via lib/demo-mode; client pages propagate via useDemoQuery hook (window.location.search read inside useEffect — no Suspense bailout)
  • Following feed intentionally NOT gated (if you actively follow someone, you want their posts)
  • Real users currently visible in production: 3 — kingdomportfoliosllc, affluenceer, estremeraevan
2026-04-26

Single-wallet model locked in (donor sub-account experiment reverted)

SHIPPED

One Coinbase Smart Wallet per user — both follows AND donations route to wallet_address.

  • Donor sub-account architecture was attempted (sprout-donor-wallet flow) then REMOVED entirely after wallet_sendCalls couldn't recognize the sub-account factory
  • Files deleted: lib/ensure-donor-wallet.ts, app/api/payments/donor-wallet/*
  • DonateModal copy updated to reflect single-wallet recipient
  • donation_wallet_address column ignored in code path (still in DB, harmless)
  • Smart Wallet–only enforcement preserved via smartWalletOnly preference + useIsWalletACoinbaseSmartWallet hook
  • Outcome: every recipient lookup just reads users.wallet_address — clean, predictable, recipient-routing works for follows + donations + tier subs
2026-04-26

Gas Overhead Projector at /admin/gas-overhead

SHIPPED

Interactive paymaster-burn estimator: gas runs ~9% of DOLLA's revenue cut at every scale point.

  • Sliders for total users, % verified, % sovereign, ops/user/mo per tier, $/op
  • Live monthly ops + gas burn + DOLLA revenue (49% of premium tiers) + net + gas/revenue ratio
  • Scale milestones table (1K → 10M users) with same assumptions applied — confirms gas does not eat us alive
  • Boundary slider: drop verified % to 1, push free ops to 15 → margin flips negative; tells us where to throttle free-user sponsorship
2026-04-26

Sovereign-to-Sovereign custom donations

SHIPPED

Sovereign users can send custom $ donations to Sovereign creators — one-time or recurring.

  • Donate $ button on every creator profile (next to Follow), opens DonateModal with eligibility probe
  • One-time path: OnchainKit <Transaction> → USDC transfer → server verifies on-chain Transfer log, records payment
  • Recurring path: subscribe() spend permission → /api/payments/donate/confirm → first charge → cron auto-renews monthly or weekly
  • Recipient is always the creator's Donor Wallet (separate from their primary spending wallet)
  • Cron updated to handle target_type='donation' alongside creator follows + platform tiers — same recurring engine, three rails
  • Migration 00017: 'donation' added to subscription_target_type enum + donation_wallet_address column on users
2026-04-26

Premium tiers on Coinbase Base — recurring auto-billing live

SHIPPED

$4.99/mo Verified and $4.99/wk Sovereign tiers wired end-to-end on Base mainnet.

  • Stripe rail removed from tier subs — Coinbase Smart Wallet exclusive
  • /api/payments/subscribe + /confirm + /retry-charge endpoints with full recurring spend-permission flow
  • Charges route to PLATFORM_WALLET_ADDRESS (corp Coinbase Business USDC-on-Base sink, 0x26b2…7A95)
  • Tier-integrity guarantee: users.premium_tier only flips when on-chain charge confirms — no badge without payment
  • /api/payments/verify-tier endpoint with self-heal: resets premium_tier to 'free' if no completed payment backs it
  • UpgradeModal redesigned: portal-rendered, body-scroll-locked, true fullscreen takeover; centered on every screen size
  • FundCard inline auto-fund — when first charge fails for insufficient USDC, modal swaps to Coinbase Onramp without re-asking for a signature
2026-04-26

Mainnet flip — Sepolia → Base mainnet (chainId 8453)

LIVE

Production switched off testnet. Real USDC, real users, real revenue.

  • All env vars updated: NEXT_PUBLIC_BASE_CHAIN_ID=8453, all paymaster URLs flipped from /base-sepolia/ to /base/
  • All 500 seeded creator wallets pointed at the corp Coinbase USDC-on-Base sink so all simulated traffic settles to a real account
  • Daily cron at /api/cron/charge-subscriptions auto-detects mainnet via chainId — no code changes needed for live billing
  • First on-chain probe confirmed: paymaster returns chainId 0x2105, both EntryPoint v0.6 + v0.7 supported, SpendPermissionManager deployed
2026-04-26

Smart Wallet–only enforcement + first-class onboarding

SHIPPED

Two-wallet auto-creation flow, EOA gracefully blocked with a 60-second walkthrough.

  • Wagmi config locked to a single connector: coinbaseWallet({ preference: 'smartWalletOnly' }) with classic display — exactly one popup, always the passkey UI
  • EOA detection via connector identity (not eth_getCode) so counterfactual Smart Wallets are recognized correctly
  • /settings/wallet rebuilt as the Coinbase onboarding hub: 6-tile benefits showcase (USDC APY, Coinbase Card cashback, 0% fees, gasless gas, passkey security, regulated bank), plus a 5-step collapsible walkthrough
  • Fund-your-wallet guidance card auto-renders when balance < $1, with three numbered funding paths and a copyable address
  • Send / Cash Out card (gasless USDC transfer to any wallet) with explicit "DOLLA never holds your funds" framing + Coinbase deposit address recipe
  • All FollowModal + ConnectWalletButton + DonateModal use OnchainKit's <ConnectWallet> — no raw CoinbaseWalletSDK anywhere in app code
2026-04-26

Wallet verification engine + cross-network USDC scan

SHIPPED

Catches the "I have USDC but it's on the wrong network" footgun before it costs anyone money.

  • /api/wallet/verify endpoint: shape validation, on-chain reachability, EOA-vs-contract detection, USDC + ETH + tx-count read
  • Cross-network USDC discovery — parallel probes to Ethereum L1, Polygon, Arbitrum, Optimism — surfaces "you have $X USDC on Ethereum L1, but this address has 0 on Base"
  • Idempotent inline send via OnchainKit <Transaction>, gasless via paymaster
  • Settings/Wallet auto-runs verify on every connect + replaces the slow round-trip with wagmi's useReadContract for instant USDC balance display (cached, refetches in background)
2026-04-25

Manual wallet edit + paste in /settings/wallet

SHIPPED

Power users can paste a Coinbase Business / exchange deposit address directly.

  • 0x[a-fA-F0-9]{40} format validation on the client
  • Connected state shows Change/Disconnect controls — no need to drop the whole link to swap addresses
  • Burn-address placeholder bug fixed (collided with a real user's wallet); seeded test users cleared so the address is uniquely owned
2026-04-24

Live on justadolla.com — Sepolia testnet

LIVE

Six phases of work deployed to production. Public URL, real code, real rails.

  • All CDP + Base Account credentials synced to Vercel (11 env vars)
  • Production chain set to Base Sepolia (chainId 84532) for safe beta testing
  • /test/sponsor diagnostic page live for debugging paymaster sponsorship
  • /api/cron/charge-subscriptions ready — scheduled daily at 09:00 UTC
2026-04-24

Phase 4 — Recurring charges + real unfollow

SHIPPED

Monthly billing runs itself. Unfollow actually stops the money.

  • Vercel Cron daily job finds due subscriptions and charges them via CDP Server Wallet
  • 3-day grace period before marking expired; payments roll current_period_end forward
  • Unfollow endpoint marks follow inactive + subscription canceled — cron respects status
  • Optimistic UI with rollback on server error
2026-04-23

Phase 3 — First real $1 USDC follow, on-chain

PROVEN

A real human used the Follow button. A real dollar moved between two different wallets.

  • Follower → creator: $1.00 USDC transferred atomically via Base Account spend permission
  • Follow + subscription + payment rows persisted to Supabase
  • Two fake wallets also tested bidirectionally: alice ↔ bob, each way, gasless
  • First Mint (referral credit) recorded for a creator
2026-04-23

Phase 2 — Coinbase Base Account architecture

SHIPPED

Replaced a custom smart contract plan with Base's native spend permissions.

  • Installed @coinbase/cdp-sdk, @coinbase/onchainkit, @base-org/account
  • Provisioned DOLLA subscription-owner wallet on CDP Server Wallets
  • Paymaster + bundler wired end-to-end; gasless UX confirmed live
  • Saved weeks of custom Solidity work + audit cost
2026-04-23

Phase 1 — Production blockers cleared

SHIPPED

TypeScript strict, webhook persistence, admin guard, gamification — all real now.

  • 47 TypeScript errors fixed; strict mode enabled in production build
  • Coinbase + Stripe webhook handlers now persist payments/follows/subscriptions to DB
  • Achievement unlock logic wired — 5 tracks with real stats, calling cards, notifications
  • Admin route auth guard + Sovereign-tier gate on donations + auth on feed interactions
2026-04-15 – 2026-04-22

Polish sprint — mobile, auth, wallet UX

SHIPPED

Smooth reveals, passwordless recovery, rock-solid wallet connect.

  • First real on-chain follow plumbing ($1 USDC follower → creator)
  • Coinbase Smart Wallet integration refactored 4 times until stable
  • GPU-composited mobile animations, anticipatory scroll preload
  • Logout, forgot password, loading states, null guards, privacy + terms pages
2026-04-15

MVP complete — all pages on live Supabase

SHIPPED

710 users, 1020 posts, 2529 follows. No more mock data.

  • PostgreSQL 17 via Supabase, 8 migrations, 20+ tables, RLS on everything
  • Google OAuth + email/password auth with auto-created profiles
  • Three feeds wired (Discover, Following, Trending) + trending edge function
  • Creator dashboard, profile, search, settings — all pulling live data
  • Design system locked: glass morphism, teal accent, Outfit + JetBrains Mono
2026-04-14

Project bootstrap — monorepo + schema

SHIPPED

From blank repo to working skeleton in a day.

  • Turborepo + pnpm workspaces: apps/web, apps/mobile, 7 shared packages
  • Next.js 16 App Router, Tailwind 4, TypeScript strict
  • Full database schema designed: users, creators, follows, subscriptions, payments, achievements, mints, charities, notifications
  • Dual-rail payment package scaffolded (Coinbase Base + Stripe)
Early 2026

Thesis + design

SHIPPED

The vision: every follower is a dollar, every dollar reaches the creator, 51% back to the world.

  • BMAC validated the thesis ($4.9M ARR, 1M creators, tip-jar model)
  • DOLLA evolves it: Follow = $1/mo, 0% platform fee, USDC on Base, 51% of premium revenue donated
  • Three-page creator architecture (Free / Monthly / Weekly)
  • Interactive pitch site + 18-slide investor deck + research brief
  • Brand identity, paradigm-shifter principles, infrastructure plan

Up next

  • Mobile wallet integration — Privy embedded wallet (recommended) vs Coinbase Wallet deep-link. Picks before mobile follows / tier subs / donations can ship
  • App Store submission gaps — Apple credentials in eas.json, EAS project ID, real icon/splash, privacy policy URL, screenshots, Apple Developer enrollment
  • Supabase custom domain — map api.justadolla.com to the project (Pro plan + DNS), swap the ugly subdomain everywhere
  • Hand-recruit 10–25 real creators across the diversity categories before public launch — Substack/Beehiiv playbook
  • Stripe card → USDC fiat ramp — code is wired, returns 503 until keys pasted
  • 51% donation outflow — keep verbal commitment + retain 100% in corp account until the charity wallet directory is built
  • Compliance review — US money transmitter angles, USDC P2P likely dodges most of it but lawyer eyeball before beta opens
  • NLP moderation layer (deferred — skipped while tokens/fees are out of scope)

If 3M users show up tomorrow

The scale-readiness playbook

Late-night scenario question: by the Grace of God a celeb joins, 3M users sign up in a month. What breaks? What costs? What do we do about it now? Below is the expert-mentor read.

100K
Signups / day
10K
Hot peak / hr
$1.2M
Net MRR @ 8% premium

Healthy economics. The economics are not what breaks first. The plumbing is.

What breaks first · 2 of 6 shipped

01

The cron dies at ~5k subs

Shipped

/api/cron/charge-subscriptions was a single Vercel function looping serially. At 240k due subs/day even at 10 charge() calls/sec it's 6.6 hours of execution — past any function timeout. First celeb spike, the cron just stops finishing.

What we built

Postgres-backed queue with FOR UPDATE SKIP LOCKED. Daily cron now ENQUEUES jobs into subscription_charge_jobs (UNIQUE on (sub_id, period_end) for idempotent retries) and fires 5 parallel workers via /api/cron/charge-worker. Each worker claims a batch of 50 jobs atomically and processes them with Promise.allSettled. Failed jobs back off exponentially (1h → 6h → 24h). Throughput jumped from ~10 charges/sec serial to ~250 charges/sec across the fan-out. Trivially swappable to Inngest later.

02

CDP subscription-owner wallet nonce contention

Shipped

ONE Smart Account (dolla-subscription-owner 0xBba0…8105) was signing every charge. EVM accounts have a single sequential nonce — charges submit in order, one at a time. At 10/sec sustained that's already at the edge regardless of how many parallel workers we run.

What we built

Provisioned 10 CDP Smart Wallets (dolla-subscription-owner-shard-00 through -09). Verified the project-wide paymaster auto-sponsors all 10 — no manual allowlist needed. Each new subscriber is hashed (FNV-1a on user_id) → assigned to a deterministic shard. The user signs the spend permission against THAT shard's address. wallet_name is persisted on subscriptions + snapshotted on the charge job so the worker calls charge() against the matching wallet later. 10x nonce parallelism. Existing legacy subs (wallet_name NULL) keep working unchanged. ~100-200 charges/sec ceiling now — comfortably good for ~1M paying subscribers.

03

Supabase database tier

WEEK-1 FIX

Pro plan ($25) gives 8GB. At 3M users with profiles + posts + follows + payments + achievements... you blow through that fast. Need Team ($599/mo, 32GB) or Enterprise. Two specific things hurt before disk: connection-pool exhaustion (every Vercel function instance opens connections, you HAVE to use the pooler/Supavisor for everything except DDL); and RLS at scale becomes a CPU hot spot — audit policies, add covering indexes, consider rewriting hot paths as service-role queries.

Fix

Upgrade to Team. Move all read traffic through the pooler. Audit RLS policies for hot tables (posts, follows, payments). Consider read replicas for feed queries.

04

Materialized views thrash the DB

WEEK-1 FIX

creator_stats and post_engagement_stats — if these recompute on every page load they'll thrash Postgres. Already have the seed_creator_metrics override pattern; apply the same pattern to anything trending/discover-style.

Fix

Materialize trending + discover views. Refresh every 1–5 min via cron, not computed live.

05

Storage egress — the silent budget killer

MONTH-1 FIX

Supabase Storage is fine at small scale; egress at 3M users will cost more than every other line item combined. 3M users × 5 video views/day × 50MB ≈ 750TB/month egress @ $0.09/GB = ~$67k/month JUST for video bandwidth.

Fix

Move post media to Cloudflare R2 ($0 egress) or Bunny.net. For video specifically: Mux handles transcoding + CDN + thumbnails for ~$0.005/min viewed.

06

Vercel function invocations + bandwidth

MONTH-1 FIX

100M+ invocations/month at scale. Vercel Enterprise becomes the floor (~$20k+/yr). Edge-cache anything publicly readable: profile pages, campaign landings, share images.

Fix

ISR with 60s revalidation turns 1M page views into 17k function invocations. Cache the share-image API at the edge.

Operations that hurt before tech does

Compliance — start the conversation in month 0

At 3M users moving USDC P2P, US money transmitter laws absolutely become a question. The good news: because DOLLA is fully self-custodial, much of MSB regulation may not apply — but 'may' is a 6-figure legal-fee question. Two paths: (a) run a real KYC/MSB program (FinCEN registration, state-by-state money transmitter licenses, or use a licensed partner like Bridge.xyz / Stripe Treasury for the fiat side); (b) stay non-custodial + small — works until a state AG decides to test it.

Fix

Hire a crypto-native lawyer (not your general counsel) at 100k users. Don't wait for 1M.

Trust & Safety — the 'wholesome' stance is unenforced

Right now there's no NSFW filter, no DMCA workflow, no banned-content reporting that goes anywhere. At 3M users with a celeb in the mix, you'll get bad-faith content within hours.

Fix

Image/video moderation pipeline (Sightengine, Moderation API). DMCA takedown workflow + designated agent registration (~$6/year, surprisingly cheap). Banned-content reporting → human queue → banhammer. Account-level fraud detection (Vercel BotID, Castle.io).

Customer support — 30–150k tickets/mo

Self-service docs + chatbot covers 80%; remaining 20% is real human work.

Fix

Plan for 3–5 CS staff (or outsourced via Helpshift) when you cross 500k users. Triage by paying-tier — Sovereigns get fast lanes, they're your highest-value users.

Celeb onboarding playbook — pre-build NOW

When a celeb says yes you have 24 hours to make them look like a genius for choosing DOLLA. Build the playbook before you need it.

Fix

(1) Pre-provisioned Smart Wallet ready to claim. (2) Profile setup concierge (bio, avatar, banner, first 3 posts staged). (3) Custom Coffee Campaign image with their face baked in. (4) Live war-room dashboard for the launch hour: signups/sec, gas spend, error rate. (5) Pre-arranged interview/post schedule across their socials. (6) Pre-allocated paymaster budget tagged to their launch.

The non-obvious advice

Stripe BEFORE the celebrity

HIGH ROI

A celeb's audience is 95% non-crypto-native. They have iPhones and credit cards. They don't have Coinbase Smart Wallets. Without a card → USDC fiat ramp you lose 80%+ of conversion the moment the celeb's link goes viral.

Fix

The Stripe code is wired and returns 503 right now. Paste the keys and ship before any celeb deal closes. THE single biggest scaling lever you haven't pulled yet.

Public 51% donation dashboard at /giving

A real-time on-chain dashboard showing every dollar of the 51% pledge flowing to named charities is your moat. No competitor can copy it without actually doing it. Celebs will retweet a real-time charity flow page even if they ignore everything else. Also pre-empts the 'where does the money actually go?' press question.

Fix

Build /giving as a public page. Wire to an on-chain donation outflow cron + a charity wallet directory. Most-requested feature you don't know you need yet.

Don't try to handle 3M with solo-dev velocity

174 commits in 13 days is the reason DOLLA exists. It is NOT the system that scales it past 100k users. Mindset shift: from 'I can ship anything fast' to 'I orchestrate a team that ships anything fast.' Velocity per dev drops; total throughput rises.

Fix

Hiring order: (1) Backend/SRE eng with Postgres scaling chops. (2) Smart-contract / on-chain ops eng. (3) Trust & Safety lead (operational). (4) Crypto-native lawyer (fractional GC at first). (5) Mobile eng (finishes Privy + ships TestFlight). (6) Growth / community manager with influencer-marketing background. ~$1.5–2M/yr team. With $1.2M MRR at 3M users, covered. Without it you crater under your own success.

Reserve $100k cash for the celeb day

When the spike hits you'll need: emergency engineering hours, paymaster budget overruns, urgent legal calls, support tool licenses, a CS contractor pulled in same-day.

Fix

Pre-fund and on a corp card. The worst time to scramble for cash is when 3M users are arriving.

Cold-storage CDP wallet secrets TODAY

DO IT NOW

If CDP_WALLET_SECRET leaks, every recurring sub on the platform can be drained in one transaction. With 3M users that's a company-ending event. Right now it's in Vercel's encrypted env — fine for current scale; at scale you want Hashicorp Vault, AWS Secrets Manager, or 1Password's secrets-automation tier with audit logs.

Fix

Rotate the secret quarterly as policy. You haven't yet — start the practice now while it's just one rotation, not a coordinated team-wide event.

Per-celeb paymaster sub-accounts

When you onboard a celeb, give them their own paymaster sub-policy with a daily budget cap. If their launch is gas-bombed by a bot attack, only THEIR budget burns, not the platform's.

Fix

Coinbase Paymaster supports policy-per-app. Provision one per onboarded celeb.

Tiered rate limits keyed on auth method

Anon users: very tight. Email-only signups: tight. Coinbase Smart Wallet signups: looser (passkey + device proves real human).

Fix

Bot armies can't drain your paymaster by spamming /api/auth/wallet-signin if rate limits scale by auth-quality.

The thing nobody tells you

The hardest part of going from 1k to 3M users isn't the engineering. It's that everything you got right at small scale becomes a structural liability. The single subscription owner that was elegant at 100 subs is a single point of failure at 100k. The shipped-fast feature nobody used in beta becomes a fraud vector at scale. The "we'll fix that later" tech debt comes due all at once.

The companies that survive the spike operated at "scale-readiness" before scale arrived. That doesn't mean over-engineering — it means: queue the cron now, shard the wallet now, move media to R2 now, hire the SRE before you're on fire — even though all of it feels premature at 3 real users.

Right now DOLLA is uniquely positioned. Architecture's clean, velocity's real, unit economics work. The next 30 days of work — quietly, before any celeb is in the picture — are the ones that decide whether the celeb day is a triumph or a disaster.

✓ Done · 2026-04-27

Cron is queued (Postgres SKIP LOCKED + 5 parallel workers). Subscription owner is sharded (10 CDP wallets, deterministic routing per user). Capacity ceiling jumped from ~5K paying subs to ~1M. The two highest-leverage scaling moves shipped before they were needed.

Next biggest levers: Stripe fiat ramp · materialized views for hot reads · move media to Cloudflare R2

A Kingdom Portfolios venture. Built in public. No equity. No exit.
51% of all premium revenue donated on-chain.