This guide is for self-hosting reviewGOOSE:Slack. If you're using the SaaS version, see SETUP.md instead.
reviewGOOSE:Slack consists of two services:
- slacker - Main bot server that handles GitHub webhooks and Slack notifications
- slacker-registrar - OAuth-only service for multi-workspace installations
Single-Workspace (Self-Hosting for Your Organization):
- Use the OAuth handlers built into the main server
- Deploy only
slacker, no need forslacker-registrar - Simpler setup with fewer moving parts
- OAuth credentials configured directly in main server
Multi-Workspace (SaaS / Slack Marketplace Distribution):
- Use the separate
slacker-registrarservice - Required for supporting multiple Slack workspaces
- Each workspace gets isolated token storage
- Registrar handles OAuth for all workspaces, main server handles events
Important: The main server OAuth is designed ONLY for single-workspace installations. For multi-workspace support, you MUST use the registrar.
- Google Cloud Platform project with Cloud Run enabled
- Slack App configured at https://api.slack.com/apps
- GitHub App configured with webhook access
- ko CLI tool for building and deploying Go apps (install)
- gcloud CLI configured for your GCP project
- Go to https://api.slack.com/apps
- Click "Create New App"
- Choose "From scratch"
- Name your app (e.g., "reviewGOOSE")
- Select your development workspace
-
In your app settings, go to "OAuth & Permissions"
-
Under "Scopes", add these Bot Token Scopes:
channels:history- View messages in public channelschannels:read- View basic channel informationchat:write- Send messages as the botchat:write.public- Send messages to channels bot isn't incommands- Add slash commandsgroups:history- View messages in private channels (if invited)groups:read- View basic private channel informationim:write- Send direct messagesreactions:write- Add emoji reactionsteam:read- View workspace name and domainusers:read- View people in the workspaceusers:read.email- View email addresses
-
Under "Redirect URLs", add your OAuth callback URL:
https://your-registrar-url.run.app/oauth/callback
You'll need two values from the "App Credentials" section:
- Go to "Basic Information" in your app settings
- Scroll to "App Credentials"
- Copy these values:
- Client ID (e.g.,
9426269265270.9443955134789) - Client Secret (click "Show" to reveal)
- Client ID (e.g.,
Important: Keep your Client Secret secure - never commit it to Git or expose it publicly.
- Go to "Event Subscriptions"
- Enable events
- Set Request URL to:
https://your-server-url.run.app/slack/events - Subscribe to bot events:
app_home_opened- Update user's home tabmessage.im- Receive direct messages
- Save changes
- Go to "App Home"
- Check "Home Tab" to enable the dashboard
- Uncheck "Messages Tab" if you don't need it
For self-hosting in your own workspace, configure only the main server:
# GitHub App credentials
GITHUB_APP_ID=123456
GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----..."
# Slack webhook verification (from "App Credentials" → "Signing Secret")
SLACK_SIGNING_SECRET=abc123...
# Sprinkler WebSocket endpoint (for GitHub events)
SPRINKLER_URL=wss://sprinkler.example.com/ws
# OAuth credentials (for single-workspace OAuth)
SLACK_CLIENT_ID=9426269265270.9443955134789
SLACK_CLIENT_SECRET=abc123...
# Optional: Cloud Datastore for cross-instance state coordination
# If unset, uses JSON files only (sufficient for single-instance deployments)
DATASTORE=slacker # Database ID to use (e.g., "slacker", "(default)")
# GCP_PROJECT=my-project # Optional: Auto-detected on Cloud RunFor SaaS/Marketplace distribution, configure both services:
# GitHub App credentials
GITHUB_APP_ID=123456
GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----..."
# Slack webhook verification (from "App Credentials" → "Signing Secret")
SLACK_SIGNING_SECRET=abc123...
# Sprinkler WebSocket endpoint (for GitHub events)
SPRINKLER_URL=wss://sprinkler.example.com/ws
# Datastore for cross-instance coordination (RECOMMENDED for multi-instance)
DATASTORE=slacker # Database ID to use
# GCP_PROJECT=my-project # Optional: Auto-detected on Cloud Run
# NO OAUTH CREDENTIALS - registrar handles OAuth for multi-workspace# OAuth credentials ONLY (from "App Credentials" in Slack)
SLACK_CLIENT_ID=9426269265270.9443955134789
SLACK_CLIENT_SECRET=abc123...Note: The registrar does NOT need SLACK_SIGNING_SECRET because it only handles OAuth flows, not webhook events.
Both services can fetch secrets from Google Secret Manager instead of environment variables.
# Store Client ID
echo -n "9426269265270.9443955134789" | \
gcloud secrets create SLACK_CLIENT_ID --data-file=-
# Store Client Secret (replace with your actual secret)
echo -n "your-client-secret-here" | \
gcloud secrets create SLACK_CLIENT_SECRET --data-file=-PROJECT=your-gcp-project
# Grant access to main server
gcloud secrets add-iam-policy-binding SLACK_CLIENT_ID \
--member="serviceAccount:slacker@${PROJECT}.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding SLACK_CLIENT_SECRET \
--member="serviceAccount:slacker@${PROJECT}.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
# Grant access to registrar
gcloud secrets add-iam-policy-binding SLACK_CLIENT_ID \
--member="serviceAccount:slacker-registrar@${PROJECT}.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding SLACK_CLIENT_SECRET \
--member="serviceAccount:slacker-registrar@${PROJECT}.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"Slacker uses a hybrid storage approach:
- JSON files (default): Persistent state stored in local filesystem, survives restarts
- Cloud Datastore (optional): Adds cross-instance coordination for rolling deployments
Use JSON-only (default):
- Single-instance deployments
- Low traffic workspaces
- Development/testing
Add Datastore:
- Multi-instance Cloud Run deployments (autoscaling > 1)
- Rolling deployments with zero downtime
- High availability requirements
-
Create a Datastore database:
# Create database in Datastore mode (not Firestore Native mode) gcloud firestore databases create \ --database=slacker \ --location=us-central \ --type=datastore-mode -
Grant IAM permissions to service account:
PROJECT=your-gcp-project # Grant Datastore User role (read/write access) gcloud projects add-iam-policy-binding ${PROJECT} \ --member="serviceAccount:slacker@${PROJECT}.iam.gserviceaccount.com" \ --role="roles/datastore.user"
-
Set environment variable:
# In Cloud Run deployment or .env file DATASTORE=slacker # Use the database ID you created # GCP_PROJECT is auto-detected on Cloud Run
The hybrid system works automatically:
- Writes: Saved to both JSON and Datastore
- Reads: Try Datastore first, fall back to JSON if unavailable
- Startup: If Datastore fails to connect, gracefully degrades to JSON-only mode
- No data loss: JSON fallback ensures reliability even if Datastore is down
After deployment, check logs to confirm Datastore is active:
gcloud logging read \
"resource.type=cloud_run_revision AND resource.labels.service_name=slacker" \
--limit 10 | grep -i datastoreYou should see:
initializing Cloud Datastore for persistent state, project_id=..., database=slacker
If Datastore fails, you'll see:
using JSON files for state storage, reason=DATASTORE not set
For hosting the bot in your own Slack workspace:
# Deploy only the main server
make deploy-serverThis will:
- Build the Go binary with
ko - Create a container image
- Deploy to Cloud Run as
slacker - Create service account
slacker@PROJECT.iam.gserviceaccount.com
Configure OAuth in Slack App:
- Set redirect URL to:
https://slacker-HASH-uc.a.run.app/slack/oauth/callback
No registrar needed - the main server handles OAuth for single-workspace installs.
For supporting multiple Slack workspaces:
# Deploy both services (server + registrar)
make deployThis will deploy:
- Main server (
slacker) - handles GitHub webhooks and Slack events - Registrar (
slacker-registrar) - handles OAuth for all workspaces
Configure OAuth in Slack App:
- Set redirect URL to:
https://slacker-registrar-HASH-uc.a.run.app/oauth/callback
Important: For multi-workspace, all OAuth traffic goes through the registrar, not the main server.
If you prefer manual control:
# Set your GCP project
export GCP_PROJECT=your-project-id
# Single-workspace: Deploy main server only
APP_NAME=slacker ./hacks/deploy.sh cmd/server
# Multi-workspace: Deploy both
APP_NAME=slacker ./hacks/deploy.sh cmd/server
APP_NAME=slacker-registrar ./hacks/deploy.sh cmd/registrarAfter deployment, update your Slack app configuration based on your deployment type:
-
Go to https://api.slack.com/apps → Your App
-
Under "OAuth & Permissions", set Redirect URLs to:
https://slacker-HASH-uc.a.run.app/slack/oauth/callback -
Under "Event Subscriptions", set Request URL to:
https://slacker-HASH-uc.a.run.app/slack/events -
Under "Interactivity & Shortcuts", set Request URL to:
https://slacker-HASH-uc.a.run.app/slack/interactive
All URLs point to the main server.
-
Go to https://api.slack.com/apps → Your App
-
Under "OAuth & Permissions", set Redirect URLs to:
https://slacker-registrar-HASH-uc.a.run.app/oauth/callbackNote: OAuth goes to the registrar, not the main server!
-
Under "Event Subscriptions", set Request URL to:
https://slacker-HASH-uc.a.run.app/slack/events -
Under "Interactivity & Shortcuts", set Request URL to:
https://slacker-HASH-uc.a.run.app/slack/interactive
OAuth uses the registrar URL; events and interactivity use the main server URL.
- Visit your main server install URL:
https://slacker-HASH-uc.a.run.app/slack/install - Click "Add to Slack"
- Authorize the app for your workspace
- Verify you're redirected to success page
- Check logs to confirm token was stored:
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=slacker" --limit 50
- Visit your registrar URL:
https://slacker-registrar-HASH-uc.a.run.app/install - Click "Add to Slack"
- Authorize the app (can be done from any workspace)
- Verify you're redirected to success page
- Check registrar logs to confirm token was stored in GSM:
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=slacker-registrar" --limit 50
Check that the workspace token was stored:
# List all slack-token-* secrets
gcloud secrets list --filter="name:slack-token-"You should see a secret named slack-token-{TEAM_ID} for each installed workspace.
Multi-workspace support is only available when using the registrar service (not the main server OAuth).
- Deploy the
slacker-registrarservice - Each workspace that installs the app gets a unique token
- Tokens are stored in GSM with key pattern:
slack-token-{TEAM_ID} - Workspace metadata stored as:
slack-metadata-{TEAM_ID} - Main server fetches the correct token based on incoming webhook's team ID
The multi-workspace architecture works automatically:
- Registrar handles OAuth for unlimited workspaces
- Main server dynamically loads the correct workspace token
- No hardcoded workspace IDs or manual configuration
- Perfect for Slack Marketplace distribution
If using the main server's built-in OAuth (without registrar), you can only support one workspace. The OAuth callback in cmd/server/main.go stores tokens in a way that doesn't scale to multiple workspaces.
Both services expose health endpoints:
# Main server
curl https://slacker-HASH-uc.a.run.app/health
curl https://slacker-HASH-uc.a.run.app/healthz
# Registrar
curl https://slacker-registrar-HASH-uc.a.run.app/health# Main server logs
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=slacker" --limit 50
# Registrar logs
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=slacker-registrar" --limit 50403 Forbidden during OAuth:
- Check that SLACK_CLIENT_ID and SLACK_CLIENT_SECRET are set correctly
- Verify service account has
secretAccessorrole for both secrets - Check Cloud Run logs for "failed to fetch secret from GSM" warnings
Tokens not saving:
- Ensure service account has permission to create new secrets
- Grant
secretmanager.secretCreatorrole:gcloud projects add-iam-policy-binding ${PROJECT} \ --member="serviceAccount:slacker-registrar@${PROJECT}.iam.gserviceaccount.com" \ --role="roles/secretmanager.secretCreator"
Rate limiting errors:
- OAuth endpoints have rate limit: 10 req/s, burst 20
- This is intentional to prevent abuse
- Normal installations won't hit this limit
- Never commit secrets to Git - Use GSM or environment variables
- Rotate secrets periodically - Update Client Secret in Slack app settings
- Limit service account permissions - Only grant necessary GSM access
- Use separate service accounts - Don't share between server and registrar
- Enable Cloud Run authentication if running private registrar
- Use HTTPS only - Cloud Run enforces this by default
- Validate webhook signatures - Main server verifies all Slack requests
- Rate limit OAuth endpoints - Registrar has built-in rate limiting
Set up alerts for:
- 4xx/5xx error rates
- Failed secret access attempts
- Unusual OAuth traffic patterns
- Health check failures
Problem: Slack redirects to callback URL but gets 404
Solution:
- Verify registrar is deployed and running
- Check the route is
/oauth/callback(no/slack/prefix) - Ensure the URL in Slack app settings matches exactly
Problem: Registrar fails to start
Solution:
- Set SLACK_CLIENT_ID as environment variable in Cloud Run, OR
- Store in GSM and grant service account access
- Check logs to see if GSM fetch is failing
Problem: Only one workspace token is saved
Solution:
- This shouldn't happen - each workspace gets unique token
- Check GSM for multiple
slack-token-*secrets - Verify
StoreWorkspaceis using team ID in key name
Run the registrar locally:
# Set OAuth credentials
export SLACK_CLIENT_ID=your-client-id
export SLACK_CLIENT_SECRET=your-client-secret
# Build and run
make build-registrar
./bin/slack-registrarAccess at http://localhost:9120/install
Note: Slack OAuth callback requires HTTPS, so you'll need ngrok or similar for local testing:
ngrok http 9120
# Update Slack app redirect URL to: https://YOUR-NGROK-URL.ngrok.io/oauth/callback# Build all binaries
make build
# Build server only
make build-server
# Build registrar only
make build-registrar# Run all tests
make test
# Run with coverage
go test -v -race -cover ./...make build- Build both binariesmake build-server- Build main servermake build-registrar- Build registrarmake test- Run tests with race detectionmake lint- Run all lintersmake fmt- Format codemake deploy- Deploy both server and registrar (multi-workspace)make deploy-server- Deploy main server only (single-workspace)make deploy-registrar- Deploy registrar onlymake clean- Remove build artifacts
| Service | Binary | Purpose | Port |
|---|---|---|---|
slacker |
cmd/server | Main bot server | 9090 |
slacker-registrar |
cmd/registrar | OAuth handler | 9120 |
Both services check for secrets in this order:
- Environment variable (e.g.,
SLACK_CLIENT_ID) - Google Secret Manager (e.g., secret named
SLACK_CLIENT_ID) - Fail if not found
This allows flexibility in how you provide credentials.
- Documentation: github.com/codeGROOVE-dev/slacker
- Issues: GitHub Issues
- Architecture: See ARCHITECTURE.md
- User Setup: See SETUP.md