AoAH Day 14: Debugging a Karakeep CLI against the live service / Dec 2025

With the Requests library under my belt, I finally got to what I actually need for myself: vibe coding OCaml library interfaces to my #selfhosted services that contain most of my data.

To start with, I use Karakeep across all my devices to bookmark things, and I'd like to be able to programmatically search through tags, for example by taking all outbound links from the blogs that I read and autosynching them with my remote service. Karakeep on the server side does some cool things like screenshot links and create local webarchives.

Unfortunately, Karakeep doesn't publish an OCaml interface. Fortunately, my new bestie Claude helped me build ocaml-karakeep without much input from me!

Approach

I keep a ton of bookmarks in Karakeep, and its Reader mode is convenient to quickly scan papers and such.
I keep a ton of bookmarks in Karakeep, and its Reader mode is convenient to quickly scan papers and such.

With a ton of pre-Christmas organising to do this weekend, I didn't have a lot of spare time. So I thought I'd use everything I've learnt so far and try to one-shot this as bravely/foolishly as possible. I setup a repo with:

  • Access to all the previous libraries, including the jsonfeed to act as an 'exemplar' for using jsont, as well as requests.
  • Cloned the karakeep source code which has the details of the API somewhere in the Node source. It uses something called tRPC that I'm not familiar with.
  • I took a ZFS snapshot of my karakeep deployment, and gave my agent a live API key to access my node. Yes, this is the 'take the seatbelt off' moment.

And then I prompted the agent to build jsont specifications derived from the Karakeep source, and then hook this up to Requests, and then iterate using my live key to debug failures until the API spec was right.

Results

This almost worked first time! While the testcases generated worked, I also got the agent to generate a karakeep CLI command. I'd made sure that, as with xdge, the Requests library also exposes Cmdliner terms that permit easy integration of HTTP requests into any CLI (to configure logging, proxies, that sort of thing).

I could just invoke the new karakeep CLI and try out different API functionality directly.
I could just invoke the new karakeep CLI and try out different API functionality directly.

Since I didn't have time to do a full coverage test of the API, I asked the agent to exercise the functionality and do live debugging. This is where the jsont codec was incredibly useful, since it provided all the right hints to the agent to take action. Good error messages aren't just for humans any more!

A typical error, for example was:

> main.exe bookmarks summarize wqa1sc1z42xcxfwwrsxeggbl
main.exe: [ERROR] Karakeep error: JSON error: Missing members in bookmark object:
 content
 createdAt
 id
File "-", line 1, characters 0-615: 

The agent had enough data from that error message to figure things out and make the jsont codec more permissive with option types.

The agent live debugs the missing JSON fields, looks up the original Karakeep source, and fixes the jsont codecs
The agent live debugs the missing JSON fields, looks up the original Karakeep source, and fixes the jsont codecs

The library also made good use of the stateful HTTP interface from Requests, for example by modifying all the default requests to use the API key:

let create ~sw ~env ~base_url ~api_key =                                                                                                                      
  let session = Requests.create ~sw env in                                                                                                                    
  let session =                                                                                                                                               
    Requests.set_auth session (Requests.Auth.bearer ~token:api_key)                                                                                           
  in                                                                                                                                                          
  { session; base_url }

The karakeep.proto subpackage has all the dedicated jsont decoders, which take care of conversion to-and-from OCaml types. I found these quite readable:

(** Type of content a bookmark can have *)                                                                                                                    
type bookmark_content_type =                                                                                                                                  
  | Link  (** A URL to a webpage *)                                                                                                                           
  | Text  (** Plain text content *)                                                                                                                           
  | Asset  (** An attached asset (image, PDF, etc.) *)                                                                                                        
  | Unknown  (** Unknown content type *)                                                                                                                      
                                                                                                                                                              
val bookmark_content_type_jsont : bookmark_content_type Jsont.t

Reflections

The new agent trick I learnt today is the power of debugging against live services. The jsont descriptions derived from the Karakeep source code provided just enough information in error messages that the agent could fix them by trying to run the command against my live service. Luckily, nothing got deleted this time, but in the future projects like Shelter will be ever more important.

Michael Dales asked an interesting question: "Why OCaml over an even more strict language?". My instinct is that OCaml is pretty much the best choice here; I think it's pretty hard to find a usable language with a stronger typing discipline. The more exotic the type systems get, the harder it is to do proof discharge. However, I believe that with some reinforcement learning magic, OxCaml's lifetimes might be gamechanging when combined with agents that can also reason about performance and memory in addition to conventional type safety.

Anyway, I now have a karakeep binary and library that's working great for my search needs. Back to bookmarking Christmas presents I needed to have obtained yesterday!


Loading recent items...