Claude-skill-registry bknd-env-config

Use when configuring environment variables for Bknd projects. Covers .env files, secrets management, env injection in config, platform-specific variables, and production security.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/bknd-env-config" ~/.claude/skills/majiayu000-claude-skill-registry-bknd-env-config && rm -rf "$T"
manifest: skills/data/bknd-env-config/SKILL.md
source content

Environment Variables Configuration

Configure environment variables for Bknd applications across development and production.

Prerequisites

  • Bknd project initialized (
    bknd.config.ts
    exists)
  • Understanding of your deployment target (local, Cloudflare, Vercel, etc.)

When to Use UI Mode

  • Viewing current config via admin panel
  • N/A for environment variables - all done via code/files

When to Use Code Mode

  • Creating
    .env
    files
  • Configuring secrets in
    bknd.config.ts
  • Setting up platform-specific env vars
  • All environment configuration tasks

Code Approach

Step 1: Create .env File

Create

.env
in project root:

# Database
DB_URL=file:data.db
DB_TOKEN=

# Auth
JWT_SECRET=your-secret-here-min-32-chars

# Server
PORT=3000

# Development
LOCAL=true

Step 2: Inject Env in Config

Access env vars via the

env
parameter in
bknd.config.ts
:

import type { CliBkndConfig } from "bknd";

export default {
  app: (env) => ({
    connection: {
      url: env.DB_URL ?? "file:data.db",
      authToken: env.DB_TOKEN,
    },
    auth: {
      jwt: {
        secret: env.JWT_SECRET ?? "dev-secret-change-in-prod",
      },
    },
  }),
} satisfies CliBkndConfig;

The

env
parameter receives all environment variables loaded from
.env
files and system environment.

Step 3: Use .dev.vars for Dev Overrides (Optional)

Bknd loads env files in order (later takes precedence):

  1. .env
    - Base configuration
  2. .dev.vars
    - Development-specific overrides (Cloudflare style)

Create

.dev.vars
for local dev overrides:

# .dev.vars - Dev-only, overrides .env
DB_URL=:memory:
JWT_SECRET=dev-only-secret

Common Environment Variables

Database

VariableDescriptionExample
DB_URL
Database connection URL
file:data.db
,
libsql://db.turso.io
DB_TOKEN
LibSQL/Turso auth token
eyJhbGciOiJFZERTQSIs...

Authentication

VariableDescriptionExample
JWT_SECRET
JWT signing secret (min 32 chars)
your-very-long-secret-key-here
GOOGLE_CLIENT_ID
Google OAuth client ID
123456.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET
Google OAuth secret
GOCSPX-xxx
GITHUB_CLIENT_ID
GitHub OAuth client ID
Iv1.abc123
GITHUB_CLIENT_SECRET
GitHub OAuth secret
secret_xxx

Media/Storage

VariableDescriptionExample
S3_ACCESS_KEY
S3/R2 access key
AKIAIOSFODNN7EXAMPLE
S3_SECRET_KEY
S3/R2 secret key
wJalrXUtnFEMI/K7MDENG/...
S3_ENDPOINT
S3-compatible endpoint
https://bucket.s3.region.amazonaws.com
CLOUDINARY_CLOUD_NAME
Cloudinary cloud name
my-cloud
CLOUDINARY_API_KEY
Cloudinary API key
123456789012345
CLOUDINARY_API_SECRET
Cloudinary API secret
abcdefghijk...

Server

VariableDescriptionDefault
PORT
Server port
3000
LOCAL
Disable telemetry-
NODE_ENV
/
ENVIRONMENT
Environment mode
development

Complete Configuration Example

import type { CliBkndConfig } from "bknd";
import { em, entity, text } from "bknd";

const schema = em({
  posts: entity("posts", { title: text().required() }),
});

export default {
  app: (env) => ({
    // Database
    connection: {
      url: env.DB_URL ?? "file:data.db",
      authToken: env.DB_TOKEN,
    },

    // Production flag
    isProduction: env.NODE_ENV === "production",

    // Pass all secrets to app
    secrets: env,
  }),

  config: {
    data: schema.toJSON(),

    // Auth with env-based secrets
    auth: {
      enabled: true,
      jwt: {
        secret: env.JWT_SECRET,
        issuer: "my-app",
      },
      strategies: {
        password: { enabled: true },
        google: env.GOOGLE_CLIENT_ID ? {
          config: {
            name: "google",
            type: "oidc",
            client: {
              client_id: env.GOOGLE_CLIENT_ID,
              client_secret: env.GOOGLE_CLIENT_SECRET,
            },
          },
        } : undefined,
      },
    },

    // Media with env-based adapter config
    media: {
      enabled: true,
      adapter: {
        type: "s3",
        config: {
          access_key: env.S3_ACCESS_KEY,
          secret_access_key: env.S3_SECRET_KEY,
          url: env.S3_ENDPOINT,
        },
      },
    },
  },
} satisfies CliBkndConfig;

Platform-Specific Configuration

Cloudflare Workers/Pages

Use

wrangler.toml
for non-secret vars and dashboard for secrets:

# wrangler.toml
[vars]
ENVIRONMENT = "production"

Set secrets via CLI:

npx wrangler secret put JWT_SECRET
npx wrangler secret put DB_TOKEN

Access in config:

import type { CloudflareBkndConfig } from "bknd/adapter/cloudflare";

export default {
  app: (env) => ({
    connection: env.DB,  // D1 binding
    isProduction: env.ENVIRONMENT === "production",
    secrets: env,
  }),
} satisfies CloudflareBkndConfig;

Vercel

Use Vercel dashboard or CLI for env vars:

vercel env add JWT_SECRET production
vercel env add DB_URL production

Or

.env.local
for local development (auto-loaded by Next.js):

# .env.local
DB_URL=file:data.db
JWT_SECRET=dev-secret

Docker

Pass via docker-compose or

-e
flag:

# docker-compose.yml
services:
  app:
    environment:
      - DB_URL=file:/data/app.db
      - JWT_SECRET=${JWT_SECRET}
    env_file:
      - .env.production

Generate .env Template

Use CLI to generate env template from your config:

# Output required secrets as template
npx bknd secrets --template --format env

# Save to file
npx bknd secrets --template --format env --out .env.example

This creates a template without actual values, safe for version control.

SyncSecrets Option

Auto-generate

.env.example
on config changes:

export default {
  syncSecrets: {
    enabled: true,
    outFile: ".env.example",
    format: "env",  // or "json"
  },
  app: (env) => ({ ... }),
} satisfies CliBkndConfig;

Environment-Based Feature Flags

Conditionally enable features based on environment:

export default {
  app: (env) => ({
    connection: { url: env.DB_URL ?? "file:data.db" },
  }),
  config: {
    auth: {
      enabled: true,
      // Only enable OAuth in production (requires secrets)
      strategies: {
        password: { enabled: true },
        google: env.GOOGLE_CLIENT_ID ? {
          config: {
            name: "google",
            type: "oidc",
            client: {
              client_id: env.GOOGLE_CLIENT_ID,
              client_secret: env.GOOGLE_CLIENT_SECRET,
            },
          },
        } : undefined,
      },
    },
    // Only enable S3 media in production
    media: env.S3_ACCESS_KEY ? {
      enabled: true,
      adapter: {
        type: "s3",
        config: {
          access_key: env.S3_ACCESS_KEY,
          secret_access_key: env.S3_SECRET_KEY,
          url: env.S3_ENDPOINT,
        },
      },
    } : {
      enabled: false,
    },
  },
} satisfies CliBkndConfig;

Database Connection Priority

Bknd resolves database connection in order:

  1. --db-url
    CLI argument
  2. Config file
    connection.url
  3. --memory
    flag (uses
    :memory:
    )
  4. DB_URL
    environment variable
  5. Fallback:
    file:data.db

Verification

Check env loading:

# Server logs show connection source
npx bknd run
# Look for: "Using connection from ..."

Test env injection:

// Temporarily log env in config
app: (env) => {
  console.log("Loaded env:", Object.keys(env));
  return { ... };
},

Verify secrets command:

npx bknd secrets --template

Common Pitfalls

.env Not Loading

Problem: Env vars undefined in config

Fix: Check file location and format:

# .env must be in project root (same level as bknd.config.ts)
ls -la .env

# No quotes around values
DB_URL=file:data.db     # Correct
DB_URL="file:data.db"   # May cause issues

JWT_SECRET Too Short

Problem: Auth fails or warning about weak secret

Fix: Use minimum 32 characters:

# Generate secure secret
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# or
openssl rand -hex 32

Secrets in Version Control

Problem: Committed

.env
with real secrets

Fix:

# Add to .gitignore
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo ".dev.vars" >> .gitignore

# Remove from git history if committed
git rm --cached .env

Platform Env Not Available

Problem:

env.VAR
is undefined in deployed app

Fix: Platform-specific setup:

  • Vercel: Add via dashboard or
    vercel env add
  • Cloudflare: Add via
    wrangler secret put
    or dashboard
  • Docker: Check
    environment:
    or
    env_file:
    in compose

Wrong Fallback in Production

Problem: Using dev defaults in production

Fix: Fail fast instead of fallback:

app: (env) => {
  if (!env.JWT_SECRET && env.NODE_ENV === "production") {
    throw new Error("JWT_SECRET required in production");
  }
  return {
    auth: {
      jwt: { secret: env.JWT_SECRET ?? "dev-only" },
    },
  };
},

DOs and DON'Ts

DO:

  • Use
    .env.example
    as template (no real values)
  • Generate JWT_SECRET with crypto-safe randomness
  • Use platform-specific secret management in production
  • Validate required secrets on app start
  • Use
    syncSecrets
    to keep
    .env.example
    updated

DON'T:

  • Commit
    .env
    with real secrets
  • Use weak or short JWT secrets
  • Hardcode secrets in config files
  • Use same secrets across environments
  • Log env vars containing secrets

Related Skills

  • bknd-local-setup - Initial project setup
  • bknd-setup-auth - Configure authentication
  • bknd-oauth-setup - OAuth provider configuration
  • bknd-storage-config - Storage adapter configuration
  • bknd-production-config - Production configuration
  • bknd-deploy-hosting - Deployment options