bknd-production-config
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseConfigure for Production
生产环境配置
Prepare and secure your Bknd application for production deployment.
为你的Bknd应用做好生产部署的准备并完成安全配置。
Prerequisites
前置条件
- Working Bknd application tested locally
- Database provisioned (see )
bknd-database-provision - Hosting platform selected (see )
bknd-deploy-hosting
- 已在本地测试通过的可用Bknd应用
- 已配置好数据库(参考)
bknd-database-provision - 已选定托管平台(参考)
bknd-deploy-hosting
When to Use UI Mode
何时使用UI模式
- Viewing current configuration in admin panel
- Verifying Guard settings are active
- Checking auth configuration
- 在管理面板中查看当前配置
- 验证Guard设置是否已激活
- 检查身份验证配置
When to Use Code Mode
何时使用代码模式
- All production configuration changes
- Setting environment variables
- Configuring security settings
- Setting up adapters
- 所有生产环境配置变更
- 设置环境变量
- 配置安全设置
- 设置适配器
Code Approach
代码配置方法
Step 1: Enable Production Mode
步骤1:启用生产模式
Set to disable development features:
isProduction: truetypescript
// bknd.config.ts
export default {
app: (env) => ({
connection: { url: env.DB_URL },
isProduction: true, // or env.NODE_ENV === "production"
}),
};What does:
isProduction: true- Disables schema auto-sync (prevents accidental migrations)
- Hides detailed error messages from API responses
- Disables admin panel modifications (read-only)
- Enables stricter security defaults
设置以禁用开发环境特性:
isProduction: truetypescript
// bknd.config.ts
export default {
app: (env) => ({
connection: { url: env.DB_URL },
isProduction: true, // or env.NODE_ENV === "production"
}),
};isProduction: true- 禁用架构自动同步(防止意外迁移)
- 从API响应中隐藏详细错误信息
- 禁用管理面板的修改功能(只读模式)
- 启用更严格的安全默认设置
Step 2: Configure JWT Authentication
步骤2:配置JWT身份验证
Critical: Never use default or weak JWT secrets in production.
typescript
export default {
app: (env) => ({
connection: { url: env.DB_URL },
isProduction: true,
auth: {
jwt: {
secret: env.JWT_SECRET, // Required, min 32 chars
alg: "HS256", // Or "HS384", "HS512"
expires: "7d", // Token lifetime
issuer: "my-app", // Optional, identifies token source
fields: ["id", "email", "role"], // Claims in token
},
cookie: {
httpOnly: true, // Prevent XSS access
secure: true, // HTTPS only
sameSite: "strict", // CSRF protection
expires: 604800, // 7 days in seconds
},
},
}),
};Generate secure secret:
bash
undefined**关键注意事项:**生产环境中绝不要使用默认或弱JWT密钥。
typescript
export default {
app: (env) => ({
connection: { url: env.DB_URL },
isProduction: true,
auth: {
jwt: {
secret: env.JWT_SECRET, // 必填,至少32个字符
alg: "HS256", // 或"HS384"、"HS512"
expires: "7d", // 令牌有效期
issuer: "my-app", // 可选,标识令牌来源
fields: ["id", "email", "role"], // 令牌中的声明字段
},
cookie: {
httpOnly: true, // 防止XSS攻击获取
secure: true, // 仅在HTTPS下生效
sameSite: "strict", // 防止CSRF攻击
expires: 604800, // 7天,单位为秒
},
},
}),
};生成安全密钥:
bash
undefinedNode.js
Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
OpenSSL
OpenSSL
openssl rand -hex 32
undefinedopenssl rand -hex 32
undefinedStep 3: Enable Guard (Authorization)
步骤3:启用Guard(授权)
typescript
export default {
app: (env) => ({
connection: { url: env.DB_URL },
isProduction: true,
config: {
guard: {
enabled: true, // Enforce all permissions
},
},
}),
};Without Guard enabled, all authenticated users have full access.
typescript
export default {
app: (env) => ({
connection: { url: env.DB_URL },
isProduction: true,
config: {
guard: {
enabled: true, // 强制执行所有权限规则
},
},
}),
};如果未启用Guard,所有已认证用户将拥有完全访问权限。
Step 4: Configure CORS
步骤4:配置CORS
typescript
export default {
app: (env) => ({
// ...
config: {
server: {
cors: {
origin: env.ALLOWED_ORIGINS?.split(",") ?? ["https://myapp.com"],
credentials: true, // Allow cookies
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
},
},
},
}),
};typescript
export default {
app: (env) => ({
// ...
config: {
server: {
cors: {
origin: env.ALLOWED_ORIGINS?.split(",") ?? ["https://myapp.com"],
credentials: true, // 允许携带Cookie
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
},
},
},
}),
};Step 5: Configure Media Storage
步骤5:配置媒体存储
Never use local storage in production serverless. Use cloud providers:
typescript
// AWS S3
export default {
app: (env) => ({
// ...
config: {
media: {
enabled: true,
body_max_size: 10 * 1024 * 1024, // 10MB max upload
adapter: {
type: "s3",
config: {
bucket: env.S3_BUCKET,
region: env.S3_REGION,
accessKeyId: env.S3_ACCESS_KEY,
secretAccessKey: env.S3_SECRET_KEY,
},
},
},
},
}),
};
// Cloudflare R2
config: {
media: {
adapter: {
type: "r2",
config: { bucket: env.R2_BUCKET },
},
},
}
// Cloudinary
config: {
media: {
adapter: {
type: "cloudinary",
config: {
cloudName: env.CLOUDINARY_CLOUD,
apiKey: env.CLOUDINARY_KEY,
apiSecret: env.CLOUDINARY_SECRET,
},
},
},
}**重要提示:**在无服务器生产环境中绝不要使用本地存储。请使用云存储提供商:
typescript
// AWS S3
export default {
app: (env) => ({
// ...
config: {
media: {
enabled: true,
body_max_size: 10 * 1024 * 1024, // 最大上传大小10MB
adapter: {
type: "s3",
config: {
bucket: env.S3_BUCKET,
region: env.S3_REGION,
accessKeyId: env.S3_ACCESS_KEY,
secretAccessKey: env.S3_SECRET_KEY,
},
},
},
},
}),
};
// Cloudflare R2
config: {
media: {
adapter: {
type: "r2",
config: { bucket: env.R2_BUCKET },
},
},
}
// Cloudinary
config: {
media: {
adapter: {
type: "cloudinary",
config: {
cloudName: env.CLOUDINARY_CLOUD,
apiKey: env.CLOUDINARY_KEY,
apiSecret: env.CLOUDINARY_SECRET,
},
},
},
}Complete Production Configuration
完整生产环境配置
typescript
// bknd.config.ts
import type { CliBkndConfig } from "bknd";
import { em, entity, text, relation, enumm } from "bknd";
const schema = em(
{
users: entity("users", {
email: text().required().unique(),
name: text(),
role: enumm(["admin", "user"]).default("user"),
}),
posts: entity("posts", {
title: text().required(),
content: text(),
published: enumm(["draft", "published"]).default("draft"),
}),
},
({ users, posts }) => ({
post_author: relation(posts, users), // posts.author_id -> users
})
);
type Database = (typeof schema)["DB"];
declare module "bknd" {
interface DB extends Database {}
}
export default {
app: (env) => ({
// Database
connection: {
url: env.DB_URL,
authToken: env.DB_TOKEN,
},
// Schema
schema,
// Production mode
isProduction: env.NODE_ENV === "production",
// Authentication
auth: {
enabled: true,
jwt: {
secret: env.JWT_SECRET,
alg: "HS256",
expires: "7d",
fields: ["id", "email", "role"],
},
cookie: {
httpOnly: true,
secure: env.NODE_ENV === "production",
sameSite: "strict",
expires: 604800,
},
strategies: {
password: {
enabled: true,
hashing: "bcrypt",
rounds: 12,
minLength: 8,
},
},
allow_register: true,
default_role_register: "user",
},
// Authorization
config: {
guard: {
enabled: true,
},
roles: {
admin: {
implicit_allow: true, // Full access
},
user: {
implicit_allow: false,
permissions: [
"data.posts.read",
{
permission: "data.posts.create",
effect: "allow",
},
{
permission: "data.posts.update",
effect: "filter",
condition: { author_id: "@user.id" },
},
{
permission: "data.posts.delete",
effect: "filter",
condition: { author_id: "@user.id" },
},
],
},
anonymous: {
implicit_allow: false,
is_default: true, // Unauthenticated users
permissions: [
{
permission: "data.posts.read",
effect: "filter",
condition: { published: "published" },
},
],
},
},
// Media storage
media: {
enabled: true,
body_max_size: 10 * 1024 * 1024,
adapter: {
type: "s3",
config: {
bucket: env.S3_BUCKET,
region: env.S3_REGION,
accessKeyId: env.S3_ACCESS_KEY,
secretAccessKey: env.S3_SECRET_KEY,
},
},
},
// CORS
server: {
cors: {
origin: env.ALLOWED_ORIGINS?.split(",") ?? [],
credentials: true,
},
},
},
}),
} satisfies CliBkndConfig;typescript
// bknd.config.ts
import type { CliBkndConfig } from "bknd";
import { em, entity, text, relation, enumm } from "bknd";
const schema = em(
{
users: entity("users", {
email: text().required().unique(),
name: text(),
role: enumm(["admin", "user"]).default("user"),
}),
posts: entity("posts", {
title: text().required(),
content: text(),
published: enumm(["draft", "published"]).default("draft"),
}),
},
({ users, posts }) => ({
post_author: relation(posts, users), // posts.author_id -> users
})
);
type Database = (typeof schema)["DB"];
declare module "bknd" {
interface DB extends Database {}
}
export default {
app: (env) => ({
// 数据库配置
connection: {
url: env.DB_URL,
authToken: env.DB_TOKEN,
},
// 数据架构
schema,
// 生产模式
isProduction: env.NODE_ENV === "production",
// 身份验证
auth: {
enabled: true,
jwt: {
secret: env.JWT_SECRET,
alg: "HS256",
expires: "7d",
fields: ["id", "email", "role"],
},
cookie: {
httpOnly: true,
secure: env.NODE_ENV === "production",
sameSite: "strict",
expires: 604800,
},
strategies: {
password: {
enabled: true,
hashing: "bcrypt",
rounds: 12,
minLength: 8,
},
},
allow_register: true,
default_role_register: "user",
},
// 授权配置
config: {
guard: {
enabled: true,
},
roles: {
admin: {
implicit_allow: true, // 完全访问权限
},
user: {
implicit_allow: false,
permissions: [
"data.posts.read",
{
permission: "data.posts.create",
effect: "allow",
},
{
permission: "data.posts.update",
effect: "filter",
condition: { author_id: "@user.id" },
},
{
permission: "data.posts.delete",
effect: "filter",
condition: { author_id: "@user.id" },
},
],
},
anonymous: {
implicit_allow: false,
is_default: true, // 未认证用户
permissions: [
{
permission: "data.posts.read",
effect: "filter",
condition: { published: "published" },
},
],
},
},
// 媒体存储
media: {
enabled: true,
body_max_size: 10 * 1024 * 1024,
adapter: {
type: "s3",
config: {
bucket: env.S3_BUCKET,
region: env.S3_REGION,
accessKeyId: env.S3_ACCESS_KEY,
secretAccessKey: env.S3_SECRET_KEY,
},
},
},
// CORS配置
server: {
cors: {
origin: env.ALLOWED_ORIGINS?.split(",") ?? [],
credentials: true,
},
},
},
}),
} satisfies CliBkndConfig;Environment Variables Template
环境变量模板
Create or set in your platform:
.env.productionbash
undefined创建文件或在你的托管平台中设置:
.env.productionbash
undefinedRequired
必填项
NODE_ENV=production
DB_URL=libsql://your-db.turso.io
DB_TOKEN=your-turso-token
JWT_SECRET=your-64-char-random-secret-here-generate-with-openssl
NODE_ENV=production
DB_URL=libsql://your-db.turso.io
DB_TOKEN=your-turso-token
JWT_SECRET=your-64-char-random-secret-here-generate-with-openssl
CORS
CORS配置
ALLOWED_ORIGINS=https://myapp.com,https://www.myapp.com
ALLOWED_ORIGINS=https://myapp.com,https://www.myapp.com
Media Storage (S3)
媒体存储(S3)
S3_BUCKET=my-bucket
S3_REGION=us-east-1
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=secret...
S3_BUCKET=my-bucket
S3_REGION=us-east-1
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=secret...
Or Cloudinary
或使用Cloudinary
CLOUDINARY_CLOUD=my-cloud
CLOUDINARY_KEY=123456
CLOUDINARY_SECRET=secret
CLOUDINARY_CLOUD=my-cloud
CLOUDINARY_KEY=123456
CLOUDINARY_SECRET=secret
OAuth (if used)
OAuth(如果使用)
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
---GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
---Security Checklist
安全检查清单
Authentication
身份验证
- JWT secret is 32+ characters, randomly generated
- JWT secret stored in environment variable, not code
- Cookie set
httpOnly: true - Cookie in production (HTTPS)
secure: true - Cookie or
sameSite: "strict""lax" - Password hashing uses bcrypt with rounds >= 10
- Minimum password length enforced (8+ chars)
- JWT密钥长度至少32个字符,为随机生成
- JWT密钥存储在环境变量中,而非代码里
- 已设置Cookie的
httpOnly: true - 生产环境中已设置Cookie的(HTTPS)
secure: true - Cookie的设置为
sameSite或"strict""lax" - 密码哈希使用bcrypt,且迭代次数>=10
- 已强制执行最小密码长度(8个字符以上)
Authorization
授权
- Guard enabled ()
guard.enabled: true - Default role defined for anonymous users
- Admin role does NOT use unless intended
implicit_allow - Sensitive entities have explicit permissions
- Row-level security filters user-owned data
- 已启用Guard()
guard.enabled: true - 已为匿名用户定义默认角色
- 管理员角色未使用(除非有明确需求)
implicit_allow - 敏感数据实体已配置明确的权限规则
- 行级安全已过滤用户自有数据
Data
数据
- set
isProduction: true - Database credentials in environment variables
- No test/seed data in production
- Backups configured for database
- 已设置
isProduction: true - 数据库凭据存储在环境变量中
- 生产环境中无测试/种子数据
- 已为数据库配置备份
Media
媒体存储
- Cloud storage configured (not local filesystem)
- Storage credentials in environment variables
- CORS configured on storage bucket
- Max upload size limited ()
body_max_size
- 已配置云存储(而非本地文件系统)
- 存储凭据存储在环境变量中
- 已在存储桶上配置CORS
- 已限制最大上传大小()
body_max_size
Network
网络
- CORS origins explicitly listed (no wildcard )
* - HTTPS enforced (via platform/proxy)
- API rate limiting configured (if needed)
- 已明确列出CORS允许的源(无通配符)
* - 已强制使用HTTPS(通过托管平台/代理)
- 已配置API速率限制(如有需要)
Platform-Specific Security
特定平台安全配置
Cloudflare Workers
Cloudflare Workers
typescript
// Secrets set via wrangler
// wrangler secret put JWT_SECRET
// wrangler secret put DB_TOKEN
export default hybrid<CloudflareBkndConfig>({
app: (env) => ({
connection: d1Sqlite({ binding: env.DB }),
isProduction: true,
auth: {
jwt: { secret: env.JWT_SECRET },
cookie: {
httpOnly: true,
secure: true,
sameSite: "strict",
},
},
}),
});typescript
// 通过wrangler设置密钥
// wrangler secret put JWT_SECRET
// wrangler secret put DB_TOKEN
export default hybrid<CloudflareBkndConfig>({
app: (env) => ({
connection: d1Sqlite({ binding: env.DB }),
isProduction: true,
auth: {
jwt: { secret: env.JWT_SECRET },
cookie: {
httpOnly: true,
secure: true,
sameSite: "strict",
},
},
}),
});Vercel
Vercel
bash
undefinedbash
undefinedSet via Vercel CLI or dashboard
通过Vercel CLI或控制台设置
vercel env add JWT_SECRET production
vercel env add DB_URL production
vercel env add DB_TOKEN production
undefinedvercel env add JWT_SECRET production
vercel env add DB_URL production
vercel env add DB_TOKEN production
undefinedDocker
Docker
yaml
undefinedyaml
undefineddocker-compose.yml
docker-compose.yml
services:
bknd:
environment:
- NODE_ENV=production
- JWT_SECRET=${JWT_SECRET} # From .env or host
# Never put secrets directly in docker-compose.yml
---services:
bknd:
environment:
- NODE_ENV=production
- JWT_SECRET=${JWT_SECRET} # 来自.env文件或主机环境
# 绝不要将密钥直接写入docker-compose.yml
---Testing Production Config Locally
本地测试生产环境配置
Test with production-like settings before deploying:
bash
undefined在部署前,使用类生产环境设置进行测试:
bash
undefinedCreate .env.production.local (gitignored)
创建.env.production.local文件(已加入.gitignore)
NODE_ENV=production
DB_URL=libsql://test-db.turso.io
DB_TOKEN=test-token
JWT_SECRET=test-secret-min-32-characters-here
NODE_ENV=production
DB_URL=libsql://test-db.turso.io
DB_TOKEN=test-token
JWT_SECRET=test-secret-min-32-characters-here
Run with production env
使用生产环境变量运行
NODE_ENV=production bun run index.ts
NODE_ENV=production bun run index.ts
Or source the file
或加载环境变量文件
source .env.production.local && bun run index.ts
**Verify:**
1. Admin panel is read-only (no schema changes)
2. API errors don't expose stack traces
3. Auth requires valid JWT
4. Guard enforces permissions
---source .env.production.local && bun run index.ts
**验证项:**
1. 管理面板为只读模式(无法修改架构)
2. API错误不会暴露堆栈跟踪
3. 身份验证需要有效的JWT
4. Guard已强制执行权限规则
---Common Pitfalls
常见问题
"JWT_SECRET required" Error
"JWT_SECRET required" 错误
Problem: Auth fails at startup
Fix: Ensure JWT_SECRET is set and accessible:
bash
undefined**问题:**启动时身份验证失败
**解决方法:**确保JWT_SECRET已设置且可访问:
bash
undefinedCheck env is loaded
检查环境变量是否已加载
echo $JWT_SECRET
echo $JWT_SECRET
Cloudflare: set secret
Cloudflare:设置密钥
wrangler secret put JWT_SECRET
wrangler secret put JWT_SECRET
Docker: pass env
Docker:传递环境变量
docker run -e JWT_SECRET="your-secret" ...
undefineddocker run -e JWT_SECRET="your-secret" ...
undefinedGuard Not Enforcing Permissions
Guard未强制执行权限
Problem: Users can access everything
Fix: Ensure Guard is enabled:
typescript
config: {
guard: {
enabled: true, // Must be true!
},
}**问题:**用户可以访问所有资源
**解决方法:**确保Guard已启用:
typescript
config: {
guard: {
enabled: true, // 必须设置为true!
},
}Cookies Not Set (CORS Issues)
Cookie未设置(CORS问题)
Problem: Auth works in Postman but not browser
Fix:
typescript
auth: {
cookie: {
sameSite: "lax", // "strict" may block OAuth redirects
secure: true,
},
},
config: {
server: {
cors: {
origin: ["https://your-frontend.com"], // Explicit, not "*"
credentials: true,
},
},
}**问题:**在Postman中身份验证正常,但在浏览器中失败
解决方法:
typescript
auth: {
cookie: {
sameSite: "lax", // "strict"可能会阻止OAuth重定向
secure: true,
},
},
config: {
server: {
cors: {
origin: ["https://your-frontend.com"], // 明确指定,不要使用"*"
credentials: true,
},
},
}Admin Panel Allows Changes
管理面板允许修改
Problem: Schema can be modified in production
Fix: Set :
isProduction: truetypescript
isProduction: true, // Locks admin to read-only**问题:**生产环境中可以修改架构
**解决方法:**设置:
isProduction: truetypescript
isProduction: true, // 将管理面板锁定为只读模式Detailed Errors Exposed
暴露详细错误信息
Problem: API returns stack traces
Fix: hides internal errors. Also check for custom error handlers exposing details.
isProduction: true**问题:**API返回堆栈跟踪
解决方法:会隐藏内部错误。同时检查是否有自定义错误处理程序暴露了详细信息。
isProduction: trueDOs and DON'Ts
相关技能
DO:
- Set in production
isProduction: true - Generate cryptographically secure JWT secrets (32+ chars)
- Enable Guard for authorization
- Use cloud storage for media
- Set explicit CORS origins
- Use environment variables for all secrets
- Test production config locally first
- Enable HTTPS (via platform/proxy)
- Set cookie and
secure: truehttpOnly: true
DON'T:
- Use default or weak JWT secrets
- Commit secrets to version control
- Use wildcard () CORS origins
* - Leave Guard disabled in production
- Use local filesystem storage in serverless
- Expose detailed error messages
- Skip the security checklist
- Use password hashing (use
sha256)bcrypt - Set on non-admin roles
implicit_allow: true
- bknd-deploy-hosting - 部署到托管平台
- bknd-database-provision - 设置生产数据库
- bknd-env-config - 环境变量配置
- bknd-setup-auth - 身份验证配置
- bknd-create-role - 定义授权角色
- bknd-storage-config - 媒体存储设置
Related Skills
—
- bknd-deploy-hosting - Deploy to hosting platforms
- bknd-database-provision - Set up production database
- bknd-env-config - Environment variable setup
- bknd-setup-auth - Authentication configuration
- bknd-create-role - Define authorization roles
- bknd-storage-config - Media storage setup
—