<?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: wayknow</title>
    <description>The latest articles on DEV Community by wayknow (@wayknow123).</description>
    <link>https://dev.to/wayknow123</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%2F4049103%2F90366d7b-f4a8-41ee-aa6f-f7cbc7454ed9.png</url>
      <title>DEV Community: wayknow</title>
      <link>https://dev.to/wayknow123</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wayknow123"/>
    <language>en</language>
    <item>
      <title>I shipped my first iOS app solo — App Store Connect was harder than the app</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Wed, 23 Sep 2026 06:48:41 +0000</pubDate>
      <link>https://dev.to/wayknow123/i-shipped-my-first-ios-app-solo-app-store-connect-was-harder-than-the-app-3cnk</link>
      <guid>https://dev.to/wayknow123/i-shipped-my-first-ios-app-solo-app-store-connect-was-harder-than-the-app-3cnk</guid>
      <description>&lt;p&gt;I just shipped &lt;a href="https://apps.apple.com/us/app/snapcal-barcode-calorie-log/id6812662105" rel="noopener noreferrer"&gt;SnapCal&lt;/a&gt;, a barcode calorie tracker I built solo in about two weeks: SwiftUI, iOS 17+, zero third-party dependencies, 85 unit tests. Building it was fine. Getting it &lt;em&gt;out the door&lt;/em&gt; taught me things no tutorial covers.&lt;/p&gt;

&lt;p&gt;This is the list of what actually bit me — five traps, with the code and the commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The 404 that made my app look broken
&lt;/h2&gt;

&lt;p&gt;SnapCal scans a barcode, asks &lt;a href="https://world.openfoodfacts.org" rel="noopener noreferrer"&gt;Open Food Facts&lt;/a&gt; about it, then lets you log it. My HTTP layer was the standard shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;validateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URLResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;httpResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;HTTPURLResponse&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kt"&gt;APIError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invalidResponse&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;guard&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="mi"&gt;299&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kt"&gt;APIError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;httpError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;httpResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statusCode&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;p&gt;And &lt;code&gt;APIError.httpError&lt;/code&gt; rendered as &lt;code&gt;"Server error (404)"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's the thing: Open Food Facts returns &lt;strong&gt;HTTP 404&lt;/strong&gt; with &lt;code&gt;{"status":0,"status_verbose":"product not found"}&lt;/code&gt; for any barcode that is well-formed but not in the database. That's not a failure — it's the most common answer you'll get. Coverage is heavily skewed by country:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Region&lt;/th&gt;
&lt;th&gt;Products in OFF&lt;/th&gt;
&lt;th&gt;Have calorie data (sample of 100)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Global&lt;/td&gt;
&lt;td&gt;4,759,550&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;France&lt;/td&gt;
&lt;td&gt;1,268,282&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USA&lt;/td&gt;
&lt;td&gt;972,695&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Japan&lt;/td&gt;
&lt;td&gt;44,641&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;China&lt;/td&gt;
&lt;td&gt;1,683&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the &lt;em&gt;expected&lt;/em&gt; outcome for a random package is "no data", and my app was greeting users with a red "Server error (404)". A tester told me: "some barcodes work, some say something went wrong — your server logic is broken." He was right, but not the way he thought: the API was fine, my error taxonomy was wrong.&lt;/p&gt;

&lt;p&gt;The fix is one early return, before validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;withRetry&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// OFF returns 404 for "well-formed barcode, not in the database" — that's&lt;/span&gt;
&lt;span class="c1"&gt;// "nothing found", not "something is wrong". Return nil so the UI can offer&lt;/span&gt;
&lt;span class="c1"&gt;// manual entry instead of a scary server error.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;httpResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;HTTPURLResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httpResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="nf"&gt;validateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the view, "not found" gets its own state with a way forward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;food&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchByBarcode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;barcode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;scannedFood&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;food&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorMessage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c1"&gt;// nil meaning: no error, just no data&lt;/span&gt;
    &lt;span class="n"&gt;showNotFoundAlert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;            &lt;span class="c1"&gt;// "Product not found" + [Add Manually]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I went one step further: when you type in a food that isn't in the database, SnapCal stores it under that barcode in a dictionary of its own (&lt;code&gt;userFoodsByBarcode&lt;/code&gt;, kept separate from the API cache so it never gets evicted). Scan the same product again and it resolves instantly, offline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; a "not found" is a state, not an error. If your empty state is dressed up as a failure, users will conclude your app is broken — and they'll be right about the experience even if they're wrong about the cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. New developer accounts get asked for homework (Guideline 2.1)
&lt;/h2&gt;

&lt;p&gt;The morning after I submitted 1.0, I got "Information Needed — Guideline 2.1". No bug, no rejection of the app itself. Apple wanted:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a screen recording on a &lt;strong&gt;physical device&lt;/strong&gt;, running the latest OS, starting from app launch&lt;/li&gt;
&lt;li&gt;purpose and target audience&lt;/li&gt;
&lt;li&gt;setup instructions / demo credentials&lt;/li&gt;
&lt;li&gt;every external service the app talks to&lt;/li&gt;
&lt;li&gt;regional differences&lt;/li&gt;
&lt;li&gt;anything regulated or third-party-licensed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your app has accounts, UGC or paid content, the recording must show those flows — including account &lt;em&gt;deletion&lt;/em&gt;, which is a hard requirement.&lt;/p&gt;

&lt;p&gt;What I'd do differently: &lt;strong&gt;record that video before you submit.&lt;/strong&gt; Mine was 3 minutes on an iPhone 15 Pro, and it was the only thing that took real time to produce after the fact. Everything else is copy: "no accounts, no login, no UGC, no IAP, so those flows don't exist; the only external service is Open Food Facts; nutrition data is ODbL v1.0."&lt;/p&gt;

&lt;p&gt;Reply in the App Review conversation &lt;strong&gt;and&lt;/strong&gt; paste the same text into &lt;code&gt;App Review Information → Notes&lt;/code&gt; — Apple asks for both, and the notes carry over to future submissions. No new build is needed for a 2.1 reply; you just resubmit.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. "Approved" is not "live", and my check was lying to me
&lt;/h2&gt;

&lt;p&gt;I released 1.0, and then… nothing appeared. So I "verified" the store page by fetching it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://apps.apple.com/us/app/snapcal-barcode-calorie-log/id6812662105  → HTTP 200 ✅
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was not the app page. My IP geolocates to a storefront where the app wasn't distributed yet, and instead of 404ing, Apple quietly serves that storefront's &lt;em&gt;Today&lt;/em&gt; home page. &lt;strong&gt;The status code tells you the server answered — it doesn't tell you which page you got.&lt;/strong&gt; Read the title and the content, or ask Apple's lookup endpoint about your bundle ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://itunes.apple.com/lookup?bundleId=tech.wayknow.snapcal&amp;amp;country=us"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even then, treat any single response as a hint rather than a fact — that index lags behind the storefronts and can be noisy. The ground truth for availability is &lt;code&gt;App Store Connect → Pricing and Availability&lt;/code&gt;: mine listed 174 of 175 territories, with mainland China the only exception.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The app was missing from all of Europe, and nothing warned me
&lt;/h2&gt;

&lt;p&gt;A couple of days after release, App Store Connect was still showing a banner I'd scrolled past: the &lt;strong&gt;EU Digital Services Act&lt;/strong&gt; requires Apple to verify and display trader contact information for apps distributed in the EU, and an app with no declared trader status isn't distributed there at all. SnapCal simply wasn't reachable in any EU storefront.&lt;/p&gt;

&lt;p&gt;The declaration lives at &lt;code&gt;Business → Agreements → Compliance → Digital Services Act&lt;/code&gt;. Two choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"This is a trader account"&lt;/strong&gt; — you enter address, phone and email, they get &lt;strong&gt;published on your App Store product page in the EU&lt;/strong&gt;, you upload a document proving your business name and address, and you verify both email and phone with 2FA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"This is not a trader account"&lt;/strong&gt; — no contact information, click Done, you're finished.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SnapCal 1.0 is free, has no ads and no in-app purchases, so "not a trader" is the honest answer, and it's reversible. But the moment you sell anything — even a one-time unlock — you are a trader, and your address goes public across the EU. Worth deciding &lt;em&gt;before&lt;/em&gt; you monetize, not after.&lt;/p&gt;

&lt;p&gt;The storefronts came back within a day of declaring, and they came back on their own — no resubmission, no new build. SnapCal is now available in every storefront except mainland China, which needs an ICP filing.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Two submission-day traps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A pending agreement silently blocks every submission.&lt;/strong&gt; Apple updated the Developer Program License Agreement. There's a banner about it on the Business page, and until the account holder reviews and accepts it, you cannot submit a new version. No error on the version page — the submit just isn't available. Fix: &lt;code&gt;Business → Agreements → the "Free Apps" row → Review and Accept Terms&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Xcode Organizer can't see your archive.&lt;/strong&gt; I built 1.0.1 with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcodebuild archive &lt;span class="nt"&gt;-project&lt;/span&gt; SnapCal.xcodeproj &lt;span class="nt"&gt;-scheme&lt;/span&gt; SnapCal &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-destination&lt;/span&gt; &lt;span class="s1"&gt;'generic/platform=iOS'&lt;/span&gt; &lt;span class="nt"&gt;-archivePath&lt;/span&gt; build/SnapCal.xcarchive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Organizer only lists archives in &lt;code&gt;~/Library/Developer/Xcode/Archives/&lt;/code&gt;. So when I opened &lt;code&gt;Distribute App → Upload&lt;/code&gt;, the only archive on offer was the &lt;em&gt;old&lt;/em&gt; 1.0 build, and Apple rejected it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This bundle is invalid. The value for key CFBundleShortVersionString [1.0.0]
must contain a higher version than that of the previously approved version [1.0.0]  (90062)
Invalid Pre-Release Train. The train version '1.0.0' is closed for new build submissions  (90186)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the archive into the folder Organizer watches, or just archive to the default location. And always confirm the version/build number Organizer shows before you hit Upload.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell my past self
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Treat 404 as data, not failure — and give "not found" a real UI.&lt;/li&gt;
&lt;li&gt;Record the App Review video before submitting; new accounts get asked.&lt;/li&gt;
&lt;li&gt;Verify launch by reading the page, not the status code — and check availability in App Store Connect, not in a public API that lags.&lt;/li&gt;
&lt;li&gt;Declare the DSA trader status before launch; "not a trader" is reversible, "no declaration" costs you the EU.&lt;/li&gt;
&lt;li&gt;Accept pending agreements and double-check which archive you're uploading.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this is in the app. All of it decided whether anyone could install it.&lt;/p&gt;




&lt;p&gt;SnapCal is free: barcode scanning, no ads, no account, and everything stays on your device. It's available in every storefront except mainland China. 1.0.1 (recently- logged foods in Quick Add, one-tap delete, better "not in the database" handling) shipped on 2026-09-23; 1.0.2 followed on 2026-09-24 and only adds a one-time rating prompt (after 3 days and 5 logged items, once per version). If you want to poke holes in it, I'm around — the App Store link is &lt;a href="https://apps.apple.com/us/app/snapcal-barcode-calorie-log/id6812662105" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post also lives on the &lt;a href="https://wayknow.tech/blog/snapcal-app-store-connect-gotchas.html" rel="noopener noreferrer"&gt;WayKnow blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>indidev</category>
      <category>appstore</category>
    </item>
    <item>
      <title>Building a Privacy-First Cookie Editor: CHIPS, Interceptors &amp; Auto-Cleanup in MV3</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Mon, 24 Aug 2026 06:27:15 +0000</pubDate>
      <link>https://dev.to/wayknow123/building-a-privacy-first-cookie-editor-chips-interceptors-auto-cleanup-in-mv3-4j38</link>
      <guid>https://dev.to/wayknow123/building-a-privacy-first-cookie-editor-chips-interceptors-auto-cleanup-in-mv3-4j38</guid>
      <description>&lt;p&gt;CrumbKit is a free, open-source cookie editor for Chrome. We released v1.2 in August 2026 with three features that required non-obvious Manifest V3 implementation work: &lt;strong&gt;CHIPS partitioned cookie support&lt;/strong&gt;, a &lt;strong&gt;Set-Cookie response interceptor&lt;/strong&gt;, and &lt;strong&gt;scheduled auto-cleanup rules&lt;/strong&gt;. Here's how we built each one — and what we learned.&lt;/p&gt;

&lt;p&gt;Full disclosure: I built CrumbKit. Everything below is from the actual codebase — no marketing fluff, no hand-waving.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. CHIPS Partitioned Cookies — The New Third-Party Cookie
&lt;/h2&gt;

&lt;p&gt;Chrome is phasing out third-party cookies in favor of &lt;strong&gt;CHIPS (Cookies Having Independent Partitioned State)&lt;/strong&gt;. A partitioned cookie is scoped to a specific top-level site — two different websites can each have their own cookie with the same name and domain, but they're completely isolated from each other.&lt;/p&gt;

&lt;p&gt;This is a big deal for cookie editors because it means the traditional dedup key — &lt;code&gt;name + domain + path&lt;/code&gt; — is no longer unique. Without handling &lt;code&gt;partitionKey&lt;/code&gt;, you'd silently overwrite or lose cookies.&lt;/p&gt;

&lt;h3&gt;
  
  
  How we implemented it
&lt;/h3&gt;

&lt;p&gt;The change touched four layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cookie normalization:&lt;/strong&gt; We added &lt;code&gt;partitionKey&lt;/code&gt; to the normalized cookie object:&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;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other fields&lt;/span&gt;
    &lt;span class="na"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;partitionKey&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&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;&lt;strong&gt;Deduplication:&lt;/strong&gt; The dedup key now includes the partition key. Two cookies with the same name/domain/path but different top-level sites are treated as distinct:&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;partitionKey&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Export formats:&lt;/strong&gt; All six export formats had to be updated. JSON includes the full &lt;code&gt;partitionKey&lt;/code&gt; object. CSV serializes it as a JSON string. Set-Cookie headers add the &lt;code&gt;Partitioned&lt;/code&gt; attribute. Puppeteer scripts pass the &lt;code&gt;partitionKey&lt;/code&gt; through. The key principle: partition data is never silently dropped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI:&lt;/strong&gt; Partitioned cookies show a blue "P" badge in the cookie list. The edit form displays the top-level site as read-only text with a hint: "(CHIPS — set by server)". Users can't edit the partition key because it's determined by the browser, not the cookie itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we learned
&lt;/h3&gt;

&lt;p&gt;Chrome's &lt;code&gt;chrome.cookies&lt;/code&gt; API returns &lt;code&gt;partitionKey&lt;/code&gt; on partitioned cookies, but most cookie editor tutorials and existing tools don't handle it. CookieJar was the only competitor we found with full CHIPS support. The lack of awareness around partitioned cookies is a real gap — as Chrome rolls out CHIPS more broadly, tools that don't handle it will silently produce incorrect exports.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Set-Cookie Interceptor — Watching the Network
&lt;/h2&gt;

&lt;p&gt;One thing we noticed: developers often want to see what cookies a server is setting via &lt;code&gt;Set-Cookie&lt;/code&gt; response headers, but Chrome's DevTools buries this in the Network tab. We wanted a one-click way to see, inspect, and add intercepted cookies.&lt;/p&gt;

&lt;h3&gt;
  
  
  The MV3 challenge
&lt;/h3&gt;

&lt;p&gt;In Manifest V3, you can't use &lt;code&gt;chrome.webRequest.onHeadersReceived&lt;/code&gt; to modify requests. But you &lt;em&gt;can&lt;/em&gt; use it to &lt;strong&gt;observe&lt;/strong&gt; them. The key is the &lt;code&gt;extraInfoSpec&lt;/code&gt; parameter:&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onHeadersReceived&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// details.responseHeaders contains Set-Cookie headers&lt;/span&gt;
    &lt;span class="k"&gt;for &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;header&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseHeaders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;set-cookie&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;// Parse and store the intercepted cookie&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;urls&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;&amp;lt;all_urls&amp;gt;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;responseHeaders&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;The &lt;code&gt;['responseHeaders']&lt;/code&gt; extraInfoSpec tells Chrome to include response headers in the event. Without it, you only get the request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data storage
&lt;/h3&gt;

&lt;p&gt;We store intercepted cookies in &lt;code&gt;chrome.storage.session&lt;/code&gt; — a volatile store that's cleared when the browser restarts. This is intentional: intercepted cookies are transient data (they're what the server &lt;em&gt;tried&lt;/em&gt; to set, not what's actually in the browser). The store is capped at 50 entries to prevent bloat.&lt;/p&gt;

&lt;p&gt;The service worker runs as a persistent-ish background script (MV3 service workers can be terminated), so we use a keep-alive port pattern to maintain the interceptor connection when the popup is open.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we learned
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;chrome.webRequest&lt;/code&gt; API is read-only in MV3, which is fine for an interceptor — you're watching, not modifying. But there's a gotcha: service workers can be killed by Chrome at any time. The interceptor needs to re-register its listener when the service worker starts up, not just when the extension is installed. We handle this with a &lt;code&gt;chrome.runtime.onStartup&lt;/code&gt; + &lt;code&gt;chrome.runtime.onInstalled&lt;/code&gt; double-registration pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Scheduled Auto-Cleanup — Chrome Alarms as a Cron Job
&lt;/h2&gt;

&lt;p&gt;The auto-cleanup feature lets users set rules like "delete all advertising cookies every hour" or "delete cookies older than 30 days every Sunday." Rules run in the background via &lt;code&gt;chrome.alarms&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Each rule has a schedule (in minutes), a target (domain, category, or age), and a list of matching cookies. When the user creates a rule, we call &lt;code&gt;chrome.alarms.create()&lt;/code&gt; with the interval. When the alarm fires, the service worker:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gets all cookies via &lt;code&gt;chrome.cookies.getAll({})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Filters by the rule's target (domain pattern, category, or age threshold)&lt;/li&gt;
&lt;li&gt;Deletes matching cookies via &lt;code&gt;chrome.cookies.remove()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Optionally sends a &lt;code&gt;chrome.notifications&lt;/code&gt; notification&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Classification in the service worker
&lt;/h3&gt;

&lt;p&gt;Here's the tricky part: the popup's cookie classification uses a bundled &lt;code&gt;tracking-domains.json&lt;/code&gt; file loaded via &lt;code&gt;chrome.runtime.getURL()&lt;/code&gt;. But service workers can't access the DOM, so &lt;code&gt;chrome.runtime.getURL()&lt;/code&gt; doesn't work the same way.&lt;/p&gt;

&lt;p&gt;Our solution: the service worker uses &lt;strong&gt;inline classification&lt;/strong&gt; — the same regex patterns as the main classification module, but hardcoded into the service worker. This avoids loading the JSON file in the background context. Domain-based matching (checking if a cookie's domain appears in the tracker list) is available when creating rules in the options page, where the DOM is accessible.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we learned
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;chrome.alarms&lt;/code&gt; is Chrome's built-in cron job. It's reliable, survives service worker restarts, and is exactly the right tool for background tasks. The minimum interval is 1 minute, which is fine for cookie cleanup. One caveat: alarms don't fire while Chrome is closed. If the user has a "daily" rule and doesn't open Chrome for a week, it only fires once when they open the browser. This is acceptable for cookie cleanup — the cookies will still be there waiting to be deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Six Export Formats — The Keyword Spam Trap
&lt;/h2&gt;

&lt;p&gt;This isn't a technical challenge, but it's a lesson we learned the hard way. CrumbKit supports six export formats: JSON, Netscape, cURL, CSV, Puppeteer, and Set-Cookie headers.&lt;/p&gt;

&lt;p&gt;When we submitted v1.2 to the Chrome Web Store, &lt;strong&gt;the listing was rejected for keyword spam&lt;/strong&gt;. The automated审核 system flagged the enumeration of six format names in two places — even though every format name corresponds to a real feature. The system interpreted the comma-separated list of technical terms as SEO keyword stuffing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; reduce any format enumeration to three names maximum, use "and more" for the rest, and never repeat the same list in two places within the description. The CWS automated审核 treats repeated lists of technical terms as spam, regardless of whether they're real features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture: Why Free Matters
&lt;/h2&gt;

&lt;p&gt;Every cookie editor on the market is either abandoned (EditThisCookie), paid (CookieJar at $4.99/month), or has ads (Cookie Editor). None of them hit all three: &lt;strong&gt;free, open source, and actively maintained.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CrumbKit is free because it's an acquisition tool for our product family. We make money from &lt;a href="https://wayknow.tech/snapmark.html" rel="noopener noreferrer"&gt;SnapMark&lt;/a&gt; and &lt;a href="https://wayknow.tech/clearjson.html" rel="noopener noreferrer"&gt;ClearJSON&lt;/a&gt;. CrumbKit is how developers discover WayKnow. The cookie editor market has no validated paid demand — EditThisCookie had 3M free users for a decade, Cookie-Editor has 2M free users. Making CrumbKit free maximizes reach.&lt;/p&gt;

&lt;p&gt;The entire extension is under 85KB of pure vanilla JavaScript. Zero frameworks, zero build step, zero dependencies. The &lt;code&gt;chrome.cookies&lt;/code&gt; API hasn't changed since 2016. This thing will run for years without maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;CrumbKit is free, MIT open source, and on the Chrome Web Store and Edge Add-ons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chromewebstore.google.com/detail/crumbkit/ggnfjnagciaomejccfjceniohpdkcbjl" rel="noopener noreferrer"&gt;Install on Chrome Web Store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://microsoftedge.microsoft.com/addons/detail/crumbkit" rel="noopener noreferrer"&gt;Install on Microsoft Edge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wayknow/crumbkit" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building a Chrome extension and run into MV3 issues — especially with service workers, &lt;code&gt;chrome.webRequest&lt;/code&gt;, or &lt;code&gt;chrome.alarms&lt;/code&gt; — the CrumbKit codebase is a working reference. MIT licensed, so steal freely.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;CrumbKit is part of the &lt;a href="https://wayknow.tech" rel="noopener noreferrer"&gt;WayKnow&lt;/a&gt; product family — privacy-first browser tools with zero tracking and no sign-up required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>privacy</category>
    </item>
    <item>
      <title>EditThisCookie Was Removed. A Malware Clone Took Its Name. Here's What to Use</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Thu, 13 Aug 2026 06:44:17 +0000</pubDate>
      <link>https://dev.to/wayknow123/editthiscookie-was-removed-a-malware-clone-took-its-name-heres-what-to-use-published-true-1boc</link>
      <guid>https://dev.to/wayknow123/editthiscookie-was-removed-a-malware-clone-took-its-name-heres-what-to-use-published-true-1boc</guid>
      <description>&lt;p&gt;EditThisCookie had millions of users. It was the default answer to "how do I edit cookies in Chrome?" for a decade. Then Chrome's Manifest V2 platform was retired, the extension never migrated, and in late 2024 it disappeared from the Chrome Web Store.&lt;/p&gt;

&lt;p&gt;What happened next is the part nobody warned anyone about: &lt;strong&gt;a malicious clone took its name.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The clone
&lt;/h2&gt;

&lt;p&gt;An extension called "EditThisCookie®" — note the ®, the dead giveaway — appeared on the store and got downloaded by tens of thousands of people searching for the original. Security researchers confirmed it was malware: obfuscated code, phishing and ad-injection mechanisms, and the ability to steal sensitive data, especially when you were logged into Facebook.&lt;/p&gt;

&lt;p&gt;Google eventually removed it. But think about what that means: people who just wanted to edit a cookie — the most innocuous developer task imaginable — installed malware, because the tool they'd trusted for years vanished without a farewell and the search results were a minefield.&lt;br&gt;
That's the real lesson of the MV2 migration: &lt;strong&gt;a cookie editor is a session-instrument tool.&lt;/strong&gt; It has access to your cookies on every site. When an unmaintained cookie editor dies, its name becomes a honeypot. Whatever you install next needs to be auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist I now apply to any cookie editor
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;MV3-native&lt;/strong&gt; — if it's not built for Manifest V3, it's already dead, just not buried&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; — a cookie editor that you can't audit is a liability you're choosing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recent updates&lt;/strong&gt; — check the store's "last updated" date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lookalike branding&lt;/strong&gt; — any extension that brands itself as "the replacement for the dead original" deserves extra scrutiny&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The right permissions&lt;/strong&gt; — &lt;code&gt;cookies&lt;/code&gt; is expected; broad host permissions without a clear reason are not&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What to actually use in 2026
&lt;/h2&gt;

&lt;p&gt;I tested the current field so you don't have to re-run this gauntlet. Full disclosure: I built one of these (CrumbKit), so take my ranking with that in mind — I've flagged where mine is genuinely weak.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome DevTools&lt;/strong&gt; — the zero-install baseline. Right-click → Inspect → Application → Cookies. Fine for the occasional edit, painful as a daily workflow. No search across domains, no. export/import.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CookieMate&lt;/strong&gt; — open source (MIT), MV3-native, and the one feature that matters most for ex-EditThisCookie users: &lt;strong&gt;it imports EditThisCookie's JSON backups directly&lt;/strong&gt;. If you have an old export, this is the smoothest migration. Simple editor, no fancy extras.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CookieJar&lt;/strong&gt; — the feature-rich contender: cookie classification, per-site privacy scores, profiles (dev vs. prod cookie sets), multiple export formats, CHIPS partitioned-cookie support. &lt;br&gt;
Free tier plus a paid Pro tier. Honestly the closest thing to "EditThisCookie with a 2026 paint job."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CrumbKit&lt;/strong&gt; — the one I built: everything free, MIT open source, zero network requests. Where it genuinely beats the others: a &lt;strong&gt;Set-Cookie interceptor&lt;/strong&gt; that shows you which cookie a server just set (in the response headers) and adds it with one click — no other tool in this list does that. Plus scheduled auto-cleanup, 6 export formats (JSON, Netscape, cURL, CSV, Puppeteer, Set-Cookie headers), batch editing, profiles, and a privacy score. Where it loses: it's younger than the alternatives and I'm a solo maintainer — if you only edit a cookie once a month, DevTools is genuinely enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating your old backups
&lt;/h2&gt;

&lt;p&gt;If you still have an EditThisCookie export: CookieMate imports its JSON format directly, and CrumbKit imports JSON and Netscape. If you don't have a backup, re-login to your sites — the old cookies are gone either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The MV2 migration didn't just break extensions — it created a trust vacuum that scammers filled within weeks. The defense isn't "find the most popular replacement." It's "find the one you can audit." Open source, zero telemetry, and a recent update date beat a familiar name every time.&lt;/p&gt;

&lt;p&gt;If you've got a horror story from the EditThisCookie aftermath — or a cookie workflow you can't live without — drop it in the comments. I'm building feature lists off those answers.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>security</category>
      <category>extensions</category>
    </item>
    <item>
      <title># CSS Scan Has 20,000 Users — Here's the Opportunity Everyone's Missing</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Fri, 07 Aug 2026 08:34:43 +0000</pubDate>
      <link>https://dev.to/wayknow123/-css-scan-has-20000-users-and-a-35-rating-heres-the-opportunity-everyones-missing-2289</link>
      <guid>https://dev.to/wayknow123/-css-scan-has-20000-users-and-a-35-rating-heres-the-opportunity-everyones-missing-2289</guid>
      <description>&lt;p&gt;I spent last week digging into the CSS inspection tool market. I wanted to understand why the most recommended paid extension in this category — CSS Scan — has 20,000+ users despite charging a premium price for what is, at heart, a single-purpose utility.&lt;/p&gt;

&lt;p&gt;Here's what I found.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;CSS Scan is a Chrome extension that lets you hover over any element on a page and instantly copy its computed CSS. It's the tool front-end developers recommend on Hacker News, in blog roundups, everywhere.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version:&lt;/strong&gt; 4.5, last updated April 2026 (actively maintained)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users:&lt;/strong&gt; 20,000+ professional developers (their claim)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price:&lt;/strong&gt; $120 one-time license (list price — frequently discounted; 42% off at the time of writing, bringing it to $69), limited to 3 browsers simultaneously&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we have a category leader that's actively maintained, has real paying users, and a price tag that feels detached from what the tool actually does. That's the pattern I look for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Users Actually Complain About
&lt;/h2&gt;

&lt;p&gt;The recurring theme across the store reviews is price, not bugs. The functionality is praised — hover-to-copy genuinely saves hours when you're trying to match a design or reverse-engineer a layout. But a one-time payment in the $69–120 range, license-activated with a 3-browser device limit, creates exactly the "do I really want to pay this much" hesitation that kills impulse conversions.&lt;/p&gt;

&lt;p&gt;A browser extension that reads computed styles with a standard API — no server, no infrastructure, no recurring cost — priced like a design tool suite. That's the mismatch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Free Alternatives: Why None of Them Won
&lt;/h2&gt;

&lt;p&gt;If the price is the problem, why hasn't a free alternative taken the crown? I looked at all of them.&lt;/p&gt;

&lt;p&gt;| Tool | Price | The Problem |&lt;br&gt;
  |------|-------|-------------|&lt;br&gt;
  | CSSViewer | Free | The classic hover-view tool. Old, minimal updates — styles can break on modern pages |&lt;br&gt;
  | CSS Peeper | Free | The original big name in this space. Now ad-supported; users have turned on it |&lt;br&gt;
  | CSS Peek | Free | Nice and tiny, but no momentum — a curiosity, not a category player |&lt;br&gt;
  | Tailwind CSS Scan &amp;amp; Copy | Free | Focused on Tailwind conversion only, almost no distribution |&lt;br&gt;
  | ExtractCSS | Free | A newer free option with component extraction, but it's early — no track record |&lt;br&gt;
  | CSS Scanner (not Scan) | Free | A handful of users. Effectively dead on arrival |&lt;br&gt;
  | SuperDev Pro | Free tier | 50+ tools in one sidebar — a Swiss army knife, not a focused CSS tool |&lt;/p&gt;

&lt;p&gt;The pattern is clear: &lt;strong&gt;everyone competing with CSS Scan is either free-but-neglected, ad-driven, or a side feature of a bigger product.&lt;/strong&gt; Nobody has done what CSS Scan did — a focused, polished, paid hover-to-copy tool — at a price that doesn't feel insulting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Weird Gap
&lt;/h2&gt;

&lt;p&gt;Here's the thing that stood out to me most: the two paid players in this niche are CSS Scan at $69–120 (depending on the promo) and UI-Ray at $39. Both are one-time purchases. Both are  locally processed. Both are functionally similar to each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neither has moved down-market.&lt;/strong&gt; There's no $15–19 version of this tool with modern polish, no-subscription, local-only. The demand is validated — 20K users proves developers pay for this. The price anchoring is proven — the $69–120 band is "established." That leaves the $19 band completely open.&lt;/p&gt;

&lt;p&gt;And if you've ever shipped a Chrome extension, you know hover-detection + overlay + computed-style extraction is a weekend project with a good foundation — not a six-month engineering  effort. The barrier to entry is low, which makes the fact that nobody's done it yet more surprising.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Update: this article got a bit of attention, and I decided to stop analyzing and actually ship it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Full disclosure: I built the thing I described. &lt;strong&gt;&lt;a href="https://chromewebstore.google.com/detail/csspick-inspect-copy-css/kadcnmgmnjnjcggfbphjnbndkoadkghj" rel="noopener noreferrer"&gt;CSSPick&lt;/a&gt; is live on the Chrome Web Store now&lt;/strong&gt; — a hover-to-copy CSS inspector at &lt;strong&gt;$19 one-time&lt;/strong&gt;, no subscription, everything computed locally in your browser. Same core interaction as CSS Scan: hover, click, copy — with &lt;br&gt;
the CSS grouped into Color / Typography / Spacing / Size &amp;amp; Box / Background / Border &amp;amp; Shadow / Layout so you can grab just what you need.&lt;/p&gt;

&lt;p&gt;Why $19? Because the pricing itself is the objection — and a one-time purchase at $19 hits the "sure, why not" range where utility tools actually convert. I'd rather have 500 paying users at $19 than 100 at $69, and so would the market, apparently.&lt;/p&gt;

&lt;p&gt;It also asks for minimal permissions — &lt;code&gt;activeTab&lt;/code&gt; + &lt;code&gt;storage&lt;/code&gt; + &lt;code&gt;scripting&lt;/code&gt;, no "access to all your data on all websites" warning when you install it. Free version is genuinely useful (inspect + copy single properties); Pro unlocks Copy All CSS and Copy Selector.&lt;/p&gt;

&lt;p&gt;If you try it and the analysis was wrong somewhere — or the tool is missing something — tell me in the comments. Especially tell me: &lt;strong&gt;what's the CSS property or workflow you copy by hand every single day?&lt;/strong&gt; That's the feature list I want to hear about next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build update
&lt;/h2&gt;

&lt;p&gt;This article identified an opportunity: CSS Scan has 20K users but a 3.5★ rating, and users complain about the $95 price. I built&lt;br&gt;
  &lt;strong&gt;&lt;a href="https://www.producthunt.com/products/csspick?launch=csspick" rel="noopener noreferrer"&gt;CSSPick&lt;/a&gt;&lt;/strong&gt; to fill that gap.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$19 one-time (vs $95)&lt;/li&gt;
&lt;li&gt;Free tier: view + copy single properties&lt;/li&gt;
&lt;li&gt;Pro: Copy All CSS + Selector&lt;/li&gt;
&lt;li&gt;100% local, minimal permissions, no tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.producthunt.com/products/csspick?launch=csspick" rel="noopener noreferrer"&gt;Product Hunt launch today&lt;/a&gt; · &lt;a href="https://chromewebstore.google.com/detail/csspick-inspect-csspick/kadcnmgmnjnjcggfbphjnbndkoadkghj" rel="noopener noreferrer"&gt;Chrome Web Store&lt;/a&gt;&lt;br&gt;
&lt;a href="https://wayknow.tech/csspick.html" rel="noopener noreferrer"&gt;website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>showdev</category>
      <category>webdev</category>
      <category>chrome</category>
    </item>
    <item>
      <title>I Tested Every JSON Viewer After the Formatter Scandal. Here's What I Found.</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Thu, 06 Aug 2026 07:25:53 +0000</pubDate>
      <link>https://dev.to/wayknow123/i-tested-every-json-viewer-after-the-formatter-scandal-heres-what-i-found-4dcl</link>
      <guid>https://dev.to/wayknow123/i-tested-every-json-viewer-after-the-formatter-scandal-heres-what-i-found-4dcl</guid>
      <description>&lt;p&gt;Remember JSON Formatter? The Chrome extension with 200K+ users that auto-formatted JSON in your browser? In mid-2026 it went closed-source, partnered with GiveFreely, and started injecting geolocation tracking plus donation popups into checkout pages. The developer's defense was "it's for charity." The community's response was a collective "uninstall immediately."&lt;/p&gt;

&lt;p&gt;I'm a developer. I look at JSON all day — API responses, config files, debug dumps. I needed a replacement. So I tested every major JSON viewer on the market.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Contenders
&lt;/h2&gt;

&lt;h3&gt;
  
  
  JSON Formatter (arnav-kr) — The spiritual successor
&lt;/h3&gt;

&lt;p&gt;This is the most popular open-source fork. 60+ themes, collapsible toolbar, keyboard shortcuts for everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; Truly open source. Actively maintained. Works offline. The free tier is generous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catch:&lt;/strong&gt; No large file support. Above ~5MB it starts to struggle. No JWT decoding, no advanced export formats. If you just need basic formatting, it's perfect. If you work with bigger data, keep reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Viewer Pro (PatilWeb) — Power user's choice
&lt;/h3&gt;

&lt;p&gt;300K+ users, 4.7 stars. Tree view, chart view, JSONPath with autocomplete, custom CSS. It's the most feature-rich free option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; Breadcrumb navigation is genius. Chart visualization for numeric data. Active development (v7.1 as of October 2025).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catch:&lt;/strong&gt; Closed source. Smart UI is cool but sometimes doesn't trigger when you expect it to. Heavy — noticeably slower page loads on large JSON.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Alexander (Wes Bos) — The celebrity pick
&lt;/h3&gt;

&lt;p&gt;Built by Wes Bos specifically in response to the Formatter scandal. Clean, minimal, trustworthy by association.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; Dead simple. Interactive tree, path inspection, dark/light/auto themes. Built by someone the community trusts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catch:&lt;/strong&gt; Feature-light compared to others. No search. No export. No large file handling. It's a formatter, not a toolkit.&lt;/p&gt;

&lt;h3&gt;
  
  
  JsonDiscovery — The different one
&lt;/h3&gt;

&lt;p&gt;Instead of adding a toolbar, it transforms the entire page into an interactive explorer. Tree, table, and list views. Right-click context menus for copying paths and objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; Highest rated (4.88). Innovative UX. JORA query language with autocomplete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catch:&lt;/strong&gt; Takes over the entire page — no way to see raw JSON side-by-side. Learning curve for the query syntax. Closed source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firefox Built-in — The one you already have
&lt;/h3&gt;

&lt;p&gt;If you use Firefox, you already have a JSON viewer. Collapsible tree, syntax highlighting, search. It's fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; Zero install. Zero trust issues (it's the browser). Always there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catch:&lt;/strong&gt; Chrome users are out of luck. No themes. No copy-to-path. No export. It's a viewer, not a tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dealbreaker&lt;/th&gt;
&lt;th&gt;arnav-kr&lt;/th&gt;
&lt;th&gt;PatilWeb&lt;/th&gt;
&lt;th&gt;Wes Bos&lt;/th&gt;
&lt;th&gt;JsonDiscovery&lt;/th&gt;
&lt;th&gt;Firefox&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No large file support&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Closed source&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heavy / slow&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Too minimal&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phones home?&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;No tool was: fully open source + handles large files + privacy-guaranteed + feature-complete.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Built One
&lt;/h2&gt;

&lt;p&gt;After two weeks of switching between tools depending on what I was doing, I built ClearJSON.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Formatting &amp;amp; Viewing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-detects JSON, JSON-LD, JSON:API, NDJSON — any JSON-like content type&lt;/li&gt;
&lt;li&gt;Collapsible tree view with indent guides, element counts, and depth indicators&lt;/li&gt;
&lt;li&gt;Syntax highlighting for all 7 JSON token types (strings, numbers, booleans, null, keys, punctuation, links)&lt;/li&gt;
&lt;li&gt;10 free themes with automatic dark/light system following&lt;/li&gt;
&lt;li&gt;Click-to-copy values, right-click for JSONPath or subtree&lt;/li&gt;
&lt;li&gt;Auto-detected clickable links, hover-to-preview images (PNG/JPG/GIF/SVG/WebP)&lt;/li&gt;
&lt;li&gt;Line numbers in raw view, status bar with node count/max depth/file size/parse time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Privacy — the reason I built it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero network requests for free users. Not one. The extension has no analytics, no tracking, no accounts, no ads.&lt;/li&gt;
&lt;li&gt;The Pro license verification is the ONLY network call, and only when activating a key.&lt;/li&gt;
&lt;li&gt;All processing (parsing, formatting, rendering, search) happens on your device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro features ($2.99/month, cancel anytime):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual scrolling for 500 MB+ JSON files (Web Worker + streaming parser)&lt;/li&gt;
&lt;li&gt;JWT auto-decode — detects &lt;code&gt;eyJ...&lt;/code&gt; tokens, inline displays header + payload, highlights expiry&lt;/li&gt;
&lt;li&gt;Regex search with match navigation and full-document highlighting&lt;/li&gt;
&lt;li&gt;Multi-format export: CSV, TSV, YAML, TypeScript type definitions (recursive inference)&lt;/li&gt;
&lt;li&gt;20 additional premium themes (Monokai, Dracula, Nord, One Dark, Solarized, Catppuccin, Tokyo Night, Gruvbox, and more)&lt;/li&gt;
&lt;li&gt;Custom keyboard shortcuts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Tech Stack (for the curious)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vanilla JS + IIFE module pattern.&lt;/strong&gt; Zero frameworks, zero build steps. 28 files, ~59KB zipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS variable-driven theming.&lt;/strong&gt; 30 themes defined as key-value pairs, injected at runtime via &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming parser (Pro):&lt;/strong&gt; Web Worker receives raw text in chunks, builds a flat node array, main thread renders only visible rows via virtual tree. 10,000+ nodes renders in under a second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT decode:&lt;/strong&gt; Regex match → Base64Url decode (handles &lt;code&gt;-&lt;/code&gt; → &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt; → &lt;code&gt;/&lt;/code&gt;, missing padding) → &lt;code&gt;JSON.parse&lt;/code&gt; header/payload → inline render in tree view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript type generation:&lt;/strong&gt; Recursive inference with duplicate structure deduplication. Generates proper &lt;code&gt;interface&lt;/code&gt; blocks for nested objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License server:&lt;/strong&gt; Cloudflare Workers + D1 (SQLite). 7-day cache + offline fallback. Only called during key activation/verification.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Honest Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chrome/Edge only. No Firefox version yet (Manifest V3 vs V2 differences).&lt;/li&gt;
&lt;li&gt;Free tier limited to 2MB files. This is a deliberate choice — virtual scrolling is complex infrastructure, and it's how the Pro tier makes money.&lt;/li&gt;
&lt;li&gt;No JSONPath query. JSON Query Tool and JSON Viewer Pro already do this well for free. I'm not going to build a worse version just to check a box.&lt;/li&gt;
&lt;li&gt;No JSON Diff. Same reason — Just JSON and JsonKing do it better, for free.&lt;/li&gt;
&lt;li&gt;Still early. ~43 installs as of writing. The product is solid, the user base is not.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why $2.99/month?
&lt;/h3&gt;

&lt;p&gt;JSON viewers have exactly zero marginal cost. No servers, no storage, no API calls — which is precisely why an honest subscription works: the money funds maintenance and new features, never servers and never data harvesting. I spent two years selling ClearJSON as a one-time purchase and watched the market move past it — lifetime plans have quietly disappeared from paid extensions, and subscription is what users actually support. $2.99/month, cancel anytime, free tier stays fully functional.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://chromewebstore.google.com/detail/clearjson/bgcicghmdpefapfdeghgealacphkgobk" rel="noopener noreferrer"&gt;ClearJSON on Chrome Web Store&lt;/a&gt; | &lt;a href="https://microsoftedge.microsoft.com/addons/detail/clearjson/kdebbkdldjhhboafpflimanekmhinelg" rel="noopener noreferrer"&gt;Microsoft Edge Add-ons&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/wayknow/clearjson" rel="noopener noreferrer"&gt;github.com/wayknow/clearjson&lt;/a&gt; (MIT)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What are you using to view JSON? Did I miss a good one?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>json</category>
      <category>productivity</category>
      <category>formatter</category>
    </item>
    <item>
      <title>I Tested Every Chrome Screenshot Extension. Here's Why I Built My Own.</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Tue, 04 Aug 2026 09:02:28 +0000</pubDate>
      <link>https://dev.to/wayknow123/i-tested-every-chrome-screenshot-extension-heres-why-i-built-my-own-1inj</link>
      <guid>https://dev.to/wayknow123/i-tested-every-chrome-screenshot-extension-heres-why-i-built-my-own-1inj</guid>
      <description>&lt;h1&gt;
  
  
  I Tested Every Chrome Screenshot Extension. Here's Why I Built My Own.
&lt;/h1&gt;

&lt;p&gt;I take a lot of screenshots. Bug reports, design feedback, tutorial steps, competitor research — easily 20+ a day. So I went looking for the perfect Chrome screenshot extension. What I found was surprising.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Contenders
&lt;/h2&gt;

&lt;p&gt;I tested every major screenshot extension with 10,000+ users on the Chrome Web Store. Here's what I found:&lt;/p&gt;

&lt;h3&gt;
  
  
  GoFullPage — Great at one thing, useless at everything else
&lt;/h3&gt;

&lt;p&gt;GoFullPage does exactly what the name says: full-page screenshots. It scrolls, stitches, and spits out a long image. 10/10 for that one feature.&lt;/p&gt;

&lt;p&gt;But you can't capture just the visible area. You can't select a region. There are no annotation tools on the free tier. Want to draw an arrow? Pay up. It's a one-trick tool in a category that demands versatility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Price:&lt;/strong&gt; $1/month. Seems cheap, but over 3 years that's $36 for something that can't even draw a rectangle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Awesome Screenshot — Awesome features, concerning privacy
&lt;/h3&gt;

&lt;p&gt;Feature-wise, Awesome Screenshot is the most complete. Screenshots, screen recording, annotations, cloud sharing. It does everything.&lt;/p&gt;

&lt;p&gt;But here's the catch: your screenshots go to their cloud. You need an account. The free tier nags you constantly. And the price? &lt;strong&gt;$72/year.&lt;/strong&gt; For a screenshot tool. Over 3 years you're paying $216 — more than a Microsoft Office subscription.&lt;/p&gt;

&lt;p&gt;Also, the extension is heavy. Like, noticeably-slows-down-your-browser heavy. No thank you.&lt;/p&gt;

&lt;h3&gt;
  
  
  FireShot — The one that broke my heart
&lt;/h3&gt;

&lt;p&gt;FireShot was the closest to what I wanted. Full-page capture, annotation tools, PDF export, one-time purchase. I almost bought it.&lt;/p&gt;

&lt;p&gt;Then I noticed: &lt;strong&gt;the paid version still has a watermark.&lt;/strong&gt; "Captured by FireShot" sits at the bottom of every screenshot. You paid $40 and they still stamp their brand on your work.&lt;/p&gt;

&lt;p&gt;Also: their PDF output is bizarrely bloated. A 3-page document hit 2.9MB. Their tech is aging, and there are rumors they're killing the lifetime option to go subscription-only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lightshot — The ticking time bomb
&lt;/h3&gt;

&lt;p&gt;Lightshot is simple and fast. Click, drag, annotate, done. 3.2★ on the Web Store.&lt;/p&gt;

&lt;p&gt;The problem? Screenshots uploaded to their server are &lt;strong&gt;automatically deleted after a few months.&lt;/strong&gt; Their privacy policy is vague about what "deleted" means. Also, it completely breaks on Google Docs. If you're a heavy Docs user, Lightshot is unusable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nimbus — Feature overload
&lt;/h3&gt;

&lt;p&gt;Nimbus has everything: screenshots, screen recording, video editing, cloud storage, team collaboration. It's trying to be an entire creative suite inside a browser extension.&lt;/p&gt;

&lt;p&gt;The result? A cluttered interface that takes 10 clicks to do what should take 2. The free version adds a watermark. The paid version is $60/year. It's overengineered for what most people need.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;After testing all five, a pattern emerged:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dealbreaker&lt;/th&gt;
&lt;th&gt;GoFullPage&lt;/th&gt;
&lt;th&gt;Awesome&lt;/th&gt;
&lt;th&gt;FireShot&lt;/th&gt;
&lt;th&gt;Lightshot&lt;/th&gt;
&lt;th&gt;Nimbus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Missing core modes&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud upload forced&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paid still watermarked&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscription only&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bloat / performance&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;No single tool was: fully-featured + local-first + watermark-free + one-time purchase.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Built One
&lt;/h2&gt;

&lt;p&gt;I'm a developer. I know my way around the Chrome extension APIs. How hard could it be?&lt;/p&gt;

&lt;p&gt;Turns out: harder than expected. But also extremely satisfying. Here's what I built:&lt;/p&gt;

&lt;h3&gt;
  
  
  SnapMark — Screenshots + Annotation, 100% Local
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;7 capture modes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visible area (one click, instant)&lt;/li&gt;
&lt;li&gt;Full page (auto-scroll with overlap blending for seamless stitches)&lt;/li&gt;
&lt;li&gt;Region selection (drag to select, real-time W×H display)&lt;/li&gt;
&lt;li&gt;Smart element selection (hover to detect DOM elements, click to capture)&lt;/li&gt;
&lt;li&gt;Timer capture (3/5/10 second countdown — for dropdowns and tooltips)&lt;/li&gt;
&lt;li&gt;Clean &amp;amp; Capture (remove ads, banners, cookie popups before capturing)&lt;/li&gt;
&lt;li&gt;Batch capture (paste URLs, auto-capture up to 20 pages)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8 annotation tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arrow, Rectangle, Ellipse, Text, Freehand, Blur/Mosaic, Step Numbers (①②③), Crop&lt;/li&gt;
&lt;li&gt;Full IME support for Chinese, Japanese, and Korean text input&lt;/li&gt;
&lt;li&gt;17 system fonts, 9 font sizes (12–144px)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What makes it different:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero data leaves your browser.&lt;/strong&gt; Screenshots, annotations, exports — all local.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No account, no sign-up.&lt;/strong&gt; Install and go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free tier is genuinely complete.&lt;/strong&gt; 4 capture modes, all annotation tools, PNG export. No watermark on screenshots.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro is $39 one time.&lt;/strong&gt; No subscription. One payment, lifetime access.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Tech Stack (for the curious)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vanilla JS + ES modules.&lt;/strong&gt; Zero frameworks, zero build steps. The entire extension is ~80KB zipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canvas 2D annotation engine.&lt;/strong&gt; Full-resolution rendering (canvas.width = image.naturalWidth), CSS handles display scaling. Mouse coordinates are transformed from CSS space to canvas space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full-page stitching:&lt;/strong&gt; 50px overlap between segments + Canvas gradient blending (&lt;code&gt;destination-in&lt;/code&gt; composite) to eliminate visible seams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IME text input:&lt;/strong&gt; Transparent &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; overlaid on canvas during text editing. Same approach Figma and Excalidraw use. Handles CJK composition events natively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF export:&lt;/strong&gt; Hand-rolled &lt;code&gt;Uint8Array&lt;/code&gt; builder. No PDF library. Minimal output size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License server:&lt;/strong&gt; Cloudflare Workers + D1 (SQLite). Optional — only for Pro verification. Free users never make a network request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Honest Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chrome-only right now. Firefox and Edge versions are on the roadmap.&lt;/li&gt;
&lt;li&gt;No screen recording. (Competing with Loom is a different product category.)&lt;/li&gt;
&lt;li&gt;No cloud sharing by design. If you need team collaboration with shared links, this isn't your tool.&lt;/li&gt;
&lt;li&gt;Still early. v1.4.0 just shipped. There will be bugs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why $39 Lifetime?
&lt;/h3&gt;

&lt;p&gt;I hate subscriptions. Every screenshot tool charging $5-10/month is extracting way more value than they provide. The marginal cost of a screenshot tool is essentially zero — no servers, no storage, no API calls. Charging monthly for it feels dishonest.&lt;/p&gt;

&lt;p&gt;$39 once, use it forever. If the math works, great. If not, the free tier is genuinely useful and I'll keep improving it regardless.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://chromewebstore.google.com/detail/snapmark-full-page-screen/eppopiophjmfnoimmaklpbmkfmgpfgmj" rel="noopener noreferrer"&gt;SnapMark on Chrome Web Store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://wayknow.tech/snapmark.html" rel="noopener noreferrer"&gt;wayknow.tech/snapmark&lt;/a&gt;   &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Feedback welcome — especially on the annotation UX. What would make you switch from your current tool?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>software</category>
      <category>tools</category>
    </item>
    <item>
      <title>I Built a JSON Formatter After the Original Started Injecting Ads — Here's What I Learned</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Thu, 30 Jul 2026 07:57:39 +0000</pubDate>
      <link>https://dev.to/wayknow123/i-built-a-json-formatter-after-the-original-started-injecting-ads-heres-what-i-learned-56kf</link>
      <guid>https://dev.to/wayknow123/i-built-a-json-formatter-after-the-original-started-injecting-ads-heres-what-i-learned-56kf</guid>
      <description>&lt;p&gt;A few months ago, the most popular JSON formatter Chrome extension (2M+ users) was sold. The new owners immediately closed the source, injected affiliate ads, and started tracking user&lt;br&gt;
  geolocation.&lt;/p&gt;

&lt;p&gt;HN exploded. Developers fled. Five open-source alternatives appeared within a year.&lt;/p&gt;

&lt;p&gt;But here's the thing that bothered me: all of them are free, and none of them have a business model.&lt;/p&gt;

&lt;p&gt;This is exactly how we got here. A maintainer burns out, sells to the highest bidder, and 2 million users wake up to ads in their browser. The cycle just repeats.&lt;/p&gt;

&lt;p&gt;So I built ClearJSON (&lt;a href="https://wayknow.tech/clearjson.html" rel="noopener noreferrer"&gt;https://wayknow.tech/clearjson.html&lt;/a&gt;). Not because the world needed another JSON formatter — but because it needed one that wouldn't betray its users.&lt;/p&gt;

&lt;p&gt;What makes it different&lt;/p&gt;

&lt;p&gt;100% local, verifiable in DevTools. Open the Network tab — there's nothing. Never connects to a server, never sends data anywhere. I can't track you even if I wanted to.&lt;/p&gt;

&lt;p&gt;Handles 100MB+ JSON files without freezing. Every free alternative calls JSON.parse() on the main thread. That works fine at 2MB, not at 100MB. ClearJSON does the parsing in a Web Worker so&lt;br&gt;
  the browser stays responsive. This was the hardest part to build and the reason most alternatives skip it.&lt;/p&gt;

&lt;p&gt;JWT auto-decoding. Half the reason I open JSON is to inspect JWTs. Click a token and it decodes the header + payload inline.&lt;/p&gt;

&lt;p&gt;MIT license. Even if I disappear tomorrow, anyone can fork it. The license actually protects users this time.&lt;/p&gt;

&lt;p&gt;The business model question&lt;/p&gt;

&lt;p&gt;I charge $29 one-time for Pro (large files, JWT, exports, more themes, custom shortcuts). The free tier has everything most people need.&lt;/p&gt;

&lt;p&gt;A few people have asked: "Why not just make it free? There are five free alternatives."&lt;/p&gt;

&lt;p&gt;Because "free" is how the last one got sold. A one-time purchase with no recurring server costs means there's no incentive to sell out. No investors to please, no data to monetize. Just a&lt;br&gt;
  tool that works.&lt;/p&gt;

&lt;p&gt;The MCP server (npx -y clearjson-mcp) is also MIT licensed — so AI agents can format and validate JSON directly, including large files that would crash other MCP tools.&lt;/p&gt;

&lt;p&gt;What I'd do differently&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start marketing before shipping. I built the whole thing, launched, and then started thinking about distribution. Should have been building an audience in parallel.&lt;/li&gt;
&lt;li&gt;The "betrayal" story is a hook, not a business. People upvote the drama, but they don't automatically buy the product.&lt;/li&gt;
&lt;li&gt;CWS organic discovery is slow. Without reviews and installs, you rank on page&amp;nbsp;10. External traffic (blog, Reddit, HN) is the only way to bootstrap.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Links&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome Web Store (&lt;a href="https://chromewebstore.google.com/detail/clearjson/bgcicghmdpefapfdeghgealacphkgobk" rel="noopener noreferrer"&gt;https://chromewebstore.google.com/detail/clearjson/bgcicghmdpefapfdeghgealacphkgobk&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;GitHub (&lt;a href="https://github.com/wayknow/clearjson" rel="noopener noreferrer"&gt;https://github.com/wayknow/clearjson&lt;/a&gt;) (MIT)&lt;/li&gt;
&lt;li&gt;MCP Server on npm (&lt;a href="https://www.npmjs.com/package/clearjson-mcp" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/clearjson-mcp&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Full comparison of JSON Formatter alternatives (&lt;a href="https://wayknow.tech/blog/json-formatter-alternatives.html" rel="noopener noreferrer"&gt;https://wayknow.tech/blog/json-formatter-alternatives.html&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>softwaredevelopment</category>
      <category>tools</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building privacy-first developer tools — no accounts, no tracking, one-time purchase</title>
      <dc:creator>wayknow</dc:creator>
      <pubDate>Tue, 28 Jul 2026 06:12:25 +0000</pubDate>
      <link>https://dev.to/wayknow123/building-privacy-first-developer-tools-no-accounts-no-tracking-one-time-purchase-4p54</link>
      <guid>https://dev.to/wayknow123/building-privacy-first-developer-tools-no-accounts-no-tracking-one-time-purchase-4p54</guid>
      <description>&lt;p&gt;Hey DEV! I'm building the WayKnow family of developer tools after getting frustrated with the state of browser extensions.&lt;/p&gt;

&lt;p&gt;The origin story: The most popular JSON Formatter extension (2M users) was sold and started injecting ads + tracking geolocation. EditThisCookie (3M users) got killed by Manifest V2. I realized there was a gap for tools that are genuinely private, open source, and don't force subscriptions.&lt;/p&gt;

&lt;p&gt;So far I've shipped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ClearJSON — JSON viewer that handles 100MB+ files without crashing, $29 lifetime&lt;/li&gt;
&lt;li&gt;SnapMark — Screenshot + annotation tool, $39 lifetime (vs competitors at $72/year)&lt;/li&gt;
&lt;li&gt;CrumbKit — Free cookie editor, MIT licensed, minimal permissions&lt;/li&gt;
&lt;li&gt;ColorPeek — macOS color picker for developers, $19 lifetime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is local-first, MIT licensed, one-time purchase. No backend, no user accounts, no telemetry.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about building Chrome extensions, dealing with CWS review, or the buy-once business model!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>chrome</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
