Untrusted Claude Code and Radicle setup

Written on by j23n in privacy ai chezmoi radicle

Claude confinement

I don't trust Claude Code, and especially myself, to not inadvertently upload any or all of my private data to Anthropic's servers and thus be immortalized in logs.

As such, Claude lives in a dedicated lxc container on my machine. If need be, I can mount shared volumes, or forward ports to/from the host, but I find that's rarely necessary.

Here's how I've set things up.

host$ lxc launch ubuntu:24.04 claudes-home # create the container
host$ lxc exec claudes-home /bin/bash # get a shell inside the container

I use chezmoi to manage my dotfiles, so getting set up on a new machine is trivial:

claudes-home$ sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply j23n -b $HOME/.local/bin

In my case, this will install various utilities I use (helix, tmux, git, radicle, etc.) and pull my various config files so I feel right at home.

Next, the claude code installation instructions are straightforward.

Claude contributions and Radicle

This is relatively straightforward! I treat Claude as a contributor to my repositories. It looks at open issues, works on solutions, submits PRs, and I review and merge.

To do so, Claude needs it's own radicle DID inside its container. Radicle makes this easy by accepting a RAD_KEYGEN_SEED environment variable, which I can pass in when a container gets created.

host$ mkdir -p ~/.config/claude-contrib/rad
host$ python3 -c "import secrets; print(secrets.token_hex(32))" > ~/.config/claude-contrib/rad/seed 
host$ chmod 600 ~/.config/claude-contrib/rad/seed

This seed then needs to be made available inside the container

host$ lxc config set claudes-home environment.RAD_KEYGEN_SEED="$(cat ~/.config/claude-contrib/rad/seed)"
host$ lxc config set claudes-home environment.RAD_SEED_REPOS="rad:z31hE1wco9132nedsomethingrandom"

The environment variable persists through restarts and is available for any process started using lxc exec.

My dotfiles include a radicle guide for Claude to properly understand delegates, patches, seeding, etc.

Workflow

When I want Claude to work on something, I tell claude to clone a given repository (say, rad:zVdCS3rdBtnLLui7jaHTf4X9j6FD).

It can look at issues or I can assign issues directly to Claude as it has its own DID, an it can submit patches. Since it's not a delegate of any of my repositories, only I can merge patches, so changes always go through a PR process.

This works surprisingly well for me!