Documentation Index
Fetch the complete documentation index at: https://docs.barekey.dev/llms.txt
Use this file to discover all available pages before exploring further.
Use the Barekey SDK when your code should read variables directly instead of shelling out to the CLI or depending on a pulled .env file.
For most server-side code, import from @barekey/sdk/server.
Install
Package entrypoints
| Import | Use for |
|---|
@barekey/sdk/server | server-side reads, CLI-session auth, API-backed mode, standalone .env mode, and typegen |
@barekey/sdk/public | public variables only, including browser-facing reads |
@barekey/sdk | combined root export, but the explicit subpaths are clearer in app code |
The simplest server client
import { BarekeyClient } from "@barekey/sdk/server";
export const barekey = new BarekeyClient();
This works when:
barekey.json can be found in the current directory or a parent directory
- the runtime can authenticate with either
BAREKEY_ACCESS_TOKEN or a stored CLI login
How the server SDK resolves configuration
BarekeyClient supports three configuration styles.
Option 1: rely on barekey.json
const client = new BarekeyClient();
This is usually the best local-development setup.
Option 2: pass the scope explicitly
const client = new BarekeyClient({
organization: "acme",
project: "web",
environment: "production",
});
Option 3: pass a json object
const client = new BarekeyClient({
json: {
organization: "acme",
project: "web",
environment: "production",
},
});
You must provide either:
- all of
organization, project, and environment
- or
json
- or nothing, in which case Barekey tries to load
barekey.json
Do not mix explicit fields with json.
barekey.json reference
This file is used by both the SDK and CLI.
{
"$schema": "./node_modules/@barekey/sdk/dist/barekey.schema.json",
"organization": "acme",
"project": "web",
"environment": "development",
"config": {
"mode": "centralized",
"typegen": "semantic",
"disallow_ambigious_keys": true
}
}
Top-level keys
| Key | Type | Required | Notes |
|---|
organization | string | Yes in centralized mode | Canonical organization key |
project | string | Yes in centralized mode | Project slug |
environment | string | Yes in centralized mode | Stage name |
org | string | No | Alias for organization |
stage | string | No | Alias for environment |
$schema | string | No | JSON Schema path for editor autocomplete and validation |
config | object | No | Extra SDK and runtime behavior |
config keys
| Key | Allowed values | Default | What it does |
|---|
config.mode | "centralized" or "standalone" | "centralized" | Chooses API-backed Barekey mode or local .env file mode |
config.typegen | "semantic" or "minimal" | "semantic" in centralized mode, "minimal" in standalone mode | Controls the style of generated SDK types |
config.disallow_ambigious_keys | boolean | true | Makes non-generated keys a type error unless you opt out |
Search behavior
The SDK and CLI search for barekey.json starting from the current working directory and walking upward through parent directories.
That means one repo-level file can cover a whole monorepo.
How auth is resolved
In centralized mode, BarekeyClient resolves credentials in this order:
BAREKEY_ACCESS_TOKEN
- a stored CLI session created by
barekey login
BAREKEY_ACCESS_TOKEN
If BAREKEY_ACCESS_TOKEN is set, the SDK uses it directly.
Optional:
BAREKEY_API_URL overrides the default API base URL
Example:
export BAREKEY_ACCESS_TOKEN="bk_at_..."
export BAREKEY_API_URL="https://api.barekey.dev"
This is the best fit for production deploys and CI.
CLI session fallback
If BAREKEY_ACCESS_TOKEN is not set, the SDK tries to reuse the local CLI login. That is convenient for local development because you can log in once with:
Then your app can usually run without extra token wiring.
Reading values
get() returns a promise-like handle.
If the variable has a known generated type, await returns the parsed value directly.
const url = await barekey.get("DATABASE_URL");
const featureEnabled = await barekey.get("FEATURE_ENABLED");
Batch reads preserve input order:
const [databaseUrl, redisUrl] = await barekey.get([
"DATABASE_URL",
"REDIS_URL",
] as const);
const result = await barekey.get("DATABASE_URL").inspect();
result.value;
result.rawValue;
result.name;
result.kind;
result.declaredType;
result.visibility;
result.decision;
result.selectedArm;
This is especially useful for:
- debugging
- logging resolution metadata
- checking
ab_roll decisions
Dynamic reads and caching
get() accepts BarekeyGetOptions:
type BarekeyGetOptions = {
dynamic?: true | { ttl: number | Date | { epochMilliseconds: number } };
seed?: string;
key?: string;
};
dynamic
Use dynamic when a value should be refreshed instead of coming from the static definition cache.
const value = await barekey.get("PUBLIC_MESSAGE", {
dynamic: true,
});
With a TTL:
const value = await barekey.get("PUBLIC_MESSAGE", {
dynamic: { ttl: 5_000 },
});
ttl means:
- numbers are milliseconds
Date uses its timestamp
- objects with
epochMilliseconds are also accepted
seed and key
Use these for deterministic ab_roll evaluation:
const checkoutVariant = await barekey.get("CHECKOUT_FLOW", {
seed: user.id,
key: "checkout-v1",
});
The same seed and key pair gives the same result for the same variable.
Typegen
Barekey can write generated types into your installed @barekey/sdk package.
Run:
Then known keys become typed in the SDK.
Typegen modes
semantic
This is the centralized default.
It preserves Barekey metadata in the generated types, including:
- kind
- visibility
- rollout state
minimal
This is the standalone default.
It only generates the resolved value type, such as string, boolean, or an inferred object shape from local .env files.
SDK-side typegen refresh
BarekeyClient also accepts:
const client = new BarekeyClient({
typegen: false,
});
Or:
const client = new BarekeyClient({
typegen: {
ttl: 5_000,
},
});
Notes:
- automatic typegen refresh only runs in
development
- it only applies when filesystem access is available
- set
typegen: false to disable it
Requirements validation
You can validate the resolved configuration against any Standard Schema v1 validator before reads proceed.
Pass the schema directly. You do not need to wrap ~standard yourself.
import { z } from "zod";
import { BarekeyClient } from "@barekey/sdk/server";
const client = new BarekeyClient({
requirements: z.object({
DATABASE_URL: z.string().url(),
STRIPE_SECRET_KEY: z.string().min(1),
DEBUG_MODE: z.boolean(),
}),
});
This works with any library that exposes Standard Schema v1 metadata, including Zod and ArkType-compatible schemas.
Use this when you want startup-time guarantees that your resolved Barekey values form a valid config object.
Standalone mode
Standalone mode makes the server SDK read local .env* files instead of calling the Barekey API.
Set:
{
"config": {
"mode": "standalone"
}
}
In this mode:
- filesystem access is required
organization, project, and environment may be omitted
typegen becomes minimal
- values are inferred from local
.env content
This is useful when you want one SDK API for local and centralized setups.
Read more in Local development.
Public client
Use PublicBarekeyClient for public variables:
import { PublicBarekeyClient } from "@barekey/sdk/public";
const client = new PublicBarekeyClient({
organization: "acme",
project: "web",
environment: "production",
});
const title = await client.get("PUBLIC_TITLE");
React .tsx
Use @barekey/react when you want public values to feel like normal React data reads.
Install it alongside the SDK:
import { PublicBarekeyClient } from "@barekey/sdk/public";
import { BarekeyProvider, useBarekey } from "@barekey/react";
import barekeyConfig from "../barekey.json";
const client = new PublicBarekeyClient({
json: barekeyConfig,
});
function HeroBanner() {
const env = useBarekey();
const [title, tagline] = env.get([
"PUBLIC_TITLE",
"PUBLIC_TAGLINE",
] as const);
return (
<section>
<h1>{title}</h1>
<p>{tagline}</p>
</section>
);
}
export function App() {
return (
<BarekeyProvider client={client}>
<HeroBanner />
</BarekeyProvider>
);
}
You can also pass a public client directly to useBarekey() when you want to bootstrap values before rendering a provider tree:
import { PublicBarekeyClient } from "@barekey/sdk/public";
import { useBarekey } from "@barekey/react";
import barekeyConfig from "../barekey.json";
function Bootstrap() {
const env = useBarekey(new PublicBarekeyClient({
json: barekeyConfig,
}));
return <div>{env.get("PUBLIC_TITLE")}</div>;
}
Important public-client rules
- it only reads public variables
- it does not use CLI auth
- it supports
baseUrl
- it does not support standalone mode
If config.mode is "standalone", PublicBarekeyClient throws.
Errors you should expect
The SDK throws BarekeyError subclasses. Common ones:
| Error code | Meaning |
|---|
NO_CONFIGURATION_PROVIDED | Barekey could not find explicit config or a barekey.json file |
INVALID_CONFIGURATION_PROVIDED | Config shape is incomplete or invalid |
NO_CREDENTIALS_PROVIDED | No access token and no CLI session were available |
INVALID_CREDENTIALS_PROVIDED | The stored or provided credentials are invalid |
VARIABLE_NOT_FOUND | The named variable does not exist |
UNAUTHORIZED | The API rejected the current token |
REQUIREMENTS_VALIDATION_FAILED | Your requirements schema rejected the resolved config |
Example:
import { BarekeyError } from "@barekey/sdk/server";
try {
const value = await barekey.get("DATABASE_URL");
} catch (error) {
if (error instanceof BarekeyError) {
console.error(error.code, error.requestId);
}
throw error;
}
Practical patterns
- Create one shared client module and import it where needed.
- Use CLI-session auth locally and
BAREKEY_ACCESS_TOKEN in production.
- Run
barekey typegen after config changes.
- Use
inspect() when you need metadata, not just the value.
- Use standalone mode only on the server side.
For CLI-driven local workflows, read CLI and Local development.