A Go library for creating and managing GitHub Apps using the GitHub App Manifest flow. Provides a web-based installer, multiple credential storage backends, and utilities for configuration management in containerized environments.
- Unified runtime - Single API for both HTTP servers and Lambda functions
- Web-based installer - User-friendly UI for creating GitHub Apps with pre-configured permissions
- Multiple storage backends - AWS SSM Parameter Store,
.envfiles, or individual files - Hot reload support - Reload configuration via SIGHUP or installer callback
- SSM ARN resolution - Resolve AWS SSM Parameter Store ARNs in environment variables (useful for Lambda)
- Ready gate - HTTP middleware that returns 503 until configuration is loaded
go get github.com/cruxstack/github-app-setup-go| Package | Description |
|---|---|
ghappsetup |
Unified runtime for HTTP servers and Lambda functions |
installer |
HTTP handler implementing the GitHub App Manifest flow |
configstore |
Storage backends for GitHub App credentials |
configwait |
Startup wait logic and ready gate middleware |
ssmresolver |
Resolves SSM Parameter Store ARNs in environment vars |
The ghappsetup.Runtime provides unified lifecycle management for both HTTP
servers and Lambda functions:
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"github.com/cruxstack/github-app-setup-go/ghappsetup"
"github.com/cruxstack/github-app-setup-go/installer"
)
func main() {
ctx := context.Background()
// Create runtime with unified lifecycle management
runtime, err := ghappsetup.NewRuntime(ghappsetup.Config{
LoadFunc: loadConfig,
AllowedPaths: []string{"/healthz", "/setup", "/callback", "/"},
})
if err != nil {
log.Fatal(err)
}
// Set up routes
mux := http.NewServeMux()
mux.HandleFunc("/healthz", runtime.HealthHandler())
mux.HandleFunc("/webhook", webhookHandler)
// Create installer using convenience method (auto-wires Store and reload callback)
installerHandler, err := runtime.InstallerHandler(installer.Config{
Manifest: installer.Manifest{
URL: "https://example.com",
Public: false,
DefaultPerms: map[string]string{
"contents": "read",
"pull_requests": "write",
},
DefaultEvents: []string{"pull_request", "push"},
},
AppDisplayName: "My GitHub App",
})
if err != nil {
log.Fatal(err)
}
mux.Handle("/setup", installerHandler)
mux.Handle("/callback", installerHandler)
// Start HTTP server with ReadyGate middleware
srv := &http.Server{
Addr: ":8080",
Handler: runtime.Handler(mux),
}
go srv.ListenAndServe()
// Block until config loads, then listen for SIGHUP reloads
if err := runtime.Start(ctx); err != nil {
log.Fatal(err)
}
log.Println("Configuration loaded, service is ready")
runtime.ListenForReloads(ctx)
}
func loadConfig(ctx context.Context) error {
// Validate required environment variables are present
if os.Getenv("GITHUB_APP_ID") == "" {
return fmt.Errorf("GITHUB_APP_ID not set")
}
return nil
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
// Handle GitHub webhooks
w.WriteHeader(http.StatusOK)
}| Variable | Description | Default |
|---|---|---|
GITHUB_URL |
GitHub base URL (for GHE Server) | https://github.com |
GITHUB_ORG |
Organization (empty = personal account) | - |
GITHUB_APP_INSTALLER_ENABLED |
Enable the installer UI (true, 1, yes) |
- |
| Variable | Description | Default |
|---|---|---|
STORAGE_MODE |
Backend: envfile, files, or aws-ssm |
envfile |
STORAGE_DIR |
Directory/path for local storage backends | ./.env |
AWS_SSM_PARAMETER_PREFIX |
SSM parameter path prefix (for aws-ssm) |
- |
AWS_SSM_KMS_KEY_ID |
Custom KMS key for SSM encryption | AWS managed |
AWS_SSM_TAGS |
JSON object of tags for SSM parameters | - |
| Variable | Description | Default |
|---|---|---|
CONFIG_WAIT_MAX_RETRIES |
Maximum retry attempts | 30 |
CONFIG_WAIT_RETRY_INTERVAL |
Duration between retries (e.g., 2s) |
2s |
Stores credentials as encrypted SecureString parameters:
store, err := configstore.NewAWSSSMStore("/my-app/prod/",
configstore.WithKMSKey("alias/my-key"),
configstore.WithTags(map[string]string{
"Environment": "production",
}),
)Parameters are stored at paths like /my-app/prod/GITHUB_APP_ID,
/my-app/prod/GITHUB_APP_PRIVATE_KEY, etc.
Saves credentials to a .env file, preserving existing content:
store := configstore.NewLocalEnvFileStore("./.env")Saves each credential as a separate file:
store := configstore.NewLocalFileStore("./secrets/")
// Creates: ./secrets/app-id, ./secrets/private-key.pem, etc.The Runtime supports hot-reloading configuration via SIGHUP signals. When the installer saves new credentials, it automatically triggers a reload via the callback:
// ListenForReloads handles both SIGHUP signals and installer callbacks
runtime.ListenForReloads(ctx)For manual reload triggering:
// Trigger a reload programmatically
runtime.Reload()For AWS Lambda functions, use EnsureLoaded() for lazy initialization:
var runtime *ghappsetup.Runtime
func init() {
runtime, _ = ghappsetup.NewRuntime(ghappsetup.Config{
LoadFunc: func(ctx context.Context) error {
// Resolve SSM parameters passed as ARNs
if err := ssmresolver.ResolveEnvironmentWithDefaults(ctx); err != nil {
return err
}
return validateConfig()
},
})
}
func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (Response, error) {
// Lazy-load config with retries (idempotent after first success)
if err := runtime.EnsureLoaded(ctx); err != nil {
return Response{StatusCode: 503, Body: "Service unavailable"}, nil
}
return handleRequest(ctx, req)
}The Runtime auto-detects Lambda environments and adjusts retry settings:
- HTTP: 30 retries, 2-second intervals (suitable for startup)
- Lambda: 5 retries, 1-second intervals (suitable for cold starts)
For Lambda deployments where secrets are passed as SSM ARNs:
// Resolve all environment variables that contain SSM ARNs
err := ssmresolver.ResolveEnvironmentWithRetry(ctx, ssmresolver.NewRetryConfigFromEnv())
// Or resolve a single value
resolver, _ := ssmresolver.New(ctx)
value, err := resolver.ResolveValue(ctx, os.Getenv("MY_SECRET"))After a GitHub App is created, the following credentials are stored:
| Key | Description |
|---|---|
GITHUB_APP_ID |
The numeric App ID |
GITHUB_APP_SLUG |
The app's URL slug |
GITHUB_APP_HTML_URL |
URL to the app's GitHub settings page |
GITHUB_WEBHOOK_SECRET |
Webhook signature secret |
GITHUB_CLIENT_ID |
OAuth client ID |
GITHUB_CLIENT_SECRET |
OAuth client secret |
GITHUB_APP_PRIVATE_KEY |
Private key (PEM format) |
MIT License - Copyright 2025 CruxStack