Native SDKs for JavaScript/TypeScript, Python and Go, with OpenFeature providers, and any other language through OpenFeature (OFREP). A CLI and GitHub integrations for CI and pull requests. The code that runs in your app is Apache-2.0, and every bundle it receives is signed.
The native SDKs download the signed configuration once, verify it and evaluate in-process, with live updates. Every other language reads the same flags through an OpenFeature OFREP provider.
All three run the same evaluation engine, tested against 21,998 shared evaluation cases generated from the reference engine: same inputs, same results in JavaScript, Python and Go.
Any language via OpenFeature (OFREP)
The NanoFlags edge implements the OpenFeature Remote Evaluation Protocol. Official OFREP providers exist for these languages and more, so teams using them can call NanoFlags today:
Accessors are local and synchronous. They never perform I/O and never throw: without a bundle they return your fallback.
checkout.ts
const on = flags.boolean("checkout-v2", false, { key: user.id, region: "EU" });
// Local and synchronous. Returns false only if no configuration has ever loaded.
import { createClient } from"@nanoflags/sdk";
import { NanoFlagsProvider, useFlag, useFlagsStatus } from"@nanoflags/sdk/react";
const client = createClient({
sdkKey: "nf_pub_…",
baseUrl: "https://edge.nanoflags.io",
bootstrap: window.__FLAGS__, // the bundle the server rendered with, so the first render matches
});
exportfunction App({ user }) {
return (
<NanoFlagsProvider client={client} context={{ key: user.id }}>
<Checkout />
</NanoFlagsProvider>
);
}
function Checkout() {
const v2 = useFlag("checkout-v2", false);
const { status } = useFlagsStatus(); // "not-ready" | "ready" | "stale"return v2 ? <NewCheckout /> : <OldCheckout />;
}
import os
from nanoflags import NanoFlags # pip install nanoflags
flags = NanoFlags(os.environ["NANOFLAGS_SDK_KEY"], service="checkout-api")
flags.wait_until_ready(timeout=5) # never raises
on = flags.bool_variation("checkout-v2", False, {"key": user.id, "region": "EU"})
# Evaluated locally. Falls back to False only if no config has ever loaded.
flags.on_change(lambda e: print(f"r{e.revision}", e.transport, e.latency_ms))
import nanoflags "github.com/motazalmaroai/nanoflags-sdk/go"
client, err := nanoflags.New(os.Getenv("NANOFLAGS_SDK_KEY"), nanoflags.WithService("checkout-api"))
if err != nil {
log.Fatal(err)
}
defer client.Close()
_ = client.WaitForReady(ctx) // nil once a configuration is in use
on := client.BoolVariation("checkout-v2", nanoflags.Context{"key": user.ID, "region": "EU"}, false)
// Evaluated locally. Falls back to false only if no config has ever loaded.
OpenFeature
Keep your call sites standard.
The OpenFeature providers wrap a NanoFlags client: for the JavaScript server and web SDKs, for Python and for Go. Evaluation stays local inside the provider; NanoFlags keeps doing the loading and the live updates. Your flag calls use the OpenFeature standard API.
from openfeature import api
from openfeature.evaluation_context import EvaluationContext
from nanoflags import NanoFlags
from nanoflags.openfeature import NanoFlagsProvider # pip install "nanoflags[openfeature]"
api.set_provider_and_wait(NanoFlagsProvider(NanoFlags(sdk_key), close_client=True))
on = api.get_client().get_boolean_value("checkout-v2", False, EvaluationContext(targeting_key=user.id))
@nanoflags/cli works with a CI key (export, scenarios, code references) or an admin key (all of that, plus creating flags and publishing). SDK keys are refused.
nanoflags flags create <key> --type boolean
Create a flag with a type, default, name and description.
nanoflags eval <flag> --context '{…}'
Evaluate a context against the draft or the published revision, with the reason and the trace.
nanoflags scenarios run --source draft
Run scenario checks for a commit; exit code 1 when one fails.
nanoflags refs scan . --repo owner/name
Find flag references in your code and upload them.
nanoflags publish --expected-revision <n>
Publish the draft, following the environment's policy.
nanoflags export --out project.json
Export the whole project as JSON.
Pass the key in NANOFLAGS_KEY, not as an argument: arguments are visible in process lists and shell history.
Two ways to bring NanoFlags into pull requests: an Action you add to a workflow, and an App you install once.
GitHub Action
Runs your scenario checks for a commit and fails the job when one fails; optionally uploads code references. Use a CI key: it can run scenarios but can't publish, and the action refuses admin keys.
Every published revision is signed with Ed25519 before it is distributed. The public keys are served without authentication, so you can check a bundle with nothing but curl and your own code.
01
Fetch the public keys
A JSON list of JWKs, each with a kid and a status. During a rotation the old key is listed as retiring.
02
Read the signature headers
The edge sends the signature, the key id and the environment id with every signed bundle.
03
Verify the canonical string
Ed25519 over the string below, where the last line is the SHA-256 of the bundle text exactly as sent. The SDK does exactly this.