You write a flag
The admin console (or the API) writes to the server. Every write is atomic: it bumps a single per-(project, environment) version number and appends an audit-history entry attributed to an actor. Nothing half-applies.
No feature theatre. This page tells you plainly what wagle does, how it does it, and (because it is the honest part) the choices that carry a real trade-off. If wagle doesn't fit your problem, we'd rather you find out here than after you sign up.
Wagle stores your flags, segments and typed config (bool, number, string, JSON), keyed by (project, environment). Instead of asking a server "what should this user see?" on every request, your app pulls the whole config set once and evaluates locally. The server's job is to be the source of truth, version changes atomically, and hand them to your SDKs on their next poll, not to sit on the hot path. It's built for apps where a per-evaluation network round-trip simply isn't affordable.
The admin console (or the API) writes to the server. Every write is atomic: it bumps a single per-(project, environment) version number and appends an audit-history entry attributed to an actor. Nothing half-applies.
The write produces a new, immutable in-memory snapshot for that (project, environment) and swaps it in atomically. Readers see the whole old version or the whole new one, never a torn mix of the two.
The SDK pulls that snapshot once (a sync), then re-checks on an interval you choose. An unchanged poll is answered with a tiny “nothing new” response, so short intervals stay cheap. Server-only flags never leave the server.
Your app evaluates against the in-memory snapshot: hash the entity into a bucket, walk the rules, serve a variation. No network call, no database, per evaluation, ever. That is the whole point.
Evaluation runs inside your process against an in-memory snapshot. No round-trip to wagle, no database read. This is what makes it viable for very-high-traffic apps: evals are free and never rate-limited.
The Go and TS SDKs and the server share one normative algorithm, pinned by a byte-for-byte vector file checked in CI. The same entity gets the same variation in every language, with no drift between your backend and your web client.
A write is all-or-nothing and bumps one version. History records who changed what, with a per-flag value diff you can read back. Readers never observe a partial batch.
One flag, N environment configs. dev / staging / prod share the flag's identity but roll out independently, and each environment buckets on its own salt, so promoting config does not copy who was in the 5%.
xxHash64 over (project:environment:flag:entity), mod 10000. A 5% rollout is a stable 5%: the same entity always lands in the same bucket until you change the weights.
Mark a flag server-only and its key, rules and rollout are excluded from the client sync: an internal kill-switch never ships in a public browser bundle, while your backend can still evaluate it.
Every one of these is a deliberate choice with a real cost. We list them because a flag system that hides its edges is the one that bites you in production.
Because SDKs evaluate a local snapshot, a change takes a moment to reach every process: each SDK picks it up on its next poll (default every 30s, tunable per client). It is fast, but not an atomic global switch. If you need a change to hit every instance in the same millisecond, that is not how wagle works.
Freshness is best-effort; recovery is a re-pull, not replay. Reads never fail, but if wagle is briefly unreachable, your app keeps serving the last config it saw (stale-but-available) until the next successful poll. That is the right default for availability, but it means "turn this off now" is bounded by the poll interval.
Every call is authenticated: a bearer JWT or an httpOnly session cookie resolves to a user or agent principal, writes are role-gated per project (admin/editor/viewer) and history records the real actor. What it is not: enterprise IdP glue. No SSO/SAML/SCIM, no custom roles: email/password plus revocable agent tokens, three fixed roles. If your org lives in Okta, that gap is real today.
A segment cannot reference another segment. This is permanent, not a missing feature: a flat graph keeps the blast radius of a segment edit readable in one hop ("which flags does this change touch?"). You compose by ANDing multiple segments in a rule, which covers the real need without hidden fan-out.
Every instance holds the whole index in memory, per (project, environment). That is the cost of zero-hop eval. It is tiny for normal flag counts, but wagle is built for high eval traffic, not for millions of distinct flags or unbounded key sets.
A segment's total values are capped (default 10,000) so its members ship whole inside one torn-free sync. If your use case is a 100k-entity allow-list, wagle is not the tool for that list today: the cap is what keeps sync bounded and un-paginated.
ListFlags / ListSegments return the whole set, sorted, with no pagination, deliberately simple for human-scale projects. A single project with tens of thousands of flags is outside what the admin surface is tuned for.
So the Go and TS SDKs evaluate a pattern identically, MATCHES is restricted to the RE2∩JS common subset and numeric parsing is strict (no 1_000, no lenient 18abc → 18). A few engine-specific regex constructs are rejected up front. It is a real constraint on rule authors, the price of cross-SDK fairness.
You pay only for syncs: evaluations are always free. Take the desk and wire your first flag in an afternoon.