Bot that automates GitHub operations with Okta integration and Slack notifications. Deploy as AWS Lambda, standard HTTP server, or container.
- Okta group sync - Automatically sync Okta groups to GitHub teams
- Orphaned user detection - Identify org members not in any synced teams
- PR compliance monitoring - Detect and notify when PRs bypass branch protection
- Automatic reconciliation - Detects external team changes and triggers sync
- Flexible configuration - Enable only what you need via environment variables
- Slack notifications - Rich messages for violations and sync reports
- Go ≥ 1.24
- GitHub App (setup guide)
- Optional: Okta API Service app (setup guide)
- Optional: Slack app (setup guide)
The bot can be deployed in multiple ways:
Run as a long-lived HTTP server on any VPS, VM, or container platform:
# build
make build-server
# run (or use systemd, Docker, Kubernetes, etc.)
./dist/server
# server listens on PORT (default: 8080)
# endpoints:
# POST /webhooks - GitHub webhook receiver
# POST /scheduled/okta-sync - Trigger Okta sync (call via cron)
# GET /server/status - Health check
# GET /server/config - Config (secrets redacted)Scheduling Okta Sync: Use any cron service or scheduler to POST to
/scheduled/okta-sync periodically. No EventBridge required.
Deploy as serverless function with automatic scaling:
# build for Lambda
make build-lambda # creates dist/bootstrapSee cmd/lambda/README.md for complete Lambda deployment instructions including API Gateway and EventBridge configuration.
All configuration values support direct values or AWS SSM parameter references. For sensitive values like secrets and private keys, use SSM parameters with automatic decryption:
# Direct value
APP_GITHUB_WEBHOOK_SECRET=my-secret
# SSM parameter (automatically decrypted if SecureString)
APP_GITHUB_WEBHOOK_SECRET=arn:aws:ssm:us-east-1:123456789012:parameter/github-bot/webhook-secretRequirements for SSM parameters:
- Valid AWS credentials with
ssm:GetParameterpermission - Full SSM parameter ARN in format:
arn:aws:ssm:REGION:ACCOUNT:parameter/path/to/param - SecureString parameters are automatically decrypted
| Variable | Description |
|---|---|
APP_GITHUB_APP_ID |
GitHub App ID |
APP_GITHUB_APP_PRIVATE_KEY |
Private key (PEM) |
APP_GITHUB_APP_PRIVATE_KEY_PATH |
Path to private key file |
APP_GITHUB_INSTALLATION_ID |
Installation ID |
APP_GITHUB_ORG |
Organization name |
APP_GITHUB_WEBHOOK_SECRET |
Webhook signature secret |
| Variable | Description |
|---|---|
APP_OKTA_DOMAIN |
Okta domain |
APP_OKTA_CLIENT_ID |
OAuth 2.0 client ID |
APP_OKTA_PRIVATE_KEY |
Private key (PEM) or use |
APP_OKTA_PRIVATE_KEY_PATH |
Path to private key file |
APP_OKTA_GITHUB_USER_FIELD |
User profile field for username |
APP_OKTA_SYNC_RULES |
JSON array (see examples) |
APP_OKTA_SYNC_SAFETY_THRESHOLD |
Max removal ratio (default: 0.5 = 50%) |
APP_OKTA_ORPHANED_USER_NOTIFICATIONS |
Notify about orphaned users |
| Variable | Description |
|---|---|
APP_PR_COMPLIANCE_ENABLED |
Enable monitoring (true) |
APP_PR_MONITORED_BRANCHES |
Branches to monitor (e.g., main,master) |
| Variable | Description |
|---|---|
APP_SLACK_TOKEN |
Bot token (xoxb-...) |
APP_SLACK_CHANNEL |
Default channel ID |
| Variable | Description |
|---|---|
APP_DEBUG_ENABLED |
Verbose logging (default: false) |
Map Okta groups to GitHub teams using JSON rules:
[
{
"name": "sync-engineering-teams",
"enabled": true,
"okta_group_pattern": "^github-eng-.*",
"github_team_prefix": "eng-",
"strip_prefix": "github-eng-",
"sync_members": true,
"create_team_if_missing": true
},
{
"name": "sync-platform-team",
"enabled": true,
"okta_group_name": "platform-team",
"github_team_name": "platform",
"sync_members": true,
"team_privacy": "closed"
}
]Rule Fields:
name- Rule identifierenabled- Enable/disable ruleokta_group_pattern- Regex to match Okta groupsokta_group_name- Exact Okta group name (alternative to pattern)github_team_prefix- Prefix for GitHub team namesgithub_team_name- Exact GitHub team name (overrides pattern)strip_prefix- Remove this prefix from Okta group namesync_members- Sync members between Okta and GitHubcreate_team_if_missing- Auto-create GitHub teamsteam_privacy-secretorclosed
Sync Safety Features:
- Active users only: Only syncs users with
ACTIVEstatus in Okta, automatically excluding suspended or deprovisioned accounts - External collaborator protection: Never removes outside collaborators (non-org members), preserving contractors and partner access
- Outage protection: Safety threshold (default 50%) prevents mass removal if Okta/GitHub is experiencing issues. Sync aborts if removal ratio exceeds threshold
- Orphaned user detection: Identifies organization members not in any Okta-synced teams and sends Slack notifications. Enabled by default when sync is enabled.
Detailed setup guides for each integration:
- GitHub App Setup - Create and install the GitHub App with required permissions
- Okta Setup - Configure API Services app for group sync
- Slack Setup - Create Slack app for notifications
# run server locally
make server
# run all tests
make test
# integration tests (offline, uses mock servers)
make test-verify
# specific package
go test -race -count=1 ./internal/github
# specific test
go test -race -count=1 ./internal/okta -run TestGroupSyncFROM golang:1.24-alpine AS builder
WORKDIR /app
COPY . .
RUN apk add --no-cache make && make build-server
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/dist/server /server
EXPOSE 8080
CMD ["/server"] ┌─────────────────────────────────────────────────┐
│ github-ops-app │
│ │
┌──────────────┐ │ ┌───────────────────────────────────────────┐ │
│ GitHub │ webhooks │ │ Webhook Handler │ │
│ │───────────────▶ • PR merge events │ │
│ • PR merge │ │ │ • Team membership changes │ │
│ • Team edit │ │ │ • Signature verification │ │
└──────────────┘ │ └─────────────┬─────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ PR Compliance Check │ │
┌──────────────┐ │ │ • Branch protection verification │────────┐
│ Okta │ │ │ • Required checks validation │ │ │
│ │ │ │ • Bypass detection │ │ │
│ • Groups │◀──────────────┴─────────────────────────────────────────┘ │ │
│ • Users │ │ │ │
└──────────────┘ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ Okta Sync Engine │ │ │
│ │ │ • Match groups via rules │ │ │
└─────────────────────────▶ • Create/update GitHub teams │ │ │
│ │ • Sync team membership │ │ │
│ │ • Orphaned user detection │────────┤
│ │ • Safety threshold protection │ │ │
┌──────────────┐ │ └─────────────────────────────────────────┘ │ │
│ GitHub │ │ │ │ │
│ Teams API │◀─────────────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ • Teams │ └─────────────────────────────────────────────────┘ │
│ • Members │ │
└──────────────┘ │
┌──────────────┐ │
│ Slack │◀─────────────────────────────────────┘
│ │ Notifications
│ • Alerts │ • PR violations
│ • Reports │ • Sync reports
└──────────────┘ • Orphaned users
- Trigger: Scheduled cron/EventBridge or team membership webhook
- Fetch: Query Okta groups matching configured rules
- Match: Apply sync rules to map Okta groups → GitHub teams
- Sync: Add/remove GitHub team members (ACTIVE Okta users only)
- Safety: Abort if removal ratio exceeds threshold (default 50%)
- Report: Send Slack notification with changes and orphaned users
- Receive: GitHub webhook on PR merge to monitored branch
- Verify: Validate webhook signature (HMAC-SHA256)
- Check: Query branch protection rules and required status checks
- Detect: Identify bypasses (admin override, missing reviews, failed checks)
- Notify: Send Slack alert with violation details
Common issues:
- Unauthorized from GitHub: Check app installation and permissions
- Group not found from Okta: Verify domain and scopes
- Webhook signature fails: Verify
APP_GITHUB_WEBHOOK_SECRETmatches - No Slack notifications: Verify token has
chat:writeand bot is in channel
MIT