errandRun a coding agent from a chat channel, in a sandbox it cannot escape.
source

Getting started ​

What you need ​

  • A chat bot token, and one channel for it to serve.
  • A credential for a model provider.
  • Linux, with bailey, rootless podman, or Incus. The sandbox is not optional, so one of them has to be there. The Incus backend also needs the errand project and a guest image; see sandboxing and image/.
  • kage, the agent, newer than 0.5.1: on the host's PATH under bailey, or in the image under podman and incus. errand asks it which providers it knows (kage providers --json) and refuses to start when it cannot say.

Build it ​

sh
git clone https://github.com/QaidVoid/errand
cd errand
cargo build --release

That produces dist/errand: one binary carrying the web interface and its own runtime, with what it may do compiled into it. A host that runs it needs neither a checkout nor a toolchain.

Configure it ​

Write ~/.config/errand/config.json. The smallest file that starts:

json
{
  "chat": {
    "token": "the bot token",
    "channelId": "the one channel to serve",
    "allowedUserIds": ["the accounts that may drive sessions"]
  },
  "agent": {
    "provider": "anthropic",
    "providers": {
      "anthropic": {
        "credentialName": "ANTHROPIC_API_KEY",
        "credential": "the provider key"
      }
    }
  },
  "projectRoot": "/srv/errand/projects",
  "stateDir": "/var/lib/errand"
}

Everything else has a default, listed in configuration. Keep the file mode 0600: it holds two credentials.

Run it ​

sh
errand run

It checks the sandbox before it touches the chat service, and says what it can and cannot enforce on this host. If it cannot enforce something the configuration asks for, it stops rather than starting anyway. See sandboxing for what that report means.

Then post in the served channel. The first message opens a thread and starts a session; replies in that thread are prompts.

When it will not start ​

The exit code says which kind of problem it was, so a service can tell a refusal from a crash.

codewhat happened
2the configuration, the sandbox backend, or the token was refused
3the backend cannot enforce a guarantee the configuration demands
4another daemon already holds this state directory

Two daemons on one bot token both act on every message, which is what 4 prevents. A lock left by a process that is gone is taken over.