[{"data":1,"prerenderedAt":14},["ShallowReactive",2],{"case-why-we-built-devpeek-en":3,"blog-list-en":11},{"slug":4,"title":5,"summary":6,"date":7,"featured":8,"seoDescription":9,"html":10},"why-we-built-devpeek","Why We Built DevPeek (1): HTTPS Decrypted, Body Still Gibberish","The night before a release, TLS was already open—but changing one request field still meant digging up encrypt\u002Fdecrypt scripts. That pushed us toward a proxy tool with business-layer crypto built in—and DevPeek started there.","2026-07-09",true,"DevPeek origin series, part 1: the manual request-body encrypt\u002Fdecrypt grind—and why we set out to build a proxy tool that owns business-layer crypto.","\u003Ch2>9:40 PM, pinged in the group again\u003C\u002Fh2>\n\u003Cp>Night before a release. QA dropped a line:\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>“Staging login is broken—production users are reporting it too.”\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>Not a scheduled joint-debug session. People were already chasing updates in the thread.\u003C\u002Fp>\n\u003Cp>Proxy on, HTTPS decrypting fine. That \u003Ccode>POST \u002Fapi\u002Fuser\u002Flogin\u003C\u002Fcode> showed \u003Cstrong>200\u003C\u002Fstrong>—the path looked healthy. I opened the request body anyway:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;payload&quot;: &quot;U2FsdGVkX1+8K3v… (long string continues)&quot;,\n  &quot;sign&quot;: &quot;a8f3c2…&quot;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>TLS was fine. \u003Ccode>payload\u003C\u002Fcode> was still a Base64 blob. We had been shipping this style of API for years: gateway-wrapped body, AES on the fields, a sign on the side. Normal on a slow day. That night was not slow.\u003C\u002Fp>\n\u003Ch2>Fine most days. Miserable when you are rushed.\u003C\u002Fh2>\n\u003Cp>My usual loop looked something like this:\u003C\u002Fp>\n\u003Cp>Copy the ciphertext. Open an online decrypt page—I keep three of those bookmarked. Paste, get plaintext. Backend says “try field X,” so I edit it, encrypt again, and replay in Postman or a script.\u003C\u002Fp>\n\u003Cp>Charles handles HTTPS. Business-layer crypto still lives somewhere else. When I am not in a hurry, hopping windows is annoying but survivable. When time is tight, bouncing between capture, browser, and terminal gets old fast.\u003C\u002Fp>\n\u003Ch2>That night\u003C\u002Fh2>\n\u003Cp>The \u003Ccode>payload\u003C\u002Fcode> in the request was still ciphertext. I could not tell where login was failing. I dropped a screenshot in the backend channel: “Can you help read the body plaintext?”\u003C\u002Fp>\n\u003Cp>Message sent. Reply not in yet. The decrypt script in my terminal was already running—done this so many times it is basically reflex.\u003C\u002Fp>\n\u003Cp>Then the reply:\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>“Staging uses key set B. Do not decrypt with production keys.”\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>The API itself was fine. Time went to matching keys and environment first. Until the plaintext lines up, there is nothing useful to debug.\u003C\u002Fp>\n\u003Cp>Then a small ask: “Change \u003Ccode>userId\u003C\u002Fcode> in the request to 0 and run login again—see if auth is misjudging.”\u003C\u002Fp>\n\u003Cp>That means touching the login request the app actually sends. Same encrypted body. Same grind: decrypt, edit the field, encrypt again, let the app emit something the server will accept.\u003C\u002Fp>\n\u003Cp>I tried Breakpoints in Charles to edit it in flight. Open the body—ciphertext. Cannot edit. Back to the Python script under \u003Ccode>scripts\u002Fdebug\u002F\u003C\u002Fcode>, untouched for three months. Postman can send a version, but headers, cookies, and signing order never quite match the real app path, so you never fully trust the result.\u003C\u002Fp>\n\u003Cp>QA kept asking: “So can we log in on staging?”\u003C\u002Fp>\n\u003Cp>I burned nearly an hour in that manual loop. Looking back, they wanted to check one or two fields.\u003C\u002Fp>\n\u003Cp>Before standup, product added: “Check signup too—same encryption as login.” New URL. Copy the script. Tweak it. Same grind again.\u003C\u002Fp>\n\u003Cp>A new backend teammate asked which key each endpoint uses. The doc is in Confluence. Not something you see in the capture UI.\u003C\u002Fp>\n\u003Ch2>We were never stuck on TLS\u003C\u002Fh2>\n\u003Cp>Charles and Fiddler earn their keep stripping HTTPS. The grind sits one layer up: request bodies still ciphertext, and every edit or replay still done by hand—decrypt, change, encrypt again.\u003C\u002Fp>\n\u003Cp>Certs are fine. The endpoint returns 200. The thread is still blowing up. And somehow you still cannot skip copying ciphertext, matching staging vs production keys, or the decrypt–edit–re-encrypt–replay loop.\u003C\u002Fp>\n\u003Ch2>After that night\u003C\u002Fh2>\n\u003Cp>Before standup, my head was still on last night’s login request.\u003C\u002Fp>\n\u003Cp>The proxy already sat on the path. TLS was stripped. Business-layer crypto still lived outside the proxy—external sites, terminal scripts, Postman, each doing its own thing. Change one \u003Ccode>userId\u003C\u002Fcode>? They wanted one or two fields checked, and the whole manual loop had to run again.\u003C\u002Fp>\n\u003Cp>After grinding through that night, a blunt thought surfaced: \u003Cstrong>what if one tool owned capture and business-layer crypto together?\u003C\u002Fstrong> The proxy is already in the chain—decrypt scripts, environment keys, edit-and-replay should not mean hopping across four windows.\u003C\u002Fp>\n\u003Cp>That is roughly where DevPeek started.\u003C\u002Fp>\n\u003Cp>The first piece we wanted to pull in was the part that night would not let go of—\u003Cstrong>outbound requests\u003C\u002Fstrong>. Charles handles TLS; we would handle the layer above: read ciphertext in the proxy, edit plaintext, encrypt again on the way out—and request-only first, because that was the slowest, hardest-to-skip slice of joint debugging.\u003C\u002Fp>\n\u003Cp>That became \u003Cstrong>param transform\u003C\u002Fstrong>. It maps onto the same manual steps, except you never leave the capture UI:\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Conversion config\u003C\u002Fstrong> handles how to decrypt and re-encrypt. Staging key set B vs production key set A lives next to your capture context—no more Confluence hunting. The Python under \u003Ccode>scripts\u002Fdebug\u002F\u003C\u002Fcode> can move into script conversion; common AES setups can use built-in algorithms.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Param transform rules\u003C\u002Fstrong> decide which requests and which parameters get converted. Right-click \u003Ccode>POST \u002Fapi\u002Fuser\u002Flogin\u003C\u002Fcode>, pick URL + \u003Ccode>payload\u003C\u002Fcode>, bind the conversion config—save once, matching traffic hits it automatically.\u003C\u002Fp>\n\u003Cp>Once that is in place, you mostly work in plaintext: decrypted fields show up in details; when backend says “try \u003Ccode>userId\u003C\u002Fcode> = 0,” edit in Mock tamper or the “Debug API” drawer and send—DevPeek re-encrypts on the way out while headers, cookies, and signing stay on the real app path. Signup? Change the URL match, reuse the config.\u003C\u002Fp>\n\u003Cp>The list still records what the client actually sent; plaintext lives in details for comparison. Postman and that three-month-old Python script can step back.\u003C\u002Fp>\n\u003Cp>An hour that night was really about one or two fields. DevPeek grew from there; param transform was the first piece to land—turning that verification into “edit the value, hit send.”\u003C\u002Fp>\n\u003Ch2>Next in the series\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Why We Built DevPeek (2): That H5 page inside the app—debug it on your PC\u003C\u002Fstrong>—mobile pages opened through the proxy, mirrored in the Debug tab, with our own panel for DOM, console, and network—not just squinting at the phone.\u003C\u002Fp>\n\u003Chr>\n\u003Cp>If you are still hand-rolling request bodies under pressure, \u003Ca href=\"\u002F\">try DevPeek\u003C\u002Fa> or say hi on \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FGYPengDev\u002Fdevpeek\u002Fdiscussions\">GitHub Discussions\u003C\u002Fa>—param transform guide: \u003Ca href=\"\u002Fdocs\u002Fparam-transform\u002F\">docs\u003C\u002Fa>.\u003C\u002Fp>\n",{"items":12},[13],{"slug":4,"title":5,"summary":6,"date":7,"featured":8,"seoDescription":9},1783606045253]