Category Archives: AI

Dev Stack, Part XIII: Local AI

This is the latest post documenting my tech stack built around using coding agents safely. The previous posts were:

Up until this past week, I had been using cloud based AI, but the endgame of this setup was to get onto local AI. In Part II, I mentioned that I moved to Linux on a Framework Desktop. That machine is using an AMD Ryzen AI Max+ with 64GB of unified memory. This is definitely good enough to run some interesting local models, but I do regret not getting a 128GB back in December when I bought it. When prices come down again, I will be upgrading.

For hosting models, I use Ollama. I tried a bunch of models. For each one, I gave it a simple command as a way to “eval” it: “Build and run nebulas tests”. I’ll explain more in the next post when I write about my coding agent (Pi.dev). Of the ones I tried, only qwen3-coder-next could do this without more help.

Here are some random technical notes about the setup:

  • For a Framework, everything you need to know is documented well this AMD Playbook about Ollama.
  • The main difference between an AMD setup and others is how you allocate memory to the GPU. A Ryzen uses Unified memory (shared between the CPU and CPU), which is similar to how the Apple M series chips work. In the playbook, it explains how to do that. I decided to set the minimum to 512k via the BIOS and then used amd-ttm to set the shared memory pool to 48GB for now.
  • With the above setup and the qwen-coder-next model, I get 93% of the model running on the GPU and 7% on the CPU. This is tolerable, but it would be better to be at 100%.
  • However, I am getting 36 tokens/sec which is more than fast enough.

In the next post, I’ll explain how I sandbox Pi in the Docker and connect it to Ollama.

Dev Stack, Part XII: Sandboxing Update (Docker)

Late last year, I completely changed my dev stack to Python on Linux with some other things. I wrote a series about it at the time:

In Part XI, I described using KVM (a full virtualized VM) and specifically not Docker. At the time, I wrote:

Docker: I am not that comfortable with it, and it seems like running GUIs (like VSCode) is not trivial. It would be more efficient with sharing installed software, but wasting disk space is just not an issue.

After using a VM for several months, I have decided to bite the bullet and learn Docker more and get comfortable with it. I’m not trying to run VSCode in it, just terminal based coding agents (which I will describe in a future post).

My setup is simple. I started with a Dockerfile based on node because I wanted to use Pi.dev and that is installed via npm. I gave it the minimal tools needed to build my project (a game built with C and Raylib), which is just gcc, X11, OpenGL, and a few other things.

To harden the security, I created a docker private network and put the Docker into that network. From there it has no internet or network access at all. I opened up one port so that the Docker could talk to Ollama on my host machine.

In the next post of this series, I’ll elaborate on the entire local AI and Pi.dev setup.

My 2021 AI Coding Thoughts Revisited

In February 2021, I wrote Robotic Pair Programmers, which is my description of what I would want out of an AI coding assistant. I am pretty sure I was unaware of any “good” AI coding assistants, but a month later I took a look at Kite, which was an early attempt. I think there must have been something in the air. Copilot was released that June. ChatGPT would be released the next year.

It is now more than five years later, and we’re Approaching Infinity, where the doublings of computing are on a much larger base. We are past the bend in the exponential curve. But, still, there might be things that are constant (or at least aren’t immediately obsolete) in the three doublings we had since I wrote it.

The main thing I got wrong is that I thought the main way to use AI would be as a pair programmer, which was true for the first iteration. Now, it’s more as an independent co-worker that you manage. I still do a lot of pair programming with AI, but this won’t be true in the industry five years from now.

Here are sentences that I think still apply after five years.

Let’s say I index every Xcode project in GitHub, every iOS tutorial, every iOS question in Stack Overflow. Could that be distilled somehow and then shown to me at the right time?

Yup. And distilling is exactly the right word for this. This is how it works now, but that wasn’t clear to me then.

Whatever we do, we need to make sure that nearly every suggestion is useful.

I still think this is important. The ever increasing percentage of times that the agent is exactly correct has been the main thing driving usefulness in the last six months. It used to be true that you had to weigh whether it was better to correct wrong code versus writing it yourself. That ratio has tipped in AI’s favor.

Conserving flow should be the driver for how this works.

This is definitely true when you are coding with suggestions or doing smaller changes. It feels less important when doing a long agent run (or dark factory/loop engineering style systems). But, you see memes about how programmers are watching reels while agents do work, and it makes me sad. Part of the reason I make programs is to make me into someone that is better at making programs. This is true for the way I use AI, but I am not under pressure to be faster. Productivity is a goal, but not at the expense of my enjoyment.

Siri AI is a Malware Vector

I hope you are able to use the latest Apple OSs with Siri AI completely turned off. I believe that, as described, it will be a fertile ground for malware reminiscent of Windows 20 years ago.

I would love if anyone has information about the details of Siri AI that refute this.

1. Stopping prompt injections is impossible right now.

To back this up, read Anthropic’s system card for Opus 4.8. Page 77 shows the various top model’s probability of stopping prompt injections. Opus 4.8 is just under 10% with 100 attempts. Gemini (which Siri is based on) is 45% with 100 attempts.

This may be an inevitable and unsolvable problem. So …

2. We must assume that any Agent that has been exposed to text that we don’t trust is under the control of an adversary.

This is a design constraint right now. The rest of the system must be architected around this assumption.

I would never run an agent on my personal machine, because …

3. There is a lot of untrusted text on my personal devices.

Here is a partial list: All incoming emails and texts, all documents I didn’t write, all e-books, sites I browse. Siri AI can “look” at apps I am running. If you code on your machine, then all dependencies (every README, skill, etc).

This means that any file type with text is potential malware, not just executables or scripts.

But that’s not the only thing on your machine …

4. There are also a lot of “secrets” on your machine

The partial list above also includes your trusted text with your secrets. Things like: your passwords (if you let Siri AI reset them as shown in the keynote), your emails and texts, photos, financial information, personal documents, and bitcoin wallets.

So, you have a high potential to let an AI Agent that is under an adversary’s control see a secret. This is not ok, because …

5. The Agent is able to “do things”

For example: form a URL and make a network request with it, control applications, show an image from the internet (which is a special case of requesting a URL).

Siri AI will likely ask for approval, but …

6. Approval-based permission models don’t work

There is no way to make an informed decision about what is safe for an AI Agent to do. Even so, you won’t likely be asked to approve URL requests. Also, approval fatigue is real.

Apple didn’t show any permission prompts, but I assume that there will be some because they are not using …

7. The “better” (not perfect) solution is sandboxes, firewalls, OS-level auth

My opinion is the best way to run agents is in sandboxes with their own accounts (not as you or super-user) with OS-level authorization and firewalls in place. And then … just let the agent go.

The agent will be exposed to prompt injections, but there are no secrets in the VM and I limit its actions the same way I would limit a logged in user on a shared machine. This is just normal system level user access control.

I wrote about this more in Escaping the Lethal Trifecta of AI Agents and Limiting the Chance of Code Agent Prompt Injections

Limiting the Chance of Code Agent Prompt Injections

Yesterday, I wrote about the Lethal Trifecta when using coding agents and how I am escaping it via sandboxing. I built a place to code where there is nothing valuable to lose. The agents might be poisoned by prompt injection and able to phone home, but there’s nothing to send. I can wipe the entire VM at any time and rebuild it from a snapshot or from scratch easily.

This deals with one leg of the trifecta, which is sufficient, but I don’t ignore the other two.

To limit the chance of an agent being exposed to a prompt injections, I build on an architecture of very limited dependencies. My current project is to build visualizations in JS on D3. I only include D3 on pages in the browser (it’s not on my machine). I don’t use npm, and I have no other dependencies.

The thing I miss most is jest, but I decided to build a minimal testing framework (just need to run functions and make assertions). I run the tests in a browser, so I get access to a DOM too, which I could test against. All of the code for this project only makes sense inside of a web page in the browser, which is another sandbox. It’s like Inception up in here.

My other projects are python based and live in their own VM. I need some dependencies there (pandas, numpy, matplotlib and more). The main thing I am doing is keeping that separate from the visualization project so that any issue in one doesn’t affect the other.

Nothing else that I need for the project (that I didn’t create) lives in that VM.

My main exposure to untrusted text is that I let the agent browse the web. I don’t see how I could avoid this, which is why this leg of the trifecta could never be the one I eliminate.

Escaping the Lethal Trifecta of AI Agents

The “Lethal Trifecta” is a term coined by Simon Willison that posits that you are open to an attacker stealing your data using your own AI agent if that agent has:

You need all three to be vulnerable, but usage of Claw or Coding agents will have them by default. I would say that the second two are almost impossible to stop.

#2 Untrusted content includes all of your incoming email and messages, all documents you didn’t write, all packages you have downloaded (via pip, npm, or whatever) and every web page you let the agent read. I have no idea how to make an agent useful without some of these (especially web searching).

#3 External communication includes any API call you let it make, embedded images in responses, or just letting it read the web. Even if you whitelist domains, agents have found ways to piggyback communication because many URLs/APIs have a way of embedding a follow-up URL inside of them.

For my uses, I find it impossible to avoid these two. Reduce? Yes, but not eliminate.

So, my only chance to escape the trifecta is to not give agents access to my private data. This means that I would never let an agent process my email or messages. I also would never run them on my personal laptop. I would never let them login as me to a service.

This is why I built hardware and software sandboxes to code in. Inside a VM on a dedicated machine, there is no private data at all. I use it while assuming that all code inside that VM is untrusted and that my agent is compromised. I do my best to try to make sure that won’t happen, but my main concern is that there is no harm if it does happen.

Incidentally, this same lethal trifecta also applies to every package you install into your coding projects. If an NPM package can (1) read your secrets (2) is untrusted and (3) can communicate, then you may suffer from a supply chain attack. It’s obvious that code you install and run makes #2 and #3 impossible to safeguard against. Not having secrets in the VM is the best solution for supply chain attacks too.

Tomorrow, I’ll follow up with how I reduce the other two legs of the lethal trifecta.

What Makes a Good First Vibe Coding Project

Code can be dangerous to run. It could have security issues. It could leak secrets. If you don’t know what you are doing yet, vibe coding is a good way encounter those problems fast.

Here are some aspects of a project that make it a good one to start learning how to vibe code. This won’t make them perfectly safe (no code that you don’t read could be). But, here’s where to start.

  1. It is a tool that only you will use
  2. It doesn’t need to deploy code to a server that is exposed to the public Internet
  3. It doesn’t need access to any services that require authentication
  4. It is meant to be a prototype
  5. It is run client-side only in a sandbox. For example: a 100% in-browser JavaScript or mobile app.

Games fit most of these.

I’ve been having fun with my nephew writing JavaScript games using PhaserJS. Agents seem to know this library well and we almost never need to look at the code. The games run in a sandbox (the browser) and don’t require any server-side code (that could be hacked).

Joke Templates

Back in the nineties, I was interviewing someone and he mentioned the idea of joke templates. I can’t remember his example, but when I told my boss, he said, “Oh yeah, I love the one where someone says a number and then you multiply it by seven and say it’s that many in dog years.”

My favorite joke template is the two problems one. I think it was originally: “You have a problem and you think, ‘I know: I’ll use a regular expression’ — now you have two problems.”

I’m a sucker for any variant on this. I just posted this to LinkedIn:

You have a problem with your AI code generator undoing its own work when you add something new, so … “I know,“ you think, “I’ll add another LLM to check the code of the first one.“ Now you have two problems.

RegExes aren’t AI, but it felt that way sometimes since they are so good at what they are good at. But, just like LLMs, they suck at what they suck at. Generally, they are great at finding answers that have objective, verifiable truth. But, they are not good at knowing a secret fact. The key is to provide the facts and ability to verify, and then let the LLM iterate to the solution.

Vibe Coding vs. Vibe Engineering

I try to use Vibe Coding in Andrej Karpathy’s original sense:

There’s a new kind of coding I call “vibe coding”, where you fully give in to the vibes, embrace exponentials, and forget that the code even exists.

Which makes it hard to describe what I do, which is not that. I have been calling it AI-Assisted Programming, but that’s too long. Simon Willison proposed Vibe Engineering:

I feel like vibe coding is pretty well established now as covering the fast, loose and irresponsible way of building software with AI—entirely prompt-driven, and with no attention paid to how the code actually works. This leaves us with a terminology gap: what should we call the other end of the spectrum, where seasoned professionals accelerate their work with LLMs while staying proudly and confidently accountable for the software they produce?

I propose we call this vibe engineering, with my tongue only partially in my cheek.

He wrote this in October, but it only started to sink in with me recently when he wrote about JustHTML and how it was created. Read the author, Emil Stenström’s, account of how he wrote it with coding agents. This is not vibe coding. He is very much in the loop. I think his method will produce well-architected code with minimal tech debt. Like I said in my book: “The amount of tech debt the AI introduces into my project is up to me.” I think this is true for Emil too.

My personal workflow is to go commit by commit, because it’s the amount of code I can review. But, I see the benefit of Emil’s approach and will try it soon.

Protecting Myself

I was recently on the Scrum Master Toolbox podcast with Vasco Duarte in a series about AI assisted coding. In it, I said that I read every line of AI generated code (and fix it, if necessary) before I commit it.

This isn’t exactly right.

I read all code, even my own, and fix it before I commit. Doing it for AI is just an extension of how I normally work. I do this for my code because I often find problems. This is even more true for AI generated code.

Another reason to do this is because it makes code go through code review and testing faster. I have written about that previously:

Now that I am the only programmer on my project, I don’t need to worry about code review, but I do have to worry about DevOps, and frankly, I am not willing to trust AI to write code that I have to run. I have already fixed code that introduced beginner level security problems, so pure Vibe Coding on a project meant to be used by others on the web is not an option for me.