<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  
  <title>Johannes Stillig</title>
  <subtitle>Notes on AI, security and technology - and which of it survives a normal Tuesday.</subtitle>
  <link href="https://stillig.net/feed.xml" rel="self" />
  <link href="https://stillig.net/" />
  <updated>2026-07-29T18:00:00Z</updated>
  <id>https://stillig.net/</id>
  <author>
    <name>Johannes Stillig</name>
  </author>
  <entry>
    <title>The attacker can prove what they took. You can only prove what you logged.</title>
    <link href="https://stillig.net/posts/prove-what-they-took/" />
    <updated>2026-07-29T18:00:00Z</updated>
    <id>https://stillig.net/posts/prove-what-they-took/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;I&#39;m Johannes. I have spent eighteen years on the response side of security, and the thing I keep returning to is that a control is only worth what you can prove about it afterwards.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In roughly one breach in five, the first person to tell the world what happened to your customers&#39; data is the person who took it.&lt;/p&gt;
&lt;p&gt;IBM&#39;s numbers this year: 50% of breaches are found by the organisation&#39;s own security teams, 31% by a benign third party, and 19% are disclosed by the attacker. That last group is the one worth sitting with. It means the opening account of the incident is written by someone with a motive, a timeline that suits them, and a folder of your data to quote from.&lt;/p&gt;
&lt;p&gt;Everyone prepares for the regulator. The regulator gives you a private conversation, a phased process, and lawyers on both sides who understand that facts take time. That is the easy adversary. Nothing about the public version of a breach works like that.&lt;/p&gt;
&lt;p&gt;Here is the asymmetry nobody plans for, and it is not about security at all.&lt;/p&gt;
&lt;p&gt;The attacker can prove what they took. They have it. They can post a thousand records and let anyone verify the claim in an afternoon.&lt;/p&gt;
&lt;p&gt;You can only prove what you logged.&lt;/p&gt;
&lt;p&gt;If you did not log it, you cannot rebut them. You can deny, and a denial without evidence is a press release. Their claim comes with a sample. Yours comes with a spokesperson. There is no contest.&lt;/p&gt;
&lt;h2 id=&quot;the-ninety-minute-silence&quot; tabindex=&quot;-1&quot;&gt;The ninety minute silence&lt;/h2&gt;
&lt;p&gt;The shape of a bad breach is always the same and it is not the shape people expect.&lt;/p&gt;
&lt;p&gt;Nobody argues about whether to contain. That part is well drilled and everybody knows their lines. The hard question lands about ninety minutes in, usually from the general counsel or the comms lead, and it is always a version of: what did they actually take?&lt;/p&gt;
&lt;p&gt;Then the room goes quiet.&lt;/p&gt;
&lt;p&gt;Not because anybody is incompetent. Because answering it requires knowing which systems were touched, what lived on them, which records were read rather than merely reachable, and whether anything left the estate. That is four separate evidence problems, and every one of them is answerable only from data that somebody chose to collect, at a retention period somebody chose, long before there was a question.&lt;/p&gt;
&lt;p&gt;I have sat in that silence more than once. It is the most expensive thirty seconds of the whole incident, because everything public downstream is decided in it.&lt;/p&gt;
&lt;h2 id=&quot;absence-of-evidence-is-not-neutral&quot; tabindex=&quot;-1&quot;&gt;Absence of evidence is not neutral&lt;/h2&gt;
&lt;p&gt;This is the part I would most like people to take away, because it runs against instinct.&lt;/p&gt;
&lt;p&gt;Security teams treat &amp;quot;we have not found evidence of exfiltration&amp;quot; as a neutral, honest, cautious statement. It is not neutral. In public it reads as &amp;quot;we do not know,&amp;quot; and into that gap flows the worst plausible number.&lt;/p&gt;
&lt;p&gt;Once a figure is circulating, you inherit it. If a leak site claims four million records and you cannot say otherwise with evidence, four million is the number in every article, every customer email, every board pack, and every counterparty&#39;s risk review for the next two years. You can correct it later. Corrections never travel as far as the original, and the people who mattered most have already updated.&lt;/p&gt;
&lt;p&gt;So the practical stakes are not the fine. They are these, and all of them are decided by whether you can produce a credible, specific, fast answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Whether you notify everyone or the affected. One of those is survivable.&lt;/li&gt;
&lt;li&gt;Whether your largest customers hear a number from you or read one somewhere else.&lt;/li&gt;
&lt;li&gt;Whether &amp;quot;we contained it in days and here is exactly what was in scope&amp;quot; is available to you as a sentence at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a period during my career I ran a cyber practice inside a law firm, acting for people whose exposure was reputational long before it was regulatory. That work changes how you see this. For those clients the fine was a rounding error. The story was the whole event, and the story was decided almost entirely by who could evidence their version first.&lt;/p&gt;
&lt;h2 id=&quot;the-clock-you-are-actually-racing&quot; tabindex=&quot;-1&quot;&gt;The clock you are actually racing&lt;/h2&gt;
&lt;p&gt;The 172 is the number that makes this hard.&lt;/p&gt;
&lt;p&gt;In the best measured case this year, where an organisation&#39;s own security team found the breach itself, it took 172 days to identify and another 52 to contain. That is the good outcome. Third party and supply chain compromise took the longest of anything: 196 days to identify, 71 to contain, 267 in total, on data that is yours and logs that belong to somebody else.&lt;/p&gt;
&lt;p&gt;So the attacker has had most of a year inside an estate you were not watching closely enough to notice, and you have somewhere between hours and a couple of days before the public version sets.&lt;/p&gt;
&lt;p&gt;You are not racing the regulator&#39;s clock. You are racing the first article.&lt;/p&gt;
&lt;p&gt;And it does not end quickly. 86% of breached organisations reported operational disruption. 65% said they had not fully recovered. 45% raised prices afterwards, about a third of them by more than 15%, which is the part your customers experience directly and remember longer than the incident.&lt;/p&gt;
&lt;h2 id=&quot;you-cannot-investigate-your-way-out-of-logs-you-never-kept&quot; tabindex=&quot;-1&quot;&gt;You cannot investigate your way out of logs you never kept&lt;/h2&gt;
&lt;p&gt;The instinct after a breach is to buy investigation: forensics, threat intel, a bigger bridge call. That is the wrong end of the problem. The speed of your answer was set months earlier by a handful of unglamorous decisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Know what data you hold and where.&lt;/strong&gt; Not a data map produced once for an audit and never reconciled since. Something that would let you say in an afternoon what categories of personal data sit on a given host. Most organisations discover mid-incident that this document has been wrong for two years.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log reads, not just writes.&lt;/strong&gt; Nearly everyone logs authentication and change. Far fewer log access to the data itself at a granularity that separates &amp;quot;this account could have read ten million records&amp;quot; from &amp;quot;this account did read four thousand.&amp;quot; That distinction is the difference between a notification to everybody and a notification to the affected. It is the most commonly missing piece I have seen, and it is the one that decides your public number.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Set retention longer than your detection time.&lt;/strong&gt; If logs roll at 30 or 90 days and breaches are found at 172, you have built a system that guarantees the evidence expires before the question is asked. Cheapest fix on this list, most often skipped, because retention is a line item nobody has to defend until the week it decides everything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get evidence rights from vendors in the contract.&lt;/strong&gt; Not a security questionnaire. A clause covering what they preserve, for how long, and how fast they hand it over. Ask at renewal, when you have leverage, rather than at 2am, when you have none. Given supply chain compromise takes 267 days, this is where the gap is widest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rehearse the scope question on its own.&lt;/strong&gt; Most tabletops rehearse containment and comms, which are the parts you are already good at. Run one where the only objective is to establish what was taken, and put a clock on it. The result is usually clarifying and mildly humiliating.&lt;/p&gt;
&lt;p&gt;None of that is a product and all of it is boring. That is the point. The organisations that hold the narrative are not the ones with the best incident response. They are the ones who made dull decisions about evidence eighteen months earlier.&lt;/p&gt;
&lt;h2 id=&quot;the-evidence-that-arrives-late&quot; tabindex=&quot;-1&quot;&gt;The evidence that arrives late&lt;/h2&gt;
&lt;p&gt;Everything above depends on decisions you already made, or did not. There is one source of evidence that does not work that way, and it is the one victims handle worst.&lt;/p&gt;
&lt;p&gt;When data is posted on a leak site, sold, or sent to you as proof of the claim, that dump is evidence. It is the attacker&#39;s own account of what they took, in the most verifiable form that exists: the data itself. It owes nothing to your retention policy or your logging coverage. It is simply there, and it is frequently the fastest available route to a number you can actually stand behind.&lt;/p&gt;
&lt;p&gt;Almost nobody can use it in time.&lt;/p&gt;
&lt;p&gt;It shows up as hundreds of gigabytes, sometimes terabytes, of mixed and broken formats. Some of it is junk. Some of it is recycled from older breaches and pasted in to inflate the claim. Some of it is fabricated outright for the same reason. Establishing which records are real, which are yours, which are still current, and which actual people are behind them is a data engineering problem of real size, and it lands on a security team during the worst week of its year, usually with a spreadsheet and a deadline measured in hours.&lt;/p&gt;
&lt;p&gt;So the clock runs. Weeks go by on &amp;quot;we are investigating the scope&amp;quot; while the attacker&#39;s number, which cost them an afternoon to assert, stands unchallenged and hardens into the accepted figure.&lt;/p&gt;
&lt;p&gt;That gap is closable. It is a processing problem, not a mystery, and the reason it stays open is that almost nobody has treated it as one.&lt;/p&gt;
&lt;h2 id=&quot;what-i-got-wrong&quot; tabindex=&quot;-1&quot;&gt;What I got wrong&lt;/h2&gt;
&lt;p&gt;I would rather not fill this section with other people&#39;s incidents. That is cheap, and it spends confidentiality that is not mine to spend. So here is mine, and it is the part of my own career I am least comfortable with.&lt;/p&gt;
&lt;p&gt;I started out breaking into things. Years of hands-on penetration testing before I ever sat on the response side of anything.&lt;/p&gt;
&lt;p&gt;A penetration test answers one question well: can someone get in. The report says how, and what was reached, and it is graded on the depth of access demonstrated. I do not remember ever writing a section on whether the client could have reconstructed what I did after I left. Nobody asked for one. It was not what the engagement was for.&lt;/p&gt;
&lt;p&gt;So I spent the formative years of my career producing exactly one half of the problem I am now describing to you.&lt;/p&gt;
&lt;p&gt;I always knew precisely what I had touched, because I kept a log of my own work. The client got my report, which is not the same thing at all. It is my account, offered voluntarily, by someone with an interest in it being impressive. A real intruder writes no report.&lt;/p&gt;
&lt;p&gt;That asymmetry sat in front of me for years and I read it as a scoring system rather than a defect. It took being on the other side, watching a room go quiet, to notice that the thing I was best at was precisely the thing that left the client with no evidence of their own.&lt;/p&gt;
&lt;p&gt;I do not think that is only a personal failing. The whole offensive industry still works this way. We prove access. We almost never test whether the target could have told.&lt;/p&gt;
&lt;h2 id=&quot;what-is-still-unsolved&quot; tabindex=&quot;-1&quot;&gt;What is still unsolved&lt;/h2&gt;
&lt;p&gt;The honest frontier is not better logging. It is knowing, before an incident, whether the evidence you hold would actually answer the question.&lt;/p&gt;
&lt;p&gt;You can audit whether logging is enabled. You cannot easily audit whether your logs would let you establish, inside a day, which records left the estate. Those are different properties and only the second one matters at 2am. Nearly every organisation I have looked at assumes it has the second because it has measured the first.&lt;/p&gt;
&lt;p&gt;I do not have a good test for it either. If you have built one, I would genuinely like to hear how it works.&lt;/p&gt;
&lt;h2 id=&quot;what-to-take-from-this&quot; tabindex=&quot;-1&quot;&gt;What to take from this&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;In about one breach in five, the first public account is written by the attacker.&lt;/li&gt;
&lt;li&gt;The attacker can prove what they took. You can only prove what you logged.&lt;/li&gt;
&lt;li&gt;Absence of evidence is not neutral. In public it defaults to their number.&lt;/li&gt;
&lt;li&gt;&amp;quot;We are investigating the scope&amp;quot; is a sentence you inherit for two years.&lt;/li&gt;
&lt;li&gt;You cannot investigate your way out of logs you never kept. Evidence is a decision you made six months ago.&lt;/li&gt;
&lt;li&gt;Retention shorter than your detection time guarantees the evidence expires before the question arrives.&lt;/li&gt;
&lt;li&gt;Log reads, not just writes. &amp;quot;Could have accessed&amp;quot; and &amp;quot;did access&amp;quot; are different notifications.&lt;/li&gt;
&lt;li&gt;Auditing that logging is on is not the same as auditing that it would answer the question.&lt;/li&gt;
&lt;li&gt;The dump is evidence too, and it is the only evidence that arrives after the decisions were made. Being able to read it fast is worth more than most of your tooling.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything above is checkable, which is the standard I try to hold my own work to. It is also why I spend my days building an investigations engine that turns a dump like that into an answer in hours instead of weeks, with every finding traced back to the source bytes it came from, because the difference between a victim who can say what was taken and one who cannot is mostly a data problem nobody has been treating as one: &lt;a href=&quot;https://www.tidebreak.ai&quot;&gt;that work is here&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;source&quot; tabindex=&quot;-1&quot;&gt;Source&lt;/h2&gt;
&lt;p&gt;IBM, &lt;em&gt;Cost of a Data Breach Report 2025&lt;/em&gt;. Detection split (Figure 13), lifecycle by detection method (Figure 15), lifecycle by attack vector (Figure 10), operational disruption and recovery (pages 22 and 41), post-breach pricing (Figures 36 and 37). https://www.ibm.com/reports/data-breach&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>I let AI agents run my life. They can read everything and send nothing.</title>
    <link href="https://stillig.net/posts/i-let-ai-agents-run-my-life/" />
    <updated>2026-07-28T18:00:00Z</updated>
    <id>https://stillig.net/posts/i-let-ai-agents-run-my-life/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;I&#39;m Johannes. I build systems that run on their own without going off the rails: a security product during the day, and this at night. If there&#39;s a thread through everything I make, it&#39;s that autonomy should be earned structurally, not hoped for.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Right now, while I&#39;m writing this, software I built is reading my email. It knows my calendar. It has my contacts. Every morning it hands me a brief of what matters, and overnight it files what came in, drafts the replies I owe, and keeps my tasks in sync across my devices. I did not check any of it this morning. I did not have to.&lt;/p&gt;
&lt;p&gt;If that sentence makes you slightly uncomfortable, good. It should, and the discomfort is the whole point of the design.&lt;/p&gt;
&lt;p&gt;Everyone who builds a &amp;quot;second brain&amp;quot; builds one to &lt;em&gt;remember&lt;/em&gt;: a place to capture notes and organize them so a human can read them back later. Storage. Mine does almost none of that. I built one to &lt;em&gt;act&lt;/em&gt;. It is ten small autonomous agents, running on a schedule on my own laptop, that do the boring, consequential, easy-to-drop work of a day: triage the inbox, file what&#39;s filable, draft what needs a human, brief me, and once a week think about my own notes and tell me what I&#39;m circling.&lt;/p&gt;
&lt;p&gt;The idea of an external memory is old, and it has a good lineage. Vannevar Bush imagined the Memex in 1945. Douglas Engelbart spent the 1960s on a research program he simply called augmenting human intellect. Niklas Luhmann kept a paper slip-box of ninety thousand notes that out-wrote entire departments, a method Sönke Ahrens later made usable for the rest of us. And more recently Tiago Forte gave the whole thing a name, the Second Brain, while tools like Roam and Obsidian handed a generation of us bidirectional links and a cult. I have read most of it, and I owe it. But notice what every one of those is about: remembering. Capturing, connecting, recalling, so a human can think better later. I wanted the other end of the problem. I did not need help remembering. I needed the remembering to do something without me.&lt;/p&gt;
&lt;p&gt;Handing that much of your life to AI agents is either reckless or it isn&#39;t, and the difference is entirely in two design decisions. The first: it can read everything and send nothing. It will draft an email; it will never send one. Anything that would reach another person waits for me. The second: none of it touches a cloud. Not &amp;quot;we promise not to look&amp;quot; - it physically cannot leave the machine, and I&#39;ll show you exactly why in a moment.&lt;/p&gt;
&lt;p&gt;This is the same bet I make when I let AI agents commit to my codebase overnight, just pointed at my life instead of my work: give them real autonomy, and engineer it so the autonomy is safe rather than trusted. And as with anything I actually run, I&#39;ll tell you which parts earn their keep and which parts I built, admired, and quietly switched off.&lt;/p&gt;
&lt;h2 id=&quot;a-normal-morning&quot; tabindex=&quot;-1&quot;&gt;A normal morning&lt;/h2&gt;
&lt;p&gt;Here is what it actually looks like, because the abstraction is less convincing than the Tuesday.&lt;/p&gt;
&lt;p&gt;I wake up and there is a brief waiting. Not a wall of everything, a brief: what&#39;s on today, what came in overnight and where it went, who I owe a reply, the two things I flagged that are now due. The inbox has already sorted itself while I slept, most of it filed to the right place, a couple of things deliberately held back because they needed a decision only I could make. My tasks are on my phone, in sync, because something reconciled them at 6am. A draft or two sits ready, written with the context already pulled in, waiting for me to read, fix a line, and send.&lt;/p&gt;
&lt;p&gt;The difference from before is not that I do less work. It&#39;s that I start the day oriented instead of excavating. I used to open a full inbox and spend the first hour reconstructing where I was. Now the reconstruction happened without me, and I get to start at the part that needs a human.&lt;/p&gt;
&lt;h2 id=&quot;read-everything-send-nothing&quot; tabindex=&quot;-1&quot;&gt;Read everything, send nothing&lt;/h2&gt;
&lt;p&gt;That morning only works because the agents can see the whole picture: the mail, the calendar, the notes, the running record of what&#39;s owed. Broad read access is the entire reason the thing is useful. It is also, obviously, the scary part, so here is the rule that makes the scary part safe.&lt;/p&gt;
&lt;p&gt;Reading is reversible. If an agent mis-files a note or misreads a thread, the worst case is I move something back, and I&#39;ve lost thirty seconds. Sending is not reversible. An AI that emails the wrong person the wrong thing at 3am cannot be un-emailed, and no amount of &amp;quot;it&#39;s usually right&amp;quot; makes that risk acceptable when the downside is a relationship or a deal. So I made the one irreversible action the one thing the system cannot do on its own.&lt;/p&gt;
&lt;p&gt;Every outward action is drafted and queued. The email is written and sits until I press send. The follow-up is composed, with the context, and waits for my yes. The human is the only path from the system to another person, and as a second layer, a draft can only even be addressed to someone already in my world, never a new recipient the system invented. This is exactly the shape of how I let agents work on my code: they can commit all day, but they cannot deploy. Automate the reversible, put a human on the irreversible, and you can hand over far more than feels comfortable without handing over the thing that can actually hurt you.&lt;/p&gt;
&lt;h2 id=&quot;private-by-construction-not-by-promise&quot; tabindex=&quot;-1&quot;&gt;Private by construction, not by promise&lt;/h2&gt;
&lt;p&gt;Here is the part I care about most, and the part almost every other version of this skips.&lt;/p&gt;
&lt;p&gt;Your email, your calendar, your contacts, the running record of who you owe what: that is the single most sensitive corpus you own. It is a map of your relationships, your money, your health, your leverage. The default move in 2026 is to pipe all of it into someone else&#39;s model, because that is where the convenient assistant lives, and to accept a sentence in a terms-of-service page as the entire privacy architecture. Most people make that data-flow decision without ever noticing they made it.&lt;/p&gt;
&lt;p&gt;I build a company whose whole premise is that a claim is only as good as the evidence you can trace it to. I was not going to run my own life on a promise I can&#39;t verify.&lt;/p&gt;
&lt;p&gt;So Brain is local. The agents run on my laptop, against files on my disk, and the assistant they talk to is reached in a way that keeps the corpus on the machine. That is a stance, not a feature, and stances are easy to say and easy to quietly break the first time cloud sync would be convenient. So I made it structural. The safety net that versions and backs up the whole vault every hour has one hard rule wired into it: if it ever finds that the vault has been pointed at a remote, any remote, it refuses to run and says so. The system that protects my data is built to stop working the instant that data could leave. Privacy here is not something I intend. It is something the code enforces against my own future laziness.&lt;/p&gt;
&lt;p&gt;That is the difference I want you to take from this. &amp;quot;We don&#39;t train on your data&amp;quot; is a promise, and a promise is a thing you audit and hope about. &amp;quot;The data physically cannot leave this machine&amp;quot; is a property, and a property is a thing you can check. When the corpus is your entire personal life, I think that difference is the only one that matters, and I think most people have it exactly backwards: they will agonize over a password and then hand a decade of their correspondence to whichever assistant had the nicest onboarding.&lt;/p&gt;
&lt;p&gt;It costs me things to run it this way, and I owe you honesty about those.&lt;/p&gt;
&lt;h2 id=&quot;what-running-it-locally-actually-costs&quot; tabindex=&quot;-1&quot;&gt;What running it locally actually costs&lt;/h2&gt;
&lt;p&gt;Local is not free and it is not automatically virtuous, and pretending otherwise is how you lose the reader who has actually tried it.&lt;/p&gt;
&lt;p&gt;It lives on one machine. When I&#39;m out with just my phone, the agents are not running for me in the moment; they run on a schedule, where the data is. I gave up the seamless assistant-on-every-device experience that a hosted service hands you for nothing. I gave up someone else keeping it running, which means when an integration breaks there is no support desk, there is me on a Sunday. And I pay for it in exactly the friction you&#39;d expect: the edges that a polished cloud product would have smoothed over, I smooth over myself or route around.&lt;/p&gt;
&lt;p&gt;I took that trade on purpose, and only because of what the corpus is. For my entire personal and professional correspondence, I will pay real friction for a privacy property I can verify. For a shopping list I would not bother, and I&#39;d use whatever was easiest. That&#39;s the honest calculus: local is right when the data is sensitive enough to earn the friction, and the mistake is treating it as a religion instead of a trade.&lt;/p&gt;
&lt;h2 id=&quot;what-i-built-and-dont-use&quot; tabindex=&quot;-1&quot;&gt;What I built and don&#39;t use&lt;/h2&gt;
&lt;p&gt;Every post like this is a victory lap, and every system like this is half-abandoned. So let me tell you which parts of mine actually survive contact with a normal week, because the honest answer is not the flattering one.&lt;/p&gt;
&lt;p&gt;The parts I use every day are embarrassingly boring. The morning brief. The inbox getting triaged and filed while I sleep. My tasks staying in sync so I stop dropping things. That&#39;s it. That&#39;s the core, and if I&#39;d stopped there I&#39;d have ninety percent of the value for a fraction of the code.&lt;/p&gt;
&lt;p&gt;The parts I&#39;m proudest of are the parts I barely touch. I built a &amp;quot;thinking&amp;quot; layer that runs structured passes over my own notes: challenge an assumption, connect two ideas, surface the patterns I&#39;m circling. It is the most intellectually satisfying thing in the whole system and it is the thing I open least, because it turns out the bottleneck in my thinking was never a lack of prompts to think harder. I built a reconciliation engine that detects duplicate and drifting notes and proposes merges, production-grade, the kind of thing you&#39;d want across a million records. My vault has a few hundred notes. I built it because I &lt;em&gt;could&lt;/em&gt;, and because it mirrored a real problem in the product I sell, and it solves a problem I do not have. And I built a whole graphical cockpit to see all of this at a glance, and then I kept living in the terminal, because the terminal was already where I was.&lt;/p&gt;
&lt;p&gt;Then there are the edges that just quietly don&#39;t work and that I route around instead of fixing. A calendar integration that needs re-consenting more often than it should. A capture path I set up, tested once, and never wired all the way through. None of it is broken enough to force a fix, which is exactly why it never gets one. The gap between &amp;quot;works in a demo&amp;quot; and &amp;quot;works on a Tuesday when I&#39;m busy&amp;quot; is where personal software goes to die, and mine has its share of graves.&lt;/p&gt;
&lt;p&gt;If there&#39;s a lesson in that, and I think there is, it&#39;s this: the usefulness of a feature turned out to be almost inversely proportional to how clever it felt to build. The dull scheduled job that files an email is load-bearing. The elegant engine that reasons about my notes is a trophy. I keep both, but I&#39;ve stopped confusing which is which, and I no longer let the fun of building a thing stand in for evidence that I&#39;ll use it.&lt;/p&gt;
&lt;h2 id=&quot;what-to-take-from-this&quot; tabindex=&quot;-1&quot;&gt;What to take from this&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Build systems that act, not archive. The value is in the doing, not the storing.&lt;/li&gt;
&lt;li&gt;Put the human on the only outbound path. Automate the reversible, gate the irreversible.&lt;/li&gt;
&lt;li&gt;Enforce privacy as a property, not a promise. If the data can leave, eventually it will.&lt;/li&gt;
&lt;li&gt;The boring scheduled job is load-bearing; the clever engine is a trophy. Do not confuse them.&lt;/li&gt;
&lt;li&gt;Local is a deliberate trade, not a default virtue. Make it where the data earns the friction.&lt;/li&gt;
&lt;li&gt;Most of the value is in the least impressive tenth. Build that first, and maybe only that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;ve been running this for a while now, and the honest state of it is: the boring core is indispensable and the clever parts are a museum. If you&#39;ve built something like this and made the clever parts &lt;em&gt;stick&lt;/em&gt; past the honeymoon, I would genuinely like to know what survived for you, because mostly they haven&#39;t for me.&lt;/p&gt;
&lt;p&gt;And if the instinct underneath all of this is familiar to you - keep the sensitive corpus where you can verify what happens to it, make every claim traceable back to its source, never let the system take the one action you can&#39;t undo - that is not just how I run my own life. It is the whole idea behind what I build during the day. &lt;a href=&quot;https://www.tidebreak.ai&quot;&gt;That&#39;s here.&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;on-whose-shoulders-this-stands&quot; tabindex=&quot;-1&quot;&gt;On whose shoulders this stands&lt;/h2&gt;
&lt;p&gt;The second-brain idea has a fifty-year lineage, and this post is a departure from it, not a discovery. The sources, if you want them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vannevar Bush, &amp;quot;As We May Think,&amp;quot; &lt;em&gt;The Atlantic&lt;/em&gt;, 1945 - the Memex. https://www.w3.org/History/1945/vbush&lt;/li&gt;
&lt;li&gt;Douglas Engelbart, &amp;quot;Augmenting Human Intellect: A Conceptual Framework,&amp;quot; SRI, 1962. https://www.dougengelbart.org/pubs/augment-3906.html&lt;/li&gt;
&lt;li&gt;Sönke Ahrens, &lt;em&gt;How to Take Smart Notes&lt;/em&gt;, 2017 - the modern systematization of Niklas Luhmann&#39;s Zettelkasten.&lt;/li&gt;
&lt;li&gt;Michael Nielsen, &amp;quot;Augmenting Long-term Memory,&amp;quot; 2018. https://augmentingcognition.com/ltm.html&lt;/li&gt;
&lt;li&gt;Andy Matuschak &amp;amp; Michael Nielsen, &amp;quot;How can we develop transformative tools for thought?&amp;quot;, 2019. https://numinous.productions/ttft/&lt;/li&gt;
&lt;li&gt;Tiago Forte, &amp;quot;Building a Second Brain: The Definitive Introductory Guide.&amp;quot; https://fortelabs.com/blog/basboverview/&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>AI writes code faster than you can review it. Here is what I did about it.</title>
    <link href="https://stillig.net/posts/ai-writes-faster-than-you-can-review/" />
    <updated>2026-07-28T09:00:00Z</updated>
    <id>https://stillig.net/posts/ai-writes-faster-than-you-can-review/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;I&#39;m Johannes. I&#39;m building Tidebreak, an investigations engine where every finding traces back to the source bytes it came from. That obsession with provenance is also why I can only write about my own engineering one way: with the parts I got wrong left in.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the last twelve weeks I made 4,111 commits to a codebase that is now about 455,000 lines: a real production system, 154 database tables, six hundred-odd endpoints, the works. I wrote almost none of it by hand.&lt;/p&gt;
&lt;p&gt;That sentence is either exciting or alarming to you, and the honest answer is that it should be both. Because the thing nobody tells you about coding with AI is that the writing was never the bottleneck, and it was never the danger either. Agents will produce correct-looking code faster than any human can read it. So the moment you let them run, both the ceiling on your speed and the thing most likely to hurt you move downstream, past the keystroke, into a place you can no longer watch by eye.&lt;/p&gt;
&lt;p&gt;I learned this the way you learn most things: by getting it wrong first. What kept the codebase from quietly rotting under that volume was not better prompts. It was a set of guardrails that make the rules the system depends on impossible to break silently, and an autonomous loop that is built so it &lt;em&gt;cannot&lt;/em&gt; do the unsafe thing rather than &lt;em&gt;trusted&lt;/em&gt; not to. This is a post about those, and about the parts that broke anyway, because a post that only shows the parts that worked is not worth your time.&lt;/p&gt;
&lt;h2 id=&quot;the-failure-that-scares-me-is-the-quiet-one&quot; tabindex=&quot;-1&quot;&gt;The failure that scares me is the quiet one&lt;/h2&gt;
&lt;p&gt;Everyone writes about AI hallucinating a bug. That is the boring failure, and your tests catch it. The failure that actually costs you is silent.&lt;/p&gt;
&lt;p&gt;An agent, asked to add a feature, notices there is already a scoring function two directories over and writes a second one that is subtly different. Or it returns a cleaner error message on a permission check and, in doing so, tells an attacker that a record exists. Or it reads a user id where the rule of the system is to read an organization id, and eleven months of careful isolation gets a hairline crack.&lt;/p&gt;
&lt;p&gt;None of these fail a test, because a test encodes what you thought of, not what you forgot. At forty-eight commits a day you will not catch them by reading diffs, because you will not read the diffs. The problem is not correctness in the small. It is review bandwidth and invariant drift, and neither of those is fixed by a smarter model.&lt;/p&gt;
&lt;h2 id=&quot;invariants-as-guardrails-not-comments&quot; tabindex=&quot;-1&quot;&gt;Invariants as guardrails, not comments&lt;/h2&gt;
&lt;p&gt;So the first real discipline is this: write down the rules the system cannot survive breaking, and then make each one break the build if it is violated, rather than living in a comment that hopes an agent reads it.&lt;/p&gt;
&lt;p&gt;A few of mine, kept general:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cross-tenant access returns &amp;quot;not found,&amp;quot; never &amp;quot;forbidden.&amp;quot; A forbidden response is more helpful to a user and it silently confirms the record exists. An agent optimizing for a clear error message will reach for forbidden every time. A guard rejects it, so the helpful-but-wrong version never lands.&lt;/li&gt;
&lt;li&gt;One capability has exactly one implementation. The second copy of a scoring or de-duplication function is a build failure, not a code-review note. This is the single most useful rule I have: it turns &amp;quot;please reuse the existing thing&amp;quot; from a hope into a constraint, and agents respond to constraints, not pleas.&lt;/li&gt;
&lt;li&gt;Some numbers may never be collapsed. A risk signal I track on two independent axes has to stay two numbers through every layer, because averaging them hides the case that matters. That is enforced in code, not prose, because prose is exactly what a fast agent skims.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The rule that makes the whole thing scale is the meta-rule: every non-negotiable ships with an automated backstop, or it does not ship. A rule an agent can violate without the build going red is not a rule. It is a wish, and wishes do not survive contact with an automated contributor that has read your style but not your scars.&lt;/p&gt;
&lt;p&gt;I will be honest about the cost. This is real work paid up front, and below a certain velocity it is not worth it, because you would just read the diffs. The trade flips the moment the machine is committing faster than you can review. Then the guardrails are not bureaucracy. They are the only reason you can let go of the wheel at all.&lt;/p&gt;
&lt;h2 id=&quot;an-autonomous-loop-that-cannot-hurt-you&quot; tabindex=&quot;-1&quot;&gt;An autonomous loop that cannot hurt you&lt;/h2&gt;
&lt;p&gt;The guardrails let agents commit safely. The next piece let them work while I sleep.&lt;/p&gt;
&lt;p&gt;Every night a loop runs over the backlog: it triages what is worth doing, de-risks each candidate, builds it, reviews the result, and integrates what passes. The interesting thing about it is not that it is careful. It is that it is built so carefulness is not required. It structurally cannot deploy. It cannot push a red main. It cannot open its own pull request or approve its own work. If the final gate comes back red, it hard-resets the entire batch rather than salvage a piece it should not trust.&lt;/p&gt;
&lt;p&gt;That is the whole philosophy in one line: I did not make the loop trustworthy, I made trust unnecessary. An autonomous process you have to trust is one you have to watch, and a process you have to watch is not autonomous. The only version that lets you actually walk away is the one where the worst thing it can do is waste a night&#39;s compute.&lt;/p&gt;
&lt;h2 id=&quot;isolation-and-specialists&quot; tabindex=&quot;-1&quot;&gt;Isolation and specialists&lt;/h2&gt;
&lt;p&gt;Two smaller pieces hold that up. Parallel agents each work in their own isolated copy of the repository, so two of them cannot clobber each other&#39;s changes, and one checkout is reserved for integration only, guarded so no feature work lands there by accident. And the agents are not interchangeable: there is one that builds, one that reviews, one that plans, and the reviewer is pinned to the strongest model available, unconditionally. You can cost-optimize a lot of things in a setup like this. The gate is not one of them.&lt;/p&gt;
&lt;h2 id=&quot;the-gate-is-one-local-script&quot; tabindex=&quot;-1&quot;&gt;The gate is one local script&lt;/h2&gt;
&lt;p&gt;My continuous integration is deliberately dull. One script is the entire gate, and it runs the same way on my machine as anywhere else; the hosted CI mostly just dispatches it. That sounds unambitious until you remember who the primary user of your gate now is. It is not you, running it a few times a day. It is a machine, iterating against it a hundred times a night. Determinism and a single source of truth stop being nice-to-haves and start being the thing that keeps the whole loop from chasing ghosts.&lt;/p&gt;
&lt;h2 id=&quot;what-broke&quot; tabindex=&quot;-1&quot;&gt;What broke&lt;/h2&gt;
&lt;p&gt;If the rest of this reads like it worked, let me correct the impression, because the interesting part is where it did not.&lt;/p&gt;
&lt;p&gt;I did not know what an overnight run cost. One of them burned 13.6 million tokens, and I learned that number by counting it by hand, afterward, when something felt off. There was no hard budget ceiling on unattended work at the time, nothing that would have stopped a loop that misjudged its own progress from running until morning. The industry has a small graveyard of these stories, some with four and five-figure price tags. I got a cheap lesson, and the real fix, a spend cap that does not depend on the agent&#39;s own judgment, came after the scare, not before it.&lt;/p&gt;
&lt;p&gt;A nightly run crashed once and left a handful of issues marked in-progress, half-built, in a state the next morning&#39;s triage could not see. So the loop kept skipping them, because as far as it could tell someone was already on them. Nobody was. I built the self-heal that resets orphaned work on an abnormal restart because of that morning, not in anticipation of it. Almost every guardrail in this system has that shape: it is a scar, not a prediction.&lt;/p&gt;
&lt;p&gt;Two agents sharing one working directory have tangled each other&#39;s changes more than once. Discipline, it turns out, is not structure. A rule that says each agent gets its own workspace is only as good as the enforcement behind it, and I have watched a second session quietly undo the first&#39;s work while both believed they were alone. That gap is not fully closed. I am telling you because a post that pretends otherwise is not worth reading.&lt;/p&gt;
&lt;p&gt;And when I finally audited my own setup against how the best teams do this, the verdict was uncomfortable and correct: my automation was ahead of almost anyone&#39;s, and my visibility was the weakest part of the whole thing. I had been flying a fast aircraft with half the instruments dark. The token-count-by-hand story is just the most quotable symptom of that.&lt;/p&gt;
&lt;p&gt;I lead with these because the numbers at the top of this post are only worth anything if you believe the person reporting them.&lt;/p&gt;
&lt;h2 id=&quot;where-the-bottleneck-actually-went&quot; tabindex=&quot;-1&quot;&gt;Where the bottleneck actually went&lt;/h2&gt;
&lt;p&gt;Here is the part I have not solved. The question stopped being &amp;quot;can I run more agents&amp;quot; a while ago. It is now &amp;quot;can I review and merge fast enough,&amp;quot; and past a handful of parallel agents, the answer is me. I am the ceiling. More parallelism just grows the queue of things waiting on my attention.&lt;/p&gt;
&lt;p&gt;The honest frontier from here is not a better prompt or a bigger fan-out. It is a real automated quality gate that earns enough trust to merge without me, and the tooling to review in batches instead of one change at a time. That is the actual hard problem in AI-assisted development at volume, and anyone selling you &amp;quot;just add more agents&amp;quot; has not hit the wall yet.&lt;/p&gt;
&lt;h2 id=&quot;what-to-take-from-this&quot; tabindex=&quot;-1&quot;&gt;What to take from this&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Machine-enforce your invariants, or they will drift. A rule the build does not check is a wish.&lt;/li&gt;
&lt;li&gt;Make the dangerous action structurally impossible, not policy-forbidden. Do not build a careful autonomous loop; build one that cannot do the unsafe thing.&lt;/li&gt;
&lt;li&gt;Never cost-optimize the reviewer.&lt;/li&gt;
&lt;li&gt;Put a hard ceiling on anything that runs unattended, independent of the agent&#39;s own judgment.&lt;/li&gt;
&lt;li&gt;Instrument before you scale. Automation without visibility is just a faster way to be surprised.&lt;/li&gt;
&lt;li&gt;Your bottleneck is review, not generation. Spend your best effort there.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is finished, and my real bottleneck now is the one I just described. If you have actually solved review-at-machine-speed, I would genuinely like to hear how, because I have not.&lt;/p&gt;
&lt;p&gt;And if you came for the engineering but your real world is investigations, breach data, financial crime, anything where a conclusion is only as good as the evidence beneath it: that is what I am building Tidebreak for, and the same discipline in this post is the reason every claim it makes traces back to a source. &lt;a href=&quot;https://www.tidebreak.ai&quot;&gt;See it here.&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Hello, world</title>
    <link href="https://stillig.net/posts/hello-world/" />
    <updated>2026-07-28T00:00:00Z</updated>
    <id>https://stillig.net/posts/hello-world/</id>
    <content type="html">&lt;p&gt;This is the first post. If you are reading it, the pipeline works.&lt;/p&gt;
&lt;h2 id=&quot;what-this-proves&quot; tabindex=&quot;-1&quot;&gt;What this proves&lt;/h2&gt;
&lt;p&gt;The build renders Markdown, highlights code at build time, generates a tag
archive, and puts the result behind a CDN with a content policy strict enough
to block its own stylesheet if anything is misconfigured.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://stillig.net/posts/hello-world/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;what-it-does-not-prove&quot; tabindex=&quot;-1&quot;&gt;What it does not prove&lt;/h2&gt;
&lt;p&gt;That anything worth reading is here yet.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;It has, once. See &lt;code&gt;scripts/deploy.sh&lt;/code&gt; for the full account. &lt;a href=&quot;https://stillig.net/posts/hello-world/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
</feed>