← Projects
carlink icon

carlink

Active

A Rust CLI that lets an AI agent diagnose a car through an OBDLink EX — trouble codes, self-tests, live data and history as JSON, with code clearing kept behind deliberate safety checks.

rustcliobd2automotiveaiagentslocal-first

I bought an OBDLink EX for my three cars, which are a 2004 Honda CR-V, a 2016 Honda Odyssey and a 2020 Hyundai Kona Electric, and I realized fairly quickly that I didn't want to be the one driving the scan tool. What I wanted was to be able to ask an agent what was wrong with the van, and to have it actually go and find out. carlink is the piece in the middle of that, which is a command-line program that talks to the adapter, and an agent skill that tells the agent how to use it.

There is no UI at all, and that was the first decision rather than the last one. Every command prints exactly one JSON object, and every failure comes back as JSON as well, with an error, and an error_kind, and a what_to_do, so that an agent never has to guess at what a stack trace meant. Anything that I want to see for myself, like a history of the battery or a trend across visits, gets built on top of those same commands.

Written for the weakest agent that might run it

The skill is the part that I spent the most time on, because the agent reading it might not be a strong one, and it might be sitting in a shop with a customer standing next to the car. So it reads like a runbook rather than a reference, with numbered steps, and a table for every field it will see, and the exact sentence to say before anything is cleared. To test it I gave a smaller model realistic jobs, like a Kona with its airbag light on, or a CR-V owner who wanted the light cleared before an inspection on Friday, with nothing but the skill to go on, and then I fixed whatever it got confused about. That took four rounds before the confusions were down to wording.

The one thing it writes

Reading a car is safe, and clearing it is not, so carlink sends one kind of write, which is clearing codes, and it keeps it behind a series of checks that each have a reason:

  • It needs a yes, and a VIN. The agent has to explain what clearing erases and get an explicit yes, and the VIN has to match the car that is connected
  • It refuses when it can't be sure, which means a running engine, a car that contradicts what the agent claimed, or a code read that came back incomplete
  • It never clears the Kona's battery, airbag or smart-key modules, because a cleared code in one of them can hide a battery recall or an airbag fault from the people who need to see it, and on that car it never sends the generic clear at all
  • It never retries a clear, and it judges the outcome only from a fresh scan rather than from the adapter saying OK

None of the adapter's configuration commands are exposed either, because a few of them permanently change the adapter, and a passthrough is the kind of thing that an agent would eventually find a reason to use.

Built without the car

The whole thing was built against a simulator that ships inside the program, so --port sim:odyssey-2016 behaves like the van, and faults like a running engine or a 29-bit CAN bus can be combined to rehearse the awkward cases. The research lives alongside the code as a wiki in the repo, so that every byte offset and every threshold cites the page that it came from. After the first version I gave an agent eight hours to review it adversarially and fix what it found, test first, and it logged 175 findings and every decision it made along the way.

What it hasn't done yet is talk to a real car. That is the next step, and there is a --trace flag and a checklist for each car so that the first surprise becomes data rather than a mystery.