Core Concepts

GitOps Flag Sync

Treat feature flags as versioned source code. Sync your flag targeting rules directly from Git repositories using declarative manifests.

Overview

ToggleAI's GitOps Flag Sync (Flags-as-Code) service enables developers to manage feature flag configurations via pull requests rather than manual console edits. By declaring targeting rules, percentage rollouts, and variants in a version-controlled YAML config file (.toggleai/flags.yaml), every configuration change is audited in your git history, peer-reviewed before it goes live, and automatically synchronized to ToggleAI's edge runtime.

This approach aligns feature flag management with standard software delivery practices — the same review, approval, and deployment workflows your team already uses for code now govern flag changes. Rollbacks are as simple as reverting a commit.

How the GitOps Engine Works

Under the hood, the GitOps Engine is a background service inside the ToggleAI API that reacts to incoming webhook events from your git provider. Here is a precise breakdown of each phase:

Phase 1 — Webhook Receipt

When a pushevent or a merged pull request is detected on your configured branch, your git provider (GitHub, GitLab, or Bitbucket) dispatches a signed HTTP POST to your project's unique ToggleAI webhook endpoint (e.g., https://api.toggleai.fun/git/webhooks). ToggleAI validates the HMAC-SHA256 signature in the X-Hub-Signature-256 header to confirm the payload is authentic and untampered.

Phase 2 — File Fetch & Parse

The engine uses the stored access token to fetch the latest .toggleai/flags.yaml at the exact commit SHA referenced in the webhook payload. The YAML is parsed and validated against the ToggleAI config schema. Unknown fields emit a warning; structurally invalid documents abort the sync and trigger an error notification.

Phase 3 — Diff & Change Detection

The engine computes a diff between the parsed YAML state and the current configuration stored in the ToggleAI database. Only changed flags and configs are updated — unchanged entities are untouched — making syncs efficient and reducing database churn for large flag sets.

Phase 4 — Database Write & Cache Invalidation

Validated changes are committed to the primary database in a single atomic transaction. Immediately after, the edge cache TTLs for affected flags are invalidated so that SDK clients receive updated evaluations on the next polling cycle or SSE push — with no additional manual intervention.

Phase 5 — Sync Status & Audit Log

A sync event entry (including commit SHA, timestamp, changed entities, and status) is appended to the project audit log. The GitOps dashboard in the console reflects the latest sync state, including any validation warnings or conflict tickets needing manual resolution.

GitOps Sync Flow

The following diagram illustrates the complete end-to-end journey of a config change — from a developer commit in a local branch to live SDK evaluation at the edge.

Loading diagram...

Step-by-Step Setup Guide

Step 1: Connect Repository

Navigate to the project settings in the ToggleAI console under Git Connections and select your provider (GitHub, GitLab, or Bitbucket). Provide a fine-grained access token with read access to repository Contents and read/write access to Webhooks (required for bidirectional sync). The token is encrypted at rest using AES-256.

Step 2: Add Configuration File

Create a file named .toggleai/flags.yaml at the root of your repository. This file is the single source of truth for your flag states, targeting rules, and remote config values. You can scaffold the initial file from your existing flags using the ToggleAI CLI:

bash
toggleai flags export --format yaml > .toggleai/flags.yaml

Step 3: Configure Webhook

ToggleAI provides a unique webhook payload URL visible in the Git Connections dashboard. Register it in your repository webhook settings:

  • Payload URL: https://api.toggleai.fun/git/webhooks/{project-id}
  • Content type: application/json
  • Events: Push and Pull Request (merged)
  • Secret: Copy the HMAC secret from the console and paste it into the webhook secret field

Step 4: Select Sync Direction

Choose your synchronization direction in the connection settings. We recommend Git-only for teams adopting Flags-as-Code for the first time to establish a clean one-way source of truth before enabling bidirectional sync.

YAML Syntax Reference

The .toggleai/flags.yaml file uses a structured schema with two top-level sections: flags for boolean/string/number/json feature flags, and configs for typed remote configuration values.

Top-Level Fields

FieldTypeRequiredDescription
versioninteger✅ YesSchema version. Currently must be 1.
projectstring✅ YesYour ToggleAI project ID as shown in console settings (e.g. proj_prod_billing).
flagsmap⬜ OptionalMap of flag keys to flag definition objects. Keys must be lowercase kebab-case.
configsmap⬜ OptionalMap of config keys to remote config definition objects. At least one of flags or configs is expected.

Flag Definition Fields (flags.*)

FieldTypeRequiredDescription
namestring✅ YesHuman-readable display name shown in the console dashboard.
typeenum✅ YesValue type: boolean, string, number, or json.
descriptionstring⬜ OptionalPlain-text description of the flag's purpose. Displayed in the console and included in audit log entries.
tagsstring[]⬜ OptionalLabels for filtering and organizing flags (e.g. [billing, experiment]).
permanentboolean⬜ OptionalWhen true, the Lifecycle Manager never marks this flag as stale. Use for kill switches and ops controls.
environmentsmap✅ YesEnvironment-specific targeting rules. Keys must match environment slugs in your ToggleAI project (e.g. production, staging).
variationsobject[]⬜ OptionalNamed variants for A/B tests and multivariate experiments. Each has a key and a typed value.
targeting_rulesobject[]⬜ OptionalOrdered list of targeting rules evaluated top-to-bottom. Each rule includes conditions and a serve variant or value. First match wins.

Environment Object Fields (flags.*.environments.*)

FieldTypeDescription
enabledbooleanMaster on/off switch. When false, all evaluations return the default value regardless of targeting rules.
rollout_percentageinteger (0–100)Percentage of users who receive the 'on' value. Uses deterministic hashing on user ID for consistent bucketing.
default_valueanyValue returned when a user is outside the rollout bucket or no targeting rule matches.

Targeting Rule Operators

Each condition in a targeting rule uses one of the following operators to match user attributes passed in the evaluation context:

OperatorDescriptionExample
equalsExact match (string or number)plan equals "premium"
not_equalsDoes not matchregion not_equals "eu"
containsSubstring match (strings only)email contains "beta"
ends_withString ends with valueemail ends_with "@acme.com"
starts_withString starts with valueuserId starts_with "usr_beta_"
inAttribute value is in a listcountry in [US, CA, GB]
not_inAttribute value is NOT in a listplan not_in [free, trial]
regexAttribute matches a regular expressionuserId regex "^usr_[0-9]+$"
greater_thanNumeric comparison (greater than)account_age_days greater_than 30
less_thanNumeric comparison (less than)usage_mb less_than 500

Full Annotated Example

yaml
# .toggleai/flags.yaml
version: 1                        # Schema version — always 1 for now
project: "proj_prod_billing"      # Your ToggleAI project ID

flags:
  new-billing-dashboard:          # Flag key — lowercase kebab-case
    name: "New Billing Dashboard" # Display name shown in console
    type: boolean                 # Type: boolean | string | number | json
    description: "Enables the redesigned billing dashboard for eligible users."
    tags: [billing, dashboard]    # Searchable labels in the console
    permanent: true               # Never auto-archived by Lifecycle Manager

    environments:
      production:
        enabled: true             # Master switch — false means flag is entirely off
        rollout_percentage: 50    # Serve 'on' value to 50% of users (deterministic hash)
        default_value: false      # Returned for users outside the rollout bucket
      staging:
        enabled: true
        rollout_percentage: 100   # All staging users get the flag
        default_value: false

    variations:                   # Optional: named variants for A/B tests
      - key: control              # Variant key — referenced in targeting_rules.serve
        value: false
      - key: treatment
        value: true

    targeting_rules:              # Ordered rules — first match wins
      - description: "Internal employees always get treatment"
        conditions:
          - attribute: email      # User attribute key in the evaluation context
            operator: ends_with   # See operator table above
            value: "@acme.com"
        serve: treatment          # Variant key or a literal typed value

      - description: "Premium users get early access"
        conditions:
          - attribute: plan
            operator: equals
            value: "premium"
        serve: treatment

  dark-mode-toggle:
    name: "Dark Mode"
    type: boolean
    tags: [ui, experiment]
    environments:
      production:
        enabled: true
        rollout_percentage: 25
        default_value: false
      staging:
        enabled: true
        rollout_percentage: 100
        default_value: false

configs:
  max-checkout-limit:             # Config key — lowercase kebab-case
    type: number                  # Type: string | number | boolean | json
    description: "Maximum items allowed in a single checkout session."
    environments:
      production:
        value: 100                # Resolved value returned by the SDK
      staging:
        value: 10

  checkout-api-endpoint:
    type: string
    environments:
      production:
        value: "https://api.acme.com/v3/checkout"
      staging:
        value: "https://staging-api.acme.com/v3/checkout"

  feature-limits:
    type: json                    # JSON configs can hold complex structured data
    environments:
      production:
        value:
          max_items: 50
          allow_bundles: true
          rate_limit_rps: 100

Sync Directions & Conflict Resolution

ToggleAI supports three distinct synchronization directions configured per-project. Choose the mode that matches your team's workflow:

Git → ToggleAI (Git-only, Recommended)

Commits to your configured branch are the only way to change flag configurations. The console becomes read-only — all edits must go through a pull request. This is the most auditable and conflict-free mode, recommended for regulated industries and large teams.

ToggleAI → Git (Console-only)

Changes made in the ToggleAI console are automatically committed and pushed to the connected branch. The YAML file stays up to date as a git-backed audit trail, but the console remains the primary interface. Useful for teams gradually migrating to Flags-as-Code.

Bidirectional Sync

Changes from both Git pushes and console edits are synchronized. Non-conflicting changes are resolved automatically. When the same flag is modified in both Git and the console simultaneously, ToggleAI pauses auto-sync for that flag and creates a Conflict Ticket requiring manual resolution.

Validation & Safety

The GitOps Engine runs multiple validation layers before deploying any config change to protect production workloads:

Schema Validation

Every incoming YAML document is validated against the ToggleAI JSON Schema. Fields with incorrect types, unsupported operators, or missing required keys are rejected with a detailed error referencing the exact field path (e.g., flags.new-billing-dashboard.type).

CI Dry-Run via ToggleAI CLI

Add the following GitHub Actions workflow to validate pull requests before merging:

yaml
# .github/workflows/toggleai-validate.yml
name: Validate ToggleAI Config
on: [pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install ToggleAI CLI
        run: npm install -g @toggleai/cli
      - name: Validate flags.yaml
        run: toggleai flags validate .toggleai/flags.yaml
        env:
          TOGGLEAI_API_KEY: ${{ secrets.TOGGLEAI_API_KEY }}

Rollback via Git Revert

Because the YAML file is versioned in git, rolling back a bad flag change requires only a git revert. When the revert is pushed, ToggleAI's webhook handler re-processes the file and restores the previous configuration atomically — no manual intervention required.

bash
# Roll back the last commit to .toggleai/flags.yaml
git revert HEAD --no-edit
git push origin main