Make VMs Great Again

I have trust issues.

When Claude Code was released last year I was interested in playing with it, but struggled to find a way to run it in a secure, isolated manner. Trusting its own sandbox was obviously not in the cards. I explored other people’s solutions – mostly using things like Bubblewrap and Docker – but none of them fully satisfied me. It also quickly became evident that most of the value from using Claude Code comes from the --dangerously-skip-permissions argument, which gives it the ability to pursue a targeted task without constant permission requests. This requires even stricter isolation.

Eventually I landed on a full virtual machine being the only viable option. I reinstalled Vagrant for the first time in almost a decade and was off to the races.

I was a heavy user of Vagrant in the twenty-teens. I still really like the interface. But when revisiting it today the software felt rather heavy and clunky. It defaults to using VirtualBox, which has its own set of issues. There is a community provided libvirt plugin, but that seems largely abandoned. “Abandoned” seems to be the word for most of the Vagrant community – probably caused in part by the license change.

So for the past month I’ve been building Migrant, a lightweight VM management tool for running assumed-malicious AI agents in ephemeral environments. The heavy lifting is done by libvirt and QEMU. Migrant started out as just a way to get a more Vagrant-like interface with modern tooling. I use cloud-init to initialize the image, Ansible to configure it, and libvirt for the VM management. But because the whole raison d’ĂȘtre of the project is the fact that non-deterministic systems are inherently untrustworthy, Migrant expanded to have a suite of security features. It has network isolation, so the agent can’t compromise the rest of your LAN. It has shared folder isolation, so that the agent can’t exhaust the host disk or engage in any symlink traversal shenanigans. It has WireGuard tunnel support, implemented host-side such that the VM cannot bypass it (because why wouldn’t you want to run all your agents through Mullvad).

I think it’s pretty great. I use it regularly.

Migrant also serves as my testament as to how agentic coding should work. I’ve written it using Claude Code (initially running in a Vagrant-managed VM, but since the first public commit I’ve been building Migrant-in-Migrant), but it is the antithesis of “vibe coding”. I design the systems. I tell the agent how things should work. I review every line of code it produces. Most of the time I reject its first attempts. I take ownership of and responsibility for commits. The result, I think, is a pretty reasonable looking codebase.

My conclusion thus far is that coding agents are useful tools. They’re an accelerant. They’re great for exploring a problem space. There’s no going back to software development without them, but if they’re not being actively driven by an opinionated human with domain-knowledge and expertise, what they produce is mostly crap. Maybe that will change the future. For now, if you’re not challenging every line of output from the clankers, you’re doing it wrong. I suspect this applies equally to the application of LLMs in other areas, but personally I haven’t found LLMs to be useful for anything other than writing code.

Link Log 2026-03-05

Death to Trust

Our Algorithmic Grey-Beige World

The Slow Death of the Power User

Plasma 6.4 Wayland vs X11, processor and power benchmarks

Improving Visual Processing During Deadly Force Encounters and Recommendations for Office

The effect of rear bicycle light configurations on drivers’ perception of cyclists’ presence and proximity

LoFi Festival x Echo World (Elijah Nang, 2023)

Point Bonita

Tracking Investments with Ledger

I have a few investment accounts I track with Ledger. Ledger has ways of tracking commodities that are too complex for me. My investments are index funds of different flavors. I neither know nor care to know what individual components they consist of. They mostly manage themselves. I do want to know their balance, but I do not care to think about them more than a few times per year, so the up-to-date-ness factor can be pretty low (which is the complete opposite of everything else that I track in Ledger).

I represent these things in Ledger using an asset account for the fund, with subaccounts for contributions, earnings, and fees.

Contributions into the accounts are entered as soon as they happen, since those contributions are being debited out of an account where the up-to-date-ness factor is critical. So a transfer from my bank account to my Roth IRA may look like this:

2025-02-01 Investment Company
    Assets:Invest:RothIRA:Contributions     $1000.00
    Assets:Bank:Checking

Earnings and fees are tracked, but I only make those entries about once per quarter. However, the investment companies provide monthly statements. That level of granularity can be useful to have stored. So when I perform my quarterly updates, I’ll download each of the monthly statements from the past quarter and use them to make three sets of entries – two per month for the three months in the quarter. For example, in January 2026 I might make entries for the 2025 Q4 activity such as:

2025-10-31 * Investment Company
    Assets:Invest:RothIRA:Earnings            $79.05
    Income:Invest:Earnings

2025-10-31 * Investment Company
    Expenses:Fees:Invest                       $1.05
    Assets:Invest:RothIRA:Fees

2025-11-30 * Investment Company
    Assets:Invest:RothIRA:Earnings           $103.25
    Income:Invest:Earnings

2025-11-30 * Investment Company
    Expenses:Fees:Invest                       $2.20
    Assets:Invest:RothIRA:Fees

2025-12-31 * Investment Company
    Assets:Invest:RothIRA:Earnings           $-13.82
    Income:Invest:Earnings

2025-12-31 * Investment Company
    Expenses:Fees:Invest                       $1.31
    Assets:Invest:RothIRA:Fees

Before making the first entry, I will ask Ledger for the balance of Assets:Invest:RothIRA as of 2025-10-01 and confirm that the value matches the October statement’s opening balance. I will then read the total earnings and fees from the October statement and make the two October entries in my journal. Then I ask Ledger for the balance of the account as of 2025-10-31 and make sure the reported value matches the statement’s closing balance. Rinse and repeat for the next two months. I can ignore any contributions mentioned on the statement because those entries were already entered in my journal whenever I made the contribution.

These transactions are marked as cleared with the * symbol as I write the entry because the data comes direct from the statement of past activity. There is no separate reconciliation process.

The PDF statements themselves then get stored, but (unlike receipts, invoices, and checks) that happens in a different repository.

Doing things this way allows me to query for the value of the accounts (as of the last quarter or so), have some idea of how they are performing, and keep track of fees. I don’t really care to know any more details than this, nor do I care to think about these accounts with any more frequency than this.

Tax Payments with Ledger

How I record taxes in Ledger is pretty simple. In my demonstration of how I record salary payments the example entry included tax deductions that show the basic account structure.

2025-12-26 Acme Inc
    Assets:Bank:Checking                    $1000.00
    Expenses:Tax:Federal:FY2025              $200.00
    Expenses:Tax:State:FY2025                 $50.00
    Expenses:Insurance:Medical                $20.00
    Income:Salary

I pay state and federal taxes, so I have two main accounts: Expenses:Tax:Federal and Expenses:Tax:State.

Tax payments (and refunds for overpayments) can occur in years other than the tax years that the payments are related to. This requires some way to query transactions by tax years rather than just the posting date. You could do this by tagging the transactions, but I prefer to use a fourth account level to indicate the fiscal year. Thus my 2025 taxes live in Expenses:Tax:Federal:FY2025 and Expenses:Tax:State:FY2025.

If I pay an accountant to help me file taxes, I will log the payment against the Expenses:Tax:Preparation account, which uses the same fiscal year subaccount.

2026-02-01 The Accountant
    Expenses:Tax:Preparation:FY2025          $100.00
    Assets:Bank:Checking

If I receive a federal refund in March, I will debit it out of the appropriate tax account. I also tag these transactions with refund-tax so I can easily query them later.

2026-03-15 IRS
    ; 2025 Tax Refund
    ; :refund-tax:
    Assets:Bank:Checking                    $1000.00
    Expenses:Tax:Federal:FY2025

If instead I discover that I still owe federal taxes in March, I will credit the payment into the appropriate tax account. I tag these transactions with final-tax so I can easily query them later.

2026-03-15 IRS
    ; 2025 Federal Taxes
    ; :final-tax:
    Expenses:Tax:Federal:FY2025             $1000.00
    Assets:Bank:Checking

Some years I have made quarterly estimated tax payments instead of having taxes withheld from my paycheck. These entries look as you would expect, but I tag them with estimated-tax as well as the quarter number for future queries.

2025-09-02 IRS
    ; 2025 Q3 Estimated Tax
    ; :estimated-tax:q3:
    Expenses:Tax:Federal:FY2025             $1000.00
    Assets:Bank:Checking

The bottom line with all of this is that I can do a simple ledger balance fy2025 and see a complete and easy to understand picture of my 2025 taxes. Or I can run ledger balance expenses:tax:federal to see what I’ve paid in federal taxes over the past 14 years, broken out by year.

As with most things related to Ledger, this seems like pretty basic stuff when you’re doing it but becomes a superpower when you realize how most of the rest of the world lives.

Salary Tracking with Ledger

In my previous descriptions of how I perform plain text accounting I did not discuss logging salary income with Ledger. My basic strategy is to create an entry with all the same data that appears on the paystub. The only number that actually matters to me is whatever amount ends up in my bank account, but it is still important to have withholdings documented for later querying.

A simplified entry would look like this:

2025-12-26 Acme Inc
    Assets:Bank:Checking                    $1000.00
    Expenses:Tax:Federal:FY2025              $200.00
    Expenses:Tax:State:FY2025                 $50.00
    Expenses:Insurance:Medical                $20.00
    Income:Salary

At the time of entry, I store a PDF of the paystub just like a receipt. (In Ye Olden Days this came from scanning a piece of paper, but now I just download the PDF from a web site.) This entry will be reconciled and get cleared with the * mark the next time I login to the bank account and verify the amount I received.

If I want to know how much money the employer or a government thinks I made last year, I can just ask Ledger for the balance of the Income:Salary account.

$ ledger balance income:salary --period 2025
        $-1270.00   Income:Salary

However, this number has no real bearing on my reality. What is much more useful to me is the ability to query how much money I actually received last year, e.g. my take-home pay. I can do this by asking Ledger to show the balance of the bank account, limiting it to postings that involved the Income:Salary account.

$ ledger balance assets:bank:checking -l "any(account =~ /Income:Salary/)" --period 2025
        $1000.00    Assets:Bank:Checking

If the only financial relationship you have with your employer is them giving you money in the form of a salary, then you could simplify this by just asking Ledger for the balance of transactions in the bank account from that payee.

$ ledger balance assets:bank:checking and "@Acme Inc" --period 2025

However, my employer sells stuff, and sometimes I buy that stuff, so for me the above query would show me my salary less my employee purchases. Which is mostly worthless.

When I receive a W-2 after the end of the year, I check all of its entries – gross pay, pre- and post-tax deductions, etc – against Ledger with a simple ledger balance "@Acme Inc" --period 2025. Having all this data stored locally in queryable plain text, rather than needing to access some web portal or read through various PDFs like a prehistoric savage, is a key life strategy for me.

Night Moves

After falling out of the habit for a few months, I resumed my night runs at the end of December. I’ve been back into it for about a month now. It’s great, speed-running night city, just me and the coyotes.

After Dark

New to this cycle, I now stop at one of those outdoor fitness areas near the end of my run and knock out some pull-ups. (It turns out using a bar is in fact easier than my normal finger pull-ups.) There’s usually a couple other weirdos there.

Link Log 2026-01-21

Chimera

VBL 2023

White Rabbit

Designing a Life

Personal computing

When Sega Built the Most Extreme Arcade Cabinet

Beat Godfather Meets Glitter Mainman: William Burroughs Interviews David Bowie

Tam Cruising

I brush my teeth while standing on one foot.

It takes about two minute to brush your teeth. These two minutes are an opportunity to improve balance and ankle strength. Sometimes I switch feet halfway through, other times I’m feeling more ambitious and will balance on one foot in the morning and the other that night.