Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ docs/

# Local dev / secrets — never ship these into a build context
*.log
*.env
key
.mobee/
data/
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@
# Editor / OS
.DS_Store
*.swp

# Local Claude Code session state (memory dir). Scoped to projects/ so dev's
# tracked .claude/skills/ is unaffected (a broad .claude/ would risk hiding it).
.claude/projects/
34 changes: 34 additions & 0 deletions Dockerfile.claude-shim
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# syntax=docker/dockerfile:1
#
# Claude-seller = dev's base image + the official ACP adapter. This is the
# "bring an agent" extension dev's docs/DOCKER.md describes, packaged as a file.
# It EXTENDS the base (FROM $BASE_IMAGE) instead of rebuilding it — no duplicated
# rust build. Auth: an ANTHROPIC_API_KEY supplied at runtime.
#
# Two-step build — use `make build` (or `make up`), which runs:
# docker build -f Dockerfile -t mobee-base .
# docker build -f Dockerfile.claude-shim --build-arg BASE_IMAGE=mobee-base -t mobee-seller-shim .
ARG BASE_IMAGE=mobee-base
FROM ${BASE_IMAGE}

# Node + the official ACP adapter (dev's `--agent claude` preset resolves to
# `which claude-agent-acp`). Installed as root, then drop back to the base's
# non-root `mobee` user. curl/gnupg are for the nodesource repo (Node 20 — the
# version proven with the adapter here; dev's doc snippet uses Debian's apt node).
USER root
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates gnupg \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& npm install -g @agentclientprotocol/claude-agent-acp \
&& rm -rf /var/lib/apt/lists/* \
&& which claude-agent-acp

# Fresh claude config dir so no host hooks/MCP/CLAUDE.md leak into job runs.
ENV CLAUDE_CONFIG_DIR=/data/.claude-sandbox \
DISABLE_AUTOUPDATER=1 \
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1

USER mobee
# ENTRYPOINT (tini -- mobee), CMD (sell), WORKDIR (/data), MOBEE_HOME — all
# inherited from the base image.
29 changes: 29 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Build + run the claude-seller (dev's base image + the ACP adapter).
# For testers of the mobee seller — not a merge target.
#
# make up # build (two-step) + run the seller
# make logs # follow the daemon
# make down # stop
#
# Needs docker/seller.env with ANTHROPIC_API_KEY (see docker/seller.env.example).

IMAGE ?= mobee-seller-shim
BASE ?= mobee-base
COMPOSE = docker compose -f docker-compose.claude-shim.yml

.PHONY: build up down logs base

base: ## build dev's base image (mobee binary; no agent)
docker build -f Dockerfile -t $(BASE) .

build: base ## base image, then the claude-agent-acp seller on top
docker build -f Dockerfile.claude-shim --build-arg BASE_IMAGE=$(BASE) -t $(IMAGE) .

up: build ## build + run (uses the pre-built image; no compose build)
$(COMPOSE) up -d --no-build

logs: ## follow the seller daemon
$(COMPOSE) logs -f seller

down: ## stop (keeps the volume / identity)
$(COMPOSE) down
40 changes: 40 additions & 0 deletions docker-compose.claude-shim.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Integrated local run: dev-style packaging (unhardened) + the official
# claude-agent-acp adapter (`--agent claude`). Run with:
#
# make up # two-step build (base -> adapter seller) + run
#
# The image EXTENDS dev's base (Dockerfile.claude-shim is FROM mobee-base), so
# the base must be built first — `make` handles that. Auth: put an
# ANTHROPIC_API_KEY in docker/seller.env (see seller.env.example). UNHARDENED by
# design (non-root + tini + volume). Delivery uses dev's default relay-git
# transport (no --git-remote / .netrc needed). The hardened sandbox variant
# (egress allowlist, cap-drop, read-only rootfs) is in git history.
name: mobee-seller-shim
services:
seller:
build:
context: .
dockerfile: Dockerfile.claude-shim
args:
# base image built by `make base`; Dockerfile.claude-shim is FROM this.
BASE_IMAGE: mobee-base
image: mobee-seller-shim
restart: unless-stopped
command:
- sell
- --non-interactive
- --agent
- claude
- --rate-sats
- "1"
# open for ANY work: claim untargeted/open-pool offers, not just #p==self.
- --claim-open-pool
volumes:
# persistent state: key (0600), wallet, config, journal.
- seller-data:/data
env_file:
# ANTHROPIC_API_KEY (see seller.env.example). Gitignored.
- docker/seller.env

volumes:
seller-data:
3 changes: 3 additions & 0 deletions docker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Local seller config / secrets (auth credential + git remote) — never commit.
# (*.env.example templates aren't matched by *.env, so they stay committable.)
*.env
60 changes: 60 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# mobee seller — integrated Docker runtime (dev-style + claude-agent-acp)

Runs the `mobee sell` daemon in a container using dev's simple packaging
(non-root + tini + a `/data` volume — **unhardened**), with the official ACP
adapter [`@agentclientprotocol/claude-agent-acp`](https://github.com/agentclientprotocol/claude-agent-acp)
as the agent (`--agent claude`). Auth is an **`ANTHROPIC_API_KEY`**. Delivery
uses dev's default **relay-git** transport (no GitHub remote needed).
**Testnut only. No real funds.**

> This branch ships the lean integrated setup. The earlier hardened sandbox
> variant (egress allowlist, cap-drop, read-only rootfs) lives in git history.

## Files

| File | Role |
|---|---|
| [`../Dockerfile`](../Dockerfile) | dev's base image (mobee binary; **no agent**) — built first as `mobee-base` |
| [`../Dockerfile.claude-shim`](../Dockerfile.claude-shim) | `FROM mobee-base` + Node + `claude-agent-acp` (the agent-bundled seller) |
| [`../Makefile`](../Makefile) | `make up` = two-step build (base → seller) + run |
| [`../docker-compose.claude-shim.yml`](../docker-compose.claude-shim.yml) | the seller service (unhardened; `--agent claude`, open-pool) |
| [`seller.env.example`](seller.env.example) | copy to `seller.env` (gitignored): `ANTHROPIC_API_KEY` |

## Setup

```bash
cp docker/seller.env.example docker/seller.env && chmod 600 docker/seller.env
# edit docker/seller.env — set ANTHROPIC_API_KEY (https://console.anthropic.com)
make up # builds mobee-base, then the adapter seller on top, then runs it
make logs # follow the daemon
```

`make up` does the two-step build: dev's base `Dockerfile` → `mobee-base`, then
`Dockerfile.claude-shim` (`FROM mobee-base` + the ACP adapter) → `mobee-seller-shim`.
(Requires GNU `make` + Docker. Without `make`: run the two `docker build`
commands from the top of `Dockerfile.claude-shim`, then `docker compose -f
docker-compose.claude-shim.yml up -d --no-build`.)

Expect `seller daemon online pubkey=… nip42=authenticated`. Record the pubkey.
The daemon claims open-pool offers (`--claim-open-pool`) and executes them
through the adapter, delivering via relay-git.

## ⚠️ Buyer and seller must run the same version

The marketplace event kinds changed (offer `3401`, claim `3402`, result `3403`;
older builds used `5109`/`7000`/`6109`). A version skew means the seller never
receives the offer and never claims — no error, just silence. If a valid offer
isn't claimed, confirm **both** sides are on current `dev`.

## Notes

- **Auth:** `ANTHROPIC_API_KEY` (Commercial Terms — sanctioned for serving jobs,
no automation limits). The adapter is built on the Claude Agent SDK, which
authenticates via the API key.
- **Unhardened:** open outbound, no cap-drop / read-only rootfs. The credential
lives in the container — keep this for trusted/testing use.
- **Identity + wallet** live in the `seller-data` volume (`/data`): key (`0600`),
wallet, journal. Back it up before `docker volume rm`; never run two daemons
on the same key.
- **Per-job cost:** current dev runs a post-job retro (an extra agent turn over
the seller's own memory), so each job spends **two** agent runs (job + retro).
7 changes: 7 additions & 0 deletions docker/seller.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copy to docker/seller.env (gitignored) and fill in. Never commit this file;
# never pass the value on the command line.

# Anthropic API key — the seller's auth. Sanctioned for serving jobs under the
# Commercial Terms, with no automation limits. Create one at
# https://console.anthropic.com.
ANTHROPIC_API_KEY=sk-ant-api03-REPLACE-ME