# Blueclaw

> A POSIX-isolated agent host: a Go daemon that runs an AI agent harness on behalf of the person who asked, executes every tool call as that person's own unprivileged Linux user, holds side-effecting calls at an approval gate, and writes every step to a durable event ledger.

- **Type:** MCP server
- **Install:** `agentstack add mcp-yeomyeonggeori-blueclaw`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [yeomyeonggeori](https://agentstack.voostack.com/s/yeomyeonggeori)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [yeomyeonggeori](https://github.com/yeomyeonggeori)
- **Source:** https://github.com/yeomyeonggeori/blueclaw

## Install

```sh
agentstack add mcp-yeomyeonggeori-blueclaw
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# blueclaw — a POSIX-isolated, multi-user agent host

[](https://github.com/yeomyeonggeori/blueclaw/actions/workflows/ci.yml)
[](go.mod)
[](LICENSE)

> **Status: pre-alpha, under active development.** The interfaces, the wire
> grammar, the configuration keys and the database schema all still change
> without notice, and there is no release, no versioning policy and no upgrade
> path between commits. It is published so the design can be read and argued
> with, not so it can be depended on. If you run it, pin a commit and expect to
> read diffs.

A company runs one agent on one machine and everyone talks to it. To a harness
that runs as whoever started it, the whole company is one Unix account: one home
directory, one set of files, one view of every secret, and no record of which
person authorized which side effect. Sales can read engineering's drafts because
nothing on the machine knows they are different people.

blueclaw is the host that makes them different people, and POSIX is how. It is
an open source, self-hosted Go daemon that runs an AI agent harness on behalf of
whichever person asked, executes each requester's tool calls as their own
unprivileged Linux user, holds side-effecting calls at an approval gate, and
writes every step to a durable event ledger. It owns identity, isolation, task
state, the tool catalog, and delivery.

`blueclaw-cli` is the terminal client above; the host is what it connects to.

The agent loop is a replaceable component behind a Go interface
(`agentcontract.Harness`, one method). Five harnesses are selectable, one of them in this
repository. Swapping it does not move the isolation boundary, because tool
execution never leaves blueclaw.

## What blueclaw is, and what it is not

| It is | It is not |
|---|---|
| a host process that runs an agent harness on other people's behalf | an agent, an agent loop, or a model |
| a POSIX identity boundary between the people sharing one machine | a container runtime or a sandbox technology |
| a durable task store with an append-only event ledger | a chat client |
| an MCP client that mounts external tool servers into the catalog, and an MCP server that publishes the requester's catalog to an external harness | a general-purpose MCP server for arbitrary clients |
| a Go binary you build from source and self-host | a hosted service or a packaged binary release |

Read the right-hand column literally. blueclaw separates requesters from each
other, not the agent from the machine; a container does the latter, and the two
compose.

blueclaw is the agent host inside InternKim, an on-premise AI automation
appliance.

## Why an agent host, not another agent

Agent harnesses are already abundant: Claude Code, Codex, opencode, Gemini CLI,
and the loop bundled here. They all run as whoever started them. On a shared,
multi-user machine that means one Unix account for every requester, no
per-person file separation, and no record of which human authorized which side
effect.

Self-hosted assistants have the same shape from the other direction.
[openclaw](https://github.com/openclaw/openclaw) and the projects around it put
a capable agent on your own machine, and that is the right answer for one
person. Nothing stops a team pointing several people at one of them. They were
simply not designed for it, so the second person who asks for something gets the
first person's home directory, the first person's secrets, and a ledger that
cannot say who authorized what. blueclaw starts from the assumption those
projects do not make.

blueclaw takes the opposite split. The harness decides *what* to call. blueclaw
decides *who it runs as*, *whether it runs at all*, and *what is written down*.
A harness that executes tools inside its own process is not an acceptable
integration, because it takes back the only thing the host exists to provide.

The mechanical claim is narrow and testable: tool execution runs as an
unprivileged POSIX user derived from each requester's identity, and
[POSIX](https://pubs.opengroup.org/onlinepubs/9799919799/) ownership and mode
bits are the only access boundary. There is no executable allowlist, no denied
command list, no denied path prefix, and no instruction anywhere in a prompt
telling the model what it may not touch. A command the requester may not run is
not refused by blueclaw; it fails at the kernel, the same way it would fail for
that person at a shell.

That is an opinion, and it cuts both ways. Inside the requester's permissions
the agent is left alone — it may install a package, walk the filesystem, run a
build, try something and undo it, without asking a policy engine whether each
step is on a list. Every such list is a second, worse copy of the permissions
the kernel already enforces: it goes stale, it blocks work the person is
entitled to do, and the model learns to route around it. Confining a process is
a solved problem, and the solution is fifty years old.

So the boundary is drawn once, at identity, and it is absolute. What is left is
judgment about effects that leave the machine — sending a message, publishing a
site, changing a shared calendar — and that goes to a person at the approval
gate.

## How it works

- **connector** — a platform adapter; normalizes an inbound message.
- **task run** — a durable record of one unit of work.
- **event ledger** — the append-only events belonging to a task run.
- **approval gate** — holds a tool call until a human authorizes it.
- **POSIX projection** — maps policy objects to real Linux users and groups.

```text
  chat platform / HTTP ingress
            |
            v
  blueclaw (Go daemon)
    connectors · policy · task store · approvals · tool catalog · POSIX projection
            |
            +-- agentcontract.Harness --+-- .dependency/bluecollar   (Go, in-process)
            |                           +-- an ACP or CLI agent, started as the requester
            |
            +-- tool execution --> blueclaw-posix-helper --> each requester's UID/GID/groups
```

### Intake: connectors normalize a message

Four connector adapters are registered at boot — `mattermost`, `slack`,
`signal`, and `api` — plus `buzz` when `connectors.buzz.enabled` is set
(`internal/app/application.go`). Each turns a
platform-specific payload into the same normalized conversation turn. The `api`
connector needs no chat platform at all and is the way to drive blueclaw from
`curl` or a test.

Intake resolves the sender to a person in the policy document. That resolved
person, not the daemon account, is the identity every later step runs under.

### Task runs and the event ledger

A task run is a row in the task store with one of nine statuses
(`.dependency/bluecollar/taskstate/task_type.go`): `planned`, `running`, `waiting_user_input`,
`waiting_approval`, `blocked`, `interrupted`, `completed`, `failed`,
`cancelled`.

Every step appends an event through `TaskEventService.AppendTaskEvent`
(`.dependency/bluecollar/taskstate/task_event_service.go`). Event names follow a fixed wire grammar —
`tool..requested`, `tool..result`, `approval.pending_call`,
`approval.executed`, `agent.task_launched`, and, for a tool an external harness
ran outside the catalog, `harness.tool_permitted` and `harness.tool_refused`. A
reader can reconstruct what happened without access to the harness's internal
types.

### Approval gates

An approval gate pauses a task run before a side-effecting tool call executes
and records `approval.pending_call` with the exact call
(`internal/approvalgate`, `taskstate.TaskRunService.PauseTaskRun`). Because the
held call is persisted, approval survives a daemon restart and does not block a
live request.

How the approved call then runs depends on which harness held it. The bundled
loop replays the recorded call verbatim
(`.dependency/bluecollar/approval_gate.go`). Every other harness is told in its
next prompt that the approval arrived and asked to issue that exact call again,
so the replay depends on the agent reproducing its own arguments. Levelling
every harness up to a verbatim replay needs the result of a host-executed call
to reach the agent, and the only channel for that today is the loop's own
in-process observation format, so it waits for the ledger to cross the harness
boundary.

Which calls are gated comes from descriptor metadata, not from the tool's name.
`toolcontract` carries an `ApprovalScope` and one of 14 `SideEffectClass` values
per tool (`.dependency/bluecollar/toolcontract/registry.go`), from `none` and `read` through
`workspace_write`, `external_send`, `site_publish`, and `destructive`.

The gate belongs to the host, not to a harness. `internal/approvalgate` holds
the call and `internal/mcpserver` consults it before invoking anything from the
catalog, so every harness meets the same gate on the same calls.

Sending a message,
changing a calendar, publishing a site — every outward or irreversible effect
exists only as a catalog tool, and an external agent has no other route to one.
`internal/acpharness` also refuses ACP's own filesystem and terminal methods,
which pushes an agent's file and shell work back onto the catalog rather than
letting it run beside the gate.

What remains outside is the tools an agent runs inside its own process: goose's
shell, Claude Code's editor. Those are answered yes. The boundary there is
POSIX — the agent runs as the requester's unprivileged user, and a shell call it
makes itself can do no more than one made through `terminal_run`. The answer is
recorded: `harness.tool_permitted` and `harness.tool_refused`
go to the event ledger, so reading a task afterwards shows the calls the catalog
never saw.

One asymmetry is not ours to fix. ACP has a permission channel and a command
line does not, so a CLI harness offers nothing to record.

### POSIX identity projection

Every person in the policy document projects to a real Linux user and every
circle to a real group (`internal/security/posix_identity.go`):

| Policy object | Linux object | Symbol |
|---|---|---|
| person | `bc_person_` user with a primary group of the same name | `LinuxPersonUserName` |
| circle | `bc_circle_` group | `LinuxCircleGroupName` |
| everyone | `bc_shared` supplementary group | `posixSharedGroupName` |
| service internals | `blueclaw` user | `blueclawServiceUserName` |

Names are lowercased, reduced to `[a-z0-9_-]`, and capped at 31 characters. A
lossy or truncated normalization gets a hash suffix, so two people cannot
collide onto one account (`shortenedLinuxName`).

`POSIXStateForPolicy` compiles the policy into the users, groups, and directory
modes the daemon applies at every boot:

| Path | Owner:group | Mode |
|---|---|---|
| `/private/people/` (and `tmp/`, `artifacts/`) | the person | `0700` |
| `/circles/` | `blueclaw`:`bc_circle_` | `2770` |
| `/shared` | `blueclaw`:`bc_shared` | `2755` |
| `/shared/public`, `/shared/cache/**` | `blueclaw`:`bc_shared` | `2775` |
| `/private`, `/private/people`, `/circles` | `blueclaw`:`blueclaw` | `0711` |

`/.blueclaw` — the daemon's own state, logs, configuration, and
identity map — appears in no projected directory entry, so it is never chowned
or chgrped to a task user and stays owned by the service account.

UIDs and GIDs are allocated from 100000 upward through a persisted allocation
table (`cmd/blueclaw-posix-helper/main.go`), so a person keeps the same numeric
identity across restarts and reprovisions and existing file ownership does not
drift.

## Quickstart

There is no packaged install path in this repository: no Makefile, no
Dockerfile, no service unit, no release binaries. Running blueclaw means
building from source. The appliance tooling that provisions, packages, and
deploys it lives in a separate private repository.

Requirements: Go 1.26, [Bun](https://bun.sh) 1.3, Postgres, and one
OpenAI-compatible model endpoint — Ollama, vLLM, LM Studio, OpenRouter, or
anything else speaking that API.

**1. Start `llmd`, the model sidecar.** It holds the provider credentials and
runs beside the daemon.

```bash
cd llmd
printf 'a-local-secret' > /tmp/llmd-auth
BLUECLAW_LLMD_AUTH_KEY_PATH=/tmp/llmd-auth \
BLUECLAW_LLMD_SOCKET_PATH=/tmp/llmd.sock \
BLUECLAW_LLMD_LLAMA_BASE_URL=http://127.0.0.1:11434/v1 \
BLUECLAW_LLMD_LLAMA_MODEL=your-model \
BLUECLAW_LLMD_LLAMA_STRUCTURED_OUTPUTS_ENABLED=true \
BLUECLAW_LLMD_LOCAL_ONLY=true \
bun run src/main.ts
```

`LLAMA_BASE_URL` points at any OpenAI-compatible server; the name is historical.
`http://127.0.0.1:11434/v1` is Ollama. For a hosted provider, set
`OPENROUTER_API_KEY` and drop the local variables. `llmd/README.md` lists the credential
setting.

Treat a local model as a development convenience. The runtime asks for
structured output natively and falls back to a forced tool call when the server
rejects that; Ollama treats the forced choice as a hint, so a model may answer
in prose and fail the turn. Small models also struggle with the larger runtime
schemas.

**2. Start the daemon.** Copy `config/runtime.standalone.example.json`, set your
Postgres connection string and the llmd socket and key paths, then:

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

`cmd/blueclaw` takes exactly two flags, `--runtime` and `--policy`; everything
else is configuration. The 29 migrations under `migrations/` are applied in
order at boot.

A standalone deployment reports `capabilityd: not_configured` and checks only
`llmd`. There is no capability service, so the calendar, task, mail, and site
operations an appliance supplies are simply absent. The agent loop, skills, the
terminal, and files work.

**3. Enable per-person POSIX isolation.** Until this step every requester shares
the daemon's account; this is the step that makes blueclaw multi-user. The
projection is applied only when
`terminal.posixHelperPath` is set. Build and install the setuid helper, then
point the configuration at it:

```bash
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
```

The daemon then synchronizes users, groups, and directory modes from the policy
document at every boot (`internal/app/application.go`).

**4. Give it work, as two different people.** Address two people from your
policy by email through the `api` connector:

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

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

The two runs execute as different Linux users with different `0700` home
directories. Neither can read the other's file, and the ledger records which of
them authorized which side effect. That is the whole claim, and it takes about
thirty seconds to watch.

To watch a whole scenario instead, the lab runner drives the agent loop and
writes every request, response, tool call, and artifact to a directory:

```bash
go run ./cmd/blueclaw-lab virtual-session --scenario presentation \
  --artifact-dir .artifacts/blueclaw-e2e --live-llm --llm-unix-socket /tmp/llmd.sock
```

Live runs spend money, so they are never enabled by configuration alone. The
explicit `--live-llm` flag or `BLUECLAW_E2E_LIVE=1` is required
(`cmd/blueclaw-lab/main.go`). Scenario names resolve through
`e2e.BuiltinScenario`; the scenarios are defined in `internal/e2e/scenarios.go`,
and `--scenario-file` loads one from JSON instead.

## Terminal interface

`cmd/blueclaw-cli` is how you watch and answer a running daemon from a terminal.
It talks to the admin API over HTTP, so it runs wherever you can reach the
daemon.

```b

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [yeomyeonggeori](https://github.com/yeomyeonggeori)
- **Source:** [yeomyeonggeori/blueclaw](https://github.com/yeomyeonggeori/blueclaw)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-yeomyeonggeori-blueclaw
- Seller: https://agentstack.voostack.com/s/yeomyeonggeori
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
