S
Get Started

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.

VariableOption it feedsDevelopmentProduction
NODE_ENVENVOptional, defaults to developmentSet to production
PORTportOptional, defaults to 5000Many hosts provide it
MONGODB_URImongoDBURIOptional, a local MongoDB is usedRequired
JWT_SECRETjwtSecretRequired when sign-in is configuredRequired when sign-in is configured
MASTER_KEYmasterKeyRequired for two-factor codes and encrypted fieldsRequired for two-factor codes and encrypted fields
FRONTEND_URLfrontendURLNeeded by a browser app on another originNeeded by a browser app on another origin
BACKEND_URLbackendURLOptionalNeeded for social sign-in
RESEND_API_KEYresendAPIKeyOptional, codes are printed to the consoleRequired for emailed codes and links
SYSTEM_EMAIL_SENDERsystemEmailSenderOptionalRequired for emailed codes and links
CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY and CLOUDINARY_API_SECRETcloudinaryCloudName, cloudinaryAPIKey and cloudinaryAPISecretRequired when a route accepts filesRequired when a route accepts files
GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRETThe google entry of the social sign-in providersNeeded for Google sign-inNeeded for Google sign-in
GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRETThe github entry of the social sign-in providersNeeded for GitHub sign-inNeeded 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:

Bash
NODE_ENV=development
PORT=5000
MONGODB_URI=mongodb://127.0.0.1:27017
FRONTEND_URL=http://localhost:3000

A 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:

Bash
# 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=
Never Commit .env

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.

Text
node_modules
dist
.env

Generate Secrets

generateJWTSecret() and generateMasterKey() are exported by StarkForge. Both are asynchronous and return a string. This script prints one .env line for each:

TypeScript
// 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:

Bash
npx tsx scripts/generate-secrets.ts >> .env

The 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.

Keep Both Keys Safe

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:

TypeScript
// 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:

TypeScript
// 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:

Text
✖ Invalid input: expected number, received NaN
  → at PORT
✖ Too small: expected string to have >=32 characters
  → at JWT_SECRET
✖ Invalid URL
  → at FRONTEND_URL

Set 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.

:::