github-ops-app
Bot that automates GitHub operations with Okta integration and Slack
notifications. Deploy as AWS Lambda, standard HTTP server, or container.
Features
- 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
Quick Start
Prerequisites
Deployment Options
The bot can be deployed in multiple ways:
Option 1: Standard HTTP Server
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.
Option 2: AWS Lambda
Deploy as serverless function with automatic scaling:
# build for Lambda
make build-lambda # creates dist/bootstrap
See cmd/lambda/README.md for complete Lambda deployment
instructions including API Gateway and EventBridge configuration.
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-secret
Requirements for SSM parameters:
- Valid AWS credentials with
ssm:GetParameter permission
- Full SSM parameter ARN in format:
arn:aws:ssm:REGION:ACCOUNT:parameter/path/to/param
- SecureString parameters are automatically decrypted
Required: GitHub
| 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 |
Optional: Okta Sync
| 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 |
Optional: PR Compliance
| Variable |
Description |
APP_PR_COMPLIANCE_ENABLED |
Enable monitoring (true) |
APP_PR_MONITORED_BRANCHES |
Branches to monitor (e.g., main,master) |
Optional: Slack
| Variable |
Description |
APP_SLACK_TOKEN |
Bot token (xoxb-...) |
APP_SLACK_CHANNEL |
Default channel ID |
Other
| Variable |
Description |
APP_DEBUG_ENABLED |
Verbose logging (default: false) |
APP_BASE_PATH |
URL prefix to strip (e.g., /api/v1) |
Okta Sync Rules
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 identifier
enabled - Enable/disable rule
okta_group_pattern - Regex to match Okta groups
okta_group_name - Exact Okta group name (alternative to pattern)
github_team_prefix - Prefix for GitHub team names
github_team_name - Exact GitHub team name (overrides pattern)
strip_prefix - Remove this prefix from Okta group name
sync_members - Sync members between Okta and GitHub
create_team_if_missing - Auto-create GitHub teams
team_privacy - secret or closed
Sync Safety Features:
- Active users only: Only syncs users with
ACTIVE status 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.
Integration Setup
Detailed setup guides for each integration:
Development
# 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 TestGroupSync
Docker Deployment
FROM 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"]
How It Works
┌─────────────────────────────────────────────────┐
│ 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
Okta Sync Flow
- 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
PR Compliance Flow
- 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
Troubleshooting
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_SECRET matches
- No Slack notifications: Verify token has
chat:write and bot is in channel
License
MIT