<?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: Phillipe Silva</title>
    <description>The latest articles on DEV Community by Phillipe Silva (@fromkitchentocode).</description>
    <link>https://dev.to/fromkitchentocode</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4015724%2F80370980-9435-49a6-bfa7-7219578a3d90.png</url>
      <title>DEV Community: Phillipe Silva</title>
      <link>https://dev.to/fromkitchentocode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fromkitchentocode"/>
    <language>en</language>
    <item>
      <title>How I turned my SaaS's client portal into a white-label kit other devs can resell</title>
      <dc:creator>Phillipe Silva</dc:creator>
      <pubDate>Sun, 05 Jul 2026 03:36:16 +0000</pubDate>
      <link>https://dev.to/fromkitchentocode/how-i-turned-my-saass-client-portal-into-a-white-label-kit-other-devs-can-resell-bfo</link>
      <guid>https://dev.to/fromkitchentocode/how-i-turned-my-saass-client-portal-into-a-white-label-kit-other-devs-can-resell-bfo</guid>
      <description>&lt;p&gt;Running a healthcare SaaS (CareRoots) meant building a client-facing portal — a place where each clinic could log in, see their own patients/orders, and get notified when something needed attention. Straightforward enough.&lt;/p&gt;

&lt;p&gt;The catch: the portal's "admin" side had &lt;strong&gt;zero independent identity&lt;/strong&gt;. It fully piggybacked on the main app's own session. Which was fine, right up until I decided to pull it out into a standalone white-label kit other developers could install for their own clients — and realized the admin panel couldn't exist without the rest of CareRoots running underneath it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What had to be built from scratch
&lt;/h2&gt;

&lt;p&gt;Turning a tightly-coupled internal feature into something that stands on its own meant building, essentially from zero:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A real admin login system — its own session, its own cookie, its own config file, fails closed if unconfigured&lt;/li&gt;
&lt;li&gt;A portal-local Cloudflare analytics library (previously reached across into the parent app's own integration)&lt;/li&gt;
&lt;li&gt;Its own SMTP mailer (same story — used to reach into the site root)&lt;/li&gt;
&lt;li&gt;Its own push-notification helper, so a client with a new order gets a real browser push, installable as an app via "Add to Home Screen," no App Store review needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is exotic engineering. It's just the unglamorous work every "extract this into a product" project runs into: the thing that worked because it was &lt;em&gt;one system&lt;/em&gt; has to be re-taught how to be &lt;em&gt;two systems that don't trust each other by default&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 real bugs the extraction surfaced
&lt;/h2&gt;

&lt;p&gt;Worth sharing because none of them were "hard" bugs — they were the kind that hide in code that's never been forced to run standalone before:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The auto-slug generator lowercased &lt;em&gt;after&lt;/em&gt; stripping non-alphanumerics, not before.&lt;/strong&gt; So &lt;code&gt;"Test Client"&lt;/code&gt; became &lt;code&gt;"est-lient"&lt;/code&gt; instead of &lt;code&gt;"test-client"&lt;/code&gt; — the uppercase &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt; got treated as different characters than their lowercase selves during the strip step, and vanished. Any business name with capital letters got mangled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A failed reply-email was computed but never rendered anywhere.&lt;/strong&gt; The code correctly detected that sending failed — it just never told the user. A client hitting "reply" during an outage would see... nothing. No error, no success message. Looked like the button did nothing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A login page called a helper function (&lt;code&gt;e()&lt;/code&gt;, an HTML-escaping shortcut) without requiring the file that defines it.&lt;/strong&gt; This one's on me — introduced during the extraction itself, not a pre-existing bug. Caught it because the page threw a fatal error the first time it actually ran standalone, isolated from the rest of the app where that helper happened to already be loaded by something else.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The white-label problem nobody warns you about
&lt;/h2&gt;

&lt;p&gt;Here's the one that isn't a "bug" exactly: the kit's shipped defaults were, at first, literally CareRoots' own brand colors and fonts. Made sense — I copy-pasted the real thing to start from. But shipping a &lt;em&gt;white-label&lt;/em&gt; product with your &lt;em&gt;other&lt;/em&gt; product's exact visual identity baked in as the default undercuts both: it dilutes the original brand, and it tells the kit's actual buyer "here's someone else's colors, good luck making this look like yours."&lt;/p&gt;

&lt;p&gt;Redesigned it to a distinct palette (charcoal + ochre/amber, Sora + Work Sans) that's recognizably its own thing — not a reskin excuse, an actual identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your own admin panel, session fully separate from every client's login&lt;/li&gt;
&lt;li&gt;Each client gets their own portal: orders inbox, reply-by-email&lt;/li&gt;
&lt;li&gt;Real push notifications via the Web Push API — installable, no App Store&lt;/li&gt;
&lt;li&gt;Per-client Cloudflare traffic analytics (degrades gracefully if a client has no zone configured)&lt;/li&gt;
&lt;li&gt;Document storage with tiered limits and virus scanning&lt;/li&gt;
&lt;li&gt;Bilingual EN/PT client-facing UI&lt;/li&gt;
&lt;li&gt;Plain PHP, no build tools, no framework, runs on SQLite — any standard PHP host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First 5 buyers get it for &lt;strong&gt;$79&lt;/strong&gt; (then $249), one-time payment, 30-day money-back guarantee.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://fromkitchentocode.com/go.php?to=chk-devto" rel="noopener noreferrer"&gt;https://fromkitchentocode.com/go.php?to=chk-devto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to go deeper on the session-isolation approach, the push/PWA setup, or anything else in the comments.&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>saas</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
