Evaluation
Feature evaluation is local: the SDK downloads definitions and evaluates them in-process.
Filter types and parameters: Feature filters.
Context
Evaluation takes a toggly.Context:
ctx := toggly.Context{
Identity: "user-123",
Groups: []string{"beta"},
Traits: map[string]any{
"country": "US",
"plan": "pro",
},
Claims: map[string]string{
"role": "admin",
},
Request: &toggly.RequestContext{
UserAgent: "Mozilla/5.0 ... Chrome/120.0.0.0 ...",
AcceptLanguage: "en-US,en;q=0.9",
Country: "US",
},
}
| Field | Used by |
|---|---|
Identity | Percentage, Targeting, segment percentage gates |
Groups | Targeting group rules |
Traits | Custom filters you register |
Claims | UserClaims (Claim + Value exact match) |
Request | HTTP segment filters (browser / language / country / device / OS) |
Entity | ContextProperty (per-call domain object) |
Identity, Groups, Traits, and Claims are user context. Do not put page-entity attributes there — use Entity instead. See Entity & page context.
Boolean evaluation
enabled, err := client.IsEnabled(context.Background(), "MyFeature", ctx)
If a feature is not found:
- returns
falseby default - returns
trueifEnableUndefinedOnDevelopmentis set
HTTP segment filters + Request context
Server-side Go evaluates these built-in filters against Context.Request and Context.Claims. HTTP helpers map request headers; your authentication layer supplies identity, groups, and claims:
| Filter name(s) | Reads from | Match |
|---|---|---|
BrowserFamily | Request.UserAgent | Parsed browser name substring (case-insensitive); "Other" / empty UA → fail |
BrowserLanguage | Request.AcceptLanguage | Substring of the Accept-Language value (case-insensitive) |
Country, CountryFamily | Request.Country | Exact ISO country code (case-insensitive) |
DeviceType | Request.UserAgent | Parsed device family substring; "Other" / empty UA → fail |
OS, OperatingSystem | Request.UserAgent | Parsed OS family substring; "Other" / empty UA → fail |
UserClaims | Claims[Claim] | Exact string equality on claim type + value |
Percentage is required on these Go segment filters (0–100). Missing Percentage or a value ≤0 fails closed (feature off). With a non-empty Identity, the gate is sticky via the same percentile hash as Percentage filters; without identity, a partial percentage (0 < Percentage < 100) uses a random draw. Percentage >= 100 always passes the gate.
Missing Request, empty required fields, or missing claims also fail closed for that filter.
Wiring headers in togglyhttp
togglyhttp.Middleware, MiddlewareWith, and FeatureGate fill missing request metadata from headers. They do not infer an authenticated user or claims. Use the authentication callbacks in HTTP middleware, or combine an existing trusted context with request metadata:
// trustedUser comes from your authentication layer before the first evaluation.
func contextForRequest(r *http.Request, trustedUser toggly.Context) toggly.Context {
return togglyhttp.FromHttpRequest(r, trustedUser)
}
FromHttpRequest reads User-Agent, Accept-Language, and the first non-empty country header in this order: CF-IPCountry, X-Vercel-IP-Country, CloudFront-Viewer-Country. Header names are case-insensitive. A non-empty field in trustedUser.Request takes precedence; missing fields are filled individually. Only trust country headers set by your own proxy.
UserClaims reads Claims, not HTTP headers. Set it from your verified principal or JWT. Ordinary evaluation does not fetch definitions again when these per-request values change.
Ambient and per-call context
client.IsEnabled(r.Context(), key, toggly.Context{}) uses context attached by the middleware. Per-call non-empty identity and non-nil groups, claims, traits, request, or entity override the corresponding ambient field. An empty identity inherits the ambient identity; an empty but non-nil collection replaces the ambient collection. A per-call Request pointer replaces the entire ambient Request; that merge is different from the field-by-field header filling above. Build a fresh context when you need an explicitly anonymous evaluation rather than reusing an authenticated request.
See Integrations for FeatureGate options.
Entity context
Trusted Go servers evaluate full definitions and Context Property filters against a per-call entity on Context.Entity.
Register kinds once before creating the client so the dashboard Contexts page lists properties (PUT sdk/{appKey}/contexts). Opt out with DisableEntityContextRegistration: true. This registration is server-side schema setup, not request identity.
type Order struct {
ID string
Status string
Total float64
}
toggly.RegisterContext("Order", func(v any) toggly.EntityContext {
o := v.(Order)
return toggly.EntityContext{
Kind: "Order",
Key: o.ID,
Attributes: map[string]any{"Status": o.Status, "Total": o.Total},
}
}, &toggly.EntityContextSchemaRegistration{
KeyProperty: "Id",
Properties: []toggly.EntityContextPropertySchema{
{Name: "Id", Type: "string"},
{Name: "Status", Type: "string"},
{Name: "Total", Type: "number"},
},
})
order := Order{ID: "ord-1", Status: "paid", Total: 42.0}
userCtx := toggly.Context{
Identity: "user-123",
Entity: toggly.MapEntity("Order", order),
}
enabled, err := client.IsEnabled(context.Background(), "OrderBadge", userCtx)
You can also set Entity directly without MapEntity. An unregistered kind passed to MapEntity produces no entity; missing entities or required attributes fail closed. The evaluator checks attributes and does not enforce the filter's ContextKind against Entity.Kind. Supply the correct domain object yourself; a kind label is not a validation or authorization boundary.
Feature gates (Any / All)
You can evaluate a gate over multiple features:
ok, err := client.EvaluateGate(
context.Background(),
[]string{"FeatureA", "FeatureB"},
toggly.RequirementAll,
ctx,
false,
)
Built-in filters
The Go SDK includes built-in evaluators for:
- AlwaysOn / AlwaysOff
- Percentage (deterministic rollout based on
Context.Identityonly; requiresIdentity) - TimeWindow
- Targeting (users, groups, and deterministic default rollout percentage per identity)
- BrowserFamily, BrowserLanguage, Country / CountryFamily, DeviceType, OS / OperatingSystem, UserClaims (see HTTP segment filters)
- ContextProperty (via
Context.Entity)
Definitions serve the short names above — use those in configuration and docs.
Dashboard parameter names and semantics: Feature filters.
Per-SDK Local vs Worker vs fail-closed: SDK × filter matrix.