<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Francesco Costantino</title>
    <description>The latest articles on DEV Community by Francesco Costantino (@brawlvalue).</description>
    <link>https://dev.to/brawlvalue</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3648553%2F031ff3d8-c443-447e-9d46-94275bd95e1a.jpg</url>
      <title>DEV Community: Francesco Costantino</title>
      <link>https://dev.to/brawlvalue</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brawlvalue"/>
    <language>en</language>
    <item>
      <title>How I built a viral gaming SaaS with my 14yo son (and survived a massive traffic spike on AWS without going bankrupt)</title>
      <dc:creator>Francesco Costantino</dc:creator>
      <pubDate>Wed, 08 Apr 2026 07:21:15 +0000</pubDate>
      <link>https://dev.to/brawlvalue/how-i-built-a-viral-gaming-saas-with-my-14yo-son-and-survived-a-massive-traffic-spike-on-aws-31pa</link>
      <guid>https://dev.to/brawlvalue/how-i-built-a-viral-gaming-saas-with-my-14yo-son-and-survived-a-massive-traffic-spike-on-aws-31pa</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrg61zf19x4tzj993mg3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrg61zf19x4tzj993mg3.jpg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
It started on a random Saturday afternoon with a simple question from my 14-year-old son: &lt;em&gt;"Dad, how much is my Brawl Stars account actually worth?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We searched online. Nothing but shady websites asking for passwords or outdated forums. So, as a Dev with 20 years of experience, I said: &lt;em&gt;"Let's build it ourselves."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;48 hours later, &lt;strong&gt;&lt;a href="https://brawlvalue.com" rel="noopener noreferrer"&gt;BrawlValue.com&lt;/a&gt;&lt;/strong&gt; was born. &lt;/p&gt;

&lt;p&gt;What started as a simple API calculator accidentally turned into a massive, hyper-local gamified platform. The Gen-Z kids didn't just want to know their account value; they wanted to flex it. We introduced School and City Leaderboards. &lt;/p&gt;

&lt;p&gt;The result? The "Dark Social" (WhatsApp school groups) exploded. In just a few days, we mapped &lt;strong&gt;over 1,500 schools&lt;/strong&gt;, acquired &lt;strong&gt;5,000+ verified users&lt;/strong&gt; via Discord OAuth2, and handled massive traffic spikes driven by top gaming influencers.&lt;/p&gt;

&lt;p&gt;Here is how I architected the system to survive the hype without paying thousands of dollars to Jeff Bezos.&lt;/p&gt;


&lt;h3&gt;
  
  
  1. The Nginx "Russian Roulette" Hack (Bypassing Rate Limits)
&lt;/h3&gt;

&lt;p&gt;The official game API has strict rate limits per IP. When a famous YouTuber dropped a video about our site, we expected thousands of concurrent requests to fetch the same Player Tags. A standard setup would have resulted in &lt;code&gt;429 Too Many Requests&lt;/code&gt; and a crashed site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; I generated 3 different API keys for my single AWS EC2 IP. Then, I used the native Nginx &lt;code&gt;split_clients&lt;/code&gt; module to balance the load evenly across the keys based on the &lt;code&gt;$request_id&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;split_clients&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;$&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kn"&gt;request_id&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;$brawl_token&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;33.3%&lt;/span&gt;   &lt;span class="s"&gt;"TOKEN_1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;33.3%&lt;/span&gt;   &lt;span class="s"&gt;"TOKEN_2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;*&lt;/span&gt;       &lt;span class="s"&gt;"TOKEN_3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combined with &lt;code&gt;proxy_cache_lock on;&lt;/code&gt; (to prevent the Thundering Herd problem), this single Nginx trick absorbed the entire traffic spike. Zero Node.js overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Offloading Compute to PostgreSQL (Supabase)
&lt;/h3&gt;

&lt;p&gt;I initially ran a &lt;code&gt;c6i.xlarge&lt;/code&gt; EC2 instance, but calculating dynamic ranks for 5,000 users across 1,500 schools in Node.js was a memory-leak nightmare waiting to happen.&lt;/p&gt;

&lt;p&gt;I shifted the entire load to &lt;strong&gt;Supabase&lt;/strong&gt; (PostgreSQL). Instead of sorting arrays in JS, I wrote a materialized View using Window Functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;season_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;school_id&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;flex_score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;school_rank&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js simply fetches a pre-calculated JSON. CPU usage on my AWS instance dropped to &lt;strong&gt;0.08%&lt;/strong&gt;. I literally downgraded the server to a &lt;code&gt;t3.small&lt;/code&gt; during the peak and saved my wallet.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AI-Powered Zero-Click Moderation (Qwen3-VL)
&lt;/h3&gt;

&lt;p&gt;To enter the Leaderboard, users must prove they own the account. Manual verification for 5,000 kids? Impossible.&lt;/p&gt;

&lt;p&gt;I built a custom Discord Bot using &lt;code&gt;discord.js&lt;/code&gt;. Users simply drop a screenshot of their game profile in a specific channel. The bot instantly opens a private thread, sends the image to Alibaba Cloud's &lt;strong&gt;Qwen3-VL&lt;/strong&gt; (an ultra-fast Multimodal LLM), extracts the Player Tag via OCR, verifies it against the DB, and auto-assigns the "Verified" role. 100% automated KYC for teenagers.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The "Ego-Trap" Monetization (Webhook Magic)
&lt;/h3&gt;

&lt;p&gt;How to monetize kids who don't have credit cards? You sell social status.&lt;br&gt;
We created a "VIP Brawler" role. Users pay €3 via Ko-Fi. &lt;br&gt;
Ko-Fi triggers a Webhook ➔ My Node.js server parses the payload ➔ Updates Supabase (&lt;code&gt;is_vip: true&lt;/code&gt;) ➔ The Next.js frontend instantly turns their generated "Share Card" into a glowing Gold/Neon design. &lt;br&gt;
Zero manual work. Pure passive income driven by gaming vanity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92kh4yadf7fspqpuo576.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92kh4yadf7fspqpuo576.png" alt=" " width="558" height="985"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Today, we are launching on Product Hunt!
&lt;/h3&gt;

&lt;p&gt;We transitioned from a weekend toy to an enterprise-grade Data-Scouting architecture. We are now officially launching &lt;strong&gt;Season 1&lt;/strong&gt; and trying to make some noise on Product Hunt.&lt;/p&gt;

&lt;p&gt;If you love bootstrapped projects, dirty but highly effective Nginx hacks, and indie-hacking survival stories, &lt;strong&gt;I would love your honest feedback and support on our launch page today!&lt;/strong&gt; 👇&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.producthunt.com/products/brawl-value" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/brawl-value&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know in the comments if you have any questions about the stack, Supabase RLS, or how to handle Gen-Z traffic spikes!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How I built a Serverless, P2P Parental Control app using WebRTC and Kotlin</title>
      <dc:creator>Francesco Costantino</dc:creator>
      <pubDate>Sat, 06 Dec 2025 22:31:01 +0000</pubDate>
      <link>https://dev.to/brawlvalue/how-i-built-a-serverless-p2p-parental-control-app-using-webrtc-and-kotlin-fjj</link>
      <guid>https://dev.to/brawlvalue/how-i-built-a-serverless-p2p-parental-control-app-using-webrtc-and-kotlin-fjj</guid>
      <description>&lt;p&gt;As a parent and an Android developer, I faced a dilemma. I wanted to monitor my child's activity on TikTok and YouTube to ensure their safety, but I hated the privacy implications of existing parental control apps.&lt;/p&gt;

&lt;p&gt;Almost every commercial solution works the same way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Collect child's data (location, history, app usage).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upload everything to a centralized Cloud Database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Display it on the parent's phone.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I didn't want my child's location history sitting on a third-party server. So, I decided to over-engineer a solution for myself. I spent the last 6 months building SafeStream, a completely serverless, Peer-to-Peer monitoring system.&lt;/p&gt;

&lt;p&gt;Here is how I built it using Kotlin, Jetpack Compose, and WebRTC.&lt;/p&gt;

&lt;p&gt;The Architecture: "No Backend" approach&lt;br&gt;
The core idea is simple: The Parent device is the server. Data should flow directly from Device A (Child) to Device B (Parent) without persisting anywhere in between.&lt;/p&gt;

&lt;p&gt;To achieve this, I used WebRTC (Web Real-Time Communication) not for video calls, but for its DataChannels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guardian App (Child)&lt;/strong&gt;: Runs a background service that collects data locally. It acts as a WebRTC peer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parent App&lt;/strong&gt;: Acts as the viewer peer. It receives JSON payloads directly and stores them in a local Room (SQLite) database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signaling&lt;/strong&gt;: I use Firebase Realtime Database strictly for the handshake (SDP Offer/Answer exchange). No user data touches Firebase.&lt;/p&gt;

&lt;p&gt;The Tech Stack&lt;br&gt;
&lt;strong&gt;Language&lt;/strong&gt;: 100% Kotlin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI&lt;/strong&gt;: Jetpack Compose (Material3) for a modern "Reel-like" feed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P2P&lt;/strong&gt;: Google's WebRTC library wrapped in a custom WebRTCConnectionManager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traversal&lt;/strong&gt;: STUN/TURN servers to bypass NATs and firewalls (essential for 4G/5G connections).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encryption&lt;/strong&gt;: AES-256-GCM on top of WebRTC's DTLS (Paranoia level: High).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Technical Challenges&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. Extracting Video Metadata (The ethical hack)&lt;/strong&gt;&lt;br&gt;
TikTok and YouTube don't offer APIs for "watch history". I implemented an Accessibility Service (GuardianAccessibilityService.kt) that scans the UI hierarchy.&lt;/p&gt;

&lt;p&gt;It detects when the user is in a video app (com.zhiliaoapp.musically or com.google.android.youtube).&lt;/p&gt;

&lt;p&gt;It scrapes the contentDescription or text of the visible views to extract the video title and channel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimization&lt;/strong&gt;: To avoid battery drain, this logic is heavily debounced and only runs when the screen content changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The WebRTC Handshake in Background&lt;/strong&gt;&lt;br&gt;
Keeping a WebRTC connection alive in the background on modern Android is... painful. Android's Doze Mode and App Standby Buckets are aggressive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: I implemented a foreground service with a persistent notification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turn-on-demand:&lt;/strong&gt; The connection isn't always open. The Parent sends a high-priority FCM (Firebase Cloud Message) to "wake up" the Child device, which then initiates the WebRTC signaling process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Transferring Images over P2P&lt;/strong&gt;&lt;br&gt;
Sending high-res screenshots over a DataChannel requires chunking. WebRTC has a limit on message size (approx 16KB to be safe). I wrote a chunking algorithm that splits the Bitmap (compressed as WebP) into small binary packets, sends them sequentially, and reassembles them on the Parent side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Business Model (Indie Dev Reality)&lt;/strong&gt;&lt;br&gt;
I'm releasing this app for free. However, running TURN servers (bandwidth relay) costs money. To make it sustainable without subscriptions or selling data, I implemented a Credit System:&lt;/p&gt;

&lt;p&gt;Text logs and real-time location are free (low bandwidth).&lt;/p&gt;

&lt;p&gt;Downloading high-res screenshots costs "Credits".&lt;/p&gt;

&lt;p&gt;Users get free daily credits or can watch a Rewarded Ad (AdMob) to refill them. This pays for the TURN bandwidth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's Next?&lt;/strong&gt;&lt;br&gt;
I'm launching the Open Beta next week. I'm looking for feedback on the P2P stability across different network carriers (CGNAT is my enemy).&lt;/p&gt;

&lt;p&gt;If you are curious about the P2P implementation or want to test it out, check it out here: &lt;a href="https://www.safestreamguardian.com/en/architecture" rel="noopener noreferrer"&gt;safestreamguardian Architecture - beta subscription&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>webrtc</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
