<?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>mel</title>
        <link>https://melqtx.com</link>
        <description><![CDATA[what are you looking at fool?]]></description>
        <atom:link href="https://melqtx.com/rss.xml" rel="self"
                   type="application/rss+xml" />
        <lastBuildDate>Tue, 11 Aug 2026 00:00:00 UT</lastBuildDate>
        <item>
    <title>the six digits are just math</title>
    <link>https://melqtx.com/blog/the-six-digits-are-just-math/</link>
    <description><![CDATA[<p>You log into some website. It asks for the six-digit code from your authenticator app. Even when you are off the internet, the code is still there. Thirty seconds later it changes. Somehow the website already knows the new one.</p>
<p>This feels like the phone and server must be secretly talking to each other. They are not. Your phone does not need the internet to receive the code because nobody is sending it a code. It’s just basic maths(insert normans meme)</p>
<p>The phone calculates it. The server calculates the same thing. If both started with the same secret and agree on roughly what time it is, they get the same six digits.</p>
<p>That is basically the entire magic trick.</p>
<h2 id="not-all-of-2fa-works-like-this">Not all of 2fa works like this</h2>
<p>2FA means proving who you are with two different kinds of evidence, or <strong>factors</strong>:</p>
<ul>
<li>Something you know, like a password</li>
<li>Something you have, like your phone or a security key</li>
<li>Something you are, like a fingerprint</li>
</ul>
<p>SMS codes, push notifications, hardware keys, recovery codes, and authenticator apps all work differently. The offline six-digit system we use via google auth or mircoslop is just <strong>TOTP</strong>, or time-based one-time password. A password plus a TOTP code is two-factor authentication because finding the password alone is not enough. The code by itself is not magically “two factor.” It is just the second proof.</p>
<h2 id="the-qr-code-is-where-it-begins">The QR code is where it begins</h2>
<p>When a website asks you to set up an authenticator, it shows a QR code. That square is not opening some live connection to the server. It mostly contains a long random secret and a little metadata about it.</p>
<p>Inside, it looks roughly like this:</p>
<pre class="text"><code>otpauth://totp/example:mel?secret=JBSWY3DPEHPK3PXP&amp;issuer=example</code></pre>
<p>The important part is <code>secret=...</code>. The website generates a long random value there. Scanning the QR code copies it into your authenticator app. Now there are two copies, where the math is being done:</p>
<pre class="text"><code>your authenticator app  ──&gt; same secret &lt;──  the website</code></pre>
<p>The secret is normally written using Base32, which is just a convenient way to represent bytes. It is not encryption.</p>
<p>This is also why the setup QR code is easy to break. Anyone who photographs it can import the same secret and generate the same future codes. Basically like a second password for the time being.</p>
<h2 id="turning-time-into-six-digits">Turning time into six digits</h2>
<p>After setup, both sides runs the same maths to get to the six digit code.</p>
<p>First, take the current Unix time: the number of seconds since January 1, 1970. Then divide it into 30-second windows:</p>
<pre class="text"><code>time_window = floor(unix_time / 30)</code></pre>
<p>Every moment within the same window produces the same number. When the next window starts, the number increases by one. That is the little countdown circle in your authenticator app.</p>
<p>Next, combine this time window with the shared secret using a cryptographic function HMAC. Most authenticator setups use HMAC-SHA1, though the standard also allows SHA-256 and SHA-512:</p>
<pre class="text"><code>digest = HMAC-SHA1(secret, time_window)</code></pre>
<p>HMAC produces a long, unpredictable-looking result. The actual TOTP standard encodes the time window as eight bytes and then uses a step called <strong>dynamic truncation</strong> to select part of that result. Finally, it keeps the last six decimal digits:</p>
<pre class="text"><code>code = truncated_result mod 1,000,000</code></pre>
<p>The remainder is always between <code>000000</code> and <code>999999</code>, which is exactly the six-digit shape we want. Leading zeroes count, so <code>004201</code> is a real code.</p>
<p>In more realistic way, it looks kinda like this:</p>
<pre class="text"><code>counter = floor(current_unix_time / 30)
message = counter encoded as 8 bytes
digest  = HMAC-SHA1(secret, message)

offset  = last 4 bits of digest&#39;s final byte
number  = 4 bytes from digest starting at offset
number  = number with its sign bit removed

code    = number mod 1,000,000</code></pre>
<p>The truncation details look cursed because cryptographic standards are supposed to be complicated for some reason, but the model is simple:</p>
<pre class="text"><code>shared secret + current 30-second window = current code</code></pre>
<p>Same secret, same math, same output.</p>
<h2 id="why-no-internet-is-required">Why no internet is required</h2>
<p>Your phone already has both inputs it needs:</p>
<ol type="1">
<li>The secret it received when you scanned the QR code</li>
<li>The current time from its own clock</li>
</ol>
<p>The website has its copy of the secret and it does its own math. Neither side needs to send the code to the other beforehand. They calculate it independently and only compare results when you log in.</p>
<p>This is similar to two people agreeing on a secret formula and starting their stopwatches together. They can go to opposite sides of the planet and still write down the same answer every thirty seconds.</p>
<p>The authenticator works w/out internet because time still passes. It might even work on another planet, Big if True.</p>
<h2 id="what-if-the-clocks-are-slightly-wrong">What if the clocks are slightly wrong</h2>
<p>Phones and servers are not perfectly synchronized. Your code might arrive near the exact moment one 30-second window becomes the next. If the server checked only its current window, correct codes would randomly fail around that boundary, But they do not work this way, Servers usually check a small range: the current window, and perhaps one window before and after it.</p>
<pre class="text"><code>phone says window 1000
server checks 999, 1000, and 1001</code></pre>
<p>This thingy handles a little clock drift and network delay. Too much tolerance would keep codes valid for longer and make guessing easier, so the window stays small. If your phone’s clock is badly wrong, your codes stop working even though the secret is correct.</p>
<h2 id="why-six-digits-are-enoughish">Why six digits are enoughish</h2>
<p>A six-digit code has only one million possibilities. That sounds large until you remember that, computationally, counting to one million is basically nothing. The reason TOTP survives that tiny search space is that you are not supposed to get a million guesses.</p>
<p>The shared secret is the strong part. A good secret has far more possible values than the displayed code, and HMAC makes it impractical to work backward from observed codes to recover that secret. Seeing <code>381204</code> now does not let you calculate the next code.</p>
<p>The short lifetime also helps. Stealing one code normally gives an attacker only seconds to use it. But “normally” is doing some work there.</p>
<h2 id="hotp-the-version-without-a-clock">HOTP, the version without a clock</h2>
<p>TOTP comes from an older design of HMAC-based one-time password. Instead of the current time window, HOTP uses a counter that increases after every code:</p>
<pre class="text"><code>shared secret + counter = code</code></pre>
<p>TOTP basically replaces “how many codes have we generated?” with “which 30-second window are we in?”. That makes synchronization easier because both devices already have clocks.</p>
<h2 id="tldr">tldr;</h2>
<p>Your authenticator app is not receiving six-digit messages from every service on the screen. It is storing a different shared secret for each account and repeatedly running the same standardised calculation.</p>
<p>When you scan the QR code, the service gives your phone a secret. Every thirty seconds, both sides combine that secret with the current time window using HMAC and shorten the result to six digits.</p>
<p>Nothing is being sent to your phone. There is no daemon whispering codes into it in the background. Both sides simply know the same secret, look at roughly the same clock, and arrive at the same answer.</p>
<p>Very Unix, really: a small secret, a small function, and an unreasonable amount of faith hoping the clock is in sync.
If you want the exact recipes, they live in <a href="https://www.rfc-editor.org/rfc/rfc6238">RFC 6238</a> for TOTP and <a href="https://www.rfc-editor.org/rfc/rfc4226">RFC 4226</a> for HOTP.</p>]]></description>
    <pubDate>Tue, 11 Aug 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/the-six-digits-are-just-math/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>nobody asked for an ai agent</title>
    <link>https://melqtx.com/blog/nobody-asked-for-an-ai-agent/</link>
    <description><![CDATA[<p>I spend enough time around AI that it is very easy to believe everybody else
does too. My timeline is models, evals, harnesses, people giving agents access
to their entire computers, and founders announcing that some job has been
solved forever. Then I talk to literally anyone outside that circle and the
future becomes much less evenly distributed.</p>
<p>They use ChatGPT, obviously. A friend asks it to explain something before an
exam, somebody pastes in an email to make it sound less angry, my cousins use it
instead of Google when they cannot be bothered opening five links. It is already
a very popular product, and pretending otherwise because normal people do not
use it the way we do would be stupid.</p>
<p>But almost none of them use agents.</p>
<p>Nobody I know outside tech is handing a model a goal and letting it work through
their browser, manage their inbox, organize their files, book something, or deal
with all the small administrative garbage that apparently makes up adult life.
There is no feeling that you are missing out if you do not have an agent, no
college group chat suddenly full of people telling each other that this thing
changed everything, and no equivalent of that period where everybody seemed to
join Instagram or start using Uber at once.</p>
<p>This is weird because, at least from inside the bubble, the technology looks
ready. The models can see screens, use browsers, call tools, write code, recover
from mistakes and keep working for hours. Every large tech company has some
version of an agent, and every second startup is building one for lawyers,
recruiters, dentists, support teams, sales teams, landlords, or whatever other
profession appeared in the founder’s dropdown that morning. The demos look
completely insane, the funding announcements say the market is enormous, and
then the demo ends and most people go back to moving information between the
same twelve browser tabs by hand.</p>
<p>I keep coming back to Google because the difference feels almost embarrassingly
simple. Google did not have to teach people that they had a search problem. We
already wanted to know where a place was, how to fix something, what a word
meant, who that actor was, or why our computer had started making that noise.
You opened one page, typed the half-formed question already in your head, and
got something useful without understanding PageRank, information retrieval, or
whatever was happening behind the white screen.</p>
<p>More importantly, Google did not ask for much trust before proving its value.
It did not need your inbox, calendar, files, browser history and credit card to
answer the first question. If it failed, you got a useless link and tried
again. The product met a need everyone already understood, made it much easier,
and kept the machinery out of the way.</p>
<p>Agents begin with a much stranger bargain. Give this model access to a large
part of your digital life, explain the task with enough detail that it does not
misunderstand you, and then supervise it because there is still a chance it
emails the wrong person, buys the wrong flight, fills a form with invented
information, or deletes the one file you actually needed. The model might be
right almost every time, but the remaining failures matter much more once it
can do things instead of merely suggesting them.</p>
<p>So we have reached this funny place where an agent is capable enough to do the
work but not reliable enough for us to stop watching it. Instead of completing
the task ourselves, we supervise a very fast intern that has read the entire
internet and may still experience sudden brain damage halfway through an
expense report.</p>
<p>Progress, ig.</p>
<p>There is another part which I think computer people miss because we have trained
ourselves to see everything as a system. We look at somebody doing the same
annoying task every week and immediately see a workflow: collect these inputs,
call this service, put the result there, add an approval step, done. The person
doing the job probably does not see a workflow at all. They see Monday.</p>
<p>Their work lives across an email from last week, a spreadsheet called
<code>final_v2_REAL.xlsx</code>, two browser tabs, something their manager said during
lunch, and a password sitting in a WhatsApp chat. Half the process is an
unwritten exception to the other half, and the context needed to automate it
exists mostly inside their head. Then somebody arrives with a blank text box
and says an agent can automate their workflow, as if describing the workflow is
not already most of the work.</p>
<p>What workflow bro.</p>
<p>This is probably why people use frontier models as Google plus Grammarly. Those
uses have a shape that makes sense immediately: ask a question and receive an
answer, or paste bad writing and receive less bad writing. The model stays
inside the box, the output can be checked before it leaves, and nothing happens
to the rest of your life because it misunderstood one sentence.</p>
<p>An agent crosses that boundary. It does things, and doing things requires
context, permission and trust, which are exactly the three things most agents
ask you to provide before they have done anything useful enough to deserve
them. We keep treating this as a capability problem because capability is the
part the industry knows how to measure, but I am not sure another twenty points
on a benchmark fixes the feeling of giving a machine control over the parts of
your life where mistakes are annoying, expensive, or embarrassing.</p>
<p>Maybe the real problem is that the current pitch begins with what the agent can
do rather than something a person already wants done. It can browse the web,
use a computer, call hundreds of tools, remember your preferences and operate
for six hours without you. Very cool, but why do I need that?</p>
<p>People did not use Uber because their phones could combine GPS, payments and
real-time driver coordination. They used it because pressing a button and
getting a car was obviously better than standing outside trying to find one.
The complicated technology disappeared behind one clear outcome, while AI
products still make the technology the main event. We talk about the model,
context window, harness, tools, reasoning effort and prompt, which means using
an agent still feels like operating an agent.</p>
<p>Whatever finally reaches everyone probably will not feel like that. Maybe it
lives inside the browser and notices that you open the same five tabs every
Friday, or it handles one annoying task perfectly before asking for access to
the rest of your life. Maybe it earns trust one action at a time, makes every
step legible and reversible, and never once asks the user to build a workflow.
Maybe nobody calls it an agent at all; you just notice that the expense report
is done.</p>
<p>I do think that moment is coming. There is too much capability sitting here for
nothing to happen, and once an agent is both useful and boring enough, letting
it handle things will probably feel as normal as searching Google does now. But
“the models are ready” and “people want this product” are different claims,
and the distance between them might be the entire problem rather than some
temporary inconvenience we solve by making the model larger.</p>
<p>The interesting question is no longer whether an agent can use a computer. It
can. The question is why somebody outside this tiny circle would let it, what
they already need badly enough to take that risk, and how the product proves
itself before demanding access to everything.</p>
<p>Google became Google because nearly everybody had questions and it made getting
answers stupidly easy. Agents become the next Google when they find the same
thing for action: something nearly everybody already wants done, with less
effort than doing it themselves and less anxiety than handing it to a machine.</p>
<p>Until then, we have the most advanced models ever built opening Jira tickets
for people building more AI agents.</p>
<p>Kinda rough lol.</p>]]></description>
    <pubDate>Sat, 08 Aug 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/nobody-asked-for-an-ai-agent/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>after the proof</title>
    <link>https://melqtx.com/blog/after-the-proof/</link>
    <description><![CDATA[<p>Two days ago OpenAI published <a href="https://openai.com/index/ten-advances-in-mathematics/">ten new results in mathematics and theoretical
computer science</a>, all
found by an internal version of Astra, their next model.</p>
<p>The list has sphere packing, non-sofic groups, circuit lower bounds, quantum
games, the closest vector problem, and five other things I understand even less.
I know enough theoretical CS to see a few familiar words and not enough to tell
you if the proofs are actually beautiful.</p>
<p>So yea, I asked a model to explain what the model had done.</p>
<p>The successful runs used enough tokens to cost around $2,000 at Sol API prices.
Humans, with help from the same model, prepared the arguments into a <a href="https://cdn.openai.com/pdf/ten-proofs-oai.pdf">249-page
paper</a>. Astra then formalized
them in Lean, and OpenAI released the <a href="https://github.com/openai/ten-proofs">certificates on
GitHub</a>.</p>
<p>Model finds proof. Humans make paper. Model makes sure proof compiles.</p>
<p>Kinda rough place to be standing in that pipeline lol.</p>
<p>The day after, Fernando Borretti wrote <a href="https://borretti.me/article/mathematics-without-mathematicians">Mathematics Without
Mathematicians</a>.
His point is that people keep finding one last important job for the human. We
will pick the problems. We will explain the answers. We will decide what is
worth keeping. At the very least, somebody has to understand it all.</p>
<p>Then the models get better and that job disappears too.</p>
<p>I wanted to disagree with this. It would be nice if he was just doomerposting
and we could all go back to whatever we were doing. But “humans will have
better taste forever” is not a bet I feel great about making right now. Neither
is “the models can prove it but surely they cannot explain it.” Every permanent
line people draw around us has started looking suspiciously temporary.</p>
<p>What actually bothered me was the Lean part.</p>
<p>The $2,000 makes discovery cheap, but Lean makes the result checkable without
trusting the model’s prose or waiting for a person to understand 249 pages.
Another model can take the checked result, use it somewhere else, and pass that
result further down. Math to science to engineering, and somewhere at the end
we get something that works.</p>
<p>The proof never has to fit inside one person’s head.</p>
<p>I know the small version of this from code. I can send an agent into a project I
know nothing about and get back a working diff. The tests pass, the feature
works, very cool. Then I close the terminal and realize I could not rebuild the
same thing on my own.</p>
<p>Sometimes I learned something. Sometimes the task just got completed near me.
Both look exactly the same in git.</p>
<p>That is the bit I cannot stop thinking about. Not whether the output is real.
The output is real. It is whether anything happened to me while it was being
made. Like, did I spend a single brain cell in the process?</p>
<p>And now I am supposed to go learn some of the subjects in that list. Spend a
few years getting through the basics while the frontier is apparently doing
this.</p>
<p>Amazing timing bro.</p>
<p>If learning was only a long route toward becoming a useful answer-producing
machine, then we may be cooked. There is no honest motivational quote I can put
here that fixes that.</p>
<p><img src="/images/hi.jpg" /></p>
<p>But then I look at the stuff I already make. X already had a website before I
built Xeet. Torrent clients existed before tork. macOS had a settings app before
I rebuilt the same machine 254 times with Nix. None of these projects began
because humanity was stuck waiting for me to save it. Nor did I make the world
a better place or whatever. I saw something, got annoyed, wanted to feel cool,
or was just curious enough to see if I could make it work differently.</p>
<p>Having a better tool already available did not kill that feeling.</p>
<p>Maybe math can still be like that. Not “the humans remain economically
necessary” or some other cope with a six-month expiry date. Just that knowing a
thing and receiving the answer to it are different experiences. A model can
give me a perfect explanation, but it cannot do the understanding for me. That
part still has to happen in my head.</p>
<p>This does not save mathematical jobs, status, or the whole social world around
research. Borretti is probably right that very few people can run on pure
intrinsic motivation forever. I definitely cannot predict what any of this
looks like in ten years.</p>
<p>I also do not need a ten-year philosophy before opening a book today.</p>
<p>Astra can reach the frontier before I understand the problem statement. Fine.
I still want to know why the thing is true.</p>
<p>Maybe that is cope too. idk, ask me again after I figure out what a non-sofic
group is.</p>]]></description>
    <pubDate>Mon, 03 Aug 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/after-the-proof/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>between rebuilds 002</title>
    <link>https://melqtx.com/blog/between-rebuilds-002/</link>
    <description><![CDATA[<p>Mostly Xeet this time. Some tork, some darwin config, a links page, two movies,
and one thing I gave up on.</p>
<p>Atuin says 254 rebuilds. That is twenty since 001, which for me counts as
restraint.</p>
<p><img src="/images/inputstats.png" /></p>
<p>A million keys in a month. 189 km of scrolling, which I refuse to think about.</p>
<h2 id="xeet">Xeet</h2>
<p><a href="https://github.com/melqtx/xeet">Xeet</a> is an X client for the terminal.</p>
<p><img src="/images/xeet.png" /></p>
<p>I built the first version last September with the official API, because that is
what you use. OAuth 1.0a, four credentials, a developer account, an app you have
to name for some reason. So before typing 200 characters in a terminal you had
to go be an X developer for twenty minutes.</p>
<p>I used it a couple of times and never opened it again.</p>
<p>It sat there until I was talking to <a href="https://blitzb.com">blitz</a> about something
else, and somewhere in that conversation the obvious thing landed. The website
posts fine. It has never once asked me for an API key. I log in and it works,
so the browser is already an authenticated client, and I had just decided the
developer API was the only door because it is the one with a sign on it.</p>
<p>Months. Over that.</p>
<p>So, DevTools. Post something, watch the network tab. It is a GraphQL call named
<code>CreateTweet</code> and the auth is just my session, <code>auth_token</code> and <code>ct0</code>, with
<code>ct0</code> repeated in a header. I replayed it outside the browser and deleted fields
until it broke.</p>
<p>That flipped the whole design. Instead of asking you for credentials, <code>xeet auth</code> finds the session already sitting in your browser, checks it, and keeps
its own copy in the keychain.</p>
<p>Then Linux happened. I built all of this on a Mac and had quietly assumed a
browser is a browser, which is wrong, Firefox and Zen keep their cookies
somewhere else entirely. I also had no way to check any of it. So I started
sending binaries to <a href="https://vaeseth.me">vaeseth</a> to run on their machine, and
that back and forth is how it actually started working. hehe.</p>
<p>The other half of the work is keeping an unsupported endpoint alive. The query
IDs in the URL rotate, so Xeet reads the current ones out of the JS bundles the
web app already ships. GraphQL will hand you a 200 with the error in the body. A
create request that times out might have gone through, so retrying can post
twice, which is why mutations here do a read-only check instead of retrying.</p>
<p>Finding the endpoint was not clever. Anyone who opens DevTools can see it.
Sitting on it for months was the actual event.</p>
<h2 id="tork">tork</h2>
<p><a href="https://github.com/melqtx/tork">tork</a> is at <code>v0.3.2</code>.</p>
<p>Best change: the same torrent listed across five indexes now folds into one row
and grabs it with every tracker those indexes knew between them. Better swarm,
and I stop scrolling past six copies of the same release. Filters, verified
downloads, and <code>Y</code> to yank a magnet from anywhere came along too.</p>
<p><code>brew install tork</code>, <code>yay -S tork</code>, <code>nix run github:melqtx/tork</code>. The nixpkgs PR
is still sitting there.</p>
<h2 id="dots">dots</h2>
<p>I have been on Helix for ages, that is not the news. What changed is how much of
the Mac nix-darwin owns now. System settings live in the config instead of in a
pane I have to go find. It is a nice feeling, having the machine written down.</p>
<p>Hammerspoon keeps earning its place. Right command is <code>F18</code>, held it becomes a
hyper layer, <code>hyper + h j k l</code> moves focus. Same motion in Zellij, in Helix, and
between windows.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">nh</span> darwin switch</span></code></pre></div>
<h2 id="the-site">The site</h2>
<p>There is a <a href="/links">/links</a> page now, a live feed of things I read.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">addlink</span> https://example.com/thing paper</span></code></pre></div>
<p>The title gets fetched from the page, and a Discord <code>/link</code> command hits the
same worker.</p>
<p>Site is still Hakyll, markdown in, static HTML out, one flake, pushed to Pages.
Everything on it that moves is a Cloudflare Worker: logs, links, the hit
counter. My photos just live in the repo, so GitHub is my cloud storage now.</p>
<p>Workers are overpowered and I love them. One <code>worker.js</code>, a <code>wrangler.toml</code>,
<code>wrangler deploy</code>, live in seconds, D1 right next to it, all in the free tier.
Nothing to renew, nothing to SSH into. It is the only piece of my setup that has
never broken on me.</p>
<h2 id="elsewhere">Elsewhere</h2>
<p>Watched The Odyssey and it was so good. I will defend Nolan in most rooms and
this did not make that job harder.</p>
<p><img src="/images/odyssey.png" /></p>
<p>The part that got me was Argos. Twenty years, everyone else needs proof, and the
dog just knows immediately, and he has been waiting the whole time for exactly
that one moment and then he is done. Sat there for a second after that one.</p>
<p>Also watched Spider-Man Brand New Day, which ruled. Bro fumbled MJ though, and I
have not really recovered from it. I am choosing to believe that gets fixed.</p>
<p><a href="https://meteorsmp.com">meteorsmp</a> is closed now.</p>
<p><img src="/images/metorsmp.png" /></p>
<p>I miss it more than I expected to. It was genuinely fun, and I do not think I
appreciated at the time that it was a specific set of people at a specific
moment and not really a server at all.</p>
<h2 id="growing-up-apparently">Growing up, apparently</h2>
<p>I have a bad habit of not finishing things. I pick something up, move really
fast with it, get addicted for a few weeks, then drop it like it never existed.
There is no plan behind any of it. Whatever has my attention that week gets all
of me, and when the high wears off I move on and leave the last thing at seventy
percent.</p>
<p>Case in point, I started messing with inference stuff this month. Got somewhere,
hit the part where I actually did not understand what I was doing, decided I was
too dumb for it, and have not opened it in three weeks. Which is the same shape
as Xeet sitting dead from September to now, except Xeet got lucky and had a
conversation happen to it.</p>
<p>I do not have a fix. I just noticed the pattern clearly enough this month that I
cannot pretend it is not there anymore.</p>]]></description>
    <pubDate>Sat, 01 Aug 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/between-rebuilds-002/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>between rebuilds 001</title>
    <link>https://melqtx.com/blog/between-rebuilds-001/</link>
    <description><![CDATA[<p>eh the past two weeks were mostly tork, fucking around with my dots, and rebuilding this site more times than any normal hooman should. atuin says 234 rebuilds.</p>
<p>calling this <strong>between rebuilds</strong>. every 15 days or so ill just dump whatever i was messing with. not a changelog, just like a large log, so i can keep track of time.</p>
<h2 id="tork">tork</h2>
<p><a href="https://github.com/melqtx/tork">tork</a> went from a small torrent search tui to a whole terminal client somehow.</p>
<p>you throw it a search, magnet, infohash, local <code>.torrent</code> file or url and it just figures shit out. groups duplicates, checks the swarm, lets you preview files, handles the download without bouncing you to another app. also got a linux iso section cuz finding official isos shouldn’t take twelve tabs.</p>
<p>working on autopilot too. you can say something like:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">tork</span> autopilot <span class="st">&quot;all breaking bad seasons 1080p under 40GB&quot;</span></span></code></pre></div>
<p>and it searches, picks the good results, explains its choices, and asks before downloading anything.</p>
<p>the annoying part is torrent metadata is very chaotic. names, seasons, sizes, quality tags, everyone’s writing them with different levels of iq. correctly classifying stuff is way harder than making the command look cool. idk how far i wanna take it yet.</p>
<p>strict socks5 proxy support now. searches, http trackers, tcp peers all go through it. dht, utp, udp trackers get disabled so nothing leaks around it.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">tork</span> proxy tor</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">tork</span> doctor <span class="at">--proxy-check</span></span></code></pre></div>
<p>submitted it to nixpkgs, as the repo is still kinda new, so they need some kinda signal that people are using it, that part is still in draft. if we somehow manage to reach like 250ish stars (we wont), i guess then i will try getting it into official homebrew too. for now it’s in my tap.</p>
<p>current release <code>v0.3.1</code> somehow.</p>
<h2 id="dots">dots</h2>
<p>fucked with my <a href="https://github.com/melqtx/dots">dotfiles</a> a ton.</p>
<figure>
<img src="/images/between-rebuilds-001-rice.png" alt="gotta show the rice" />
<figcaption aria-hidden="true">gotta show the rice</figcaption>
</figure>
<p>tried aerospace, amethyst, basically every tiling wm on mac. they all kinda suck rn. or maybe macos dev beta 3 broke everything. or maybe it’s just me idk eh.</p>
<p>tried <a href="https://github.com/apphane-dev/nehir">nehir</a> too, like niri’s scrolling layout but for mac. used it for a bit and it’s actually pretty good ngl. scrolling wms just feel better to my brain than forcing perfect grids all the time.</p>
<p>still ended up doing the parts i care about in hammerspoon anyway.</p>
<p>hhkb right command remapped to <code>F18</code>, hammerspoon turns it into a hyper layer. hold it + <code>h j k l</code> to move focus. the consistency is nice, <code>F18 + h</code> always means left, whether in zellij or windows or just navigation in general. So it feels kinda natural navigating.</p>
<figure>
<img src="/images/between-rebuilds-001-zellij-tabs.png" alt="i might have a tab problem" />
<figcaption aria-hidden="true">i might have a tab problem</figcaption>
</figure>
<p>might have a tab problem in zellij lol.</p>
<p>whole mac is still nix-darwin + home manager so after every experiment:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">nh</span> darwin switch</span></code></pre></div>
<p>and hope i didn’t just invent a new way to break fish. (i actually did)</p>
<h2 id="the-site">the site</h2>
<p>site’s still hakyll. markdown in, static site out. the whole build lives in one nix flake now.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">nix</span> build .#site</span></code></pre></div>
<p>builds the haskell generator, runs hakyll, spits out the site. github actions runs the same thing and publishes to pages.</p>
<p>old ci took like 30min rebuilding the entire haskell world every time. now generator and content are separate nix inputs so changing a post reuses the expensive build. with cache it’s usually around a minute.</p>
<figure>
<img src="/images/between-rebuilds-001-languages.png" alt="eh" />
<figcaption aria-hidden="true">eh</figcaption>
</figure>
<p>it got it all somehow lol, but yea its kinda fast now.</p>
<h3 id="hammerspoon-stats">hammerspoon stats</h3>
<p>weird input numbers on the about page come from hammerspoon.</p>
<p>it watches keypresses, clicks, scrolling, mouse movement, dragging and writes totals to a local json every few seconds. once a day or on publish it copies the snapshot into the site. hakyll reads the json and turns it into that little panel with keys, mouse distance, scroll distance, clicks. completely useless info, which is exactly why i wanted it.</p>
<h3 id="live-logs">live logs</h3>
<p>log panel used to be editing <code>log.md</code>, committing, and redeploying for every one-line thought. funny idea with a terrible implementation.</p>
<p>now <code>log "message"</code> hits a cloudflare worker. worker verifies, cleans it, adds timestamp, stores in d1.</p>
<pre class="text"><code>terminal or discord
        -&gt; cloudflare worker
        -&gt; d1
        -&gt; browser polls /logs every 5s</code></pre>
<p>public site only reads from <code>GET /logs</code>. write token stays on my machine. static pages have a loading placeholder instead of old logs baked in.</p>
<p>same worker handles discord <code>/log</code> command too. both use the same validation and db so i don’t have two different systems.</p>
<h2 id="fin">fin</h2>
<p>that was basically it. torrents, window managers, and a static website slowly becoming less static.</p>
<p>imma go back, my limit is about to reset lol. see you after the next rebuild.</p>]]></description>
    <pubDate>Tue, 14 Jul 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/between-rebuilds-001/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>nix darwin log</title>
    <link>https://melqtx.com/blog/nix-darwin/</link>
    <description><![CDATA[How I setup my Mac with Nix, nix-darwin, home-manager and a bunch of terminal stuff.]]></description>
    <pubDate>Wed, 01 Jul 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/nix-darwin/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>dick feynman</title>
    <link>https://melqtx.com/blog/dick-feynman/</link>
    <description><![CDATA[<p>we as a society need to recognize Richard Feynman more. he was the thing I aspire to be most: too smart to be killed. Nothing could touch that man. He joined the Manhattan Project and promptly broke into every safe in Los Alamos to read all the secret documents just for funsies. The government’s response? “Just don’t let him in your office”. That was the solution. Stop him at the door so he can’t steal military secrets out of boredom. He also constantly messed with the official censors by writing to his relatives in code and filling his mail with powdered Pepto-Bismol. While lecturing in Brazil, he joined a samba band and performed in Carnaval. Just because. He won a Nobel Prize after messing around throwing plates in the air and deciding to figure out the mechanics behind a spinning plate being tossed across a room. I love Richard Feynman so much.</p>
<figure>
<img src="/images/feynman.jpeg" alt="Think Different - Richard Feynman" />
<figcaption aria-hidden="true">Think Different - Richard Feynman</figcaption>
</figure>]]></description>
    <pubDate>Thu, 08 Jan 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/dick-feynman/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>twenty and terrified (in a good way i guess?)</title>
    <link>https://melqtx.com/blog/twenty-and-terrified/</link>
    <description><![CDATA[<p>I turn 20 in August, and I’ve been thinking about how absolutely asleep I was six months ago.
Not literally asleep(maybe), though I did plenty of that lately. I mean asleep in the sense that I thought I understood the trajectory of things knowing that I am the one in control. I’d write some code, AI would maybe autocomplete a line or two if I was lucky, and that was neat but not, you know, scary. The tools were helpers. More like a autocorrect.
Then somewhere between then and now, something shifted, and I didn’t notice until I was already in a different world.</p>
<p>The thing about being insignificant is that it comes in two two pills, and I’m taking both right now.
There’s the normal kind of insignificant you feel at 20. You’re one person. The universe is very large. Your world feels small in retrospect. You haven’t done anything important yet. This is the kind of insignificance that’s almost comfortable(almost) ,it takes the pressure off. You have time to figure things out.
Then there’s this other pill I’m taking now, which is more like: I’m insignificant and the timeline is compressing. I look at what these models could barely do in March and what they can do now, and I try to do the math on what August looks like, let alone next March. The extrapolation makes my mind go brrrr.
Six months ago, I’d spend an afternoon reading something stupid, try to replicate it and then finally figuring it out, feel proud. Now I describe the problem to Claude, get a solution in thirty seconds, and feel… what exactly? Grateful? Obsolete? Both?
The ceiling keeps rising, which should be exciting. And it is! It is exciting. I can build things I couldn’t have imagined building before. I’m learning faster than I ever have. But the floor is rising faster, and I can’t tell if I’m climbing or just being lifted, and I definitely can’t tell what happens when I’m not needed to do the climbing anymore.</p>
<p>Feynman is the only person in history who left a mark on me without me ever meeting him. I read his lectures, watched old videos of him explaining things on blackboards, absorbed this way he had of being completely honest about not knowing something while being utterly delighted to figure it out.(at one point I even knew first 50 digits of pi, just to reach the Feynman constant)</p>
<p>I think about how Feynman would use AI. He wouldn’t ask it to solve his problems, he’d use it to check his own solutions, to argue with, to explore the weird edge cases. He’d probably spend hours trying to make it explain something wrong just to understand where the reasoning breaks down. The tool would be in service of his curiosity, not replacing it. Imagine Claude code playing bongo drums with Feynman.
I’m not Feynman. But most days I’m oscillating. Twenty minutes of genuine fascination, then an hour of low-grade dread. I’ll be excited about what I can build with all this, then suddenly terrified that I’m building skills for a world that won’t exist by the time I’m good at them. The feeling that I need to speedrun becoming competent before competence itself becomes obsolete.
It’s not even different moods on different days. It’s the same day. The same hour. Sometimes the same five minutes.</p>
<p>I haven’t built anything significant in the last month. I’ve mostly been thinking, reading, paralyzed by the sense that whatever I choose to build might be obsolete before I finish it.
Maybe that’s the problem. Feynman didn’t ask “what’s my role?” or “what should I optimize for?” He asked “what’s interesting?” And then he’d just go figure it out, not because it was strategic but because not knowing was slop back then.
I’m watching my friends sprint in different directions. Some have suddenly accelerated and are moving at 100× the speed they were before. Others are acting like nothing is changing, still optimizing for normal career paths. I’m stuck in the middle, refreshing my timeline, reading shit I don’t understand(like at all), waiting for some clarity that never seems to come.</p>
<p>The tree metaphor people love, “the best time to plant a tree was twenty years ago, the second best time is now” well, that doesn’t work anymore. What do you do when you’re not sure if trees matter? When the forest might plant itself? When even the metaphor feels outdated before you finish typing it?</p>
<p>I’m trying to figure out how to be 20 right now. Not the normal kind of 20, where you’re supposed to be figuring out your major and your career and making mistakes that’ll make good stories later. But 20 in 2026, when the timeline for “later” keeps compressing.
I don’t have good answers. Most days I just feel small.
But I think about Feynman, who looked at everything with this open wonder, who thought the universe was there to be figured out and that figuring it out was the point. Not because it would lead somewhere, but because not knowing was worse than knowing.
Six months ago I was asleep. Now I’m awake, even if I’m not sure what to do with that wakefulness yet.
I turn 20 in August. By then, who knows what the models will be able to do. Who knows what I’ll be doing with them, or if “doing” will even mean what it means now.
But I’ll be here. Paying attention. Trying to figure it out.
That has to count for something. Ig.</p>
<p><img src="/images/IMG_1062.JPG" /></p>]]></description>
    <pubDate>Sat, 03 Jan 2026 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/twenty-and-terrified/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>testing features</title>
    <link>https://melqtx.com/blog/test-features/</link>
    <description><![CDATA[<p>let’s test if everything works properly with math, code, and formatting.</p>
<h2 id="inline-math">inline math</h2>
<p>here’s some inline math: <span class="math inline"><em>E</em> = <em>m</em><em>c</em><sup>2</sup></span> and the quadratic formula <span class="math inline">$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$</span>.</p>
<h2 id="display-math">display math</h2>
<p>the gaussian integral:</p>
<p><span class="math display">$$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$$</span></p>
<p>matrix multiplication:</p>
<p><span class="math display">$$\begin{pmatrix} a &amp; b \\ c &amp; d \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} ax + by \\ cx + dy \end{pmatrix}$$</span></p>
<h2 id="physics-equations">physics equations</h2>
<p>the schrödinger equation:</p>
<p><span class="math display">$$i\hbar \frac{\partial}{\partial t}\Psi = \hat{H}\Psi$$</span></p>
<p>maxwell’s equations:</p>
<p><span class="math display">$$\nabla \cdot \mathbf{E} = \frac{\rho}{\epsilon_0}$$</span></p>
<p><span class="math display">$$\nabla \times \mathbf{B} - \mu_0 \epsilon_0 \frac{\partial \mathbf{E}}{\partial t} = \mu_0 \mathbf{J}$$</span></p>
<h2 id="complex-examples">complex examples</h2>
<p>euler’s identity:</p>
<p><span class="math display"><em>e</em><sup><em>i</em><em>π</em></sup> + 1 = 0</span></p>
<p>a sum:</p>
<p><span class="math display">$$\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}$$</span></p>
<p>an integral with limits:</p>
<p><span class="math display">$$\int_0^1 x^2 dx = \frac{1}{3}$$</span></p>
<h2 id="code-blocks">code blocks</h2>
<p>python should be properly highlighted:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> fibonacci(n):</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;&quot;&quot;Return the nth Fibonacci number.&quot;&quot;&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> n <span class="op">&lt;=</span> <span class="dv">0</span>:</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="dv">0</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">elif</span> n <span class="op">==</span> <span class="dv">1</span>:</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="dv">1</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span>:</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> fibonacci(n<span class="op">-</span><span class="dv">1</span>) <span class="op">+</span> fibonacci(n<span class="op">-</span><span class="dv">2</span>)</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="co"># calculate first 10 fibonacci numbers</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">10</span>):</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span>(<span class="ss">f&quot;fibonacci(</span><span class="sc">{</span>i<span class="sc">}</span><span class="ss">) = </span><span class="sc">{</span>fibonacci(i)<span class="sc">}</span><span class="ss">&quot;</span>)</span></code></pre></div>
<p>rust code:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> main() <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> vec <span class="op">=</span> <span class="pp">vec!</span>[<span class="dv">1</span><span class="op">,</span> <span class="dv">2</span><span class="op">,</span> <span class="dv">3</span><span class="op">,</span> <span class="dv">4</span><span class="op">,</span> <span class="dv">5</span>]<span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    vec<span class="op">.</span>iter()</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>map(<span class="op">|</span>x<span class="op">|</span> x <span class="op">*</span> <span class="dv">2</span>)</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>filter(<span class="op">|</span>x<span class="op">|</span> x <span class="op">&gt;</span> <span class="op">&amp;</span><span class="dv">5</span>)</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>for_each(<span class="op">|</span>x<span class="op">|</span> <span class="pp">println!</span>(<span class="st">&quot;{}&quot;</span><span class="op">,</span> x))<span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 id="block-quotes">block quotes</h2>
<blockquote>
<p>if the individual lived five hundred or one thousand years, this clash might not exist or at least might be considerably reduced. he then might live and harvest with joy what he sowed in sorrow.</p>
</blockquote>
<h2 id="lists">lists</h2>
<p>unordered list:</p>
<ul>
<li>first item</li>
<li>second item with <strong>bold text</strong></li>
<li>third item with <em>italic text</em></li>
</ul>
<p>ordered list:</p>
<ol type="1">
<li>first item</li>
<li>second item with <code>inline code</code></li>
<li>third item</li>
</ol>
<h2 id="tables">tables</h2>
<table>
<thead>
<tr class="header">
<th>column 1</th>
<th>column 2</th>
<th>column 3</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>cell 1,1</td>
<td>cell 1,2</td>
<td>cell 1,3</td>
</tr>
<tr class="even">
<td>cell 2,1</td>
<td>cell 2,2</td>
<td>cell 2,3</td>
</tr>
<tr class="odd">
<td>cell 3,1</td>
<td>cell 3,2</td>
<td>cell 3,3</td>
</tr>
</tbody>
</table>
<h2 id="mixed-content">mixed content</h2>
<p>combining math and code: the fibonacci sequence can be expressed as <span class="math inline"><em>F</em><sub><em>n</em></sub> = <em>F</em><sub><em>n</em> − 1</sub> + <em>F</em><sub><em>n</em> − 2</sub></span> with base cases <span class="math inline"><em>F</em><sub>0</sub> = 0</span> and <span class="math inline"><em>F</em><sub>1</sub> = 1</span>.</p>
<p>here’s a function that uses memoization:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> fib <span class="op">=</span> (n<span class="op">,</span> memo <span class="op">=</span> {}) <span class="kw">=&gt;</span> {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> (n <span class="kw">in</span> memo) <span class="cf">return</span> memo[n]<span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> (n <span class="op">&lt;=</span> <span class="dv">1</span>) <span class="cf">return</span> n<span class="op">;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    memo[n] <span class="op">=</span> <span class="fu">fib</span>(n <span class="op">-</span> <span class="dv">1</span><span class="op">,</span> memo) <span class="op">+</span> <span class="fu">fib</span>(n <span class="op">-</span> <span class="dv">2</span><span class="op">,</span> memo)<span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> memo[n]<span class="op">;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span></span></code></pre></div>
<p>the time complexity is reduced from <span class="math inline"><em>O</em>(2<sup><em>n</em></sup>)</span> to <span class="math inline"><em>O</em>(<em>n</em>)</span> using dynamic programming.</p>]]></description>
    <pubDate>Sun, 21 Dec 2025 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/test-features/</guid>
    <dc:creator>mel</dc:creator>
</item>
<item>
    <title>binaries and text</title>
    <link>https://melqtx.com/blog/binaries-and-text/</link>
    <description><![CDATA[<p>every file on your computer is a sequence of bytes. a byte is just a number from 0 to 255. the file itself does not come with a tiny label saying “i am text” or “i am an image”. software has to decide what those numbers mean.</p>
<p>that is the whole trick behind text and binary files:</p>
<ul>
<li>a <strong>text file</strong> interprets its bytes as characters using an encoding such as utf-8</li>
<li>a <strong>binary file</strong> interprets its bytes according to some other format, such as png, mp3, zip, or a program’s own data structure</li>
</ul>
<p>both are made of bytes. “text” and “binary” describe how we read those bytes.</p>
<h2 id="the-same-bytes-can-mean-different-things">the same bytes can mean different things</h2>
<p>put the word <code>hello</code> in a file and inspect it:</p>
<pre class="console"><code>$ printf hello &gt; hello.txt
$ xxd hello.txt
00000000: 6865 6c6c 6f                             hello</code></pre>
<p>the left side shows the bytes in hexadecimal. <code>68</code> represents <code>h</code> in utf-8 (and ascii), <code>65</code> represents <code>e</code>, and so on. a text editor knows that mapping, so it shows <code>hello</code> instead of five numbers.</p>
<p>now take the bytes at the beginning of a png:</p>
<pre class="text"><code>89 50 4e 47 0d 0a 1a 0a</code></pre>
<p>those bytes are a <strong>file signature</strong>. a png reader recognizes them and starts decoding image dimensions, colors, pixels, and metadata. a text editor tries to turn the same bytes into characters and mostly produces nonsense. the bytes did not change; the interpretation did.</p>
<h2 id="what-makes-text-readable">what makes text readable</h2>
<p>an encoding is an agreement between bytes and characters. ascii covers basic english characters. utf-8 covers those same characters plus writing systems, symbols, and emoji from across unicode.</p>
<p>for example, <code>A</code> is one byte in utf-8:</p>
<pre class="text"><code>41</code></pre>
<p>the character <code>é</code> takes two:</p>
<pre class="text"><code>c3 a9</code></pre>
<p>this is why “one character equals one byte” is a bug waiting to happen. the string <code>café</code> has four characters but five bytes in utf-8. many emoji use four bytes, and some visible symbols are made from several unicode characters.</p>
<p>an encoding mismatch is what gives you text like <code>cafÃ©</code>. the file may be perfectly intact; it was simply decoded using the wrong rules.</p>
<p>utf-8 is the sensible default for new text files. when opening text in code, specify it instead of depending on whatever default the operating system happens to use:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="cf">with</span> <span class="bu">open</span>(<span class="st">&quot;notes.txt&quot;</span>, <span class="st">&quot;r&quot;</span>, encoding<span class="op">=</span><span class="st">&quot;utf-8&quot;</span>) <span class="im">as</span> <span class="bu">file</span>:</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    notes <span class="op">=</span> <span class="bu">file</span>.read()       <span class="co"># str: decoded characters</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="cf">with</span> <span class="bu">open</span>(<span class="st">&quot;photo.png&quot;</span>, <span class="st">&quot;rb&quot;</span>) <span class="im">as</span> <span class="bu">file</span>:</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    photo <span class="op">=</span> <span class="bu">file</span>.read()       <span class="co"># bytes: the original byte values</span></span></code></pre></div>
<p>in python, text mode decodes bytes into a <code>str</code>. binary mode returns <code>bytes</code> unchanged. writing works in reverse: text mode encodes a string, while binary mode expects bytes.</p>
<h2 id="formats-are-more-useful-than-extensions">formats are more useful than extensions</h2>
<p>extensions are hints, not proof. renaming <code>photo.png</code> to <code>homework.txt</code> does not turn its pixels into prose. many formats identify themselves using a signature near the beginning of the file:</p>
<ul>
<li>png starts with <code>89 50 4e 47 0d 0a 1a 0a</code></li>
<li>jpeg usually starts with <code>ff d8 ff</code></li>
<li>pdf starts with <code>%PDF-</code></li>
<li>zip commonly starts with <code>50 4b 03 04</code>, which looks like <code>PK</code> in ascii</li>
</ul>
<p>some familiar files are containers for other files. a <code>.docx</code>, for example, is a zip archive containing xml, images, and metadata. it has text inside it, but the document as a whole must be handled as a zip-based binary format. this is why the text/binary distinction is useful, but not a perfect taxonomy.</p>
<p>on unix-like systems, a few commands make unknown files less mysterious:</p>
<pre class="console"><code>file mystery.dat             # make an educated guess about the format
xxd mystery.dat | head       # inspect the first few bytes
strings mystery.dat | head   # find readable runs of text inside it</code></pre>
<p><code>file</code> checks the contents instead of trusting the name. <code>xxd</code> gives you an exact view of the bytes. <code>strings</code> is handy when a binary contains error messages, paths, or metadata, but its output is only a clue—not a safe representation of the whole file.</p>
<h2 id="how-binary-files-get-corrupted">how binary files get corrupted</h2>
<p>opening a binary file in a text editor does not normally hurt it. saving it can.</p>
<p>a text editor may try to decode the bytes, replace sequences it considers invalid, change the character encoding, or convert line endings. once it writes those changes back, the original byte sequence is gone and the program expecting a png, zip, or executable may reject it.</p>
<p>line endings are a smaller version of the same problem. unix text files normally end lines with the byte <code>0a</code> (<code>LF</code>). windows traditionally uses <code>0d 0a</code> (<code>CRLF</code>). text-aware tools may translate between them. that is helpful for prose and source code, but disastrous if those bytes are part of a binary structure.</p>
<p>when copying or hashing binary data, work with bytes all the way through:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> pathlib <span class="im">import</span> Path</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>data <span class="op">=</span> Path(<span class="st">&quot;source.png&quot;</span>).read_bytes()</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>Path(<span class="st">&quot;copy.png&quot;</span>).write_bytes(data)</span></code></pre></div>
<p>if the copy is supposed to be exact, compare hashes:</p>
<pre class="console"><code>sha256sum source.png copy.png</code></pre>
<p>the two hashes should match. on macos, use <code>shasum -a 256</code> if <code>sha256sum</code> is not installed.</p>
<h2 id="binary-data-inside-text-systems">binary data inside text systems</h2>
<p>sometimes binary data has to travel through something that only accepts text, such as json, an email body, or an environment variable. base64 solves this by representing arbitrary bytes with ordinary text characters.</p>
<p>that does <strong>not</strong> make the data human-readable, compressed, or encrypted. it is just a reversible transport encoding, and it makes the data roughly one-third larger.</p>
<pre class="console"><code>base64 &lt; tiny.png &gt; tiny.png.b64
base64 --decode &lt; tiny.png.b64 &gt; restored.png   # common on linux
base64 -D &lt; tiny.png.b64 &gt; restored.png         # macos</code></pre>
<p>if a protocol already supports raw bytes—an http response body or a multipart file upload, for example—base64 is often unnecessary overhead.</p>
<h2 id="choosing-a-format">choosing a format</h2>
<p>use text when people should be able to inspect, edit, diff, or repair the data with ordinary tools. source code, configuration, logs, csv, json, and markdown benefit from that transparency.</p>
<p>use a binary format when compact size, exact types, fast parsing, compression, or media-specific structure matters. images, audio, video, archives, databases, and compiled programs usually belong here.</p>
<p>neither one is automatically better. json is easy to debug but can be verbose and cannot directly represent raw bytes. a binary serialization format can be smaller and preserve types precisely, but it needs compatible tooling. choose based on who must read the data and what the system needs to do with it.</p>
<h2 id="the-short-version">the short version</h2>
<p>files are bytes. formats give those bytes structure. encodings turn some of them into text.</p>
<p>when a file behaves strangely, do not just stare at its extension. ask four questions:</p>
<ol type="1">
<li>what format are these bytes supposed to follow?</li>
<li>if it is text, which character encoding does it use?</li>
<li>is any tool silently converting the encoding or line endings?</li>
<li>do i need characters here, or do i need the original bytes?</li>
</ol>
<p>that mental model is enough to explain most file corruption, mojibake, failed uploads, and “works on my machine” encoding bugs. everything else is just learning the rules of the particular format in front of you.</p>]]></description>
    <pubDate>Sun, 11 May 2025 00:00:00 UT</pubDate>
    <guid>https://melqtx.com/blog/binaries-and-text/</guid>
    <dc:creator>mel</dc:creator>
</item>

    </channel>
</rss>
