Buzznode is one persistent, browser-accessible Linux computer for one
Buzz agent. It joins an existing Buzz
workspace and runs that agent through the headless buzz-acp harness.
Buzznode is not a Buzz workspace client. It does not contain Buzz Desktop, and it does not run a relay, database, object store, or other server-side Buzz service. Use Buzzbox or another Buzz client to manage the workspace, create agents, and communicate with them. Use Buzznode to give one of those agents a dedicated browser, terminal, filesystem, runtime login, and long-lived state.
Not an official Buzz project. Buzznode is an independent, community-built environment that packages published Buzz releases. It is not affiliated with, endorsed by, or sponsored by the Buzz project, buzz.xyz, or Block, Inc. "Buzz" is used here only to describe what this image runs and what it is compatible with; all trademarks belong to their respective owners. Report problems with Buzznode here, not to the upstream Buzz project.
You need credentials for a managed agent — either a buzznode-v1: enrollment
bundle, which Buzzbox produces, or
the agent's relay URL and private key from any other Buzz client. Buzznode
publishes native AMD64 and ARM64 images. Choose the container runtime installed
on your host.
docker run --detach \
--name buzznode \
--shm-size 1g \
--publish 127.0.0.1:6904:6901 \
ghcr.io/pdparchitect/buzznode:latestpodman run --detach \
--name buzznode \
--shm-size 1g \
--publish 127.0.0.1:6904:6901 \
ghcr.io/pdparchitect/buzznode:latestApple's container tool requires Apple
silicon and macOS 26 or later. Start its service once, then give the browser
desktop and agent harness enough memory:
container system start
container run --detach \
--name buzznode \
--memory 4g \
--shm-size 1g \
--publish 127.0.0.1:6904:6901 \
ghcr.io/pdparchitect/buzznode:latestOpen http://127.0.0.1:6904. The setup window asks for the bundle; paste it, or press Enter to type the relay URL and private key instead. Pick Codex, Claude Code, or Goose, and the node connects to your relay and starts handling messages for that agent.
Docker and Podman select the host's native image automatically; Apple
container selects ARM64 on Apple silicon. These commands are for trying the
node out: its state lives in anonymous volumes, so replacing the container
loses the enrollment and leaves the old volumes behind on disk. For anything
you intend to keep, use the
persistent setup below.
Create or select a managed agent in the Buzz client associated with your relay. You need either:
- a
buzznode-v1:enrollment bundle; or - the relay URL, agent private key, authorization, and response policy for manual setup.
When using Buzzbox, its convenience flow is:
- Right-click the Buzzbox desktop and choose Agent Setup → Create New Agent for Buzznode.
- Complete Buzz's managed-agent form and save it.
- Copy the
buzznode-v1:enrollment bundle displayed by Buzzbox.
To use an existing agent instead, stop its local harness and choose Agent Setup → Move Existing Agent to Buzznode.
When using another Buzz workspace, obtain the managed agent credentials through that workspace's client or provisioning process. Buzznode does not contact, discover, or require a Buzzbox instance.
Once the container is running, the first desktop opens a larger terminal setup window. Paste the enrollment bundle, then choose a Codex, Claude Code, or Goose runtime. The wizard confirms completion and waits for Enter before becoming a normal Buzznode terminal; it does not close the window. The bundle supplies:
- the workspace relay WebSocket URL, such as
wss://buzz.example.com; - the agent private key;
- its authorization tag; and
- whether anyone, its owner, an allowlist, or nobody may activate it.
Buzznode separately asks for an optional relay API token because Buzzbox does not store that token with the managed agent.
Buzznode validates the bundle's relay, private key, authorization, and response policy before reporting that enrollment was accepted.
The enrollment bundle is base64-encoded, not encrypted, and contains the agent private key. Treat it like a password and paste it only into Buzznode setup. Once the node is connected, do not run the same agent's local harness in Buzzbox.
The wizard stores connection credentials in
~/.config/buzznode/environment with mode 0600, offers to configure the
selected runtime, and starts buzz-acp. Buzznode discovers the channels that
contain this agent and handles messages addressed to it.
A long-lived node should survive being recreated, so name its volumes and give
it a restart policy. If you already started the container above, remove it and
its anonymous volumes first with docker rm --force --volumes buzznode.
docker run --detach \
--name buzznode \
--restart unless-stopped \
--shm-size 1g \
--publish 127.0.0.1:6904:6901 \
--volume buzznode-workspace:/workspace \
--volume buzznode-config:/home/buzznode/.config \
--volume buzznode-data:/home/buzznode/.local/share \
--volume buzznode-nest:/home/buzznode/.buzz \
--volume buzznode-codex:/home/buzznode/.codex \
--volume buzznode-claude:/home/buzznode/.claude \
ghcr.io/pdparchitect/buzznode:latestPodman accepts the same command with podman in place of docker. With
Apple's tool, use container in place of docker, remove
--restart unless-stopped, and add --memory 4g. Apple container preserves
the named volumes and stopped container but does not expose a Docker-style
restart policy; restart it with container start buzznode.
Setup then runs once. Enrollment, runtime login, browser sessions, and working
files stay put across docker rm and image upgrades. See
Persistence for what each volume holds, and
One node, one agent for running more than one.
To skip the wizard, configure the node from its terminal:
printf '%s\n' "$BUZZNODE_ENROLLMENT_BUNDLE" |
buzznode configure --enrollment-stdin --runtime codex
buzznode runtime-login
buzznode startManual --relay-url, --private-key, --auth-tag, --respond-to, and
--respond-to-allowlist options remain available for non-Buzzbox clients. Use
buzznode setup for normal interactive configuration so secrets do not appear
in shell history. A raw key from buzz-admin generate-key is not a replacement
for creating a managed agent because it has no Buzz profile, owner attestation,
or channel membership.
Buzznode has no runtime dependency on Buzzbox. It can connect to any compatible
Buzz relay that is reachable from the container, including a hosted relay over
wss://, a relay on another server, or a local development relay.
Buzzbox is only an optional onboarding convenience. Its Agent Setup menu can
create a managed agent and package the relay URL, identity, authorization, and
response policy into a buzznode-v1: enrollment bundle. Another Buzz client or
provisioning system can supply the same information instead.
The buzz-local Docker network, ws://buzzbox:3000 relay address, and the two
projects' coordinated Makefile examples are strictly for local testing. They
are not part of the Buzznode architecture and are not required in deployment.
Any compatible Buzz workspace Independent Buzznode
--------------------------------- ---------------------------
Create and manage workspace Run one existing agent
Provide enrollment or credentials ----> Connect to its configured relay
Add agent to channels Run Codex, Claude, or Goose
Chat with and mention agent <---- Handle messages through buzz-acp
The buzz command-line tool remains in the node because buzz-acp makes it
available to the running agent for Buzz messaging and tools. The graphical
buzz-desktop application is deliberately absent and cannot be started.
Buzzbox is a ready-to-run Buzz workspace — desktop, relay, and storage in one image — which makes it the easiest way to exercise a node end to end. The two remain independent projects; their Makefiles coordinate only through an optional Docker network and the relay URL.
Clone Buzzbox next to this repository, then from the buzzbox directory:
make up \
BUZZ_NETWORK=buzz-local \
PUBLIC_RELAY_URL=ws://buzzbox:3000Open Buzzbox at http://127.0.0.1:6903 and choose Agent Setup → Create New Agent for Buzznode. Complete the Buzz form and copy the enrollment bundle.
From this buzznode directory:
make up \
BUZZ_NETWORK=buzz-local \
RELAY_URL=ws://buzzbox:3000Open Buzznode at http://127.0.0.1:6904. The relay URL is prefilled in the setup wizard for manual setup, but the recommended flow is to paste the Buzzbox enrollment bundle. Leave the local API token empty, select a runtime, and complete its login.
Once setup is complete, verify the saved configuration and relay connection:
make connection-testThe first project started creates buzz-local; Docker DNS resolves buzzbox
on that network. Each Makefile still owns only its own container. make stop
in either directory does not stop or remove the other project.
Create another Buzznode container with a different container name and volume prefix for another agent. Keeping nodes isolated gives each agent its own:
- Buzz identity and harness process;
- browser sessions and runtime credentials;
- desktop and filesystem state;
/workspace; and- start, stop, restart, and logs lifecycle.
- the headless
buzz-acp,buzz,buzz-agent, andbuzz-dev-mcptools; - Codex with
codex-acp; - Claude Code with
claude-agent-acp; - Goose with native ACP support;
- Chrome on AMD64 or Chromium on ARM64 for login flows and browser-based agent tasks;
- a terminal, Git, GitHub CLI, Docker CLI, Python, Node.js, pnpm, and common development tools; and
- an Openbox desktop exposed through KasmVNC.
Buzznode contains no buzz-desktop, buzz-relay, PostgreSQL, Redis, or MinIO.
The desktop is about a quarter of the image. It is kept because the node is
meant to be inspectable, because runtime authentication needs a real browser,
and because it is the substrate for computer use: scrot, xdotool, wmctrl,
and a Chromium-family browser are already present, so an agent can drive the
same display a human watches through KasmVNC. See
IMAGE-SIZE.md for the measured
breakdown and the reasoning, and run make size-report to reproduce it.
A purpose-built web application could have taken the desktop's place as the way to reach the node: a terminal, a log view, and a file browser served over HTTP would be far smaller than an X session. That was considered and set aside. It would be a second product to design, build, secure, and maintain alongside the node itself, which is not where the early effort belongs. More to the point, it would not actually remove the graphical stack. Runtime authentication needs a real browser, and computer use needs a real display with real input, so the browser, Xvnc, the fonts, and the software GL renderer stay in the image either way — and those are the bulk of the cost. Openbox, tint2, and the rest of the desktop shell add roughly 22 MiB on top of components already being paid for. Given that, the node lives off the land: it surfaces what is already installed rather than reimplementing a thinner version of it.
buzznode setup
buzznode status
buzznode doctor
buzznode runtime-login
buzznode runtime-login codex device
buzznode start
buzznode stop
buzznode restart
buzznode logsRight-click the desktop for the same agent controls, runtime login actions, terminal, browser, task manager, and harness logs. Runtime login opens a chooser instead of assuming browser authentication. Codex supports device code, desktop browser, or API-key authentication. Claude Code supports Claude subscription, Anthropic Console, long-lived setup-token, or organization SSO flows.
From this directory:
make check
make up
make smokeThe desktop opens at http://127.0.0.1:6904. Useful overrides include:
PORT=8080 RESOLUTION=1600x900 make up
PLATFORM=linux/arm64 make build
DOCKER=podman make upThe Makefile selects linux/amd64 or linux/arm64 from the host by default.
make stop removes the node container but preserves its named volumes.
| Component | Version |
|---|---|
| Buzz headless tools | 0.4.26 |
| Codex | 0.145.0 |
| Claude Code | 2.1.220 |
| Goose | 1.44.0 |
| Codex ACP adapter | 1.1.7 |
| Claude ACP adapter | 0.62.0 |
On AMD64, the Buzz .deb and Goose archive are SHA-256 verified during the
image build. Only the required headless Buzz binaries are extracted from the
.deb; the package and its desktop application are not installed. Upstream
does not publish a Linux ARM64 package, so ARM64 builds compile only those
headless tools from the pinned tag after verifying its exact Git commit. The
ARM64 Goose archive is independently SHA-256 verified.
Keep separate volumes for:
/workspace— the node's working files;~/.configand~/.local/share— node, browser, desktop, and Goose state;~/.buzz— the Buzz agent nest;~/.codex— Codex state; and~/.claude— Claude Code state.
Buzznode is a trusted, single-user workstation:
- the saved agent private key can act as that agent;
- KasmVNC browser authentication and TLS are disabled;
- the
agentuser has passwordless sudo; - coding agents can operate on
/workspace; - browser sessions and agent credentials persist in volumes; and
- Codex runs with
sandbox_mode = "danger-full-access", because its bubblewrap sandbox cannot create a user namespace inside a container, so no sandbox mode is enforceable here whatever is configured. The setting states what is true rather than implying a boundary that does not exist; the boundary is the container. Override withBUZZNODE_CODEX_SANDBOX_MODE.
The provided Makefile binds the desktop to 127.0.0.1. Keep that default, or
put Buzznode behind authentication, TLS, and suitable network controls. Never
publish port 6901 directly to an untrusted network, expose the saved agent
key, or reuse a human Buzz private key as the node identity.
See RELEASES.md for the image release process.
Access to the desktop web application is not authenticated. KasmVNC is started
with -disableBasicAuth and TLS disabled, and the KasmVNC password written on
first boot exists only because KasmVNC checks that the file is there. It is not
an access control and should not be treated as one.
This is a deliberate decision, and built-in authentication is not planned. A node reachable beyond loopback is expected to be fronted by whichever access layer already suits its environment: a conventional reverse proxy, or preferably a zero-trust access proxy such as Cloudflare Access or an equivalent identity-aware proxy. Those systems already own identity, session lifetime, device posture, revocation, and audit, and in an enterprise deployment they are the layer that has to be satisfied regardless of what the node does.
Adding a second mechanism inside the node would not strengthen that arrangement, it would compete with it: two session models to keep aligned, two places to revoke an operator, and an open question about which one wins when they disagree. Leaving the node unauthenticated keeps a single enforcement point and a single path forward — the proxy is the front door, and there is no second door to reason about or accidentally leave open.
The practical consequence is that the loopback bind is the only boundary until an access layer is put in front of it. Treat exposing the node without one as publishing an unauthenticated root shell, because that is what it is.