Slack-native GitOps for GitHub, Cloudflare, and Doppler.
conCIerge turns structured Slack requests into reviewed Terraform pull requests. It reads live Terraform locals plus a YAML schema from the external jae-labs/terraform repo, drives a dynamic Slack modal flow, edits HCL, opens a PR, and posts the request summary back to #concierge. It never mutates infra directly; the apply boundary stays in the Terraform repo's normal review + CI/CD pipeline.
- Schema-driven flow: categories, resources, actions, modal fields all live in
concierge-schema.yaml. - Generic HCL engine: one editor handles
map_entry,singleton, andmembershipresources. - Nonce-keyed thread state rejects stale modal submissions.
- GitHub App auth, no long-lived personal tokens.
slog+ OpenTelemetry + Sentry; logs/traces flow through Grafana Alloy in production.
Prerequisites: Go 1.25+, Slack app credentials, GitHub App credentials with access to the Terraform repo. Doppler is optional for secret injection.
Required environment:
| Var | Purpose |
|---|---|
SLACK_BOT_TOKEN |
Bot OAuth token |
SLACK_REQUESTS_CHANNEL_ID |
Where request summaries are posted |
SLACK_APP_TOKEN or SLACK_SIGNING_SECRET |
Socket Mode app token / HTTP signing secret |
GITHUB_APP_ID, GITHUB_APP_INSTALLATION_ID, GITHUB_APP_PRIVATE_KEY |
GitHub App credentials |
GITHUB_OWNER, GITHUB_REPO |
Target Terraform repo (not this repo) |
Optional:
| Var | Default |
|---|---|
SLACK_MODE |
socket (also http) |
SLACK_USER_IDS |
comma-separated allow-list |
GITHUB_COMMIT_AUTHOR_NAME |
conCierge Bot |
GITHUB_COMMIT_AUTHOR_EMAIL |
239121271+luiz1361@users.noreply.github.com |
Local:
doppler run -- go run ./cmd/concierge # or: go run ./cmd/concierge
air # live reloadProduction runs the same binary with SLACK_MODE=http behind nginx and exposes GET /healthz.
| Domain | Resource | Actions |
|---|---|---|
| GitHub | Repository | Add, Remove, Update |
| GitHub | Org Settings | Update |
| GitHub | Team Membership | Add, Remove, Change Role |
| Cloudflare | DNS Records | Add, Remove, Update |
| Doppler | Projects | Add, Remove, Update |
The exact list is whatever lives in concierge-schema.yaml at runtime, not in code.
App code emits structured logs via slog and traces/metrics via OpenTelemetry. In production, Grafana Alloy tails the Nomad allocation log files into Loki, collects traces and can fan them out to Tempo and optionally Sentry, and metrics are exposed via Prometheus scrape + optional OTLP export. Exceptions and errors are sent to Sentry via Alloy logs forwarding rather than direct Sentry SDK exceptions capture.
Relevant env vars (all optional):
OTEL_SERVICE_NAME,OTEL_ENVIRONMENT,SERVICE_VERSIONOTEL_EXPORTER_OTLP_ENDPOINT(default127.0.0.1:4317; also accepts full URLs such ashttp://127.0.0.1:4317),OTEL_EXPORTER_OTLP_PROTOCOL(grpc)OTEL_EXPORTER_OTLP_METRICS_ENDPOINT(falls back to traces endpoint),OTEL_EXPORTER_OTLP_METRICS_PROTOCOLMETRICS_ENABLED,METRICS_LISTEN_ADDR(loopback-only)SENTRY_DSN,SENTRY_ENVIRONMENT,SENTRY_RELEASECONCIERGE_TRACE_SAMPLE_PERCENTAGE(default100)CONCIERGE_TRACE_SLOW_THRESHOLD_MS(default1000)
| Workflow | Trigger | Behavior |
|---|---|---|
ci.yml |
push to main, PRs |
format, lint, test+coverage, multi-arch build, security scan |
release.yml |
push to main |
release artifacts, GHCR image, deploy to OCI via jae-labs/ansible, cap CI-side Nomad wait time |
Release assets cover linux/{amd64,arm64} and darwin/{amd64,arm64}.
Production observability settings used by Ansible are injected from release.yml runner secrets at deploy time; if Honeycomb, Grafana Profiles, or Sentry OTLP secrets are omitted there, the rendered Alloy config excludes those pipelines.
| Repo | Purpose |
|---|---|
jae-labs/terraform |
Schema + Terraform source of truth |
jae-labs/ansible |
OCI host config + production deploy |
| Doc | What |
|---|---|
| Architecture | Runtime design, packages, request lifecycle |
| Adding a Resource Type | Schema-first checklist |
| Validation Patterns | Where/how input is validated |
| Modals and Blocks | Block Kit and callback ID conventions |
go test ./...Read AGENTS.md and the docs in docs/ before changing flow behavior or Terraform file paths. This bot has a hard contract with jae-labs/terraform; trimming README text does not relax it.
See LICENSE.
