Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save anthonyrussano/a93737b4bfb7718f72f8a6eb26e54621 to your computer and use it in GitHub Desktop.

Select an option

Save anthonyrussano/a93737b4bfb7718f72f8a6eb26e54621 to your computer and use it in GitHub Desktop.
Buzz headless agent-runner to hosted community (russano): remaining manual steps

Remaining steps (manual) — headless agent-runner → hosted Buzz

Goal: run only the agent container against your existing hosted community, not a new local full stack.

From your Buzz local runbook gist: community is russano.communities.buzz.xyz (Block hosted relay). Agents must run on a PC; mobile is client-only.


0. What you already have in the repo

These were added under ~/Workspace/buzz:

Path Role
Dockerfile.agent Sprig image (buzz-acp + buzz-agent + MCP + buzz)
docker-compose.agent.yml Full stack or agent-only
.env.agent.example Template
docs/agent-runner-container.md Design notes
Just targets agent-runner-build, agent-runner-up, agent-runner-up-agent, agent-runner-down, agent-runner-logs, agent-runner-ps
Image buzz-agent-runner:local (built once during the spike)

Do not use just agent-runner-up for real use (that brings postgres/redis/relay). Use agent-only against the hosted URL.


1. Vault (on the machine only — never paste tokens in chat)

export VAULT_ADDR=https://vault.wikip.co   # or http://vault.wikip.co:8200
# token only in ~/.vault-token or VAULT_TOKEN in this shell — not chat

vault token lookup   # must succeed
vault kv get -format=json secret/buzz
# If that path is empty/wrong: vault kv list secret/  and find the buzz entry

Presence check only (no values printed):

vault kv get -format=json secret/buzz \
  | python3 -c 'import json,sys; d=json.load(sys.stdin)["data"]["data"]; print("keys:", sorted(d.keys())); print("nonempty:", {k: bool(str(v).strip()) for k,v in d.items()})'

Expect something like identity + relay + LLM provider fields (names vary). Map them to .env.agent yourself.

Also check related paths if needed: secret/ai/, secret/app/, etc. — only if secret/buzz is incomplete.


2. Create .env.agent (gitignored)

cd ~/Workspace/buzz
cp .env.agent.example .env.agent
chmod 600 .env.agent

Set at least:

# Hosted community — WebSocket form
BUZZ_RELAY_URL=wss://russano.communities.buzz.xyz

# Agent Nostr identity (from Vault / desktop keyring export you trust)
BUZZ_PRIVATE_KEY=...          # nsec or hex — one agent identity

# Optional if hosted community requires them
# BUZZ_API_TOKEN=...
# BUZZ_AUTH_TAG=...

# In-image runtime
BUZZ_ACP_AGENT_COMMAND=buzz-agent
BUZZ_ACP_AGENT_ARGS=
BUZZ_ACP_MCP_COMMAND=buzz-dev-mcp
BUZZ_ACP_AGENTS=1
BUZZ_ACP_RESPOND_TO=anyone    # tighten to owner-only later
# BUZZ_ACP_AGENT_OWNER=<your hex pubkey>   # if owner-only

# LLM (pick one path that matches Vault)
BUZZ_AGENT_PROVIDER=anthropic   # or openai
ANTHROPIC_API_KEY=...
ANTHROPIC_MODEL=claude-sonnet-4-5
# or OPENAI_COMPAT_* / OPENAI_API_KEY for OpenAI-compat

AGENT_WORKSPACE=./.agent-workspace

Fill values from Vault offline; never commit .env.agent.


3. Point compose at hosted relay only (optional small edits)

If you want compose defaults to match real use:

  1. In docker-compose.agent.yml, default BUZZ_RELAY_URL for agent-runner to wss://russano.communities.buzz.xyz (or leave env-only).
  2. Prefer starting only agent-runner so postgres/redis/relay never start:
docker compose -f docker-compose.agent.yml --env-file .env.agent up -d agent-runner

depends_on: relay with required: false should allow agent-only; if compose still wants relay, temporarily remove depends_on for agent-runner or use a tiny override file:

# docker-compose.agent-only.yml
services:
  agent-runner:
    depends_on: !reset []
docker compose -f docker-compose.agent.yml -f docker-compose.agent-only.yml --env-file .env.agent up -d agent-runner
  1. Docs: in docs/agent-runner-container.md, mark hosted community path as the primary “real use” path; full local stack = dev/spike only.

4. Justfile targets to add/adjust

You already have build/up/logs. Add or rename so the happy path is agent-only + checks:

# Build agent image only
agent-runner-build-agent:
    docker build -f Dockerfile.agent -t buzz-agent-runner:local .

# Real use: agent only, hosted relay (needs .env.agent)
agent-runner-up-hosted:
    #!/usr/bin/env bash
    set -euo pipefail
    test -f .env.agent
    mkdir -p .agent-workspace
    docker compose -f docker-compose.agent.yml --env-file .env.agent up -d --build agent-runner

agent-runner-logs:
    docker compose -f docker-compose.agent.yml --env-file .env.agent logs -f agent-runner

agent-runner-down:
    docker compose -f docker-compose.agent.yml --env-file .env.agent stop agent-runner
    # or: ... down  (only if you didn't start the full stack)

# Presence: required env keys set in .env.agent (no values printed)
agent-runner-verify-env:
    #!/usr/bin/env bash
    set -euo pipefail
    python3 - <<'PY'
    from pathlib import Path
    need = ["BUZZ_PRIVATE_KEY","BUZZ_RELAY_URL","BUZZ_AGENT_PROVIDER"]
    raw = Path(".env.agent").read_text()
    vals = {}
    for line in raw.splitlines():
        line=line.strip()
        if not line or line.startswith("#") or "=" not in line: continue
        k,v=line.split("=",1); vals[k.strip()]=v.strip().strip('"').strip("'")
    for k in need:
        v=vals.get(k,"")
        print(f"{k}: {'OK' if v else 'MISSING'}")
    # provider-specific
    p=vals.get("BUZZ_AGENT_PROVIDER","").lower()
    if p=="anthropic":
        print("ANTHROPIC_API_KEY:", "OK" if vals.get("ANTHROPIC_API_KEY") else "MISSING")
        print("ANTHROPIC_MODEL:", "OK" if vals.get("ANTHROPIC_MODEL") else "MISSING")
    elif p=="openai":
        print("OPENAI path:", "OK" if vals.get("OPENAI_COMPAT_API_KEY") or vals.get("OPENAI_API_KEY") else "MISSING")
    print("RELAY looks hosted:", "OK" if "communities.buzz" in vals.get("BUZZ_RELAY_URL","") or vals.get("BUZZ_RELAY_URL","").startswith("wss://") else "CHECK")
    PY

# Auth/connect smoke via container logs (no secret dump)
agent-runner-verify-auth:
    #!/usr/bin/env bash
    set -euo pipefail
    docker compose -f docker-compose.agent.yml --env-file .env.agent logs --tail=100 agent-runner \
      | rg -i 'buzz-acp starting|agent_pool_ready|agent initialized|relay connect|auth|error|ready' || true

# Soft E2E checklist (human confirms channel reply)
agent-runner-e2e-check:
    @echo "1) Container running: docker compose -f docker-compose.agent.yml --env-file .env.agent ps"
    @echo "2) Logs show agent_pool_ready + successful relay connect (not connection refused)"
    @echo "3) Agent npub is member of a channel on russano.communities.buzz.xyz"
    @echo "4) From desktop/phone: @mention the agent; expect a reply"
    @echo "5) docker compose ... logs -f agent-runner while testing"

Wire agent-runner-up-agent / agent-runner-up-hosted to the same agent-only command.


5. Build and run

cd ~/Workspace/buzz
source ./bin/activate-hermit   # for just, if you use just

just agent-runner-build-agent
# or: docker build -f Dockerfile.agent -t buzz-agent-runner:local .

just agent-runner-verify-env
mkdir -p .agent-workspace

docker compose -f docker-compose.agent.yml --env-file .env.agent up -d agent-runner
# or your new just agent-runner-up-hosted

docker compose -f docker-compose.agent.yml --env-file .env.agent ps
docker compose -f docker-compose.agent.yml --env-file .env.agent logs -f agent-runner

Healthy log lines (approx):

  • buzz-acp starting: relay=wss://russano.communities.buzz.xyz ...
  • agent initialized / agent_pool_ready
  • Relay connect succeeds (no endless Connection refused)

Bad:

  • Missing private key
  • Auth errors (token/membership)
  • LLM provider errors only when a turn actually runs

6. Community / membership (hosted)

Same mental model as the local runbook gist:

  1. Agent has its own Nostr key (BUZZ_PRIVATE_KEY).
  2. That identity must be in the community and in the channel you @mention.
  3. Desktop or CLI can add the agent / accept invite — whatever you already use for Honey/Fizz.
  4. Phone stays on russano.communities.buzz.xyz; agent “online” = this container running.

If the agent never sees mentions: membership, wrong relay URL (wss:// vs ws://), or BUZZ_ACP_RESPOND_TO=owner-only without owner set.


7. E2E (you confirm)

  1. Container up + clean connect logs
  2. In channel: @AgentName Reply exactly: HEADLESS_OK
  3. Reply appears
  4. Optional: docker compose ... logs shows turn / tool activity

That’s the success bar from the original plan (no GUI).


8. Hardening (after it works)

  • BUZZ_ACP_RESPOND_TO=owner-only + your pubkey
  • One container per agent identity
  • No host port publish needed for agent-runner
  • Rotate any token that was pasted into chat
  • Don’t put .env.agent in git (already gitignored)

9. Verified vs still pending

Item Status
Repo inspection / architecture Done
Dockerfile.agent + image build Done (once)
Compose + docs + basic Just targets Done (spike shape)
Headless buzz-acp init without GUI Done (local smoke)
Gist path: hosted russano.communities.buzz.xyz Known
Vault path secret/buzz exists Known (list only)
Map Vault → .env.agent without leaking values You
Agent-only against hosted relay You
Just: verify-env / verify-auth / e2e-check You (snippet above)
Compose default = hosted, not full stack You (small edit)
Channel membership + real @mention reply You
Token pasted in chat rotated You (recommended)

10. Commands cheat sheet

cd ~/Workspace/buzz
export VAULT_ADDR=https://vault.wikip.co
vault token lookup
# fill .env.agent from secret/buzz (offline)

docker build -f Dockerfile.agent -t buzz-agent-runner:local .
mkdir -p .agent-workspace
docker compose -f docker-compose.agent.yml --env-file .env.agent up -d agent-runner
docker compose -f docker-compose.agent.yml --env-file .env.agent logs -f agent-runner

Stop:

docker compose -f docker-compose.agent.yml --env-file .env.agent stop agent-runner

Related

Personal runbook — remaining manual steps after the headless agent-runner spike. No secrets included.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment