# Gemmbot

Company-wide AI assistant for [Gemma Analytics](https://gemmaanalytics.com), powered by [Claude Code](https://docs.anthropic.com/en/docs/claude-code).

Gemmbot gives every team member their own AI coding assistant in Slack — each session runs in an isolated Docker container with per-user secrets, GitHub access, and Claude Code authentication. A management UI handles onboarding, secrets, and admin controls.

## How it works

```
User messages in Slack
        │
        ▼
   ┌─────────┐     ┌──────────────┐     ┌────────────────┐
   │ Slack Bot│────▶│ Docker       │────▶│ Claude Code CLI │
   │ (Node.js)│    │ Container    │     │ (per session)   │
   └─────────┘     └──────────────┘     └────────────────┘
        │                 │
        │                 ├── Workspace directory (isolated)
        │                 ├── User secrets (env vars)
        │                 ├── GitHub access (scoped)
        │                 └── Dev server hosting (*.gemmbot.gemmaanalytics.com)
        │
   ┌─────────┐
   │ Mgmt UI │  ← Google OAuth login, secrets, Claude Code auth
   │ (Next.js)│
   └─────────┘
```

## Repository structure

```
gemmbot/
├── bot/                  # Slack bot — Node.js + Slack Bolt (Socket Mode)
│   ├── src/              # 17 modules: app, session, docker, claude, commands, ...
│   ├── templates/        # System prompt templates (base + feature-specific)
│   └── bin/              # Helper scripts (vpn-exec, request-secret)
├── ui/                   # Management UI — Next.js 15 + NextAuth + Prisma
│   ├── src/app/          # Pages: login, settings, secrets, sessions, admin
│   ├── src/lib/          # Auth config, Prisma client, crypto
│   └── prisma/           # Schema + migrations (SQLite)
├── docker/               # Session container image (Ubuntu 22.04 + Claude Code CLI)
├── bin/                   # Shared CLI tools (vpn-exec, vpn-ns, request-secret)
└── .github/              # CI/CD workflows + deploy scripts
```

## Features

- **Slack integration** — DM or @mention in channels. One user per thread, explicit release.
- **Docker isolation** — Each session runs in its own container with resource limits (4 GB memory, 2 CPU cores).
- **Per-user Claude Code auth** — OAuth PKCE flow via management UI. Each user's token is encrypted at rest.
- **Encrypted secrets** — Users manage secrets (API keys, DB credentials) in the UI. Injected as env vars into containers. AES-256-GCM encryption shared between bot and UI.
- **Dev server hosting** — Claude can spin up dev servers, accessible via `*.gemmbot.gemmaanalytics.com` with automatic SSL (Caddy reverse proxy).
- **GitHub App integration** — Scoped repo access for the whole team, ephemeral access to external repos.
- **Business-hours cleanup** — Idle sessions are cleaned up after 24 business hours (Mon–Fri 9–17 Berlin time) with warnings at 12h and 4h before teardown.
- **Feature flags** — Email, VPN, page serving, secret dropoff, self-improvement — configurable per deployment.
- **Model/provider switching** — Per-session model and provider selection (Anthropic API, AWS Bedrock).
- **Daily server audit** — Automated 8 AM Berlin time audit of running dev servers with Slack notifications.
- **Management UI** — Google OAuth (restricted to `@gemmaanalytics.com`), admin panel for user/role management, session overview, kill switch.

## Deployment modes

The same codebase supports two modes:

- **Multi-user** (`AUTH_MODE=multi_user`) — Shared Slack bot for the whole team. Users onboard via the management UI. Thread ownership prevents conflicts.
- **Single-user** (`AUTH_MODE=single_user`) — Personal bot for one person. No UI needed, no thread ownership logic.

## Prerequisites

1. **EC2 instance** — Ubuntu (the [bootstrap script](deploy/bootstrap.sh) installs everything else).
2. **DNS** — `gemmbot.gemmaanalytics.com` + `*.gemmbot.gemmaanalytics.com` → EC2 public IP.
3. **Slack App** — Socket Mode, Event Subscriptions (`message.im`, `app_mention`, `message.channels`), Bot Token Scopes (`chat:write`, `files:read`, `files:write`, `reactions:write`, `users:read`, `users:read.email`). Tokens are configured via the management UI after deployment.
4. **Google Cloud OAuth** — Authorized redirect URI: `https://gemmbot.gemmaanalytics.com/api/auth/callback/google`.
5. **GitHub App** — (multi-user) With `contents` and `pull_requests` permissions for Gemma-Analytics org.

## Server setup

Three steps: bootstrap the EC2 instance, add GitHub Secrets, deploy.

**1. Bootstrap** (one-time — installs Docker, Node.js 22, Caddy, AWS CLI, creates users/dirs):

```bash
git clone https://github.com/Gemma-Analytics/gemmbot.git /tmp/gemmbot
sudo bash /tmp/gemmbot/deploy/bootstrap.sh
```

**2. Add GitHub Secrets** to the repo (see full list below).

**3. Deploy** — merge the PR or trigger workflows via `workflow_dispatch`. CI/CD writes all config files (`.env`, Caddyfile, GitHub App key) onto the server and starts everything.

No manual file editing on the server. All configuration is driven by GitHub Secrets.

See [`deploy/`](deploy/) for the systemd unit files, Caddyfile, and bootstrap script.

## CI/CD

Automated via GitHub Actions. All deploys go to the EC2 instance via SSH + rsync.

| Workflow | Trigger | What it does |
|---|---|---|
| `deploy-bot.yml` | Push to `main` (bot/, docker/, bin/) | Write bot .env, build session image → ECR, rsync bot to EC2, restart |
| `deploy-ui.yml` | Push to `main` (ui/) | Write UI .env, build Next.js, rsync to EC2, run migrations, restart |
| `deploy-infra.yml` | Push to `main` (deploy/) | Deploy Caddyfile, systemd units, reload Caddy |
| `preview-deploy.yml` | `/preview` comment or `preview` label | Build UI from PR branch, deploy to `preview.gemmbot.gemmaanalytics.com` |
| `preview-cleanup.yml` | PR close/merge or `/preview-down` | Tear down the preview environment |
| `preview-stale.yml` | Daily 3 AM UTC | Clean up previews older than 48h |
| `pr-welcome.yml` | PR opened | Post available commands (`/preview`, `/preview-down`) |

Only one preview environment can exist at a time. Deploying a new preview replaces any existing one.

All `.env` files and config are written by CI/CD from GitHub Secrets — no manual server configuration needed.

## GitHub Secrets required

All secrets go in the repo: Settings → Secrets and variables → Actions.

**EC2 / SSH (used by all workflows):**

| Secret | Description |
|---|---|
| `EC2_HOST` | EC2 instance hostname or IP |
| `EC2_USER` | SSH user on EC2 (e.g. `gemmbot`) |
| `EC2_SSH_KEY` | Private SSH key for deployment (PEM format) |

**Encryption (shared between bot and UI — must be the same value):**

| Secret | Description |
|---|---|
| `ENCRYPTION_KEY` | AES-256 key for encrypting secrets at rest. Generate: `openssl rand -hex 32` |

**Google OAuth (UI .env):**

| Secret | Description |
|---|---|
| `GOOGLE_CLIENT_ID` | OAuth 2.0 Client ID from Google Cloud Console |
| `GOOGLE_CLIENT_SECRET` | OAuth 2.0 Client Secret |

**NextAuth (UI .env):**

| Secret | Description |
|---|---|
| `NEXTAUTH_SECRET` | Random string for session encryption. Generate: `openssl rand -hex 32` |

**GitHub App (bot deploy — written as a file on EC2):**

| Secret | Description |
|---|---|
| `GH_APP_PRIVATE_KEY` | Full PEM contents of the GitHub App private key |

Total: **8 secrets**. Docker images are pushed to GitHub Container Registry (ghcr.io) using the built-in `GITHUB_TOKEN` — no AWS credentials needed.

Slack tokens (`SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN`) are configured via the management UI's Setup page (Admin → Setup), not as GitHub Secrets. TLS certificates are provisioned automatically by Caddy using on-demand TLS (no Cloudflare needed).

## User onboarding (multi-user mode)

1. Visit `gemmbot.gemmaanalytics.com` and sign in with your Gemma Google account
2. Click "Connect Claude Code" to authenticate with Anthropic
3. (Optional) Add personal secrets (API keys, DB credentials) in the Secrets page
4. DM or @mention Gemmbot in Slack — your Slack account is automatically linked by email

## Commands

Users can type these in any active session thread:

| Command | Description |
|---|---|
| `stop` | End the current session and destroy the container |
| `release` | Release thread ownership so another user can claim it |
| `persist` | Keep the session alive beyond the idle timeout |
| `status` | Show session info (model, provider, uptime, cost) |
| `model <name>` | Switch Claude model for this session |
| `provider <name>` | Switch provider (anthropic / bedrock) |
| `cost` | Show accumulated token usage and estimated cost |
| `workspaces` | List active workspace directories |

## Local development

```bash
# Bot (starts without Slack — configure tokens via UI Setup page)
cd bot && npm install && npm run dev

# UI (requires .env with Google OAuth + NextAuth config)
cd ui && npm install && npx prisma migrate dev && npm run dev
```

Both bot and UI share the same SQLite database (WAL mode for concurrent access).

## Security

- All secrets encrypted at rest (AES-256-GCM) with a shared encryption key
- Docker containers run as non-root (`claude-session` user)
- No shell interpretation in container commands (`execFileSync` everywhere)
- Management UI restricted to `@gemmaanalytics.com` Google accounts
- Admin API on localhost only (not exposed externally)
- Deactivated users immediately blocked from sign-in and have sessions invalidated
- Input validation on all API endpoints (Slack ID format, key format, length limits)
- Reserved internal keys protected from user override

## Architecture decisions

See [PLAN.md](PLAN.md) for the full implementation plan, database schema, feature flag system, deployment guide, and migration checklist.
