Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ workspaces/*
# SSH keys for Terraform
charts/peerbot/provider/hetzner/ssh_key
charts/peerbot/provider/hetzner/ssh_key.pub
.github-app-private-key.pem
92 changes: 56 additions & 36 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@

### Package Architecture
- **`packages/core`**: Shared code between gateway and worker (interfaces, utils, types). Any code reused by both must live here.
- **`packages/gateway`**: Platform-agnostic gateway. Slack code under `src/slack/`. Future chat platforms (Discord, Teams) will live alongside as separate modules in dispatcher pattern.
- **`packages/gateway`**: Platform-agnostic gateway. Slack code under `src/slack/`. Orchestration under `src/orchestration/`. Future chat platforms (Discord, Teams) will live alongside as separate modules in dispatcher pattern.
- **`packages/worker`**: Claude-specific logic in `src/claude/`. Worker talks only to gateway and agent (Claude CLI). No Slack/platform knowledge.
- **`packages/orchestrator`**: Deployment engine only. Talks to compute (Docker/K8s) and Redis queues. No platform knowledge.

### Module Boundaries
- Gateway: Slack → `src/slack/`, future platforms → `src/dispatcher/{platform}/`
- Gateway: Slack → `src/slack/`, orchestration → `src/orchestration/`, future platforms → `src/dispatcher/{platform}/`
- Worker: Platform-agnostic, Claude logic isolated to `src/claude/`
- Core: Shared interfaces, utils, types for gateway+worker
- Orchestrator: Compute engine (Docker/K8s) + Redis only

### Repository Layout
- Monorepo managed by Bun workspaces: `packages/gateway`, `packages/orchestrator`, `packages/worker`, `packages/core`.
- Top-level: `Makefile`, `bin/` (CLI/setup), `sidecar.yaml`, `charts/peerbot` (Helm), `workspaces/`, `.env*`.
- Monorepo managed by Bun workspaces: `packages/gateway`, `packages/worker`, `packages/core`.
- Top-level: `Makefile`, `bin/` (CLI/setup), `charts/peerbot` (Helm), `workspaces/`, `.env*`.
- TypeScript sources under `packages/*/src`. Tests in `packages/*/src/__tests__` and `packages/core/tests`.
- **ALWAYS prefer `bun` commands over `npm`**
- When fixing unused parameter errors, remove the parameter entirely if possible rather than prefixing with underscore
Expand All @@ -27,18 +25,24 @@ We currently use WhatsApp as the messaging platform (Slack support also availabl
There is also a public endpoint in gateway to trigger running the agent.

#### Orchestration
- We support DockerDeploymentManager and KubernetesDeploymentManager.
- **Deployment modes**: Kubernetes (production), Docker (development), Local (development without Docker)
- All workers are sandboxed with your settings.

**Local Deployment Mode** (`DEPLOYMENT_MODE=local`):
- Workers run as child processes of the gateway (no Docker required)
- Uses Anthropic Sandbox Runtime (`@anthropic-ai/sandbox-runtime`) for OS-level isolation
- Sandboxing configuration via `SANDBOX_ENABLED`:
- `unset` (default): Auto-detect - enable if srt installed, warn if not
- `true`: Explicitly enable (fails if srt not installed)
- `false`: Disable sandboxing (escape hatch for troubleshooting)
- Workers use HTTP proxy for network filtering (same as Docker/K8s modes)
- Git operations require `GIT_TEMPLATE_DIR=""` (set automatically)
- **Known limitation**: Complex git clone fails in sandbox; use git worktree pattern (gateway clones, creates worktree for worker)

#### MCP
- The users pass the PEERBOT_MCP_SERVERS_URL env (.peerbot/mcp.config.json) to enable MCP proxy in the gateway.
- The workers get the MCP settings from gateway's internal config endpoint and their JWT token from environment variables to perform MCP calls through proxy.
- Peerbot includes following MCPs in the workers by default: // TODO explain them and make it concise
- AskUser
-- put one example
- UploadFile
- [processmanager]
- ? (add anything else)
- Users pass the PEERBOT_MCP_SERVERS_URL env (pointing to `.peerbot/mcp.config.json`) to enable MCP proxy in the gateway.
- Workers get MCP settings from gateway's internal config endpoint and use their JWT token to perform MCP calls through the proxy.
- Built-in MCPs available to workers: AskUser (request user input), UploadFile (share files with user).

#### Network

Expand All @@ -64,7 +68,7 @@ TypeScript packages must be compiled from `src/` → `dist/`. If you modify any
- The "is running" thread status indicator (with rotating messages) provides user feedback during processing; visible "Still processing" heartbeat messages are not sent to avoid clutter.
- Anytime you make changes in the code, you MUST:

1. Have the bot running via sidecar (`/process-management`) for development.
1. Have the gateway running (see Development Mode below).
2. Test the bot using the test script:
```bash
./scripts/test-bot.sh "@me test prompt"
Expand All @@ -73,7 +77,6 @@ The script automatically handles sending the message, waiting for response, and
```bash
./scripts/test-bot.sh "@me first message" "follow up question" "another question"
```
3. Check logs using `get_logs("gateway")` via `/process-management` to verify the bot works properly.

- If you create ephemeral files, you MUST delete them when you're done with them.
- Use Docker to build and run the Slack bot in development mode, K8S for production.
Expand All @@ -98,22 +101,25 @@ File attachments are fully supported in all message contexts (DM, app mentions,
```

### Starting Development
**Automatic!** Just open this project in Claude Code - sidecar auto-starts:
1. Redis server (port 6379)
2. Package watcher (rebuilds on changes)
3. Gateway with hot reload (port 8080)
```bash
# Terminal 1: Start Redis
redis-server

# Terminal 2: Watch and rebuild packages on changes
make watch-packages

A tmux session opens showing all process output.
# Terminal 3: Run gateway with hot reload
cd packages/gateway && bun run dev
```

### Managing Processes
Use `/process-management` if needed:
- `list_processes` - See status
- `get_logs("gateway")` - View logs
- `restart_process("gateway")` - Restart
Or use Docker Compose for a simpler setup:
```bash
docker compose up
```

### Hot Reload
- **Gateway**: Runs with `bun --watch`, auto-restarts on source changes
- **Packages**: The `packages` process watches and rebuilds TypeScript packages
- **Packages**: Use `make watch-packages` to auto-rebuild on changes
- **Worker**: Run `make clean-workers` after worker code changes

### Testing
Expand All @@ -124,16 +130,16 @@ Use `/process-management` if needed:
## Deployment Instructions

When making changes to the Slack bot:
1. **Development**: Open project in Claude Code (auto-starts). View logs with `get_logs("gateway")`
1. **Development**: Start gateway with `cd packages/gateway && bun run dev`
2. **Kubernetes deployment**: Use `make deploy` for production deployment

## Environment Configuration

The `.env` file is the single source of truth for all secrets and configuration.

### Local Development
- Sidecar automatically reloads gateway when `.env` changes (via `envFile: .env`)
- No manual action needed
- Gateway reads `.env` on startup
- Restart gateway after `.env` changes: `cd packages/gateway && bun run dev`

### Kubernetes Deployment
When `.env` changes, sync secrets to K8s using Sealed Secrets:
Expand All @@ -159,13 +165,27 @@ brew install kubeseal

The gateway deployment has a checksum annotation that triggers automatic pod restart when secrets change via `helm upgrade`.

### Secrets Strategy Matrix

| Environment | Script | Approach | Git Safe |
|-------------|--------|----------|----------|
| **Production** | `./scripts/seal-env.sh --apply` | SealedSecrets | Yes - encrypted |
| **Staging** | `./scripts/seal-env.sh --apply` | SealedSecrets | Yes - encrypted |
| **Local K8s** | `./scripts/sync-env-to-k8s.sh` | Plain Secrets | No - dev only |
| **Docker** | N/A (reads .env directly) | File mount | N/A |

**Key Rules:**
- **Production**: Always use SealedSecrets. Never commit plain secrets to Git.
- **Local dev**: Use `sync-env-to-k8s.sh` for convenience (creates plain K8s secrets that disappear when cluster is deleted).
- **Never mix**: Choose one strategy per cluster and stick with it.

## Development Configuration

- Rate limiting is disabled in local development
- Worker image built with `make build-worker` or `make setup`

### Docker Compose (Alternative)
For quick demos without sidecar, docker-compose.yml is available:
For running everything in containers:
```bash
docker compose up
```
Expand All @@ -192,7 +212,7 @@ PUBLIC_GATEWAY_URL=https://your-domain.com

2. **Configure OAuth callback URL** in your OAuth provider:
```
https://buraks-macbook-pro.brill-kanyu.ts.net/mcp/oauth/callback
${PUBLIC_GATEWAY_URL}/mcp/oauth/callback
```

3. **Configure MCP servers** with OAuth (two options):
Expand Down Expand Up @@ -300,7 +320,7 @@ curl -X POST http://localhost:8080/api/messaging/send \
```

### Check Logs
Use `/process-management`:
```
get_logs("gateway", tail=50)
Gateway logs are output to the terminal where it's running. For Docker:
```bash
docker compose logs -f gateway
```
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@AGENTS.md
Use termos UI components per project conventions when applicable.
11 changes: 3 additions & 8 deletions Dockerfile.gateway
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,23 @@ ENV PATH="/usr/local/bin:${PATH}"

WORKDIR /app

# Set up workspace for gateway + core + github
# Set up workspace for gateway + core
COPY tsconfig.json ./
RUN echo '{ "name": "@peerbot/gateway-build", "private": true, "workspaces": [ "packages/core", "packages/github", "packages/gateway" ] }' > package.json
RUN echo '{ "name": "@peerbot/gateway-build", "private": true, "workspaces": [ "packages/core", "packages/gateway" ] }' > package.json
COPY packages/core/package.json ./packages/core/
COPY packages/github/package.json ./packages/github/
COPY packages/gateway/package.json ./packages/gateway/

# Install dependencies with Bun (faster than npm)
RUN --mount=type=cache,target=/root/.bun/install/cache bun install || true

# Copy source code
COPY packages/core/ ./packages/core/
COPY packages/github/ ./packages/github/
COPY packages/gateway/ ./packages/gateway/

# Build core and github packages (gateway has type errors, run from source)
# Build core package (gateway runs from source for faster iteration)
WORKDIR /app/packages/core
RUN bun run build

WORKDIR /app/packages/github
RUN bun run build

# Install tsx for running TypeScript with Node.js
WORKDIR /app
RUN npm install -g tsx
Expand Down
31 changes: 21 additions & 10 deletions Dockerfile.worker
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ RUN apt-get update && apt-get install -y \
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& apt-get update \
&& apt-get install -y gh \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install matplotlib \
&& ln -sf /bin/bash /bin/sh
Expand All @@ -54,6 +55,22 @@ RUN addgroup --gid 1001 claude && \
echo 'claude ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
usermod -aG docker claude

# Install Nix (single-user mode for container simplicity)
# This allows workers to use nix-shell or nix develop for custom environments
RUN mkdir -p /nix && chown claude:claude /nix
USER claude
RUN curl -L https://nixos.org/nix/install | sh -s -- --no-daemon
# Add nixpkgs channel for nix-shell -p to work
RUN /home/claude/.nix-profile/bin/nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs && \
/home/claude/.nix-profile/bin/nix-channel --update
USER root
# Add Nix to PATH and set NIX_PATH for all users
RUN echo 'export PATH="/home/claude/.nix-profile/bin:$PATH"' >> /etc/profile.d/nix.sh && \
echo 'export NIX_PATH="nixpkgs=/home/claude/.nix-defexpr/channels/nixpkgs"' >> /etc/profile.d/nix.sh && \
chmod +x /etc/profile.d/nix.sh && \
echo 'export PATH="/home/claude/.nix-profile/bin:$PATH"' >> /home/claude/.bashrc && \
echo 'export NIX_PATH="nixpkgs=/home/claude/.nix-defexpr/channels/nixpkgs"' >> /home/claude/.bashrc

RUN npm install -g @anthropic-ai/claude-code && \
echo "NPM global packages:" && \
npm list -g --depth=0 && \
Expand All @@ -68,14 +85,13 @@ RUN echo 'export PATH="/home/bun/.bun/bin:$PATH"' >> /home/claude/.bashrc && \
echo 'export PATH="/root/.cargo/bin:$PATH"' >> /home/claude/.bashrc

# Create minimal package.json for worker-only workspace
RUN echo '{ "name": "@peerbot/worker-build", "private": true, "workspaces": [ "packages/core", "packages/github", "packages/worker" ] }' > package.json
RUN echo '{ "name": "@peerbot/worker-build", "private": true, "workspaces": [ "packages/core", "packages/worker" ] }' > package.json

# Copy dependency manifests
COPY bun.lock ./bun.lock
COPY tsconfig.json ./
COPY packages/worker/package.json ./packages/worker/
COPY packages/core/package.json ./packages/core/
COPY packages/github/package.json ./packages/github/

# Install all dependencies including devDependencies for building
RUN --mount=type=cache,target=/root/.bun/install/cache bun install
Expand All @@ -84,15 +100,12 @@ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
COPY packages/ ./packages/
COPY scripts/ ./scripts/

# Build core and github packages in all modes (required for worker imports)
# These must be built since worker imports from them via package.json main field
# Build core package in all modes (required for worker imports)
# This must be built since worker imports from it via package.json main field
RUN echo "Building @peerbot/core package..."; \
cd /app/packages/core && rm -f tsconfig.tsbuildinfo && bun run build && \
# Create symlink so built core can resolve its dependencies from root node_modules \
ln -sf /app/node_modules /app/packages/core/node_modules && \
echo "Building @peerbot/github package..."; \
cd /app/packages/github && rm -f tsconfig.tsbuildinfo && bun run build && \
ln -sf /app/node_modules /app/packages/github/node_modules
ln -sf /app/node_modules /app/packages/core/node_modules

# For production mode, also build worker during image creation
# For dev mode, worker runs from source but still needs core built
Expand All @@ -112,11 +125,9 @@ RUN mkdir -p /workspace && \
chmod 755 /workspace && \
# Create dist directories for TypeScript compilation
mkdir -p /app/packages/core/dist && \
mkdir -p /app/packages/github/dist && \
mkdir -p /app/packages/worker/dist && \
# Only chown specific directories that claude needs write access to
chown -R claude:claude /app/packages/core/dist && \
chown -R claude:claude /app/packages/github/dist && \
chown -R claude:claude /app/packages/worker/dist && \
chown -R claude:claude /app/packages/worker/scripts 2>/dev/null || true && \
chown -R claude:claude /app
Expand Down
16 changes: 7 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ help:
@echo " make clean-workers - Remove worker containers only"
@echo ""
@echo "Development:"
@echo " Use /process-management to start/stop sidecar processes (redis, packages, gateway)"
@echo " redis-server - Start Redis"
@echo " make watch-packages - Watch and rebuild packages"
@echo " cd packages/gateway && bun run dev - Run gateway with hot reload"

# Build all TypeScript packages in dependency order
build-packages:
@echo "📦 Building all TypeScript packages..."
@echo " 1️⃣ Building packages/core..."
@cd packages/core && bun run build
@echo " 2️⃣ Building packages/github..."
@cd packages/github && bun run build
@echo " 3️⃣ Building packages/gateway..."
@echo " 2️⃣ Building packages/gateway..."
@cd packages/gateway && bun run build
@echo " 4️⃣ Building packages/worker..."
@echo " 3️⃣ Building packages/worker..."
@cd packages/worker && bun run build
@echo "✅ All packages built successfully!"

Expand Down Expand Up @@ -152,9 +152,8 @@ logs:
echo "View logs with:"; \
echo " kubectl logs -f <pod-name> -n peerbot"; \
else \
echo "For development, use /process-management to view logs:"; \
echo " get_logs(\"gateway\")"; \
echo " get_logs(\"redis\")"; \
echo "For development, view logs in the terminal where gateway is running"; \
echo "Or use: docker compose logs -f gateway"; \
fi

# Stop worker containers
Expand All @@ -163,7 +162,6 @@ down:
@docker ps -q --filter "label=app.kubernetes.io/component=worker" | xargs -r docker stop 2>/dev/null || true
@docker ps -aq --filter "label=app.kubernetes.io/component=worker" | xargs -r docker rm 2>/dev/null || true
@echo "✅ Worker containers stopped"
@echo "Note: For sidecar processes (redis, gateway), use /process-management"

# Clean up everything including volumes
clean:
Expand Down
Loading
Loading