blueclaw

Quickstart

Go 1.26, Bun 1.3 for the TypeScript packages, Postgres, and a language model endpoint that speaks the OpenAI chat completions API. The bundled harness and…

Requirements

Go 1.26, Bun 1.3 for the TypeScript packages, Postgres, and a language model endpoint that speaks the OpenAI chat completions API. The bundled harness and the memory store are git submodules, so clone recursively:

git clone --recursive https://github.com/yeomyeonggeori/blueclaw.git
cd blueclaw
go build ./...

Configure

config/runtime.standalone.example.json names every model, the database and the key as references such as $\{BLUECLAW_MODEL}, and the loader fills each one in from the environment at boot. A reference whose variable is unset stops the daemon and names the variable. .monkeys at the repository root holds the values:

@standalone
OPENROUTER_API_KEY
BLUECLAW_MODEL_ENDPOINT=https://openrouter.ai/api/v1
BLUECLAW_MODEL=z-ai/glm-5.3-flash
BLUECLAW_EMBEDDING_MODEL=baai/bge-m3
BLUECLAW_DECISION_ENDPOINT=https://openrouter.ai/api/alpha/decisions
BLUECLAW_DECISION_MODEL=~typesafe/jev-latest
BLUECLAW_DATABASE_URL=postgres://blueclaw:blueclaw@127.0.0.1:5432/blueclaw?sslmode=disable

monkeys keeps OPENROUTER_API_KEY in the operating system's keychain (monkeys remember @standalone OPENROUTER_API_KEY stores it once) and sets all of them for one command. Without it, export the same variables. A model entry names the variable that holds its key with apiKeyEnvironment; apiKeyPath reads a key file instead, and an entry may name only one of the two.

One OpenRouter key reaches all three models. The decision model answers intake's closed questions. Kev serves the same API on your own machine: point BLUECLAW_DECISION_ENDPOINT at its /v1/systemone and set BLUECLAW_DECISION_MODEL to kev-latest. A 4B chat model served locally is not enough, since intake asks the chat model for answers in a fixed schema that small models break.

Copy config/policy.example.json to policy.json and add the people who may use the daemon. Each person needs a personID, emails, and the circles they belong to:

{
  "personID": "00000000-0000-0000-0000-000000000002",
  "displayName": "Alex",
  "emails": ["sample@example.com"],
  "securityLevelName": "member",
  "securityLevelRank": 10,
  "circles": ["member"]
}

company.timeZone in the same file sets the clock that schedules and relative dates read.

Start the daemon

monkeys run go run ./cmd/blueclaw --runtime config/runtime.standalone.example.json --policy policy.json
curl -s localhost:8081/admin/api/health | jq '.status, .languageModel, .protocolIdentity.passed'

Without --runtime and --policy the daemon reads runtime.json and policy.json from $BLUECLAW_HOME, then $XDG_CONFIG_HOME/blueclaw, then ~/.blueclaw. The embedded migrations run at boot. The listen address comes from baseURL (the standalone example uses port 8081; with no baseURL it is 127.0.0.1:8080).

A model that fails to initialize shows in languageModel.error; health answers 503 and the task workers stay stopped while the HTTP surface stays up for diagnosis.

Ask it something

The api connector needs no chat platform. Send a message as a person from your policy, then read the reply:

curl -s -X POST localhost:8081/connectors/api/events -H 'content-type: application/json' \
  -d '{"conversationID":"dm:api:sample@example.com","messageID":"m1","senderID":"sample@example.com",
       "replyTargetID":"dm:api:sample@example.com","prompt":"What is the capital of Australia?"}'

curl -s 'localhost:8081/agent/api/replies?conversationID=dm:api:sample@example.com'

A reply arrives within seconds. Messages that arrive before the previous one is answered are read together as one turn.

Turn on per-person isolation

This step needs Linux. Until terminal.posixHelperPath is set, the daemon cannot act as anyone, and tools that touch the workspace as the requester fail closed. Build and install the setuid helper:

go build -o /usr/local/bin/blueclaw-posix-helper ./cmd/blueclaw-posix-helper
sudo chown root:root /usr/local/bin/blueclaw-posix-helper
sudo chmod 4755 /usr/local/bin/blueclaw-posix-helper

Set terminal.posixHelperPath to that path and restart. The daemon then creates one Linux user per person and one group per circle at every boot. An external harness (acp, claude-code, codex, antigravity) refuses to run without this step.

Send work as two people

With the helper in place, address two people from your policy:

for sender in sample@example.com example@example.com; do
  curl -s -X POST localhost:8081/connectors/api/events -H 'content-type: application/json' \
    -d "{\"conversationID\":\"dm:api:$sender\",\"messageID\":\"m2\",\"senderID\":\"$sender\",
         \"replyTargetID\":\"dm:api:$sender\",\"prompt\":\"Write your name to a file in your home directory.\"}"
done

The two runs execute as different Linux users with different 0700 home directories, and neither can read the other's file.

Watch it from a terminal

go build -o blueclaw-cli ./cmd/blueclaw-cli
./blueclaw-cli --base-url http://127.0.0.1:8081

blueclaw-cli reads the admin API. Its four screens (switched with 14 or tab) list task runs, replay one run from its ledger, list the approvals waiting on a person (y confirms the held call, a confirms every held call in that task, n cancels), and report which harness is running and whether calls run as the requester. Run without an enrolled daemon, it walks through setup and can manage a local Postgres for you (internal/enrollment).

On this page