Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
bb1046a
feat(spawner): server-hosted agents with identity relocation
siddhartpai Jul 26, 2026
5f9aeb8
fix: integrate rebase onto main (global config arg, relocation field …
siddhartpai Jul 27, 2026
56ac3b7
fix(spawner): reclaim agent-container names squatted by a retired spa…
siddhartpai Jul 27, 2026
c96da60
fix(desktop): key server-agent persona lookup by slug, not display name
siddhartpai Jul 27, 2026
c72d0a7
test(desktop): fix server-agents deploy-menu spec
siddhartpai Jul 27, 2026
796fe6c
fix(desktop): clear trusted spawners on community switch
siddhartpai Jul 27, 2026
81bb2d9
fix(spawner): scope container and volume names per spawner identity
siddhartpai Jul 27, 2026
aad6c54
fix(spawner): defer relay frames that interleave a persona query inst…
siddhartpai Jul 27, 2026
41d6191
fix(desktop): retire a relocated agent before handing out its key
siddhartpai Jul 27, 2026
3a25298
docs(core): correct spawner kind docs; fix advertised capacity and st…
siddhartpai Jul 27, 2026
f73ec36
chore: drop scratch files from the spawner PR
siddhartpai Jul 27, 2026
772de31
fix(desktop): unwedge spawner subscriptions, guard slug collisions, h…
siddhartpai Jul 27, 2026
3eb97b3
fix(spawner): install the rustls CryptoProvider before connecting
siddhartpai Jul 28, 2026
8fac443
fix(deploy): pass the spawner's name and host defaults through compose
siddhartpai Jul 28, 2026
f1e7462
docs: design spec for first-party server-agent prompt/model editing
siddhartpai Jul 26, 2026
7a0fc72
docs: implementation plan for server-agent prompt/model editing
siddhartpai Jul 26, 2026
3dac314
feat(sdk): spawner AI catalog, prompt_hash status field, PromptMateri…
siddhartpai Jul 26, 2026
919654a
feat(spawner): advertise AI catalog, echo applied prompt_hash in status
siddhartpai Jul 26, 2026
f261149
feat(desktop): Tauri command to build signed spawner prompt-update fr…
siddhartpai Jul 26, 2026
5422078
feat(desktop): spawner AI catalog + prompt-hash parsing and prompt-up…
siddhartpai Jul 26, 2026
47fc2ee
chore(desktop): lockfile bump for buzz-sdk sha2 dependency
siddhartpai Jul 26, 2026
de41379
feat(desktop): queued, status-acked spawner prompt updates
siddhartpai Jul 26, 2026
26f626e
fix(desktop): hold off resending delivered spawner prompt updates
siddhartpai Jul 26, 2026
2e11c46
feat(desktop): server-agent edit policy
siddhartpai Jul 26, 2026
bc21bf3
feat(desktop): server-agent edit policy - add trim test
siddhartpai Jul 26, 2026
3dad2a4
feat(desktop): server-aware Edit agent dialog
siddhartpai Jul 26, 2026
f3c4df1
fix(desktop): reconcile server agent AI fields with spawner catalog
siddhartpai Jul 26, 2026
4d4ee7d
feat(desktop): push prompt/model edits to the spawner on save
siddhartpai Jul 26, 2026
a66159d
test(spawner): owner_sim exercises prompt-update push and ack
siddhartpai Jul 26, 2026
e9db774
refactor(desktop): extract server prompt-update push to satisfy file-…
siddhartpai Jul 26, 2026
d4fb6fa
fix(desktop): trim prompt material before spawner push
siddhartpai Jul 26, 2026
d81aace
fix(spawner): preserve team instructions across prompt updates
siddhartpai Jul 26, 2026
2e6265c
fix(desktop): rehydrate spawner prompt-update queue lazily per relay
siddhartpai Jul 26, 2026
8737fa2
fix(desktop): skip empty prompt pushes and soften the pending chip
siddhartpai Jul 26, 2026
cce5e77
fix(desktop): never hydrate or persist the prompt-update queue withou…
siddhartpai Jul 27, 2026
34db34c
test(desktop): screenshot spec for server-aware agent editing
siddhartpai Jul 27, 2026
c403118
fix(desktop): don't require local API keys when editing server-hosted…
siddhartpai Jul 27, 2026
d1c6c71
chore(desktop): drop runtimeAvailabilityWarning import left unused by…
siddhartpai Jul 27, 2026
3faa39e
fix(deploy): pass the spawner AI catalog through compose
siddhartpai Jul 28, 2026
98506cd
docs: design spec for per-owner server agent credentials
siddhartpai Jul 27, 2026
2d9d90c
feat(sdk): credential update/ack attestation frames and needs_credent…
siddhartpai Jul 27, 2026
c2b12b6
fix(spawner): handle credential frames in attestation match
siddhartpai Jul 27, 2026
76fdce8
feat(spawner): per-owner credential store with prefix classification
siddhartpai Jul 27, 2026
f81906d
feat(spawner): inject per-owner credential into agent env, displacing…
siddhartpai Jul 27, 2026
3c6d3be
feat(spawner): hold agents without owner credential; apply credential…
siddhartpai Jul 27, 2026
248beb1
feat(desktop): tauri commands to sign credential updates and decode acks
siddhartpai Jul 27, 2026
ef453f7
feat(desktop): credential update transport with encrypted ack waiter
siddhartpai Jul 27, 2026
67ce0a1
feat(desktop): spawner credential card and needs-credential badge
siddhartpai Jul 27, 2026
351c132
test(desktop): screenshots for spawner credential card and needs-cred…
siddhartpai Jul 27, 2026
69ee079
refactor(desktop): drop manual spawner connect-by-pubkey card
siddhartpai Jul 27, 2026
6576702
docs(deploy): note per-owner credential requirement; fmt fixes
siddhartpai Jul 27, 2026
c8ccee5
docs: self-hosted backend guide (relay + spawner) linked from README
siddhartpai Jul 27, 2026
c8f7281
docs: complete the local spawner command (agent relay URL, local image)
siddhartpai Jul 27, 2026
9854ad1
docs: self-hosted guide — same-host rule, agent relay URL, local imag…
siddhartpai Jul 27, 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 @@ -485,6 +485,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