<?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: David Raphi</title>
    <description>The latest articles on DEV Community by David Raphi (@draphy).</description>
    <link>https://dev.to/draphy</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%2F3076702%2F1f42d62e-d720-4b3b-bd03-7c48a934aa04.png</url>
      <title>DEV Community: David Raphi</title>
      <link>https://dev.to/draphy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/draphy"/>
    <language>en</language>
    <item>
      <title>🚀 Just shipped Nooxy - turn your Notion workspace into a personal website in 5 mins for free</title>
      <dc:creator>David Raphi</dc:creator>
      <pubDate>Fri, 02 Jan 2026 09:06:56 +0000</pubDate>
      <link>https://dev.to/draphy/nooxy-free-notion-to-custom-domain-personal-website-in-5-minutes-5b3a</link>
      <guid>https://dev.to/draphy/nooxy-free-notion-to-custom-domain-personal-website-in-5-minutes-5b3a</guid>
      <description>&lt;p&gt;&lt;em&gt;How an open-source Notion reverse proxy saved me from reinventing the wheel&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/draphy/nooxy" rel="noopener noreferrer"&gt;github.com/draphy/nooxy&lt;/a&gt; · &lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/nooxy" rel="noopener noreferrer"&gt;npmjs.com/package/nooxy&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment I Almost Over-Engineered Everything
&lt;/h2&gt;

&lt;p&gt;It started with a simple goal: I wanted a personal website at &lt;a href="https://draphy.org" rel="noopener noreferrer"&gt;draphy.org&lt;/a&gt;. Somewhere to put my projects, write about stuff I'm learning, maybe host my resume. Nothing fancy.&lt;/p&gt;

&lt;p&gt;My first instinct? Build a production-level CMS from scratch.&lt;/p&gt;

&lt;p&gt;I'm a developer. I know React, Next.js, databases, all that stuff. I could totally build something custom. I was already sketching database schemas in my head. User auth, CMS backend, maybe even a headless setup with Sanity or something.&lt;/p&gt;

&lt;p&gt;Then reality hit me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why am I building enterprise architecture for a site that'll get maybe 50 visitors a month?&lt;/strong&gt; This isn't a startup. Nobody's asking for features. I don't need to scale to a million users. It's literally just me putting stuff on the internet.&lt;/p&gt;

&lt;p&gt;I was about to spend weeks building something I could set up in minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Notion Problem (And Why Existing Solutions Didn't Work)
&lt;/h2&gt;

&lt;p&gt;I already document everything in Notion anyway. Notes, project ideas, random thoughts at 2am. It's all there. And Notion has this "Publish to Web" thing that lets you share pages publicly.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Perfect&lt;/em&gt;, I thought. I'll just publish my Notion pages and point my custom domain to them.&lt;/p&gt;

&lt;p&gt;Then I saw the pricing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Real Cost of Notion's Custom Domain
&lt;/h3&gt;

&lt;p&gt;Turns out Notion buries the real cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plus Plan&lt;/strong&gt;: $10/month (annual) or $12/month (monthly)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Domain Add-on&lt;/strong&gt;: Additional $8/month (annual) or $10/month (monthly)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total&lt;/strong&gt;: $18-22/month just to use your own domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a personal website that I update maybe once a week, that's &lt;strong&gt;$216-264/year&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And for that money? You still can't add custom CSS. Can't inject JavaScript. Can't control your SEO meta tags properly. You're stuck with whatever Notion decides looks good.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Search for Alternatives
&lt;/h3&gt;

&lt;p&gt;So I went looking for alternatives. Someone must have solved this already, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fruition&lt;/strong&gt; was everywhere in search results. Cloudflare Workers script, reverse proxy for Notion, open-source, free. Looked perfect.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;I checked the &lt;a href="https://github.com/stephenou/fruitionsite" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;. &lt;strong&gt;189 open issues.&lt;/strong&gt; Blank screens, broken navigation, databases not rendering after some Notion update in 2023. The project's basically dead.&lt;/p&gt;

&lt;p&gt;People were frustrated:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Site is not loading... a white screen with an infinitely looping loading circle."&lt;/p&gt;

&lt;p&gt;"Back button functionality is broken, causing the signup screen to appear when using browser back navigation."&lt;/p&gt;

&lt;p&gt;"Database not rendered - after Notion update 09.2023"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The project is effectively abandoned, with dozens of people scrambling to piece together workarounds that take hours to implement. And even then, there's no guarantee they'll work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NoteHost&lt;/strong&gt; was another option. At least it's an actual &lt;a href="https://www.npmjs.com/package/notehost" rel="noopener noreferrer"&gt;npm package&lt;/a&gt;, which was nice. But last update was like 10 months ago, barely any community around it, and it was missing stuff I wanted.&lt;/p&gt;

&lt;p&gt;I tried a few other GitHub repos too. &lt;code&gt;notion-custom-domain&lt;/code&gt;, &lt;code&gt;notion-proxy&lt;/code&gt;, things like that. Same story everywhere: outdated, barely documented, half-working.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Why Not Build It Myself?" Moment
&lt;/h2&gt;

&lt;p&gt;After wasting an entire Saturday on broken Fruition configs and reading through GitHub issues that went nowhere, I just stopped.&lt;/p&gt;

&lt;p&gt;Wait. I'm a developer. I know how reverse proxies work. Why am I fighting someone else's broken code when I could just... write my own?&lt;/p&gt;

&lt;p&gt;So that's what I did. I called it &lt;strong&gt;Nooxy&lt;/strong&gt; because all the good names were taken and I was tired.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Nooxy?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/draphy/nooxy" rel="noopener noreferrer"&gt;Nooxy&lt;/a&gt; is a Notion reverse proxy. TypeScript, zero dependencies, works on Cloudflare Workers or Node.js. You point it at your Notion pages, it serves them on your domain.&lt;/p&gt;

&lt;p&gt;What's actually different about it:&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero Dependencies
&lt;/h3&gt;

&lt;p&gt;I went a bit obsessive here, but no regrets. Zero runtime dependencies means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster cold starts&lt;/strong&gt; on edge computing platforms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smaller bundle size&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No security vulnerabilities&lt;/strong&gt; from third-party packages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No dependency conflicts&lt;/strong&gt; or version mismatches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you deploy to Cloudflare Workers, it's just your code. No bloat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Works Where You Need It
&lt;/h3&gt;

&lt;p&gt;Runs on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers&lt;/strong&gt; (what I use)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js 18.17+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Anything with &lt;code&gt;Request&lt;/code&gt;/&lt;code&gt;Response&lt;/code&gt; support really&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The 5-Minute Setup
&lt;/h3&gt;

&lt;p&gt;If you already have a Notion page published, you can have your custom domain working in under 5 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install nooxy&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;nooxy

&lt;span class="c"&gt;# 2. Initialize configuration&lt;/span&gt;
npx nooxy init

&lt;span class="c"&gt;# 3. Edit your config (add your domain and Notion page IDs)&lt;/span&gt;
&lt;span class="c"&gt;# 4. Generate required files&lt;/span&gt;
npx nooxy generate

&lt;span class="c"&gt;# 5. Deploy to Cloudflare Workers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's literally it. I spent more time picking the domain name than setting this up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stuff That Actually Matters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Clean URLs with Slug Mapping
&lt;/h3&gt;

&lt;p&gt;Instead of ugly Notion URLs like &lt;code&gt;notion.so/My-Page-abc123def456...&lt;/code&gt;, you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;slugToPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_HOME_PAGE_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_ABOUT_PAGE_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/projects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_PROJECTS_PAGE_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_BLOG_PAGE_ID&lt;/span&gt;&lt;span class="dl"&gt;'&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;Your visitors see &lt;code&gt;yourdomain.com/about&lt;/code&gt;, not a 32-character hash.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Customization Control
&lt;/h3&gt;

&lt;p&gt;Inject custom CSS, JavaScript, and HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* nooxy/head.css */&lt;/span&gt;
&lt;span class="nc"&gt;.notion-topbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Hide Notion's default navigation */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.notion-page-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&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;Add custom functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nooxy/body.js&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Add smooth scrolling, analytics, custom interactions... whatever you need&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Notion site, your rules.&lt;/span&gt;&lt;span class="dl"&gt;'&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;h3&gt;
  
  
  SEO That Doesn't Suck
&lt;/h3&gt;

&lt;p&gt;Notion's SEO is pretty bad out of the box. With Nooxy you can actually control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Meta tags per page&lt;/strong&gt;: Titles, descriptions, the works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Graph stuff&lt;/strong&gt;: So your links don't look terrible when shared&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter Cards&lt;/strong&gt;: Same deal for Twitter/X&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON-LD&lt;/strong&gt;: For the SEO nerds (I am one)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pageMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_PAGE_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'My Custom Title - Draphy',&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'A description optimized for search engines',&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourdomain.com/og-image.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;David Raphi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&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;h3&gt;
  
  
  Local Development Support
&lt;/h3&gt;

&lt;p&gt;Test your site locally before deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Nooxy automatically detects localhost and adjusts&lt;/span&gt;
&lt;span class="c1"&gt;// No special configuration needed&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeNooxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SITE_CONFIG&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;wrangler dev&lt;/code&gt; and see your changes instantly at &lt;code&gt;localhost:8787&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Instance Support
&lt;/h3&gt;

&lt;p&gt;Running multiple Notion sites? Nooxy supports that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prodProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeNooxy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;configKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productionConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stagingProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeNooxy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;configKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stagingConfig&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;Each instance gets its own cache and configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic Minification
&lt;/h3&gt;

&lt;p&gt;The CLI automatically minifies your custom CSS, JavaScript, and HTML during the build process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nooxy generate  &lt;span class="c"&gt;# Minifies by default&lt;/span&gt;
npx nooxy generate &lt;span class="nt"&gt;--no-minify&lt;/span&gt;  &lt;span class="c"&gt;# Skip minification if needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Stacks Up (Honestly)
&lt;/h2&gt;

&lt;p&gt;Look, I built this thing so I'm biased. But here's the actual comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Nooxy&lt;/th&gt;
&lt;th&gt;Fruition&lt;/th&gt;
&lt;th&gt;NoteHost&lt;/th&gt;
&lt;th&gt;Notion Sites&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$18-22/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom Domain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom CSS/JS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SEO Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zero Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Actively Maintained&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (189 issues)&lt;/td&gt;
&lt;td&gt;Partially&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLI Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Local Dev Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-instance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Use Nooxy If...
&lt;/h3&gt;

&lt;p&gt;You'll probably like it if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't want to pay monthly for something this simple&lt;/li&gt;
&lt;li&gt;Want to actually customize your site (CSS, JS, whatever)&lt;/li&gt;
&lt;li&gt;Care about SEO&lt;/li&gt;
&lt;li&gt;Like testing locally before deploying&lt;/li&gt;
&lt;li&gt;Are fine with Cloudflare Workers or Node.js&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Skip Nooxy If...
&lt;/h3&gt;

&lt;p&gt;Not gonna pretend it's for everyone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You hate terminals (you'll need to run a few commands)&lt;/li&gt;
&lt;li&gt;You want official Notion support (this is just me)&lt;/li&gt;
&lt;li&gt;You're not using Cloudflare or Node&lt;/li&gt;
&lt;li&gt;You need fancy analytics dashboards built-in&lt;/li&gt;
&lt;li&gt;You need visitors to see real-time edits or collaboration stuff&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What About Just Paying Notion?
&lt;/h3&gt;

&lt;p&gt;Fair question. Notion Sites isn't terrible if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have the budget ($18-22/month adds up though)&lt;/li&gt;
&lt;li&gt;Don't care about custom styling&lt;/li&gt;
&lt;li&gt;Just want it to work with zero effort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing wrong with that. I just didn't want another monthly subscription for something I could build in a weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It's Going For Me
&lt;/h2&gt;

&lt;p&gt;Been running &lt;a href="https://os.draphy.org" rel="noopener noreferrer"&gt;os.draphy.org&lt;/a&gt; on Nooxy for a while now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;: Took like 5 minutes. Most of that was finding my Notion page IDs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed&lt;/strong&gt;: It's on Cloudflare's edge network so it's fast everywhere. Haven't had complaints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: Basically none. I forget it's even running most of the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customization&lt;/strong&gt;: I've got my own nav, custom styles, proper SEO. Stuff that Notion just won't let you do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Want to try it? Here's the quick version:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install and Initialize
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;nooxy
npx nooxy init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;nooxy/&lt;/code&gt; directory with all configuration files.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configure Your Site
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;nooxy/config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SITE_CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yourdomain.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;notionDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-workspace.notion.site&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;siteName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Site Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;slugToPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_HOME_PAGE_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_ABOUT_PAGE_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// These are auto-generated, just import them&lt;/span&gt;
  &lt;span class="na"&gt;customHeadCSS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HEAD_CSS_STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;customHeadJS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HEAD_JS_STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;customBodyJS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BODY_JS_STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;customHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HEADER_HTML_STRING&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;h3&gt;
  
  
  3. Find Your Notion Page IDs
&lt;/h3&gt;

&lt;p&gt;Open your Notion page → Share → Copy link. The ID is the 32-character string at the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;notion.so/My-Page-Title-abc123def456789012345678901234
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        This is your page ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Generate and Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nooxy generate  &lt;span class="c"&gt;# Creates minified string files&lt;/span&gt;

&lt;span class="c"&gt;# Then deploy to Cloudflare Workers&lt;/span&gt;
wrangler deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Point Your Domain
&lt;/h3&gt;

&lt;p&gt;In Cloudflare, add a CNAME record pointing to your Worker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built It This Way
&lt;/h2&gt;

&lt;p&gt;A few things I kept in mind:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't overbuild.&lt;/strong&gt; A personal site doesn't need a CMS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependencies are debt.&lt;/strong&gt; Every package you add is a future breaking change waiting to happen. I wanted something I wouldn't have to fix every time some library updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make it actually usable.&lt;/strong&gt; CLI tools, TypeScript, local dev. Not because they're trendy but because I actually wanted to use this thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Found a Bug? Want to Contribute?
&lt;/h2&gt;

&lt;p&gt;It's open source (MIT). PRs welcome.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code&lt;/strong&gt;: &lt;a href="https://github.com/draphy/nooxy" rel="noopener noreferrer"&gt;github.com/draphy/nooxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues&lt;/strong&gt;: &lt;a href="https://github.com/draphy/nooxy/issues" rel="noopener noreferrer"&gt;github.com/draphy/nooxy/issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discussions&lt;/strong&gt;: &lt;a href="https://github.com/draphy/nooxy/discussions" rel="noopener noreferrer"&gt;github.com/draphy/nooxy/discussions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;I wanted a personal website. I almost spent weeks building a CMS I didn't need. Then I almost paid Notion $20/month for features I could build myself.&lt;/p&gt;

&lt;p&gt;Instead I spent a weekend building Nooxy. Now my site runs for free, looks how I want, and I don't think about it.&lt;/p&gt;

&lt;p&gt;If you use Notion and want a custom domain without the subscription, maybe give it a try. Five minutes, free, you own everything.&lt;/p&gt;

&lt;p&gt;If it's useful, &lt;a href="https://github.com/draphy/nooxy" rel="noopener noreferrer"&gt;star it on GitHub&lt;/a&gt;. If it's broken, open an issue. Either way, thanks for reading.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the author&lt;/strong&gt;: David Raphi is a full-stack engineer who's shipped systems for US government agencies (Florida DCF, Republican Party of Florida) and led teams delivering election-critical platforms in 1-week sprints. Lives in Cloudflare's stack and has a thing for zero-dependency packages (PushForge is another one).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/draphy" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://x.com/draphyofficial" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; · &lt;a href="https://linkedin.com/in/draphy" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="mailto:contact@draphy.org"&gt;contact@draphy.org&lt;/a&gt; · &lt;a href="mailto:david@draphy.org"&gt;david@draphy.org&lt;/a&gt;&lt;/p&gt;




</description>
      <category>opensource</category>
      <category>notion</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🚀PushForge: Modern Web Push Notifications Made Simple🚀</title>
      <dc:creator>David Raphi</dc:creator>
      <pubDate>Tue, 22 Apr 2025 21:44:30 +0000</pubDate>
      <link>https://dev.to/draphy/pushforge-modern-web-push-notifications-made-simple-599p</link>
      <guid>https://dev.to/draphy/pushforge-modern-web-push-notifications-made-simple-599p</guid>
      <description>&lt;p&gt;I'm thrilled to introduce &lt;strong&gt;PushForge v1.0.0&lt;/strong&gt;, my latest open-source contribution to the web development ecosystem. PushForge is a zero-dependency toolkit designed to make Web Push Notifications implementation seamless across multiple platforms including Node.js, Browsers, Deno, Bun, and even Cloudflare Workers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/draphy/pushforge" rel="noopener noreferrer"&gt;github.com/draphy/pushforge&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/org/pushforge" rel="noopener noreferrer"&gt;npmjs.com/org/pushforge&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;builder&lt;/strong&gt;: &lt;a href="https://npmjs.com/package/@pushforge/builder" rel="noopener noreferrer"&gt;npmjs.com/package/@pushforge/builder&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I Built PushForge ❓
&lt;/h2&gt;

&lt;p&gt;When my team needed push notifications for a project, we discovered existing libraries were unnecessarily heavy and restrictive, especially for edge computing environments. This constraint led us to develop a custom solution, which eventually inspired me to create a more comprehensive package with broader compatibility.&lt;/p&gt;

&lt;p&gt;PushForge addresses these challenges by providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A lightweight solution with zero external dependencies&lt;/li&gt;
&lt;li&gt;Cross-platform compatibility for modern JavaScript environments&lt;/li&gt;
&lt;li&gt;TypeScript-first development for robust applications&lt;/li&gt;
&lt;li&gt;Complete control over the notification process&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔑 Key Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cross-Platform Compatibility
&lt;/h3&gt;

&lt;p&gt;PushForge works seamlessly across multiple environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; (v16+)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web Browsers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deno&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bun&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare Workers&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This versatility ensures that regardless of your tech stack, you can implement push notifications without platform-specific workarounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero Dependencies
&lt;/h3&gt;

&lt;p&gt;PushForge is engineered to be lightweight and self-contained, making it especially valuable for edge computing environments where bundle size is critical. By eliminating external dependencies, we reduce potential security vulnerabilities and ensure a smaller footprint in your projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  TypeScript-First Design
&lt;/h3&gt;

&lt;p&gt;The entire library is built with TypeScript, providing comprehensive type definitions out of the box. This approach delivers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved developer experience with autocomplete suggestions&lt;/li&gt;
&lt;li&gt;Type safety to catch errors during development&lt;/li&gt;
&lt;li&gt;Better documentation through type annotations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Complete VAPID Authentication &amp;amp; Web Push Protocol Support
&lt;/h3&gt;

&lt;p&gt;PushForge implements the full Web Push Protocol specification, ensuring your notifications are secure and compatible with all modern browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Technical Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Web Push Protocol Implementation
&lt;/h3&gt;

&lt;p&gt;PushForge implements several critical components of the Web Push Protocol:&lt;/p&gt;

&lt;h4&gt;
  
  
  VAPID Authentication (JWT with ES256)
&lt;/h4&gt;

&lt;p&gt;PushForge uses the ES256 algorithm (ECDSA using P-256 curve and SHA-256 hash) to create and verify JSON Web Tokens (JWTs) for Voluntary Application Server Identification (VAPID). This process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generates a JWT using the server's private key&lt;/li&gt;
&lt;li&gt;Signs it with the ES256 algorithm&lt;/li&gt;
&lt;li&gt;Includes the server's contact information&lt;/li&gt;
&lt;li&gt;Sets appropriate expiration times (up to 24 hours)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resulting authorization header allows push services to validate that your server is authorized to send notifications to the user.&lt;/p&gt;

&lt;h4&gt;
  
  
  ECDH Key Exchange
&lt;/h4&gt;

&lt;p&gt;Elliptic Curve Diffie-Hellman (ECDH) key exchange enables secure communication between your server and the user's browser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The browser generates a public/private key pair during subscription&lt;/li&gt;
&lt;li&gt;Your server receives the public key (&lt;code&gt;p256dh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;PushForge uses ECDH to create a shared secret using your server's generated key pair and the user's public key&lt;/li&gt;
&lt;li&gt;This shared secret becomes the basis for encryption&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  HKDF Key Derivation
&lt;/h4&gt;

&lt;p&gt;HMAC-based Key Derivation Function (HKDF) transforms the shared ECDH secret into appropriate encryption keys:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Combines the shared secret with the authentication secret (&lt;code&gt;auth&lt;/code&gt;) provided by the browser&lt;/li&gt;
&lt;li&gt;Uses a salt value (randomly generated for each message)&lt;/li&gt;
&lt;li&gt;Derives encryption key and nonce (initialization vector) for AES-GCM&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  AES-GCM Encryption
&lt;/h4&gt;

&lt;p&gt;Advanced Encryption Standard with Galois/Counter Mode (AES-GCM):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Encrypts the notification payload using the derived encryption key&lt;/li&gt;
&lt;li&gt;Provides authenticated encryption (data integrity and confidentiality)&lt;/li&gt;
&lt;li&gt;Incorporates appropriate padding to obscure message length&lt;/li&gt;
&lt;li&gt;Outputs the encrypted content ready for transmission&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Compliant Implementation Details
&lt;/h4&gt;

&lt;p&gt;PushForge follows RFC 8291 (Message Encryption for Web Push) standards, ensuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content-Encoding: aes128gcm format&lt;/li&gt;
&lt;li&gt;Proper salt generation and record size limits&lt;/li&gt;
&lt;li&gt;Correct padding implementation&lt;/li&gt;
&lt;li&gt;Authentication tag handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚒️ Development Workflow
&lt;/h2&gt;

&lt;p&gt;PushForge employs modern development tools to ensure code quality and streamlined contributions:&lt;/p&gt;

&lt;h3&gt;
  
  
  Semantic Release
&lt;/h3&gt;

&lt;p&gt;Automated versioning and changelog generation based on conventional commit messages, which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Determines version numbers automatically (major, minor, patch)&lt;/li&gt;
&lt;li&gt;Generates detailed release notes&lt;/li&gt;
&lt;li&gt;Publishes updates to npm when merging to main branch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GitHub Actions for CI/CD
&lt;/h3&gt;

&lt;p&gt;Continuous integration and deployment pipelines that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the test suite on every pull request&lt;/li&gt;
&lt;li&gt;Verify code formatting and linting&lt;/li&gt;
&lt;li&gt;Ensure type checking passes&lt;/li&gt;
&lt;li&gt;Automate the release process&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Biome for Code Quality
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://biomejs.dev/" rel="noopener noreferrer"&gt;Biome&lt;/a&gt; provides fast, modern formatting and linting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enforces consistent code style&lt;/li&gt;
&lt;li&gt;Catches potential bugs early&lt;/li&gt;
&lt;li&gt;Runs as part of pre-commit checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing with Vitest
&lt;/h3&gt;

&lt;p&gt;Comprehensive test coverage using &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests for core functionality&lt;/li&gt;
&lt;li&gt;Integration tests for different environments&lt;/li&gt;
&lt;li&gt;Fast execution through Vite's architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Management with Linear
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://linear.app/" rel="noopener noreferrer"&gt;Linear&lt;/a&gt; helps track issues and plan development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages the feature roadmap&lt;/li&gt;
&lt;li&gt;Coordinates sprint planning&lt;/li&gt;
&lt;li&gt;Keeps issue backlog organized&lt;/li&gt;
&lt;li&gt;Integrates with GitHub for streamlined workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💡 When to Use PushForge
&lt;/h2&gt;

&lt;p&gt;PushForge is particularly well-suited for projects that require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edge Computing Compatibility&lt;/strong&gt;: Deploy to serverless or edge environments without worry about dependencies or platform limitations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript Projects&lt;/strong&gt;: Benefit from full type safety and improved developer experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Push Logic&lt;/strong&gt;: Implement complex notification strategies with full control over the process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform Solutions&lt;/strong&gt;: Build unified notification systems that work across different JavaScript environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📈 Future Plans
&lt;/h2&gt;

&lt;p&gt;I'm actively working on expanding PushForge with these planned features:&lt;/p&gt;

&lt;h3&gt;
  
  
  Batching &amp;amp; Queuing System
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Efficient bulk notification delivery&lt;/li&gt;
&lt;li&gt;Rate limiting and automatic retry mechanisms&lt;/li&gt;
&lt;li&gt;Priority-based queuing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Built-in Request Functionality
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Currently, you provide your own fetch implementation&lt;/li&gt;
&lt;li&gt;Future versions will include built-in request handling&lt;/li&gt;
&lt;li&gt;Support for automatic error handling and retries&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Legacy GCM Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Backward compatibility for older notification systems&lt;/li&gt;
&lt;li&gt;Support for Google Cloud Messaging legacy endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comprehensive Examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ready-to-use examples for popular frameworks (React, Vue, Angular, etc.)&lt;/li&gt;
&lt;li&gt;Server-side implementations for Node.js, Express, Fastify, etc.&lt;/li&gt;
&lt;li&gt;Service worker templates for quick implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started with PushForge
&lt;/h2&gt;

&lt;p&gt;Here's a quick guide to implementing PushForge in your project:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Installation
&lt;/h3&gt;

&lt;p&gt;Choose your preferred package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# NPM&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @pushforge/builder

&lt;span class="c"&gt;# Yarn&lt;/span&gt;
yarn add @pushforge/builder

&lt;span class="c"&gt;# pnpm&lt;/span&gt;
pnpm add @pushforge/builder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Generate VAPID Keys
&lt;/h3&gt;

&lt;p&gt;Use the built-in CLI tool to generate your VAPID keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @pushforge/builder generate-vapid-keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output a public key and private key that you'll use for authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Send Notifications from Your Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buildPushHTTPRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pushforge/builder&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Load the private key from your secure environment&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;privateJWK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VAPID_PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// User subscription from browser push API&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://fcm.googleapis.com/fcm/send/DEVICE_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;p256dh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USER_PUBLIC_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USER_AUTH_SECRET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Create message with payload&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New Message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You have a new message!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/images/icon.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Time-to-live in seconds&lt;/span&gt;
    &lt;span class="na"&gt;urgency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Options: "very-low", "low", "normal", "high"&lt;/span&gt;
    &lt;span class="na"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;updates&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Optional topic for replacing notifications&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;adminContact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mailto:admin@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Contact information&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Build the push notification request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buildPushHTTPRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;privateJWK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Send the push notification&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;body&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;h2&gt;
  
  
  🫂 Join the Community
&lt;/h2&gt;

&lt;p&gt;Contributions to PushForge are welcome and encouraged! The project follows a structured workflow for contributions as outlined in the &lt;a href="https://github.com/draphy/pushforge/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contributing Guidelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Whether you want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Report bugs or suggest features&lt;/li&gt;
&lt;li&gt;Improve tests or documentation&lt;/li&gt;
&lt;li&gt;Submit pull requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how to contribute:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an issue in the GitHub tracker describing your proposed change&lt;/li&gt;
&lt;li&gt;Wait for a Linear issue number to be assigned&lt;/li&gt;
&lt;li&gt;Fork the repository and create a branch with the proper naming convention:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   username/wpn-issuenumber-issuetitle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Make your changes following our &lt;a href="https://github.com/draphy/pushforge/blob/main/CONTRIBUTING.md#6-commit-guidelines" rel="noopener noreferrer"&gt;commit guidelines&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Submit a pull request linking to the issue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's build the best push notification solution together! 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  📖 References
&lt;/h2&gt;

&lt;p&gt;My work on PushForge was informed by these key resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc8291" rel="noopener noreferrer"&gt;RFC 8291: Message Encryption for Web Push&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API" rel="noopener noreferrer"&gt;Web Crypto API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Using_VAPID_keys_for_application_server_key" rel="noopener noreferrer"&gt;VAPID Keys for Web Push&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/rfc7518#section-3.4" rel="noopener noreferrer"&gt;JSON Web Algorithms (RFC 7518)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman" rel="noopener noreferrer"&gt;Elliptic‑curve Diffie–Hellman&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc5869" rel="noopener noreferrer"&gt;HKDF (RFC 5869)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Encryption/Using_AES-GCM" rel="noopener noreferrer"&gt;AES‑GCM Encryption&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I'm excited to see what you'll build with PushForge! Feel free to star the repository, open issues, or reach out with questions. Together, we can make web push notifications simpler and more accessible for developers everywhere.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>npm</category>
    </item>
  </channel>
</rss>
