ToggleFleet
Documentation · Ruby SDK 0.2.1 · REST API v1

From first flag to local evaluation.

Create a flag, generate an environment key, add the Ruby SDK, and ship behind a release control. This guide also covers the REST endpoints, ETag refresh, environments, history, rollback, and key rotation.

Setting up with an AI agent

Hand this guide to your coding agent.

Copy a ready-made prompt containing the install steps, the actor-identity decision, the five gates, group predicates, and the verification order from this page. Paste it into Claude Code, Cursor, Copilot, or any agent with repository access. It is written to keep the SDK key out of your repository and to stop for confirmation before anything reaches production.

You are adding ToggleFleet - a hosted feature-flag service - to a Ruby
application for the first time. Work strictly in the order below and stop for
confirmation before anything is enabled in production.

CRITICAL CONSTRAINTS - do not violate these:
1. The SDK key is a server-side secret. It must never appear in browser
   JavaScript, a mobile bundle, a committed file, a log line, or a test fixture.
   Read it from the environment only.
2. Do not call the ToggleFleet HTTP API from a request path. The gem evaluates
   flags in-process; a flag check must not make a network request.
3. Every flag ships OFF. Add the code path behind a flag that is off, then turn
   it on from the dashboard.
4. If you cannot determine something with certainty - which actor ID to use,
   whether a code path is safe to gate - say so and stop. Do not guess.

HOW IT WORKS (this shapes every decision below)
The process fetches the whole environment configuration once, refreshes it in
the background with conditional (ETag) requests, and evaluates flags locally. A
check is a hash lookup, not an HTTP call. If ToggleFleet is unreachable the
last-good configuration keeps being used; before the first successful fetch,
flags return config.default.

THE FIVE GATES - a flag is ON if ANY gate matches, evaluated in this order:
  boolean              on for every evaluation
  actor                an explicit list of stable actor IDs
  group                a group name whose predicate you register in Ruby
  percentage_of_actors deterministic per (flag, actor) - sticky, use for rollouts
  percentage_of_time   random per call - NOT sticky, use for sampling only
Gate state is per environment. There is nothing else - no expression language.

STEP 1 - KEY (write it into no file)
One SDK key per environment, generated in Settings -> SDK keys and shown once.
Put it in whatever secret mechanism this app already uses, as
TOGGLEFLEET_SDK_KEY. Tell me which environment's key you have been given and
confirm before continuing.

STEP 2 - INSTALL
  gem "togglefleet", "~> 0.2"     # MIT, no runtime dependencies, Ruby >= 3.0
config/initializers/togglefleet.rb:
  ToggleFleet.configure do |config|
    config.sdk_key          = ENV.fetch("TOGGLEFLEET_SDK_KEY")
    config.default          = false  # result when a flag is unknown or not yet loaded
    config.refresh_interval = 15     # seconds, jittered +/-15%
    config.open_timeout     = 3
    config.read_timeout     = 5
    config.logger           = Rails.logger
  end
  ToggleFleet.start               # background refresh; fork-safe as of v0.2.0
Do NOT add on_worker_boot wiring for Puma, Unicorn or Passenger. The client
detects a forked child and restarts its own refresh thread.

STEP 3 - ACTOR IDENTITY (decide this BEFORE writing any flag check)
ToggleFleet derives the actor ID from, in order: #togglefleet_id, then #id, then
#to_s. Whatever that returns is both the string actor gates match on AND the
input to percentage bucketing - so changing it later re-shuffles who is inside a
rollout. Choose one now and state which:
  - the bare id (the default, nothing to write), or
  - define togglefleet_id on the actor class for a namespaced or UUID identity.

STEP 4 - GROUPS (only if the app needs them)
Group membership is decided by YOUR code; ToggleFleet stores only the group name
enabled for a flag, and no actor attribute ever leaves the process. Register
predicates in the initializer, before any evaluation:
  ToggleFleet.register_group(:admins)   { |user| user.admin? }
  ToggleFleet.register_group(:internal) { |user| user.email.end_with?("@yourco.com") }
A predicate that raises is logged and treated as not matching. Keep them cheap
and free of side effects - they run on every evaluation that reaches the group
gate.

STEP 5 - GATE THE CODE PATH
  if ToggleFleet.enabled?(:checkout_v2, actor: current_user)
    render "checkout/v2"
  else
    render "checkout/current"
  end
Rules:
  - Pass actor: whenever an actor exists. Without one, the actor and
    percentage-of-actors gates cannot match.
  - Use groups: [:eu] only for a group that no registered predicate can derive
    from the actor.
  - The else branch must be the current, working behaviour. Do not gate a path
    whose off state is broken.
  - Keep the flag key a literal symbol at the call site so it stays greppable.
  - Need every flag at once (e.g. to bootstrap a client-side payload)? Use
    ToggleFleet.all(actor: current_user) server-side. Never ship the key to a
    browser to let it evaluate for itself.

STEP 6 - VERIFY (stop and ask me before turning anything on in production)
  - In tests, keep the network out: stub ToggleFleet.enabled? per example, or
    rely on config.default and assert the off path.
  - Exercise BOTH branches in development or staging.
  - Confirm the environment's "Last connected" time in Settings has updated.
  - If the app has metrics, attach them:
      config.on_evaluation = ->(flag, actor, result) {
        StatsD.increment("feature_flag.#{flag}.#{result}")
      }

REST API - only for services that cannot use the gem
  GET /v1/config    whole environment + ETag; send If-None-Match, expect 304
  GET /v1/evaluate  ?flag=&actor=&groups= for a single decision
Authorization: Bearer $TOGGLEFLEET_SDK_KEY, 600 requests/min per IP. There is
deliberately no browser CORS: the key reads the entire environment config.
Prefer /v1/config plus local evaluation over per-check /v1/evaluate calls.

DELIVERABLES:
- the actor-identity decision and why
- every flag key created, and what its off state does
- every file changed
- confirmation that the SDK key appears in no committed file
- anything you were unsure about - say so rather than guessing

Reference: https://togglefleet.com/docs
01 / Quick start

Ship behind a flag in five steps.

  1. Create a free workspace and open Flags.
  2. Create a stable key such as checkout_v2.
  3. Open Settings, generate a key for Development, and copy it immediately.
  4. Add the togglefleet gem and configure the key.
  5. Call ToggleFleet.enabled? around the code path you want to control.
key
SDK keys are shown once.

ToggleFleet stores a SHA-256 hash and a display prefix, not the full credential. Generate a separate key for each environment.

02 / Environment key

Generate one server credential per environment.

Go to Settings → SDK keys and connections. Select Generate new key for Development and copy the full value from the one-time reveal.

.envnever commit this file
TOGGLEFLEET_SDK_KEY=tf_live_…

The key grants read access to one environment's complete flag configuration and server-side evaluation endpoint. Keep it out of browser code, source control, logs, screenshots, and support tickets.

Rotate an exposed key

Generate a new key in Settings. Rotation invalidates the previous key immediately. Update the application secret, deploy it, and confirm the environment's Last connected time changes.

03 / Ruby SDK

Install and configure the gem.

The open-source gem is published on RubyGems as togglefleet and licensed under MIT.

Gemfilebundle install
gem "togglefleet", "~> 0.2"
config/initializers/togglefleet.rbserver-side key
ToggleFleet.configure do |config|
  config.sdk_key          = ENV.fetch("TOGGLEFLEET_SDK_KEY")
  config.refresh_interval = 15     # background refresh seconds
  config.default          = false  # fail-safe for unknown flags
  config.open_timeout     = 3
  config.read_timeout     = 5
end

ToggleFleet.start

Then evaluate anywhere in the process:

application codelocal check
if ToggleFleet.enabled?(:checkout_v2, actor: current_user)
  render "checkout/v2"
else
  render "checkout/current"
end
0ms*
No ToggleFleet network request occurs inside enabled?.

The check uses the latest configuration held in memory. *Normal local execution time still applies.

04 / Targeting

A flag is on when any gate matches.

Gate state is isolated by environment. Boolean is evaluated first, followed by actor, group, percentage of actors, and percentage of time.

GateDashboard valueBehavior
BooleanOn or offOn for every evaluation when enabled.
ActorComma-separated IDsOn for listed stable actor identifiers.
GroupComma-separated namesOn when a group supplied or resolved in your app matches.
% of actors0–100Deterministic per flag and actor; suitable for gradual rollouts.
% of time0–100Random per evaluation; useful for sampling, not sticky cohorts.
05 / Actors and groups

Stable IDs in. Stable rollout out.

The Ruby SDK derives an actor ID in this order:

  1. actor.togglefleet_id when defined
  2. actor.id when available, including ActiveRecord models
  3. The string value of the actor itself
actor examplesRuby
ToggleFleet.enabled?(:beta, actor: current_user)
ToggleFleet.enabled?(:beta, actor: "account_42")
ToggleFleet.enabled?(:beta)  # boolean and % time gates only

Register group predicates

Group membership stays in your application; ToggleFleet stores only the group names enabled for a flag.

initializergroup resolution
ToggleFleet.register_group(:admins) { |user| user.admin? }
ToggleFleet.register_group(:internal) do |user|
  user.email.end_with?("@yourco.com")
end

ToggleFleet.enabled?(:beta, actor: current_user)
ToggleFleet.enabled?(:eu_pricing, actor: user, groups: [:eu])

Resolve every flag

bulk snapshotRuby
ToggleFleet.all(actor: current_user)
# => { "checkout_v2" => true, "billing_portal" => false }
06 / REST API

Two server-side JSON endpoints.

Use an environment SDK key as an exact Bearer credential. These endpoints intentionally do not enable browser CORS: exposing an SDK key to frontend JavaScript would grant anyone with that key access to the complete environment configuration. REST traffic is limited to 600 requests per minute per client IP; use /v1/config plus local evaluation for high-volume checks.

GET /v1/evaluate

Evaluate one flag on the server. Pass comma-separated groups when group targeting is required.

evaluateHTTP
curl https://togglefleet.com/v1/evaluate \
  -H "Authorization: Bearer $TOGGLEFLEET_SDK_KEY" \
  -G --data-urlencode "flag=checkout_v2" \
     --data-urlencode "actor=account_42" \
     --data-urlencode "groups=beta,paid"

# { "flag": "checkout_v2", "enabled": true }

GET /v1/config

Fetch the complete environment for local caching. The response includes an ETag. Send it back as If-None-Match; unchanged configuration returns HTTP 304 with no response body.

conditional configurationHTTP
curl -i https://togglefleet.com/v1/config \
  -H "Authorization: Bearer $TOGGLEFLEET_SDK_KEY"

# HTTP/2 200
# etag: "7f…"
# { "flags": { "checkout_v2": { … } } }

curl -i https://togglefleet.com/v1/config \
  -H "Authorization: Bearer $TOGGLEFLEET_SDK_KEY" \
  -H 'If-None-Match: "7f…"'

# HTTP/2 304
StatusMeaning
200Valid evaluation or configuration response.
304Configuration is unchanged for the supplied ETag.
400Missing or invalid flag key.
401Missing, malformed, rotated, or unknown SDK key.
404The requested flag does not exist in this workspace.
429Rate limit reached; back off before retrying.
500Unexpected service error; continue using last-good local state.
07 / Environments

Keep Development, Staging, and Production independent.

Each environment has its own SDK key and complete set of flag states. A newly created workspace starts with Development, Staging, and Production; generate each key only when you are ready to connect that environment.

Promote a tested state

On the Flags page, select the destination environment and use Copy states. This overwrites all destination gate values with the source environment's current values and records the operation in history.

!
Environment copy is intentionally broad.

It copies every flag state into the destination. Review both environments first and use change history if a rollback is required afterward.

Use multiple environments in one process

standalone clientsRuby
prod_config = ToggleFleet::Configuration.new.tap do |config|
  config.sdk_key = ENV.fetch("PROD_TOGGLEFLEET_KEY")
end

prod = ToggleFleet::Client.new(prod_config).start
08 / History and rollback

Every flag-state change keeps its prior value.

The History page records the operator, timestamp, environment, action, and summary for flag, environment, key, membership, and workspace operations.

Flag-state updates are restorable. Select Rollback beside a restorable entry to put the flag back into the state captured immediately before that change. The rollback itself creates another history event.

Rollback changes configuration immediately.

Connected SDKs receive the restored state on their next background refresh. Use a safe rollout and verify production behavior after restoration.

09 / Reliability

Design the application to keep deciding.

  • Local evaluation: enabled? reads in-process state.
  • Conditional refresh: ETags turn unchanged polls into HTTP 304 responses.
  • Last-good state: a failed refresh does not erase the previous configuration.
  • Fail-safe default: before the first successful load, unknown flags return config.default.
  • Bounded network calls: open and read timeouts are configurable.
  • Instrumentation: attach config.on_evaluation for your own metrics or logging.
instrumentationRuby
ToggleFleet.configure do |config|
  config.sdk_key = ENV.fetch("TOGGLEFLEET_SDK_KEY")
  config.on_evaluation = ->(flag, actor, result) {
    StatsD.increment("feature_flag.#{flag}.#{result}")
  }
end
10 / Security checklist

Treat a flag service as production infrastructure.

  • Use a different SDK key for each environment.
  • Keep keys in a server-side secret manager or protected environment variable.
  • Never expose keys to browser or mobile application bundles.
  • Rotate a key immediately if it enters a log, commit, screenshot, or ticket.
  • Invite teammates through verified identity-provider email addresses.
  • Use percentage rollouts and non-critical flags for the first integration.
  • Review History after environment copy, key rotation, and production flag changes.