Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7d9d3d2
feat(spawner): server-hosted agents with identity relocation
siddhartpai Jul 26, 2026
9b739e9
fix: integrate rebase onto main (global config arg, relocation field …
siddhartpai Jul 27, 2026
18fcf06
fix(spawner): reclaim agent-container names squatted by a retired spa…
siddhartpai Jul 27, 2026
eced08a
fix(desktop): key server-agent persona lookup by slug, not display name
siddhartpai Jul 27, 2026
595cb1e
test(desktop): fix server-agents deploy-menu spec
siddhartpai Jul 27, 2026
ef69f94
fix(desktop): clear trusted spawners on community switch
siddhartpai Jul 27, 2026
cbe39d8
fix(spawner): scope container and volume names per spawner identity
siddhartpai Jul 27, 2026
5203789
fix(spawner): defer relay frames that interleave a persona query inst…
siddhartpai Jul 27, 2026
37ed4f8
fix(desktop): retire a relocated agent before handing out its key
siddhartpai Jul 27, 2026
1b4dca5
docs(core): correct spawner kind docs; fix advertised capacity and st…
siddhartpai Jul 27, 2026
a153e6c
chore: drop scratch files from the spawner PR
siddhartpai Jul 27, 2026
99b665e
fix(desktop): unwedge spawner subscriptions, guard slug collisions, h…
siddhartpai Jul 27, 2026
2a0289b
fix(spawner): install the rustls CryptoProvider before connecting
siddhartpai Jul 28, 2026
9c96cfc
fix(deploy): pass the spawner's name and host defaults through compose
siddhartpai Jul 28, 2026
d0ff641
docs: design spec for first-party server-agent prompt/model editing
siddhartpai Jul 26, 2026
30ffbd3
docs: implementation plan for server-agent prompt/model editing
siddhartpai Jul 26, 2026
588b912
feat(sdk): spawner AI catalog, prompt_hash status field, PromptMateri…
siddhartpai Jul 26, 2026
9154b77
feat(spawner): advertise AI catalog, echo applied prompt_hash in status
siddhartpai Jul 26, 2026
54cd044
feat(desktop): Tauri command to build signed spawner prompt-update fr…
siddhartpai Jul 26, 2026
e60e252
feat(desktop): spawner AI catalog + prompt-hash parsing and prompt-up…
siddhartpai Jul 26, 2026
1a79ea8
chore(desktop): lockfile bump for buzz-sdk sha2 dependency
siddhartpai Jul 26, 2026
2c8ab8d
feat(desktop): queued, status-acked spawner prompt updates
siddhartpai Jul 26, 2026
ee72204
fix(desktop): hold off resending delivered spawner prompt updates
siddhartpai Jul 26, 2026
a66d907
feat(desktop): server-agent edit policy
siddhartpai Jul 26, 2026
1a19f10
feat(desktop): server-agent edit policy - add trim test
siddhartpai Jul 26, 2026
3356ecc
feat(desktop): server-aware Edit agent dialog
siddhartpai Jul 26, 2026
fda658e
fix(desktop): reconcile server agent AI fields with spawner catalog
siddhartpai Jul 26, 2026
1501f2c
feat(desktop): push prompt/model edits to the spawner on save
siddhartpai Jul 26, 2026
6b806cb
test(spawner): owner_sim exercises prompt-update push and ack
siddhartpai Jul 26, 2026
5770781
refactor(desktop): extract server prompt-update push to satisfy file-…
siddhartpai Jul 26, 2026
ba4bade
fix(desktop): trim prompt material before spawner push
siddhartpai Jul 26, 2026
656f671
fix(spawner): preserve team instructions across prompt updates
siddhartpai Jul 26, 2026
ef55a1f
fix(desktop): rehydrate spawner prompt-update queue lazily per relay
siddhartpai Jul 26, 2026
d3f848f
fix(desktop): skip empty prompt pushes and soften the pending chip
siddhartpai Jul 26, 2026
2aaf9e1
fix(desktop): never hydrate or persist the prompt-update queue withou…
siddhartpai Jul 27, 2026
9f6310e
test(desktop): screenshot spec for server-aware agent editing
siddhartpai Jul 27, 2026
c6dae50
fix(desktop): don't require local API keys when editing server-hosted…
siddhartpai Jul 27, 2026
9c833a5
fix(deploy): pass the spawner AI catalog through compose
siddhartpai Jul 28, 2026
c8f4059
docs: design spec for per-owner server agent credentials
siddhartpai Jul 27, 2026
1f59433
feat(sdk): credential update/ack attestation frames and needs_credent…
siddhartpai Jul 27, 2026
35b0cde
fix(spawner): handle credential frames in attestation match
siddhartpai Jul 27, 2026
47ba1e3
feat(spawner): per-owner credential store with prefix classification
siddhartpai Jul 27, 2026
f5028f9
feat(spawner): inject per-owner credential into agent env, displacing…
siddhartpai Jul 27, 2026
0c50a11
feat(spawner): hold agents without owner credential; apply credential…
siddhartpai Jul 27, 2026
0a51d74
feat(desktop): tauri commands to sign credential updates and decode acks
siddhartpai Jul 27, 2026
ab9421c
feat(desktop): credential update transport with encrypted ack waiter
siddhartpai Jul 27, 2026
9a0ea1f
feat(desktop): spawner credential card and needs-credential badge
siddhartpai Jul 27, 2026
c4fd8d3
test(desktop): screenshots for spawner credential card and needs-cred…
siddhartpai Jul 27, 2026
23a1d5a
refactor(desktop): drop manual spawner connect-by-pubkey card
siddhartpai Jul 27, 2026
7e86100
docs(deploy): note per-owner credential requirement; fmt fixes
siddhartpai Jul 27, 2026
de32845
docs: self-hosted backend guide (relay + spawner) linked from README
siddhartpai Jul 27, 2026
d7b838d
docs: complete the local spawner command (agent relay URL, local image)
siddhartpai Jul 27, 2026
999d6f6
docs: self-hosted guide — same-host rule, agent relay URL, local imag…
siddhartpai Jul 27, 2026
78345d7
fix: add relocation field to snapshot fidelity test fixture
siddhartpai Jul 30, 2026
2f40757
fix: pass targetChannel slot when forwarding definition submit options
siddhartpai Jul 30, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,6 @@ identity.key

# Helm dependency tarballs — regenerable from Chart.lock via `helm dependency build`
deploy/charts/*/charts/*.tgz

# Personal GUI-session launcher (see AGENTS.md); kept out of the repo.
run-dev.command
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,11 @@ reconnects preserve pending avatar verification work):
- `resetAgentObserverStore()` — agent observer relay store
- `resetActiveAgentTurnsStore()` — active agent turn timers
- `resetAgentWorkingSignal()` — agent working indicator signal
- `resetSpawnerAttestationStore()` — pending spawner attestation prompts
- `resetSpawnerStatusStore()` — server-agent status rows
- `resetSpawnerDirectoryStore()` — announced spawner directory
- `resetTrustedSpawners()` — approved-spawner set (identity-scoped: a spawner
trusted under the old identity must not auto-sign under the new one)
- `resetAvatarProfileSync()` — pending verified-avatar profile writes
- `resetAvatarPresentations()` — avatar probes, previews, and Retry toasts
- `resetSidebarRelayConnectionCardState()` — sidebar relay card dismiss state
Expand Down
100 changes: 99 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"crates/buzz-relay",
"crates/buzz-spawner",
"crates/buzz-core",
"crates/buzz-conformance",
"crates/buzz-push-gateway",
Expand Down
106 changes: 106 additions & 0 deletions Dockerfile.acp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# syntax=docker/dockerfile:1.7
#
# The agent runtime image: one `buzz-acp` harness per container, spawned by
# `buzz-spawner`. This is the server-side equivalent of what the desktop app
# launches locally via `managed_agents/runtime.rs::spawn_agent_child`.
#
# `sprig` is a multicall binary (crates/sprig/src/main.rs) that dispatches on
# argv[0], so one build produces the harness, the built-in agent, and the dev
# MCP tools. The symlinks below are how the harness finds them — it spawns its
# ACP agent by name over stdio.
ARG RUST_VERSION=1.95
ARG DEBIAN_VERSION=bookworm

FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS chef
RUN cargo install cargo-chef --locked --version 0.1.71
WORKDIR /build

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential pkg-config libssl-dev ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=planner /build/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
RUN cargo build --release --locked -p sprig --bin sprig \
&& cargo build --release --locked -p buzz-cli --bin buzz \
&& strip target/release/sprig target/release/buzz

FROM debian:${DEBIAN_VERSION}-slim AS runtime
LABEL org.opencontainers.image.title="Buzz ACP Agent" \
org.opencontainers.image.description="buzz-acp harness runtime for server-hosted Buzz agents" \
org.opencontainers.image.source="https://github.com/block/buzz" \
org.opencontainers.image.licenses="Apache-2.0"

# git and ca-certificates are not optional: agents clone and push repositories
# through the relay's smart-HTTP transport, authenticated by git-credential-nostr.
#
# node and curl exist for the optional Claude Code runtime below: the ACP adapter
# ships as an npm package and the CLI installs via a shell script.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl git \
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --system --gid 1000 agent \
&& useradd --system --uid 1000 --gid 1000 --home-dir /home/agent --create-home --shell /bin/bash agent

COPY --from=builder /build/target/release/sprig /usr/local/bin/sprig
COPY --from=builder /build/target/release/buzz /usr/local/bin/buzz

# Multicall dispatch: sprig routes on argv[0].
RUN for name in buzz-acp buzz-agent buzz-dev-mcp git-credential-nostr git-sign-nostr; do \
ln -s /usr/local/bin/sprig "/usr/local/bin/$name"; \
done

# Claude Code and its ACP adapter, for running agents on a Claude subscription
# instead of a metered API key.
#
# # Authentication in a container
#
# The subscription flow is browser-based and interactive, which a container
# cannot do. The non-interactive equivalent is a long-lived token from
# `claude setup-token` (run once on a machine with a browser), supplied as
# `CLAUDE_CODE_OAUTH_TOKEN`. `buzz-acp` does not clear the environment when it
# spawns the adapter, so the token reaches the CLI unchanged.
#
# Forward it with the spawner's own passthrough, which takes variable *names*:
#
# BUZZ_SPAWNER_AGENT_ENV=CLAUDE_CODE_OAUTH_TOKEN
# BUZZ_SPAWNER_AGENT_COMMAND=claude-agent-acp
#
# The alternative — bind-mounting a pre-authenticated `~/.claude` — is not
# supported by the spawner and would share one credential store across every
# agent on the host.
ARG CLAUDE_ACP_VERSION=0.62.0
# The installer takes only [stable|latest|VERSION] and always installs into
# $HOME/.local/bin, so it runs with HOME pointed at the agent's home (this RUN
# is root, whose $HOME the agent user cannot read) and the result is symlinked
# onto PATH. `HOME=... bash` rather than `HOME=... curl`: the variable has to
# reach the shell that does the installing, not the download.
RUN npm install -g "@agentclientprotocol/claude-agent-acp@${CLAUDE_ACP_VERSION}" \
&& npm cache clean --force \
&& curl -fsSL https://claude.ai/install.sh | HOME=/home/agent bash -s -- stable \
&& ln -sf /home/agent/.local/bin/claude /usr/local/bin/claude \
&& chown -R agent:agent /home/agent \
&& /usr/local/bin/claude --version

# The nest. buzz-spawner mounts a per-agent named volume here, so an agent's
# workspace survives restarts and config changes but is invisible to every
# other agent on the host.
#
# `.claude` is created up front and owned by the agent: the CLI writes state
# there, and it must not fall back to a root-owned path or a read-only home.
RUN mkdir -p /home/agent/.buzz /home/agent/.claude && chown -R agent:agent /home/agent

USER agent:agent
WORKDIR /home/agent/.buzz
ENV HOME=/home/agent
# Default to the in-house agent, which needs only an API key. Override with
# BUZZ_SPAWNER_AGENT_COMMAND=claude-agent-acp to use a Claude subscription.
ENV BUZZ_ACP_AGENT_COMMAND=buzz-agent
ENTRYPOINT ["/usr/local/bin/buzz-acp"]
43 changes: 43 additions & 0 deletions Dockerfile.spawner
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# syntax=docker/dockerfile:1.7
ARG RUST_VERSION=1.95
ARG DEBIAN_VERSION=bookworm

FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS chef
RUN cargo install cargo-chef --locked --version 0.1.71
WORKDIR /build

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential pkg-config libssl-dev ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=planner /build/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
RUN cargo build --release --locked -p buzz-spawner --bin buzz-spawner \
&& strip target/release/buzz-spawner

FROM debian:${DEBIAN_VERSION}-slim AS runtime
LABEL org.opencontainers.image.title="Buzz Spawner" \
org.opencontainers.image.description="Reconciles Nostr agent specs into Docker-isolated buzz-acp containers" \
org.opencontainers.image.source="https://github.com/block/buzz" \
org.opencontainers.image.licenses="Apache-2.0"
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --system --gid 1000 buzz \
&& useradd --system --uid 1000 --gid 1000 --home-dir /var/lib/buzz-spawner --create-home --shell /usr/sbin/nologin buzz
COPY --from=builder /build/target/release/buzz-spawner /usr/local/bin/buzz-spawner

# Runs as root, unlike the relay and push gateway. Talking to the Docker socket
# requires membership in the host's docker group, whose GID varies per host, so
# baking a fixed GID here would break on most machines. The socket mount is
# already root-equivalent on the host (see deploy/compose/README.md) — dropping
# to a non-root user inside the container would not change that, only obscure
# it. An operator who wants real privilege reduction should point the spawner at
# a rootless Docker or Podman socket via DOCKER_HOST.
WORKDIR /var/lib/buzz-spawner
ENTRYPOINT ["/usr/local/bin/buzz-spawner"]
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ For a split-terminal workflow (relay logs separate from Vite output), use `just

Want a single-node / VPS relay instead of the local-dev stack? Use the production Compose bundle in [`deploy/compose/`](deploy/compose/README.md) (`docker compose` + Postgres, Redis, MinIO, optional Caddy/TLS). The root [`docker-compose.yml`](docker-compose.yml) is for day-to-day development only.

For the full walkthrough of standing up your own backend — relay, the `buzz-spawner` for server-hosted agents, per-owner Claude credentials, and every gotcha we hit along the way — see [**Self-hosting a full Buzz backend**](docs/self-hosted-backend.md).

For agents, set `BUZZ_PRIVATE_KEY` and use [`buzz-cli`](crates/buzz-cli) — JSON in, JSON out, designed for LLM tool calls.

---
Expand Down
Loading