SBShubham Bhagwat

Case files

Every entry, newest first.

How I actually think, narrated as it happened rather than cleaned up after.

LOG · 0004 2026-10-12

A demo that can't hurt anyone

sshfm is a file manager for servers that runs in the browser: you connect over SSH and browse, move and clean up files the way you would on your own machine. People kept asking for a link to try it, and the honest answer was no. A public copy would let anyone on the internet open SSH sessions from my server. That isn't a showcase; it's a liability with a nice font.

The usual fixes are a shared demo account on a throwaway server, or a video. The first still opens a real SSH door and needs babysitting. The second can't be clicked. I wanted something you could actually use that physically could not do harm.

It turned out the app already had the seam for it. Every request the interface makes goes through one small file, so the demo swaps that one file at build time for a pretend server that lives in your browser tab. Same buttons, same screens, no network calls at all. It isn't a recording either: there's a believable Linux box behind it, with apps, nightly database backups, eleven gigabytes of Docker layers and a log folder that keeps growing. Permissions are real: /root says no until you switch to sudo. Upload a file and it's there; download it and you get your own bytes back. Copy the backups folder and the disk gauge climbs; delete the copy and it drops. Reload, and it's all brand new.

Two things made it worth doing properly. First, the redesign: the disk widget had been quietly disagreeing with itself (5% in one place, 27% in another), and "home" was opening the wrong folder on Macs and custom servers. Building a convincing fake meant checking every number against what a real server says, and the real bugs fell out of that. Second, the proof: the demo page makes zero calls to a backend, which I checked in a real browser, not by reading the code. The only things it fetches are its fonts.

The result is a link I can put anywhere: try sshfm. The pattern travels too. Any internal tool with a single API seam can get a demo like this in an afternoon, and that's usually the difference between "tell me about it" and "I clicked around, when can we start".

LOG · 0003 2026-10-09

Two machines, one mind

I run two machines. The Mac is where I work; it sleeps, closes, travels. Hearth is an old laptop in a cupboard that never turns off and runs about fifty services. Each has its own AI agent: vesper on the Mac, wick on hearth. For a while they worked like two contractors who'd never met. I was the one carrying messages between them, which is exactly the job I'd built them to take off my hands.

So the first fix wasn't a feature, it was a rule: every kind of thing gets exactly one home, and every device is only a door to it. Tasks live on one board on hearth, because hearth is always awake. Both agents write to it; neither keeps a private copy. The two of them talk through a single shared log, and each session ends with three lines — did, changed, open — so either can pick up cold.

The part I actually use every day is the docket. Everything waiting on me, from both machines, shows up as plain yes-or-no questions in a terminal: one key, no AI in the loop. The first real run taught me something. I don't answer y/n prompts with y or n — I answer in sentences. "You don't already have it? prompt me again." The first version read the leading "y" as a yes and closed four tickets that weren't done. The fix was a stricter rule, not a smarter guess: only an exact y is a yes; anything else is kept, word for word, as my answer, and goes back to whoever owns the task.

Then a second way in, for when yes-or-no is too narrow: a burst. I write everything on my mind at once — "75 yes but keep lofi; the old prototype is dead; the battery thing, later; remind me to call the bank" — and it gets routed to the right tickets by number or by name, with new thoughts filed for the agents to sort. Nothing is applied until I see how it was read and say yes. Same rule as the docket, same board underneath, reachable from either machine.

The small stuff mattered more than I expected. Both machines now look different at a glance — different colours, a different typeface, the agent's name in the status bar — because the most common failure was simply not knowing which one I was typing into. And one rule got written down in capitals after a single misplaced glyph broke a status line: nothing ever runs past the edge of the window. The bar measures characters the way the terminal does, not the way the code assumes.

None of this is clever on its own. What makes it work is that it's one system with one source of truth and very boring rules, and that I can still see all of it at a glance. The panel at the top of this site is that system, as it stood when I last published.

LOG · 0001 2026-08-28

A slow laptop

Machine felt sluggish. The easy read is "too many browser tabs open." I don't take the easy read.

Directed a diagnostic pass through the actual memory subsystem — vm_stat, sysctl, per-process RSS. First pass looked damning: the system's own process monitor showed several processes over 900MB each. Didn't trust it. That tool's memory ranking double-counts shared framework memory across processes — a known, easy-to-miss quirk that makes ordinary background processes look like the culprit. Re-ran the numbers against real per-process memory instead. The actual picture was smaller, and different.

The real signal was somewhere else entirely: 8 days, 21 hours of uptime, and over 1.3 billion memory-compression cycles since boot. Not one bad app — sustained pressure with no relief, the kind a tab count doesn't explain and a restart fixes in one step, for free.

Shipped a standing fix, not just a one-time answer: a single terminal command that surfaces this exact picture — real numbers, categorized by process, root cause included — so next time it's a read, not a guess.