Skip to content

bradlet/self-hosted-goff

Repository files navigation

Feature Flag Service

A self-hosted feature flag platform built on GO Feature Flag. It provides a relay proxy for flag evaluation and a lightweight web UI to improve DX when managing boolean feature flags.

This repository is also a template for deploying the same architecture in your own GCP project. See Using This as a Template to get started.

Architecture

graph TB
    subgraph Internet
        browser["Browser Client<br/>(flag management UI)"]
        app["Application Client<br/>(flag evaluation)"]
    end

    subgraph "Google Cloud – your-gcp-project-id / us-east1"
        subgraph "Web Service ALB"
            web_fwd["Forwarding Rule :443"]
            web_https["HTTPS Proxy<br/>+ Managed SSL"]
            web_urlmap["URL Map"]
            web_iap["Identity-Aware Proxy<br/>(@yourdomain.com only)"]
            web_backend["Backend Service"]
            web_neg["Serverless NEG"]
        end

        subgraph "Relay Proxy ALB"
            relay_fwd["Forwarding Rule :443"]
            relay_https["HTTPS Proxy<br/>+ Managed SSL"]
            relay_urlmap["URL Map"]
            relay_armor["Cloud Armor<br/>60 req/min per IP"]
            relay_backend["Backend Service"]
            relay_neg["Serverless NEG"]
        end

        subgraph "Cloud Run"
            web["ff-web-service<br/>(Go + Svelte SPA)<br/>SA: ff-web-service"]
            proxy["ff-relay-proxy<br/>(GO Feature Flag)<br/>SA: ff-relay-proxy"]
        end

        gcs[("GCS: your-flags-bucket<br/>flags.goff.yaml")]
        slack["Slack Webhook<br/>(flag change notifications)"]
    end

    browser -->|HTTPS| web_fwd
    web_fwd --> web_https --> web_urlmap --> web_iap --> web_backend --> web_neg --> web

    app -->|HTTPS| relay_fwd
    relay_fwd --> relay_https --> relay_urlmap --> relay_armor --> relay_backend --> relay_neg --> proxy

    web -->|"read/write<br/>(objectAdmin)"| gcs
    proxy -->|"read<br/>(objectViewer)"| gcs
    proxy -->|notifications| slack

    style web_iap fill:#f59e0b,stroke:#d97706,color:#000
    style relay_armor fill:#ef4444,stroke:#dc2626,color:#fff
    style gcs fill:#4285f4,stroke:#1a73e8,color:#fff
Loading

Access Control

Principal Resource Role Purpose
ff-relay-proxy SA GCS your-flags-bucket storage.objectViewer Read flag config
ff-web-service SA GCS your-flags-bucket storage.objectAdmin Read/write flag config
ff-web-service SA Cloud Run ff-relay-proxy run.invoker Only authorized caller
domain:yourdomain.com Web service backend iap.httpsResourceAccessor UI access via IAP

Traffic Flow

Flag evaluation (application clients):

  1. HTTPS request hits the relay proxy ALB
  2. Cloud Armor enforces 60 requests/minute per IP
  3. Request reaches the relay proxy Cloud Run service
  4. Relay proxy reads flag config from GCS (polled every 120s)

Flag management (browser):

  1. HTTPS request hits the web service ALB
  2. IAP requires Google authentication with an @yourdomain.com email
  3. Request reaches the web service Cloud Run service
  4. Web service reads/writes flag config directly in GCS

Both Cloud Run services are configured with INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER — they cannot be accessed directly, only through their respective load balancers.

Components

/proxy — GO Feature Flag Relay Proxy

Wraps the official gofeatureflag/go-feature-flag Docker image with a config template that substitutes environment variables at startup via envsubst.

Environment variables:

  • GCS_BUCKET — GCS bucket name
  • GCS_OBJECT — path to the flags YAML file in the bucket
  • SLACK_WEBHOOK_URL — Slack incoming webhook for change notifications
  • ADMIN_API_KEY — API key for /admin/ endpoints (evaluation endpoints are open)

/service — Web Service + UI

A Go HTTP server that:

  • Serves a prebuilt Svelte SPA (embedded in the binary via go:embed)
  • GET /api/flags — reads the GOFF YAML from GCS, returns all flags as JSON
  • PUT /api/flags — updates a boolean flag in the GOFF YAML and writes it back to GCS

Environment variables:

  • GCS_BUCKET — GCS bucket name
  • GCS_OBJECT — path to the flags YAML file
  • PORT — HTTP listen port (default 8080)

/infra — Terraform

Provisions all GCP infrastructure. See infra/variables.tf for required inputs.

Prerequisites:

  • GCS bucket your-tf-state-bucket must exist (Terraform remote backend)
  • OAuth consent screen must be configured in GCP console (one-time, for IAP)
  • Deploying principal needs roles/iap.admin
cd infra
terraform init
terraform plan -var-file=prod.tfvars
terraform apply -var-file=prod.tfvars

After applying, point DNS records for the relay proxy and web service domains to the IP addresses in the Terraform outputs.

Examples

We provide 2 examples to help demonstrate how to interact with the relay proxy in both client and server-side applications.

Testing locally

We use fake-gcs-emulator to enable a complete local test environment. The docker-compose file performs all setup required to run the relay proxy, web UI and GCS emulator.

docker compose up --build -d

Then, interact with the various components:

Managing Feature Flags

The Web UI exposes a simple interface for viewing all existing feature flags, and editing simple flags (only Boolean variants are supported at time of writing).

Flag updates in the UI are persisted to GCS; however, the tracked flag configuration is considered the source-of-truth: once a change to that file is committed to main, it will overwrite the existing state of the object in GCS. Therefore, if you alter a flag value in the UI and want it to stick around, you should alter the file directly and commit the change.

See the Go Feature Flag docs to learn about the other ways to create and interact with feature flags.

Live updates for complex feature flag values

Since we don't currently support non-boolean flag updates through the web UI, the following procedure can be followed to update other flag values:

  1. Pre-requisite: Have object write access on gs://your-flags-bucket and access to gsutil
  2. Make the desired changes to flags/flags.goff.yaml
  3. Run gsutil cp flags/flags.goff.yaml gs://your-flags-bucket/flags.goff.yaml

We currently configure the relay proxy to poll for flag updates every 2 minutes. If you want to force an immediate refresh, you can interact with the relay proxy's admin refresh endpoint; e.g.,

curl -X 'POST' \
  'http://<your_domain>:1031/admin/v1/retriever/refresh' \
  -H 'accept: application/json' \
  -H 'X-API-Key: <your_admin_api_key>' \

For local testing, we've set the API key to local-dev-key in docker-compose.yaml.

Claude Skill

This repository also exposes a goff-flags skill to make it easier to configure complex feature flags.


Using This as a Template

This repository is designed to be forked and adapted. The architecture diagram above reflects the full production setup. Follow the steps below to stand it up in your own GCP environment.

1. Fill in required configuration

Search the codebase for the following placeholder values and replace them with your own:

Placeholder Where Description
your-gcp-project-id infra/variables.tf, infra/backend.tf, .gitlab-ci.yml Your GCP project ID
your-tf-state-bucket infra/backend.tf GCS bucket for Terraform remote state
your-flags-bucket infra/variables.tf, docker-compose.yaml, service/main.go GCS bucket for flag YAML storage
yourdomain.com infra/variables.tf (iap_access_domain) Google Workspace domain for IAP access
your-gar-repo .gitlab-ci.yml Artifact Registry repository name
YOUR_PROJECT_NUMBER .gitlab-ci.yml Numeric GCP project number (from gcloud projects describe)
YOUR_WIF_POOL_ID / YOUR_WIF_PROVIDER_ID .gitlab-ci.yml Workload Identity Federation pool/provider IDs

2. Bootstrap GCP infrastructure

Run the bootstrap script once to provision the GCS state bucket, Artifact Registry repository, Workload Identity Federation pool, and CI/CD service account:

./scripts/bootstrap-infra.sh \
  YOUR_PROJECT_ID \
  YOUR_PROJECT_NAME \
  YOUR_GITLAB_GROUP \
  admin@yourdomain.com \
  https://gitlab.yourdomain.com

The script prints the exact GCP_WORKLOAD_IDENTITY_PROVIDER and GCP_SERVICE_ACCOUNT values to paste into .gitlab-ci.yml.

3. Configure OAuth consent screen (one-time, for IAP)

Before Terraform can enable IAP, you must configure an OAuth consent screen in the GCP console:

https://console.cloud.google.com/apis/credentials/consent?project=YOUR_PROJECT_ID

This is a manual one-time step per project. The deploying principal also needs roles/iap.admin.

4. Create required secrets in Secret Manager

The CI/CD pipeline reads the relay proxy admin API key from Secret Manager. Create it once:

echo -n "your-api-key" | gcloud secrets create ff-relay-proxy-admin-api-key \
  --data-file=- \
  --project=YOUR_PROJECT_ID

Also set the following as GitLab CI/CD variables (Settings → CI/CD → Variables):

  • SLACK_WEBHOOK_URL — Slack incoming webhook URL
  • RELAY_PROXY_DOMAIN — public domain for the relay proxy (e.g. flags-api.yourdomain.com)
  • WEB_SERVICE_DOMAIN — public domain for the web UI (e.g. flags.yourdomain.com)
  • GCS_FLAGS_BUCKET — the GCS bucket name for flag storage

5. Deploy with Terraform

cd infra
terraform init
terraform apply -var="project_id=YOUR_PROJECT_ID" \
                -var="flags_bucket_name=YOUR_FLAGS_BUCKET" \
                -var="iap_access_domain=yourdomain.com" \
                -var="relay_proxy_domain=flags-api.yourdomain.com" \
                -var="web_service_domain=flags.yourdomain.com" \
                -var="relay_proxy_image=..." \
                -var="web_service_image=..." \
                -var="slack_webhook_url=..." \
                -var="relay_proxy_admin_api_key=..."

Point DNS A records for both domains to the IP addresses printed in the Terraform outputs.

CI/CD

GitLab: An example pipeline is provided in .gitlab-ci.yml. It uses Workload Identity Federation (keyless auth) to authenticate to GCP — no long-lived service account keys required. On every push to main, it builds Docker images via Cloud Build, runs a Terraform plan, and applies if the plan succeeds. Flag config changes trigger a direct gsutil cp sync to GCS.

GitHub Actions: Example workflows will be added in a future update.

About

Open Source example template to self-host Go Feature Flag's relay proxy in Cloud Run with a slim, helpful UI and secure access patterns baked in.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors