Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e667728
feat(spawner): server-hosted agents with identity relocation
siddhartpai Jul 26, 2026
25dab2b
docs: design spec for first-party server-agent prompt/model editing
siddhartpai Jul 26, 2026
2b4cbf9
docs: implementation plan for server-agent prompt/model editing
siddhartpai Jul 26, 2026
34ada48
feat(sdk): spawner AI catalog, prompt_hash status field, PromptMateri…
siddhartpai Jul 26, 2026
b58b3f3
feat(spawner): advertise AI catalog, echo applied prompt_hash in status
siddhartpai Jul 26, 2026
b1eb4ee
feat(desktop): Tauri command to build signed spawner prompt-update fr…
siddhartpai Jul 26, 2026
376e100
feat(desktop): spawner AI catalog + prompt-hash parsing and prompt-up…
siddhartpai Jul 26, 2026
5ed34ee
chore(desktop): lockfile bump for buzz-sdk sha2 dependency
siddhartpai Jul 26, 2026
73ecc92
feat(desktop): queued, status-acked spawner prompt updates
siddhartpai Jul 26, 2026
4646148
fix(desktop): hold off resending delivered spawner prompt updates
siddhartpai Jul 26, 2026
a0b069c
feat(desktop): server-agent edit policy
siddhartpai Jul 26, 2026
4a75610
feat(desktop): server-agent edit policy - add trim test
siddhartpai Jul 26, 2026
5cdafb3
feat(desktop): server-aware Edit agent dialog
siddhartpai Jul 26, 2026
367f315
fix(desktop): reconcile server agent AI fields with spawner catalog
siddhartpai Jul 26, 2026
0a09ae6
feat(desktop): push prompt/model edits to the spawner on save
siddhartpai Jul 26, 2026
1ac1e45
test(spawner): owner_sim exercises prompt-update push and ack
siddhartpai Jul 26, 2026
2e292dc
refactor(desktop): extract server prompt-update push to satisfy file-…
siddhartpai Jul 26, 2026
ad9290a
fix(desktop): trim prompt material before spawner push
siddhartpai Jul 26, 2026
7ba080e
fix(spawner): preserve team instructions across prompt updates
siddhartpai Jul 26, 2026
683c217
fix(desktop): rehydrate spawner prompt-update queue lazily per relay
siddhartpai Jul 26, 2026
afa6c28
fix(desktop): skip empty prompt pushes and soften the pending chip
siddhartpai Jul 26, 2026
2dba9d5
fix(desktop): never hydrate or persist the prompt-update queue withou…
siddhartpai Jul 27, 2026
f44c0b7
test(desktop): screenshot spec for server-aware agent editing
siddhartpai Jul 27, 2026
7030a0b
fix(desktop): don't require local API keys when editing server-hosted…
siddhartpai Jul 27, 2026
cb7b212
docs: design spec for per-owner server agent credentials
siddhartpai Jul 27, 2026
5571267
feat(sdk): credential update/ack attestation frames and needs_credent…
siddhartpai Jul 27, 2026
114f1b6
fix(spawner): handle credential frames in attestation match
siddhartpai Jul 27, 2026
642a30c
feat(spawner): per-owner credential store with prefix classification
siddhartpai Jul 27, 2026
9cf0eea
feat(spawner): inject per-owner credential into agent env, displacing…
siddhartpai Jul 27, 2026
0d8d4fe
feat(spawner): hold agents without owner credential; apply credential…
siddhartpai Jul 27, 2026
ae4043d
feat(desktop): tauri commands to sign credential updates and decode acks
siddhartpai Jul 27, 2026
162d127
feat(desktop): credential update transport with encrypted ack waiter
siddhartpai Jul 27, 2026
0ac300a
feat(desktop): spawner credential card and needs-credential badge
siddhartpai Jul 27, 2026
92c2148
test(desktop): screenshots for spawner credential card and needs-cred…
siddhartpai Jul 27, 2026
6a0b2f8
docs(deploy): note per-owner credential requirement; fmt fixes
siddhartpai Jul 27, 2026
d1bf00a
docs: self-hosted backend guide (relay + spawner) linked from README
siddhartpai Jul 27, 2026
387ac5b
fix: integrate rebase onto main (global config arg, relocation field …
siddhartpai Jul 27, 2026
fa48238
docs: complete the local spawner command (agent relay URL, local image)
siddhartpai Jul 27, 2026
6864602
docs: self-hosted guide — same-host rule, agent relay URL, local imag…
siddhartpai Jul 27, 2026
739654b
fix(spawner): reclaim agent-container names squatted by a retired spa…
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
99 changes: 98 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"]
Loading