Skip to main content

What is .env?

The .env file stores configuration values that change between environments (development, staging, production) or contain sensitive information like API keys and secrets.
Why use .env files? - Keep secrets out of code (never commit them to git) - Different values for different environments - Easy to change without modifying code - Follows the 12-factor app methodology

How It Works

  1. You create a .env file in the project root
  2. The app loads these values at startup
  3. Code accesses them via process.env.VARIABLE_NAME
# .env file
DATABASE_URL=postgres://user:pass@localhost:5432/mydb

# In code
const dbUrl = process.env.DATABASE_URL;

Files in This Project

FilePurposeCommitted to Git?
.env.exampleTemplate with all variablesYes
.envYour actual configurationNo (gitignored)
.env.localLocal overrides (optional)No
.env.testTest environment (optional)No

Quick Setup

# Copy the template
cp .env.example .env

# Edit with your values
nano .env  # or use any text editor

Required Variables

These must be set for the application to start.

Database Connection

JWT Secret

The JWT secret is used to sign authentication tokens. It must be:
  • At least 32 characters long
  • Random and unpredictable
  • Different for each environment
1

Generate a secure secret

Run this command in your terminal:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
This outputs a 64-character hex string like:
a1b2c3d4e5f6...  (64 characters)
2

Add to .env

JWT_SECRET=a1b2c3d4e5f6...your-generated-secret
Never use the example value in production! Generate a unique secret for each environment.

LLM Configuration

Hitler uses LLMs for natural language task parsing. You need at least one provider configured.
1

Create an Anthropic account

Go to console.anthropic.com and sign up.
2

Get your API key

  1. Navigate to API Keys in the dashboard 2. Click Create Key 3. Copy the key (starts with sk-ant-)
3

Add to .env

ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
LLM_MODEL_ANTHROPIC=claude-3-haiku-20240307
LLM_PROVIDER=anthropic
Available Models:
ModelSpeedCostBest For
claude-3-haiku-20240307FastestLowestDevelopment, simple tasks
claude-3-sonnet-20240229MediumMediumProduction
claude-3-opus-20240229SlowestHighestComplex reasoning

Option 2: OpenAI (GPT)

1

Create an OpenAI account

Go to platform.openai.com and sign up.
2

Get your API key

  1. Go to API Keys section 2. Click Create new secret key 3. Copy the key (starts with sk-)
3

Add to .env

bash OPENAI_API_KEY=sk-your-key-here LLM_MODEL_OPENAI=gpt-4o-mini LLM_PROVIDER=openai
Available Models:
ModelSpeedCostBest For
gpt-4o-miniFastLowDevelopment, simple tasks
gpt-4oMediumMediumProduction
gpt-4-turboSlowerHigherComplex tasks

LLM Settings

# Maximum tokens in LLM response (default: 1024)
LLM_MAX_TOKENS=1024

# Which provider to use: anthropic | openai
LLM_PROVIDER=anthropic
No LLM key? The app will use mock responses in development. Task parsing will return placeholder data, which is fine for UI development but not realistic testing.

Slack Integration

The Hitler Slack bot uses Socket Mode, which means it connects to Slack via WebSocket instead of HTTP webhooks. This is simpler for local development since you don’t need ngrok or a public URL.

Create a Slack App

1

Go to Slack API

Visit api.slack.com/apps and click Create New App.
2

Choose creation method

Select From scratch, enter a name (e.g., “Hitler Dev”), and select your workspace.
3

Get Signing Secret

In Basic Information, scroll to App Credentials: - Copy Signing Secret → This is your SLACK_SIGNING_SECRET - Copy Client ID and Client Secret (needed for OAuth login on web dashboard)

Enable Socket Mode

1

Navigate to Socket Mode

In your Slack App settings, click Socket Mode in the left sidebar.
2

Enable Socket Mode

Toggle Enable Socket Mode to ON.
3

Create App-Level Token

  1. Click Generate Token and Scopes 2. Give it a name (e.g., “socket-token”) 3. Add the connections:write scope 4. Click Generate 5. Copy the token (starts with xapp-) → This is your SLACK_APP_TOKEN
The App-Level Token is only shown once! Save it immediately to your .env file.

Configure OAuth Scopes

In your Slack App settings, go to OAuth & Permissions: Bot Token Scopes (required):
app_mentions:read  # Receive @mentions
chat:write         # Send messages
commands           # Handle slash commands
im:history         # Read DM history
im:read            # View DM info
im:write           # Start DMs
users:read         # Get user info
team:read          # Get workspace info
User Token Scopes (for OAuth login on web dashboard):
identity.basic   # Basic user identity
identity.email   # User's email

Install to Workspace and Get Bot Token

1

Install the App

  1. Go to Install App in the left sidebar 2. Click Install to Workspace 3. Authorize the requested permissions
2

Copy Bot Token

After installation, you’ll see Bot User OAuth Token. Copy it (starts with xoxb-) → This is your SLACK_BOT_TOKEN

Configure Event Subscriptions

1

Enable Events

Go to Event Subscriptions and toggle Enable Events on.
2

Subscribe to Bot Events

Under Subscribe to bot events, add: - app_home_opened - App home tab views - app_mention
  • @mentions in channels - message.im - Direct messages
3

Save Changes

Click Save Changes at the bottom.
With Socket Mode, you don’t need to set a Request URL - events are delivered over the WebSocket connection.

Create Slash Command (Optional)

1

Go to Slash Commands

Click Slash Commands in the left sidebar.
2

Create Command

  1. Click Create New Command 2. Command: /hitler 3. Short Description: “Interact with Hitler” 4. Click Save

Add All Credentials to .env

# Slack Bot (Socket Mode) - Required for bot functionality
SLACK_BOT_TOKEN=xoxb-your-bot-token              # From OAuth & Permissions
SLACK_APP_TOKEN=xapp-your-app-level-token        # From Socket Mode settings
SLACK_SIGNING_SECRET=your-signing-secret         # From Basic Information

# Slack OAuth - Required for "Sign in with Slack" on web dashboard
SLACK_CLIENT_ID=1234567890.1234567890            # From Basic Information
SLACK_CLIENT_SECRET=abcdef1234567890abcdef       # From Basic Information

Run the Bot

# Start the API first (the bot needs it)
pnpm --filter @hitler/api dev

# In another terminal, start the bot
pnpm --filter @hitler/bot-slack dev
You should see:
⚡️ Hitler Slack bot running on port 3002
   API URL: http://localhost:3000
   Socket Mode: enabled
Now you can DM your bot in Slack or @mention it in channels!

Email Configuration (Optional)

For sending email notifications (password resets, alerts, etc.).
1

Create a Resend account

Go to resend.com and sign up.
2

Get your API key

In the dashboard, go to API Keys and create a new key.
3

Verify your domain

Add DNS records to verify your sending domain.
4

Add to .env

bash RESEND_API_KEY=re_123456789 EMAIL_FROM=notifications@yourdomain.com

Option 2: SMTP

For using any SMTP server (Gmail, SendGrid, Mailgun, etc.):
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
EMAIL_FROM=your-email@gmail.com
For Gmail, you need to create an App Password, not your regular password.

Secrets Storage (Production)

In production, platform OAuth tokens (Slack) are stored encrypted in Cloudflare KV.

Set Up Cloudflare KV

1

Create a Cloudflare account

Go to cloudflare.com and sign up.
2

Create a KV namespace

  1. Go to Workers & PagesKV
  2. Click Create a namespace
  3. Name it (e.g., “hitler-secrets-prod”)
  4. Copy the Namespace ID
3

Get your Account ID

Find it in the right sidebar of any Cloudflare page, or in Account Home.
4

Create an API token

  1. Go to My ProfileAPI Tokens
  2. Click Create Token
  3. Use Edit Cloudflare Workers template
  4. Copy the token
5

Generate encryption key

node scripts/generate-encryption-key.js
This outputs a base64-encoded 32-byte key.
6

Add to .env

CLOUDFLARE_ACCOUNT_ID=abc123def456
CLOUDFLARE_KV_NAMESPACE_ID=xyz789
CLOUDFLARE_API_TOKEN=your-api-token
SECRETS_ENCRYPTION_KEY=base64-encoded-key
Cloudflare KV is only needed in production. In development, secrets are stored in memory (which is fine for local testing).

Context Memory (Optional)

The context memory system enables passive intelligence by listening to Slack channel messages, extracting facts, and building organizational memory.

URL Fetching (Jina Reader)

JINA_API_KEY=jina_your-api-key
Get a key from jina.ai. Used to fetch and extract content from URLs shared in channels. Falls back to @extractus/article-extractor if Jina is unavailable.

Web Search (Tavily)

TAVILY_API_KEY=tvly-your-api-key
Get a key from tavily.com. Used by the search_web LLM tool to search the web for current information.

Tuning Parameters

# Messages per extraction batch (default: 20)
CONTEXT_BATCH_SIZE=20

# Batch processing interval in ms (default: 300000 = 5 min)
CONTEXT_BATCH_INTERVAL_MS=300000

# Days to keep raw channel_messages before cleanup (default: 7)
CONTEXT_RAW_RETENTION_DAYS=7
The context memory system also uses OPENAI_API_KEY (for text-embedding-3-small embeddings) and ANTHROPIC_API_KEY (for Claude Haiku fact extraction). These are already configured as part of the LLM setup above.

Rate Limiting

Configure API rate limits to prevent abuse.
# Time window in milliseconds (default: 60000 = 1 minute)
RATE_LIMIT_WINDOW_MS=60000

# Maximum requests per window (default: 100)
RATE_LIMIT_MAX_REQUESTS=100
Example configurations:
EnvironmentWindowMax RequestsResult
Development6000010001000 req/min (relaxed)
Production60000100100 req/min (standard)
Strict600003030 req/min (for sensitive endpoints)

Task Configuration

# Hours before task drafts auto-expire (default: 24)
TASK_DRAFT_EXPIRY_HOURS=24
Task drafts that aren’t confirmed within this time are automatically deleted.

Logging

# Log level: debug | info | warn | error
LOG_LEVEL=debug
LevelWhen to Use
debugDevelopment - verbose output
infoProduction - normal operations
warnProduction - only warnings and errors
errorProduction - only errors

Complete .env Template

Here’s a complete template with all variables:
# =============================================
# DATABASE
# =============================================
DATABASE_URL=postgres://hitler:hitler@localhost:5432/hitler
REDIS_URL=redis://localhost:6379

# =============================================
# SERVER PORTS
# =============================================
API_PORT=3001
WEB_PORT=3005
BOT_SLACK_PORT=3002

# =============================================
# API SERVER
# =============================================
NODE_ENV=development
JWT_SECRET=GENERATE_ME_WITH_CRYPTO_RANDOM_BYTES
JWT_EXPIRES_IN=7d
# IMPORTANT: API_KEY must be identical in root .env (read by bot-slack) and apps/api/.env (read by API).
# If they don't match, bots will get 401 errors when calling the API.
API_KEY=hitler_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_dev_secret_key

# =============================================
# WEB APP
# =============================================
NEXT_PUBLIC_API_URL=http://localhost:3005/api

# =============================================
# LLM (choose one or both)
# =============================================
ANTHROPIC_API_KEY=sk-ant-...
LLM_MODEL_ANTHROPIC=claude-3-haiku-20240307

OPENAI_API_KEY=sk-...
LLM_MODEL_OPENAI=gpt-4o-mini

LLM_MAX_TOKENS=1024
LLM_PROVIDER=anthropic

# =============================================
# SLACK (Socket Mode)
# =============================================
# Bot credentials (required for bot functionality)
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-level-token
SLACK_SIGNING_SECRET=

# OAuth credentials (required for "Sign in with Slack")
SLACK_CLIENT_ID=
SLACK_CLIENT_SECRET=

# Notifications (optional)
SLACK_WEBHOOK_URL=

# =============================================
# EMAIL (optional)
# =============================================
RESEND_API_KEY=re_...
EMAIL_FROM=noreply@yourdomain.com

# =============================================
# CLOUDFLARE KV (production only)
# =============================================
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_KV_NAMESPACE_ID=
CLOUDFLARE_API_TOKEN=
SECRETS_ENCRYPTION_KEY=

# =============================================
# RATE LIMITING
# =============================================
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100

# =============================================
# TASKS
# =============================================
TASK_DRAFT_EXPIRY_HOURS=24

# =============================================
# CONTEXT MEMORY (optional)
# =============================================
JINA_API_KEY=jina_...                  # URL fetching via Jina Reader API
TAVILY_API_KEY=tvly-...                # Web search via Tavily
OPENAI_EMBEDDING_MODEL=text-embedding-3-small  # Embedding model for context memory
CONTEXT_BATCH_SIZE=20                  # Messages per extraction batch
CONTEXT_BATCH_INTERVAL_MS=300000       # Batch interval (5 min)
CONTEXT_RAW_RETENTION_DAYS=7           # Days to keep raw channel_messages

# =============================================
# LOGGING
# =============================================
LOG_LEVEL=debug

Security Best Practices

Never commit .env

The .gitignore already excludes .env files. Never override this.

Use different secrets per environment

Generate unique JWT secrets, encryption keys for dev/staging/prod.

Rotate secrets regularly

Change API keys and secrets periodically, especially after team changes.

Use a secrets manager in production

Consider tools like HashiCorp Vault, AWS Secrets Manager, or Doppler.

Troubleshooting

Ensure you’ve copied .env.example to .env and the database is running:
cp .env.example .env
make db-up
Generate a proper secret: bash node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
You need to set either ANTHROPIC_API_KEY or OPENAI_API_KEY for real LLM responses.
“You must provide an appToken” error:
  • Set SLACK_APP_TOKEN in your .env (starts with xapp-)
  • Generate one in Slack App → Socket Mode → App-Level Tokens
“Invalid token” error:
  • Verify SLACK_BOT_TOKEN is correct (starts with xoxb-)
  • Re-install the app to your workspace if needed
  1. Ensure Socket Mode is enabled in your Slack App settings 2. Verify Event Subscriptions has message.im and app_mention subscribed 3. For channel messages, the bot must be invited to the channel 4. Check that SLACK_SIGNING_SECRET matches your app
  1. Restart the dev server after changing .env
  2. Check for typos in variable names
  3. Ensure no extra spaces around =