Environment Variables
Keep secrets and per-environment settings out of your code and load them into StarkForge
StarkForge never reads process.env itself. Your code reads the variables and passes the values to new Forge as options, so every input to the framework is visible in your own files and you can validate it once, at start-up. This page lists the variable names the documentation uses, shows how to load them from a .env file, how to generate the two secrets and how to check everything with Zod.
The Variables
The names below are a convention, not something StarkForge requires. You are free to use other names as long as your code passes the values to the matching option.
| Variable | Option it feeds | Development | Production |
|---|---|---|---|
NODE_ENV | ENV | Optional, defaults to development | Set to production |
PORT | port | Optional, defaults to 5000 | Many hosts provide it |
MONGODB_URI | mongoDBURI | Optional, a local MongoDB is used | Required |
JWT_SECRET | jwtSecret | Required when sign-in is configured | Required when sign-in is configured |
MASTER_KEY | masterKey | Required for two-factor codes and encrypted fields | Required for two-factor codes and encrypted fields |
FRONTEND_URL | frontendURL | Needed by a browser app on another origin | Needed by a browser app on another origin |
BACKEND_URL | backendURL | Optional | Needed for social sign-in |
RESEND_API_KEY | resendAPIKey | Optional, codes are printed to the console | Required for emailed codes and links |
SYSTEM_EMAIL_SENDER | systemEmailSender | Optional | Required for emailed codes and links |
CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY and CLOUDINARY_API_SECRET | cloudinaryCloudName, cloudinaryAPIKey and cloudinaryAPISecret | Required when a route accepts files | Required when a route accepts files |
GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET | The google entry of the social sign-in providers | Needed for Google sign-in | Needed for Google sign-in |
GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET | The github entry of the social sign-in providers | Needed for GitHub sign-in | Needed for GitHub sign-in |
The Configuration page describes each option, and the Authentication page explains the sign-in variables.
Load a .env File
Node.js loads a file of NAME=value lines when you start it with --env-file. You need no extra package, and tsx accepts the same flag. The dev and start scripts from Installation already use it. Create .env in the project root:
NODE_ENV=development
PORT=5000
MONGODB_URI=mongodb://127.0.0.1:27017
FRONTEND_URL=http://localhost:3000A variable that already exists in the real environment wins over the same name in the file. That is what you want on a server, where the host's settings must override a file that was copied by mistake.
Commit a second file, .env.example, with the same names and no secrets. It tells the next developer what to set:
# Copy this file to .env and fill in the values.
NODE_ENV=development
PORT=5000
MONGODB_URI=mongodb://127.0.0.1:27017
FRONTEND_URL=http://localhost:3000
# Generate both secrets with: npx tsx scripts/generate-secrets.ts
JWT_SECRET=
# MASTER_KEY=The .env file holds real secrets. List it in .gitignore, and commit only .env.example. A secret that reached a repository must be replaced, even if you delete the commit afterwards.
node_modules
dist
.envGenerate Secrets
generateJWTSecret() and generateMasterKey() are exported by StarkForge. Both are asynchronous and return a string. This script prints one .env line for each:
// scripts/generate-secrets.ts
import { generateJWTSecret, generateMasterKey } from "@starklabs/forge";
console.log(`JWT_SECRET=${await generateJWTSecret()}`);
console.log(`MASTER_KEY=${await generateMasterKey()}`);Run it once and append the output to your .env file. Run it again for production, because every environment needs its own secrets:
npx tsx scripts/generate-secrets.ts >> .envThe JWT secret is a long random string, and the master key is base64 text of exactly 32 random bytes. StarkForge rejects a jwtSecret shorter than 32 characters and a masterKey of any other length.
If you lose MASTER_KEY, every encrypted field and every two-factor secret becomes unreadable, and nothing can recover it. If you change JWT_SECRET, every session token stops working and everyone has to sign in again. Store both in a password manager or your host's secret store.
Check Variables With Zod
A missing or malformed variable should stop the server at start-up with a clear message, not fail later in the middle of a request. Parse process.env with Zod in one file and import the result everywhere else:
// src/env.ts
import { z } from "zod";
const schema = z.object({
NODE_ENV: z.string().default("development"),
PORT: z.coerce.number().int().min(0).max(65535).default(5000),
MONGODB_URI: z.string().min(1).optional(),
JWT_SECRET: z.string().min(32),
MASTER_KEY: z.string().min(1).optional(),
FRONTEND_URL: z.url().optional(),
});
const parsed = schema.safeParse(process.env);
if (!parsed.success) {
console.error(z.prettifyError(parsed.error));
process.exit(1);
}
export const env = parsed.data;z.coerce.number() turns the text of PORT into a number, which new Forge needs. The other rules check the shape only. StarkForge still checks the values it receives, for example it requires mongoDBURI in production. Pass the checked values to new Forge:
// src/server.ts
import { Forge } from "@starklabs/forge";
import { env } from "./env.js";
const forge = new Forge({
ENV: env.NODE_ENV,
port: env.PORT,
mongoDBURI: env.MONGODB_URI,
jwtSecret: env.JWT_SECRET,
masterKey: env.MASTER_KEY,
frontendURL: env.FRONTEND_URL,
});
await forge.start();If you use the layout from Project Structure, the same lines belong in createOptions() in src/config.ts. With PORT=abc, a short secret and a bad FRONTEND_URL, the server prints this and exits with code 1:
✖ Invalid input: expected number, received NaN
→ at PORT
✖ Too small: expected string to have >=32 characters
→ at JWT_SECRET
✖ Invalid URL
→ at FRONTEND_URLSet Variables on a Host
Hosting platforms let you define environment variables in a dashboard or a command line. Use the same names there as in your .env file, and do not upload the file itself. With Docker, pass the variables when you run the container, for example docker run --env-file .env, and keep .env out of the image. The Deployment page lists everything to set before you go live.
Where to Go Next
Authentication
Use the secrets to add sign-up, sign-in and sessions.
Deployment
Set the production variables and go live.
:::