beadle

module
v0.16.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: MIT

README

beadle

A programmable agent daemon controlled by email, secured by GPG.

License CI Go Go Report Card Working Backwards

Beadle is an autonomous agent that receives instructions via email and executes them as multi-stage pipelines. Commands are programs, the daemon is the shell, pipelines are pipes, and GPG signatures are sudo. By design, the owner controls what beadle can do by signing command definitions, and the trust gate controls who can trigger it. The trust gate is enforced today; command-signature enforcement is defined but not yet wired (see below).

How it works. You send an email. Beadle verifies your identity (PGP signature or Proton E2E encryption), checks your permissions, decomposes your instruction into a pipeline of commands, and executes them — mixing AI reasoning with fast CLI tools. You get the result back as an email reply. The daemon runs on your machine. No cloud service, no API keys shared with third parties.

Example. You email "summarize this" from your PGP-verified account:

  1. Beadle verifies your identity and x (execute) permission
  2. The planner maps "summarize" to a pipeline: [summarize, notify, reply]
  3. Stage 0: Claude reads your email and produces a structured summary (45s)
  4. Stage 1: biff wall broadcasts "New summary: Deploy plan" to the team (10ms)
  5. Stage 2: Claude formats the summary and emails it back to you (45s)

The summary flows through the pipeline as JSON. Stage 1 is a side-effect (passthrough) — it reads the data but doesn't modify it. Stage 2 receives the full summary, not stage 1's "ok" output.

Two types of commands:

  • Claude commands spawn an AI session for reasoning tasks: summarization, analysis, code generation. 45-60 seconds per stage.
  • CLI commands exec binaries directly for deterministic operations: notifications, status checks, data transforms. Milliseconds per stage.

Both types are defined as YAML files with a signature field for owner GPG-signing. Command-signature verification is currently a stub and is not yet enforced; today the daemon gates execution on transport trust — a PGP-verified sender matched to a permitted contact — not on the command definitions themselves. Signature enforcement is the planned next step.

beadle-email

The shipping component is beadle-email — an MCP server that gives Claude Code a real email address with cryptographic trust at every layer.

  • Claude emails you. Session summaries, build reports, deploy notifications — anything Claude produces can be mailed to you or your team. No webhook plumbing, no Slack integration. Just email.
  • You email Claude. Send instructions, ask questions, or forward context from your phone. Beadle's four-level PGP trust model and per-contact permissions (rwx) control exactly what Claude can read, reply to, and act on — nothing is implicit.

Platforms: macOS, Linux

Quick Start

curl -fsSL https://raw.githubusercontent.com/punt-labs/beadle/fc23865/install.sh | sh

Downloads the beadle-email binary, verifies its SHA256 checksum, and attempts to install the Claude Code plugin (MCP tools + slash commands + hooks). If plugin installation fails, the script falls back to registering the standalone MCP server (no slash commands or hooks). Runs doctor to check your setup. Restart Claude Code after install. If you previously registered beadle-email as a standalone MCP server via claude mcp add, remove it first with claude mcp remove beadle-email to avoid duplicate registrations.

Plugin install provides MCP tools, slash commands (/inbox, /mail, /send, /contacts, /beadle), and hooks (two-channel display, automatic session setup). Standalone MCP provides only MCP tools --- no slash commands or hooks.

Inspect before running
curl -fsSL https://raw.githubusercontent.com/punt-labs/beadle/fc23865/install.sh -o install.sh
cat install.sh
sh install.sh
Manual install (other MCP clients)
mkdir -p ~/.local/bin
curl -fsSL https://github.com/punt-labs/beadle/releases/latest/download/beadle-email-darwin-arm64 -o ~/.local/bin/beadle-email
chmod +x ~/.local/bin/beadle-email

Replace darwin-arm64 with your platform: darwin-amd64, linux-arm64, linux-amd64. Ensure ~/.local/bin is on your PATH. Configure your MCP client to run beadle-email serve.

Prerequisites
  • An IMAP server for reading (Proton Bridge on localhost, Fastmail, Gmail IMAP, etc.)
  • An SMTP server for sending (Proton Bridge, or any SMTP with STARTTLS support; implicit TLS/SMTPS on port 465 is not yet supported)
  • GPG for signature verification
  • (Optional) Resend API key for fallback sending

Features

  • 23 MCP tools (21 always-on + 2 poll tools gated on config) --- list, search, read, reply, send, mark read/unread (single + batch), move/archive (single + batch), download attachments, verify signatures, inspect MIME, classify trust, list folders, address book (list/find/add/remove contacts), whoami, switch_identity, enable/disable repo guidance, inbox polling (set interval, get status)
  • Multi-identity via ethos --- identity resolved per-request from ethos sidecar. Repo-local config pins identity. Mid-session switching via switch_identity tool. Fallback to default-identity file
  • Two-dimensional trust --- transport trust (trusted/verified/untrusted/unverified) + identity permissions (rwx per contact per identity). Both must pass before autonomous action
  • Four-level transport trust --- trusted (Proton-to-Proton E2E), verified (valid PGP), untrusted (bad PGP), unverified (no signature)
  • Inline PGP verification --- list_messages runs gpg --verify on signed messages automatically
  • Slash commands (plugin only) --- /inbox (process your inbox), /mail (email someone), /send (multi-channel outbound), /beadle (enable/disable repo guidance)
  • Two-channel display (plugin only) --- compact panel summaries with full data in context, no raw JSON in conversation
  • Proton Bridge native --- IMAP STARTTLS for reading, SMTP for sending, Resend API fallback
  • Credential isolation --- secrets resolved at runtime from OS keychain, never stored in config files
  • Health checks --- doctor validates all dependencies; status shows active identity and configuration

MCP Tools

Tool Purpose
list_messages List messages with trust levels. PGP signatures verified inline. Repo-scoped by default; all_repos widens. Paginate with offset.
search_messages Search the mailbox by from / subject / since / text (server-side IMAP SEARCH). Repo-scoped by default; all_repos widens.
read_message Read full message body, headers, attachments, and trust classification. Does not mark the message read.
reply_message Reply to a message: threads via In-Reply-To/References, preserves the Re: + repo tag, quotes the original. Write-gated like send_email.
send_email Send via Proton Bridge SMTP (primary) or Resend API (fallback). Resolves contact names inline.
mark_message / batch_mark_messages Mark one or many messages read (or \Seen cleared with unread). Reports the count actually changed.
move_message Move a message to another folder. Defaults to Archive.
batch_move_messages Move multiple messages to another folder in one call. Returns the count actually moved.
list_folders List all IMAP mailbox folders.
show_mime Inspect multipart MIME structure, PGP parts, and attachments.
verify_signature Verify PGP signature on a message. Returns signer info and key ID.
check_trust Detailed trust classification with encryption type, origin analysis, and identity permission for sender.
download_attachment Extract an attachment by MIME part index (from show_mime). Saves to identity-scoped directory.
list_contacts List all contacts with effective permissions for the active identity.
find_contact Look up a contact by name, email, or alias. Shows effective permissions.
add_contact Add a contact (name, email, aliases, GPG key ID, permissions).
remove_contact Remove a contact by name.
whoami Return the active identity (email, display name, ethos handle).
switch_identity Switch the active identity for this session. Pass an ethos handle or empty to reset.
enable Turn beadle's repo guidance on or off in this repo (action: enable or disable). Deposits the guide + enabled marker and adds the @.punt-labs/beadle/CLAUDE.md import, or removes them. Same marker as the CLI enable/disable.
set_poll_interval Set automatic inbox polling interval (1m, 5m, 10m, 15m, 30m, 1h, 2h) or disable (n).
get_poll_status Return current polling configuration: interval, active state, last check time, unseen count.

Commands

Available when installed as a Claude Code plugin.

Command What it does
/inbox Check beadle's email inbox. Optional natural language filter.
/inbox 5m Set inbox polling interval (1m, 5m, 10m, 15m, 30m, 1h, 2h).
/inbox n Disable automatic inbox polling.
/inbox status Show current polling configuration.
/mail Mail something to the owner or a specific recipient.
/send Send via any channel (email today, Signal later).
/contacts Manage address book (list, add with permissions, remove, find).
/beadle enable / /beadle disable Turn beadle's repo guidance on or off in this repo (writes the enabled marker + CLAUDE.md import). Commit the change via a PR.

Setup

Credential setup

Beadle resolves credentials at runtime through a priority chain: OS keychain → secret file → environment variable. On Linux, the keychain layer tries pass first, then secret-tool (libsecret / GNOME Keyring). On macOS, it uses the system Keychain.

# macOS Keychain (macOS)
security add-generic-password -s beadle -a imap-password -w 'your-bridge-password'
security add-generic-password -s beadle -a resend-api-key -w 'your-resend-key'
security add-generic-password -s beadle -a gpg-passphrase -w 'your-gpg-passphrase'

# pass (Linux, recommended — GPG-encrypted at rest with your own key)
pass insert beadle/imap-password    # prompts for the value, hides input
pass insert beadle/resend-api-key
pass insert beadle/gpg-passphrase

# secret-tool (Linux, fallback — GNOME Keyring via libsecret)
secret-tool store --label='beadle imap-password' service beadle account imap-password
secret-tool store --label='beadle resend-api-key' service beadle account resend-api-key
secret-tool store --label='beadle gpg-passphrase' service beadle account gpg-passphrase

# Or secret files (~/.punt-labs/beadle/secrets/<name>, mode 600)
mkdir -p ~/.punt-labs/beadle/secrets
printf '%s' 'your-bridge-password' > ~/.punt-labs/beadle/secrets/imap-password
chmod 600 ~/.punt-labs/beadle/secrets/imap-password

# Or environment variables
export BEADLE_IMAP_PASSWORD='your-bridge-password'
export BEADLE_RESEND_API_KEY='your-resend-key'

Create the configuration file (~/.punt-labs/beadle/email.json) with your connection parameters.

Proton Bridge (localhost):

mkdir -p ~/.punt-labs/beadle
cat > ~/.punt-labs/beadle/email.json << 'EOF'
{
  "imap_host": "127.0.0.1",
  "imap_port": 1143,
  "imap_user": "you@example.com",
  "smtp_host": "127.0.0.1",
  "smtp_port": 1025,
  "smtp_user": "you@example.com",
  "from_address": "you@example.com",
  "poll_interval": "30m"
}
EOF

External IMAP + SMTP (Fastmail, Gmail, etc.):

{
  "imap_host": "imap.fastmail.com",
  "imap_port": 143,
  "imap_user": "you@fastmail.com",
  "smtp_host": "smtp.fastmail.com",
  "smtp_port": 587,
  "smtp_user": "you@fastmail.com",
  "from_address": "you@fastmail.com",
  "poll_interval": "10m"
}

Trust Model

Level Sender Detection What It Means
trusted Proton → Proton X-Pm-Content-Encryption: end-to-end + X-Pm-Origin: internal End-to-end encrypted by Proton infrastructure
verified External gpg --verify returns exit 0 Valid PGP signature from a known key
untrusted External gpg --verify returns non-zero PGP signature present but invalid or from unknown key
unverified External No multipart/signed No PGP signature present

PGP verification uses an isolated GNUPGHOME per operation. When no key is attached to the message, keys are bridged from the system keyring (~/.gnupg/) into the isolated environment.

Identity

Beadle reads identity from ethos (sidecar pattern — file reads, no import dependency). Resolution chain:

  1. Repo-local config.punt-labs/ethos.yaml with agent: <handle>
  2. Global ethos active~/.punt-labs/ethos/active
  3. Handle → ethos identity YAML~/.punt-labs/ethos/identities/<handle>.yaml supplies name and email; ~/.punt-labs/ethos/identities/<handle>.ext/beadle.yaml supplies the GPG key id. Always global, never a repo-local vendored copy.
  4. Default identity~/.punt-labs/beadle/default-identity (plain email string)

Each identity gets its own directory under ~/.punt-labs/beadle/identities/<email>/ with separate email.json, contacts.json, and attachments/. Root files are auto-migrated on first use.

Contact Permissions

Each contact has an optional permissions map keyed by identity email. Permissions use the Unix rwx model:

Permission Meaning
r (read) Beadle reads and surfaces the message. No autonomous action.
w (write) Beadle may compose and send replies to this contact.
x (execute) Beadle may execute instructions from this contact.

All permissions are stored explicitly. There are no implicit overrides. Contacts without explicit permissions default to --- (no permissions). The address book is a whitelist: messages from unknown senders appear redacted in listings and cannot be read. r and w are enforced; x enforcement is planned. This is orthogonal to transport trust — both must be sufficient for autonomous action.

CLI

beadle-email list [--folder F] [--count N] [--offset N] [--unread] [--all-repos]  # List (current repo by default)
beadle-email read <uid> [--folder F]                    # Read a message (leaves it unread)
beadle-email search [--from A] [--subject S] [--since D] [--text T] [--count N] [--offset N] [--unread] [--all-repos]  # Search the mailbox
beadle-email mark <uid> [--ids a,b,c] [--unread] [--folder F]  # Mark message(s) read/unread
beadle-email send --to ADDR --subject S --body B        # Send an email
beadle-email reply <uid> --body B [--folder F]          # Reply to a message (threaded, quoted)
beadle-email move <uid> [--folder F] [--to DEST]        # Move a message
beadle-email folders                                    # List IMAP folders
beadle-email contact list|add|remove|find               # Manage contacts
beadle-email install                                    # Set up beadle-email (once per machine)
beadle-email uninstall                                  # Remove beadle-email
beadle-email enable                                     # Turn on agent guidance in this repo
beadle-email disable [--purge]                          # Turn it off (--purge deletes the dir)
beadle-email serve [--config PATH]                      # Start MCP server
beadle-email doctor [--config PATH]                     # Check installation health
beadle-email status [--config PATH]                     # Current state summary
beadle-email identity                                   # Show active identity
beadle-email identity set <handle>                      # Set per-repo identity
beadle-email version                                    # Print version

# Global flags (supported by most subcommands; not: install, uninstall, version)
beadle-email --json list                                # JSON output
beadle-email --verbose doctor                           # Debug logging
beadle-email --quiet send --to ...                      # Errors only

install is a once-per-machine step (the binary and the MCP server, which is provided by the beadle marketplace plugin). enable and disable are per-repo: enable, run inside a repo, deposits beadle's agent user-guide at .punt-labs/beadle/CLAUDE.md, writes an enabled marker, and adds the single @.punt-labs/beadle/CLAUDE.md import line to the repo's CLAUDE.md so an agent working there learns how to drive beadle. disable removes that line and the marker, leaving the guide dormant; disable --purge deletes the .punt-labs/beadle/ directory outright. This follows the org Tool Enable/Disable standard.

When a single mailbox is shared by an agent across repos, list shows only the current repo's mail by default (matched by the X-Beadle-Repo header or the [owner/repo] subject tag, with the repo taken from the git origin remote). Pass --all-repos to see the whole mailbox. If the current directory has no repo, list shows everything. The background new-mail poller's count is scoped the same way; the beadle-daemon mission runner is not (it processes owner commands from every repo).

Documentation

Design Log | Pipeline v2 Design | Email Channel Plan | Changelog

Development

make build                 # Build beadle-email binary
make check                 # All quality gates: vet + staticcheck + markdownlint + tests
make lint                  # Lint only (vet + staticcheck)
make test                  # Tests only (go test -race)
make dist                  # Cross-compile for darwin/linux arm64/amd64
make help                  # List all targets

License

MIT

Directories

Path Synopsis
cmd
beadle-daemon command
Command beadle-daemon is a background daemon that monitors email and executes GPG-signed owner instructions.
Command beadle-daemon is a background daemon that monitors email and executes GPG-signed owner instructions.
beadle-email command
Command beadle-email is a stdio MCP server providing email channel tools for Beadle.
Command beadle-email is a stdio MCP server providing email channel tools for Beadle.
internal
channel
Package channel defines the shared contract for Beadle communication channels.
Package channel defines the shared contract for Beadle communication channels.
claudemd
Package claudemd adds and removes a single @-import line in a user-owned CLAUDE.md, disturbing no other byte — with one mandated exception below.
Package claudemd adds and removes a single @-import line in a user-owned CLAUDE.md, disturbing no other byte — with one mandated exception below.
contacts
Package contacts provides address book storage and lookup for Beadle.
Package contacts provides address book storage and lookup for Beadle.
daemon
Package daemon implements the mail-triggered mission pipeline for beadle-daemon.
Package daemon implements the mail-triggered mission pipeline for beadle-daemon.
email
Package email implements the email channel: IMAP/SMTP clients, MIME parsing, trust classification, and PGP-signed message composition.
Package email implements the email channel: IMAP/SMTP clients, MIME parsing, trust classification, and PGP-signed message composition.
enable
Package enable turns beadle's CLAUDE.md guidance composition on and off in a repo.
Package enable turns beadle's CLAUDE.md guidance composition on and off in a repo.
identity
Package identity resolves which identity beadle operates as.
Package identity resolves which identity beadle operates as.
mcp
Package mcp defines the MCP tool surface for Beadle's email channel.
Package mcp defines the MCP tool surface for Beadle's email channel.
paths
Package paths provides the single root directory for all beadle data.
Package paths provides the single root directory for all beadle data.
pgp
Package pgp handles PGP signature verification by shelling out to the gpg binary.
Package pgp handles PGP signature verification by shelling out to the gpg binary.
secret
Package secret resolves credentials from OS credential stores or files.
Package secret resolves credentials from OS credential stores or files.
session
Package session reads the ethos session roster to enumerate participants (human + agent) in the current Claude Code session.
Package session reads the ethos session roster to enumerate participants (human + agent) in the current Claude Code session.
testenv
Package testenv creates complete identity environments for integration tests.
Package testenv creates complete identity environments for integration tests.
testserver
Package testserver provides in-process IMAP and SMTP servers for integration testing.
Package testserver provides in-process IMAP and SMTP servers for integration testing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL