> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fireweave.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Official FireWeave SDK documentation.
> Product noun is control point. OpenFeature and the wire protocol use flagKey — do not invent controlPointKey.
> Do not invent APIs, packages, env vars, or endpoints. Guardrails are a typed stub (UnsupportedCapability). OpenFeature Tracking (spec section 6) is not implemented.
> registerTarget and identify exist on Node, Python, and Web only. Go and Java have no target-registration API on master.
> sendExposure defaults to false. Java close() does not flush exposures.

# Quickstart

> Install a FireWeave SDK, evaluate a control point offline, register a target where the API exists, record an outcome, and shut down.

This walkthrough goes **Install → Configure → Initialize → Register target → Evaluate → Record outcome → Shutdown**. Examples use the deterministic in-memory adapter, so you do not need a network or a project key. Example keys match the SDK docs: `new-checkout` and `user_42`.

OpenFeature and the wire still say `flagKey`. The product name is **control point**. Evaluation **never throws** — a failure returns your default.

<Warning>
  **Lead with the registry** where the package is published (verified 2026-08-17): npm `@fireweaveai/sdk@2.1.0` (`latest` = 2.1.0), `@fireweaveai/web-sdk@2.1.0`, and PyPI `fireweave` 0.1.0. Pin Node **2.1.0** — npm **2.0.0** is a different API (direct PostHog adapter and `./posthog`). The Go module and Java `0.1.0-SNAPSHOT` artifacts are still unpublished. Checkout remains an alternative for contributing or an unreleased tree.
</Warning>

## Prerequisites

* **Node / Bun / Deno:** Node.js ≥ 20.20, or Bun ≥ 1.2, or Deno ≥ 2.0. Optional peer `@openfeature/server-sdk` only if you use the provider.
* **Python:** Python ≥ 3.10 and a virtualenv.
* **Go:** Go 1.25. The module is unpublished — clone [FireWeave-HQ/fireweave-sdk](https://github.com/FireWeave-HQ/fireweave-sdk) and use a `replace` directive.
* **Java:** JDK 11+ and Maven. Clone the SDK repo; artifacts install to your local `~/.m2` as `0.1.0-SNAPSHOT`.
* **Browser:** A frontend toolchain. `@fireweaveai/web-sdk@2.1.0` is on npm. The package reads **no** environment variables.

Offline steps need no FireWeave credentials. Production needs a project key (`project-api-key_…`) and a fw-server base URL — see [Configuration](/production/configuration).

<Steps>
  <Step title="Install">
    Use the published registry where it exists. Checkout is optional except for Go and Java.

    <Tabs>
      <Tab title="Node">
        npm `latest` is **2.1.0** (verified 2026-08-17). Pin it. **2.0.0** is still published and still ships PostHog / `./posthog`.

        ```bash theme={null}
        npm install @fireweaveai/sdk@2.1.0
        ```

        Add `@openfeature/server-sdk` only if you use the provider. Deno can `import { … } from 'npm:@fireweaveai/sdk'` with no install step; that follows npm `latest` (**2.1.0** today). Pin `@2.1.0` in an import map if you need to stay off 2.0.0.

        <Note>
          APIs on this page are from SDK `master`. The npm 2.1.0 tarball `gitHead` is `fd19cad`, not audit commit `dfeb478` — **NEEDS VERIFICATION** that every snippet matches that tarball exactly. Checkout if you need a specific tree.
        </Note>

        ```bash theme={null}
        git clone https://github.com/FireWeave-HQ/fireweave-sdk && cd fireweave-sdk
        (cd sdks/node && npm install && npm run build)
        # in your app:
        npm install ../fireweave-sdk/sdks/node/packages/sdk
        ```
      </Tab>

      <Tab title="Python">
        PyPI has `fireweave` **0.1.0** (verified 2026-08-17):

        ```bash theme={null}
        python -m venv .venv
        .venv/bin/pip install 'fireweave[openfeature]'
        ```

        Omit `[openfeature]` if you will not use the provider. Checkout if you need a specific commit:

        ```bash theme={null}
        git clone https://github.com/FireWeave-HQ/fireweave-sdk && cd fireweave-sdk
        python -m venv .venv
        .venv/bin/pip install -e 'sdks/python[openfeature]'
        ```
      </Tab>

      <Tab title="Go">
        No verified public module tag. In your app `go.mod`:

        ```
        require github.com/FireWeave-HQ/fireweave-sdk/sdks/go v0.0.0
        replace github.com/FireWeave-HQ/fireweave-sdk/sdks/go => ../fireweave-sdk/sdks/go
        ```

        Clone the repo so the `replace` path exists. Do not `go get` a published version as if it works.
      </Tab>

      <Tab title="Java">
        Not on Maven Central. Parent POM: do not publish.

        ```bash theme={null}
        git clone https://github.com/FireWeave-HQ/fireweave-sdk && cd fireweave-sdk/sdks/java
        mvn install
        ```

        ```xml theme={null}
        <dependency>
          <groupId>ai.fireweave</groupId>
          <artifactId>fireweave-sdk</artifactId>
          <version>0.1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
          <groupId>ai.fireweave</groupId>
          <artifactId>fireweave-testing</artifactId>
          <version>0.1.0-SNAPSHOT</version>
          <scope>test</scope>
        </dependency>
        ```

        Add `fireweave-openfeature` only if you use OpenFeature (`dev.openfeature:sdk` **1.15.1**).
      </Tab>

      <Tab title="Browser">
        `@fireweaveai/web-sdk` **2.1.0** is on npm (verified 2026-08-17; tarball `gitHead` `dfeb478`).

        ```bash theme={null}
        npm install @fireweaveai/web-sdk@2.1.0
        ```

        Add `@openfeature/web-sdk` only if you use the provider. Checkout if you need an unreleased tree:

        ```bash theme={null}
        git clone https://github.com/FireWeave-HQ/fireweave-sdk && cd fireweave-sdk
        (cd sdks/web && npm install && npm run build)
        # in your app:
        npm install ../fireweave-sdk/sdks/web/packages/sdk
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Configure">
    Construct an adapter and a runtime. In-memory fixtures use `type`, `enabled`, `value`, and `variant`.

    <Tabs>
      <Tab title="Node">
        ```js theme={null}
        import { FireweaveClient, FireweaveRuntime, InMemoryAdapter } from '@fireweaveai/sdk';

        const runtime = new FireweaveRuntime(new InMemoryAdapter({
          flags: {
            'new-checkout': { type: 'boolean', enabled: true, value: true, variant: 'on' },
          },
        }));
        const fireweave = new FireweaveClient(runtime);
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        from fireweave import FireweaveClient, FireweaveRuntime, InMemoryAdapter

        runtime = FireweaveRuntime(InMemoryAdapter({
            "new-checkout": {"type": "boolean", "enabled": True, "value": True, "variant": "on"},
        }))
        fireweave = FireweaveClient(runtime)
        ```
      </Tab>

      <Tab title="Go">
        ```go theme={null}
        adapter := inmemory.New(inmemory.WithFlags(map[string]inmemory.Flag{
            "new-checkout": {Type: fireweave.FlagTypeBoolean, Enabled: true, Value: true, Variant: "on"},
        }))
        runtime := fireweave.NewRuntime(adapter, fireweave.Config{})
        client := fireweave.NewClient(runtime)
        ```

        Imports: `…/sdks/go/fireweave` and `…/sdks/go/adapters/inmemory`.
      </Tab>

      <Tab title="Java">
        ```java theme={null}
        import ai.fireweave.sdk.FireweaveClient;
        import ai.fireweave.sdk.FireweaveConfig;
        import ai.fireweave.sdk.FireweaveRuntime;
        import ai.fireweave.testing.FlagDefinition;
        import ai.fireweave.testing.InMemoryAdapter;
        import com.fasterxml.jackson.databind.ObjectMapper;

        import java.util.Map;

        ObjectMapper mapper = new ObjectMapper();
        Map<String, FlagDefinition> flags = Map.of("new-checkout",
            FlagDefinition.fromJson(mapper.readTree(
                "{\"type\":\"boolean\",\"enabled\":true,\"variant\":\"on\",\"value\":true}")));
        FireweaveRuntime runtime = new FireweaveRuntime(
            FireweaveConfig.builder().build(), new InMemoryAdapter(flags));
        FireweaveClient fireweave = new FireweaveClient(runtime);
        ```

        Java does not read `FW_*` from the environment. Pass `FireweaveConfig` explicitly when you leave in-memory mode.
      </Tab>

      <Tab title="Browser">
        ```js theme={null}
        import {
          FireweaveWebClient,
          FireweaveWebRuntime,
          InMemoryWebAdapter,
        } from '@fireweaveai/web-sdk';

        const runtime = new FireweaveWebRuntime(new InMemoryWebAdapter({
          flags: {
            'new-checkout': { type: 'boolean', enabled: true, value: true, variant: 'on' },
          },
        }));
        const fireweave = new FireweaveWebClient(runtime);
        ```

        The web SDK never reads `process.env` or `import.meta.env`.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Initialize">
    Wait until the runtime is usable before treating values as live. After shutdown, evaluations return defaults with `AlreadyClosed`.

    <Tabs>
      <Tab title="Node">
        ```js theme={null}
        await fireweave.initialize();
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        fireweave.initialize()
        ```
      </Tab>

      <Tab title="Go">
        ```go theme={null}
        ctx := context.Background()
        if err := runtime.Initialize(ctx); err != nil {
            panic(err)
        }
        ```

        Every blocking Go call takes `context.Context`.
      </Tab>

      <Tab title="Java">
        ```java theme={null}
        runtime.initialize();
        ```
      </Tab>

      <Tab title="Browser">
        ```js theme={null}
        await fireweave.initialize();
        ```

        Initialize prefetches the decision cache. Reads after that are **synchronous**. If prefetch exceeds 5 seconds against a remote adapter, the runtime enters **STALE** and getters return defaults with reason `STALE`.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Register a target">
    Node, Python, and Web can register durable properties (`kind`, `properties`). The call **never throws** (Node returns `{ ok, error? }`). Go and Java have **no** `registerTarget` on `master` — skip this step and pass `targetingKey` on evaluate.

    <Warning>
      `InMemoryAdapter` / `InMemoryWebAdapter` do not implement registration. They return `UnsupportedCapability` so a harness does not look registered when it is not. The repo test-server stub also does **not** implement `POST /v1/targets/register`. This step is real against `FireweaveRemoteAdapter` (Node/Python/Web).
    </Warning>

    <Tabs>
      <Tab title="Node">
        ```js theme={null}
        const registered = await runtime.registerTarget('user_42', {
          kind: 'user',
          properties: { plan: 'pro', region: 'eu-west' },
        });
        if (!registered.ok) {
          console.warn('registerTarget failed', registered.error);
        }
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        from fireweave import RegisterTargetOptions

        result = runtime.register_target(
            "user_42",
            RegisterTargetOptions(kind="user", properties={"plan": "pro", "region": "eu-west"}),
        )
        if not result.ok:
            print("register_target failed", result.error)
        ```
      </Tab>

      <Tab title="Browser">
        ```js theme={null}
        const registered = await fireweave.identify('user_42', {
          kind: 'user',
          properties: { plan: 'pro', region: 'eu-west' },
        });
        ```

        `identify` calls `registerTarget` and then `setContext({ targetingKey })`. It is not OpenFeature `identify`.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Evaluate a control point">
    Pass a stable `targetingKey`. Do not invent an SDK helper named `fw.isOn` — that API does not exist.

    <Tabs>
      <Tab title="Node">
        ```js theme={null}
        const enabled = await fireweave.controlPoints.getBooleanValue('new-checkout', false, {
          targetingKey: 'user_42',
        });
        console.log('new-checkout:', enabled);
        ```

        Detailed Decision: `fireweave.controlPoints.evaluate('new-checkout', 'boolean', false, { targetingKey: 'user_42' })`. `client.flags` is the same object as `client.controlPoints` (deprecated alias; not removed in 2.x).
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        from fireweave import EvaluationContext

        enabled = fireweave.control_points.get_boolean_value(
            "new-checkout", False, EvaluationContext(targeting_key="user_42")
        )
        print("new-checkout:", enabled)
        ```

        `client.flags` is the same object as `client.control_points`.
      </Tab>

      <Tab title="Go">
        ```go theme={null}
        decision := client.Flags().Evaluate(
            ctx,
            "new-checkout",
            fireweave.FlagTypeBoolean,
            false,
            fireweave.NewEvaluationContext("user_42", nil),
            fireweave.EvaluateOptions{},
        )
        fmt.Println("new-checkout:", decision.Value)
        ```

        Go has no `controlPoints` namespace and no typed getters. `EvaluateOptions.SendExposure` defaults to false when left unset.
      </Tab>

      <Tab title="Java">
        ```java theme={null}
        import ai.fireweave.sdk.EvaluationContext;

        boolean enabled = fireweave.getBooleanValue(
            "new-checkout",
            false,
            EvaluationContext.builder().targetingKey("user_42").build());
        System.out.println("new-checkout: " + enabled);
        ```

        Native helpers are **boolean and string only**. Other types use `evaluate` or the OpenFeature provider. There is no `controlPoints` facade.
      </Tab>

      <Tab title="Browser">
        ```js theme={null}
        const enabled = fireweave.controlPoints.getBooleanValue('new-checkout', false, {
          targetingKey: 'user_42',
        });
        console.log('new-checkout:', enabled);
        ```

        No `await` — web getters are synchronous cache reads.
      </Tab>
    </Tabs>

    <Note>
      Evaluate-path exposures are **off** unless you pass `sendExposure: true` (or the language equivalent). Default is **false** in every language.
    </Note>
  </Step>

  <Step title="Record an outcome">
    Optional for a first run; this completes the release-safety loop. `recordOutcome` is a **signal** (kind `outcome`), not `releases.complete`.

    <Tabs>
      <Tab title="Node">
        ```js theme={null}
        fireweave.signals.recordOutcome({ name: 'checkout', status: 'completed' });
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        fireweave.signals.record_outcome("checkout", "completed")
        ```
      </Tab>

      <Tab title="Go">
        ```go theme={null}
        _ = client.Signals().RecordOutcome(ctx, fireweave.OutcomeSignal{
            Name:   "checkout",
            Status: "completed",
        })
        ```

        Go extensions return `error`, not a result struct.
      </Tab>

      <Tab title="Java">
        ```java theme={null}
        fireweave.signals().recordOutcome("checkout", "completed");
        ```
      </Tab>

      <Tab title="Browser">
        ```js theme={null}
        fireweave.signals.recordOutcome({ name: 'checkout', status: 'completed' });
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Shut down">
    One runtime, one shutdown. Node, Python, and Web flush exposures inside `shutdown`. Java does **not**.

    <Tabs>
      <Tab title="Node">
        ```js theme={null}
        await fireweave.shutdown();
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        fireweave.shutdown()
        ```

        Never raises; idempotent. You can also use `with FireweaveClient(runtime) as fireweave:`.
      </Tab>

      <Tab title="Go">
        ```go theme={null}
        _ = client.Exposures().Flush(ctx)
        _ = runtime.Shutdown(ctx)
        ```
      </Tab>

      <Tab title="Java">
        ```java theme={null}
        fireweave.exposures().flush();
        fireweave.close(); // does not flush
        ```
      </Tab>

      <Tab title="Browser">
        ```js theme={null}
        await fireweave.shutdown();
        ```

        The client also flushes on `visibilitychange` → hidden and `pagehide` (`keepalive` / `sendBeacon`) unless you disable unload flush.
      </Tab>
    </Tabs>
  </Step>
</Steps>

## Production (remote adapter)

Swap the in-memory adapter for `FireweaveRemoteAdapter` (Web: `FireweaveRemoteWebAdapter`). Auth is `Authorization: Bearer <key>`. Use a FireWeave project key (`project-api-key_…`), not a PostHog `phc_` / `phs_` / `phx_` key.

<Tabs>
  <Tab title="Node">
    Reads `FW_API_URL` and `FW_PROJECT_API_KEY` when omitted.

    ```js theme={null}
    import { FireweaveClient, FireweaveRemoteAdapter, FireweaveRuntime } from '@fireweaveai/sdk';

    const runtime = new FireweaveRuntime(new FireweaveRemoteAdapter({
      apiUrl: process.env.FW_API_URL,
      apiKey: process.env.FW_PROJECT_API_KEY,
    }));
    const fireweave = new FireweaveClient(runtime);
    await fireweave.initialize();
    ```

    `https` is required off-loopback; `http` is loopback-only.
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from fireweave import FireweaveClient, FireweaveRemoteAdapter, FireweaveRuntime

    runtime = FireweaveRuntime(FireweaveRemoteAdapter())  # FW_API_URL, FW_PROJECT_API_KEY
    fireweave = FireweaveClient(runtime)
    fireweave.initialize()
    ```

    Or pass `api_url=` / `api_key=` on `FireweaveRemoteAdapter`.
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    adapter := remote.New(remote.Config{}) // FW_API_URL, FW_PROJECT_API_KEY
    runtime := fireweave.NewRuntime(adapter, fireweave.Config{})
    client := fireweave.NewClient(runtime)
    if err := runtime.Initialize(ctx); err != nil {
        panic(err)
    }
    ```

    Import `…/sdks/go/adapters/remote`.
  </Tab>

  <Tab title="Java">
    Java does **not** call `System.getenv`. Set `host` and `projectApiKey` on `FireweaveConfig`. Default `allowedHosts` is PostHog hosts + loopback — add your fw-server hostname or initialize will fail configuration checks.

    ```java theme={null}
    import ai.fireweave.sdk.FireweaveRemoteAdapter;

    import java.util.Set;

    FireweaveConfig config = FireweaveConfig.builder()
        .host(apiUrl)
        .projectApiKey(apiKey)
        .allowedHosts(Set.of(/* fw-server hostname */, "localhost", "127.0.0.1"))
        .build();
    FireweaveRuntime runtime = new FireweaveRuntime(config, new FireweaveRemoteAdapter());
    runtime.initialize();
    ```
  </Tab>

  <Tab title="Browser">
    `apiUrl` and `apiKey` are **required constructor fields**. Do not embed an `attest:write` project key in a browser bundle.

    ```js theme={null}
    import {
      FireweaveRemoteWebAdapter,
      FireweaveWebClient,
      FireweaveWebRuntime,
    } from '@fireweaveai/web-sdk';

    const runtime = new FireweaveWebRuntime(
      new FireweaveRemoteWebAdapter({ apiUrl, apiKey }),
    );
    const fireweave = new FireweaveWebClient(runtime);
    await fireweave.initialize();
    ```

    Spec/ADR-0009 describe a scoped `fw_public_…` browser key family. Whether fw-server already issues those keys is **not verified** from the SDK repo.
  </Tab>
</Tabs>

Which hostname is the customer-facing fw-server URL is **not verified** from the SDK repository. Set `FW_API_URL` / `apiUrl` / Java `host` from your project.

## OpenFeature

You can evaluate through OpenFeature instead of (or in addition to) `FireweaveClient`. Extensions stay on the FireWeave client. Providers exist in all five packages. Tracking (OF spec §6) is not implemented.

Node example (wait for READY; `lazyReady` defaults **true** on `FireweaveProvider`):

```js theme={null}
import { OpenFeature } from '@openfeature/server-sdk';
import { FireweaveProvider, FireweaveRuntime, InMemoryAdapter } from '@fireweaveai/sdk';

const runtime = new FireweaveRuntime(new InMemoryAdapter({
  flags: { 'new-checkout': { type: 'boolean', enabled: true, value: true, variant: 'on' } },
}));
await OpenFeature.setProviderAndWait(new FireweaveProvider(runtime, { lazyReady: false }));
const enabled = await OpenFeature.getClient().getBooleanValue('new-checkout', false, {
  targetingKey: 'user_42',
});
await OpenFeature.close();
```

Per-language providers and resolver tables: [OpenFeature](/openfeature).

## Next steps

* Language surfaces: [Node](/sdks/node), [Python](/sdks/python), [Go](/sdks/go), [Java](/sdks/java), [Browser](/sdks/web), [Compatibility](/sdks/compatibility)
* Concepts: [Control points](/concepts/control-points), [Targeting](/concepts/targeting), [Exposures](/concepts/exposures), [Signals](/concepts/signals), [Adapters](/concepts/adapters)
* Production: [Configuration](/production/configuration), [Lifecycle](/production/lifecycle), [Errors](/production/errors)
* Tests without a network: [Testing](/testing)
* If a getter always returns the default: [Troubleshooting](/troubleshooting)
