<?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: marsdiscovery</title>
    <description>The latest articles on DEV Community by marsdiscovery (@marsdiscovery_3d15f443c16).</description>
    <link>https://dev.to/marsdiscovery_3d15f443c16</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%2F3967735%2Febcffc8c-37bd-4c4d-a217-71fba4ca3392.jpg</url>
      <title>DEV Community: marsdiscovery</title>
      <link>https://dev.to/marsdiscovery_3d15f443c16</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marsdiscovery_3d15f443c16"/>
    <language>en</language>
    <item>
      <title>The webhook signature matched in Postman. Staging still returned 401.</title>
      <dc:creator>marsdiscovery</dc:creator>
      <pubDate>Tue, 23 Jun 2026 02:06:45 +0000</pubDate>
      <link>https://dev.to/marsdiscovery_3d15f443c16/anthropics-friday-kill-switch-changed-how-i-split-ai-from-deterministic-work-1cnc</link>
      <guid>https://dev.to/marsdiscovery_3d15f443c16/anthropics-friday-kill-switch-changed-how-i-split-ai-from-deterministic-work-1cnc</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcks4kbtc9lrtj5ag5uob.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcks4kbtc9lrtj5ag5uob.jpg" alt=" " width="800" height="343"&gt;&lt;/a&gt;&lt;br&gt;
We wired up a new billing webhook last sprint. Local tests passed. Postman collection green. Merged on Friday.&lt;/p&gt;

&lt;p&gt;Monday morning staging logged hundreds of &lt;code&gt;401 invalid signature&lt;/code&gt; from the provider's retry queue. The handler was maybe fifty lines. The secret lived in an env var copied straight from the vendor dashboard.&lt;/p&gt;

&lt;p&gt;I replayed the same payload in Postman with the same &lt;code&gt;X-Signature&lt;/code&gt; header. Verification passed. Staging failed on requests that looked identical.&lt;/p&gt;

&lt;p&gt;The gap wasn't the HMAC math on paper. It was &lt;strong&gt;which bytes&lt;/strong&gt; we hashed.&lt;/p&gt;

&lt;p&gt;Our API gateway parsed JSON before the route handler ran. By the time we read &lt;code&gt;req.body&lt;/code&gt;, it was a JavaScript object serialized again — keys in a different order, sometimes a trailing newline gone. The provider had signed the &lt;strong&gt;raw request body&lt;/strong&gt; from the wire. We were hashing a re-exported string.&lt;/p&gt;

&lt;p&gt;Everything in logs looked fine: one-line JSON, correct algorithm in the header, secret without obvious typos. That's what made it annoying. I spent an hour convinced the vendor had rotated keys without telling us.&lt;/p&gt;

&lt;p&gt;What I check now on webhook PRs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Raw body first.&lt;/strong&gt; If the framework touches JSON before your verifier runs, find where to hook the unparsed buffer. Signature schemes don't care about your ORM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secret encoding.&lt;/strong&gt; Dashboards often show base64-looking strings. Is the env var the decoded key bytes, or the base64 text itself? Wrong layer and HMAC still "works" in a scratch script — with the wrong input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One failing payload, viewed cleanly.&lt;/strong&gt; I copy the exact body and signature into separate tabs so I'm not squinting at escaped quotes wrapped in a narrow terminal.&lt;/p&gt;

&lt;p&gt;Browser pages I keep for these checks — one job each, local processing, no account for a two-minute look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/json-formatter/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/json-formatter/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/base64/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/base64/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/hash-generator/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/hash-generator/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I maintain a small browser toolkit called DevCove for exactly these chores when I'm already in DevTools on another tab. &lt;code&gt;curl --data-binary&lt;/code&gt; and a server-side replay fixture still win for the real fix; this is for ruling out "wrong bytes" vs "wrong secret" before you open a ticket with the vendor.&lt;/p&gt;

&lt;p&gt;Half the outcome was code: verify on raw body, add one canned replay test. The other half was stopping the assumption that Postman parity means production parity.&lt;/p&gt;

&lt;p&gt;If you maintain signed webhooks — where do you usually lose the plot? Middleware reordering the body, secret encoding, or something else entirely?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Anthropic's Friday kill switch changed how I split AI from deterministic work</title>
      <dc:creator>marsdiscovery</dc:creator>
      <pubDate>Tue, 16 Jun 2026 06:29:33 +0000</pubDate>
      <link>https://dev.to/marsdiscovery_3d15f443c16/anthropics-friday-kill-switch-changed-how-i-split-ai-from-deterministic-work-i6n</link>
      <guid>https://dev.to/marsdiscovery_3d15f443c16/anthropics-friday-kill-switch-changed-how-i-split-ai-from-deterministic-work-i6n</guid>
      <description>&lt;p&gt;I had a half-finished refactor open in Cursor when a teammate dropped a link in Slack: Anthropic had pulled Fable 5 and Mythos 5 offline. Not a rate limit. Not scheduled maintenance. &lt;strong&gt;Gone.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first thought wasn't policy. It was practical. A script we run in CI uses Claude to summarize test failures. Another engineer routes security-triage prompts through the API. We both assumed the model would be there on Monday like electricity.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;A letter at 5:21 p.m.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;According to Anthropic and reporting from &lt;a href="https://www.lawfaremedia.org/article/a-kill-switch-for-frontier-ai" rel="noopener noreferrer"&gt;Lawfare&lt;/a&gt; and &lt;a href="https://techcrunch.com/2026/06/15/the-us-governments-anthropic-models-ban-was-never-about-an-ai-jailbreak/" rel="noopener noreferrer"&gt;TechCrunch&lt;/a&gt;, the U.S. Commerce Department sent a letter on Friday, June 12, at 5:21 p.m. Eastern. Citing export-control authorities, it told Anthropic to cut off Fable 5 and Mythos 5 for any foreign national — inside or outside the United States, including foreign employees at Anthropic itself.&lt;/p&gt;

&lt;p&gt;Anthropic can't reliably sort API customers by nationality. So the company did the only thing that guaranteed compliance: it disabled both models for &lt;strong&gt;everyone&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Within hours, one of the most capable model pairs on the market was unavailable worldwide. No court hearing. No public text of the letter. Just a product that existed on Thursday and didn't on the weekend.&lt;/p&gt;

&lt;p&gt;If you build on someone else's API, that sequence should bother you.&lt;/p&gt;

&lt;h2&gt;
  
  
  This wasn't really about a jailbreak — or maybe it was
&lt;/h2&gt;

&lt;p&gt;The official reasoning is still fuzzy. Anthropic believes the letter relates to a guardrail bypass. David Sacks framed it as a serious vulnerability — reportedly demonstrated by a "trusted partner" (widely reported as Amazon security researchers) — and said Anthropic refused to patch or de-deploy when asked.&lt;/p&gt;

&lt;p&gt;Anthropic pushed back: the issue was narrow, partly known before launch, and present in rival frontier models too. Pulling the models globally, they argued, would essentially halt frontier deployments for every lab if this became the standard.&lt;/p&gt;

&lt;p&gt;Then Katie Moussouris — founder of Luta Security — weighed in. Anthropic had shared a private paper describing the alleged bypass. Moussouris wrote that the behavior "should never have triggered an export control." The difference, she explained, was largely between asking a model to &lt;em&gt;review code for security issues&lt;/em&gt; and asking it to &lt;em&gt;fix this code&lt;/em&gt;. Same output class, different phrasing.&lt;/p&gt;

&lt;p&gt;Dozens of security researchers signed an open letter asking the administration to revoke the order. Their argument: yanking advanced models from defenders during an active dispute is worse for U.S. cybersecurity than the risk being debated.&lt;/p&gt;

&lt;p&gt;Axios, citing sources, described personality friction between Anthropic and the administration as a bigger driver than the technical report itself. That part is harder to prove. But the pattern fits: Anthropic is already suing over a Pentagon supply-chain blacklist after refusing unrestricted military use of its models. Whether or not you trust Anthropic's safety posture, it's difficult to read this as a purely technical intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export controls meet SaaS
&lt;/h2&gt;

&lt;p&gt;Lawfare's analysis is worth sitting with even if you don't follow trade policy. The Export Administration Regulations were built for chips and tangible technology. Biden's AI Diffusion Rule briefly put model weights on the control list; Trump rescinded it on retaking office. This Anthropic action may be the first time export controls were used to choke access to a live API model.&lt;/p&gt;

&lt;p&gt;That raises messy questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are the &lt;strong&gt;weights&lt;/strong&gt; being controlled, or the &lt;strong&gt;outputs&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Is foreign access to a U.S. SaaS product an "export" at all?&lt;/li&gt;
&lt;li&gt;If guardrails fail, does that justify a blanket ban on all non-U.S. persons — including allied researchers?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Congress passed nothing resembling a clear licensing regime for frontier AI before this happened. Anthropic's CEO had actually argued days earlier that government should be able to block dangerous deployments — with protections against arbitrary decisions. A standardless letter on a Friday evening is probably not what he meant.&lt;/p&gt;

&lt;p&gt;Foreign capitals will wonder whether American AI infrastructure is reliable for critical systems. Today's favorite U.S. model can become a bargaining chip tomorrow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke for developers on the ground
&lt;/h2&gt;

&lt;p&gt;Most of us aren't training frontier models. We paste stack traces into chat windows, wire up API calls, and move on. The kill switch still landed in real workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teams mid-sprint on Fable or Mythos had to fail over, rewrite prompts, or pause features.&lt;/li&gt;
&lt;li&gt;Startups without multi-provider abstraction learned an expensive lesson about single-vendor dependency.&lt;/li&gt;
&lt;li&gt;Security engineers who wanted Claude for log triage found the tool missing during an already tense news cycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is catastrophic if you treat LLMs as optional accelerators. It &lt;strong&gt;is&lt;/strong&gt; catastrophic if you've designed your product so the model &lt;em&gt;is&lt;/em&gt; the product — no fallback, no offline path, no deterministic substitute for the boring parts.&lt;/p&gt;

&lt;p&gt;AI is great at judgment-heavy work: explaining an unfamiliar codebase, drafting a migration plan, suggesting why a flaky test might be order-dependent. It's terrible as the only way to decode a JWT, format a JSON error body, or confirm whether a log timestamp is seconds or milliseconds. Those jobs don't need reasoning. They need correctness, speed, and a clear boundary about where your data goes.&lt;/p&gt;

&lt;p&gt;When Anthropic's models went dark, the deterministic chores didn't disappear. Pipelines still emitted malformed JSON. Staging still handed us tokens we shouldn't paste into random websites. Cron expressions in YAML still deserved a second look before deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed — a little
&lt;/h2&gt;

&lt;p&gt;I'm not abandoning AI assistants. They're too useful for the messy middle of engineering. But the weekend reminded me to separate two buckets:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bucket A&lt;/strong&gt; — work where the model adds judgment: design tradeoffs, unfamiliar APIs, long explanations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bucket B&lt;/strong&gt; — pure transformation: format, decode, encode, validate, generate a UUID, parse a cron field.&lt;/p&gt;

&lt;p&gt;Bucket B should never depend on a frontier API being online, priced the same, or politically acceptable in every jurisdiction. It should run locally, finish in seconds, and not send customer payloads to a third party.&lt;/p&gt;

&lt;p&gt;I maintain a small browser workbench called &lt;a href="https://devcove.dev/" rel="noopener noreferrer"&gt;DevCove&lt;/a&gt; for exactly those Bucket B tasks — separate pages, one job each, processing in the browser after load. Not because local tools are morally superior, but because they're boring in the right way. They don't get export-controlled.&lt;/p&gt;

&lt;p&gt;Tabs I kept open even while the Claude API was down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devcove.dev/en/tools/json-formatter/" rel="noopener noreferrer"&gt;JSON formatter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcove.dev/en/tools/jwt-decoder/" rel="noopener noreferrer"&gt;JWT decoder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcove.dev/en/tools/timestamp-converter/" rel="noopener noreferrer"&gt;Timestamp converter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a longer take on why local-first tooling still matters when AI is everywhere, see &lt;a href="https://devcove.dev/en/articles/local-first-developer-tools-in-the-ai-era/" rel="noopener noreferrer"&gt;Local-first developer tools in the AI era&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt;, IDE plugins, and one-liners in the terminal all work too. The point isn't which tab you use. The point is having a fallback that doesn't require a model endpoint, an account, or a policy debate in Washington.&lt;/p&gt;

&lt;h2&gt;
  
  
  The precedent outlives the outage
&lt;/h2&gt;

&lt;p&gt;By the time you read this, Anthropic and the administration may have patched things up. Models may be back. That's almost beside the point.&lt;/p&gt;

&lt;p&gt;The precedent is set: a U.S. AI product can be switched off globally on short notice, with limited transparency, while the industry argues about whether the triggering incident even qualifies as a national-security event. If you're an engineering lead, that belongs in your risk register next to cloud region outages and vendor price hikes.&lt;/p&gt;

&lt;p&gt;If you're an individual developer, the lesson is smaller and more actionable: &lt;strong&gt;don't merge Bucket A and Bucket B.&lt;/strong&gt; Let AI help you think. Let local, deterministic tools handle the fragments of your day that shouldn't need a network round trip — especially the fragments that contain secrets.&lt;/p&gt;

&lt;p&gt;How are you handling provider risk now — multi-model routing, self-hosted weights for specific tasks, or fingers crossed and a cached API key in the vault?&lt;/p&gt;

&lt;p&gt;What's in your fallback plan when the API you depend on goes dark on a Friday?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>claude</category>
    </item>
    <item>
      <title>The webhook signature matched in Postman. Staging still returned 401.</title>
      <dc:creator>marsdiscovery</dc:creator>
      <pubDate>Sat, 06 Jun 2026 08:09:55 +0000</pubDate>
      <link>https://dev.to/marsdiscovery_3d15f443c16/the-webhook-signature-matched-in-postman-staging-still-returned-401-1fmj</link>
      <guid>https://dev.to/marsdiscovery_3d15f443c16/the-webhook-signature-matched-in-postman-staging-still-returned-401-1fmj</guid>
      <description>&lt;p&gt;We wired up a new billing webhook last sprint. Local tests passed. Postman collection green. Merged on Friday.&lt;/p&gt;

&lt;p&gt;Monday morning staging logged hundreds of &lt;code&gt;401 invalid signature&lt;/code&gt; from the provider's retry queue. The handler was maybe fifty lines. The secret lived in an env var copied straight from the vendor dashboard.&lt;/p&gt;

&lt;p&gt;I replayed the same payload in Postman with the same &lt;code&gt;X-Signature&lt;/code&gt; header. Verification passed. Staging failed on requests that looked identical.&lt;/p&gt;

&lt;p&gt;The gap wasn't the HMAC math on paper. It was &lt;strong&gt;which bytes&lt;/strong&gt; we hashed.&lt;/p&gt;

&lt;p&gt;Our API gateway parsed JSON before the route handler ran. By the time we read &lt;code&gt;req.body&lt;/code&gt;, it was a JavaScript object serialized again — keys in a different order, sometimes a trailing newline gone. The provider had signed the &lt;strong&gt;raw request body&lt;/strong&gt; from the wire. We were hashing a re-exported string.&lt;/p&gt;

&lt;p&gt;Everything in logs looked fine: one-line JSON, correct algorithm in the header, secret without obvious typos. That's what made it annoying. I spent an hour convinced the vendor had rotated keys without telling us.&lt;/p&gt;

&lt;p&gt;What I check now on webhook PRs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Raw body first.&lt;/strong&gt; If the framework touches JSON before your verifier runs, find where to hook the unparsed buffer. Signature schemes don't care about your ORM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secret encoding.&lt;/strong&gt; Dashboards often show base64-looking strings. Is the env var the decoded key bytes, or the base64 text itself? Wrong layer and HMAC still "works" in a scratch script — with the wrong input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One failing payload, viewed cleanly.&lt;/strong&gt; I copy the exact body and signature into separate tabs so I'm not squinting at escaped quotes wrapped in a narrow terminal.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgc0xjym60rjae6c6axur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgc0xjym60rjae6c6axur.png" alt=" " width="799" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Browser pages I keep for these checks — one job each, local processing, no account for a two-minute look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/json-formatter/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/json-formatter/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/base64/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/base64/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/hash-generator/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/hash-generator/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I maintain a small browser toolkit called DevCove for exactly these chores when I'm already in DevTools on another tab. &lt;code&gt;curl --data-binary&lt;/code&gt; and a server-side replay fixture still win for the real fix; this is for ruling out "wrong bytes" vs "wrong secret" before you open a ticket with the vendor.&lt;/p&gt;

&lt;p&gt;Half the outcome was code: verify on raw body, add one canned replay test. The other half was stopping the assumption that Postman parity means production parity.&lt;/p&gt;

&lt;p&gt;If you maintain signed webhooks — where do you usually lose the plot? Middleware reordering the body, secret encoding, or something else entirely?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The redirect_uri matched in the dashboard. OAuth still failed.</title>
      <dc:creator>marsdiscovery</dc:creator>
      <pubDate>Thu, 04 Jun 2026 07:18:26 +0000</pubDate>
      <link>https://dev.to/marsdiscovery_3d15f443c16/the-redirecturi-matched-in-the-dashboard-oauth-still-failed-50hl</link>
      <guid>https://dev.to/marsdiscovery_3d15f443c16/the-redirecturi-matched-in-the-dashboard-oauth-still-failed-50hl</guid>
      <description>&lt;p&gt;We spent an afternoon convinced the identity provider was broken. Login worked locally. Staging failed with the usual vague error — redirect mismatch, invalid request, pick your vendor wording.&lt;/p&gt;

&lt;p&gt;The redirect_uri in our admin console was copied from the ticket. Character for character, it looked identical to what the browser sent. No trailing slash debate. No http vs https surprise.&lt;/p&gt;

&lt;p&gt;Then I dumped the authorize URL from the network tab and compared it to what we had registered.&lt;/p&gt;

&lt;p&gt;One query value had been encoded twice. Another used + for a space in a place our stack later read as a literal plus. The path wasn't wrong. The encoding layers were fighting each other.&lt;/p&gt;

&lt;p&gt;This is the boring class of bug: nothing shows up in a unit test because each function "works" in isolation. The failure only appears when a full URL travels through a form, a framework helper, and a provider that compares strings literally.&lt;/p&gt;

&lt;p&gt;What I watch for now:&lt;/p&gt;

&lt;p&gt;Before blaming the IdP, decode the authorize URL once in a sane viewer. I want to see each query key and value split apart, not a single opaque percent soup.&lt;/p&gt;

&lt;p&gt;Plus vs %20. application/x-www-form-urlencoded treats + as space. Generic URI encoding uses %20. Mix those rules in one redirect chain and you get ghosts.&lt;/p&gt;

&lt;p&gt;Nested URLs. Some flows put a return URL inside a query parameter. Encode the inner URL as a component, not as "the whole string again with extra % on the %."&lt;/p&gt;

&lt;p&gt;One-line error JSON from the token endpoint still shows up in the same incident. Pretty-printing it once saves ten minutes of guessing whether the problem is OAuth or your own handler.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjazxciaz3eehclcrxapo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjazxciaz3eehclcrxapo.png" alt=" " width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Browser tabs I keep for these checks — one job per page, local processing, no account for a two-minute look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/url-encoder/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/url-encoder/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/json-formatter/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/url-encoder/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devcove.dev/en/tools/base64/" rel="noopener noreferrer"&gt;https://devcove.dev/en/tools/url-encoder/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I maintain a small toolkit called DevCove for exactly these chores (format, encode, decode in the tab I already have open). curl and server logs still win for reproduction; this is for the moment you're staring at a URL in DevTools and need to know if you're looking at double encoding or a real mismatch.&lt;/p&gt;

&lt;p&gt;Not claiming OAuth is easy. Half our fix was process: register the exact bytes the browser will send, document which encoder each service uses, stop "fixing" URLs by encoding until it looks scary.&lt;/p&gt;

&lt;p&gt;If you've shipped OAuth lately — where do you catch encoding bugs first? Provider logs, proxy logs, or a scratch pad in the browser?&lt;/p&gt;

</description>
      <category>api</category>
      <category>devjournal</category>
      <category>security</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
