Skip to content
Merged
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
13 changes: 7 additions & 6 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ builds:
- -s -w -X github.com/nnemirovsky/peerbus/internal/version.Version={{ .Version }}

archives:
# One archive per os/arch bundling the single binary plus LICENSE + README.
# Single static binary — no archive wrapping. Users grab the raw binary,
# chmod +x, and run it. Matches the sluice release style; no LICENSE/README
# bundling because the binary is the artifact and both files are already in
# the repo / release notes. name_template intentionally omits the version
# (sluice convention) so `latest/download/peerbus_<os>_<arch>` always works.
- id: peerbus
ids:
- peerbus
formats:
- tar.gz
name_template: "peerbus_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
files:
- LICENSE
- README.md
- binary
name_template: "peerbus_{{ .Os }}_{{ .Arch }}"

changelog:
disable: true
Expand Down
37 changes: 19 additions & 18 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
# Multi-stage build for the peerbus broker.
# Multi-stage build for the peerbus binary.
#
# BROKER ONLY. peerbus's topology is one long-lived broker + many thin,
# ephemeral adapter processes. Adapters are spawned by each agent runtime
# (Claude Code spawns `peerbus adapter --adapter=cc` per session; a
# drain-agent spawns `peerbus adapter --adapter=generic` as its own stdio MCP
# child) and are NEVER containerized here. Containerizing an adapter — or
# worse, running the broker per session — is exactly the cc2cc orphaned
# -server failure mode the broker/adapter split designs out. This image is the
# managed, long-lived broker service only.
# The image bakes in the full `peerbus` multi-command binary (v0.2.0+); the
# ENTRYPOINT is `peerbus` and CMD defaults to `["serve"]`, so by default the
# container is the long-lived broker service. CMD is overridable for ops
# tasks:
# docker run --rm peerbus:latest --version
# docker run --rm -v peerbus-data:/data peerbus:latest audit verify \
# --db /data/peerbus.db
#
# As of v0.2.0 peerbus ships ONE multi-command binary (git/kubectl style); the
# image bakes in `peerbus serve` as the default entrypoint+command so the
# container runs ONLY the `serve` subcommand by design. Only the build target
# (`./cmd/peerbus` instead of `./cmd/peerbus-broker`) changed — the supervision
# contract is identical.
# Adapter mode (`adapter --adapter=cc|generic`) is technically runnable too,
# but adapters are stdio MCP children of the agent runtime (Claude Code spawns
# the cc adapter per session; a drain-agent spawns the generic adapter). Running
# an adapter as a long-lived container service has no agent stdio to attach to
# and ends up either orphaned or replicated per session — the cc2cc failure
# mode the broker/adapter split designs out. Use the release binary, not the
# container, for adapters.
#
# Pure Go: the durable store uses modernc.org/sqlite (no cgo), so the build
# is CGO_ENABLED=0 and the final image is distroless/static (no libc, no
# shell). One static binary, nothing else.
# Pure Go: the durable store uses modernc.org/sqlite (no cgo), so the build is
# CGO_ENABLED=0 and the final image is distroless/static (no libc, no shell).
# One static binary, nothing else.

FROM golang:1.25 AS build
WORKDIR /src
Expand All @@ -29,7 +30,7 @@ RUN CGO_ENABLED=0 go build -ldflags '-s -w' -o /out/peerbus ./cmd/peerbus

FROM gcr.io/distroless/static-debian12
LABEL org.opencontainers.image.source="https://github.com/nnemirovsky/peerbus"
LABEL org.opencontainers.image.description="peerbus broker — agent-agnostic durable message bus (broker only)"
LABEL org.opencontainers.image.description="peerbus — agent-agnostic durable message bus (broker by default; CMD overridable for audit/version)"
LABEL org.opencontainers.image.licenses="MIT"
COPY --from=build /out/peerbus /usr/local/bin/peerbus
# Durable SQLite store (queue + blake3 audit chain) lives on a mounted
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ PEERBUS_TOKENS=... PEERBUS_HMAC_SECRET=... ./peerbus serve
./peerbus audit verify # walk the blake3 audit chain
```

`deploy/peerbus-broker.run` (s6) is an alternative to compose. The container image is the repo-root `Dockerfile` (broker only, pure-Go static, distroless). Do **not** run the broker per session.
`deploy/peerbus-broker.run` (s6) is an alternative to compose. The container image is the repo-root `Dockerfile` (pure-Go static, distroless); it bakes in the full `peerbus` binary with `serve` as the default CMD, so `docker run peerbus:latest` is the broker. CMD is overridable (e.g. `docker run --rm -v peerbus-data:/data peerbus:latest audit verify --db /data/peerbus.db`) but adapters are stdio children of the agent runtime — don't run them as a container service. Do **not** run the broker per session either.

### 2. Wire an adapter

Expand Down
9 changes: 5 additions & 4 deletions deploy/compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# peerbus broker — managed, long-lived service (broker ONLY)
# peerbus broker — managed, long-lived service (default `serve` role)
#
# WHY THIS COMPOSE FILE RUNS ONLY THE BROKER, NEVER AN ADAPTER:
# WHY THIS COMPOSE FILE RUNS THE BROKER, NEVER AN ADAPTER:
#
# peerbus's topology is one long-lived broker + many thin, ephemeral adapter
# processes. The broker holds the durable SQLite queue and the blake3
Expand Down Expand Up @@ -40,8 +40,9 @@ services:
# The canonical broker image is the repo-root Dockerfile (the same one
# the Docker release workflow publishes to ghcr.io). The image ships the
# single `peerbus` multi-command binary with `serve` as the default
# command — the broker-only role is baked into the image's CMD, not into
# the binary.
# command — the binary itself is multi-role (subcommands `serve`,
# `audit verify`, `adapter --adapter=<mode>`), but this manifest pins it
# to `serve` because adapters belong to agent runtimes, not containers.
build:
context: ..
dockerfile: Dockerfile
Expand Down
Loading