Kaiten

Feature Flags

OpenFeature & OFREP

Evaluate feature flags using the standard OpenFeature Remote Evaluation Protocol — compatible with any OpenFeature SDK.

Overview

Kaiten implements the OpenFeature Remote Evaluation Protocol (OFREP), making it compatible with any OpenFeature SDK. No proprietary client library is required: configure a standard OFREP provider and evaluate flags from Go, Python, JavaScript, or any supported language.


Evaluate a single flag

Send a POST request with the flag key and an evaluation context:

curl -X POST http://localhost:6060/api/ofrep/v1/evaluate/flags/delivery-tracking-enabled \
  -H "Authorization: Bearer ksh_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "context": {
      "targetingKey": "sakura-tokyo",
      "kaiten": { "instanceSlug": "sakura-tokyo-prod" }
    }
  }'

Response:

{
  "key": "delivery-tracking-enabled",
  "variant": "on",
  "value": true,
  "reason": "TARGETING_MATCH"
}
FieldDescription
keyThe flag slug
variantThe name of the resolved variant
valueThe variant's payload (boolean, string, number, or object)
reasonWhy this variant was selected — TARGETING_MATCH (a rule matched), SPLIT (a rollout distribution), DEFAULT (no rule matched), STATIC (the default variant, no rules), DISABLED (the flag is off). A failed evaluation answers 400 with errorCode and errorDetails instead of variant, value and reason: PROVIDER_FATAL when the context has no targetingKey, FLAG_NOT_FOUND for an unknown key

Bulk evaluation

Evaluate all flags for an instance in a single request:

curl -X POST http://localhost:6060/api/ofrep/v1/evaluate/flags \
  -H "Authorization: Bearer ksh_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "context": {
      "targetingKey": "sakura-tokyo",
      "kaiten": { "instanceSlug": "sakura-tokyo-prod" }
    }
  }'

Response:

{
  "flags": [
    {
      "key": "delivery-tracking-enabled",
      "variant": "on",
      "value": true,
      "reason": "TARGETING_MATCH"
    },
    {
      "key": "promo-banner",
      "variant": "salmon-week",
      "value": { "bannerText": "Fresh salmon week — new arrivals daily", "bannerColor": "#C91D21" },
      "reason": "TARGETING_MATCH"
    }
  ]
}

Abridged: the response carries one entry per flag of the organization. Sakura Tokyo is on Premium, so promo-banner's rule Standard or Premium license serves salmon-week; for Ninja Osaka, on Starter, the same request serves the default, control.

Bulk evaluation is more efficient when your application needs multiple flags at startup or on each request: it takes one HTTP call instead of N.


Using OpenFeature SDKs

Configure any OpenFeature SDK to use Kaiten as its OFREP provider. The provider is the generic OFREP provider of each SDK — go-sdk-contrib/providers/ofrep in Go, openfeature-provider-ofrep in Python, @openfeature/ofrep-provider in Node — and it takes two things: the API base URL and the caller's token. The base URL keeps its /api prefix, because the provider appends /ofrep/v1/… to it: http://localhost:6060/api on the Community Edition, as below, and https://app.kaiten.sh/api on Kaiten Cloud.

Each snippet registers the provider with the call that waits until it is ready. In Go that is not optional: SetProvider returns at once, and an evaluation made before the provider is ready returns the default value with a PROVIDER_NOT_READY error.

package main

import (
    "context"
    "fmt"
    "log"

    ofrep "github.com/open-feature/go-sdk-contrib/providers/ofrep"
    "github.com/open-feature/go-sdk/openfeature"
)

func main() {
    provider := ofrep.NewProvider("http://localhost:6060/api", ofrep.WithBearerToken("ksh_xxx"))
    if err := openfeature.SetProviderAndWait(provider); err != nil {
        log.Fatal(err)
    }

    client := openfeature.NewClient("my-app")
    value, err := client.BooleanValue(context.Background(), "delivery-tracking-enabled", false,
        openfeature.NewEvaluationContext("sakura-tokyo", map[string]any{
            "kaiten": map[string]any{"instanceSlug": "sakura-tokyo-prod"},
        }))
    if err != nil {
        log.Fatal(err) // value holds the default here, and the error says why
    }
    fmt.Println(value)
}
from openfeature import api
from openfeature.evaluation_context import EvaluationContext
from openfeature.contrib.provider.ofrep import OFREPProvider

provider = OFREPProvider(
    base_url="http://localhost:6060/api",
    headers_factory=lambda: {"Authorization": "Bearer ksh_xxx"},
)
api.set_provider_and_wait(provider)

client = api.get_client("my-app")
value = client.get_boolean_value(
    "delivery-tracking-enabled",
    default_value=False,
    evaluation_context=EvaluationContext(
        targeting_key="sakura-tokyo",
        attributes={"kaiten": {"instanceSlug": "sakura-tokyo-prod"}},
    ),
)
print(value)
import { OpenFeature } from '@openfeature/server-sdk';
import { OFREPProvider } from '@openfeature/ofrep-provider';

const provider = new OFREPProvider({
  baseUrl: 'http://localhost:6060/api',
  headers: [['Authorization', 'Bearer ksh_xxx']],
});
await OpenFeature.setProviderAndWait(provider);

const client = OpenFeature.getClient('my-app');
const value = await client.getBooleanValue('delivery-tracking-enabled', false, {
  targetingKey: 'sakura-tokyo',
  kaiten: { instanceSlug: 'sakura-tokyo-prod' },
});
console.log(value);

Context mapping

OpenFeature SDKs pass an EvaluationContext that maps to Kaiten's OFREP context:

OpenFeature fieldOFREP context keyUsage
targetingKeytargetingKeyRequired. The subject the evaluation is bucketed on; when it names a customer slug, Kaiten resolves that customer's License and entitlement facts (__kaiten.license, __kaiten.entitlements), read from the customer's oldest instance
kaiten.instanceSlug (or kaiten.instanceId)kaitenNames the instance being evaluated; resolves __kaiten.instance, __kaiten.customer, __kaiten.deploymentZone
Custom attributesAny keyAvailable in CEL targeting rules

Example: passing custom context

With the client configured above, custom attributes travel in the same context:

value, err := client.BooleanValue(context.Background(), "delivery-tracking-enabled", false,
    openfeature.NewEvaluationContext("sakura-tokyo", map[string]any{
        "kaiten":       map[string]any{"instanceSlug": "sakura-tokyo-prod"},
        "orderChannel": "mobile-app",
        "customerName": "Sakura Tokyo",
    }))
value = client.get_boolean_value(
    "delivery-tracking-enabled",
    default_value=False,
    evaluation_context=EvaluationContext(
        targeting_key="sakura-tokyo",
        attributes={
            "kaiten": {"instanceSlug": "sakura-tokyo-prod"},
            "orderChannel": "mobile-app",
            "customerName": "Sakura Tokyo",
        },
    ),
)
const value = await client.getBooleanValue('delivery-tracking-enabled', false, {
  targetingKey: 'sakura-tokyo',
  kaiten: { instanceSlug: 'sakura-tokyo-prod' },
  orderChannel: 'mobile-app',
  customerName: 'Sakura Tokyo',
});

These custom attributes are available in CEL targeting rules, next to the facts Kaiten resolves itself — the License, for one, is never something you pass:

orderChannel == "mobile-app" && __kaiten.license.familySlug == "premium"

Flag manifest

Kaiten exposes the OpenFeature flag manifest at:

GET /api/openfeature/v0/manifest

It returns the flags that carry a fallback_value in their metadata, each as { key, type, defaultValue, name, description } — the OpenFeature flag manifest an SDK uses for offline fallbacks.


Why OpenFeature

BenefitDescription
No vendor lock-inSwitch flag providers by changing one line of configuration — evaluation code stays the same
Multi-language supportOfficial SDKs for Go, Python, JavaScript, Java, .NET, PHP, Ruby, and more
Standard protocolOFREP is an open specification — any compliant provider works
Community ecosystemHooks, middleware, and integrations from the OpenFeature community

On this page