Why & how · no bullshit

What wagle is, how it
works, and where it isn't.

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.

What it is

A feature-flag & typed remote-config service that evaluates in your process.

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.

How it works

Write once on the server. Evaluate everywhere, locally.

01

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.

02

The server builds one snapshot

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.

03

Your SDK seeds, then polls

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.

04

Evaluation is local

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.

Where it's strong

The parts we'll happily oversell, except we won't.

Zero network hops on the eval path

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.

Every SDK evaluates identically

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.

Atomic, versioned, auditable writes

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.

Environments are a first-class axis

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%.

Deterministic bucketing

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.

Server-only flags stay server-side

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.

Where it's less strong

The trade-offs, stated plainly.

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.

Flag changes are eventually consistent, not instant

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.

If wagle is unreachable, the SDK serves the last known snapshot

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.

Auth is built in, but deliberately simple

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.

Segments are one level deep, on purpose

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.

Memory scales with your config size

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.

Segment membership is bounded; no member pagination

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.

Admin lists return everything, unpaginated

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.

Regex and numeric rules use a portable subset

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.

Is it for you?

A good fit

  • High-traffic apps where a per-eval network hop is unaffordable
  • Teams that want the same flag decision across backend and web/mobile
  • Multi-project, multi-environment setups from day one
  • Anyone who wants an audit trail and torn-free config reads

Look elsewhere if…

  • Cases needing instant, atomic global flips (sub-second, fleet-wide)
  • Enterprise SSO/SAML or fine-grained custom roles (three fixed roles today)
  • Giant allow-lists as segments (100k+ members per segment)
  • Projects with tens of thousands of flags per project

Still reading? Then it probably fits.

You pay only for syncs: evaluations are always free. Take the desk and wire your first flag in an afternoon.