Commit 09436df
authored
infra(cpb): add database, env vars, and setup scripts for Connecting People Bot (#120)
## Summary
- Add CPB (Connecting People Bot) PostgreSQL database infrastructure:
dedicated `cpb_bot` database, `cpb_app` user, and all required
environment variables
- Add `scripts/cpb-setup-db.sh` — idempotent production database setup
script with SQL injection protection and error handling
- Append CPB user/database creation to `scripts/init-db.sh` for dev
environment auto-initialization
All changes are **additive** — no existing n8n or Temporal functionality
is modified.
## Files Changed
| File | Change |
|------|--------|
| `.env.example` | CPB variables for dev (with defaults) and prod (with
placeholders) sections |
| `docker-compose.yml` | CPB env vars for `postgresql` service (3 vars)
+ `n8n` service (14 vars) |
| `docker-compose.prod.yml` | CPB env vars for `n8n` service with
`:?required` / `:-default` syntax |
| `scripts/init-db.sh` | Append CPB user/database creation block (dev
auto-init) |
| `scripts/cpb-setup-db.sh` | **New file** — idempotent production
database setup |
## Security Hardening
The production setup script (`cpb-setup-db.sh`) includes:
- **SQL injection prevention**: `validate_pg_identifier()` function
validates username/database name against `^[a-zA-Z_][a-zA-Z0-9_]*$` with
63-char PostgreSQL limit
- **Error detection**: `-v ON_ERROR_STOP=1` ensures psql exits on SQL
errors (not silently ignored)
- **Pipeline safety**: `set -eo pipefail` catches both command and
pipeline failures
- **Password escaping**: Single quotes escaped for SQL string literal
safety
## Setup Guide for DevOps
### Development (fresh setup)
```bash
# 1. Copy env vars (CPB defaults are already included)
cp .env.example .env
# Edit .env — set CPB_SLACK_BOT_TOKEN to existing SLACK_TOKEN value for dev testing
# 2. Start services (fresh volume required for init-db.sh to run)
docker compose down -v
docker compose up -d
# 3. Verify CPB database exists
docker compose exec postgresql psql -U cpb_app -d cpb_bot -c "SELECT 1"
# 4. Verify CPB env vars are visible to n8n
docker compose exec n8n env | grep CPB_
```
### Development (existing volume)
If you already have a running PostgreSQL volume, `init-db.sh` won't
re-run. Create the CPB database manually:
```bash
docker compose exec postgresql psql -U postgres <<-EOSQL
CREATE USER cpb_app WITH ENCRYPTED PASSWORD 'cpb_password';
CREATE DATABASE cpb_bot OWNER cpb_app;
GRANT ALL PRIVILEGES ON DATABASE cpb_bot TO cpb_app;
EOSQL
# Restart n8n to pick up new env vars
docker compose restart n8n
docker compose exec n8n env | grep CPB_
```
### Production
```bash
# 1. Generate a secure password
openssl rand -base64 32
# 2. Run the idempotent setup script
POSTGRES_PASSWORD_CPB="<generated-password>" \
CPB_POSTGRES_HOST="<prod-postgresql-host>" \
./scripts/cpb-setup-db.sh
# 3. Update production .env with real values:
# - POSTGRES_PASSWORD_CPB (the generated password)
# - CPB_POSTGRES_HOST (already set via POSTGRES_HOST)
# - CPB_SLACK_BOT_TOKEN (from Al — CPB-specific xoxb- token)
# - CPB_CHANNEL_ID, CPB_REPORT_CHANNEL_ID (from Olivia/Al)
# - CPB_ADMIN_SLACK_ID, CPB_DEV_SLACK_ID (Slack user IDs)
# 4. Deploy
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 5. Verify
docker compose exec n8n env | grep CPB_
```
## Environment Variables Reference
| Variable | Dev Default | Prod | Description |
|----------|-------------|------|-------------|
| `POSTGRES_DB_CPB` | `cpb_bot` | `cpb_bot` | Database name |
| `POSTGRES_USER_CPB` | `cpb_app` | `cpb_app` | Database user |
| `POSTGRES_PASSWORD_CPB` | `cpb_password` | **generate** | Database
password |
| `CPB_SLACK_BOT_TOKEN` | `xoxb-placeholder` | **from Al** | Slack bot
token |
| `CPB_CHANNEL_ID` | `CXXXXXXXXX` | **from Olivia** | CPB community
channel |
| `CPB_REPORT_CHANNEL_ID` | `CXXXXXXXXX` | **from Al** | Admin report
channel |
| `CPB_ADMIN_SLACK_ID` | `UXXXXXXXXX` | **from Olivia** | Admin Slack
user ID |
| `CPB_DEV_SLACK_ID` | `UXXXXXXXXX` | **from Sashko** | Developer Slack
user ID |
| `CPB_PAIRING_LAMBDA` | `0.0578` | `0.0578` | Pairing decay rate |
| `CPB_PAIRING_ALPHA` | `0.3` | `0.3` | Repeat penalty |
| `CPB_PAIRING_TRIALS` | `50` | `50` | Matching trials |
| `CPB_PAIRING_MIN_WEIGHT` | `0.01` | `0.01` | Minimum weight floor |
## Rollback
All changes are additive. To rollback:
```bash
# Dev: remove volumes and revert
docker compose down -v
git revert <this-commit>
docker compose up -d
# Prod: drop CPB database/user
psql -h <prod-host> -U postgres -c "DROP DATABASE IF EXISTS cpb_bot; DROP ROLE IF EXISTS cpb_app;"
```
## Test Plan
- [ ] `docker compose config --quiet` passes (dev config valid)
- [ ] `docker compose -f docker-compose.yml -f docker-compose.prod.yml
config --quiet` validates (prod config — will error on missing required
vars, expected)
- [ ] `bash -n scripts/cpb-setup-db.sh` passes (no syntax errors)
- [ ] `grep -c "CPB" scripts/init-db.sh` returns 3 (user, database,
grant)
- [ ] `test -x scripts/cpb-setup-db.sh` confirms executable
- [ ] Fresh `docker compose up -d` (after `down -v`) creates `cpb_bot`
database
- [ ] `docker compose exec postgresql psql -U cpb_app -d cpb_bot -c
"SELECT 1"` returns 1
- [ ] `docker compose exec n8n env | grep CPB_` lists all 14 CPB
variables
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a Connecting People Bot: Slack-based pairing with persistent
storage and configurable pairing parameters (lambda, alpha, trials, min
weight).
* **Chores**
* Added Development/Production environment configuration and automated
DB provisioning/init support to simplify setup and deployment.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->1 parent 8c250de commit 09436df
5 files changed
Lines changed: 170 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
30 | 50 | | |
31 | 51 | | |
32 | 52 | | |
| |||
95 | 115 | | |
96 | 116 | | |
97 | 117 | | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
39 | 53 | | |
40 | 54 | | |
41 | 55 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
37 | 51 | | |
38 | 52 | | |
39 | 53 | | |
| |||
69 | 83 | | |
70 | 84 | | |
71 | 85 | | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
72 | 89 | | |
73 | 90 | | |
74 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
4 | 32 | | |
5 | | - | |
| 33 | + | |
6 | 34 | | |
7 | 35 | | |
8 | 36 | | |
9 | | - | |
| 37 | + | |
10 | 38 | | |
11 | 39 | | |
12 | 40 | | |
13 | 41 | | |
14 | 42 | | |
15 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
16 | 48 | | |
0 commit comments