Architecture
thepopebot uses a two-layer architecture:
- Event Handler - Node.js server for webhooks, Telegram chat, and cron scheduling
- Docker Agent - Pi coding agent container for autonomous task execution
┌───────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Event Handler │ ──1──► │ GitHub │ │
│ │ (creates job) │ │ (job/* branch) │ │
│ └────────▲────────┘ └────────┬────────┘ │
│ │ │ │
│ │ 2 (triggers run-job.yml) │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────┐ │
│ │ │ Docker Agent │ │
│ │ │ (runs Pi, PRs) │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ 3 (creates PR) │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────┐ │
│ │ │ GitHub │ │
│ │ │ (PR opened) │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ 4a (update-event-handler.yml) │
│ │ 4b (auto-merge.yml) │
│ │ │ │
│ 5 (Telegram notification) │ │
│ └───────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
File Structure
/
├── .github/workflows/
│ ├── auto-merge.yml # Auto-merges job PRs (checks AUTO_MERGE + ALLOWED_PATHS)
│ ├── docker-build.yml # Builds and pushes Docker image to GHCR
│ ├── run-job.yml # Runs Docker agent on job/* branch creation
│ └── update-event-handler.yml # Notifies event handler on PR opened
├── .pi/
│ ├── extensions/ # Pi extensions (env-sanitizer for secret filtering)
│ └── skills/ # Custom skills for the agent
├── docs/ # Additional documentation
├── event_handler/ # Event Handler orchestration layer
│ ├── server.js # Express HTTP server
│ ├── actions.js # Shared action executor (agent, command, http)
│ ├── cron.js # Cron scheduler
│ ├── cron/ # Working directory for command-type cron jobs
│ ├── triggers.js # Webhook trigger middleware
│ ├── triggers/ # Working directory for command-type trigger scripts
│ ├── .env # Environment config (generated by setup)
│ ├── claude/ # Claude API integration
│ └── tools/ # Job creation, GitHub, Telegram utilities
├── operating_system/
│ ├── SOUL.md # Agent identity and personality
│ ├── CHATBOT.md # Telegram chat system prompt
│ ├── JOB_SUMMARY.md # Job summary prompt
│ ├── HEARTBEAT.md # Self-monitoring
│ ├── CRONS.json # Scheduled jobs
│ └── TRIGGERS.json # Webhook trigger definitions
├── setup/ # Interactive setup wizard
│ ├── setup.mjs # Main wizard script
│ └── lib/ # Helper modules
├── logs/ # Per-job directories (job.md + session logs)
├── Dockerfile # Container definition
├── entrypoint.sh # Startup script
└── SECURITY.md # Security documentation
Event Handler
The Event Handler is a Node.js Express server that provides orchestration capabilities.
API Endpoints
| Endpoint | Method | API_KEY | Purpose |
|---|---|---|---|
/ping | GET | Y | Health check, returns {"message": "Pong!"} |
/webhook | POST | Y | Generic webhook for job creation |
/telegram/webhook | POST | N | Telegram bot webhook (uses its own secret) |
/telegram/register | POST | Y | Register Telegram webhook URL |
/github/webhook | POST | N | Receives notifications from GitHub Actions (uses its own secret) |
/jobs/status | GET | Y | Check status of a running job |
Examples:
Create a job via webhook:
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"job": "Update the README with installation instructions"}'
Check job status:
curl "http://localhost:3000/jobs/status?job_id=abc123" \
-H "x-api-key: YOUR_API_KEY"
Register Telegram webhook:
curl -X POST http://localhost:3000/telegram/register \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"bot_token": "YOUR_BOT_TOKEN",
"webhook_url": "https://your-ngrok-url.ngrok-free.dev/telegram/webhook"
}'
Components
- server.js - Express HTTP server handling all webhook routes
- cron.js - Loads CRONS.json and schedules jobs using node-cron
- triggers.js - Loads TRIGGERS.json and returns Express middleware for webhook triggers
- claude/ - Claude API integration for Telegram chat with tool use
- tools/ - Job creation, GitHub API, and Telegram utilities
GitHub Actions Workflows
| Workflow | Trigger | Purpose |
|---|---|---|
docker-build.yml | Push to main | Builds Docker image, pushes to GHCR |
run-job.yml | job/* branch created | Runs Docker agent container |
auto-merge.yml | PR opened from job/* branch | Checks AUTO_MERGE + ALLOWED_PATHS, merges if allowed |
update-event-handler.yml | After auto-merge.yml completes | Gathers job data and sends to event handler for Telegram notification |
Flow:
- Event handler creates a
job/uuidbranch via GitHub API - GitHub Actions detects branch creation → runs
run-job.yml - Docker agent executes task, commits results, creates PR
auto-merge.ymlruns → checks merge policy → squash merges (or leaves open)update-event-handler.ymlruns → gathers job data → sends to event handler → Telegram notification
Docker Agent
The container executes tasks autonomously using the Pi coding agent.
Container includes:
- Node.js 22
- Pi coding agent
- Playwright + Chromium (headless browser, CDP port 9222)
- Git + GitHub CLI
Environment Variables:
| Variable | Description | Required |
|---|---|---|
REPO_URL | Your repository URL | Yes |
BRANCH | Branch to work on (e.g., job/uuid) | Yes |
SECRETS | Base64-encoded JSON with protected credentials | Yes |
LLM_SECRETS | Base64-encoded JSON with LLM-accessible credentials | No |
Runtime Flow:
- Extract Job ID from branch name
- Start Chrome in headless mode
- Decode and export secrets (filtered from LLM's bash)
- Decode and export LLM secrets (accessible to LLM)
- Configure Git credentials
- Clone repository branch
- Run Pi with SOUL.md + job.md
- Commit all changes
- Create PR (auto-merge handled by
auto-merge.ymlworkflow)
Session Logs
Each job gets its own directory at logs/{JOB_ID}/ containing both the job description (job.md) and session logs (.jsonl). These can be used to resume sessions or review agent actions.