No description
  • Rust 76.8%
  • Nix 23.2%
Find a file
tsk 2dc45abff0
fix(fleet): gate bot membership changes by channel authority
Keep bot as the desired default, but inspect authoritative channel membership
before adding or upgrading roles. Fall back to member or preserve an existing
membership when the fleet owner lacks owner or admin authority, and warn
instead of retrying rejected changes.

Expose membershipRole for an explicit member opt-out and document the
authority-gated behavior.
2026-07-29 22:42:34 +00:00
examples feat: add fleet operations and runtime controls 2026-07-24 22:45:55 +00:00
nix fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00
src fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00
tests feat: add fleet operations and runtime controls 2026-07-24 22:45:55 +00:00
.gitignore init config 2026-07-18 03:52:00 +00:00
AGENT_CONFIGURATION.md fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00
Cargo.lock build(deps): pin Buzz client and runtime revision 2026-07-27 22:45:33 +00:00
Cargo.toml build(deps): pin Buzz client and runtime revision 2026-07-27 22:45:33 +00:00
CONFIGURATION.md fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00
flake.lock build(deps): pin Buzz client and runtime revision 2026-07-27 22:45:33 +00:00
flake.nix fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00
PLAN.md fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00
README.md fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00
STAGING.md fix(nix): provision hosted agent developer tools 2026-07-21 22:18:33 +00:00
VERIFICATION.md fix(fleet): gate bot membership changes by channel authority 2026-07-29 22:42:34 +00:00

Beekeeper

Beekeeper is a standalone, declarative Buzz agent host for NixOS. The services.beekeeper.agents attribute set is the lifecycle authority: Buzz users can invoke an allowlisted agent, but cannot create, edit, start, stop, or retire one.

Each declared agent receives a durable Nostr identity, Unix user/group, systemd unit, private state directory, workspace, journal, credential, and a required beekeeper-agent-runner sandbox boundary. Beekeeper retains its fleet-owner key, registry, reconciliation state, retirement queue, and generated agent keys in the protected /var/lib/beekeeper control-plane state.

NixOS configuration

For a complete operator walkthrough—including flake import, secrets, adding and removing agents, Goose/Codex/Claude runtime settings, and OpenRouter with Goose—see Configuring hosted agents.

{ config, pkgs, ... }: {
  services.beekeeper = {
    enable = true;
    relayUrl = "wss://buzz.example.com";
    fleetOwnerKeyFile = "/run/secrets/beekeeper-owner-key";
    ownerProfile = {
      displayName = "Beekeeper";
      about = "Buzz agent fleet controller.";
    };
    operationsChannel = "00000000-0000-0000-0000-000000000001";

    agents.reviewer = {
      profile = {
        displayName = "Reviewer";
        about = "Reviews proposed changes.";
      };
      channels = [ "00000000-0000-0000-0000-000000000002" ];
      callerPubkeys = [
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      ];
      respondTo = "allowlist";
      parallelism = 2;
      idleTimeoutSecs = 300;
      maxTurnDurationSecs = 1800;
      contextMessageLimit = 20;
      multipleEventHandling = "queue";
      permissionMode = "plan";
      capabilities = [ "messages" "reviews" ];
      personaPrompt = "Review changes for correctness and regressions.";
      runtime = "codex-acp";
      runtimePackages = [ pkgs.codex-acp ];
      mcpCommand =
        "${config.services.beekeeper.buzzPackage}/bin/buzz-dev-mcp";
      environmentFile = "/run/secrets/reviewer.env";
      resources = {
        memoryMax = "8G";
        cpuQuota = "300%";
        tasksMax = 512;
      };
      sandbox.cwd = "/source";
      sandbox.binds = [{
        source = "/srv/source";
        target = "/source";
        readOnly = true;
      }];
    };
  };
}

Hosted agents receive a baseline PATH containing Bash, Git, OpenSSL, curl, and ripgrep. Developer MCP tools are disabled by default; set mcpCommand explicitly for agents that need them. Use services.beekeeper.path for additional fleet-wide tools and runtimePackages for an agent adapter. A read-only source bind must be updated outside the sandbox; use a controlled writable checkout if the agent must fetch or modify repository state itself.

Buzz built-in personas

Beekeeper can provision Buzz's default Fizz, Honey, and Bumble personas without duplicating their profile or prompt configuration:

services.beekeeper.agents = {
  fizz = {
    preset = "fizz";
    channels = [ "00000000-0000-0000-0000-000000000002" ];
    callerPubkeys = [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ];
  };
  honey = {
    preset = "honey";
    channels = [ "00000000-0000-0000-0000-000000000002" ];
    callerPubkeys = [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ];
  };
  bumble = {
    preset = "bumble";
    channels = [ "00000000-0000-0000-0000-000000000002" ];
    callerPubkeys = [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ];
  };
};

The presets advertise the canonical builtin:* persona IDs and builtin-team:welcome team ID. Explicit fields override preset values. Because the agents share a fleet owner, the secured Buzz runtime recognizes them as verified siblings; the caller allowlist is still required for human operators.

The flake pins buzz-client, buzz-sdk, and the default deployed buzz-runtime to the same Buzz commit, including Buzz's fail-closed DM author gate. Private keys are generated at runtime, never interpolated into Nix derivations or command arguments. The stable fleet owner key is loaded with a systemd credential. Beekeeper derives a NIP-OA owner attestation for each agent, uses it while registering the agent with the relay, and passes it to the agent runtime as a separate systemd credential. The fleet owner attaches agents to every declared channel with owner-signed NIP-29 add-member events. Agents desire the bot role by default, but Beekeeper requests it only when relay state identifies the fleet owner as a channel owner or admin. Otherwise it safely uses or preserves member and upgrades automatically if the controller later gains authority. The owner must already be a member of the operations channel and every private channel listed for an agent so it can add agents and observe and reconcile private membership. Because the relay's agent-owner binding is immutable, Beekeeper rejects a fleet-owner key change while any agent is active or retiring; retire the fleet with the old key before rotating it.

On boot, beekeeper-prepare.service reconciles the optional fleet-owner profile, agent identities and kind-0 profiles, kind-10100 agent directory entries, owner-signed kind-30177 managed-agent records, and channel memberships before any agent unit may start. beekeeper.service then continues reconciliation and reports systemd health changes to the operations channel as controller-signed, machine-readable beekeeper-health events. Buzz TUI displays these reports on its existing Agents surface while keeping lifecycle controls disabled for externally managed agents. Configure ownerProfile only when fleetOwnerKeyFile contains a dedicated controller identity; Beekeeper will publish and manage that key's public profile. Reconciliation reads current relay state as well as the local registry, so deleted or changed profiles, directory entries, managed-agent records, membership roles, and memberships are repaired.

capabilities values are informational directory labels for discovery and UI display. They do not grant tools or enforce runtime permissions; sandbox binds, runtime packages, MCP configuration, and provider credentials define what an agent can actually do.

Removing a declaration queues owner-signed membership removals and an agent-signed retirement tombstone. The private agent key is deleted only after all retirement events have been accepted by the relay and the terminal registry state is durable. Interrupted credential cleanup is retried during later reconciliation.

Operators can inspect the local plan and fleet state without changing it:

beekeeper --config /path/to/beekeeper-fleet.toml \
  --state-dir /var/lib/beekeeper plan
beekeeper --config /path/to/beekeeper-fleet.toml \
  --state-dir /var/lib/beekeeper status
beekeeper --config /path/to/beekeeper-fleet.toml \
  --state-dir /var/lib/beekeeper doctor

The JSON status combines declaration, registry, credential, owner-attestation, and systemd observations. See Configuring hosted agents for the backup and restore validation workflow.

Verification

nix flake check path:. --no-build
nix build path:.#checks.x86_64-linux.package \
  path:.#checks.x86_64-linux.nixos-module -L --no-link

The full check also verifies that Beekeeper ships its sandbox runner and that the pinned Buzz runtime supplies buzz-acp, buzz-agent, and buzz-dev-mcp:

nix flake check path:. -L